Repository navigation
Conversation
LukasPaczos
force-pushed
the
develop
branch
from
September 11, 2025 14:29
3bc8c57 to
ceb6fa4
Compare
aibrahim-
force-pushed
the
develop
branch
2 times, most recently
from
May 14, 2026 14:07
231d816 to
d08fbc3
Compare
) Task/Issue URL: https://app.asana.com/1/137249556945/project/72649045549333/task/1219760542418633?focus=true Tech Design URL (if applicable): API Proposals URL(s) (if applicable): ### Description Visual polish for the rich Duck.ai card in the tab switcher grid: - Content background: chat and image content now use the primary surface color (daxColorSurface). The `daxColorTabDuckAiContentContainer` token is no longer used, so it's removed from the design system. - Label: the Chat, Transcript and Voice chat labels now use secondary text color, and their icons are tinted to match. - Layout: the content (snippet or image thumbnail) and the label are anchored to each other, so the label always sits directly under whichever one is shown. The voice face and its label are anchored the same way. - Spacing: removed the top margin on the card titles and adjusted the thumbnail margins (4dp top, 8dp bottom). ### Steps to test this PR Smoke test tab switcher ### UI changes | Before | After | | ------ | ----- | !<img width="323" height="367" alt="Screenshot 2026-10-22 at 12 26 05" src="https://github.com/user-attachments/assets/bafb60a0-7abc-4b91-a45d-05192d667560" />|<img width="328" height="367" alt="Screenshot 2026-10-22 at 11 58 35" src="https://github.com/user-attachments/assets/3b086d18-a6f4-4e2f-896b-f1126dde7a2c" />| --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Task/Issue URL: https://app.asana.com/1/137249556945/project/414730916066338/task/1219890577216994 - Automated content scope scripts dependency update This PR updates the content scope scripts dependency to the latest available version and copies the necessary files. Tests will only run if something has changed in the `node_modules/@duckduckgo/content-scope-scripts` folder. If only the package version has changed, there is no need to run the tests. If tests have failed, see https://app.asana.com/0/1202561462274611/1203986899650836/f for further information on what to do next. _`content-scope-scripts` folder update_ - [ ] All tests must pass - [ ] Privacy tests must pass _Only `content-scope-scripts` package update_ - [ ] All tests must pass - [ ] Privacy tests do not need to run Co-authored-by: daxmobile <daxmobile@users.noreply.github.com>
Task/Issue URL: https://app.asana.com/1/137249556945/project/414730916066338/task/1219921002387479 - Automated content scope scripts dependency update This PR updates the content scope scripts dependency to the latest available version and copies the necessary files. Tests will only run if something has changed in the `node_modules/@duckduckgo/content-scope-scripts` folder. If only the package version has changed, there is no need to run the tests. If tests have failed, see https://app.asana.com/0/1202561462274611/1203986899650836/f for further information on what to do next. _`content-scope-scripts` folder update_ - [ ] All tests must pass - [ ] Privacy tests must pass _Only `content-scope-scripts` package update_ - [ ] All tests must pass - [ ] Privacy tests do not need to run Co-authored-by: daxmobile <daxmobile@users.noreply.github.com>
…10186) Task/Issue URL: https://app.asana.com/1/137249556945/project/1200204095367872/task/1219798206568457?focus=true Tech Design URL (if applicable): API Proposals URL(s) (if applicable): ### Description - The camera launcher now calls `onResult` on the main thread in order to fix a `CalledFromWrongThreadException` when focussing the `NativeInputModeWidget` ### Steps to test this PR - [ ] Open Duck.ai, tap the attach icon and choose "Take Photo" - [ ] Verify that the photo is attached and the app does not crash
Task/Issue URL: https://app.asana.com/0/0/1215373984475612/f Tech Design URL (if applicable): API Proposals URL(s) (if applicable): None ### Description This makes `omnibarTextInput` display only: it shows the URL or query and the click catcher opens the native input. It removes the omnibar's focus, back key, IME and touch listeners, the back arrow and clear button, every `hasFocus`/`isEditing` branch, and the autofill autoprompt gate that relied on them. For reviewers: the native input never cancels a pending autofill credential prompt. That was not reproduced on a device (the emulator has no screen lock, so autofill is off there) and is left open. `from_focused_ntp` on the tab manager, fire and menu pixels had been false since the native input rolled out, because it read the legacy field's focus. It now comes from the native input: true on the new tab page when the native input's text field has focus (`NativeInputWidget.hasInputFocus()`). I didn't use `isNativeInputShown()`, which is true whenever the widget is attached, not only when the field has focus. Retired pixels (no firing site left; `m_addressbar_click_aichat` also removed from the registry): `m_addressbar_focus_ntp`, `m_addressbar_click_ntp`, `m_addressbar_click_website`, `m_addressbar_click_serp`, `m_addressbar_click_aichat`, `m_addressbar_focus_clear_entry_ntp`, `m_addressbar_focus_clear_entry_website`, `m_addressbar_focus_clear_entry_serp`, `m_addressbar_focus_cancel_ntp`, `m_addressbar_focus_cancel_website`, `m_addressbar_focus_cancel_serp`, `m_addressbar_focus_close_ntp`, `m_addressbar_focus_close_website`, `m_addressbar_focus_close_serp`, `m_keyboard_go_click_ntp`, `m_keyboard_go_click_website`, `m_keyboard_go_click_serp`, `m_aichat_legacy_omnibar_shown`, `m_aichat_legacy_omnibar_shown_count`, `m_aichat_legacy_omnibar_back_button_pressed`. ### Steps to test this PR _Address bar_ - [x] Install an internal build, open a site and check the address bar shows the domain and can't be typed into - [x] Tap the address bar on a page and on the new tab page, with the address bar at the top and then at the bottom, and check the native input opens each time - [x] Repeat with Settings > AI Features set to Search Only, then to Toggle between Search and Duck.ai - [x] With two tabs open, swipe the address bar sideways and check the tab changes _Duck.ai_ - [x] Open https://duck.ai from the address bar, press back, and check the previous page shows with no keyboard and no input open ### UI changes | Before | After | | ------ | ----- | | No UI changes | No UI changes | 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1201807753394693/task/1219813152401991?focus=true Tech Design URL (if applicable): API Proposals URL(s) (if applicable): ### Description - Remove subscribe pixel events from switches - Replace with switch pixel ### Steps to test this PR - [x] Apply patch on https://app.asana.com/1/137249556945/project/1209991789468715/task/1210448620621729?focus=true - [x] Install from branch - [x] Purchase a test subscription - [x] Verify `m_subscribe` pixel is sent - [x] Verify `m_privacy-pro_app_subscription-purchase_success` pixel is sent - [x] Go to App Settings > Subscription Settings - [x] Tap on 'View All Plans' - [x] Switch to another subscription - [x] Verify `m_subscribe` pixel is **not** sent - [x] Verify `m_privacy-pro_app_subscription-purchase_success` pixel is **not** sent - [x] Verify `subscription_switch` is sent ### No UI changes --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1212608036467427/task/1212839176825188 Tech Design URL (if applicable): API Proposals URL(s) (if applicable): ### Description SQLite corruption makes the default open-helper callback delete app.db, which wipes bookmarks and everything else stored there with no signal. Wrap app.db's open helper so onCorruption fires m_database_corruption (count + daily) with the database name, process name and whether the file had been opened, before the default handling runs. ### Steps to test this PR - [x] open the app - [x] create a bookmark - [x] close the app - [x] manually corrupt the DB file. - [x] open the app - [x] no bookmarks visible, and the pixel is fired. ### UI changes None. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Task/Issue URL: https://app.asana.com/1/137249556945/task/1219776338950932 Tech Design URL (if applicable): https://app.asana.com/1/137249556945/project/481882893211075/task/1219704925344696 API Proposals URL(s) (if applicable): https://app.asana.com/1/137249556945/project/1212087397361015/task/1219775711271918 ### Description Adds a plugin point that lets other features show a promo card on the redesigned Google passwords import success screen, and a temporary dummy sync plugin to exercise it. This is the base for the Sync promotion and the cross-import promos from the designs. The new public API is `PasswordsImportScreenPromotionPlugin` in `autofill-api`. ### Steps to test this PR _Promo on the success screen_ - [ ] Install an internal build. - [ ] Enable the `autofillImportRedesign` feature flag. - [ ] Open Settings. - [ ] Open "Passwords". - [ ] Open the overflow menu. - [ ] Tap "Import Passwords From Google". - [ ] Tap "Import From Google". - [ ] Complete the Google flow. - [ ] Verify the importing screen is shown until the import finishes. - [ ] Verify the completion screen shows the import results with the blue "Dummy plugin" card below them. - [ ] Tap the "Dummy plugin" card. - [x] Verify the import sheet closes. _Redesign disabled_ - [ ] Disable the `autofillImportRedesign` feature flag. - [ ] Import passwords from Google again. - [x] Verify the legacy result screen is shown without any promo card. ### UI changes N/A --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Task/Issue URL: https://app.asana.com/1/137249556945/task/1219816597348860 Tech Design URL (if applicable): N/A API Proposals URL(s) (if applicable): N/A ### Description Moves the state of the Google bookmarks import screen into a ViewModel. The import flow looks and behaves the same as before. The change prepares the screen for the upcoming redesign and fixes a few cases where the screen lost its state when the activity was recreated. - Rotating the device on the success or error screen keeps that screen. Previously it restarted the import from the Google page. - "Close" and the toolbar close button on the success or error screen keep working after the device is rotated. Previously they stopped responding. - Loading updates that the Google page sends after the import has finished no longer move the user away from the success or error screen. - Closing the screen with a result is delivered once the screen is ready to handle it, so it is not lost if it arrives while the activity is being recreated. ### Steps to test this PR _Successful import_ - [ ] Open the bookmarks screen. - [ ] Tap "Import" in the overflow menu. - [ ] Tap "Import From Google". - [ ] Sign in to Google if asked. - [ ] Wait for the import to finish. - [ ] Verify the success screen shows the number of imported bookmarks. - [ ] Tap "Close". - [ ] Verify the imported bookmarks appear in the "Imported from Chrome" folder. _Rotate on the result screen_ - [ ] Start an import from Google. - [ ] Wait for the success screen. - [ ] Rotate the device. - [ ] Verify the success screen is still shown. - [ ] Tap "Close". - [x] Verify the screen closes and the bookmarks screen is shown. _Cancel the import_ - [ ] Start an import from Google. - [ ] Tap the close button in the toolbar. - [ ] Tap "Cancel Import" in the "Cancel import?" dialog. - [x] Verify the screen closes and the bookmarks screen is shown. ### UI changes N/A --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Task/Issue URL: https://app.asana.com/1/137249556945/task/1219816597348861 Tech Design URL (if applicable): N/A API Proposals URL(s) (if applicable): N/A ### Description Adds the redesigned importing, success and error screens to the Google bookmarks import flow, behind the `autofillImportRedesign` feature flag, which now defaults to enabled on internal builds. > [!note] > On a fresh launch the activity reads `importRedesignFeature.self().isEnabled()` on the main thread and picks the design from it. The value is saved in `onSaveInstanceState` and restored from `savedInstanceState`, so the design stays the same after rotation and after process death, and a remote flag update only applies to the next import. The read is cheap enough for the main thread because toggle state is cached in memory by `CachedToggleStore`. `DuckDuckGoActivity.onCreate` already reads two `AppBrandDesignUpdateToggles` flags synchronously for every activity, and the permissions redesign reads its flag on the main thread to choose between screens. > > I considered reading the flag on a background thread in `ImportGoogleBookmarksViewModel` with a loading state until it arrives meant the toolbar, which is shown in one design and hidden in the other, either flickered or had to stay hidden until the flag was known, and the ViewModel had to know about the design. Delaying `setContentView` until a background read finished breaks restoration, because after rotation or process death the system restores the fragments before the flag arrives and they crash without their container, and fixing that needs the choice saved in state anyway. Reading the flag in `ImportFromGoogleLauncherImpl.launchBookmarksImport()`, which already runs off the main thread, would add more data to the request that the contract casts from the opaque `BookmarksImportRequest` and move a screen decision out of the screen. Letting the launcher choose between two separate activities through a resolver keeps the read off the main thread but needs a second activity and more wiring for a single flag. ### Steps to test this PR _Successful bookmarks import_ - [ ] Install an internal build. - [ ] Open the bookmarks screen. - [ ] Tap "Import" in the overflow menu. - [ ] Tap "Import From Google". - [ ] Sign in to Google if asked. - [ ] Wait for the importing screen. - [ ] Verify the importing screen is shown without a toolbar, with the info rows and the "Cancel" button at the bottom. - [ ] Wait for the import to finish. - [ ] Verify the success screen shows "Import complete!" and the number of imported bookmarks. - [ ] Tap "Done". - [x] Verify the imported bookmarks appear in the "Imported from Chrome" folder. _Design stays constant during an import_ - [ ] Start an import from Google. - [ ] Wait for the importing screen. - [ ] Rotate the device. - [x] Verify the redesigned importing screen is still shown. _Cancel from the importing screen_ - [ ] Start an import from Google. - [ ] Wait for the importing screen. - [ ] Tap "Cancel". - [ ] Tap "Cancel Import" in the "Cancel import?" dialog. - [x] Verify the screen closes and the bookmarks screen is shown. _Legacy bookmarks import_ - [ ] Disable `autofillImportRedesign` in the feature flag inventory. - [ ] Start an import from Google. - [ ] Verify the importing, success and error screens look as before, with the toolbar visible. ### UI changes | In progress | Success | Failure | | - | - | - | | <img width="540" alt="image" src="https://github.com/user-attachments/assets/aeb093dd-d9d4-4722-801b-b1d2c780eb98" /> | <img width="540" alt="image" src="https://github.com/user-attachments/assets/a87b8991-1a41-47fe-b3d7-00149ac04e9c" /> | <img width="540" alt="image" src="https://github.com/user-attachments/assets/a3d7427a-c669-4440-9582-1e38fdf09a8f" /> | --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Task/Issue URL: https://app.asana.com/1/137249556945/task/1219865357934686 Tech Design URL (if applicable): https://app.asana.com/1/137249556945/project/1216103556496795/task/1219704925344696 API Proposals URL(s) (if applicable): https://app.asana.com/1/137249556945/project/1216103556496795/task/1219808767736471 ### Description Adds a plugin point that lets other features show a promo card on the redesigned Google bookmarks import success screen, and a temporary dummy sync plugin to exercise it. This is the bookmarks counterpart of the passwords import plugin and will carry the Sync promotion and the cross-import promos from the designs. The new public API is `BookmarksImportScreenPromotionPlugin` in `autofill-api`. Like the passwords one, a plugin answers `isEligible` before the success screen shows and builds its card in `createView` whenever the screen renders. ### Steps to test this PR _Promo on the success screen_ - [ ] Install an internal build. - [ ] Enable the `autofillImportRedesign` feature flag. - [ ] Open Bookmarks. - [ ] Open the overflow menu. - [ ] Tap "Import". - [ ] Tap "Import From Google". - [ ] Complete the Google flow. - [ ] Verify the importing screen is shown until the import finishes. - [ ] Verify the "Import complete!" screen shows the number of imported bookmarks with the blue "Dummy plugin" card below it. - [ ] Switch the system dark mode setting while the screen is open. - [ ] Verify the "Dummy plugin" card is still shown. - [ ] Tap the "Dummy plugin" card. - [x] Verify the import screen closes. _Redesign disabled_ - [ ] Disable the `autofillImportRedesign` feature flag. - [ ] Import bookmarks from Google again. - [x] Verify the legacy result screen is shown without any promo card. ### UI changes N/A --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1216103556496795/task/1219573627842074 Tech Design URL (if applicable): N/A API Proposals URL(s) (if applicable): N/A ### Description Shows a Sync promo on the redesigned passwords import success screen, so users who just imported their passwords are nudged to turn on Sync & Backup and access them on their other devices. Promo card: - The card shows below the import results with a sync illustration, the title "Access your passwords across Mobile and Desktop." and a "Turn on Sync & Backup" button. - The card has no close button and no elevation. - Tapping "Turn on Sync & Backup" opens the Sync screen and closes the import sheet. When the promo shows: - At least one password was imported in this import. - Sync is available and the user is not syncing yet. - The `syncPromotion` and `syncPromotion.passwordsImport` flags are on. `passwordsImport` is enabled only in internal builds for now. - The promo only appears in the redesigned import UI. The legacy import UI is unchanged. The card is built with a new `PromoMessageView` in `sync-impl`. It follows the Message component design, with an optional close button and optional elevation, because the design system `MessageCta` always has both. Without elevation the card uses a translucent container fill, and with elevation it uses the secondary surface color. ### Steps to test this PR _Promo after importing passwords_ - [ ] Install an internal build. - [ ] Make sure Sync is turned off. - [ ] Open the Passwords screen. - [ ] Tap "Import Passwords". - [ ] Import passwords from a Google account that has passwords not yet saved in the app. - [ ] Verify the "Import complete!" screen shows the promo card below the results. - [ ] Tap "Turn on Sync & Backup". - [ ] Verify the import sheet closes. - [ ] Verify the Sync screen opens (I'll add direct sync action in a future PR). _Nothing imported_ - [ ] Import passwords from the same Google account again. - [ ] Verify the "Import complete!" screen shows no promo card. _Already syncing_ - [ ] Turn on Sync & Backup. - [ ] Import passwords from a Google account that has passwords not yet saved in the app. - [ ] Verify the "Import complete!" screen shows no promo card. ### UI changes | Light | Dark | | ------ | ----- | | <img width="540" alt="image" src="https://github.com/user-attachments/assets/f7c76699-a47c-42ab-ac50-a254f6154456" /> | <img width="540" alt="image" src="https://github.com/user-attachments/assets/64bc1a36-9731-475e-bd8e-44dc3572c205" /> | --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1216103556496795/task/1219573859775891 Tech Design URL (if applicable): N/A API Proposals URL(s) (if applicable): N/A ### Description Makes Sync promotions start a Sync flow directly instead of only opening the Sync & Backup settings screen. The Sync screen decides which flow to start based on whether this device is already synced, so every promotion behaves the same way. - When this device is not synced, the "Sync This Device" flow starts right away, the same as turning on the "Sync This Device" toggle. - When this device is already synced, the "Add Another Device" flow starts right away. - While the flow is starting, the Sync & Backup settings screen stays hidden, so it no longer flashes before the flow opens. It shows again when the user returns from the flow, or when a device authentication prompt has to appear over it. All current promotions are only shown when this device is not synced, so in practice they start "Sync This Device". The "Add Another Device" path is there for upcoming promotions aimed at users who already sync. The AI chat promo is unchanged, because it explicitly opens the "Sync With Another Device" flow. ### Steps to test this PR _Bookmarks screen banner_ - [ ] Install an internal build. - [ ] Make sure Sync is turned off. - [ ] Save at least one bookmark. - [ ] Open the Bookmarks screen. - [ ] Tap "Set Up Sync" on the Sync banner. - [ ] Verify the Sync & Backup settings screen does not flash before the flow opens. - [x] Verify the "Sync This Device" flow opens on top of the Sync & Backup settings screen. _Passwords screen banner_ - [ ] Save at least one password. - [ ] Open the Passwords screen. - [ ] Tap "Set Up Sync" on the Sync banner. - [x] Verify the "Sync This Device" flow opens on top of the Sync & Backup settings screen. _Add Bookmark sheet promo_ - [ ] Bookmark a website. - [ ] Tap "Sync Bookmarks Across Devices" in the Add Bookmark sheet. - [x] Verify the "Sync This Device" flow opens on top of the Sync & Backup settings screen. _Cancel the flow_ - [ ] Start the flow from any of the promotions above. - [ ] Close the "Sync This Device" flow without finishing it. - [ ] Verify the Sync & Backup settings screen shows with the "Sync This Device" toggle off. ### UI changes N/A --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1216103556496795/task/1219573627888415 Tech Design URL (if applicable): N/A API Proposals URL(s) (if applicable): N/A ### Description Shows a Sync promo on the redesigned bookmarks import success screen, so users who just imported their bookmarks are nudged to turn on Sync & Backup and access them on their other devices. It mirrors the promo on the passwords import success screen. Promo card: - The card shows below the import results with a sync illustration, the title "Access your bookmarks across Mobile and Desktop." and a "Turn on Sync & Backup" button. - The card has no close button and no elevation. - Tapping "Turn on Sync & Backup" starts the "Sync This Device" flow and closes the import screen. When the promo shows: - At least one bookmark was imported in this import. - Sync is available and the user is not syncing yet. - The `syncPromotion` and `syncPromotion.bookmarksImport` flags are on. `bookmarksImport` is enabled only in internal builds for now. - The promo only appears in the redesigned import UI. The legacy import UI is unchanged. ### Steps to test this PR _Promo after importing bookmarks_ - [ ] Install an internal build. - [ ] Make sure Sync is turned off. - [ ] Open the Bookmarks screen. - [ ] Tap "Import Bookmarks" in the overflow menu. - [ ] Tap "Import From Google". - [ ] Import bookmarks from a Google account that has bookmarks. - [ ] Verify the "Import complete!" screen shows the promo card below the results. - [ ] Tap "Turn on Sync & Backup". - [ ] Verify the import screen closes. - [x] Verify the "Sync This Device" flow opens. _Already syncing_ - [ ] Turn on Sync & Backup. - [ ] Import bookmarks from a Google account that has bookmarks. - [x] Verify the "Import complete!" screen shows no promo card. ### UI changes | Light | Dark | | ------ | ----- | | <img width="540" alt="image" src="https://github.com/user-attachments/assets/683c79ec-2a3a-44cc-9d22-7d5b63bd1842" /> | <img width="540" alt="image" src="https://github.com/user-attachments/assets/aa3e6775-5959-419e-8504-f5438ca338ea" /> | --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1216103556496795/task/1219899746084432 Tech Design URL (if applicable): N/A API Proposals URL(s) (if applicable): N/A ### Description Makes Sync keep track of the entry point that opened it for the whole setup flow, so a finished or abandoned setup can be attributed to the promotion that started it. - `SyncActivity` resolves the launch source it is opened with into a `SyncLaunchSource`. A source that is not a known promotion, or no source at all, resolves to `SyncLaunchSource.Other`. - The launch source is passed to every setup screen opened from Sync: "Sync This Device", "Sync With Another Device" with its "Scan QR" and "Enter Code" tabs, "Show QR Code", code processing, and "I Have a Recovery Code". - The "Sync Bookmarks Across Devices" promo in the bookmark added sheet now opens Sync with its own launch source instead of none. - Promotions build their launch source from `SyncLaunchSource`, so the values are defined in one place. ### Steps to test this PR _Setup_ - [ ] Install an internal debug build. - [ ] Run `adb logcat | grep --line-buffered -E "Pixel sent: .*(sync_promotion_(displayed|confirmed)|sync_setup_ended_(successful|failed|abandoned)|m_sync_signup_(direct|connect)|wide_sync-setup)"`. _Passwords import promo_ - [ ] Make sure Sync is off. - [ ] Import passwords from Google. - [ ] Verify `sync_promotion_displayed` is logged with `source=passwords_import`. - [ ] Tap "Turn on Sync & Backup". - [ ] Verify `sync_promotion_confirmed` is logged with `source=passwords_import`. - [ ] Tap "Sync This Device Only". - [ ] Complete the device authentication prompt. - [ ] Verify `m_sync_signup_direct` is logged with `source=promotion_passwords_import`. - [ ] Finish the flow. - [x] Verify `wide_sync-setup` is logged with `context.name=promotion_passwords_import`. _Bookmarks import promo_ - [ ] Make sure Sync is off. - [ ] Enable the `bookmarksImport` toggle of `SyncPromotionFeature`. - [ ] Import bookmarks from Google. - [ ] Verify `sync_promotion_displayed` is logged with `source=bookmarks_import`. - [ ] Tap "Turn on Sync & Backup". - [ ] Verify `sync_promotion_confirmed` is logged with `source=bookmarks_import`. - [ ] Tap "Sync This Device Only". - [ ] Complete the device authentication prompt. - [ ] Verify `m_sync_signup_direct` is logged with `source=promotion_bookmarks_import`. - [ ] Verify `wide_sync-setup` is logged with `context.name=promotion_bookmarks_import`. _Pairing from an import promo_ - [ ] Make sure Sync is off. - [ ] Import passwords from Google. - [ ] Tap "Turn on Sync & Backup". - [ ] Tap "Sync With Another Device". - [ ] Sync with another device. - [ ] Verify `sync_setup_ended_successful` is logged with `launch_source=promotion_passwords_import`. _Pairing from Sync settings_ - [ ] Close and open "Sync & Backup". - [ ] Tap "Sync With Another Device". - [ ] Sync with another device. - [ ] Verify `sync_setup_ended_successful` is logged with `launch_source=other`. _Abandoned setup_ - [ ] Make sure Sync is off. - [ ] Open "Sync & Backup". - [ ] Tap "Sync With Another Device". - [ ] Close the scanner. - [ ] Verify `sync_setup_ended_abandoned` is logged with `launch_source=other`. _Bookmark added sheet promo_ - [ ] Make sure Sync is off. - [ ] Bookmark a page. - [ ] Tap "Sync Bookmarks Across Devices" in the bookmark added sheet. - [ ] Tap "Sync This Device Only". - [ ] Complete the device authentication prompt. - [ ] Verify `m_sync_signup_direct` is logged with `source=promotion_bookmark_added_dialog`. ### UI changes N/A(Upload before screenshot)|(Upload after screenshot)| --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1142021229838617/task/1219899197827051 Tech Design URL (if applicable): N/A API Proposals URL(s) (if applicable): N/A ### Description Updates the redesigned passwords and bookmarks import screens so the Sync promo shown after a successful import is the main call to action. When the promo is on screen, the "Done" button is shown as a secondary button instead of a primary one. ### Steps to test this PR QA optional. ### UI changes | Passwords in progress | Passwords no promo | Passwords promo | | - | - | - | | <img width="540" alt="image" src="https://github.com/user-attachments/assets/e7e97ba1-efa5-47bc-b023-f4032c4ef5f4" /> | <img width="540" alt="image" src="https://github.com/user-attachments/assets/e0e35cfb-49e6-44fb-9b92-5cb9fe81e10d" /> | <img width="540" alt="image" src="https://github.com/user-attachments/assets/2eaa536c-2bf5-4466-88a9-b5c922a70459" /> | | Bookmarks no promo | Bookmarks promo | | - | - | | <img width="540" alt="image" src="https://github.com/user-attachments/assets/d65fb652-1671-4611-b5e4-9d456ab36ec0" /> | <img width="540" alt="image" src="https://github.com/user-attachments/assets/aabf098e-88fc-41e0-8d6d-2d5f2e78674a" /> | --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1211654189969294/task/1220455198841234?focus=true Tech Design URL (if applicable): API Proposals URL(s) (if applicable): ### Description Opening the tab manager with the address bar focused collapsed the input back to the address bar just before the tab manager appeared. The input is now dismissed once the tab manager covers the browser (when the tab stops), so the collapse happens off screen. If the tab manager closes before covering the browser, it's dismissed when the tab resumes instead. Duck.ai mode is unchanged ### Steps to test this PR _Focused input_ - [ ] Open a new tab, then tap the tabs button next to the focused input: the input doesn't collapse before the tab manager opens - [ ] Press back: the New Tab Page shows as before, focused - [ ] Repeat, then pick a tab with a page: the page shows with the address bar - [ ] Repeat, then tap +: the new tab opens as before ### UI changes | Before | After | | ------ | ----- | | https://github.com/user-attachments/assets/a2ac7683-1c3f-46d4-91e7-52725105eb90 | https://github.com/user-attachments/assets/377c1c14-46c2-4df4-936d-0be09f1fba06 |
Task/Issue URL: https://app.asana.com/1/137249556945/project/1212810093780571/task/1219721209616437?focus=true Tech Design URL (if applicable): https://app.asana.com/1/137249556945/project/1212810093780571/task/1220041118787533?focus=true API Proposals URL(s) (if applicable): ### Description This is part 1/5 of the new PR transitions This PR adds the engine on its own, in `/browser/tabs/transition/`. Nothing calls it yet; the browser wiring, tab manager, intent entry points and internal settings follow. - **Flag:** `TabManagerFeatureFlags.newTabTransitions()`, off by default. `NewTabTransitionConfigProvider` returns null while it's off, and every entry point starts from that. - **Picture:** `Window.capturePicture()` copies the window with `PixelCopy`, waiting at most 50 ms; on failure there's no picture and no transition. Tab appears instantly. - **Handing over:** `PendingNewTabTransitions` holds one new tab's picture and motion from the tap until that tab shows (for 3s at most). It's keyed by tab id, so only that tab can animate. - **Coordinating:** `NewTabTransitionCoordinator` receives the browser's signals (tab showing, tab list changed, tab registered, browser started, resumed and stopped) and starts, covers, or cancels: - When the list gets the new tab before the selection does, the picture covers the pager until the selection arrives. - If a tab is listed before its picture is ready, it waits for it. It only apply a cover if it's still hidden when the picture arrives. - Any other selection ends a running cover, transition, or wait. - **Playing:** `NewTabTransitionPlayer` shows the picture in a cover view behind the tab container, then grows the container in over it. - The clock advances at most 20 ms per frame, so a stall while the new tab builds pauses the motion instead of skipping it. - It follows the system animator duration scale; with animations off, including Battery Saver, it ends at once. - **Motion values:** `NewTabTransitionConfig` and `NewTabMotionFrames` hold the timing, scales, offsets and dim, with one profile per origin (browser, tab manager). - **Running state:** `NewTabTransitionState` exposes whether a transition runs and which tab is shown. `TabPagerSwipeGate` pauses pager swiping while one runs. ### Steps to test this PR _No UX change yet_ - [ ] The app builds and behaves as before: nothing calls the engine yet - [ ] Unit tests succeed ### UI changes No UI changes
Task/Issue URL: https://app.asana.com/1/137249556945/task/1220221287444491 Tech Design URL (if applicable): https://app.asana.com/1/137249556945/project/1212810093780571/task/1220041118787533?focus=true API Proposals URL(s) (if applicable): ### Description The second of five PRs for new tab transitions. Stacked on the transition engine PR. This PR wires the engine (introduced in #10365) into the browser, so new tabs opened from inside a tab grow in. - `BrowserTabFragment`: the new tab entry points ask for the transition. - `BrowserActivity`: takes the picture at the tap, hands the transition to the new tab once it exists, and passes the engine the browser's tab and lifecycle signals, with swiping tabs on and off. - `activity_browser.xml`: the cover view that shows the picture. ### Steps to test this PR Enable `tabManager` → `newTabTransitions` in the feature flag inventory. _Swiping tabs on_ - [ ] Browser menu → New tab: the new tab grows in over the page from the right side, and old one dims and slides away - [ ] Long-press on the tabs button: same transition - [ ] In a Duck.ai tab, click on + menu → Search (Or "New tab" when `duckAiPlusMenuUpdate` feature flag is off): same transition - [ ] Long-press a link on a website → Open in new tab: same transition - [ ] Any link that opens a new tab (`target="_blank"`): same transition - [ ] Turn Animations off (In your phone system settings - developer options or Battery Saver depending on your phone model ): the new tab shows at once without animation _Swiping tabs off_ (disable `swipingTabs` in the feature flag inventory) - [ ] Repeat all the steps above _Flag off_ - [ ] New tabs open exactly as before ### UI changes https://github.com/user-attachments/assets/1d5c3a40-fbe0-43f3-b4b0-c41f29c51517
Task/Issue URL: https://app.asana.com/1/137249556945/project/72649045549333/task/1219721209616438?focus=true Tech Design URL (if applicable): https://app.asana.com/1/137249556945/project/1212810093780571/task/1220041118787533?focus=true API Proposals URL(s) (if applicable): None ### Description The third of five PRs for new tab transitions. Stacked on the browser wiring PR. This PR connects the tab manager to the transition engine, so a new tab opened from it grows in from below over a picture of the tab manager. - `TabSwitcherNewTabTransition`: takes the picture at the tap and hands it to the browser when the tab manager closes. Only the latest tap can hand over, since the tab operations can finish in any order. - `TabSwitcherViewModel`: passes each tap's request id with the tab operation and closes with `CloseToNewTab`. A reused tab (a blank New Tab Page or an empty Duck.ai chat) closes without the transition. - `TabSwitcherActivity`: starts a request on the new tab and Duck.ai taps, and finishes without a window animation when the new tab grows in. **Known limitation:** after switching between Regular and Fire while in the tab manager, or rotating the device a new tab shows without the transition. The browser rebuilds itself for the new mode after you switch back to it (becuase it was in the background), so the transition is skipped. This only happen when we switch, subsequent tabs would play the animation. ### Steps to test this PR Enable `tabManager` → `newTabTransitions` in the feature flag inventory. _Swiping tabs on_ - [ ] Open the tab manager, and close any already opened NTP (so we don't switch to it). - [ ] Tap + to open a new tab: the new tab grows in from below over the tab manager - [ ] Same with + in the bottom bar (bottom or split address bar) - [ ] In Tab manager → Duck.ai button → New Chat, or picking a recent chat: same transition. - [ ] With a blank New Tab Page already open, tap +: the tab manager closes to it without the transition - [ ] Tap + twice quickly: the new tab grows in, once - [ ] Tap an existing tab: the tab manager closes as before - [ ] Switch between Regular and Fire in the tab manager, then tap +: the new tab shows without the transition, nothing left on screen _Swiping tabs off_ (disable `swipingTabs` in the feature flag inventory) - [ ] Repeat all the steps above. Exception: with a blank New Tab Page already open, + opens a new tab with the transition instead of reusing it _Flag off_ - [ ] The tab manager closes as before in all cases ### UI changes https://github.com/user-attachments/assets/32ec94f0-99b0-496f-b2dd-f9a242b069ac
Task/Issue URL: https://app.asana.com/1/137249556945/project/72649045549333/task/1220221287444492?focus=true Tech Design URL (if applicable): https://app.asana.com/1/137249556945/project/1212810093780571/task/1220041118787533?focus=true API Proposals URL(s) (if applicable): None ### Description The fourth of five PRs for new tab transitions. Stacked on the tab manager PR. This PR makes new tabs opened through an intent animate in when the browser is on screen. This covers Duck.ai's own entries (New Chat, Image, a chat from the menu) and links opened in a new tab by an intent. - `BrowserActivity.intent()`: marks in-app launches, so a launch from a widget or from text selected in another app doesn't grow in, even with the browser beside it in split screen. - `BrowserActivity.processIntent()`: the Duck.ai and open-in-new-tab branches take the picture and register the new tab's transition. Nothing animations in unless the browser is on screen, or for an intent held back until data clearing finished. A link needs the tab that asked to be on screen; a Duck.ai request carries no tab, so the tab on screen stands in. ### Steps to test this PR Enable `tabManager` → `newTabTransitions` in the feature flag inventory. _Swiping tabs on_ - [ ] On the New Tab Page, Duck.ai icon in the address bar → New Chat: the chat grows in from the right - [ ] In a Duck.ai chat, + → Image: same transition - [ ] On the New Tab Page, Duck.ai menu → a recent chat: same transition - [ ] On a page, type duck.ai in the address bar → New Chat: same transition - [ ] On a page, open the contextual Duck.ai sheet and open its chat in a new tab: same transition, over a picture of the page with the sheet _Swiping tabs off_ (disable `swipingTabs` in the feature flag inventory) - [ ] Repeat all the steps above _Flag off_ - [ ] All the above open as before ### UI changes https://github.com/user-attachments/assets/fc6e45f5-0f97-45c2-9353-9085631d4ee8
Task/Issue URL: https://app.asana.com/1/137249556945/project/72649045549333/task/1219721209616436?focus=true Tech Design URL (if applicable): https://app.asana.com/1/137249556945/project/1212810093780571/task/1220041118787533?focus=true API Proposals URL(s) (if applicable): None ### Description The last of five PRs for new tab transitions. Stacked on the intent entry points PR. This PR adds a screen to the internal build's developer settings for tuning the transition on a device. Internal builds only. - `NewTabTransitionDevSettingsActivity`: shows whether the flag is on, picks where the new tab grows in from (browser and tab manager separately), and toggles slow motion and reset. - `NewTabTransitionOriginDevSettingsActivity`: sliders for one origin (start size, offset, scale back, dim, slide left, duration). - `NewTabTransitionDevStore`: saves the settings, keeping saved values within the sliders' ranges. - `InternalNewTabTransitionConfigProvider`: replaces the default config with the saved one in internal builds. ### Steps to test this PR Internal build. Enable `tabManager` → `newTabTransitions` in the feature flag inventory. - [ ] Settings → Developer settings → New Tab Transitions: the screen opens and shows the flag as On - [ ] Turn on slow motion, open a new tab from the browser menu: the transition plays slowly - [ ] Change the browser origin to Below center, open a new tab: it grows in from below - [ ] Open an origin's animation page, move each slider, open a new tab: the changes apply - [ ] Reset: the defaults are back - [ ] Turn the flag off: the screen says the settings apply once it's on, and new tabs open as before ### UI changes https://github.com/user-attachments/assets/8dd696f3-29ee-436f-a985-0a09e314d495
Task/Issue URL: https://app.asana.com/1/137249556945/project/414730916066338/task/1220457634550540 - Automated content scope scripts dependency update This PR updates the content scope scripts dependency to the latest available version and copies the necessary files. Tests will only run if something has changed in the `node_modules/@duckduckgo/content-scope-scripts` folder. If only the package version has changed, there is no need to run the tests. If tests have failed, see https://app.asana.com/0/1202561462274611/1203986899650836/f for further information on what to do next. _`content-scope-scripts` folder update_ - [x] All tests must pass - [x] Privacy tests must pass _Only `content-scope-scripts` package update_ - [x] All tests must pass - [x] Privacy tests do not need to run Co-authored-by: daxmobile <daxmobile@users.noreply.github.com>
Task/Issue URL: https://app.asana.com/1/137249556945/project/414730916066338/task/1220477803409733 - Automated content scope scripts dependency update This PR updates the content scope scripts dependency to the latest available version and copies the necessary files. Tests will only run if something has changed in the `node_modules/@duckduckgo/content-scope-scripts` folder. If only the package version has changed, there is no need to run the tests. If tests have failed, see https://app.asana.com/0/1202561462274611/1203986899650836/f for further information on what to do next. _`content-scope-scripts` folder update_ - [ ] All tests must pass - [ ] Privacy tests must pass _Only `content-scope-scripts` package update_ - [ ] All tests must pass - [ ] Privacy tests do not need to run Co-authored-by: daxmobile <daxmobile@users.noreply.github.com>
Task/Issue URL: https://app.asana.com/1/137249556945/task/1220481274722966 Autoconsent Release: https://github.com/duckduckgo/autoconsent/releases/tag/v18.3.0 ## Description Updates Autoconsent to version [v18.3.0](https://github.com/duckduckgo/autoconsent/releases/tag/v18.3.0). ### Autoconsent v18.3.0 release notes See release notes [here](https://github.com/duckduckgo/autoconsent/blob/v18.3.0/CHANGELOG.md) ## Steps to test This release has been tested during Autoconsent development. You can check the release notes for more information. 1. Make sure that there's no unexpected failures in CI checks 2. (optional) smoke test some of the sites mentioned in the release notes 3. If there are problems, reach out to a CPM DRI Co-authored-by: sammacbeth <248111+sammacbeth@users.noreply.github.com>
) Task/Issue URL: https://app.asana.com/1/137249556945/task/1220271890253683?focus=true Tech Design URL (if applicable): API Proposals URL(s) (if applicable): ### Description ### Steps to test this PR _Pre steps_ - [x] Apply patch on https://app.asana.com/1/137249556945/project/1209991789468715/task/1210448620621729?focus=true - [x] Follow steps in https://app.asana.com/1/137249556945/task/1219969199295542 to get a staging ROW stripe account _Withdrawal date_ - [x] Install from branch - [x] Go to Settings > I have a Subscription - [x] Add staging account you purchased - [x] Go to Subscription Settings - [x] Tap on Withdrawal option - [x] Withdrawal dialog will be displayed - [x] Tap on 'Confirm Withdrawal' - [x] Verify the withdrawal date in the not subscribed screen looks correct ### No UI changes --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1198194956794324/task/1220386721157988 Tech Design URL (if applicable): API Proposals URL(s) (if applicable): ### Description Removes NetP's pcap recording. It never captured anything: `WgVpnNetworkStack` always passed `pcapConfig = null`, so the internal settings toggle had no effect. ```diff WgVpnNetworkStack - wgProtocol.startWg(tunFd, config, pcapConfig = null) + wgProtocol.startWg(tunFd, config) RealWgProtocol.startWg safelyStartWg(tunFd, config) - safelyConfigurePcap(pcapConfig) // GoBackend.wgPcap, never reached ``` Removed: - `GoBackend.wgPcap` (JNI declaration), `PcapConfig`, `NetPDefaultConfigProvider.pcapConfig()` - Internal: `enablePcapRecording` toggle, `ExportPcapContract`, the "PCAP" section in NetP internal settings (layout, strings, export/delete/share handlers), and the lint baseline entries for that code ### Steps to test this PR _NetP_ - [ ] Internal build: open NetP internal settings, the PCAP section is gone and the screen works - [ ] Enable NetP, it connects and traffic flows - [ ] Disable / re-enable NetP, no crash ### UI changes | Before | After | | ------ | ----- | | PCAP section in NetP internal settings | Section removed |
Task/Issue URL: https://app.asana.com/1/137249556945/project/1212810093780571/task/1220473431098918?focus=true Tech Design URL (if applicable): API Proposals URL(s) (if applicable): ### Description After #10425 With the selected-tab debounce removed, the pager moves to a tab by its ID instead of its index. That path didn't tell the transition that the new tab is showing, so the cover went up and timed out with no grow-in. This makes the ID path call `onTabShowing` the same way the index path does, and adds a test for it. ### Steps to test this PR _New tab transition with the debounce removed_ - [ ] Fresh install of an internal build - [ ] Enable tabManager newTabTransitions in the feature flags inventory, then restart the app - [ ] Open a new tab from the browser menu: it animates - [ ] Open a new tab from the tab manager: it animates - [ ] Open a link in a new tab from a page: it animates ### UI changes No UI Changes
Task/Issue URL: https://app.asana.com/1/137249556945/project/1212810093780571/task/1220460098860759?focus=true Tech Design URL (if applicable): API Proposals URL(s) (if applicable): ### Description Turns on new tab transitions by default in internal builds. The newTabTransitions flag now defaults to INTERNAL instead of FALSE. Play builds still have it off, and remote config can still turn it off. ### UI changes No UI changes
Task/Issue URL: https://app.asana.com/1/137249556945/project/38424471409662/task/1220303775212997?focus=true Tech Design URL (if applicable): API Proposals URL(s) (if applicable): ### Description - Moved the new chat hint strings (newChatHintDocuments, newChatHintPhoto, newChatHintImage) from donottranslate.xml to strings-duckchat.xml so Smartling translates them. - Changed the photo hint copy to the plural "Ask about photos" to match "Analyze files", following copy review. ### Steps to test this PR QA-optional --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: smartling <smartling@host-vm>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1212227266948491/task/1220177860617495 Tech Design URL (if applicable): API Proposals URL(s) (if applicable): None ### Description Adds the `sponsorBlock` remote feature to `ad-blocking-impl`, mirroring the structure added for Apple in duckduckgo/privacy-configuration#6347: - `SponsorBlockFeature`: `self()` and the `mvpEnabled` subfeature, both defaulting to `internal`. - `SponsorBlockSettingsProvider`: reads `categories` and `actionTypes` from the feature settings, falling back to `["sponsor"]` and `["skip"]` when the settings are missing, malformed or lack a key. Other keys in the settings (such as the selectors read by C-S-S) are ignored. Nothing consumes these yet. n/a ### UI changes n/a --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Task/Issue URL: https://app.asana.com/1/137249556945/project/72649045549333/task/1220177860617500 Tech Design URL (if applicable): https://app.asana.com/1/137249556945/project/481882893211075/task/1219201061224389?focus=true API Proposals URL(s) (if applicable): None ### Description Adds the native side of the `sponsorBlock` C-S-S messaging: - `SponsorBlockContentScopeJsMessageHandler` handles `getSegments` and `getConfig` and responds directly from the handler. - `getSegments` validates `videoID` (11 chars, `[A-Za-z0-9_-]`) before looking it up and returns segments sorted by start. It returns an empty list if the ID is invalid, the feature is disabled or the lookup fails. - `getConfig` returns `enabled` (from `mvpEnabled`), `skipMode` (`manual` for now) and the contract `version`. - Allowed domains are `youtube.com`, `m.youtube.com` and the Duck Player embed host. The messaging layer checks them against the main frame's host. - `SponsorBlockSegmentsProvider` with an empty implementation. The real provider will be plugged in later. - `@ContentScopePrivacyFeature("sponsorBlock")`, so the remote config for the feature is passed to C-S-S. ### Steps to test this PR - [ ] Test along with next one ### UI changes n/a --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1212227266948491/task/1220201780433055 Tech Design URL (if applicable): API Proposals URL(s) (if applicable): None ### Description Replaces the empty `SponsorBlockSegmentsProvider` with `RealSponsorBlockSegmentsProvider`: - Sends only the first 4 hex characters of the video ID's SHA-256 to the SponsorBlock proxy (`SponsorBlockService`, through the `api` Retrofit client, which sends no cookies). The hash uses the existing `String.sha256` from `common-utils`. - Keeps only the requested video from the response, then only the categories and action types allowed by the `sponsorBlock` remote config settings. - Treats segments as untrusted: drops any without exactly two finite numbers, with a negative start, with an end not after the start, or without a category or action type. A non-positive `videoDuration` becomes `null`. - Resolves to an empty list on any failure. A 404 is cached as "no segments"; other failures aren't cached, so the next call retries. - Keeps a 50-entry LRU cache of each video's valid segments and applies the remote config filter on every call, so a config change applies without refetching. Concurrent lookups for the same video share one request, which runs in the app scope so one caller cancelling doesn't cancel it for the others. - Adds `SponsorBlockHashPrefixGate`, which decides whether a lookup needs the network. It always answers yes until the local hash prefix list is available. - Never logs the video ID or the hash. ### Steps to test this PR _Pre-requisites_ - [x] Apply patch 1 below - [x] Run `npm i github:duckduckgo/content-scope-scripts#pr-releases/cris/sponsorblock-android` _Segments lookup_ - [x] Install `internalDebug` - [x] Override remote config URL with https://duckduckgo.github.io/privacy-configuration/pr-6373/v5/android-config.json - [x] Open a YouTube video with sponsored content - [x] Check sponsored segment is highlighted in green in the progress bar - [x] The skip button shows during the sponsor segment - [x] Test it also works on Duck Player, and fullscreen mode - [x] Disable Sponsorblock > mvpEnabled flag - [x] Go back to the video, check no segments are hidhlighted now ### Patches _Patch 1_ ``` Subject: [PATCH] Embedded privacy-config --- Index: privacy-config/privacy-config-impl/src/main/res/raw/privacy_config.json IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/privacy-config/privacy-config-impl/src/main/res/raw/privacy_config.json b/privacy-config/privacy-config-impl/src/main/res/raw/privacy_config.json --- a/privacy-config/privacy-config-impl/src/main/res/raw/privacy_config.json (revision 4d7a8a5) +++ b/privacy-config/privacy-config-impl/src/main/res/raw/privacy_config.json (date 1793967627046) @@ -1,6 +1,6 @@ { "readme": "https://github.com/duckduckgo/privacy-configuration", - "version": 1652275711516, + "version": 9999999999999, "features": { "adBlockingExtension": { "exceptions": [], @@ -1179,6 +1179,15 @@ ] } } + }, + "sponsorBlock": { + "state": "internal", + "exceptions": [], + "features": { + "mvpEnabled": { + "state": "internal" + } + } } }, "unprotectedTemporary": [] ``` ### UI changes | Before | After | | ------ | ----- | | No UI changes | No UI changes | 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Task/Issue URL: https://app.asana.com/1/137249556945/project/72649045549333/task/1220201946095698 Tech Design URL (if applicable): API Proposals URL(s) (if applicable): None ### Description Adds the user setting for SponsorBlock and sends it to C-S-S: - Adds a "Skip sponsored content" row to the Ad Blocking settings, above Duck Player, with a footer explaining what it does. Tapping it opens a radio dialog with Always Ask, Always Skip and Off. The row only shows while `sponsorBlock.mvpEnabled` is on. Strings live in `donottranslate.xml` for now. - Stores the mode in the ad blocking DataStore (`SponsorBlockUserPreferences`). It defaults to Always Ask. - Maps the mode into the config sent to C-S-S: Always Ask → `skipMode: "manual"`, Always Skip → `skipMode: "auto"`, Off → `enabled: false`. - `getSegments` reads the stored mode before any lookup, so Off never fetches segments. `config()` can't suspend, so it uses a cached copy of the mode, which holds the default until the first DataStore read. - The change reaches open YouTube tabs through the existing `onConfigChanged` push on tab resume. ### Steps to test this PR _Pre-requisites_ - [x] Same as #10384: apply its embedded privacy-config patch and run `npm i github:duckduckgo/content-scope-scripts#pr-releases/cris/sponsorblock-android` - [x] Install `internalDebug` _Settings_ - [x] Open Settings > Ad Blocking - [x] Check "Skip sponsored content" shows above Duck Player with "Always Ask" and the footer below it - [x] Tap it, pick each option, and check the row shows the new value - [x] Disable Sponsorblock > mvpEnabled, go back to Ad Blocking settings, and check the row and footer are hidden _Always Ask_ - [x] Open a YouTube video with sponsored content - [x] Check the segment is highlighted and the skip button shows during it _Always Skip_ - [x] Switch to Always Skip and go back to the video tab - [x] Check the sponsored segment is skipped without tapping anything _Off_ - [x] Switch to Off and go back to the video tab - [x] Check no segments are highlighted and nothing is skipped ### UI changes | Before | After | | ------ | ----- | |<img width="575" height="1284" alt="Image" src="https://github.com/user-attachments/assets/4a49fec7-26ad-4258-bccf-ca6870a73aaa" />|<img width="573" height="1290" alt="Image" src="https://github.com/user-attachments/assets/8673cd2e-22fa-49fd-a284-77d9535c096a" />| --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Task/Issue URL: https://app.asana.com/1/137249556945/task/1220357840317513 Tech Design URL (if applicable): API Proposals URL(s) (if applicable): None ### Description Sends each segment's SponsorBlock `UUID` to C-S-S in `getSegments`. C-S-S remembers the segment the user seeked into by its `uuid` and shows the skip button for it instead of auto-skipping. Without the `uuid` that check never matched, so in Always Skip mode, dragging the seekbar back into a skipped segment skipped it again. - `SegmentResponse` reads `UUID` from the response. It uses `@field:Json` because the app's Moshi has no Kotlin adapter, so a plain `@Json` on the constructor parameter isn't picked up. - `SponsorBlockSegment` carries `uuid`, and segments without one are dropped, since the C-S-S `getSegments` response schema requires it. ### Steps to test this PR _Pre-requisites_ - [x] `npm i github:duckduckgo/content-scope-scripts#pr-releases/cris/sponsorblock-android` - [x] Install `internalDebug` _Seeking back in Always Skip_ - [x] Set Settings > Ad Blocking > Skip sponsored content to Always Skip - [x] Open a YouTube video with sponsored content and let it play into a segment - [x] Check the segment is skipped automatically - [x] Drag the seekbar back into the middle of that segment - [x] Check playback continues inside the segment and the skip button shows - [x] Let it play past the segment, then into the next one, and check that one is skipped automatically Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1174433894299346/task/1220122635402769 Tech Design URL (if applicable): API Proposals URL(s) (if applicable): ### Description The empty Duck.ai Chats screen labelled its button "Open Duck.ai", although tapping it starts a new chat. The button now reuses the already translated `browserMenuNewChat` string, so no new translations are needed, and the unused string is removed. The purple illustration is unchanged; design is tracking that separately. ### Steps to test this PR _Empty Chats screen_ - [ ] Install a build with no saved Duck.ai chats - [ ] Open the All Chats menu - [ ] Check the button under "No chats yet" reads "New Chat" - [ ] Tap it and check a new Duck.ai chat opens ### UI changes | Before | After | | ------ | ----- | !(Upload before screenshot)|(Upload after screenshot)| Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…terceptor (#10508) Task/Issue URL: https://app.asana.com/1/137249556945/task/1219784082433694 Tech Design URL (if applicable): https://app.asana.com/1/137249556945/project/1211850753229323/task/1217926121070145 API Proposals URL(s) (if applicable): None ### Description Extracts the request exemption checks (trusted sites, request blocklist and its allowlists) and the request rewrite decision (GPC headers, user agent) out of `WebViewRequestInterceptor` into two classes in `request-interception-impl`, each with its own unit tests. Behaviour is unchanged; the interceptor keeps the WebView side effects and drops from 20 to 16 dependencies. ### Steps to test this PR QA optional, these steps are covered by CI. _Behaviour unchanged_ - [ ] Run `./gradlew :app:testPlayDebugUnitTest` → confirm `WebViewRequestInterceptorTest` (55), `DomainsReferenceTest` (122), `SurrogatesReferenceTest` (12) and `RequestBlocklistReferenceTest` (41) pass with only their constructor wiring changed - [ ] Run `./gradlew :request-interception-impl:testDebugUnitTest` → confirm `RequestExemptionsTest`, `RequestRewriteDecisionTest` and `TrustedSitesTest` pass _Smoke test_ - [ ] Install the app, open a site with trackers → confirm trackers are still blocked in the privacy dashboard - [ ] Add the site to the allowlist (disable protections) and reload → confirm trackers are no longer blocked - [ ] Toggle "Request desktop site" on any page → confirm the page reloads with the desktop version ### UI changes No user-facing UI changes. --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Task/Issue URL: https://app.asana.com/1/137249556945/task/1220098860376945?focus=true Tech Design URL (if applicable): API Proposals URL(s) (if applicable): ### Description Fix the failing validation for the subscription onboarding step pixels ### Steps to test this PR - [ ] N/A ### No UI changes --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Task/Issue URL: https://app.asana.com/1/137249556945/task/1220384931554018 Tech Design URL (if applicable): https://app.asana.com/1/137249556945/task/1220218515935564 API Proposals URL(s) (if applicable): https://app.asana.com/1/137249556945/project/1212087397361015/task/1220342074337452 ### Description A migrated install (one whose data arrived through a device-to-device transfer) now gets a short flow on first start: intro, notification permission, a default browser prompt and "You're all set". - The default browser step shows only when DuckDuckGo was the default browser on the previous phone, so it is these users' one prompt while their staged default browser prompts stay stopped. - `DeviceMigration` gains `isMigrationFlowPending()`, `markMigrationFlowCompleted()` and `sourceDeviceWasDefaultBrowser()`. The flow is pending only for installs first detected by a version that has it, so an install migrated earlier never gets it late. - `PendingOnboardingStarter` picks the migration flow or the new-user plan. A user who was still new on the previous phone continues new-user onboarding afterwards, minus the steps the flow already showed. A returning user gets no further onboarding. - New pixels: `m_device_migration_all_set_prompt_shown` and `m_device_migration_all_set_prompt_button_clicked`. Transfers are only turned on by the top PR of this stack, so on its own this PR changes nothing for users. ### Steps to test this PR Use a build from the top of the stack (`feature/aibrahim/device-migration-transfer`), which turns transfers on, and two phones. On the new phone, transfer apps and data during setup (Google's "Copy apps & data", or Smart Switch on Samsung) and don't open DuckDuckGo until the transfer has finished. _Migrated install, DuckDuckGo was the default browser_ - [ ] On the old phone, set DuckDuckGo as the default browser and open the app once - [ ] Transfer to the new phone and open the app - [ ] The intro shows, then the notification permission step (if not already granted), the default browser step and "You're all set" - [ ] `m_device_migration_all_set_prompt_shown` fires, and `m_device_migration_all_set_prompt_button_clicked` fires on tapping its button _Migrated install, DuckDuckGo was not the default browser_ - [ ] Same as above, with another default browser on the old phone - [ ] The flow skips the default browser step _Old phone was still a new user_ - [ ] On the old phone, reinstall the app and leave onboarding unfinished - [ ] After the transfer, the migration flow shows, then new-user onboarding continues without repeating the steps the flow showed _Not migrated_ - [ ] Fresh install: onboarding is unchanged ### UI changes | Before | After | | ------ | ----- | !(Upload before screenshot)|(Upload after screenshot)| --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Task/Issue URL: https://app.asana.com/1/137249556945/task/1220384931554018 Tech Design URL (if applicable): https://app.asana.com/1/137249556945/task/1220218515935564 API Proposals URL(s) (if applicable): https://app.asana.com/1/137249556945/project/1212087397361015/task/1220342074337452 ### Description On a migrated install without Sync, the new tab page suggests turning Sync on once the migration flow has ended, until the user dismisses it or turns Sync on. - sync-api: `SyncAfterDeviceTransferPrompt`, and `SyncActivityStartSetupFlow` to open Sync setup directly. Setup leads with "Sync With Another Device", so a user whose previous account wasn't restored can join it again. - `DeviceMigration` gains `previousDeviceHadSync()`. True when it found an account to restore. When nothing was found to restore, it is false, so that user isn't left without Sync and without the suggestion. - The Sync stamp for a future transfer is also refreshed when the app goes to the background, so a backup taken right after turning Sync on carries it. - New pixels: `m_device_migration_sync_prompt_shown` and `m_device_migration_sync_prompt_clicked`. ### Steps to test this PR Use a build from the top of the stack (`feature/aibrahim/device-migration-transfer`) and two phones, transferring as described in the PR below this one. _Previous phone without Sync_ - [ ] After the transfer, finish the migration flow - [ ] The new tab page shows the "Oh before I forget…" suggestion and `m_device_migration_sync_prompt_shown` fires - [ ] "Turn on Sync" opens Sync setup and `m_device_migration_sync_prompt_clicked` fires - [x] Once dismissed, or once Sync is on, the suggestion doesn't show again _Previous phone with Sync, account restored_ - [ ] Transfer with the Google account included ("Accounts" ticked in Smart Switch) - [x] Sync is restored silently and the suggestion doesn't show _Previous phone with Sync, nothing restored_ - [ ] Transfer without the Google account, so the restore key doesn't arrive - [ ] Sync stays off and the suggestion shows _Not migrated_ - [ ] Fresh install: the suggestion never shows ### UI changes | Before | After | | ------ | ----- | !(Upload before screenshot)|(Upload after screenshot)| --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Task/Issue URL: https://app.asana.com/1/137249556945/project/72649045549333/task/1220342074337450 Tech Design URL (if applicable): https://app.asana.com/1/137249556945/task/1220218515935564 API Proposals URL(s) (if applicable): None ### Description Turns on device-to-device transfer of app data. - Backup is enabled with our own `DeviceMigrationBackupAgent` and `fullBackupOnly`. With the `deviceMigration` remote flag off, the agent sends nothing. - Cloud backup still excludes every domain, so nothing goes to Google Drive. - A device transfer sends only the stores listed in the rules: tabs and other app data in `app.db`, history, site permissions and preferences, Duck.ai chats, downloads, dismissed remote messages, settings, WebView data and the migration's own stamps. A store added later stays on the old phone until it is listed. - `BackupRulesTest` checks that cloud backup excludes everything, that both rules files list the same stores, that no whole domain is included, and that no credential, keystore-encrypted or telemetry store is. ### Steps to test this PR Two phones are needed: Android's command-line tools can make a device-transfer backup but can't restore one. On the new phone, transfer apps and data during setup (Google's "Copy apps & data", or Smart Switch on Samsung) and don't open DuckDuckGo until the transfer has finished. _Transfer_ - [ ] On the old phone, open tabs, visit sites, add bookmarks and favorites, start a Duck.ai chat, change a few settings, download a file, save a password and sign in to Email Protection - [ ] Transfer to the new phone and open the app - [ ] Tabs, history, bookmarks and favorites, the Duck.ai chat, the settings and the downloads list arrived - [ ] Saved passwords, Email Protection and the subscription did not _Kill switch_ - [ ] With the `deviceMigration` remote flag off on the old phone, a transfer brings no app data ### UI changes | Before | After | | ------ | ----- | | No UI changes | No UI changes | --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1211850753229323/task/1220382189916774?focus=true Tech Design URL (if applicable): None API Proposals URL(s) (if applicable): None ### Description Treats a tapped login form submission as user-initiated for the redirect that returns to the app, so the app link is no longer silently dropped. ### Steps to test this PR > [!NOTE] > Prerequisites: > - [x] Download the test tools: [auth-handoff-test-tools.zip](https://github.com/user-attachments/files/34807459/auth-handoff-test-tools.zip) and unzip. > - [x] Install this branch's internal debug build, > - [x] Install the auth handoff test apk to simulate all scenarios from the unzipped folder. > - [x] Start the local server from the unzipped folder: > ``` > python3 server/server.py --port 8080 --scenarios server/scenarios_auth.py > ``` > - [x] Configure the device: > ``` > adb reverse tcp:8080 tcp:8080 > adb install -r auth-handoff-test.apk > adb shell pm set-app-links-allowed --user 0 --package com.duckduckgo.authhandofftest true > adb shell pm set-app-links --package com.duckduckgo.authhandofftest 2 localtest.me > adb shell pm set-app-links-user-selection --user 0 --package com.duckduckgo.authhandofftest true localtest.me > adb shell pm get-app-links com.duckduckgo.authhandofftest > ``` > The last command should list `localtest.me` as approved (Android 12+; the host cannot be auto-verified, hence the manual approval). _Happy path_ - [x] Login redirect reaches app under 'Ask Each Time': Tap preset **Form POST -> 302 -> callback** (Callback: App Link), set Open Links in Apps to **Ask Each Time**, tap **view**, tap Submit → confirm the "Open in app" snackbar appears and its action opens the test app. - [x] Login redirect opens app directly under 'Always': Same preset and Callback, set it to **Always**, tap **view**, tap Submit → confirm the test app opens directly. - [x] Hand-off after DuckDuckGo was closed in background: Same preset (Callback: App Link), **Always**, tap **ct_session** once, run `adb shell am force-stop com.duckduckgo.mobile.android.debug`, tap **ct_stale_session**, tap Submit → confirm the test app opens (under **Ask Each Time**, the snackbar appears instead). _Guards_ - [x] No app opens without a tap: Tap preset **No tap: auto-submit POST** (Callback: App Link), any setting, tap **view** (no Submit) → confirm no snackbar appears and the test app does not open. - [x] Late script redirect still opens no app: Tap preset **POST -> page -> script delayed (6s)** (Callback: App Link), **Ask Each Time**, tap **view**, tap Submit → confirm no snackbar and no app when the script navigates to the callback seconds later. _Edge cases_ - [x] Login redirect through multiple redirects: Tap preset **Form POST -> 2 redirects** (Callback: App Link), tap **view**, tap Submit → confirm the snackbar (**Ask Each Time**) or the test app (**Always**) as in the happy path. - [x] Multi-page login (email, then password): Tap preset **Two-step login (email, password)** (Callback: App Link), **Ask Each Time**, tap **view**, submit the email page then the password page → confirm the snackbar after the second submit. - [x] OAuth consent form returning to the app: Tap preset **Consent (OAuth-like)** (Callback: App Link), **Ask Each Time**, tap **view**, tap the consent button → confirm the snackbar; tapping it opens the test app with `code` and `state` in the logged callback. - [x] App's own link returns weren't affected: Switch Callback to **Custom scheme**, tap preset **Form POST -> 302 -> callback**, **Ask Each Time**, tap **view**, tap Submit → confirm the existing "open in app" dialog, as before this PR. ### UI changes No user-facing UI changes. --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1149059203486286/task/1220522358296710 Tech Design URL (if applicable): API Proposals URL(s) (if applicable): ### Description Part of the Node 24 upgrade campaign. This is the required change for `duckduckgo/Android`: declare a Node version, and make CI read it. ## What this does ``` add .nvmrc 24.22.1 edit .github/workflows/pixel-validation.yml node-version: 20.x -> node-version-file: .nvmrc ``` `pixel-validation.yml` is the only workflow here that runs a project install — `npm ci --verbose` in `PixelDefinitions/` — and it was pinned to **Node 20.x**, below the campaign floor of 24.14.1. Pointing it at `.nvmrc` means the version lives in one place rather than being repeated in YAML. **24.22.1** rather than the bare floor: it is a security release (seven CVEs, three High in TLS and DNS) and updates Node's root certificates to NSS 3.131.1. ## Three workflows deliberately left alone `drift-audit.lock.yml`, `android-maintenance-worker.lock.yml` and `workflow-failure-classifier.lock.yml` each set `node-version: '24'`, which is a bare major and does not strictly clear the floor — a bare `24` can resolve to `24.0.0`. **They are compiled output.** Each is generated by GitHub Agentic Workflows from its `.md` source, and none of the three sources mentions Node at all — the version is injected by the compiler. Hand-editing the `.lock.yml` files would be reverted on the next recompile, so this PR does not touch them. If the bare major matters, that is a question for the agentic-workflow toolchain, not for this repo. Those three run `npm install -g @anthropic-ai/claude-code@latest` rather than a project install, so they are not installing this repo's dependencies. ## Not included No `.npmrc` / npm cooldown. That is a separate piece of work and it needs a decision here rather than a default: this repo has **three** `package.json` + lockfile pairs (root, `PixelDefinitions/`, `.github/actions/pr-asana-sync/`), and a root `.npmrc` with `min-release-age` would also govern the `npm install -g @anthropic-ai/claude-code@latest` steps above — meaning that tooling would stop picking up releases newer than seven days. Worth agreeing before doing, not bundling in here. ## Checking ```bash git clone --depth 1 git@dub.duckduckgo.com:duckduckgo/node-upgrade.git python3 node-upgrade/check_repo.py --path . ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Task/Issue URL: https://app.asana.com/1/137249556945/project/1218312816344693/task/1219837194779269?focus=true Tech Design URL (if applicable): API Proposals URL(s) (if applicable): ### Description Adds API quality scoring for `-api` modules, with GitHub workflows to report on it. Nothing in the app or its build changes: everything is new files under `scripts/api-quality/` and `.github/`. - **Scorer** (`scripts/api-quality/`): parses every `-api` module with the Kotlin compiler's parser and scores it against eight mechanical rules. It runs standalone, outside Gradle, in about a second. - **API quality check** (PR): compares the PR with the commit it branched from and keeps one comment up to date, listing each new finding with its line, details and fix. It fails on a regression but isn't a required check, so it never blocks a merge. - **API quality weekly report** (Mondays): compares `develop` with last week's report and an optional baseline, and posts a summary plus one task per new finding group to Asana. - **API quality scorecard** and **API quality compare** (manual): score one ref, or compare two refs. Asana posting needs the `GH_API_QUALITY_*` repository variables; until they're set, the workflows only report on their artifact run pages. **Edit - New changes after Review comments:** - M1: also flags type names starting with "Real" followed by an uppercase letter (RealFoo, not Realm). - M7: also flags the Java mutable collections (ArrayList, HashMap, HashSet, LinkedHashMap, LinkedHashSet, LinkedList, ConcurrentHashMap, CopyOnWriteArrayList), the Atomic types, and the coroutines Channel and SendChannel. Because we have our own data class Channel for notifications, Channel only counts when the file imports it from `kotlinx.coroutines.channels` (by name or with a wildcard) or writes the full name. - Source sets: every non-test source set is scored (src/main plus any flavor folder like src/play), so flavor-only APIs aren't skipped. - Rule count: no more hardcoded 8; the scorecard uses the scorer's rule list, and compare.py counts the rules in rules.json. - One source for rule texts: the names, explanations and fixes live in scripts/api-quality/rules.json, which compare.py and the Asana scripts read. The Asana rules task will link to it. - Compiler setup: added a comment explaining why `MESSAGE_COLLECTOR_KEY` is set to `MessageCollector.NONE`. - score.sh: `SCORER_ROOT` can be set, which the re-baseline procedure needs in order to run the old scorer against the same code. - README: documents source sets, M1, M8 and rules.json. ### Steps to test this PR _Scripts, locally (needs Java 21)_ - [x] run `scripts/api-quality/score.sh` writes a scorecard to `scripts/api-quality/output/`. Check the scorecard and CSV files are properly generated - [x] Run tests with `python3 -m venv /tmp/aq && /tmp/aq/bin/pip install -r scripts/api-quality/requirements.txt && (cd scripts/api-quality && /tmp/aq/bin/python -m pytest)` _Tests to do after merge (manual workflows only appear once on `develop`)_ - [ ] **Actions → API quality scorecard → Run workflow** with no input shows the scorecard on the run page and as an artifact. - [ ] **API quality compare** with `develop` against an older release tag shows the comparison and fails if `develop` regressed. - [ ] **API quality weekly report → Run workflow**, box unticked, shows the full report on the run page. ### UI changes None. --------- Co-authored-by: Łukasz Paczos <lukas.paczos@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot]
Can you help keep this open source service alive? 💖 Please sponsor : )