diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5cd6df6f05d..5c4613e2873 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -6,34 +6,35 @@
-First and foremost, welcome to the **Lawnchair** Contributing Guidelines!
+Welcome to the **Lawnchair** Project, we appreciate your interest in contributing.
+Feel free to reach out to us on [Telegram](https://t.me/lccommunity) or [Discord](https://discord.com/invite/3x8qNWxgGZ) for any further questions.
### 💫 Bug reports & feature requests
> [!TIP]
-> We recommend that the Lawnchair [Nightly](https://github.com/LawnchairLauncher/lawnchair/releases/tag/nightly)
-> build be used in your report as they contain the latest changes from Lawnchair.
+> Using Lawnchair [Nightly](https://github.com/LawnchairLauncher/lawnchair/releases/tag/nightly) is recommended for bug reports and feature requests
+> as it contains the latest changes from Lawnchair.
-For [bug reports](https://github.com/LawnchairLauncher/lawnchair/issues/new?assignees=&labels=bug&projects=&template=bug_report.yaml&title=%5BBUG%5D+), please describe the bug in detail to the best of your ability
+For [bug reports](https://github.com/LawnchairLauncher/lawnchair/issues/new?assignees=&labels=bug&projects=&template=bug_report.yaml&title=%5BBUG%5D+), describe the bug in detail to the best of your ability
with steps on how to reproduce it. If applicable, attach log files.
-For [feature requests](https://github.com/LawnchairLauncher/lawnchair/issues/new?assignees=&labels=feature%2Cenhancement&projects=&template=feature_request.yaml&title=%5BFEATURE%5D+), please describe the feature you'd like to see added to Lawnchair.
+For [feature requests](https://github.com/LawnchairLauncher/lawnchair/issues/new?assignees=&labels=feature%2Cenhancement&projects=&template=feature_request.yaml&title=%5BFEATURE%5D+), describe the feature you'd like to see added to Lawnchair.
If applicable, attach log files.
-Please make sure to be civil during the discussion of your contribution as outlined by the [Code of Conduct](CODE_OF_CONDUCT.md).
+Make sure to be civil during the discussion of your contribution as outlined by the [Code of Conduct](CODE_OF_CONDUCT.md).
### 🌐 Translation
For translation, visit [Lawnchair on Crowdin](https://lawnchair.crowdin.com) and follow these tips:
-- When using quotation marks, insert the symbols specific to the target language, as listed in this [this summary table](https://en.wikipedia.org/wiki/Quotation_mark#Summary_table);
+- When using quotation marks, insert the symbols specific to the target language, as listed in this [summary table](https://en.wikipedia.org/wiki/Quotation_mark#Summary_table);
- Some English terminology may have not have commonly-used equivalents in other languages. In such cases, use short descriptive phrases—for example, the equivalent of _bottom row_ for _dock_;
- Some languages (e.g. French) have variations of words depending on if it's masculine or feminine (gender-specific); we recommend opting for gender-neutral words instead.
### 🧑💻 Code
-> [!NOTE]
-> For Lawnchair 9 to 13, see the branches with the `9-` to `13-` prefixes respectively.
+> [!TIP]
+> For Lawnchair 9 to 14, see the branches with the `9-` to `14-` prefixes respectively.
For code, it's highly recommended that you use [Android Studio](https://developer.android.com/studio),
know [Java](https://www.java.com) or preferably [Kotlin](https://kotlinlang.org/), and [Git](https://git-scm.com/).
@@ -51,19 +52,20 @@ run `git submodule update --init --recursive`.
Here are some contribution tips to help you get started:
-- Always make sure that you're up-to-date with **Lawnchair** by setting your base branch to `14-dev`.
+- Always make sure that you're up-to-date with **Lawnchair** by setting your base branch to `15-dev`.
- Make sure your code is logical and well-formatted. If using Kotlin, see [“Coding conventions” in the Kotlin documentation](https://kotlinlang.org/docs/coding-conventions.html);
-- [The `lawnchair` package](https://github.com/LawnchairLauncher/lawnchair/tree/14-dev/lawnchair) houses Lawnchair’s own code, whereas [the `src` package](https://github.com/LawnchairLauncher/lawnchair/tree/14-dev/src) includes a clone of the Launcher3 codebase with modifications. Generally, place new files in the former, keeping changes to the latter to a minimum.
+- [The `lawnchair` package](https://github.com/LawnchairLauncher/lawnchair/tree/15-dev/lawnchair) houses Lawnchair’s own code, whereas [the `src` package](https://github.com/LawnchairLauncher/lawnchair/tree/15-dev/src) includes a clone of the Launcher3 codebase with modifications. Generally, place new files in the former, keeping changes to the latter to a minimum.
#### Additional documentation
-- The Lawnchair Wiki ([at Github](https://github.com/LawnchairLauncher/lawnchair/wiki))
-- Lawnchair Visual Guidelines ([README.md](/docs/assets/README.md))
-- Lawnchair Quickstep Compat Lib ([README.md](compatLib/README.md))
-- Lawnchair Preferences Components ([README.md](lawnchair/src/app/lawnchair/ui/preferences/components/README.md))
-- SystemUI ViewCapture ([README.md](systemUIViewCapture/README.md))
-- SystemUI Common ([README.md](systemUICommon/README.md))
-- Prebuilt Library ([README.md](prebuilts/libs/README.md))
+- [The Lawnchair Wiki](https://github.com/LawnchairLauncher/lawnchair/wiki)
+- [Lawnchair Visual Guidelines](/docs/assets/README.md)
+- [Lawnchair Quickstep Compat Library](compatLib/README.md)
+- [Lawnchair Preferences Components](lawnchair/src/app/lawnchair/ui/preferences/components/README.md)
+- [SystemUI Module](systemUI/README.md)
+ - [ViewCapture](systemUI/viewcapture/README.md)
+ - [Common](systemUI/common/README.md)
+- [Prebuilt Library](prebuilts/libs/README.md)
#### Versioning scheme
@@ -105,6 +107,7 @@ The table below shows release phase used by Lawnchair:
| Status | Stage |
| ----------------- | ----- |
+| Development | 00 |
| Alpha | 01 |
| Beta | 02 |
| Release Candidate | 03 |
@@ -125,3 +128,12 @@ Strings `names` in `strings.xml` should follow this format:
| Preference choice | $1_choice | `off_choice` | Off | |
| Feature string | (feature_name)_$1 | `colorpicker_hsb` | HSB | Feature strings are strings that are confined to a specific feature. Examples include the gesture and color picker. |
| Launcher string | $1_launcher | `device_contacts_launcher` | Contacts from device | Strings that are specific to the Launcher area |
+
+#### Updating locally stored font listing
+
+Lawnchair uses a locally stored JSON file (`google_fonts.json`) to list available fonts from Google Fonts. This file should be updated periodically or before release to include the latest fonts.
+
+To update Lawnchair's font listing, follow these steps:
+1. Acquire a [Google Fonts Developer API key](https://developers.google.com/fonts/docs/developer_api#APIKey).
+2. Download the JSON file from `https://www.googleapis.com/webfonts/v1/webfonts?key=API_KEY`, replacing `API_KEY` with the API key from step 1.
+3. Replace the content of [`google_fonts.json`](lawnchair/assets/google_fonts.json) with the API response.
diff --git a/SECURITY.md b/SECURITY.md
index 430694c29c8..0e9c46f1719 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -3,7 +3,7 @@
## Supported Versions
> [!WARNING]
-> Lawnchair v2 (Play Store version) is **unsupported**. Use the newer versions instead.
+> [Lawnchair Legacy](https://play.google.com/store/apps/details?id=ch.deletescape.lawnchair.plah) is **unsupported**. Use the newer versions instead.
> See [this FAQ page](https://lawnchair.app/faq#do-you-still-support-the-play-store-version) for additional information.
The latest version of Lawnchair is the only supported version.
@@ -11,12 +11,13 @@ The latest version of Lawnchair is the only supported version.
| Version | Supported |
| -------------- | ------------------ |
| Nightly build | :white_check_mark: |
-| 14 | :white_check_mark: |
+| 15 | :white_check_mark: |
+| 14 | :x: |
| 13 | :x: |
| 12.1 | :x: |
| 12 | :x: |
| 11 | :x: |
-| Older (legacy) | :x: |
+| Older (Legacy) | :x: |
## Reporting Security issues
@@ -24,7 +25,7 @@ We appreciate your efforts to responsibly disclose your findings and will make e
acknowledge your contributions.
To report an issue, please file a [security advisory](https://github.com/LawnchairLauncher/lawnchair/security/advisories/new)
-or contact a developer (can be found in the about page of the app) in Telegram or Discord and
-state your security vulnerability starting with the words "**SECURITY**".
+or contact a developer (can be found in the about page of the app) in [Telegram](https://t.me/lccommunity) or [Discord](https://discord.com/invite/3x8qNWxgGZ) and
+state your security vulnerability starting with the words "**SECURITY**"
-We'll endeavour to respond quickly, and will keep you updated throughout the process.
+We'll endeavor to respond quickly, and will keep you updated throughout the process.
diff --git a/build.gradle b/build.gradle
index e75fd115749..af48364c9e1 100644
--- a/build.gradle
+++ b/build.gradle
@@ -42,7 +42,7 @@ allprojects {
}
plugins.withId('com.google.protobuf') {
- def protocVersion = '4.29.1'
+ def protocVersion = '4.29.2'
protobuf {
// Configure the protoc executable
protoc {
@@ -78,7 +78,7 @@ allprojects {
ext {
FRAMEWORK_PREBUILTS_DIR = "$rootDir/prebuilts/libs"
- daggerVersion = '2.53.1'
+ daggerVersion = '2.54'
addFrameworkJar = { String name ->
def frameworkJar = new File(FRAMEWORK_PREBUILTS_DIR, name)
@@ -335,7 +335,7 @@ dependencies {
implementation fileTree(dir: FRAMEWORK_PREBUILTS_DIR, include: 'WindowManager-Shell-15.jar')
withQuickstepCompileOnly fileTree(dir: FRAMEWORK_PREBUILTS_DIR, include: 'framework-15.jar')
- coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.3'
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'
implementation 'androidx.profileinstaller:profileinstaller:1.4.1'
baselineProfile projects.baselineProfile
@@ -343,7 +343,7 @@ dependencies {
implementation "androidx.recyclerview:recyclerview:1.3.2"
implementation "androidx.preference:preference-ktx:1.2.1"
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.1'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3'
implementation 'com.github.ChickenHook:RestrictionBypass:2.2'
implementation 'dev.rikka.tools.refine:runtime:4.4.0'
@@ -394,7 +394,7 @@ dependencies {
// Persian Date
implementation 'com.github.samanzamani:PersianDate:1.7.1'
- implementation 'com.airbnb.android:lottie:6.6.1'
+ implementation 'com.airbnb.android:lottie:6.6.2'
// Compose drag and drop library
implementation 'sh.calvin.reorderable:reorderable:2.4.2'
@@ -404,7 +404,7 @@ dependencies {
exclude group: "com.github.skydoves", module: "balloon"
}
- implementation("com.github.android:renderscript-intrinsics-replacement-toolkit:b6363490c3")
+ implementation 'io.github.hokofly:hoko-blur:1.5.3'
}
ksp {
diff --git a/ci.py b/ci.py
index 8668b6775cb..8a2d6c19177 100644
--- a/ci.py
+++ b/ci.py
@@ -38,14 +38,34 @@ def send_document_to_telegram_chat(chat_id, document):
def send_artifact_to_telegram_chat(chat_id):
subdirectories = os.listdir(artifact_directory)
if "beta" in github_ref:
- with open(f"{artifact_directory}/{os.listdir(artifact_directory)[0]}", "rb") as artifact:
- send_document_to_telegram_chat(chat_id=chat_id, document=artifact)
- return
-
+ beta_path = f"{artifact_directory}/{os.listdir(artifact_directory)[0]}"
+ if os.path.exists(beta_path) and os.path.isfile(beta_path):
+ with open(beta_path, "rb") as artifact:
+ send_document_to_telegram_chat(chat_id=chat_id, document=artifact)
+ else:
+ print(f"Error: Beta artifact not found at {beta_path}")
+ return
+
for each_directory in subdirectories:
full_path = f"{artifact_directory}/{each_directory}/debug"
- with open(f"{full_path}/{os.listdir(full_path)[0]}", "rb") as artifact:
- send_document_to_telegram_chat(chat_id=chat_id, document=artifact)
+ if not os.path.exists(full_path) or not os.path.isdir(full_path):
+ print(f"Warning: Directory does not exist or is not valid: {full_path}")
+ continue
+
+ try:
+ files = os.listdir(full_path)
+ if not files:
+ print(f"Warning: No files found in {full_path}")
+ continue
+
+ file_path = f"{full_path}/{files[0]}"
+ if os.path.isfile(file_path):
+ with open(file_path, "rb") as artifact:
+ send_document_to_telegram_chat(chat_id=chat_id, document=artifact)
+ else:
+ print(f"Warning: Not a file: {file_path}")
+ except Exception as e:
+ print(f"Error processing directory {full_path}: {e}")
def send_internal_notifications():
repository = git.Repo(".")
diff --git a/compatLib/README.md b/compatLib/README.md
index d7e55b3a3b9..2698ccba017 100644
--- a/compatLib/README.md
+++ b/compatLib/README.md
@@ -12,3 +12,4 @@ refers to the compatibility code for that specific Android version.
| compatLibVS | 12 |
| compatLibVT | 13 |
| compatLibVU | 14 |
+| compatLibVV | 15 |
diff --git a/docs/assets/badge-github.png b/docs/assets/badge-github.png
new file mode 100644
index 00000000000..5129ff38b71
Binary files /dev/null and b/docs/assets/badge-github.png differ
diff --git a/docs/assets/badge-google-play.png b/docs/assets/badge-google-play.png
new file mode 100644
index 00000000000..1d17b6aa53a
Binary files /dev/null and b/docs/assets/badge-google-play.png differ
diff --git a/docs/assets/badge-izzyondroid.png b/docs/assets/badge-izzyondroid.png
new file mode 100644
index 00000000000..c97854de0b0
Binary files /dev/null and b/docs/assets/badge-izzyondroid.png differ
diff --git a/docs/assets/badge-obtainium.png b/docs/assets/badge-obtainium.png
new file mode 100644
index 00000000000..c5cee26fca8
Binary files /dev/null and b/docs/assets/badge-obtainium.png differ
diff --git a/docs/assets/device-frame.png b/docs/assets/device-frame.png
index ad5ec6dcea5..46a4d3fa126 100644
Binary files a/docs/assets/device-frame.png and b/docs/assets/device-frame.png differ
diff --git a/flags/src/com/android/launcher3/FeatureFlagsImpl.java b/flags/src/com/android/launcher3/FeatureFlagsImpl.java
index af84a73063a..2ce8b5ad2dc 100644
--- a/flags/src/com/android/launcher3/FeatureFlagsImpl.java
+++ b/flags/src/com/android/launcher3/FeatureFlagsImpl.java
@@ -107,7 +107,7 @@ private void load_overrides_launcher() {
enableSmartspaceAsAWidget =
properties.getBoolean(Flags.FLAG_ENABLE_SMARTSPACE_AS_A_WIDGET, false);
enableSmartspaceRemovalToggle =
- properties.getBoolean(Flags.FLAG_ENABLE_SMARTSPACE_REMOVAL_TOGGLE, false);
+ properties.getBoolean(Flags.FLAG_ENABLE_SMARTSPACE_REMOVAL_TOGGLE, true);
enableSupportForArchiving =
properties.getBoolean(Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING, true);
enableTabletTwoPanePickerV2 =
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index eb1a55be0e1..e1b837a19c2 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionSha256Sum=f397b287023acdba1e9f6fc5ea72d22dd63669d59ed4a289a29b1a76eee151c6
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
+distributionSha256Sum=7a00d51fb93147819aab76024feece20b6b84e420694101f276be952e08bef03
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index f5feea6d6b1..f3b75f3b0d4 100755
--- a/gradlew
+++ b/gradlew
@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
-' "$PWD" ) || exit
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
diff --git a/lawnchair/res/layout/search_container_all_apps.xml b/lawnchair/res/layout/search_container_all_apps.xml
index 9966792c6dc..b42573bd2e7 100644
--- a/lawnchair/res/layout/search_container_all_apps.xml
+++ b/lawnchair/res/layout/search_container_all_apps.xml
@@ -26,7 +26,7 @@
android:id="@+id/search_wrapper"
android:layout_width="match_parent"
android:layout_height="@dimen/search_box_height"
- android:layout_gravity="bottom"
+ android:layout_gravity="center"
android:background="@drawable/search_input_fg"
android:clickable="false"
android:focusable="true"
@@ -72,7 +72,7 @@
android:id="@+id/search_icon"
android:layout_width="@dimen/search_box_height"
android:layout_height="@dimen/search_box_height"
- android:layout_gravity="bottom|center|start"
+ android:layout_gravity="center|start"
android:background="@drawable/pill_ripple"
android:layout_marginEnd="6dp"
android:clickable="true"
@@ -84,30 +84,33 @@
diff --git a/lawnchair/res/values-el-rGR/strings.xml b/lawnchair/res/values-el-rGR/strings.xml
index 81a05f3e87d..ad10bd6cb0b 100644
--- a/lawnchair/res/values-el-rGR/strings.xml
+++ b/lawnchair/res/values-el-rGR/strings.xml
@@ -67,7 +67,7 @@
Αδιαφάνεια φόντου
Αντιγράφηκε στο πρόχειρο
- Result copied to clipboard
+ Το αποτέλεσμα αντιγράφηκε στο πρόχειρο
Το στοιχείο αφαιρέθηκε
Τι να εμφανίζεται
@@ -187,7 +187,7 @@
Πακέτο εικονιδίων
Πηγή θεματοποιημένων εικονιδίων
- Tint with accent color
+ Απόχρωση με χρώμα έμφασης
Εικονίδια συστήματος
Θεματοποιημένα εικονίδια
Ανενεργό
diff --git a/lawnchair/res/values-en-rCA/strings.xml b/lawnchair/res/values-en-rCA/strings.xml
index 1581d2877d7..41eac2e41f9 100644
--- a/lawnchair/res/values-en-rCA/strings.xml
+++ b/lawnchair/res/values-en-rCA/strings.xml
@@ -21,9 +21,9 @@
Actions and Verbs
-->
- Preview
+ Solanvip1
Create
- Backup
+
Restore
Delete
Reset
diff --git a/lawnchair/res/values-pl-rPL/strings.xml b/lawnchair/res/values-pl-rPL/strings.xml
index 946a040662b..4f8f6f7907b 100644
--- a/lawnchair/res/values-pl-rPL/strings.xml
+++ b/lawnchair/res/values-pl-rPL/strings.xml
@@ -67,7 +67,7 @@
Przezroczystość tła
Skopiowano do schowka
- Result copied to clipboard
+ Wynik został skopiowany do schowka
Element usunięty
Co wyświetlić
diff --git a/lawnchair/res/values-pt-rBR/strings.xml b/lawnchair/res/values-pt-rBR/strings.xml
index e5bb37058d1..fee8bfc6e4d 100644
--- a/lawnchair/res/values-pt-rBR/strings.xml
+++ b/lawnchair/res/values-pt-rBR/strings.xml
@@ -67,7 +67,7 @@
Opacidade do fundo
Copiado para a área de transferência
- Result copied to clipboard
+ Resultado copiado para a área de transferência
Item removido
O que mostrar
@@ -143,7 +143,7 @@
Usar fundo transparente nos ícones temáticos
Ícones adaptativos automáticos
Para todos os ícones não-adaptativos
- Show shadow behind icons
+ Mostrar sombra atrás dos ícones
Luminosidade do fundo
Use 100% de luz de fundo para fundo branco
Resetar ícones customizados
@@ -162,7 +162,7 @@
Ovo
iOS
Octógono
- Hexagon
+ Hexágono
Interface do Usuário
Quadrado Arredondado
Quadrado Afiado
@@ -187,7 +187,7 @@
Pacotes de ícones
Fonte dos Ícones Temáticos
- Tint with accent color
+ Colorir com a cor de destaque
Ícones do Sistema
Ícones Temáticos
Desligado
@@ -280,12 +280,12 @@
Estado da bateria
Tocando Agora
- Maximum number of targets
- Open Smartspacer settings
- Smartspacer settings
+ Número máximo de alvos
+ Abrir as configurações do Smartspacer
+ Configurações do Smartspacer
Toque para configurar
- To use %1$s, turn on Notification Dots.
+ Para usar %1$s, ative os pontos de notificação.
Mostrar na tela inicial
O widget Resumo pode ser adicionado manualmente à tela inicial ao colocar o widget do Lawnchair
@@ -312,7 +312,7 @@
Suporte e RP
Agradecimentos
Traduzir
- Donate
+ Doar
Rotação da tela inicial
Permitir a rotação da tela inicial quando o dispositivo for girado
- Blur wallpaper (experimental)
- Blur intensity
- Factor threshold
+ Desfocar papel de parede (experimental)
+ Intensidade do desfoque
+ Limite de fator
Adicionar novos aplicativos à tela inicial
Mostrar o feed
Nenhum aplicativo de feed instalado
@@ -401,8 +401,8 @@
Usar o estilo de pop-up animado e ligeiramente consolidado do Material You
Menu flutuante
Mostrar o botão de bloqueio
- Show system settings button
- Show edit home screen button
+ Mostrar botão de configurações do sistema
+ Mostrar botão de editar tela inicial
Barra de status
Mostrar barra de status
Barra de status escura
@@ -422,8 +422,8 @@
Desativado
Barra de Pesquisa do Google
Opacidade do fundo
- Outline width
- Outline color
+ Largura do contorno
+ Cor do contorno
Raio do canto
Aplicar cor de destaque
Provedor de pesquisa
@@ -457,8 +457,8 @@
- %1$d aplicativos
- Icon preview background opacity
- Folder background opacity
+ Opacidade do fundo da pré-visualização do ícone
+ Opacidade do fundo das pastas
Cor de fundo do ícone
Máximo de colunas nas pastas
Máximo de linhas nas pastas
@@ -528,12 +528,12 @@
Contatos e mais
Via %1$s
- Maximum number of apps
- Maximum number of people
- Maximum number of files
- Maximum number of settings
- Maximum items for search history
- Maximum number of suggestions
+ Número máximo de aplicativos
+ Número máximo de contatos
+ Número máximo de arquivos
+ Número máximo de configurações
+ Máximo de itens para o histórico de pesquisa
+ Número máximo de sugestões
Atraso máximo de sugestões da Web
Para pesquisar por contatos, permita as permissões de Contatos e Telefone ao Lawnchair
@@ -541,6 +541,6 @@
Permitir permissões
Provedor de sugestões da web
Mostrar o ícone do provedor de sugestões da web na barra de pesquisa
- Match dock search bar actions
- Clicking the dock search bar will now open the app drawer search UI
+ Combinar com as ações da barra de pesquisa no dock
+ Clicar na barra de pesquisa do dock agora irá abrir a IU de pesquisa da gaveta de aplicativos
diff --git a/lawnchair/res/values-zh-rTW/strings.xml b/lawnchair/res/values-zh-rTW/strings.xml
index b2c2c72c3a6..ea2da0c8526 100644
--- a/lawnchair/res/values-zh-rTW/strings.xml
+++ b/lawnchair/res/values-zh-rTW/strings.xml
@@ -67,13 +67,13 @@
背景不透明度
已複製至剪貼板
- Result copied to clipboard
+ 結果已複製至剪貼簿
已移除物件
選擇需要顯示的項目
To lock your phone when performing a gesture, and to open Recents via gesture, Lawnchair requires accessibility access.\n\nLawnchair doesn\'t watch any user action, though the privilege to do so is required for all accessibility services. Lawnchair discards any event sent by the system.\n\nIn order to lock your phone, or to open Recents, Lawnchair uses the performGlobalAction Accessibility service.
- %1$d x %2$d
+ %1$d × %2$d
%1$s & %2$s
To access shortcuts and additional features, set Lawnchair as your default launcher
- Notification dots
+ 通知圓點
顯示通知圖示數字
- Notification dot color
- Notification counter color
- Warning: Notification dot and counter colors don\'t have enough contrast with each other
- Warning: Notification dot and counter colors might not always have enough contrast with each other
+ 通知圓點顏色
+ 通知數量顏色
+ 警告:通知圓點與通知數量的顏色對比不夠明顯
+ 警告:通知圓點與通知數量的顏色對比在某些情況下可能不夠明顯
需要通知存取權
To show Notification Dots, turn on app notifications for %1$s
Transparent themed icons
在套用主題色的圖示上使用透明背景
@@ -162,9 +162,9 @@
蛋形
iOS
八角形
- Hexagon
+ 六邊形
One UI
- Rounded square
+ 圓角方形
Sharp square
方形
方圓形
diff --git a/lawnchair/res/values/config.xml b/lawnchair/res/values/config.xml
index 1649908d55a..de6f60229d5 100644
--- a/lawnchair/res/values/config.xml
+++ b/lawnchair/res/values/config.xml
@@ -102,6 +102,7 @@
false
true
false
+ false
true
true
false
diff --git a/lawnchair/res/values/strings.xml b/lawnchair/res/values/strings.xml
index 67ea81e33bc..5566eca6566 100644
--- a/lawnchair/res/values/strings.xml
+++ b/lawnchair/res/values/strings.xml
@@ -520,6 +520,8 @@
Status bar
Show status bar
Dark status bar
+ Status bar clock
+ Dynamically hide status bar clock in first screen
Text color
Light
diff --git a/lawnchair/src/app/lawnchair/FeedBridge.kt b/lawnchair/src/app/lawnchair/FeedBridge.kt
index c35bb5ed1b8..f86a75479c1 100644
--- a/lawnchair/src/app/lawnchair/FeedBridge.kt
+++ b/lawnchair/src/app/lawnchair/FeedBridge.kt
@@ -27,6 +27,7 @@ import android.util.Log
import app.lawnchair.preferences.PreferenceManager
import app.lawnchair.util.SingletonHolder
import app.lawnchair.util.ensureOnMainThread
+import app.lawnchair.util.getSignatureHash
import app.lawnchair.util.useApplicationContext
import com.android.launcher3.BuildConfig
import com.android.launcher3.R
@@ -153,20 +154,17 @@ class FeedBridge(private val context: Context) {
private const val TAG = "FeedBridge"
private const val OVERLAY_ACTION = "com.android.launcher3.WINDOW_OVERLAY"
- private val whitelist = mapOf(
- // HomeFeeder, t.me/homefeeder
- "ua.itaysonlab.homefeeder" to 0x887456ed,
- // Librechair, t.me/librechair
- "launcher.libre.dev" to 0x2e9dbab5,
- // Smartspacer
- SmartspacerConstants.SMARTSPACER_PACKAGE_NAME to 0x15c6e36f,
- // AIDL Bridge
- "amirz.aidlbridge" to 0xb662cc2f,
- // Google
- "com.google.android.googlequicksearchbox" to 0xe3ca78d8,
- // Pixel Bridge (or launcher)
- "com.google.android.apps.nexuslauncher" to 0xb662cc2f,
- )
+ private val whitelist = mutableMapOf()
+
+ fun initializeWhitelist(context: Context) {
+ whitelist["com.saulhdev.neofeed"] = getSignatureHash(context, "com.saulhdev.neofeed")
+ whitelist["ua.itaysonlab.homefeeder"] = 0x887456ed
+ whitelist["launcher.libre.dev"] = 0x2e9dbab5
+ whitelist[SmartspacerConstants.SMARTSPACER_PACKAGE_NAME] = 0x15c6e36f
+ whitelist["amirz.aidlbridge"] = 0xb662cc2f
+ whitelist["com.google.android.googlequicksearchbox"] = 0xe3ca78d8
+ whitelist["com.google.android.apps.nexuslauncher"] = 0xb662cc2f
+ }
fun getAvailableProviders(context: Context) = context.packageManager
.queryIntentServices(
@@ -181,4 +179,8 @@ class FeedBridge(private val context: Context) {
@JvmStatic
fun useBridge(context: Context) = getInstance(context).let { it.shouldUseFeed || it.customBridgeAvailable() }
}
+
+ init {
+ initializeWhitelist(context)
+ }
}
diff --git a/lawnchair/src/app/lawnchair/LawnchairLauncher.kt b/lawnchair/src/app/lawnchair/LawnchairLauncher.kt
index 59e1152e6d4..2ce11525afe 100644
--- a/lawnchair/src/app/lawnchair/LawnchairLauncher.kt
+++ b/lawnchair/src/app/lawnchair/LawnchairLauncher.kt
@@ -176,8 +176,17 @@ class LawnchairLauncher : QuickstepLauncher() {
}
}.launchIn(scope = lifecycleScope)
- launcher.stateManager.addStateListener(statusBarClockListener)
-
+ preferenceManager2.statusBarClock.get().onEach {
+ with(launcher.stateManager) {
+ if (it) {
+ addStateListener(statusBarClockListener)
+ } else {
+ removeStateListener(statusBarClockListener)
+ // Make sure status bar clock is restored when the preference is toggled off
+ LawnchairApp.instance.restoreClockInStatusBar()
+ }
+ }
+ }
preferenceManager2.rememberPosition.get().onEach {
with(launcher.stateManager) {
if (it) {
diff --git a/lawnchair/src/app/lawnchair/allapps/AllAppsSearchInput.kt b/lawnchair/src/app/lawnchair/allapps/AllAppsSearchInput.kt
index 1d7079d7e77..32030be816d 100644
--- a/lawnchair/src/app/lawnchair/allapps/AllAppsSearchInput.kt
+++ b/lawnchair/src/app/lawnchair/allapps/AllAppsSearchInput.kt
@@ -30,6 +30,7 @@ import app.lawnchair.preferences2.subscribeBlocking
import app.lawnchair.qsb.AssistantIconView
import app.lawnchair.qsb.LawnQsbLayout.Companion.getLensIntent
import app.lawnchair.qsb.LawnQsbLayout.Companion.getSearchProvider
+import app.lawnchair.qsb.ThemingMethod
import app.lawnchair.qsb.providers.Google
import app.lawnchair.qsb.providers.GoogleGo
import app.lawnchair.qsb.providers.PixelSearch
@@ -152,6 +153,13 @@ class AllAppsSearchInput(context: Context, attrs: AttributeSet?) :
themed = themed || iconRes == R.drawable.ic_qsb_search,
method = searchProvider.themingMethod,
)
+ } else {
+ // Always theme default search icon
+ setThemedIconResource(
+ resId = R.drawable.ic_qsb_search,
+ themed = true,
+ method = ThemingMethod.TINT,
+ )
}
setOnClickListener {
diff --git a/lawnchair/src/app/lawnchair/allapps/views/SearchResultIconRow.kt b/lawnchair/src/app/lawnchair/allapps/views/SearchResultIconRow.kt
index 55c70fa2178..0658adf6b36 100644
--- a/lawnchair/src/app/lawnchair/allapps/views/SearchResultIconRow.kt
+++ b/lawnchair/src/app/lawnchair/allapps/views/SearchResultIconRow.kt
@@ -18,7 +18,6 @@ import app.lawnchair.search.model.SearchResultActionCallBack
import app.lawnchair.util.copyToClipboard
import com.android.app.search.LayoutType
import com.android.launcher3.R
-import com.android.launcher3.touch.ItemClickHandler
import com.android.launcher3.views.BubbleTextHolder
class SearchResultIconRow(context: Context, attrs: AttributeSet?) :
@@ -69,11 +68,11 @@ class SearchResultIconRow(context: Context, attrs: AttributeSet?) :
}
}
- override val isQuickLaunch get() = icon.isQuickLaunch
- override val titleText get() = icon.titleText
+ override val isQuickLaunch get() = icon.isQuickLaunch || hasFlag(flags, SearchResultView.FLAG_QUICK_LAUNCH)
+ override val titleText get() = if (icon.titleText != "") icon.titleText else title.text
override fun launch(): Boolean {
- ItemClickHandler.INSTANCE.onClick(this)
+ performClick()
return true
}
diff --git a/lawnchair/src/app/lawnchair/allapps/views/SearchResultRightLeftIcon.kt b/lawnchair/src/app/lawnchair/allapps/views/SearchResultRightLeftIcon.kt
index ab30fd1ad29..ffdd2bb46a9 100644
--- a/lawnchair/src/app/lawnchair/allapps/views/SearchResultRightLeftIcon.kt
+++ b/lawnchair/src/app/lawnchair/allapps/views/SearchResultRightLeftIcon.kt
@@ -4,6 +4,7 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.util.AttributeSet
+import android.util.Log
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
@@ -34,6 +35,8 @@ class SearchResultRightLeftIcon(context: Context, attrs: AttributeSet?) :
private var defSmsAppInfo: AppInfo? = null
private var isSmall = false
+ private var flags = 0
+
override fun onFinishInflate() {
super.onFinishInflate()
isSmall = id == R.id.search_result_small_icon_row_left_right
@@ -79,11 +82,16 @@ class SearchResultRightLeftIcon(context: Context, attrs: AttributeSet?) :
this.layoutParams = layoutParams
}
- override val isQuickLaunch: Boolean get() = false
+ override val isQuickLaunch: Boolean get() = hasFlag(flags, SearchResultView.FLAG_QUICK_LAUNCH)
override val titleText: CharSequence? get() = title.text
- override fun launch(): Boolean = false
+ override fun launch(): Boolean {
+ val logTag = "Contact or files"
+ Log.d(logTag, "in launch")
+ Log.d(logTag, performClick().toString())
+ return true
+ }
override fun bind(
target: SearchTargetCompat,
diff --git a/lawnchair/src/app/lawnchair/preferences/PreferenceManager.kt b/lawnchair/src/app/lawnchair/preferences/PreferenceManager.kt
index c79636f0073..bd0ae9ec859 100644
--- a/lawnchair/src/app/lawnchair/preferences/PreferenceManager.kt
+++ b/lawnchair/src/app/lawnchair/preferences/PreferenceManager.kt
@@ -103,7 +103,7 @@ class PreferenceManager private constructor(private val context: Context) : Base
val hotseatQsbStrokeWidth = FloatPref("pref_searchStrokeWidth", 0F, recreate)
val enableWallpaperBlur = BoolPref("pref_enableWallpaperBlur", false, recreate)
val wallpaperBlur = IntPref("pref_wallpaperBlur", 25, recreate)
- val wallpaperBlurFactorThreshold = IntPref("pref_wallpaperBlurFactor", 25, recreate)
+ val wallpaperBlurFactorThreshold = FloatPref("pref_wallpaperBlurFactor", 3.0F, recreate)
val recentsActionScreenshot = BoolPref("pref_recentsActionScreenshot", !isOnePlusStock)
val recentsActionShare = BoolPref("pref_recentsActionShare", isOnePlusStock)
diff --git a/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt b/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt
index a3dc70b371c..3e45d322dd9 100644
--- a/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt
+++ b/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt
@@ -228,6 +228,12 @@ class PreferenceManager2 private constructor(private val context: Context) : Pre
defaultValue = context.resources.getBoolean(R.bool.config_default_show_status_bar),
)
+ val statusBarClock = preference(
+ key = booleanPreferencesKey(name = "status_bar_clock"),
+ defaultValue = context.resources.getBoolean(R.bool.config_default_dynamic_hide_status_bar_clock),
+ onSet = { reloadHelper.reloadGrid() },
+ )
+
val rememberPosition = preference(
key = booleanPreferencesKey(name = "all_apps_remember_position"),
defaultValue = context.resources.getBoolean(R.bool.config_default_remember_position),
diff --git a/lawnchair/src/app/lawnchair/search/adapter/SearchTargetFactory.kt b/lawnchair/src/app/lawnchair/search/adapter/SearchTargetFactory.kt
index 1bf993640b6..eeab662f543 100644
--- a/lawnchair/src/app/lawnchair/search/adapter/SearchTargetFactory.kt
+++ b/lawnchair/src/app/lawnchair/search/adapter/SearchTargetFactory.kt
@@ -273,7 +273,6 @@ class SearchTargetFactory(
LayoutType.PEOPLE_TILE,
SearchTargetCompat.RESULT_TYPE_CONTACT_TILE,
CONTACT,
- Bundle(),
)
}
@@ -307,7 +306,6 @@ class SearchTargetFactory(
LayoutType.THUMBNAIL,
SearchTargetCompat.RESULT_TYPE_FILE_TILE,
FILES,
- Bundle(),
)
}
diff --git a/lawnchair/src/app/lawnchair/search/algorithms/LawnchairLocalSearchAlgorithm.kt b/lawnchair/src/app/lawnchair/search/algorithms/LawnchairLocalSearchAlgorithm.kt
index ad7747778ee..ca49fa98dde 100644
--- a/lawnchair/src/app/lawnchair/search/algorithms/LawnchairLocalSearchAlgorithm.kt
+++ b/lawnchair/src/app/lawnchair/search/algorithms/LawnchairLocalSearchAlgorithm.kt
@@ -47,9 +47,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.async
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.channelFlow
-import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
@@ -124,15 +121,21 @@ class LawnchairLocalSearchAlgorithm(context: Context) : LawnchairSearchAlgorithm
}
override fun doSearch(query: String, callback: SearchCallback) {
- appState.model.enqueueModelUpdateTask(object : LauncherModel.ModelUpdateTask {
- override fun execute(app: ModelTaskController, dataModel: BgDataModel, apps: AllAppsList) {
- coroutineScope.launch(Dispatchers.Main) {
- getAllSearchResults(apps.data, query, prefs).collect { allResults ->
- callback.onSearchResult(query, ArrayList(allResults))
+ appState.model.enqueueModelUpdateTask(
+ object : LauncherModel.ModelUpdateTask {
+ override fun execute(
+ app: ModelTaskController,
+ dataModel: BgDataModel,
+ apps: AllAppsList,
+ ) {
+ coroutineScope.launch(Dispatchers.Main) {
+ getAllSearchResults(apps.data, query.trimEnd(), prefs).let { allResults ->
+ callback.onSearchResult(query, ArrayList(allResults))
+ }
}
}
- }
- })
+ },
+ )
}
override fun cancel(interruptActiveRequests: Boolean) {
@@ -141,67 +144,70 @@ class LawnchairLocalSearchAlgorithm(context: Context) : LawnchairSearchAlgorithm
}
}
- private fun getAllSearchResults(
+ private suspend fun getAllSearchResults(
apps: MutableList,
query: String,
prefs: PreferenceManager,
- ): Flow> = channelFlow {
- val allResults = mutableListOf()
-
- launch {
- if (searchApps) {
- getAppSearchResults(apps, query).collect { appResults ->
- allResults.addAll(appResults)
- send(allResults.toList())
- }
- }
- getLocalSearchResults(query, prefs).collect { localResults ->
- allResults.addAll(localResults)
- send(allResults.toList())
- }
- getSearchLinks(query).collect { otherResults ->
- allResults.addAll(otherResults)
- send(allResults.toList())
+ ): List {
+ val allResults = mutableListOf()
+
+ if (searchApps) {
+ getAppSearchResults(apps, query).let { appResults ->
+ allResults.addAll(appResults)
}
}
+ getLocalSearchResults(query, prefs).let { localResults ->
+ allResults.addAll(localResults)
+ }
+ getSearchLinks(query).let { otherResults ->
+ allResults.addAll(otherResults)
+ }
+
+ setFirstItemQuickLaunch(allResults)
+ val finalResults = transformSearchResults(allResults).toList()
+
+ return finalResults
}
private fun getAppSearchResults(
apps: MutableList,
query: String,
- ): Flow> = flow {
+ ): List {
val searchTargets = mutableListOf()
val appResults = performAppSearch(apps, query)
parseAppSearchResults(appResults, searchTargets)
- setFirstItemQuickLaunch(searchTargets)
- emit(transformSearchResults(searchTargets))
+ return searchTargets
}
- private fun getLocalSearchResults(
+ private suspend fun getLocalSearchResults(
query: String,
prefs: PreferenceManager,
- ): Flow> = flow {
+ ): List {
val searchTargets = mutableListOf()
val localSearchResults = performDeviceLocalSearch(query, prefs)
parseLocalSearchResults(localSearchResults, searchTargets)
- emit(transformSearchResults(searchTargets))
+ return searchTargets
}
private fun getSearchLinks(
query: String,
- ): Flow> = flow {
+ ): List {
val searchTargets = mutableListOf()
searchTargets.add(searchTargetFactory.createHeaderTarget(SPACE))
if (useWebSuggestions) {
- withContext(Dispatchers.IO) {
- searchTargets.add(searchTargetFactory.createWebSearchTarget(query, webSuggestionsProvider))
- }
+ searchTargets.add(
+ searchTargetFactory.createWebSearchTarget(
+ query,
+ webSuggestionsProvider,
+ ),
+ )
}
searchTargetFactory.createMarketSearchTarget(query)?.let { searchTargets.add(it) }
- emit(transformSearchResults(searchTargets))
+
+ return searchTargets
}
private fun parseAppSearchResults(
@@ -217,7 +223,14 @@ class LawnchairLocalSearchAlgorithm(context: Context) : LawnchairSearchAlgorithm
if (shortcuts != null) {
if (shortcuts.isNotEmpty()) {
searchTargets.add(searchTargetFactory.createHeaderTarget(SPACE))
- singleAppResult.let { searchTargets.add(searchTargetFactory.createAppSearchTarget(it, true)) }
+ singleAppResult.let {
+ searchTargets.add(
+ searchTargetFactory.createAppSearchTarget(
+ it,
+ true,
+ ),
+ )
+ }
searchTargets.addAll(shortcuts.map(searchTargetFactory::createShortcutTarget))
}
}
@@ -238,7 +251,10 @@ class LawnchairLocalSearchAlgorithm(context: Context) : LawnchairSearchAlgorithm
searchTargets.add(suggestionsHeader)
searchTargets.addAll(
suggestions.map {
- searchTargetFactory.createWebSuggestionsTarget(it.resultData as String, suggestionProvider)
+ searchTargetFactory.createWebSuggestionsTarget(
+ it.resultData as String,
+ suggestionProvider,
+ )
},
)
}
@@ -278,7 +294,14 @@ class LawnchairLocalSearchAlgorithm(context: Context) : LawnchairSearchAlgorithm
HEADER_JUSTIFY,
)
searchTargets.add(recentKeywordHeader)
- searchTargets.addAll(recentKeyword.map { searchTargetFactory.createSearchHistoryTarget(it.resultData as RecentKeyword, suggestionProvider) })
+ searchTargets.addAll(
+ recentKeyword.map {
+ searchTargetFactory.createSearchHistoryTarget(
+ it.resultData as RecentKeyword,
+ suggestionProvider,
+ )
+ },
+ )
}
val files = filterByType(localSearchResults, FILES)
@@ -299,7 +322,10 @@ class LawnchairLocalSearchAlgorithm(context: Context) : LawnchairSearchAlgorithm
SearchUtils.normalSearch(apps, query, maxAppResultsCount, hiddenApps, hiddenAppsInSearch)
}
- private suspend fun performDeviceLocalSearch(query: String, prefs: PreferenceManager): MutableList =
+ private suspend fun performDeviceLocalSearch(
+ query: String,
+ prefs: PreferenceManager,
+ ): MutableList =
withContext(Dispatchers.IO) {
val results = ArrayList()
@@ -349,12 +375,13 @@ class LawnchairLocalSearchAlgorithm(context: Context) : LawnchairSearchAlgorithm
val timeout = maxWebSuggestionDelay.toLong()
val result = withTimeoutOrNull(timeout) {
if (prefs.searchResultStartPageSuggestion.get()) {
- WebSearchProvider.fromString(webSuggestionsProvider).getSuggestions(query, maxWebSuggestionsCount).map {
- SearchResult(
- WEB_SUGGESTION,
- it,
- )
- }
+ WebSearchProvider.fromString(webSuggestionsProvider)
+ .getSuggestions(query, maxWebSuggestionsCount).map {
+ SearchResult(
+ WEB_SUGGESTION,
+ it,
+ )
+ }
} else {
emptyList()
}
diff --git a/lawnchair/src/app/lawnchair/search/algorithms/LawnchairSearchAlgorithm.kt b/lawnchair/src/app/lawnchair/search/algorithms/LawnchairSearchAlgorithm.kt
index d9c214ecfc5..9ce375e561d 100644
--- a/lawnchair/src/app/lawnchair/search/algorithms/LawnchairSearchAlgorithm.kt
+++ b/lawnchair/src/app/lawnchair/search/algorithms/LawnchairSearchAlgorithm.kt
@@ -115,7 +115,12 @@ sealed class LawnchairSearchAlgorithm(
protected fun setFirstItemQuickLaunch(searchTargets: List) {
val hasQuickLaunch = searchTargets.any { it.extras.getBoolean(EXTRA_QUICK_LAUNCH, false) }
if (!hasQuickLaunch) {
- searchTargets.firstOrNull()?.extras?.apply {
+ // check if we have a header or spacer item. if so, we skip as there isn't any relevant
+ // action to be applied
+ val target = searchTargets.getOrNull(
+ searchTargets.indexOfFirst { it.layoutType != TEXT_HEADER },
+ )
+ target?.extras?.apply {
putBoolean(EXTRA_QUICK_LAUNCH, true)
}
}
diff --git a/lawnchair/src/app/lawnchair/theme/color/tokens/ColorTokens.kt b/lawnchair/src/app/lawnchair/theme/color/tokens/ColorTokens.kt
index c0ce897a8fd..00d3e36b352 100644
--- a/lawnchair/src/app/lawnchair/theme/color/tokens/ColorTokens.kt
+++ b/lawnchair/src/app/lawnchair/theme/color/tokens/ColorTokens.kt
@@ -144,6 +144,10 @@ object ColorTokens {
@JvmField val SurfaceBrightColor = DayNightColorToken(Neutral2_600.setLStar(98.0), Neutral2_600.setLStar(24.0))
+ @JvmField val PrimaryButton = Accent1_600
+
+ @JvmField val WidgetAddButtonBackgroundColor = PrimaryButton
+
val SwitchThumbOn = Accent1_100
val SwitchThumbOff = DayNightColorToken(Neutral2_300, Neutral1_400)
val SwitchThumbDisabled = DayNightColorToken(Neutral2_100, Neutral1_700)
diff --git a/lawnchair/src/app/lawnchair/theme/drawable/DrawableTokens.kt b/lawnchair/src/app/lawnchair/theme/drawable/DrawableTokens.kt
index 473ce365d5e..743214fa906 100644
--- a/lawnchair/src/app/lawnchair/theme/drawable/DrawableTokens.kt
+++ b/lawnchair/src/app/lawnchair/theme/drawable/DrawableTokens.kt
@@ -3,6 +3,7 @@ package app.lawnchair.theme.drawable
import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.InsetDrawable
import android.graphics.drawable.LayerDrawable
import android.graphics.drawable.RippleDrawable
import android.graphics.drawable.StateListDrawable
@@ -186,4 +187,8 @@ object DrawableTokens {
@JvmField
val WorkCard = ResourceDrawableToken(R.drawable.work_card)
.setColor(ColorTokens.Surface)
+
+ @JvmField
+ val WidgetAddButtonBackground = ResourceDrawableToken(R.drawable.widget_cell_add_button_background)
+ .setTint(ColorTokens.WidgetAddButtonBackgroundColor)
}
diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/SearchSuggestionPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/SearchSuggestionPreference.kt
index 29417f4f941..b8b84d9a701 100644
--- a/lawnchair/src/app/lawnchair/ui/preferences/components/SearchSuggestionPreference.kt
+++ b/lawnchair/src/app/lawnchair/ui/preferences/components/SearchSuggestionPreference.kt
@@ -1,5 +1,6 @@
package app.lawnchair.ui.preferences.components
+import androidx.compose.animation.Crossfade
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
@@ -159,8 +160,29 @@ private fun BottomSheetContent(
) {
ModalBottomSheetContent(
buttons = {
- OutlinedButton(onClick = { onHide() }) {
- Text(text = stringResource(id = R.string.action_apply))
+ Crossfade(
+ !isPermissionGranted && (onRequestPermission != null && permissionRationale != null),
+ label = "transition",
+ ) {
+ if (it) {
+ Row {
+ OutlinedButton(onClick = { onHide() }) {
+ Text(text = stringResource(id = android.R.string.cancel))
+ }
+ Spacer(Modifier.width(8.dp))
+ Button(
+ onClick = {
+ onRequestPermission?.invoke()
+ },
+ ) {
+ Text(text = stringResource(id = R.string.grant_requested_permissions))
+ }
+ }
+ } else {
+ OutlinedButton(onClick = { onHide() }) {
+ Text(text = stringResource(id = R.string.action_apply))
+ }
+ }
}
},
) {
@@ -183,6 +205,9 @@ private fun BottomSheetContent(
}
if (!isPermissionGranted) {
if (onRequestPermission != null && permissionRationale != null) {
+ Spacer(
+ modifier = Modifier.height(8.dp),
+ )
Card(
modifier = Modifier
.fillMaxWidth()
@@ -195,17 +220,6 @@ private fun BottomSheetContent(
Text(
text = permissionRationale,
)
- Spacer(Modifier.height(8.dp))
- Row {
- Spacer(Modifier.weight(1f))
- Button(
- onClick = {
- onRequestPermission()
- },
- ) {
- Text(text = stringResource(id = R.string.grant_requested_permissions))
- }
- }
}
}
}
diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/ExperimentalFeaturesPreferences.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/ExperimentalFeaturesPreferences.kt
index 88157ef910d..0e09d5d790b 100644
--- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/ExperimentalFeaturesPreferences.kt
+++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/ExperimentalFeaturesPreferences.kt
@@ -66,9 +66,8 @@ fun ExperimentalFeaturesPreferences(
SliderPreference(
label = stringResource(id = R.string.wallpaper_background_blur_factor),
adapter = prefs.wallpaperBlurFactorThreshold.getAdapter(),
- step = 5,
- valueRange = 0..100,
- showUnit = "%",
+ step = 1F,
+ valueRange = 0F..10F,
)
}
}
diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenPreferences.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenPreferences.kt
index 9c7b3a68020..475c420f33f 100644
--- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenPreferences.kt
+++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenPreferences.kt
@@ -23,6 +23,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
+import app.lawnchair.LawnchairApp
import app.lawnchair.data.iconoverride.IconOverrideRepository
import app.lawnchair.nexuslauncher.OverlayCallbackImpl
import app.lawnchair.preferences.getAdapter
@@ -161,6 +162,13 @@ fun HomeScreenPreferences(
label = stringResource(id = R.string.dark_status_bar_label),
)
}
+ ExpandAndShrink(visible = showStatusBarAdapter.state.value && LawnchairApp.isRecentsEnabled) {
+ SwitchPreference(
+ adapter = prefs2.statusBarClock.getAdapter(),
+ label = stringResource(id = R.string.status_bar_clock_label),
+ description = stringResource(id = R.string.status_bar_clock_description),
+ )
+ }
}
PreferenceGroup(heading = stringResource(id = R.string.icons)) {
SliderPreference(
diff --git a/lawnchair/src/app/lawnchair/util/LawnchairUtils.kt b/lawnchair/src/app/lawnchair/util/LawnchairUtils.kt
index f19a03ca381..8cde0de970c 100644
--- a/lawnchair/src/app/lawnchair/util/LawnchairUtils.kt
+++ b/lawnchair/src/app/lawnchair/util/LawnchairUtils.kt
@@ -35,9 +35,9 @@ import android.graphics.RectF
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
+import android.os.Build
import android.os.Looper
import android.provider.OpenableColumns
-import android.util.Log
import android.util.Size
import android.view.View
import android.widget.TextView
@@ -51,7 +51,6 @@ import com.android.launcher3.Utilities
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.util.Themes
import com.android.systemui.shared.system.QuickStepContract
-import com.google.android.renderscript.Toolkit
import com.patrykmichalik.opto.core.firstBlocking
import java.util.concurrent.Callable
import java.util.concurrent.ExecutionException
@@ -286,12 +285,21 @@ fun createRoundedBitmap(color: Int, cornerRadius: Float): Bitmap {
return bitmap
}
-fun blurBitmap(source: Bitmap, percent: Int, factorThreshold: Int = 25): Bitmap {
- try {
- val factor = percent.toFloat().div(100f) * factorThreshold
- return Toolkit.blur(source, factor.toInt())
- } catch (e: Exception) {
- Log.e("LawnchairUtil", "Error bluring bitmap: $e")
- return source
+fun getSignatureHash(context: Context, packageName: String): Long? {
+ return try {
+ val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ context.packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES)
+ } else {
+ context.packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
+ }
+
+ val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ packageInfo.signingInfo?.apkContentsSigners
+ } else {
+ packageInfo.signatures
+ }
+ signatures?.firstOrNull()?.hashCode()?.toLong()
+ } catch (_: PackageManager.NameNotFoundException) {
+ null
}
}
diff --git a/platform_frameworks_libs_systemui b/platform_frameworks_libs_systemui
index c343e8f06f3..039cb6fcff1 160000
--- a/platform_frameworks_libs_systemui
+++ b/platform_frameworks_libs_systemui
@@ -1 +1 @@
-Subproject commit c343e8f06f3f82d67670fe9531bcd5998d70d67a
+Subproject commit 039cb6fcff196e7e0101b1081ce2d546af7fb918
diff --git a/proguard.pro b/proguard.pro
index dd472614af0..92c14595989 100644
--- a/proguard.pro
+++ b/proguard.pro
@@ -21,6 +21,16 @@
-dontwarn dalvik.system.CloseGuard
-dontwarn lineageos.providers.LineageSettings$System
-dontwarn androidx.compose.runtime.PrimitiveSnapshotStateKt
+-dontwarn androidx.renderscript.Allocation
+-dontwarn androidx.renderscript.BaseObj
+-dontwarn androidx.renderscript.Element
+-dontwarn androidx.renderscript.FieldPacker
+-dontwarn androidx.renderscript.RSRuntimeException
+-dontwarn androidx.renderscript.RenderScript
+-dontwarn androidx.renderscript.Script$LaunchOptions
+-dontwarn androidx.renderscript.ScriptC
+-dontwarn androidx.renderscript.ScriptIntrinsicBlur
+-dontwarn androidx.renderscript.Type
# Common rules.
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 9ab9ea2c3d7..d31c8b7ac49 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -55,7 +55,6 @@
import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAUNCH;
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
-import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.testing.shared.TestProtocol.WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE;
import static com.android.launcher3.util.DisplayController.isTransientTaskbar;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -139,6 +138,7 @@
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.StableViewInfo;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.FloatingIconView;
import com.android.launcher3.views.ScrimView;
@@ -1360,16 +1360,8 @@ private static int getRotationChange(RemoteAnimationTarget[] appTargets) {
? new ArrayList<>()
: runningTaskTarget.taskInfo.launchCookies;
- int launchCookieItemId = NO_MATCHING_ID;
- for (IBinder cookie : launchCookies) {
- Integer itemId = ObjectWrapper.unwrap(cookie);
- if (itemId != null) {
- launchCookieItemId = itemId;
- break;
- }
- }
-
- return mLauncher.getFirstMatchForAppClose(launchCookieItemId, packageName,
+ return mLauncher.getFirstMatchForAppClose(
+ StableViewInfo.fromLaunchCookies(launchCookies), packageName,
UserHandle.of(runningTaskTarget.taskInfo.userId), true /* supportsAllAppsState */);
}
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index 943c08c7864..a9f7c2a4410 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -143,10 +143,12 @@ private void parseIntentExtras() {
if (uiSurfaceParam != null && UI_SURFACE_PATTERN.matcher(uiSurfaceParam).matches()) {
mUiSurface = uiSurfaceParam;
}
- ArrayList addedWidgets = getIntent().getParcelableArrayListExtra(
- EXTRA_ADDED_APP_WIDGETS, AppWidgetProviderInfo.class);
- if (addedWidgets != null) {
- mAddedWidgets = addedWidgets;
+ if (Utilities.ATLEAST_T) {
+ ArrayList addedWidgets = getIntent().getParcelableArrayListExtra(
+ EXTRA_ADDED_APP_WIDGETS, AppWidgetProviderInfo.class);
+ if (addedWidgets != null) {
+ mAddedWidgets = addedWidgets;
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index d332e5e62cd..4e8034da631 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -43,6 +43,8 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
import static com.android.wm.shell.Flags.enableTinyTaskbar;
+import static java.util.stream.Collectors.toList;
+
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.app.ActivityOptions;
@@ -1258,7 +1260,7 @@ private void launchFromOverviewTaskbar(@Nullable RecentsView recents,
boolean isLaunchingAppPair = itemInfos.size() == 2;
// Convert the list of ItemInfo instances to a list of ComponentKeys
- List componentKeys = itemInfos.stream().map(ItemInfo::getComponentKey).toList();
+ List componentKeys = itemInfos.stream().map(ItemInfo::getComponentKey).collect(toList());
recents.getSplitSelectController().findLastActiveTasksAndRunCallback(
componentKeys,
isLaunchingAppPair,
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 707ab8f1d60..87b99ff955d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -31,6 +31,8 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
+import static java.util.stream.Collectors.toList;
+
import android.annotation.BinderThread;
import android.annotation.Nullable;
import android.app.Notification;
@@ -398,7 +400,7 @@ private void applyViewChanges(BubbleBarViewUpdate update) {
if (update.bubbleKeysInOrder != null && !update.bubbleKeysInOrder.isEmpty()) {
// Create the new list
List newOrder = update.bubbleKeysInOrder.stream()
- .map(mBubbles::get).filter(Objects::nonNull).toList();
+ .map(mBubbles::get).filter(Objects::nonNull).collect(toList());
if (!newOrder.isEmpty()) {
mBubbleBarViewController.reorderBubbles(newOrder);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 393f58a043b..8e25c5c5d14 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -18,6 +18,8 @@
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
+import static java.util.stream.Collectors.toList;
+
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
@@ -463,7 +465,7 @@ public void animateBubbleNotification(BubbleBarBubble bubble, boolean isExpandin
*/
public void reorderBubbles(List newOrder) {
List viewList = newOrder.stream().filter(Objects::nonNull)
- .map(BubbleBarBubble::getView).toList();
+ .map(BubbleBarBubble::getView).collect(toList());
mBarView.reorder(viewList);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index 63d2ff24d36..b46bfc74185 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -57,7 +57,7 @@ public QuickstepInteractionHandler(QuickstepLauncher launcher) {
@SuppressWarnings("NewApi")
@Override
public boolean onInteraction(View view, PendingIntent pendingIntent,
- RemoteViews.RemoteResponse remoteResponse) {
+ RemoteViews.RemoteResponse remoteResponse) {
LauncherAppWidgetHostView hostView = findHostViewAncestor(view);
if (hostView == null) {
Log.e(TAG, "View did not have a LauncherAppWidgetHostView ancestor.");
@@ -74,15 +74,8 @@ public boolean onInteraction(View view, PendingIntent pendingIntent,
Pair options = remoteResponse.getLaunchOptions(view);
ActivityOptionsWrapper activityOptions = mLauncher.getAppTransitionManager()
.getActivityLaunchOptions(hostView);
- Object itemInfo = hostView.getTag();
- IBinder launchCookie = null;
- if (itemInfo instanceof ItemInfo) {
- launchCookie = mLauncher.getLaunchCookie((ItemInfo) itemInfo);
- activityOptions.options.setLaunchCookie(launchCookie);
- }
- if (Utilities.ATLEAST_S && !pendingIntent.isActivity() && LawnchairApp.isRecentsEnabled()) {
- // In the event this pending intent eventually launches an activity, i.e. a
- // trampoline,
+ if (!pendingIntent.isActivity()) {
+ // In the event this pending intent eventually launches an activity, i.e. a trampoline,
// use the Quickstep transition animation.
try {
IActivityTaskManagerHidden atm = Refine.unsafeCast(ActivityTaskManager.getService());
@@ -90,7 +83,7 @@ public boolean onInteraction(View view, PendingIntent pendingIntent,
atm.registerRemoteAnimationForNextActivityStart(
pendingIntent.getCreatorPackage(),
activityOptions.options.getRemoteAnimationAdapter(),
- launchCookie);
+ activityOptions.options.getLaunchCookie());
} catch (NoSuchMethodError e) {
atm.registerRemoteAnimationForNextActivityStart(
pendingIntent.getCreatorPackage(),
@@ -99,16 +92,18 @@ public boolean onInteraction(View view, PendingIntent pendingIntent,
} catch (RemoteException e) {
// Do nothing.
}
+ }
+ try {
activityOptions.options.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- if (Utilities.ATLEAST_T) {
- activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR);
- }
- Utilities.allowBGLaunch(activityOptions.options);
-
+ activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR);
+ activityOptions.options.setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+ } catch (Throwable t) {
+ // ignore
}
options = Pair.create(options.first, activityOptions.options);
if (pendingIntent.isActivity()) {
- logAppLaunch(itemInfo);
+ logAppLaunch(hostView.getTag());
}
return RemoteViews.startPendingIntent(hostView, pendingIntent, options);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 3334b32b45a..03aa1006082 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -40,7 +40,6 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED;
-import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
import static com.android.launcher3.popup.SystemShortcut.DONT_SUGGEST_APP;
@@ -1192,7 +1191,6 @@ public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInf
(v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
: Display.DEFAULT_DISPLAY);
Utilities.allowBGLaunch(activityOptions.options);
- addLaunchCookie(item, activityOptions.options);
return activityOptions;
}
@@ -1214,60 +1212,6 @@ public void enterStageSplitFromRunningApp(boolean leftOrTop) {
mSplitWithKeyboardShortcutController.enterStageSplit(leftOrTop);
}
- /**
- * Adds a new launch cookie for the activity launch if supported.
- *
- * @param info the item info for the launch
- * @param opts the options to set the launchCookie on.
- */
- public void addLaunchCookie(ItemInfo info, ActivityOptions opts) {
- IBinder launchCookie = getLaunchCookie(info);
- if (launchCookie != null) {
- opts.setLaunchCookie(launchCookie);
- }
- }
-
- /**
- * Return a new launch cookie for the activity launch if supported.
- *
- * @param info the item info for the launch
- */
- public IBinder getLaunchCookie(ItemInfo info) {
- if (info == null) {
- return null;
- }
- switch (info.container) {
- case Favorites.CONTAINER_DESKTOP:
- case Favorites.CONTAINER_HOTSEAT:
- case Favorites.CONTAINER_PRIVATESPACE:
- // Fall through and continue it's on the workspace (we don't support swiping back
- // to other containers like all apps or the hotseat predictions (which can change)
- break;
- default:
- if (info.container >= 0) {
- // Also allow swiping to folders
- break;
- }
- // Reset any existing launch cookies associated with the cookie
- return ObjectWrapper.wrap(NO_MATCHING_ID);
- }
- switch (info.itemType) {
- case Favorites.ITEM_TYPE_APPLICATION:
- case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- case Favorites.ITEM_TYPE_APPWIDGET:
- // Fall through and continue if it's an app, shortcut, or widget
- break;
- default:
- // Reset any existing launch cookies associated with the cookie
- return ObjectWrapper.wrap(NO_MATCHING_ID);
- }
- return ObjectWrapper.wrap(new Integer(info.id));
- }
-
- public void setHintUserWillBeActive() {
- addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
- }
-
@Override
public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
super.onDisplayInfoChanged(context, info, flags);
diff --git a/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt b/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt
index 27bd03d4dd8..9216e57579d 100644
--- a/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt
+++ b/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt
@@ -7,6 +7,7 @@ import android.app.backup.BackupRestoreEventLogger.BackupRestoreError
import android.content.Context
import com.android.launcher3.Flags.enableLauncherBrMetricsFixed
import com.android.launcher3.LauncherSettings.Favorites
+import com.android.launcher3.Utilities
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger
/**
@@ -29,8 +30,11 @@ class LauncherRestoreEventLoggerImpl(val context: Context) : LauncherRestoreEven
@BackupRestoreDataType private const val DATA_TYPE_APP_PAIR = "app_pair"
}
- private val restoreEventLogger: BackupRestoreEventLogger =
+ private val restoreEventLogger: BackupRestoreEventLogger? = if (Utilities.ATLEAST_S) {
BackupManager(context).delayedRestoreLogger
+ } else {
+ null
+ }
/**
* For logging when multiple items of a given data type failed to restore.
@@ -45,7 +49,7 @@ class LauncherRestoreEventLoggerImpl(val context: Context) : LauncherRestoreEven
@BackupRestoreError error: String?
) {
if (enableLauncherBrMetricsFixed()) {
- restoreEventLogger.logItemsRestoreFailed(dataType, count, error)
+ restoreEventLogger?.logItemsRestoreFailed(dataType, count, error)
}
}
@@ -57,7 +61,7 @@ class LauncherRestoreEventLoggerImpl(val context: Context) : LauncherRestoreEven
*/
override fun logLauncherItemsRestored(@BackupRestoreDataType dataType: String, count: Int) {
if (enableLauncherBrMetricsFixed()) {
- restoreEventLogger.logItemsRestored(dataType, count)
+ restoreEventLogger?.logItemsRestored(dataType, count)
}
}
@@ -68,7 +72,7 @@ class LauncherRestoreEventLoggerImpl(val context: Context) : LauncherRestoreEven
*/
override fun logSingleFavoritesItemRestored(favoritesId: Int) {
if (enableLauncherBrMetricsFixed()) {
- restoreEventLogger.logItemsRestored(favoritesIdToDataType(favoritesId), 1)
+ restoreEventLogger?.logItemsRestored(favoritesIdToDataType(favoritesId), 1)
}
}
@@ -80,7 +84,7 @@ class LauncherRestoreEventLoggerImpl(val context: Context) : LauncherRestoreEven
*/
override fun logFavoritesItemsRestored(favoritesId: Int, count: Int) {
if (enableLauncherBrMetricsFixed()) {
- restoreEventLogger.logItemsRestored(favoritesIdToDataType(favoritesId), count)
+ restoreEventLogger?.logItemsRestored(favoritesIdToDataType(favoritesId), count)
}
}
@@ -95,7 +99,7 @@ class LauncherRestoreEventLoggerImpl(val context: Context) : LauncherRestoreEven
@BackupRestoreError error: String?
) {
if (enableLauncherBrMetricsFixed()) {
- restoreEventLogger.logItemsRestoreFailed(favoritesIdToDataType(favoritesId), 1, error)
+ restoreEventLogger?.logItemsRestoreFailed(favoritesIdToDataType(favoritesId), 1, error)
}
}
@@ -112,7 +116,7 @@ class LauncherRestoreEventLoggerImpl(val context: Context) : LauncherRestoreEven
@BackupRestoreError error: String?
) {
if (enableLauncherBrMetricsFixed()) {
- restoreEventLogger.logItemsRestoreFailed(
+ restoreEventLogger?.logItemsRestoreFailed(
favoritesIdToDataType(favoritesId),
count,
error
@@ -125,7 +129,7 @@ class LauncherRestoreEventLoggerImpl(val context: Context) : LauncherRestoreEven
* done restoring items for Launcher.
*/
override fun reportLauncherRestoreResults() {
- if (enableLauncherBrMetricsFixed()) {
+ if (enableLauncherBrMetricsFixed() && restoreEventLogger != null) {
BackupManager(context).reportDelayedRestoreResult(restoreEventLogger)
}
}
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 3c665906b57..413ddbc9f8c 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -20,7 +20,6 @@
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.Utilities.mapBoundToRange;
-import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
@@ -42,7 +41,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.launcher3.util.ObjectWrapper;
+import com.android.launcher3.util.StableViewInfo;
import com.android.launcher3.views.ClipIconView;
import com.android.launcher3.views.FloatingIconView;
import com.android.launcher3.views.FloatingView;
@@ -62,12 +61,12 @@
/**
* Temporary class to allow easier refactoring
*/
-public class LauncherSwipeHandlerV2 extends
- AbsSwipeUpHandler {
+public class LauncherSwipeHandlerV2 extends AbsSwipeUpHandler<
+ QuickstepLauncher, RecentsView, LauncherState> {
public LauncherSwipeHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
- TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
- boolean continuingLastGesture, InputConsumerController inputConsumer) {
+ TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
+ boolean continuingLastGesture, InputConsumerController inputConsumer) {
super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
continuingLastGesture, inputConsumer);
}
@@ -95,8 +94,8 @@ public AnimatorPlaybackController createActivityAnimationToHome() {
TaskView sourceTaskView = mRecentsView == null && targetTaskView == null
? null
: targetTaskView == null
- ? mRecentsView.getRunningTaskView()
- : targetTaskView;
+ ? mRecentsView.getRunningTaskView()
+ : targetTaskView;
final View workspaceView = findWorkspaceView(
targetTaskView == null ? launchCookies : Collections.emptyList(),
sourceTaskView);
@@ -107,9 +106,6 @@ public AnimatorPlaybackController createActivityAnimationToHome() {
|| !mContainer.getDesktopVisibilityController().areDesktopTasksVisible());
mContainer.getRootView().setForceHideBackArrow(true);
- if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
- mContainer.setHintUserWillBeActive();
- }
if (!canUseWorkspaceView || appCanEnterPip || mIsSwipeForSplit) {
return new LauncherHomeAnimationFactory() {
@@ -144,8 +140,6 @@ private HomeAnimationFactory createIconHomeAnimationFactory(
return new FloatingViewHomeAnimationFactory(floatingIconView) {
@Nullable
private RectF mTargetRect;
- @Nullable
- private RectFSpringAnim mSiblingAnimation;
@Nullable
@Override
@@ -172,14 +166,6 @@ public RectF getWindowTargetRect() {
}
}
- @Override
- protected void playScalingRevealAnimation() {
- if (mContainer != null) {
- new ScalingWorkspaceRevealAnim(mContainer, mSiblingAnimation,
- getWindowTargetRect()).start();
- }
- }
-
@Override
public void setAnimation(RectFSpringAnim anim) {
super.setAnimation(anim);
@@ -245,6 +231,8 @@ private HomeAnimationFactory createWidgetHomeAnimationFactory(
isTargetTranslucent, fallbackBackgroundColor);
return new FloatingViewHomeAnimationFactory(floatingWidgetView) {
+ @Nullable
+ private RectF mTargetRect;
@Override
@Nullable
@@ -254,8 +242,14 @@ protected View getViewIgnoredInWorkspaceRevealAnimation() {
@Override
public RectF getWindowTargetRect() {
- super.getWindowTargetRect();
- return backgroundLocation;
+ if (enableScalingRevealHomeAnimation()) {
+ if (mTargetRect == null) {
+ mTargetRect = new RectF(backgroundLocation);
+ }
+ return mTargetRect;
+ } else {
+ return backgroundLocation;
+ }
}
@Override
@@ -266,10 +260,11 @@ public float getEndRadius(RectF cropRectF) {
@Override
public void setAnimation(RectFSpringAnim anim) {
super.setAnimation(anim);
-
- anim.addAnimatorListener(floatingWidgetView);
- floatingWidgetView.setOnTargetChangeListener(anim::onTargetPositionChanged);
- floatingWidgetView.setFastFinishRunnable(anim::end);
+ mSiblingAnimation = anim;
+ mSiblingAnimation.addAnimatorListener(floatingWidgetView);
+ floatingWidgetView.setOnTargetChangeListener(
+ mSiblingAnimation::onTargetPositionChanged);
+ floatingWidgetView.setFastFinishRunnable(mSiblingAnimation::end);
}
@Override
@@ -305,18 +300,7 @@ private View findWorkspaceView(List launchCookies, TaskView sourceTaskV
return null;
}
- // Find the associated item info for the launch cookie (if available), note that predicted
- // apps actually have an id of -1, so use another default id here
- int launchCookieItemId = NO_MATCHING_ID;
- for (IBinder cookie : launchCookies) {
- Integer itemId = ObjectWrapper.unwrap(cookie);
- if (itemId != null) {
- launchCookieItemId = itemId;
- break;
- }
- }
-
- return mContainer.getFirstMatchForAppClose(launchCookieItemId,
+ return mContainer.getFirstMatchForAppClose(StableViewInfo.fromLaunchCookies(launchCookies),
sourceTaskView.getFirstTask().key.getComponent().getPackageName(),
UserHandle.of(sourceTaskView.getFirstTask().key.userId),
false /* supportsAllAppsState */);
@@ -330,13 +314,22 @@ protected void finishRecentsControllerToHome(Runnable callback) {
}
private class FloatingViewHomeAnimationFactory extends LauncherHomeAnimationFactory {
-
private final FloatingView mFloatingView;
+ @Nullable
+ protected RectFSpringAnim mSiblingAnimation;
FloatingViewHomeAnimationFactory(FloatingView floatingView) {
mFloatingView = floatingView;
}
+ @Override
+ protected void playScalingRevealAnimation() {
+ if (mContainer != null) {
+ new ScalingWorkspaceRevealAnim(mContainer, mSiblingAnimation,
+ getWindowTargetRect()).start();
+ }
+ }
+
@Override
public void onCancel() {
mFloatingView.fastFinish();
@@ -387,4 +380,4 @@ protected void playScalingRevealAnimation() {
}
}
}
-}
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 3dec381b04d..a9da63f4809 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -19,6 +19,8 @@
import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
+import static java.util.stream.Collectors.toList;
+
import android.app.WindowConfiguration;
import android.content.Context;
import android.graphics.Rect;
@@ -175,7 +177,7 @@ public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets t
mSplitBounds.rightBottomTaskId);
List overlayTargets = Arrays.stream(targets.apps).filter(
target -> target.windowConfiguration.getWindowingMode()
- != WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW).toList();
+ != WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW).collect(toList());
// remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude,
// vice versa
@@ -223,7 +225,7 @@ public RemoteTargetHandle[] assignTargetsForDesktop(RemoteAnimationTargets targe
for (int i = 0; i < mRemoteTargetHandles.length; i++) {
RemoteAnimationTarget primaryTaskTarget = targets.apps[i];
List excludeTargets = Arrays.stream(targets.apps)
- .filter(target -> target.taskId != primaryTaskTarget.taskId).toList();
+ .filter(target -> target.taskId != primaryTaskTarget.taskId).collect(toList());
mRemoteTargetHandles[i].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(targets, excludeTargets));
mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index ba33c62d9d1..79df19f2597 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -26,6 +26,7 @@
import android.graphics.Matrix.ScaleToFit;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.util.Log;
import android.view.RemoteAnimationTarget;
import androidx.annotation.NonNull;
@@ -85,7 +86,7 @@ public abstract class SwipeUpAnimationLogic implements
protected boolean mIsSwipeForSplit;
public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
- GestureState gestureState) {
+ GestureState gestureState) {
mContext = context;
mDeviceState = deviceState;
mGestureState = gestureState;
@@ -267,7 +268,7 @@ protected RectF[] updateProgressForStartRect(Matrix[] outMatrix, float startProg
mCurrentShift.updateValue(startProgress);
RectF[] startRects = new RectF[mRemoteTargetHandles.length];
for (int i = 0, mRemoteTargetHandlesLength = mRemoteTargetHandles.length;
- i < mRemoteTargetHandlesLength; i++) {
+ i < mRemoteTargetHandlesLength; i++) {
RemoteTargetHandle remoteHandle = mRemoteTargetHandles[i];
TaskViewSimulator tvs = remoteHandle.getTaskViewSimulator();
tvs.apply(remoteHandle.getTransformParams().setProgress(startProgress));
@@ -300,14 +301,14 @@ protected TaskViewSimulator[] getRemoteTaskViewSimulators() {
* @param homeAnimationFactory The home animation factory.
*/
protected RectFSpringAnim[] createWindowAnimationToHome(float startProgress,
- HomeAnimationFactory homeAnimationFactory) {
+ HomeAnimationFactory homeAnimationFactory) {
// TODO(b/195473584) compute separate end targets for different staged split
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
RectFSpringAnim[] out = new RectFSpringAnim[mRemoteTargetHandles.length];
Matrix[] homeToWindowPositionMap = new Matrix[mRemoteTargetHandles.length];
RectF[] startRects = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
for (int i = 0, mRemoteTargetHandlesLength = mRemoteTargetHandles.length;
- i < mRemoteTargetHandlesLength; i++) {
+ i < mRemoteTargetHandlesLength; i++) {
RemoteTargetHandle remoteHandle = mRemoteTargetHandles[i];
out[i] = getWindowAnimationToHomeInternal(
homeAnimationFactory,
@@ -381,6 +382,8 @@ private RectFSpringAnim getWindowAnimationToHomeInternal(
protected class SpringAnimationRunner extends AnimationSuccessListener
implements RectFSpringAnim.OnUpdateListener, BuilderProxy {
+ private static final String TAG = "SpringAnimationRunner";
+
final Rect mCropRect = new Rect();
final Matrix mMatrix = new Matrix();
@@ -481,10 +484,26 @@ public void onUpdate(RectF currentRect, float progress) {
return;
}
mTargetTaskView.setAlpha(mAnimationFactory.isAnimatingIntoIcon() ? 1f : alpha);
- float width = mThumbnailStartBounds.width();
- float height = mThumbnailStartBounds.height();
- float scale = Math.min(currentRect.width(), currentRect.height())
- / Math.min(width, height);
+ float startWidth = mThumbnailStartBounds.width();
+ float startHeight = mThumbnailStartBounds.height();
+ float currentWidth = currentRect.width();
+ float currentHeight = currentRect.height();
+ float scale;
+
+ boolean isStartWidthValid = Float.compare(startWidth, 0f) > 0;
+ boolean isStartHeightValid = Float.compare(startHeight, 0f) > 0;
+ if (isStartWidthValid && isStartHeightValid) {
+ scale = Math.min(currentWidth, currentHeight) / Math.min(startWidth, startHeight);
+ } else {
+ Log.e(TAG, "TaskView starting bounds are invalid: " + mThumbnailStartBounds);
+ if (isStartWidthValid) {
+ scale = currentWidth / startWidth;
+ } else if (isStartHeightValid) {
+ scale = currentHeight / startHeight;
+ } else {
+ scale = 1f;
+ }
+ }
mTargetTaskView.setScaleX(scale);
mTargetTaskView.setScaleY(scale);
@@ -496,7 +515,7 @@ public void onUpdate(RectF currentRect, float progress) {
@Override
public void onBuildTargetParams(SurfaceProperties builder, RemoteAnimationTarget app,
- TransformParams params) {
+ TransformParams params) {
builder.setMatrix(mMatrix)
.setWindowCrop(mCropRect)
.setCornerRadius(params.getCornerRadius());
@@ -606,4 +625,4 @@ public void cancel() {
};
}
}
-}
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index 6f9cbfd74a7..e8782101641 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -33,6 +33,8 @@
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.isPersistentSnapPosition;
+import static java.util.stream.Collectors.toList;
+
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
@@ -336,7 +338,7 @@ public void handleAppPairLaunchInApp(AppPairIcon launchingIconView,
List extends ItemInfo> itemInfos) {
TaskbarActivityContext context = (TaskbarActivityContext) launchingIconView.getContext();
List componentKeys =
- itemInfos.stream().map(ItemInfo::getComponentKey).toList();
+ itemInfos.stream().map(ItemInfo::getComponentKey).collect(toList());
// Use TopTaskTracker to find the currently running app (or apps)
TopTaskTracker topTaskTracker = getTopTaskTracker();
@@ -362,7 +364,7 @@ public void handleAppPairLaunchInApp(AppPairIcon launchingIconView,
} else {
return INVALID_TASK_ID;
}
- }).toList();
+ }).collect(toList());
if (lastActiveTasksOfAppPair.contains(runningTaskId1)
&& lastActiveTasksOfAppPair.contains(runningTaskId2)) {
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 128b98c11ef..7f29bd8c21f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -328,7 +328,9 @@
- no
-
+
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 851b4709b35..6a682b2dd38 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -106,7 +106,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
public static final int TYPE_PIN_IME_POPUP = 1 << 22;
// Custom compose popups
- public static final int TYPE_COMPOSE_VIEW = 1 << 22;
+ public static final int TYPE_COMPOSE_VIEW = 1 << 23;
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
@@ -115,14 +115,14 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
| TYPE_ICON_SURFACE | TYPE_DRAG_DROP_POPUP | TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP
| TYPE_TASKBAR_EDUCATION_DIALOG | TYPE_TASKBAR_ALL_APPS | TYPE_OPTIONS_POPUP_DIALOG
| TYPE_ADD_TO_HOME_CONFIRMATION | TYPE_TASKBAR_OVERLAY_PROXY
- | TYPE_TASKBAR_PINNING_POPUP | TYPE_PIN_IME_POPUP;
+ | TYPE_TASKBAR_PINNING_POPUP | TYPE_PIN_IME_POPUP | TYPE_COMPOSE_VIEW;
// Type of popups which should be kept open during launcher rebind
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
| TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE | TYPE_TASKBAR_EDUCATION_DIALOG
| TYPE_TASKBAR_ALL_APPS | TYPE_OPTIONS_POPUP_DIALOG | TYPE_TASKBAR_OVERLAY_PROXY
- | TYPE_PIN_IME_POPUP;
+ | TYPE_PIN_IME_POPUP | TYPE_COMPOSE_VIEW;
/** Type of popups that should get exclusive accessibility focus. */
public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 97ce00c41d8..541e27b58bb 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -48,6 +48,7 @@
import android.text.TextUtils;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.Property;
import android.util.Size;
import android.util.TypedValue;
@@ -85,12 +86,13 @@
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.views.IconLabelDotView;
+import com.android.launcher3.views.FloatingIconViewCompanion;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Locale;
+import app.lawnchair.LawnchairApp;
import app.lawnchair.font.FontManager;
import app.lawnchair.preferences.PreferenceManager;
import app.lawnchair.util.LawnchairUtilsKt;
@@ -103,7 +105,7 @@
* too aggressive.
*/
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
- IconLabelDotView, DraggableView, Reorderable {
+ FloatingIconViewCompanion, DraggableView, Reorderable {
public static final int DISPLAY_WORKSPACE = 0;
public static final int DISPLAY_ALL_APPS = 1;
@@ -231,7 +233,7 @@ public BubbleTextView(Context context, AttributeSet attrs) {
public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mActivity = ActivityContext.lookupContext(context);
- FastBitmapDrawable.setFlagHoverEnabled(enableCursorHoverStates());
+ FastBitmapDrawable.setFlagHoverEnabled(LawnchairApp.isRecentsEnabled() && enableCursorHoverStates());
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BubbleTextView, defStyle, 0);
@@ -1275,6 +1277,13 @@ public SafeCloseable prepareDrawDragView() {
};
}
+ @Override
+ public void resetIconScale(boolean shouldReset) {
+ if (shouldReset) {
+ mIcon.resetScale();
+ }
+ }
+
private void resetIconScale() {
if (mIcon != null) {
mIcon.resetScale();
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index ad586c95f80..848bf42e3e2 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -77,6 +77,7 @@
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.patrykmichalik.opto.core.PreferenceExtensionsKt;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -84,6 +85,10 @@
import java.util.Arrays;
import java.util.Stack;
+import app.lawnchair.preferences2.PreferenceManager2;
+import app.lawnchair.theme.color.tokens.ColorTokens;
+import app.lawnchair.theme.drawable.DrawableTokens;
+
public class CellLayout extends ViewGroup {
private static final String TAG = "CellLayout";
private static final boolean LOGD = false;
@@ -208,6 +213,8 @@ public class CellLayout extends ViewGroup {
CellLayoutContainer mCellLayoutContainer;
+ public final PreferenceManager2 pref;
+
public static final FloatProperty SPRING_LOADED_PROGRESS =
new FloatProperty("spring_loaded_progress") {
@Override
@@ -244,6 +251,7 @@ public CellLayout(Context context, AttributeSet attrs, int defStyle) {
mActivity = ActivityContext.lookupContext(context);
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ pref = PreferenceManager2.getInstance(context);
resetCellSizeInternal(deviceProfile);
mCountX = deviceProfile.inv.numColumns;
@@ -258,11 +266,11 @@ public CellLayout(Context context, AttributeSet attrs, int defStyle) {
Resources res = getResources();
- mBackground = getContext().getDrawable(R.drawable.bg_celllayout);
+ mBackground = DrawableTokens.BgCellLayout.resolve(getContext());
mBackground.setCallback(this);
mBackground.setAlpha(0);
- mGridColor = Themes.getAttrColor(getContext(), R.attr.workspaceAccentColor);
+ mGridColor = ColorTokens.WorkspaceAccentColor.resolveColor(getContext());
mGridVisualizationRoundingRadius =
res.getDimensionPixelSize(R.dimen.grid_visualization_rounding_radius);
mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * deviceProfile.iconSizePx);
@@ -274,7 +282,7 @@ public CellLayout(Context context, AttributeSet attrs, int defStyle) {
for (int i = 0; i < mDragOutlines.length; i++) {
mDragOutlines[i] = new CellLayoutLayoutParams(0, 0, 0, 0);
}
- mDragOutlinePaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));
+ mDragOutlinePaint.setColor(ColorTokens.WorkspaceAccentColor.resolveColor(getContext()));
// When dragging things around the home screens, we show a green outline of
// where the item will land. The outlines gradually fade out, leaving a trail
@@ -732,7 +740,7 @@ public int getCountY() {
}
public boolean acceptsWidget() {
- return mContainerType == WORKSPACE;
+ return mContainerType == WORKSPACE || mContainerType == HOTSEAT;
}
/**
@@ -1853,7 +1861,7 @@ public int getDesiredHeight() {
public boolean isOccupied(int x, int y) {
if (x >= 0 && x < mCountX && y >= 0 && y < mCountY) {
- return mOccupied.cells[x][y];
+ return mOccupied.cells[x][y] && !PreferenceExtensionsKt.firstBlocking(pref.getAllowWidgetOverlap());
}
if (BuildConfigs.IS_STUDIO_BUILD) {
throw new RuntimeException("Position exceeds the bound of this CellLayout");
@@ -1936,7 +1944,7 @@ public GridOccupancy cloneGridOccupancy() {
}
public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
- return mOccupied.isRegionVacant(x, y, spanX, spanY);
+ return mOccupied.isRegionVacant(x, y, spanX, spanY) || PreferenceExtensionsKt.firstBlocking(pref.getAllowWidgetOverlap());
}
public void setSpaceBetweenCellLayoutsPx(@Px int spaceBetweenCellLayoutsPx) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0dd0fa6cad8..03c30ccfe64 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -72,6 +72,7 @@
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
+import static com.android.launcher3.folder.FolderGridOrganizer.createFolderGridOrganizer;
import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE;
import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.SHOW;
import static com.android.launcher3.logging.StatsLogManager.EventEnum;
@@ -242,6 +243,7 @@
import com.android.launcher3.util.ScreenOnTracker;
import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener;
import com.android.launcher3.util.SettingsCache;
+import com.android.launcher3.util.StableViewInfo;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
@@ -827,7 +829,7 @@ public void invalidateParent(ItemInfo info) {
View collectionIcon = getWorkspace().getHomescreenIconByItemId(info.container);
if (collectionIcon instanceof FolderIcon folderIcon
&& collectionIcon.getTag() instanceof FolderInfo) {
- if (new FolderGridOrganizer(getDeviceProfile())
+ if (createFolderGridOrganizer(getDeviceProfile())
.setFolderInfo((FolderInfo) folderIcon.getTag())
.isItemInPreview(info.rank)) {
folderIcon.invalidate();
@@ -2494,28 +2496,25 @@ private boolean canAnimatePageChange() {
}
/**
- * Similar to {@link #getFirstMatch} but optimized to finding a suitable view
- * for the app close
+ * Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
* animation.
*
- * @param preferredItemId The id of the preferred item to match to if it
- * exists,
- * or ItemInfo#NO_MATCHING_ID if you want to not
- * match by item id
- * @param packageName The package name of the app to match.
- * @param user The user of the app to match.
- * @param supportsAllAppsState If true and we are in All Apps state, looks for
- * view in All Apps.
+ * @param svi The StableViewInfo of the preferred item to match to if it exists or null
+ * @param packageName The package name of the app to match.
+ * @param user The user of the app to match.
+ * @param supportsAllAppsState If true and we are in All Apps state, looks for view in All Apps.
* Else we only looks on the workspace.
*/
- public @Nullable View getFirstMatchForAppClose(int preferredItemId, String packageName,
+ public @Nullable View getFirstMatchForAppClose(
+ @Nullable StableViewInfo svi, String packageName,
UserHandle user, boolean supportsAllAppsState) {
- final Predicate preferredItem = info -> info != null && info.id == preferredItemId;
- final Predicate packageAndUserAndApp = info -> info != null
- && info.itemType == ITEM_TYPE_APPLICATION
- && info.user.equals(user)
- && info.getTargetComponent() != null
- && TextUtils.equals(info.getTargetComponent().getPackageName(),
+ final Predicate preferredItem = svi == null ? i -> false : svi::matches;
+ final Predicate packageAndUserAndApp = info ->
+ info != null
+ && info.itemType == ITEM_TYPE_APPLICATION
+ && info.user.equals(user)
+ && info.getTargetComponent() != null
+ && TextUtils.equals(info.getTargetComponent().getPackageName(),
packageName);
if (supportsAllAppsState && isInState(LauncherState.ALL_APPS)) {
@@ -2540,7 +2539,7 @@ private boolean canAnimatePageChange() {
Folder folder = Folder.getOpen(this);
if (folder != null) {
View v = getFirstMatch(Collections.singletonList(
- folder.getContent().getCurrentCellLayout().getShortcutsAndWidgets()),
+ folder.getContent().getCurrentCellLayout().getShortcutsAndWidgets()),
preferredItem,
packageAndUserAndApp);
if (v == null) {
@@ -2552,7 +2551,8 @@ private boolean canAnimatePageChange() {
List containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
containers.add(mWorkspace.getHotseat().getShortcutsAndWidgets());
- mWorkspace.forEachVisiblePage(page -> containers.add(((CellLayout) page).getShortcutsAndWidgets()));
+ mWorkspace.forEachVisiblePage(page
+ -> containers.add(((CellLayout) page).getShortcutsAndWidgets()));
// Order: Preferred item by itself or in folder, then by matching package/user
return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem),
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index fdff36a62f0..27e7626d4af 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -2,23 +2,31 @@
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
+import static app.lawnchair.util.PackagePermissionManagerKt.checkAndRequestFilesPermission;
+
+import android.app.WallpaperManager;
import android.content.Context;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.ViewDebug;
import android.view.WindowInsets;
import com.android.launcher3.graphics.SysUiScrim;
import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.uioverrides.ApiWrapper;
+import com.hoko.blur.HokoBlur;
import com.patrykmichalik.opto.core.PreferenceExtensionsKt;
import com.android.launcher3.util.window.WindowManagerProxy;
import java.util.Collections;
import java.util.List;
-import app.lawnchair.LawnchairApp;
+import app.lawnchair.preferences.PreferenceManager;
import app.lawnchair.preferences2.PreferenceManager2;
public class LauncherRootView extends InsettableFrameLayout {
@@ -39,12 +47,71 @@ public class LauncherRootView extends InsettableFrameLayout {
private final SysUiScrim mSysUiScrim;
private final boolean mEnableTaskbarOnPhone;
+ private final PreferenceManager pref;
+
public LauncherRootView(Context context, AttributeSet attrs) {
super(context, attrs);
mActivity = StatefulActivity.fromContext(context);
mSysUiScrim = new SysUiScrim(this);
PreferenceManager2 prefs2 = PreferenceManager2.getInstance(getContext());
mEnableTaskbarOnPhone = PreferenceExtensionsKt.firstBlocking(prefs2.getEnableTaskbarOnPhone());
+
+ pref = PreferenceManager.getInstance(getContext());
+
+ if (pref.getEnableWallpaperBlur().get()){
+ if (checkAndRequestFilesPermission(context, pref)){
+ setUpBlur(context);
+ }
+ }
+ }
+
+ private void setUpBlur(Context context) {
+ var display = mActivity.getDeviceProfile();
+ int width = display.widthPx;
+ int height = display.heightPx;
+
+ var wallpaper = getScaledWallpaperDrawable(width, height);
+ if (wallpaper == null) {
+ return;
+ }
+
+ Bitmap originalBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(originalBitmap);
+
+ wallpaper.setBounds(0, 0, width, height);
+ wallpaper.draw(canvas);
+
+ Paint paint = new Paint();
+ paint.setColor(Color.WHITE);
+ paint.setAlpha((int) (0.2 * 255));
+ canvas.drawRect(0, 0, width, height, paint);
+
+ Bitmap blurredBitmap = HokoBlur.with(context)
+ .forceCopy(true)
+ .scheme(HokoBlur.SCHEME_OPENGL)
+ .sampleFactor(pref.getWallpaperBlurFactorThreshold().get())
+ .radius(pref.getWallpaperBlur().get())
+ .blur(originalBitmap);
+
+ setBackground(new BitmapDrawable(getContext().getResources(), blurredBitmap));
+ }
+
+ private Drawable getScaledWallpaperDrawable(int width, int height) {
+ WallpaperManager wallpaperManager = WallpaperManager.getInstance(getContext());
+ Drawable wallpaperDrawable = wallpaperManager.getDrawable();
+
+ if (wallpaperDrawable != null) {
+ Bitmap originalBitmap = Bitmap.createBitmap(
+ width, height, Bitmap.Config.ARGB_8888
+ );
+ Canvas canvas = new Canvas(originalBitmap);
+
+ wallpaperDrawable.setBounds(0, 0, width, height);
+ wallpaperDrawable.draw(canvas);
+
+ return new BitmapDrawable(getContext().getResources(), originalBitmap);
+ }
+ return null;
}
private void handleSystemWindowInsets(Rect insets) {
@@ -65,7 +132,6 @@ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
insets = WindowManagerProxy.INSTANCE.get(getContext())
.normalizeWindowInsets(getContext(), insets, mTempRect);
handleSystemWindowInsets(mTempRect);
-// computeGestureExclusionRect();
return insets;
}
@@ -114,7 +180,6 @@ protected void dispatchDraw(Canvas canvas) {
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
-// computeGestureExclusionRect();
mSysUiScrim.setSize(r - l, b - t);
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 498c1be9735..878a2c9b6a0 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -113,6 +113,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import app.lawnchair.icons.ExtendedBitmapDrawable;
import app.lawnchair.preferences.PreferenceManager;
/**
@@ -767,6 +768,9 @@ public static Pair intersectingViews = new ArrayList<>();
Rect occupiedRect = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
+ if (PreferenceExtensionsKt.firstBlocking(mCellLayout.pref.getAllowWidgetOverlap())) {
+ solution.intersectingViews = new ArrayList<>(intersectingViews);
+ return true;
+ }
+
// Mark the desired location of the view currently being dragged.
if (ignoreView != null) {
CellAndSpan c = solution.map.get(ignoreView);
@@ -150,18 +156,11 @@ private boolean rearrangementExists(int cellX, int cellY, int spanX, int spanY,
).thenComparing(
view -> ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellY()
);
- List views = new ArrayList<>();
- if (Utilities.ATLEAST_U) {
- views = solution.map.keySet().stream().sorted(comparator).toList();
- } else {
- List