diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml index db9c232673b..08dbf7be85d 100644 --- a/AndroidManifest-common.xml +++ b/AndroidManifest-common.xml @@ -64,6 +64,12 @@ + + + + + + + \ No newline at end of file diff --git a/res/drawable/ic_music_note_24dp.xml b/res/drawable/ic_music_note_24dp.xml new file mode 100644 index 00000000000..323633f8327 --- /dev/null +++ b/res/drawable/ic_music_note_24dp.xml @@ -0,0 +1,10 @@ + + + + diff --git a/res/drawable/ic_quickspace_derp.xml b/res/drawable/ic_quickspace_derp.xml new file mode 100644 index 00000000000..83bd670054b --- /dev/null +++ b/res/drawable/ic_quickspace_derp.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/res/drawable/ic_quickspace_evening.xml b/res/drawable/ic_quickspace_evening.xml new file mode 100644 index 00000000000..2192cf4d329 --- /dev/null +++ b/res/drawable/ic_quickspace_evening.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/res/drawable/ic_quickspace_midnight.xml b/res/drawable/ic_quickspace_midnight.xml new file mode 100644 index 00000000000..5b83510320e --- /dev/null +++ b/res/drawable/ic_quickspace_midnight.xml @@ -0,0 +1,7 @@ + + + diff --git a/res/drawable/ic_quickspace_morning.xml b/res/drawable/ic_quickspace_morning.xml new file mode 100644 index 00000000000..b74fc674873 --- /dev/null +++ b/res/drawable/ic_quickspace_morning.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/res/layout/quickspace_alternate_double.xml b/res/layout/quickspace_alternate_double.xml new file mode 100644 index 00000000000..8ec007f0643 --- /dev/null +++ b/res/layout/quickspace_alternate_double.xml @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/quickspace_doubleline.xml b/res/layout/quickspace_doubleline.xml new file mode 100644 index 00000000000..3eb9aec7886 --- /dev/null +++ b/res/layout/quickspace_doubleline.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/reserved_container_alternate_workspace.xml b/res/layout/reserved_container_alternate_workspace.xml new file mode 100644 index 00000000000..d67157f18be --- /dev/null +++ b/res/layout/reserved_container_alternate_workspace.xml @@ -0,0 +1,34 @@ + + + + + + + + diff --git a/res/layout/reserved_container_workspace.xml b/res/layout/reserved_container_workspace.xml new file mode 100644 index 00000000000..76efb132480 --- /dev/null +++ b/res/layout/reserved_container_workspace.xml @@ -0,0 +1,35 @@ + + + + + + + + diff --git a/res/values-de/derp_strings.xml b/res/values-de/derp_strings.xml index 80c20f2e1cc..588d68fbed1 100644 --- a/res/values-de/derp_strings.xml +++ b/res/values-de/derp_strings.xml @@ -155,7 +155,6 @@ Basierend auf AOSP Launcher3 - AP1A.240305.019 Besonderen Dank an Über Info über den DerpLauncher diff --git a/res/values-zh-rTW/derp_strings.xml b/res/values-zh-rTW/derp_strings.xml new file mode 100644 index 00000000000..488ae47389d --- /dev/null +++ b/res/values-zh-rTW/derp_strings.xml @@ -0,0 +1,408 @@ + + + + + + 主畫面設定 + + + 主畫面 + Set home panel and more + 圖示 + Set icon size and more + 應用程式抽屜 + 自訂你的應用程式抽屜 + Recents + Revamp the overview screen + 雜項 + 其他選項 + 一般 + Interface + 資訊一覽 + 搜尋列 + 快速操作 + + + 開發人員選項 + Enable some hidden features at your own risk + + + 鎖定佈局 + 禁止在桌面上新增、移除或移動圖示和小工具 + 允許在桌面上新增、移除或移動圖示和小工 + 無法將小工具新增至主畫面 + + + 桌面標籤 + 在桌面上顯示圖示名稱 + 應用程式抽屜標籤 + 在應用程式抽屜上顯示圖示名稱 + + + Swipe to access Google app + + When you swipe right from main home screen + + When you swipe left from main home screen + + + 輕觸兩下即可休眠 + 在空白處點兩下即可關閉螢幕 + + + 值:%s + 預設 + 預設值:%s\n長按即可設定 + 已設定為預設值 + + + 圖示大小 + + + 字體大小 + + + Max lines for app label + + + 搜尋 + 智慧鏡頭 + 語音搜尋 + Google 搜尋列 + 在底部顯示搜尋列 + + + 底欄背景 + 在應用程式底欄上添加透明背景 + + + 桌布滑動 + Wallpaper scrolling effect for multiple screens + + + 桌布縮放 + Zoom in or out the wallpaper when using drawer or recent apps + + + 顯示狀態列 + 在主畫面上顯示狀態列 + + + 顯示頂部陰影 + 在狀態列下方添加陰影 + + + 列高 + + + 背景模糊度 + 設定最近應用程式和應用程式抽屜的背景模糊度 + + + 建議 + 在應用程式抽屜 & 主畫面上顯示建議 + + + 背景不透明度 + + + 使用緊湊設計的操作按鈕圖示 + 在顯示較多操作按鈕時可能需要 + 螢幕截圖 + 全部清除 + 智慧鏡頭 + + + 來源 + 未知 + 上次更新 + 版本 + 更多 + + + 強制關閉 + 應用程式已強制關閉 + + + %1$s 可用 | %2$s + 記憶體資訊 + + + 應用程式搜尋欄 + Search bar on top of the app drawer + + + 重新啟動 + Restart the DerpLauncher manually to apply any pending settings + 正在重新啟動 DerpLauncher... + Restarting DerpLauncher to apply changes... + Restarting DerpLauncher to update components... + + + Shake phone to clear all tasks + + + Allow short parallax + Enable full wallpaper scroll effect on smaller numbers of pages instead of cropping the wallpaper + Single page center + Center wallpaper if only using a single page + + + 圖示包 + 預設 + 安裝更多 + 沒有可用的應用程式商店 + + + 基於 AOSP Launcher3 + 特別感謝 + 關於 + 關於 DerpLauncher 的資訊 + + + 隱藏 & 受保護的應用程式 + 解鎖後即可管理隱藏和受保護的應用程式 + 進行身份驗證後即可開啟 %1$s + 載入中\u2026 + Please set up a secure lock screen to restrict app access + 說明 + Hidden apps and their widgets are hidden from the drawer + Protected apps require authentication to be opened from the launcher + + + 主題圖示 + Follow themed icons used on home screen + + + 深色狀態列 + 在主畫面上使用深色狀態列 + + + 強制使用單色圖示 + Force monochrome icons for apps that don\'t support them natively (requires re-toggling of themed icons) + + + 快速查看 + 在主畫面頂部上顯示 + + + 歡迎來到 DerpFest! + DerpFest 時間! + + 感謝你選擇我們 + 有史以來最好的 ROM + 來我們的 Telegram 群組上 Say hi 吧! + 輕觸此處開始 + 我們愛你 3000 次 + DerpFest 萬歲! + #StayDerped + + + + 早安 + 晚安 + 午安 + 晚安 + 你好 + 今天是 + 現在是 + + + 未知藝術家 + 正在播放 + 作者 + + + 正在播放 + Show the song you\'re playing + + + Extended style + Switch to extended style + + + 隨機訊息 + Make your companion more lively with random messages + + + Good morning! + Good morning, time to rock! + What a beautiful day! + Have a nice day! + What about a small 5 minutes nap? + Let\'s get this bread. + Mornings are a fresh canvas for your daily masterpiece. + Embrace the sunrise of opportunity each morning. + In the early light, find the power to illuminate your path. + Your day begins with endless possibilities. + Every sunrise is a reminder that you can start anew. + Awake with purpose, conquer the day ahead. + Your thoughts can be the foundation of great achievements. + The world awakens, and so does your potential. + The first chapter in your daily adventure. + Let your morning routine be the launchpad for success. + Sunrise or not, your potential is always on the rise. + It\'s time for fresh ideas and bold actions. + The early bird catches the worm, but you can catch your dreams. + Find the courage to chase your aspirations. + Rise and shine, for greatness awaits. + The best time to start is now. + + + Chase your dreams, not your fears. + In every moment, a new opportunity appears. + Embrace the journey, not just the destination. + Today is a gift, that\'s why it\'s called the present. + Let your smile change the world. + Life is a story, make each chapter count. + Find joy in every moments. + The best is yet to come, keep it up. + Your chance to turn \'what if\' into \'how about that\' + Seize the day, starting right now. + Happiness is a choice you can make any time. + Believe in yourself, even on a sleepy afternoon. + Let your actions speak louder than the clock. + Your life is yours, not your parents nor your friends. + Dream big, work hard, and never give up. + You are stronger than you think. + Every failure means a step closer to your success. + Your potential is endless, unlock it. + + + Embrace the present moment, no matter the hour. + In every dusk, find a new dawn of opportunity. + Life is a journey, enjoy the scenery along the way. + Your attitude shapes your reality. + Your story is still being written, even as the sun sets. + Capture the beauty in the ordinary moments. + It\'s time to reflect and reset. + Choose joy, no matter the time on the clock. + Opportunities don\'t clock out in the evening. + Be the reason someone smiles. + Your potential knows no bounds. + Moments of magic can happen at any hour. + Your life is what you make of it, make it extraordinary. + Stay curious, stay grateful. + Be the change you wish to see in the world. + Believe in yourself, you are capable of amazing things. + The only limit is the one you set for yourself. + Success begins with a single step. + + + Seize the night, for it holds its own mysteries. + In the quiet of the this evening, find serenity. + Life\'s greatest adventures can start in the darkness. + Your journey continues even after the sun has set. + Let the night be your canvas, paint it with dreams. + In the stillness of this evening, find your inner peace. + Stars shine brightest in the darkest hours. + Dream big, even in the late hours of the day. + Your life is a story waiting to be written. + It\'s time for reflection and renewal. + Late night thoughts can lead to early morning revelations. + Find joy in the stillness of this evening. + The night sky is a vast canvas of dreams. + Your potential knows no bedtime, pursue your passions. + Stay hopeful, even in the darkest hours. + In the midst of chaos, find your inner calm. + Stay positive, work hard, make it happen. + + + Nights are the quiet whispers of opportunity. + In the stillness of the night, find your inner strength. + Embrace the darkness, for it holds the keys to your dreams. + The world sleeps, but your potential is wide awake. + The night may be late, but your ambitions are timeless. + Stay curious, stay awake, and let nights guide you. + Find the courage to chase your dreams. + Great things never come from comfort zones. + You have the power to create your own destiny. + Happiness is a choice, choose it every day. + Life is what you make it, so make it count. + Stay focused, stay determined, stay unstoppable. + You are the author of your own story. + Strive for progress, not perfection. + Every day is a new beginning, make it a great one. + Inspire others by being your authentic self. + Your attitude determines your direction. + + + Is it time to flash another update already? + Make peace, not war + Oh hey, what\'s up? + Focus on your tasks + How many screenshots do you take? + Open goodness with DerpFest + Spread love, not havoc + We didn\'t start the fire! + Time for some good music + \u003C\u003C\u003C\u003C\u003C\u003C\u003C HEAD + You need DerpFest Premium to see this + Remember the Lineage of the Unicorn + Starting from the ground zero + We love you 3000 + Delicious even without Sushi + Do something nice today + This is best Pixel Experience, isn\'t it? + No illusions, welcome to reality! + Thank you for your support + Is your device derped? + One of the buildbot\'s best picks + Sanity for your Paranoia + Try Ice Cold desserts + What a lovely experience, isn\'t it? + DerpFest is a myth, right? + You are what you flash, don\'t be Potato + What\'s on your mind? + Expecto Patronum + Wubba Lubba Dub Dub + Winner Winner ....? + rm -rf \/ + Pringles aren\'t actually potato chips.. + Remember to check device\'s battery level! + Check your email/messages. + Check your to-do list if you have one. + You can disable quickspace via home settings. + #StayDerped \m/ + DerpFest FTW \m/ + Enjoying Android 14? + Hyped about Android 15? + Proudly presented without donations + Did you checkout all the tweaks? + You have tapped almost a 100k times. + Lots of options! + I am not your F1 button + Don\'t pass me! + Did you finish your homework? ツ + This is best ROM Experience, isn\'t it? + Well done! You have earned a bonus feature + That\'s enough, stop tapping me! + Only for pro users + Got an issue? Don\'t forget a logcat + + + + 天氣更新 + 顯示目前的天氣更新 + 需要啟用天氣服務 + Cloudy + Rainy + Sunny + Stormy + Snowy + Windy + Misty + 目前位置 + 顯示目前天氣位置 + Current condition + Display current weather condition summary + + Unable to find calendar or clock activity + + + 天氣設定 + 設定圖示包和天氣服務 + diff --git a/res/values/derp_config.xml b/res/values/derp_config.xml index 7a30e19cab3..596c83a5055 100644 --- a/res/values/derp_config.xml +++ b/res/values/derp_config.xml @@ -32,4 +32,7 @@ false + + + diff --git a/res/values/derp_dimens.xml b/res/values/derp_dimens.xml index ccc2faa22c4..62b97566d3b 100644 --- a/res/values/derp_dimens.xml +++ b/res/values/derp_dimens.xml @@ -12,4 +12,10 @@ 96dp 15dp 12dp + + + 20sp + 16sp + 18sp + 14sp diff --git a/res/values/derp_strings.xml b/res/values/derp_strings.xml index 3f0af120ff1..b39e3593b8f 100644 --- a/res/values/derp_strings.xml +++ b/res/values/derp_strings.xml @@ -27,6 +27,7 @@ Other options General Interface + Quickspace Search bar Quick actions @@ -166,7 +167,7 @@ DerpLauncher Based on AOSP Launcher3 - AP1A.240305.019 + AP1A.240405.002 Special thanks to About Info about the DerpLauncher @@ -192,4 +193,228 @@ Force monochrome icons Force monochrome icons for apps that don\'t support them natively (requires re-toggling of themed icons) + + + At A Glance + Show at the top of your home screen + EEE, MMM d + EEEE, MMMM d + + + Welcome to DerpFest! + DerpFest time! + + Thank you for choosing us + Best Rom Ever. + Say hi to us on Telegram + Tap here to begin + We love you 3000 + DerpFest FTW! + #StayDerped + + + + Good morning. + Good evening. + Good afternoon. + Good night. + Good day. + Today is + It\'s + + + Unknown Artist + Now playing + By + + + Now playing + Show the song you\'re playing + + + Extended style + Switch to extended style + + + Random messages + Make your companion more lively with random messages + + + Good morning! + Good morning, time to rock! + What a beautiful day! + Have a nice day! + What about a small 5 minutes nap? + Let\'s get this bread. + Mornings are a fresh canvas for your daily masterpiece. + Embrace the sunrise of opportunity each morning. + In the early light, find the power to illuminate your path. + Your day begins with endless possibilities. + Every sunrise is a reminder that you can start anew. + Awake with purpose, conquer the day ahead. + Your thoughts can be the foundation of great achievements. + The world awakens, and so does your potential. + The first chapter in your daily adventure. + Let your morning routine be the launchpad for success. + Sunrise or not, your potential is always on the rise. + It\'s time for fresh ideas and bold actions. + The early bird catches the worm, but you can catch your dreams. + Find the courage to chase your aspirations. + Rise and shine, for greatness awaits. + The best time to start is now. + + + Chase your dreams, not your fears. + In every moment, a new opportunity appears. + Embrace the journey, not just the destination. + Today is a gift, that\'s why it\'s called the present. + Let your smile change the world. + Life is a story, make each chapter count. + Find joy in every moments. + The best is yet to come, keep it up. + Your chance to turn \'what if\' into \'how about that\' + Seize the day, starting right now. + Happiness is a choice you can make any time. + Believe in yourself, even on a sleepy afternoon. + Let your actions speak louder than the clock. + Your life is yours, not your parents nor your friends. + Dream big, work hard, and never give up. + You are stronger than you think. + Every failure means a step closer to your success. + Your potential is endless, unlock it. + + + Embrace the present moment, no matter the hour. + In every dusk, find a new dawn of opportunity. + Life is a journey, enjoy the scenery along the way. + Your attitude shapes your reality. + Your story is still being written, even as the sun sets. + Capture the beauty in the ordinary moments. + It\'s time to reflect and reset. + Choose joy, no matter the time on the clock. + Opportunities don\'t clock out in the evening. + Be the reason someone smiles. + Your potential knows no bounds. + Moments of magic can happen at any hour. + Your life is what you make of it, make it extraordinary. + Stay curious, stay grateful. + Be the change you wish to see in the world. + Believe in yourself, you are capable of amazing things. + The only limit is the one you set for yourself. + Success begins with a single step. + + + Seize the night, for it holds its own mysteries. + In the quiet of the this evening, find serenity. + Life\'s greatest adventures can start in the darkness. + Your journey continues even after the sun has set. + Let the night be your canvas, paint it with dreams. + In the stillness of this evening, find your inner peace. + Stars shine brightest in the darkest hours. + Dream big, even in the late hours of the day. + Your life is a story waiting to be written. + It\'s time for reflection and renewal. + Late night thoughts can lead to early morning revelations. + Find joy in the stillness of this evening. + The night sky is a vast canvas of dreams. + Your potential knows no bedtime, pursue your passions. + Stay hopeful, even in the darkest hours. + In the midst of chaos, find your inner calm. + Stay positive, work hard, make it happen. + + + Nights are the quiet whispers of opportunity. + In the stillness of the night, find your inner strength. + Embrace the darkness, for it holds the keys to your dreams. + The world sleeps, but your potential is wide awake. + The night may be late, but your ambitions are timeless. + Stay curious, stay awake, and let nights guide you. + Find the courage to chase your dreams. + Great things never come from comfort zones. + You have the power to create your own destiny. + Happiness is a choice, choose it every day. + Life is what you make it, so make it count. + Stay focused, stay determined, stay unstoppable. + You are the author of your own story. + Strive for progress, not perfection. + Every day is a new beginning, make it a great one. + Inspire others by being your authentic self. + Your attitude determines your direction. + + + Is it time to flash another update already? + Make peace, not war + Oh hey, what\'s up? + Focus on your tasks + How many screenshots do you take? + Open goodness with DerpFest + Spread love, not havoc + We didn\'t start the fire! + Time for some good music + \u003C\u003C\u003C\u003C\u003C\u003C\u003C HEAD + You need DerpFest Premium to see this + Remember the Lineage of the Unicorn + Starting from the ground zero + We love you 3000 + Delicious even without Sushi + Do something nice today + This is best Pixel Experience, isn\'t it? + No illusions, welcome to reality! + Thank you for your support + Is your device derped? + One of the buildbot\'s best picks + Sanity for your Paranoia + Try Ice Cold desserts + What a lovely experience, isn\'t it? + DerpFest is a myth, right? + You are what you flash, don\'t be Potato + What\'s on your mind? + Expecto Patronum + Wubba Lubba Dub Dub + Winner Winner ....? + rm -rf \/ + Pringles aren\'t actually potato chips.. + Remember to check device\'s battery level! + Check your email/messages. + Check your to-do list if you have one. + You can disable quickspace via home settings. + #StayDerped \m/ + DerpFest FTW \m/ + Enjoying Android 14? + Hyped about Android 15? + Proudly presented without donations + Did you checkout all the tweaks? + You have tapped almost a 100k times. + Lots of options! + I am not your F1 button + Don\'t pass me! + Did you finish your homework? ツ + This is best ROM Experience, isn\'t it? + Well done! You have earned a bonus feature + That\'s enough, stop tapping me! + Only for pro users + Got an issue? Don\'t forget a logcat + + + + Weather update + Display current weather update + Requires weather service to be enabled + Cloudy + Rainy + Sunny + Stormy + Snowy + Windy + Misty + Current location + Display current weather location + Current condition + Display current weather condition summary + + Unable to find calendar or clock activity + + + Weather settings + Setup icon pack and weather service diff --git a/res/values/derp_styles.xml b/res/values/derp_styles.xml new file mode 100644 index 00000000000..5f9725bdbec --- /dev/null +++ b/res/values/derp_styles.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index f2dc189e774..32a218af51e 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -406,6 +406,8 @@ 0dp 0dp 0dp + 0dp + 0dp 0dp diff --git a/res/xml/default_workspace_4x4.xml b/res/xml/default_workspace_4x4.xml new file mode 100644 index 00000000000..7a743f398ad --- /dev/null +++ b/res/xml/default_workspace_4x4.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml index 9ec56fa63a8..ab9534ef3db 100644 --- a/res/xml/device_profiles.xml +++ b/res/xml/device_profiles.xml @@ -93,6 +93,70 @@ + + + + + + + + + + + + + + + android:defaultValue="91" /> diff --git a/res/xml/launcher_home_screen_preferences.xml b/res/xml/launcher_home_screen_preferences.xml index dc4ab92254e..a9e2aa22334 100644 --- a/res/xml/launcher_home_screen_preferences.xml +++ b/res/xml/launcher_home_screen_preferences.xml @@ -140,6 +140,82 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/xml/launcher_misc_preferences.xml b/res/xml/launcher_misc_preferences.xml index cbf0b438240..2f53750a333 100644 --- a/res/xml/launcher_misc_preferences.xml +++ b/res/xml/launcher_misc_preferences.xml @@ -46,7 +46,7 @@ android:title="@string/background_blur_title" android:summary="@string/background_blur_summary" android:persistent="true" - android:max="175" + android:max="225" android:min="0" settings:units="px" android:defaultValue="23" /> diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index c7cdfa8c693..9316b1473f6 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -50,7 +50,6 @@ import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pm.UserCache; -import com.android.launcher3.qsb.QsbContainerView; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.uioverrides.ApiWrapper; import com.android.launcher3.util.IntArray; @@ -128,7 +127,6 @@ public static AutoInstallsLayout get(Context context, LauncherWidgetHolder appWi private static final String TAG_AUTO_INSTALL = "autoinstall"; private static final String TAG_FOLDER = "folder"; private static final String TAG_APPWIDGET = "appwidget"; - protected static final String TAG_SEARCH_WIDGET = "searchwidget"; private static final String TAG_SHORTCUT = "shortcut"; private static final String TAG_EXTRA = "extra"; @@ -342,7 +340,6 @@ protected ArrayMap getLayoutElementsMap() { parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser()); parsers.put(TAG_FOLDER, new FolderParser()); parsers.put(TAG_APPWIDGET, new PendingWidgetParser()); - parsers.put(TAG_SEARCH_WIDGET, new SearchWidgetParser()); parsers.put(TAG_SHORTCUT, new ShortcutParser()); return parsers; } @@ -534,24 +531,6 @@ protected int verifyAndInsert(ComponentName cn, Bundle extras) { } } - protected class SearchWidgetParser extends PendingWidgetParser { - @Override - @Nullable - @WorkerThread - public ComponentName getComponentName(XmlPullParser parser) { - return QsbContainerView.getSearchComponentName(mContext); - } - - @Override - protected int verifyAndInsert(ComponentName cn, Bundle extras) { - mValues.put(Favorites.OPTIONS, LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET); - int flags = mValues.getAsInteger(Favorites.RESTORED) - | WorkspaceItemInfo.FLAG_RESTORE_STARTED; - mValues.put(Favorites.RESTORED, flags); - return super.verifyAndInsert(cn, extras); - } - } - protected class FolderParser implements TagParser { private final ArrayMap mFolderElements; diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java index c748693f9bd..1949c9a2c3b 100644 --- a/src/com/android/launcher3/DefaultLayoutParser.java +++ b/src/com/android/launcher3/DefaultLayoutParser.java @@ -70,7 +70,6 @@ protected ArrayMap getLayoutElementsMap() { ArrayMap parsers = new ArrayMap<>(); parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser()); parsers.put(TAG_APPWIDGET, new AppWidgetParser()); - parsers.put(TAG_SEARCH_WIDGET, new SearchWidgetParser()); parsers.put(TAG_SHORTCUT, new ShortcutParser()); parsers.put(TAG_RESOLVE, new ResolveParser()); parsers.put(TAG_FOLDER, new MyFolderParser()); diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 76e6b826146..e0819c28a46 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -243,31 +243,6 @@ public class InvariantDeviceProfile implements OnSharedPreferenceChangeListener private final ArrayList mChangeListeners = new ArrayList<>(); - private static final Uri ENABLE_TASKBAR_URI = Settings.System.getUriFor( - Settings.System.ENABLE_TASKBAR); - - private final class SettingsContentObserver extends ContentObserver { - SettingsContentObserver() { - super(new Handler(Looper.getMainLooper())); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - if (ENABLE_TASKBAR_URI.equals(uri)) { - // Create the illusion of this taking effect immediately - // Also needed because TaskbarManager inits before SystemUiProxy on start - boolean enabled = Settings.System.getInt(mContext.getContentResolver(), - Settings.System.ENABLE_TASKBAR, 0) == 1; - SystemUiProxy.INSTANCE.get(mContext).setTaskbarEnabled(enabled); - - // Restart launcher - System.exit(0); - } - } - } - - private final SettingsContentObserver mSettingsObserver = new SettingsContentObserver(); - @VisibleForTesting public InvariantDeviceProfile() { } @@ -293,10 +268,6 @@ private InvariantDeviceProfile(Context context) { onConfigChanged(displayContext); } }); - - final ContentResolver resolver = mContext.getContentResolver(); - resolver.registerContentObserver(ENABLE_TASKBAR_URI, false, - mSettingsObserver); } /** diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 7aa8c4a9b2a..8d3bffbb4ad 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -204,7 +204,7 @@ import com.android.launcher3.popup.ArrowPopup; import com.android.launcher3.popup.PopupDataProvider; import com.android.launcher3.popup.SystemShortcut; -import com.android.launcher3.qsb.QsbContainerView; +import com.android.launcher3.quickspace.QuickSpaceView; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.statemanager.StatefulActivity; @@ -410,6 +410,9 @@ public class Launcher extends StatefulActivity private final SettingsCache.OnChangeListener mNaturalScrollingChangedListener = enabled -> mIsNaturalScrollingEnabled = enabled; + // QuickSpace + private QuickSpaceView mQuickSpace; + public static Launcher getLauncher(Context context) { return fromContext(context); } @@ -1050,6 +1053,9 @@ protected void onStop() { } else { mOverlayManager.onActivityStopped(); } + if (mQuickSpace != null) { + mQuickSpace.onPause(); + } hideKeyboard(); logStopAndResume(false /* isResume */); mAppWidgetHolder.setActivityStarted(false); @@ -1238,6 +1244,10 @@ protected void onResume() { TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT); super.onResume(); + if (mQuickSpace != null) { + mQuickSpace.onResume(); + } + if (mDeferOverlayCallbacks) { scheduleDeferredCheck(); } else { @@ -1263,6 +1273,9 @@ protected void onPause() { if (!mDeferOverlayCallbacks) { mOverlayManager.onActivityPaused(); } + if (mQuickSpace != null) { + mQuickSpace.onPause(); + } mAppWidgetHolder.setActivityResumed(false); } @@ -1346,6 +1359,9 @@ protected void setupViews() { // Setup Scrim mScrimView = findViewById(R.id.scrim_view); + // QuickSpace + mQuickSpace = findViewById(R.id.reserved_container_workspace); + // Setup the drag controller (drop targets have to be added in reverse order in priority) mDropTargetBar.setup(mDragController); mAllAppsController.setupViews(mScrimView, mAppsView); @@ -1746,6 +1762,10 @@ public void onDestroy() { // changes while launcher is still loading. getRootView().getViewTreeObserver().removeOnPreDrawListener(mOnInitialBindListener); mOverlayManager.onActivityDestroyed(); + + if (mQuickSpace != null) { + mQuickSpace.onPause(); + } } public LauncherAccessibilityDelegate getAccessibilityDelegate() { @@ -2315,14 +2335,6 @@ public void bindAppWidget(LauncherAppWidgetInfo item) { } private View inflateAppWidget(LauncherAppWidgetInfo item) { - if (item.hasOptionFlag(LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET)) { - item.providerName = QsbContainerView.getSearchComponentName(this); - if (item.providerName == null) { - getModelWriter().deleteItemFromDatabase(item, - "search widget removed because search component cannot be found"); - return null; - } - } final AppWidgetHostView view; if (mIsSafeModeEnabled) { view = new PendingAppWidgetHostView(this, item, mIconCache, true); diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt index 51729992d45..ac6552df358 100644 --- a/src/com/android/launcher3/ModelCallbacks.kt +++ b/src/com/android/launcher3/ModelCallbacks.kt @@ -35,8 +35,7 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks { var synchronouslyBoundPages = LIntSet() var pagesToBindSynchronously = LIntSet() - private var isFirstPagePinnedItemEnabled = - (BuildConfig.QSB_ON_FIRST_SCREEN && !FeatureFlags.ENABLE_SMARTSPACE_REMOVAL.get()) + private var isFirstPagePinnedItemEnabled = FeatureFlags.USE_QUICKSPACE_VIEW var stringCache: StringCache? = null @@ -309,15 +308,14 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks { ) val firstScreenPosition = 0 if ( - (FeatureFlags.QSB_ON_FIRST_SCREEN && - isFirstPagePinnedItemEnabled && + (isFirstPagePinnedItemEnabled && !shouldShowFirstPageWidget()) && orderedScreenIds.indexOf(FIRST_SCREEN_ID) != firstScreenPosition ) { orderedScreenIds.removeValue(FIRST_SCREEN_ID) orderedScreenIds.add(firstScreenPosition, FIRST_SCREEN_ID) } else if ( - (!FeatureFlags.QSB_ON_FIRST_SCREEN && !isFirstPagePinnedItemEnabled || + (!isFirstPagePinnedItemEnabled || shouldShowFirstPageWidget()) && orderedScreenIds.isEmpty ) { // If there are no screens, we need to have an empty screen @@ -374,8 +372,7 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks { } orderedScreenIds .filterNot { screenId -> - FeatureFlags.QSB_ON_FIRST_SCREEN && - isFirstPagePinnedItemEnabled && + isFirstPagePinnedItemEnabled && !FeatureFlags.shouldShowFirstPageWidget() && screenId == WorkspaceLayoutManager.FIRST_SCREEN_ID } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index bbbe984c724..e65efac70d9 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -58,6 +58,8 @@ import android.graphics.drawable.InsetDrawable; import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.biometrics.BiometricPrompt; +import android.icu.text.DateFormat; +import android.icu.text.DisplayContext; import android.os.Build; import android.os.Build.VERSION_CODES; import android.os.CancellationSignal; @@ -69,6 +71,7 @@ import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; +import android.text.format.DateUtils; import android.text.style.TtsSpan; import android.util.DisplayMetrics; import android.util.Log; @@ -197,6 +200,14 @@ public final class Utilities { public static final String KEY_LENS = "pref_recents_lens"; public static final String KEY_SHORT_PARALLAX = "pref_short_parallax"; public static final String KEY_SINGLE_PAGE_CENTER = "pref_single_page_center"; + public static final String KEY_RECENTS_CHIPS = "pref_recents_chips"; + public static final String DESKTOP_SHOW_QUICKSPACE = "pref_show_quickspace"; + public static final String KEY_SHOW_ALT_QUICKSPACE = "pref_show_alt_quickspace"; + public static final String KEY_SHOW_QUICKSPACE_PSONALITY = "pref_quickspace_psonality"; + public static final String KEY_SHOW_QUICKSPACE_NOWPLAYING = "pref_quickspace_np"; + public static final String KEY_SHOW_QUICKSPACE_WEATHER = "pref_quickspace_weather"; + public static final String KEY_SHOW_QUICKSPACE_WEATHER_CITY = "pref_quickspace_weather_city"; + public static final String KEY_SHOW_QUICKSPACE_WEATHER_TEXT = "pref_quickspace_weather_text"; /** * Returns true if theme is dark. @@ -879,6 +890,19 @@ public static void translateOverlappingView( } } + public static String formatDateTime(Context context) { + String styleText; + DateFormat dateFormat; + if (useAlternativeQuickspaceUI(context)) { + styleText = context.getString(R.string.quickspace_date_format_minimalistic); + } else { + styleText = context.getString(R.string.quickspace_date_format); + } + dateFormat = DateFormat.getInstanceForSkeleton(styleText, Locale.getDefault()); + dateFormat.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE); + return dateFormat.format(System.currentTimeMillis()); + } + public static boolean isWorkspaceEditAllowed(Context context) { SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext()); return !prefs.getBoolean(InvariantDeviceProfile.KEY_WORKSPACE_LOCK, false); @@ -988,7 +1012,7 @@ public static int getRecentsOpacity(Context context) { public static int getAllAppsOpacity(Context context) { SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext()); - return prefs.getInt(KEY_APP_DRAWER_OPACITY, 80); + return prefs.getInt(KEY_APP_DRAWER_OPACITY, 91); } public static boolean isShowMeminfo(Context context) { @@ -1019,4 +1043,39 @@ public static boolean isSinglePageCentered(Context context) { public static boolean isDebugDevice() { return !Build.IS_USER; } + + public static boolean showQuickspace(Context context) { + SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext()); + return prefs.getBoolean(DESKTOP_SHOW_QUICKSPACE, true); + } + + public static boolean useAlternativeQuickspaceUI(Context context) { + SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext()); + return prefs.getBoolean(KEY_SHOW_ALT_QUICKSPACE, false); + } + + public static boolean isQuickspacePersonalityEnabled(Context context) { + SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext()); + return prefs.getBoolean(KEY_SHOW_QUICKSPACE_PSONALITY, true); + } + + public static boolean isQuickspaceNowPlaying(Context context) { + SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext()); + return prefs.getBoolean(KEY_SHOW_QUICKSPACE_NOWPLAYING, true); + } + + public static boolean isQuickspaceWeather(Context context) { + SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext()); + return prefs.getBoolean(KEY_SHOW_QUICKSPACE_WEATHER, true); + } + + public static boolean QuickSpaceShowCity(Context context) { + SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext()); + return prefs.getBoolean(KEY_SHOW_QUICKSPACE_WEATHER_CITY, false); + } + + public static boolean QuickSpaceShowWeatherText(Context context) { + SharedPreferences prefs = LauncherPrefs.getPrefs(context.getApplicationContext()); + return prefs.getBoolean(KEY_SHOW_QUICKSPACE_WEATHER_TEXT, true); + } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 3fbc584f084..2a08fd2b315 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -600,9 +600,10 @@ public void onViewAdded(View child) { * Initializes and binds the first page */ public void bindAndInitFirstWorkspaceScreen() { - if ((!FeatureFlags.QSB_ON_FIRST_SCREEN + if ((!Utilities.showQuickspace(getContext()) || !mLauncher.getIsFirstPagePinnedItemEnabled()) || shouldShowFirstPageWidget()) { + insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, getChildCount()); mFirstPagePinnedItem = null; return; } @@ -614,14 +615,14 @@ public void bindAndInitFirstWorkspaceScreen() { // As workspace does not touch the edges, we do not need a full // width first page pinned item. mFirstPagePinnedItem = LayoutInflater.from(getContext()) - .inflate(R.layout.search_container_workspace, firstPage, false); + .inflate(R.layout.reserved_container_workspace, firstPage, false); } int cellHSpan = mLauncher.getDeviceProfile().inv.numSearchContainerColumns; CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, cellHSpan, 1); lp.canReorder = false; if (!firstPage.addViewToCellLayout( - mFirstPagePinnedItem, 0, R.id.search_container_workspace, lp, true)) { + mFirstPagePinnedItem, 0, R.id.reserved_container_workspace, lp, true)) { Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout"); mFirstPagePinnedItem = null; } @@ -812,7 +813,7 @@ private void convertFinalScreenToEmptyScreenIfNecessary() { // We don't want to remove the first screen even if it's empty because that's where // first page pinned item would go if it gets turned back on. - if (ENABLE_SMARTSPACE_REMOVAL.get() && screenId == FIRST_SCREEN_ID) { + if (FeatureFlags.USE_QUICKSPACE_VIEW && screenId == FIRST_SCREEN_ID) { continue; } @@ -1029,7 +1030,7 @@ public void stripEmptyScreens() { int id = mWorkspaceScreens.keyAt(i); CellLayout cl = mWorkspaceScreens.valueAt(i); // FIRST_SCREEN_ID can never be removed. - if (((!FeatureFlags.QSB_ON_FIRST_SCREEN + if (((!FeatureFlags.USE_QUICKSPACE_VIEW || shouldShowFirstPageWidget()) || id > FIRST_SCREEN_ID) && cl.getShortcutsAndWidgets().getChildCount() == 0) { diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index f807bdf7b19..a9a21be0f8f 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -62,7 +62,7 @@ private FeatureFlags() { } * @deprecated Use {@link BuildConfig#QSB_ON_FIRST_SCREEN} directly */ @Deprecated - public static final boolean QSB_ON_FIRST_SCREEN = BuildConfig.QSB_ON_FIRST_SCREEN; + public static final boolean USE_QUICKSPACE_VIEW = true; /** * Feature flag to handle define config changes dynamically instead of killing the process. diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index 33304489196..126c04c91ce 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -527,14 +527,14 @@ private void populate(BgDataModel dataModel, } // Add first page QSB - if (FeatureFlags.QSB_ON_FIRST_SCREEN && dataModel.isFirstPagePinnedItemEnabled + if (FeatureFlags.USE_QUICKSPACE_VIEW && dataModel.isFirstPagePinnedItemEnabled && !shouldShowFirstPageWidget()) { CellLayout firstScreen = mWorkspaceScreens.get(FIRST_SCREEN_ID); View qsb = mHomeElementInflater.inflate(R.layout.qsb_preview, firstScreen, false); CellLayoutLayoutParams lp = new CellLayoutLayoutParams( 0, 0, firstScreen.getCountX(), 1); lp.canReorder = false; - firstScreen.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true); + firstScreen.addViewToCellLayout(qsb, 0, R.id.reserved_container_workspace, lp, true); } measureView(mRootView, mDp.widthPx, mDp.heightPx); diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java index d8388c27090..5fa2d57868f 100644 --- a/src/com/android/launcher3/logging/StatsLogManager.java +++ b/src/com/android/launcher3/logging/StatsLogManager.java @@ -281,6 +281,12 @@ public enum LauncherEvent implements EventEnum { @UiEvent(doc = "User long presses on the bottom bezel area.") LAUNCHER_LONG_PRESS_NAVBAR(1544), + @UiEvent(doc = "User deep presses on the stashed taskbar") + LAUNCHER_DEEP_PRESS_STASHED_TASKBAR(1602), + + @UiEvent(doc = "User long presses on the stashed taskbar") + LAUNCHER_LONG_PRESS_STASHED_TASKBAR(1592), + @UiEvent(doc = "User swipes or fling in UP direction from bottom bazel area.") LAUNCHER_HOME_GESTURE(574), diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java index 7f0f683091e..a30d30da763 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -17,7 +17,6 @@ import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY; -import static com.android.launcher3.BuildConfig.QSB_ON_FIRST_SCREEN; import static com.android.launcher3.config.FeatureFlags.ENABLE_SMARTSPACE_REMOVAL; import static com.android.launcher3.config.FeatureFlags.shouldShowFirstPageWidget; import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS; @@ -132,7 +131,7 @@ public class BgDataModel { * Load id for which the callbacks were successfully bound */ public int lastLoadId = -1; - public boolean isFirstPagePinnedItemEnabled = QSB_ON_FIRST_SCREEN + public boolean isFirstPagePinnedItemEnabled = FeatureFlags.USE_QUICKSPACE_VIEW && !ENABLE_SMARTSPACE_REMOVAL.get(); /** @@ -157,7 +156,7 @@ public synchronized IntArray collectWorkspaceScreens() { screenSet.add(item.screenId); } } - if ((FeatureFlags.QSB_ON_FIRST_SCREEN + if ((FeatureFlags.USE_QUICKSPACE_VIEW && !shouldShowFirstPageWidget()) || screenSet.isEmpty()) { screenSet.add(Workspace.FIRST_SCREEN_ID); diff --git a/src/com/android/launcher3/model/DatabaseHelper.java b/src/com/android/launcher3/model/DatabaseHelper.java index 13605101e75..de9eb5039b0 100644 --- a/src/com/android/launcher3/model/DatabaseHelper.java +++ b/src/com/android/launcher3/model/DatabaseHelper.java @@ -258,7 +258,7 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Favorites.SCREEN, IntArray.wrap(-777, -778)), null); } case 30: { - if (FeatureFlags.QSB_ON_FIRST_SCREEN + if (Utilities.showQuickspace(mContext) && !shouldShowFirstPageWidget()) { // Clean up first row in screen 0 as it might contain junk data. Log.d(TAG, "Cleaning up first row"); diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java index efd55745196..086c7664be2 100644 --- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java +++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java @@ -332,7 +332,7 @@ private static void solveGridPlacement(@NonNull final DatabaseHelper helper, final GridOccupancy occupied = new GridOccupancy(trgX, trgY); final Point trg = new Point(trgX, trgY); final Point next = new Point(0, screenId == 0 - && (FeatureFlags.QSB_ON_FIRST_SCREEN + && (Utilities.showQuickspace(destReader.mContext) && (!ENABLE_SMARTSPACE_REMOVAL.get() || LauncherPrefs.getPrefs(destReader.mContext) .getBoolean(SMARTSPACE_ON_HOME_SCREEN, true)) && !shouldShowFirstPageWidget()) diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java index 43700435694..5ffdb137385 100644 --- a/src/com/android/launcher3/model/LoaderCursor.java +++ b/src/com/android/launcher3/model/LoaderCursor.java @@ -529,7 +529,7 @@ protected boolean checkItemPlacement(ItemInfo item, boolean isFirstPagePinnedIte if (!mOccupied.containsKey(item.screenId)) { GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1); - if (item.screenId == Workspace.FIRST_SCREEN_ID && (FeatureFlags.QSB_ON_FIRST_SCREEN + if (item.screenId == Workspace.FIRST_SCREEN_ID && (Utilities.showQuickspace(mContext) && !shouldShowFirstPageWidget() && isFirstPagePinnedItemEnabled)) { // Mark the first X columns (X is width of the search container) in the first row as // occupied (if the feature is enabled) in order to account for the search diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 10ae171f9a3..5492bc40da0 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -383,9 +383,7 @@ protected void loadWorkspace( mModelDelegate.markActive(); logASplit("workspaceDelegateItems"); } - mBgDataModel.isFirstPagePinnedItemEnabled = FeatureFlags.QSB_ON_FIRST_SCREEN - && (!ENABLE_SMARTSPACE_REMOVAL.get() || LauncherPrefs.getPrefs( - mApp.getContext()).getBoolean(SMARTSPACE_ON_HOME_SCREEN, true)); + mBgDataModel.isFirstPagePinnedItemEnabled = FeatureFlags.USE_QUICKSPACE_VIEW; } private void loadWorkspaceImpl( @@ -770,15 +768,8 @@ private void processWorkspaceItem(LoaderCursor c, String savedProvider = c.getAppWidgetProvider(); final ComponentName component; - if ((c.getOptions() & LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET) != 0) { - component = QsbContainerView.getSearchComponentName(mApp.getContext()); - if (component == null) { - c.markDeleted("Discarding SearchWidget without packagename "); - return; - } - } else { - component = ComponentName.unflattenFromString(savedProvider); - } + component = ComponentName.unflattenFromString(savedProvider); + final boolean isIdValid = !c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); final boolean wasProviderReady = diff --git a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java index 929f698236e..01536c6219c 100644 --- a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java +++ b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java @@ -23,6 +23,7 @@ import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; +import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.util.GridOccupancy; @@ -67,7 +68,7 @@ public int[] findSpaceForItem(LauncherAppState app, BgDataModel dataModel, int screenCount = workspaceScreens.size(); // First check the preferred screen. IntSet screensToExclude = new IntSet(); - if (FeatureFlags.QSB_ON_FIRST_SCREEN + if (Utilities.showQuickspace(app.getContext()) && !shouldShowFirstPageWidget()) { screensToExclude.add(FIRST_SCREEN_ID); } diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index 6528e757bf6..34ec2ae506e 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -1,10 +1,14 @@ package com.android.launcher3.popup; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; + import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK; +import android.app.Activity; +import android.app.ActivityManagerNative; import android.app.ActivityOptions; import android.content.ComponentName; import android.content.Context; @@ -16,10 +20,12 @@ import android.net.Uri; import android.util.Log; import android.view.View; +import android.view.WindowInsets; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; +import android.window.SplashScreen; import android.os.UserHandle; import androidx.annotation.Nullable; @@ -37,6 +43,7 @@ import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.views.ActivityContext; import com.android.launcher3.widget.WidgetsBottomSheet; +import com.android.systemui.shared.system.ActivityManagerWrapper; import java.net.URISyntaxException; import java.util.List; @@ -313,6 +320,45 @@ public void onClick(View view) { } } + public static final Factory FREE_FORM = (activity, itemInfo, originalView) -> + ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity) + ? new FreeForm(activity, itemInfo, originalView) + : null; + + public static class FreeForm extends SystemShortcut { + private final String mPackageName; + + public FreeForm(BaseDraggingActivity target, ItemInfo itemInfo, View originalView) { + super(R.drawable.ic_caption_desktop_button_foreground, R.string.recent_task_option_freeform, target, itemInfo, originalView); + mPackageName = itemInfo.getTargetComponent().getPackageName(); + } + + @Override + public void onClick(View view) { + if (mPackageName != null) { + Intent intent = mTarget.getPackageManager().getLaunchIntentForPackage(mPackageName); + if (intent != null) { + ActivityOptions options = makeLaunchOptions(mTarget); + mTarget.startActivity(intent, options.toBundle()); + AbstractFloatingView.closeAllOpenViews(mTarget); + } + } + } + + private ActivityOptions makeLaunchOptions(Activity activity) { + ActivityOptions activityOptions = ActivityOptions.makeBasic(); + activityOptions.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); + final View decorView = activity.getWindow().getDecorView(); + final WindowInsets insets = decorView.getRootWindowInsets(); + final Rect r = new Rect(0, 0, decorView.getWidth() / 2, decorView.getHeight() / 2); + r.offsetTo(insets.getSystemWindowInsetLeft() + 50, insets.getSystemWindowInsetTop() + 50); + activityOptions.setLaunchBounds(r); + activityOptions.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON); + activityOptions.setTaskOverlay(true /* taskOverlay */, true /* canResume */); + return activityOptions; + } + } + public static void dismissTaskMenuView(T activity) { AbstractFloatingView.closeOpenViews(activity, true, AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE); diff --git a/src/com/android/launcher3/quickspace/QuickEventsController.java b/src/com/android/launcher3/quickspace/QuickEventsController.java new file mode 100644 index 00000000000..0d9cee9a093 --- /dev/null +++ b/src/com/android/launcher3/quickspace/QuickEventsController.java @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2018 CypherOS + * + * 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.quickspace; + +import android.app.PendingIntent; +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.Context; +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.SharedPreferences; +import android.media.MediaMetadata; +import android.media.session.MediaController; +import android.media.session.MediaSession; +import android.media.session.MediaSessionManager; +import android.net.Uri; +import android.provider.AlarmClock; +import android.provider.Settings; +import android.text.TextUtils; +import android.widget.Toast; +import android.view.View; +import android.view.View.OnClickListener; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherFiles; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; + +import java.util.Calendar; +import java.util.Random; + +import java.util.List; + +public class QuickEventsController { + + private static final String SETTING_DEVICE_INTRO_COMPLETED = "device_introduction_completed"; + private Context mContext; + + private String mEventTitle; + private String mEventTitleSub; + private String mGreetings; + private String mClockExt; + private OnClickListener mEventTitleSubAction = null; + private int mEventSubIcon = 0; + + private boolean mIsQuickEvent = false; + private boolean mRunning = true; + private boolean mRegistered = false; + + // Device Intro + private boolean mIsFirstTimeDone = false; + private SharedPreferences mPreferences; + + // PSA + Personality + private String[] mPSAMorningStr; + private String[] mPSAEvenStr; + private String[] mPSAAfterNoonStr; + private String[] mPSAMidniteStr; + private String[] mPSARandomStr; + private String[] mPSAEarlyEvenStr; + private String[] mWelcomeStr; + private BroadcastReceiver mPSAListener = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + psonalityEvent(); + } + }; + + // NowPlaying + private boolean mEventNowPlaying = false; + private String mNowPlayingTitle; + private String mNowPlayingArtist; + private boolean mClientLost = true; + private boolean mPlayingActive = false; + + public QuickEventsController(Context context) { + mContext = context; + initQuickEvents(); + } + + public void initQuickEvents() { + mPreferences = mContext.getSharedPreferences(LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE); + mIsFirstTimeDone = mPreferences.getBoolean(SETTING_DEVICE_INTRO_COMPLETED, false); + registerPSAListener(); + updateQuickEvents(); + } + + private void registerPSAListener() { + if (mRegistered) return; + mRegistered = true; + IntentFilter psonalityIntent = new IntentFilter(); + psonalityIntent.addAction(Intent.ACTION_TIME_TICK); + psonalityIntent.addAction(Intent.ACTION_TIME_CHANGED); + psonalityIntent.addAction(Intent.ACTION_TIMEZONE_CHANGED); + mContext.registerReceiver(mPSAListener, psonalityIntent, Context.RECEIVER_NOT_EXPORTED); + } + + private void unregisterPSAListener() { + if (!mRegistered) return; + mRegistered = false; + mContext.unregisterReceiver(mPSAListener); + } + + public void updateQuickEvents() { + deviceIntroEvent(); + nowPlayingEvent(); + initNowPlayingEvent(); + psonalityEvent(); + } + + private void deviceIntroEvent() { + if (!mRunning) return; + + if (mIsFirstTimeDone) return; + + mIsQuickEvent = true; + + if (Utilities.useAlternativeQuickspaceUI(mContext)) { + mEventTitle = mContext.getResources().getString(R.string.quick_event_rom_intro_welcome_ext); + } else { + mEventTitle = mContext.getResources().getString(R.string.quick_event_rom_intro_welcome); + } + mWelcomeStr = mContext.getResources().getStringArray(R.array.welcome_message_variants); + mEventTitleSub = mWelcomeStr[getLuckyNumber(0,mWelcomeStr.length - 1)]; + mEventSubIcon = R.drawable.ic_quickspace_derp; + mGreetings = mContext.getResources().getString(R.string.quickspace_grt_general); + mClockExt = mContext.getResources().getString(R.string.quickspace_ext_two); + + mEventTitleSubAction = new OnClickListener() { + @Override + public void onClick(View view) { + mContext.getSharedPreferences(LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE) + .edit() + .putBoolean(SETTING_DEVICE_INTRO_COMPLETED, true) + .commit(); + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_HOME); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + try { + Launcher.getLauncher(mContext).startActivitySafely(view, intent, null); + } catch (ActivityNotFoundException ex) { + } + mIsQuickEvent = false; + } + }; + } + + public void nowPlayingEvent() { + if (mEventNowPlaying) { + boolean infoExpired = !mPlayingActive || mClientLost; + if (infoExpired) { + mIsQuickEvent = false; + mEventNowPlaying = false; + } + } + } + + public void initNowPlayingEvent() { + if (!mRunning) return; + + if (!mIsFirstTimeDone) return; + + if (!Utilities.isQuickspaceNowPlaying(mContext)) return; + + if (!mPlayingActive) return; + + if (mNowPlayingTitle == null) return; + + mEventTitle = mNowPlayingTitle; + mGreetings = mContext.getResources().getString(R.string.qe_now_playing_ext_one); + mClockExt = ""; + if (mNowPlayingArtist == null ) { + mEventTitleSub = mContext.getResources().getString(R.string.qe_now_playing_unknown_artist); + } else { + mEventTitleSub = mNowPlayingArtist; + } + mEventSubIcon = R.drawable.ic_music_note_24dp; + mIsQuickEvent = true; + mEventNowPlaying = true; + + mEventTitleSubAction = new View.OnClickListener() { + @Override + public void onClick(View view) { + if (mPlayingActive) { + MediaSessionManager mediaSessionManager = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); + List sessions = mediaSessionManager.getActiveSessions(null); + + if (sessions != null && !sessions.isEmpty()) { + MediaController mediaController = sessions.get(0); + MediaSession.Token token = mediaController.getSessionToken(); + PendingIntent sessionActivity = mediaController.getSessionActivity(); + + if (sessionActivity != null) { + Intent intent = sessionActivity.getIntent(); + + if (intent != null) { + ComponentName componentName = intent.getComponent(); + if (componentName != null) { + String packageName = componentName.getPackageName(); + if (packageName != null) { + Intent launchIntent = mContext.getPackageManager().getLaunchIntentForPackage(packageName); + + if (launchIntent != null) { + launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + // try if package name launch intent works + mContext.startActivity(launchIntent); + return; // Exit the method after starting the activity + } catch (Exception e) {} + } + } + } + + try { + // try session activity + mContext.startActivity(intent); + return; // Exit the method after starting the activity + } catch (Exception e) {} + } + } + + // last resort: Work required for local media actions + Intent npIntent = new Intent(Intent.ACTION_MAIN); + npIntent.addCategory(Intent.CATEGORY_APP_MUSIC); + npIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + try { + Launcher.getLauncher(mContext).startActivitySafely(view, npIntent, null); + } catch (ActivityNotFoundException ex) {} + } + } + } + }; + } + + public void psonalityEvent() { + if (!mIsFirstTimeDone || mEventNowPlaying) return; + mEventSubIcon = 0; + + mEventTitle = Utilities.formatDateTime(mContext); + mPSAMorningStr = mContext.getResources().getStringArray(R.array.quickspace_psa_morning); + mPSAEvenStr = mContext.getResources().getStringArray(R.array.quickspace_psa_evening); + mPSAEarlyEvenStr = mContext.getResources().getStringArray(R.array.quickspace_psa_early_evening); + mPSAMidniteStr = mContext.getResources().getStringArray(R.array.quickspace_psa_midnight); + mPSAAfterNoonStr = mContext.getResources().getStringArray(R.array.quickspace_psa_noon); + mPSARandomStr = mContext.getResources().getStringArray(R.array.quickspace_psa_random); + int psaLength; + + mEventTitleSubAction = new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent calendarIntent = new Intent(Intent.ACTION_MAIN); + calendarIntent.addCategory(Intent.CATEGORY_APP_CALENDAR); + + Intent clockIntent = new Intent(AlarmClock.ACTION_SHOW_ALARMS); + + PackageManager packageManager = mContext.getPackageManager(); + List calendarApps = packageManager.queryIntentActivities(calendarIntent, PackageManager.MATCH_DEFAULT_ONLY); + List clockApps = packageManager.queryIntentActivities(clockIntent, PackageManager.MATCH_DEFAULT_ONLY); + + if (!calendarApps.isEmpty()) { + calendarIntent.setPackage(calendarApps.get(0).activityInfo.packageName); + try { + mContext.startActivity(calendarIntent); + } catch (ActivityNotFoundException e) { + } + } else if (!clockApps.isEmpty()) { + clockIntent.setPackage(clockApps.get(0).activityInfo.packageName); + try { + mContext.startActivity(clockIntent); + } catch (ActivityNotFoundException e) { + } + } else { + Toast.makeText(mContext, R.string.intent_no_app_clock_found, Toast.LENGTH_SHORT).show(); + } + } + }; + + int hourOfDay = Calendar.getInstance().get(Calendar.HOUR_OF_DAY); + + if (hourOfDay >= 5 && hourOfDay <= 9) { + mGreetings = mContext.getResources().getString(R.string.quickspace_grt_morning); + mClockExt = mContext.getResources().getString(R.string.quickspace_ext_one); + } else if (hourOfDay >= 12 && hourOfDay <= 15) { + mGreetings = mContext.getResources().getString(R.string.quickspace_grt_afternoon); + mClockExt = mContext.getResources().getString(R.string.quickspace_ext_two); + } else if (hourOfDay >= 16 && hourOfDay <= 20) { + mGreetings = mContext.getResources().getString(R.string.quickspace_grt_evening); + mClockExt = mContext.getResources().getString(R.string.quickspace_ext_two); + } else if (hourOfDay >= 21 && hourOfDay <= 23) { + mGreetings = mContext.getResources().getString(R.string.quickspace_grt_night); + mClockExt = mContext.getResources().getString(R.string.quickspace_ext_two); + } else if (hourOfDay >= 0 && hourOfDay <= 3) { + mGreetings = mContext.getResources().getString(R.string.quickspace_grt_night); + mClockExt = mContext.getResources().getString(R.string.quickspace_ext_two); + } else { + mGreetings = mContext.getResources().getString(R.string.quickspace_grt_general); + mClockExt = mContext.getResources().getString(R.string.quickspace_ext_two); + } + + if (Utilities.isQuickspacePersonalityEnabled(mContext)) { + if (getLuckyNumber(13) == 7) { + psaLength = mPSARandomStr.length - 1; + mEventTitleSub = mPSARandomStr[getLuckyNumber(0, psaLength)]; + mEventSubIcon = R.drawable.ic_quickspace_derp; + mIsQuickEvent = true; + } else { + switch (hourOfDay) { + case 5: case 6: case 7: case 8: case 9: + psaLength = mPSAMorningStr.length - 1; + mEventTitleSub = mPSAMorningStr[getLuckyNumber(0, psaLength)]; + mIsQuickEvent = true; + break; + + case 19: case 20: case 21: + psaLength = mPSAEvenStr.length - 1; + mEventTitleSub = mPSAEvenStr[getLuckyNumber(0, psaLength)]; + mIsQuickEvent = true; + break; + + case 16: case 17: case 18: + psaLength = mPSAEarlyEvenStr.length - 1; + mEventTitleSub = mPSAEarlyEvenStr[getLuckyNumber(0, psaLength)]; + mIsQuickEvent = true; + break; + + case 12: case 13: case 14: case 15: + psaLength = mPSAAfterNoonStr.length - 1; + mEventTitleSub = mPSAAfterNoonStr[getLuckyNumber(0, psaLength)]; + mIsQuickEvent = true; + break; + + case 0: case 1: case 2: case 3: + psaLength = mPSAMidniteStr.length - 1; + mEventTitleSub = mPSAMidniteStr[getLuckyNumber(0, psaLength)]; + mIsQuickEvent = true; + break; + + default: + mEventTitleSub = null; + mIsQuickEvent = false; + break; + } + } + } else { + mEventTitleSub = null; + mIsQuickEvent = false; + } + } + + public boolean isQuickEvent() { + return mIsQuickEvent; + } + + public boolean isDeviceIntroCompleted() { + return mIsFirstTimeDone; + } + + public String getTitle() { + return mEventTitle; + } + + public String getActionTitle() { + return mEventTitleSub; + } + + public String getClockExt() { + return mClockExt; + } + + public String getGreetings() { + return mGreetings; + } + + public OnClickListener getAction() { + return mEventTitleSubAction; + } + + public int getActionIcon() { + return mEventSubIcon; + } + + public int getLuckyNumber(int max) { + return getLuckyNumber(0, max); + } + + public int getLuckyNumber(int min, int max) { + Random r = new Random(); + return r.nextInt((max - min) + 1) + min; + } + + public void setMediaInfo(String title, String artist, boolean clientLost, boolean activePlayback) { + mNowPlayingTitle = title; + mNowPlayingArtist = artist; + mClientLost = clientLost; + mPlayingActive = activePlayback; + } + + public boolean isNowPlaying() { + return mPlayingActive && Utilities.isQuickspaceNowPlaying(mContext); + } + + public void onPause() { + mRunning = false; + unregisterPSAListener(); + } + + public void onResume() { + mRunning = true; + registerPSAListener(); + } +} diff --git a/src/com/android/launcher3/quickspace/QuickSpaceView.java b/src/com/android/launcher3/quickspace/QuickSpaceView.java new file mode 100644 index 00000000000..7902cd96db5 --- /dev/null +++ b/src/com/android/launcher3/quickspace/QuickSpaceView.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2018-2023 crDroid Android 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.quickspace; + +import android.animation.LayoutTransition; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.ColorStateList; +import android.text.TextUtils.TruncateAt; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.util.Themes; + +import com.android.launcher3.quickspace.QuickspaceController.OnDataListener; +import com.android.launcher3.quickspace.receivers.QuickSpaceActionReceiver; +import com.android.launcher3.quickspace.views.DateTextView; + +public class QuickSpaceView extends FrameLayout implements AnimatorUpdateListener, OnDataListener { + + private static final String TAG = "Launcher3:QuickSpaceView"; + private static final boolean DEBUG = false; + + public final ColorStateList mColorStateList; + public BubbleTextView mBubbleTextView; + public final int mQuickspaceBackgroundRes; + + public ViewGroup mQuickspaceContent; + public ImageView mEventSubIcon; + public TextView mEventTitleSub; + public TextView mEventTitleSubColored; + public TextView mGreetingsExt; + public TextView mGreetingsExtClock; + public ViewGroup mWeatherContentSub; + public ImageView mWeatherIconSub; + public TextView mWeatherTempSub; + public TextView mEventTitle; + + public boolean mIsQuickEvent; + public boolean mFinishedInflate; + public boolean mWeatherAvailable; + public boolean mAttached; + + private QuickSpaceActionReceiver mActionReceiver; + public QuickspaceController mController; + + public QuickSpaceView(Context context, AttributeSet set) { + super(context, set); + mActionReceiver = new QuickSpaceActionReceiver(context); + mController = new QuickspaceController(context); + mColorStateList = ColorStateList.valueOf(Themes.getAttrColor(getContext(), R.attr.workspaceTextColor)); + mQuickspaceBackgroundRes = R.drawable.bg_quickspace; + setClipChildren(false); + } + + @Override + public void onDataUpdated() { + boolean altUI = Utilities.useAlternativeQuickspaceUI(getContext()); + mController.getEventController().initQuickEvents(); + mIsQuickEvent = mController.isQuickEvent(); + if (mEventTitle == null || (altUI && mGreetingsExt == null)) { + prepareLayout(altUI); + } + mWeatherAvailable = mController.isWeatherAvailable() && + mController.getEventController().isDeviceIntroCompleted(); + loadDoubleLine(altUI); + } + + private final void loadDoubleLine(boolean useAlternativeQuickspaceUI) { + setBackgroundResource(mQuickspaceBackgroundRes); + mEventTitle.setText(mController.getEventController().getTitle()); + if (useAlternativeQuickspaceUI) { + if (!mController.getEventController().getGreetings().isEmpty()) { + mGreetingsExt.setVisibility(View.VISIBLE); + mGreetingsExt.setText(mController.getEventController().getGreetings()); + mGreetingsExt.setEllipsize(TruncateAt.END); + mGreetingsExt.setOnClickListener(mController.getEventController().getAction()); + } else { + mGreetingsExt.setVisibility(View.GONE); + } + if (!mController.getEventController().getClockExt().isEmpty()) { + mGreetingsExtClock.setVisibility(View.VISIBLE); + mGreetingsExtClock.setText(mController.getEventController().getClockExt()); + mGreetingsExtClock.setOnClickListener(mController.getEventController().getAction()); + } else { + mGreetingsExtClock.setVisibility(View.GONE); + } + } + if (mIsQuickEvent && (Utilities.isQuickspacePersonalityEnabled(getContext()) || + mController.getEventController().isNowPlaying())) { + mEventTitle.setEllipsize(TruncateAt.MARQUEE); + mEventTitle.setMarqueeRepeatLimit(3); + mEventTitle.setSelected(true); + mEventTitle.setOnClickListener(mController.getEventController().getAction()); + mEventTitleSub.setVisibility(View.VISIBLE); + mEventTitleSub.setText(mController.getEventController().getActionTitle()); + mEventTitleSub.setEllipsize(TruncateAt.MARQUEE); + mEventTitleSub.setMarqueeRepeatLimit(3); + mEventTitleSub.setSelected(true); + mEventTitleSub.setOnClickListener(mController.getEventController().getAction()); + if (useAlternativeQuickspaceUI) { + if (mController.getEventController().isNowPlaying()) { + mEventSubIcon.setVisibility(View.GONE); + mEventTitleSub.setOnClickListener(mController.getEventController().getAction()); + mEventTitleSubColored.setVisibility(View.VISIBLE); + mEventTitleSubColored.setText(getContext().getString(R.string.qe_now_playing_by)); + mEventTitleSubColored.setOnClickListener(mController.getEventController().getAction()); + } else { + setEventSubIcon(); + mEventTitleSubColored.setText(""); + mEventTitleSubColored.setVisibility(View.GONE); + } + } else { + setEventSubIcon(); + } + } else { + mEventTitleSub.setVisibility(View.GONE); + mEventSubIcon.setVisibility(View.GONE); + if (useAlternativeQuickspaceUI) { + mEventTitleSubColored.setVisibility(View.GONE); + } + } + bindWeather(mWeatherContentSub, mWeatherTempSub, mWeatherIconSub); + } + + private void setEventSubIcon() { + int icon = mController.getEventController().getActionIcon(); + if (icon > 0) { + mEventSubIcon.setVisibility(View.VISIBLE); + mEventSubIcon.setImageTintList(mColorStateList); + mEventSubIcon.setImageResource(mController.getEventController().getActionIcon()); + mEventSubIcon.setOnClickListener(mController.getEventController().getAction()); + } else { + mEventSubIcon.setVisibility(View.GONE); + } + } + + private final void bindWeather(View container, TextView title, ImageView icon) { + if (!mWeatherAvailable || mController.getEventController().isNowPlaying()) { + container.setVisibility(View.GONE); + return; + } + String weatherTemp = mController.getWeatherTemp(); + if (weatherTemp == null || weatherTemp.isEmpty()) { + container.setVisibility(View.GONE); + return; + } + boolean hasGoogleApp = isPackageEnabled("com.google.android.googlequicksearchbox", getContext()); + container.setVisibility(View.VISIBLE); + container.setOnClickListener(hasGoogleApp ? mActionReceiver.getWeatherAction() : null); + title.setText(weatherTemp); + icon.setImageDrawable(mController.getWeatherIcon()); + } + + private final void loadViews() { + mEventTitle = (TextView) findViewById(R.id.quick_event_title); + mEventTitleSub = (TextView) findViewById(R.id.quick_event_title_sub); + mEventTitleSubColored = (TextView) findViewById(R.id.quick_event_title_sub_colored); + mEventSubIcon = (ImageView) findViewById(R.id.quick_event_icon_sub); + mWeatherIconSub = (ImageView) findViewById(R.id.quick_event_weather_icon); + mQuickspaceContent = (ViewGroup) findViewById(R.id.quickspace_content); + mWeatherContentSub = (ViewGroup) findViewById(R.id.quick_event_weather_content); + mWeatherTempSub = (TextView) findViewById(R.id.quick_event_weather_temp); + if (Utilities.useAlternativeQuickspaceUI(getContext())) { + mGreetingsExtClock = (TextView) findViewById(R.id.extended_greetings_clock); + mGreetingsExt = (TextView) findViewById(R.id.extended_greetings); + } + } + + private void prepareLayout(boolean useAlternativeQuickspaceUI) { + int indexOfChild = indexOfChild(mQuickspaceContent); + removeView(mQuickspaceContent); + if (useAlternativeQuickspaceUI) { + addView(LayoutInflater.from(getContext()).inflate(R.layout.quickspace_alternate_double, this, false), indexOfChild); + } else { + addView(LayoutInflater.from(getContext()).inflate(R.layout.quickspace_doubleline, this, false), indexOfChild); + } + + loadViews(); + getQuickSpaceView(); + } + + private void getQuickSpaceView() { + if (mQuickspaceContent.getVisibility() != View.VISIBLE) { + mQuickspaceContent.setVisibility(View.VISIBLE); + mQuickspaceContent.setAlpha(0.0f); + mQuickspaceContent.animate().setDuration(200).alpha(1.0f); + } + } + + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + invalidate(); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + if (mAttached) + return; + + mAttached = true; + if (mController != null && mFinishedInflate) { + mController.addListener(this); + } + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (!mAttached) + return; + + mAttached = false; + if (mController != null) { + mController.removeListener(this); + } + } + + public boolean isPackageEnabled(String pkgName, Context context) { + try { + return context.getPackageManager().getApplicationInfo(pkgName, 0).enabled; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + @Override + public void onFinishInflate() { + super.onFinishInflate(); + loadViews(); + mFinishedInflate = true; + mBubbleTextView = findViewById(R.id.dummyBubbleTextView); + mBubbleTextView.setTag(new ItemInfo() { + @Override + public ComponentName getTargetComponent() { + return new ComponentName(getContext(), ""); + } + }); + mBubbleTextView.setContentDescription(""); + if (isAttachedToWindow()) { + if (mController != null) { + mController.addListener(this); + } + } + } + + @Override + public void onLayout(boolean b, int n, int n2, int n3, int n4) { + super.onLayout(b, n, n2, n3, n4); + } + + public void onPause() { + mController.onPause(); + } + + public void onResume() { + mController.onResume(); + } + + public void setPadding(int n, int n2, int n3, int n4) { + super.setPadding(0, 0, 0, 0); + } + +} diff --git a/src/com/android/launcher3/quickspace/QuickspaceController.java b/src/com/android/launcher3/quickspace/QuickspaceController.java new file mode 100644 index 00000000000..c7dae879b8f --- /dev/null +++ b/src/com/android/launcher3/quickspace/QuickspaceController.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2018 CypherOS + * + * 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.quickspace; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.media.AudioManager; +import android.media.MediaMetadataRetriever; +import android.media.RemoteControlClient; +import android.media.RemoteController; +import android.os.Handler; +import android.service.notification.StatusBarNotification; +import android.util.Log; + +import com.android.internal.util.derp.OmniJawsClient; + +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.notification.NotificationKeyData; +import com.android.launcher3.notification.NotificationListener; +import com.android.launcher3.util.PackageUserKey; + +import java.util.ArrayList; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.List; + +public class QuickspaceController implements NotificationListener.NotificationsChangedListener, OmniJawsClient.OmniJawsObserver { + + public final ArrayList mListeners = new ArrayList(); + private static final String SETTING_WEATHER_LOCKSCREEN_UNIT = "weather_lockscreen_unit"; + private static final boolean DEBUG = false; + private static final String TAG = "Launcher3:QuickspaceController"; + + private final Context mContext; + private final Handler mHandler; + private QuickEventsController mEventsController; + private OmniJawsClient mWeatherClient; + private OmniJawsClient.WeatherInfo mWeatherInfo; + private Drawable mConditionImage; + + private boolean mUseImperialUnit; + + private AudioManager mAudioManager; + private Metadata mMetadata = new Metadata(); + private RemoteController mRemoteController; + private boolean mClientLost = true; + private boolean mMediaActive = false; + private ExecutorService executorService = Executors.newSingleThreadExecutor(); + + public interface OnDataListener { + void onDataUpdated(); + } + + public QuickspaceController(Context context) { + mContext = context; + mHandler = new Handler(); + mEventsController = new QuickEventsController(context); + mWeatherClient = new OmniJawsClient(context); + mRemoteController = new RemoteController(context, mRCClientUpdateListener); + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + mAudioManager.registerRemoteController(mRemoteController); + } + + private void addWeatherProvider() { + if (!Utilities.isQuickspaceWeather(mContext)) return; + mWeatherClient.addObserver(this); + queryAndUpdateWeather(); + } + + public void addListener(OnDataListener listener) { + mListeners.add(listener); + addWeatherProvider(); + listener.onDataUpdated(); + } + + public void removeListener(OnDataListener listener) { + if (mWeatherClient != null) { + mWeatherClient.removeObserver(this); + } + mListeners.remove(listener); + } + + public boolean isQuickEvent() { + return mEventsController.isQuickEvent(); + } + + public QuickEventsController getEventController() { + return mEventsController; + } + + public boolean isWeatherAvailable() { + return mWeatherClient != null && mWeatherClient.isOmniJawsEnabled(); + } + + public Drawable getWeatherIcon() { + return mConditionImage; + } + + public String getWeatherTemp() { + boolean shouldShowCity = Utilities.QuickSpaceShowCity(mContext); + boolean showWeatherText = Utilities.QuickSpaceShowWeatherText(mContext); + if (mWeatherInfo != null) { + String formattedCondition = mWeatherInfo.condition; + if (formattedCondition.toLowerCase().contains("clouds")) { + formattedCondition = mContext.getResources().getString(R.string.quick_event_weather_clouds); + } else if (formattedCondition.toLowerCase().contains("rain")) { + formattedCondition = mContext.getResources().getString(R.string.quick_event_weather_rain); + } else if (formattedCondition.toLowerCase().contains("clear")) { + formattedCondition = mContext.getResources().getString(R.string.quick_event_weather_clear); + } else if (formattedCondition.toLowerCase().contains("storm")) { + formattedCondition = mContext.getResources().getString(R.string.quick_event_weather_storm); + } else if (formattedCondition.toLowerCase().contains("snow")) { + formattedCondition = mContext.getResources().getString(R.string.quick_event_weather_snow); + } else if (formattedCondition.toLowerCase().contains("wind")) { + formattedCondition = mContext.getResources().getString(R.string.quick_event_weather_wind); + } else if (formattedCondition.toLowerCase().contains("mist")) { + formattedCondition = mContext.getResources().getString(R.string.quick_event_weather_mist); + } + String weatherTemp = (shouldShowCity ? mWeatherInfo.city : "") + " " + mWeatherInfo.temp + + mWeatherInfo.tempUnits + (showWeatherText ? " · " + formattedCondition : ""); + return weatherTemp; + } + return null; + } + + private void playbackStateUpdate(int state) { + boolean active; + switch (state) { + case RemoteControlClient.PLAYSTATE_PLAYING: + active = true; + break; + case RemoteControlClient.PLAYSTATE_ERROR: + case RemoteControlClient.PLAYSTATE_PAUSED: + default: + active = false; + break; + } + if (active != mMediaActive) { + mMediaActive = active; + } + updateMediaInfo(); + } + + public void updateMediaInfo() { + if (mEventsController != null) { + mEventsController.setMediaInfo(mMetadata.trackTitle, mMetadata.trackArtist, mClientLost, mMediaActive); + mEventsController.updateQuickEvents(); + notifyListeners(); + } + } + + @Override + public void onNotificationPosted(PackageUserKey postedPackageUserKey, + NotificationKeyData notificationKey) { + updateMediaInfo(); + } + + @Override + public void onNotificationRemoved(PackageUserKey removedPackageUserKey, + NotificationKeyData notificationKey) { + updateMediaInfo(); + } + + @Override + public void onNotificationFullRefresh(List activeNotifications) { + updateMediaInfo(); + } + + public void onPause() { + if (mEventsController != null) mEventsController.onPause(); + } + + public void onResume() { + if (mEventsController != null) { + updateMediaInfo(); + mEventsController.onResume(); + notifyListeners(); + } + } + + @Override + public void weatherUpdated() { + queryAndUpdateWeather(); + } + + @Override + public void weatherError(int errorReason) { + Log.d(TAG, "weatherError " + errorReason); + if (errorReason == OmniJawsClient.EXTRA_ERROR_DISABLED) { + mWeatherInfo = null; + notifyListeners(); + } + } + + @Override + public void updateSettings() { + Log.i(TAG, "updateSettings"); + queryAndUpdateWeather(); + } + + private void queryAndUpdateWeather() { + executorService.execute(new Runnable() { + @Override + public void run() { + try { + mWeatherClient.queryWeather(); + mWeatherInfo = mWeatherClient.getWeatherInfo(); + if (mWeatherInfo != null) { + mConditionImage = mWeatherClient.getWeatherConditionImage(mWeatherInfo.conditionCode); + } + notifyListeners(); + } catch(Exception e) { + // Do nothing + } + } + }); + } + + public void notifyListeners() { + mHandler.post(new Runnable() { + @Override + public void run() { + for (OnDataListener list : mListeners) { + list.onDataUpdated(); + } + } + }); + } + + private RemoteController.OnClientUpdateListener mRCClientUpdateListener = + new RemoteController.OnClientUpdateListener() { + + @Override + public void onClientChange(boolean clearing) { + if (clearing) { + mMetadata.clear(); + mMediaActive = false; + mClientLost = true; + } + updateMediaInfo(); + } + + @Override + public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs, + long currentPosMs, float speed) { + mClientLost = false; + playbackStateUpdate(state); + } + + @Override + public void onClientPlaybackStateUpdate(int state) { + mClientLost = false; + playbackStateUpdate(state); + } + + @Override + public void onClientMetadataUpdate(RemoteController.MetadataEditor data) { + mMetadata.trackTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, + mMetadata.trackTitle); + mMetadata.trackArtist = data.getString(MediaMetadataRetriever.METADATA_KEY_ARTIST, + mMetadata.trackArtist); + mClientLost = false; + updateMediaInfo(); + } + + @Override + public void onClientTransportControlUpdate(int transportControlFlags) { + } + }; + + class Metadata { + private String trackTitle; + private String trackArtist; + + public void clear() { + trackTitle = null; + trackArtist = null; + } + } +} diff --git a/src/com/android/launcher3/quickspace/receivers/QuickSpaceActionReceiver.java b/src/com/android/launcher3/quickspace/receivers/QuickSpaceActionReceiver.java new file mode 100644 index 00000000000..e67645ae60b --- /dev/null +++ b/src/com/android/launcher3/quickspace/receivers/QuickSpaceActionReceiver.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018-2023 crDroid Android 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.quickspace.receivers; + +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.ContentUris; +import android.content.Context; +import android.content.Intent; +import android.content.pm.LauncherApps; +import android.net.Uri; +import android.os.Process; +import android.os.UserHandle; +import android.provider.CalendarContract; +import android.view.View; +import android.view.View.OnClickListener; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; + +public class QuickSpaceActionReceiver { + + private static Context mContext; + private final LauncherApps mLauncherApps; + + public OnClickListener mCalendarClickListener; + public OnClickListener mWeatherClickListener; + + public QuickSpaceActionReceiver(Context context) { + mContext = context; + mLauncherApps = context.getSystemService(LauncherApps.class); + + mCalendarClickListener = new OnClickListener() { + @Override + public void onClick(View view) { + openGoogleCalendar(view); + } + }; + + mWeatherClickListener = new OnClickListener() { + @Override + public void onClick(View view) { + openGoogleWeather(view); + } + }; + } + + private void openGoogleCalendar(View view) { + final Uri content_URI = CalendarContract.CONTENT_URI; + final Uri.Builder appendPath = content_URI.buildUpon().appendPath("time"); + ContentUris.appendId(appendPath, System.currentTimeMillis()); + final Intent addFlags = new Intent(Intent.ACTION_VIEW) + .setData(appendPath.build()) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + try { + Launcher.getLauncher(mContext).startActivitySafely(view, addFlags, null); + } catch (ActivityNotFoundException ex) { + mLauncherApps.startAppDetailsActivity(new ComponentName("com.google.android.googlequicksearchbox", ""), Process.myUserHandle(), null, null); + } + } + + private void openGoogleWeather(View view) { + Intent intent = new Intent("android.intent.action.VIEW"); + intent.setData(Uri.parse("dynact://velour/weather/ProxyActivity")); + intent.setComponent(new ComponentName("com.google.android.googlequicksearchbox", "com.google.android.apps.gsa.velour.DynamicActivityTrampoline")); + try { + Launcher.getLauncher(mContext).startActivitySafely(view, intent, null); + } catch (ActivityNotFoundException ex) { + mLauncherApps.startAppDetailsActivity(new ComponentName("com.google.android.googlequicksearchbox", + "com.google.android.apps.gsa.velour.DynamicActivityTrampoline"), Process.myUserHandle(), null, null); + } + } + + public OnClickListener getCalendarAction() { + return mCalendarClickListener; + } + + public OnClickListener getWeatherAction() { + return mWeatherClickListener; + } +} diff --git a/src/com/android/launcher3/quickspace/views/DateTextView.java b/src/com/android/launcher3/quickspace/views/DateTextView.java new file mode 100644 index 00000000000..015ce71459d --- /dev/null +++ b/src/com/android/launcher3/quickspace/views/DateTextView.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018-2023 crDroid Android 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.quickspace.views; + +import android.annotation.TargetApi; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.icu.text.DateFormat; +import android.icu.text.DisplayContext; +import android.text.format.DateUtils; +import android.util.AttributeSet; + +import com.android.launcher3.R; +import com.android.launcher3.Utilities; + +import java.util.Locale; + +public class DateTextView extends DoubleShadowTextView { + + private DateFormat mDateFormat; + private final BroadcastReceiver mTimeChangeReceiver; + private boolean mIsVisible = false; + + public DateTextView(final Context context) { + this(context, null); + } + + public DateTextView(final Context context, final AttributeSet set) { + super(context, set, 0); + mTimeChangeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + reloadDateFormat(!Intent.ACTION_TIME_TICK.equals(intent.getAction())); + } + }; + } + + public void reloadDateFormat(boolean forcedChange) { + String format; + if (mDateFormat == null || forcedChange) { + String styleText; + Context context = getContext(); + if (Utilities.useAlternativeQuickspaceUI(context)) { + styleText = context.getString(R.string.quickspace_date_format_minimalistic); + } else { + styleText = context.getString(R.string.quickspace_date_format); + } + mDateFormat = DateFormat.getInstanceForSkeleton(styleText, Locale.getDefault()); + mDateFormat.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE); + } + format = mDateFormat.format(System.currentTimeMillis()); + setText(format); + setContentDescription(format); + } + + private void registerReceiver() { + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_TIME_TICK); + intentFilter.addAction(Intent.ACTION_TIME_CHANGED); + intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + getContext().registerReceiver(mTimeChangeReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED); + } + + private void unregisterReceiver() { + getContext().unregisterReceiver(mTimeChangeReceiver); + } + + public void onVisibilityAggregated(boolean isVisible) { + super.onVisibilityAggregated(isVisible); + if (!mIsVisible && isVisible) { + mIsVisible = true; + registerReceiver(); + reloadDateFormat(true); + } else if (mIsVisible && !isVisible) { + unregisterReceiver(); + mIsVisible = false; + } + } +} diff --git a/src/com/android/launcher3/quickspace/views/DoubleShadowTextView.java b/src/com/android/launcher3/quickspace/views/DoubleShadowTextView.java new file mode 100644 index 00000000000..1bf019f0d6b --- /dev/null +++ b/src/com/android/launcher3/quickspace/views/DoubleShadowTextView.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018-2023 crDroid Android 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.quickspace.views; + +import android.content.Context; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.widget.TextView; + +import com.android.launcher3.views.DoubleShadowBubbleTextView; + +public class DoubleShadowTextView extends TextView { + + private final DoubleShadowBubbleTextView.ShadowInfo mShadowInfo; + + public DoubleShadowTextView(Context context) { + this(context, null); + } + + public DoubleShadowTextView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public DoubleShadowTextView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mShadowInfo = new DoubleShadowBubbleTextView.ShadowInfo(context, attrs, defStyleAttr); + setShadowLayer(Math.max(mShadowInfo.keyShadowBlur + mShadowInfo.keyShadowOffsetX, mShadowInfo.ambientShadowBlur), 0f, 0f, mShadowInfo.keyShadowColor); + } + + protected void onDraw(Canvas canvas) { + if (mShadowInfo.skipDoubleShadow(this)) { + super.onDraw(canvas); + return; + } + getPaint().setShadowLayer(mShadowInfo.ambientShadowBlur, 0.0f, 0.0f, mShadowInfo.ambientShadowColor); + super.onDraw(canvas); + getPaint().setShadowLayer(mShadowInfo.keyShadowBlur, 0.0f, mShadowInfo.keyShadowOffsetX, mShadowInfo.keyShadowColor); + super.onDraw(canvas); + } +} diff --git a/src/com/android/launcher3/settings/SettingsHomescreen.java b/src/com/android/launcher3/settings/SettingsHomescreen.java index dea4e35c105..83c44ee9a82 100644 --- a/src/com/android/launcher3/settings/SettingsHomescreen.java +++ b/src/com/android/launcher3/settings/SettingsHomescreen.java @@ -46,6 +46,8 @@ import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; +import com.android.internal.util.derp.OmniJawsClient; + public class SettingsHomescreen extends CollapsingToolbarBaseActivity implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback, SharedPreferences.OnSharedPreferenceChangeListener{ @@ -109,6 +111,13 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin case Utilities.KEY_HOTSEAT_OPACITY: case Utilities.KEY_SHORT_PARALLAX: case Utilities.KEY_SINGLE_PAGE_CENTER: + case Utilities.DESKTOP_SHOW_QUICKSPACE: + case Utilities.KEY_SHOW_ALT_QUICKSPACE: + case Utilities.KEY_SHOW_QUICKSPACE_NOWPLAYING: + case Utilities.KEY_SHOW_QUICKSPACE_WEATHER: + case Utilities.KEY_SHOW_QUICKSPACE_PSONALITY: + case Utilities.KEY_SHOW_QUICKSPACE_WEATHER_CITY: + case Utilities.KEY_SHOW_QUICKSPACE_WEATHER_TEXT: LauncherAppState.getInstanceNoCreate().setNeedsRestart(); break; default: @@ -168,6 +177,9 @@ public static class HomescreenSettingsFragment extends PreferenceFragmentCompat private Preference mShowGoogleAppPref; private Preference mShowGoogleBarPref; + private Preference mWeatherPref; + + private OmniJawsClient mWeatherClient; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { @@ -193,6 +205,13 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { mShowGoogleBarPref = screen.findPreference(Utilities.KEY_DOCK_SEARCH); updateIsGoogleAppEnabled(); + mWeatherClient = new OmniJawsClient(getContext()); + mWeatherPref = getPreferenceScreen().findPreference(Utilities.KEY_SHOW_QUICKSPACE_WEATHER); + if (!mWeatherClient.isOmniJawsEnabled()) { + mWeatherPref.setEnabled(false); + mWeatherPref.setSummary(R.string.quick_event_ambient_weather_enabled_info); + } + if (getActivity() != null && !TextUtils.isEmpty(getPreferenceScreen().getTitle())) { getActivity().setTitle(getPreferenceScreen().getTitle()); } diff --git a/src/com/android/launcher3/settings/SettingsRecents.java b/src/com/android/launcher3/settings/SettingsRecents.java index aca62037006..07fc11fb7d7 100644 --- a/src/com/android/launcher3/settings/SettingsRecents.java +++ b/src/com/android/launcher3/settings/SettingsRecents.java @@ -100,7 +100,15 @@ protected void onCreate(Bundle savedInstanceState) { } @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { } + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + switch (key) { + case Utilities.KEY_RECENTS_CHIPS: + LauncherAppState.getInstanceNoCreate().setNeedsRestart(); + break; + default: + break; + } + } private boolean startPreference(String fragment, Bundle args, String key) { if (Utilities.ATLEAST_P && getSupportFragmentManager().isStateSaved()) {