From ab701482dbcb61e80673f4fdb1673dfbc0b3c3c7 Mon Sep 17 00:00:00 2001 From: vadims <118171981+vadimstruts@users.noreply.github.com> Date: Thu, 29 Jan 2026 12:17:49 +0100 Subject: [PATCH] [Shred] Auto shred UI implementation (#33414) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added UI implementation for the auto shred feature Resolves: https://github.com/brave/brave-browser/issues/52278 Resolves: https://github.com/brave/brave-browser/issues/51620 --------- Signed-off-by: Vadym Struts --- android/BUILD.gn | 2 + .../drawable/ic_brave_auto_shred.xml | 15 + .../layout/brave_shields_main_layout.xml | 8 + .../layout/brave_shields_option_layout.xml | 37 +-- .../layout/brave_shields_secondary_layout.xml | 5 + .../brave_shields_shred_site_data_layout.xml | 88 ++++++ .../layout/brave_shields_toggle_layout.xml | 41 ++- .../chrome/browser/app/BraveActivity.java | 6 + .../website/BraveShieldsContentSettings.java | 36 ++- .../settings/BravePrivacySettings.java | 3 +- .../browser/shields/BraveShieldsHandler.java | 294 ++++++++++++++++-- browser/android/BUILD.gn | 1 + .../android/brave_shields_content_settings.cc | 33 ++ browser/brave_shields/android/BUILD.gn | 1 + ...brave_first_party_storage_cleaner_utils.cc | 28 ++ .../brave_first_party_storage_cleaner_utils.h | 15 + .../BraveFirstPartyStorageCleanerUtils.java | 36 ++- browser/brave_shields/sources.gni | 1 + .../application_state_observer.cc | 5 +- .../ephemeral_storage_service_unittest.cc | 72 ++--- .../android/strings/android_brave_strings.grd | 23 +- .../ephemeral_storage_service.cc | 17 +- .../ephemeral_storage_service.h | 3 + 23 files changed, 662 insertions(+), 108 deletions(-) create mode 100644 android/java/brave-res/drawable/ic_brave_auto_shred.xml create mode 100644 android/java/brave-res/layout/brave_shields_shred_site_data_layout.xml create mode 100644 browser/brave_shields/android/brave_first_party_storage_cleaner_utils.h diff --git a/android/BUILD.gn b/android/BUILD.gn index a6822278f4b..b000cb41d80 100644 --- a/android/BUILD.gn +++ b/android/BUILD.gn @@ -294,6 +294,7 @@ android_resources("java_resources") { "java/brave-res/drawable/ic_brave.xml", "java/brave-res/drawable/ic_brave_36.xml", "java/brave-res/drawable/ic_brave_ai.xml", + "java/brave-res/drawable/ic_brave_auto_shred.xml", "java/brave-res/drawable/ic_brave_battery.xml", "java/brave-res/drawable/ic_brave_logo.xml", "java/brave-res/drawable/ic_brave_mobiledata.xml", @@ -591,6 +592,7 @@ android_resources("java_resources") { "java/brave-res/layout/brave_shields_main_layout.xml", "java/brave-res/layout/brave_shields_option_layout.xml", "java/brave-res/layout/brave_shields_secondary_layout.xml", + "java/brave-res/layout/brave_shields_shred_site_data_layout.xml", "java/brave-res/layout/brave_shields_stats_row.xml", "java/brave-res/layout/brave_shields_switcher.xml", "java/brave-res/layout/brave_shields_thank_you_layout.xml", diff --git a/android/java/brave-res/drawable/ic_brave_auto_shred.xml b/android/java/brave-res/drawable/ic_brave_auto_shred.xml new file mode 100644 index 00000000000..ce6ad7ebc2e --- /dev/null +++ b/android/java/brave-res/drawable/ic_brave_auto_shred.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/android/java/brave-res/layout/brave_shields_main_layout.xml b/android/java/brave-res/layout/brave_shields_main_layout.xml index fe3549931b1..ccba3e4429d 100644 --- a/android/java/brave-res/layout/brave_shields_main_layout.xml +++ b/android/java/brave-res/layout/brave_shields_main_layout.xml @@ -158,6 +158,14 @@ android:id="@+id/brave_shields_block_fingerprinting_layout_id" layout="@layout/brave_shields_option_layout"/> + + + + diff --git a/android/java/brave-res/layout/brave_shields_option_layout.xml b/android/java/brave-res/layout/brave_shields_option_layout.xml index 13a2a3f4437..09a09758b45 100644 --- a/android/java/brave-res/layout/brave_shields_option_layout.xml +++ b/android/java/brave-res/layout/brave_shields_option_layout.xml @@ -26,7 +26,7 @@ android:gravity="start|center_vertical" android:padding="16dp" app:srcCompat="@drawable/ic_back" - app:tint="@color/shield_back_button_tint"/> + app:tint="@color/schemes_on_surface_variant"/> + android:textColor="@color/schemes_on_surface"/> @@ -70,7 +70,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="16dp" - android:layout_marginEnd="16dp"> + android:layout_marginEnd="16dp" + android:layout_marginBottom="16dp"> + android:textColor="@color/shield_text_color" + android:layoutDirection="rtl" + app:buttonTint="@color/schemes_primary"/> - + android:textColor="@color/shield_text_color" + android:layoutDirection="rtl" + app:buttonTint="@color/schemes_primary"/> + android:textColor="@color/shield_text_color" + android:layoutDirection="rtl" + app:buttonTint="@color/schemes_primary"/> - - - \ No newline at end of file + diff --git a/android/java/brave-res/layout/brave_shields_secondary_layout.xml b/android/java/brave-res/layout/brave_shields_secondary_layout.xml index e8289273da6..c9cf759b9ae 100644 --- a/android/java/brave-res/layout/brave_shields_secondary_layout.xml +++ b/android/java/brave-res/layout/brave_shields_secondary_layout.xml @@ -43,6 +43,11 @@ android:id="@+id/brave_shields_forget_first_party_storage_id" layout="@layout/brave_shields_switcher"/> + + + diff --git a/android/java/brave-res/layout/brave_shields_shred_site_data_layout.xml b/android/java/brave-res/layout/brave_shields_shred_site_data_layout.xml new file mode 100644 index 00000000000..6be097d9a70 --- /dev/null +++ b/android/java/brave-res/layout/brave_shields_shred_site_data_layout.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/java/brave-res/layout/brave_shields_toggle_layout.xml b/android/java/brave-res/layout/brave_shields_toggle_layout.xml index 2421e207caa..a96336ea1e7 100644 --- a/android/java/brave-res/layout/brave_shields_toggle_layout.xml +++ b/android/java/brave-res/layout/brave_shields_toggle_layout.xml @@ -13,6 +13,7 @@ android:background="@color/shield_toggle_bg_color"> - + + + android:orientation="vertical"> + + + + + + - \ No newline at end of file + diff --git a/android/java/org/chromium/chrome/browser/app/BraveActivity.java b/android/java/org/chromium/chrome/browser/app/BraveActivity.java index aac8034e03e..fb7a19e56ac 100644 --- a/android/java/org/chromium/chrome/browser/app/BraveActivity.java +++ b/android/java/org/chromium/chrome/browser/app/BraveActivity.java @@ -1081,6 +1081,12 @@ public abstract class BraveActivity extends ChromeActivity ApplicationStatus.registerApplicationStateListener(mApplicationStateListener); } + Profile profile = getCurrentProfile(); + if (profile != null) { + // Triggers notification of current app state on Android. + BraveFirstPartyStorageCleanerUtils.triggerCurrentAppStateNotification(profile); + } + super.onStartWithNative(); } diff --git a/android/java/org/chromium/chrome/browser/preferences/website/BraveShieldsContentSettings.java b/android/java/org/chromium/chrome/browser/preferences/website/BraveShieldsContentSettings.java index 23fcb329769..3d713e55aef 100644 --- a/android/java/org/chromium/chrome/browser/preferences/website/BraveShieldsContentSettings.java +++ b/android/java/org/chromium/chrome/browser/preferences/website/BraveShieldsContentSettings.java @@ -28,6 +28,7 @@ public class BraveShieldsContentSettings { public static final String RESOURCE_IDENTIFIER_HTTPS_UPGRADE = "httpsUpgrade"; public static final String RESOURCE_IDENTIFIER_FORGET_FIRST_PARTY_STORAGE = "forgetFirstPartyStorage"; + public static final String RESOURCE_IDENTIFIER_SHRED_SITE_DATA = "shredSiteData"; public static final String RESOURCE_IDENTIFIER_ALLOW_ELEMENT_BLOCKER_IN_PRIVATE = "allowElementBlockerInPrivate"; @@ -37,6 +38,11 @@ public class BraveShieldsContentSettings { public static final String ALLOW_RESOURCE = "allow"; public static final String AGGRESSIVE = "aggressive"; + // Auto Shred Modes, must be consistent with brave_shields::mojom::AutoShredMode + public static final String AUTO_SHRED_MODE_NEVER = "0"; + public static final String AUTO_SHRED_MODE_LAST_TAB_CLOSED = "1"; + public static final String AUTO_SHRED_MODE_APP_EXIT = "2"; + public static final int ALWAYS = 0; public static final int ASK = 1; public static final int NEVER = 2; @@ -129,12 +135,23 @@ public class BraveShieldsContentSettings { } else if (resourceIndentifier.equals(RESOURCE_IDENTIFIER_COOKIES)) { BraveShieldsContentSettingsJni.get().setCookieControlType(settingOption, host, profile); } else if (resourceIndentifier.equals(RESOURCE_IDENTIFIER_TRACKERS)) { - BraveShieldsContentSettingsJni.get().setCosmeticFilteringControlType( - DEFAULT.equals(settingOption) ? BLOCK_THIRDPARTY_RESOURCE : settingOption, host, - profile); - BraveShieldsContentSettingsJni.get().setAdControlType( - BLOCK_THIRDPARTY_RESOURCE.equals(settingOption) ? DEFAULT : settingOption, host, - profile); + BraveShieldsContentSettingsJni.get() + .setCosmeticFilteringControlType( + DEFAULT.equals(settingOption) + ? BLOCK_THIRDPARTY_RESOURCE + : settingOption, + host, + profile); + BraveShieldsContentSettingsJni.get() + .setAdControlType( + BLOCK_THIRDPARTY_RESOURCE.equals(settingOption) + ? DEFAULT + : settingOption, + host, + profile); + } else if (resourceIndentifier.equals(RESOURCE_IDENTIFIER_SHRED_SITE_DATA)) { + BraveShieldsContentSettingsJni.get() + .setAutoShredMode(Integer.parseInt(settingOption), host, profile); } } @@ -172,6 +189,9 @@ public class BraveShieldsContentSettings { if (settings.equals(BLOCK_THIRDPARTY_RESOURCE)) { settings = DEFAULT; } + } else if (resourceIndentifier.equals(RESOURCE_IDENTIFIER_SHRED_SITE_DATA)) { + int mode = BraveShieldsContentSettingsJni.get().getAutoShredMode(host, profile); + settings = Integer.toString(mode); } return settings; } @@ -332,5 +352,9 @@ public class BraveShieldsContentSettings { boolean getAllowElementBlockerInPrivateModeEnabled(); void setAllowElementBlockerInPrivateModeEnabled(boolean enabled); + + void setAutoShredMode(int mode, String url, Profile profile); + + int getAutoShredMode(String url, Profile profile); } } diff --git a/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java b/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java index 86eb0ac5a44..c5e96fd8fd8 100644 --- a/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java +++ b/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java @@ -355,7 +355,8 @@ public class BravePrivacySettings extends PrivacySettings { (ChromeSwitchPreference) findPreference(PREF_FORGET_FIRST_PARTY_STORAGE); mForgetFirstPartyStoragePref.setOnPreferenceChangeListener(this); boolean forgetFirstPartyStorageIsEnabled = - ChromeFeatureList.isEnabled(BraveFeatureList.BRAVE_FORGET_FIRST_PARTY_STORAGE); + ChromeFeatureList.isEnabled(BraveFeatureList.BRAVE_FORGET_FIRST_PARTY_STORAGE) + && !ChromeFeatureList.isEnabled(BraveFeatureList.BRAVE_SHRED); mForgetFirstPartyStoragePref.setVisible(forgetFirstPartyStorageIsEnabled); if (ChromeFeatureList.isEnabled(BraveFeatureList.BRAVE_SHIELDS_ELEMENT_PICKER)) { diff --git a/android/java/org/chromium/chrome/browser/shields/BraveShieldsHandler.java b/android/java/org/chromium/chrome/browser/shields/BraveShieldsHandler.java index e0ad913e0cf..f2e6f301755 100644 --- a/android/java/org/chromium/chrome/browser/shields/BraveShieldsHandler.java +++ b/android/java/org/chromium/chrome/browser/shields/BraveShieldsHandler.java @@ -171,6 +171,8 @@ public class BraveShieldsHandler implements BraveRewardsHelper.LargeIconReadyCal private TextView mSiteBlockCounterText; private View mBottomDivider; private ImageView mToggleIcon; + private LinearLayout mShredSiteDataLayout; + private LinearLayout mAutoShredOptionsLayout; private BraveRewardsNativeWorker mBraveRewardsNativeWorker; private BraveRewardsHelper mIconFetcher; @@ -391,7 +393,23 @@ public class BraveShieldsHandler implements BraveRewardsHelper.LargeIconReadyCal }); } else { popupWindow.setAnimationStyle(R.style.AnchoredPopupAnimEndTop); - popupWindow.showAsDropDown(mAnchorView); + + // Add 8dp margin from right edge for showAsDropDown case + int marginRightDp = 8; + int marginRightPx = + (int) (marginRightDp * mContext.getResources().getDisplayMetrics().density); + int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels; + int[] anchorLocation = new int[2]; + mAnchorView.getLocationOnScreen(anchorLocation); + int anchorX = anchorLocation[0]; + + if (anchorX + width > screenWidth - marginRightPx) { + // Calculate offset to keep popup within bounds + int xOffset = screenWidth - width - marginRightPx - anchorX; + popupWindow.showAsDropDown(mAnchorView, xOffset, 0); + } else { + popupWindow.showAsDropDown(mAnchorView); + } } return popupWindow; @@ -502,6 +520,10 @@ public class BraveShieldsHandler implements BraveRewardsHelper.LargeIconReadyCal mBottomDivider = mToggleLayout.findViewById(R.id.bottom_divider); mToggleIcon = mToggleLayout.findViewById(R.id.toggle_favicon); + mShredSiteDataLayout = + mPopupView.findViewById(R.id.brave_shields_shred_site_data_layout_id); + mAutoShredOptionsLayout = + mPopupView.findViewById(R.id.brave_shields_auto_shred_options_layout_id); } private void setUpMainLayout() { @@ -610,6 +632,8 @@ public class BraveShieldsHandler implements BraveRewardsHelper.LargeIconReadyCal setUpSwitchLayouts(); setupDetailsLayouts(); + + setupShredSiteDataLayout(); } private void setupDetailsLayouts() { @@ -692,8 +716,6 @@ public class BraveShieldsHandler implements BraveRewardsHelper.LargeIconReadyCal mBlockShieldsOption2.setText(option2StringId); RadioButton mBlockShieldsOption3 = mBlockShieldsLayout.findViewById(R.id.option3); mBlockShieldsOption3.setText(option3StringId); - Button mBlockShieldsDoneButton = mBlockShieldsLayout.findViewById(R.id.done_button); - mBlockShieldsDoneButton.setOnClickListener(mDoneClickListener); ImageView mBlockShieldsBackButton = mBlockShieldsLayout.findViewById(R.id.back_button); mBlockShieldsBackButton.setOnClickListener(new View.OnClickListener() { @Override @@ -784,6 +806,238 @@ public class BraveShieldsHandler implements BraveRewardsHelper.LargeIconReadyCal } } + private void updateSecondaryShredSiteDataText(String text) { + LinearLayout secondaryShredSiteDataLayout = + mSecondaryLayout.findViewById( + R.id.brave_shields_secondary_shred_site_data_layout_id); + TextView secondaryShredSiteDataText = + secondaryShredSiteDataLayout.findViewById(R.id.toggle_text_sub_text); + + secondaryShredSiteDataText.setText( + mContext.getResources() + .getString(R.string.brave_shields_shred_sites_data_sub_text, text)); + secondaryShredSiteDataText.setVisibility(text == null ? View.GONE : View.VISIBLE); + } + + private void updateBraveShieldsAutoShredSiteDataSubText(String autoShredSettingOption) { + LinearLayout braveShieldsAutoShredItemLayout = + mShredSiteDataLayout.findViewById( + R.id.brave_shields_auto_shred_site_data_layout_id); + TextView braveShieldsAutoShredSiteDataSubText = + braveShieldsAutoShredItemLayout.findViewById(R.id.toggle_text_sub_text); + + if (autoShredSettingOption.equals(BraveShieldsContentSettings.AUTO_SHRED_MODE_NEVER)) { + braveShieldsAutoShredSiteDataSubText.setText( + R.string.brave_shields_auto_shred_never_mode_text); + braveShieldsAutoShredSiteDataSubText.setVisibility(View.VISIBLE); + updateSecondaryShredSiteDataText(null); + } else if (autoShredSettingOption.equals( + BraveShieldsContentSettings.AUTO_SHRED_MODE_LAST_TAB_CLOSED)) { + braveShieldsAutoShredSiteDataSubText.setText( + R.string.brave_shields_auto_shred_site_tab_closed_mode_text); + braveShieldsAutoShredSiteDataSubText.setVisibility(View.VISIBLE); + + updateSecondaryShredSiteDataText( + mContext.getResources() + .getString( + R.string.brave_shields_auto_shred_site_tab_closed_mode_text)); + } else if (autoShredSettingOption.equals( + BraveShieldsContentSettings.AUTO_SHRED_MODE_APP_EXIT)) { + braveShieldsAutoShredSiteDataSubText.setText( + R.string.brave_shields_auto_shred_app_close_mode_text); + braveShieldsAutoShredSiteDataSubText.setVisibility(View.VISIBLE); + updateSecondaryShredSiteDataText( + mContext.getResources() + .getString(R.string.brave_shields_auto_shred_app_close_mode_text)); + } else { + braveShieldsAutoShredSiteDataSubText.setVisibility(View.GONE); + updateSecondaryShredSiteDataText(null); + } + } + + private void setupShredSiteDataLayout() { + LinearLayout secondaryShredSiteDataLayout = + mSecondaryLayout.findViewById( + R.id.brave_shields_secondary_shred_site_data_layout_id); + if (!ChromeFeatureList.isEnabled(BraveFeatureList.BRAVE_SHRED)) { + secondaryShredSiteDataLayout.setVisibility(View.GONE); + return; + } + secondaryShredSiteDataLayout.setVisibility(View.VISIBLE); + + secondaryShredSiteDataLayout.setBackground(null); + + ImageView secondaryShredSiteDataShieldsIcon = + secondaryShredSiteDataLayout.findViewById(R.id.toggle_favicon); + secondaryShredSiteDataShieldsIcon.setImageResource(R.drawable.ic_chevron_right); + secondaryShredSiteDataShieldsIcon.setColorFilter( + mContext.getColor(R.color.default_icon_color_baseline)); + TextView secondaryShredSiteDataText = + secondaryShredSiteDataLayout.findViewById(R.id.toggle_text); + secondaryShredSiteDataText.setText(R.string.brave_shields_shred_sites_data_text); + + secondaryShredSiteDataLayout.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + mMainLayout.setVisibility(View.GONE); + mShredSiteDataLayout.setVisibility(View.VISIBLE); + } + }); + ImageView shredSiteDataBackButton = + mShredSiteDataLayout.findViewById(R.id.shred_site_data_back_button); + shredSiteDataBackButton.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + mShredSiteDataLayout.setVisibility(View.GONE); + mMainLayout.setVisibility(View.VISIBLE); + } + }); + + // Manual shred layout + LinearLayout braveShieldsManualShredItemLayout = + mShredSiteDataLayout.findViewById( + R.id.brave_shields_manual_shred_site_data_layout_id); + braveShieldsManualShredItemLayout.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + hideBraveShieldsMenu(); + try { + BraveActivity.getBraveActivity().shredSiteData(); + } catch (BraveActivity.BraveActivityNotFoundException e) { + Log.e(TAG, "setupShredSiteDataLayout manual shred click " + e); + } + } + }); + + String autoShredSettingOption = + BraveShieldsContentSettings.getShieldsValue( + mProfile, + mUrlSpec, + BraveShieldsContentSettings.RESOURCE_IDENTIFIER_SHRED_SITE_DATA); + + // Autoshred layout + LinearLayout braveShieldsAutoShredItemLayout = + mShredSiteDataLayout.findViewById( + R.id.brave_shields_auto_shred_site_data_layout_id); + + braveShieldsAutoShredItemLayout.setBackground(null); + braveShieldsAutoShredItemLayout.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + mShredSiteDataLayout.setVisibility(View.GONE); + mAutoShredOptionsLayout.setVisibility(View.VISIBLE); + } + }); + + // Autoshred menu item's icon + View braveShieldsAutoShredToggleDivider = + braveShieldsAutoShredItemLayout.findViewById(R.id.toggle_top_divider); + braveShieldsAutoShredToggleDivider.setVisibility(View.GONE); + ImageView braveShieldsAutoShredButtonIcon = + braveShieldsAutoShredItemLayout.findViewById(R.id.toggle_button_icon); + braveShieldsAutoShredButtonIcon.setVisibility(View.VISIBLE); + braveShieldsAutoShredButtonIcon.setImageResource(R.drawable.ic_brave_auto_shred); + braveShieldsAutoShredButtonIcon.setColorFilter( + mContext.getColor(R.color.default_icon_color_baseline)); + + // Autoshred menu item's text + TextView braveShieldsAutoShredSiteDataText = + braveShieldsAutoShredItemLayout.findViewById(R.id.toggle_text); + braveShieldsAutoShredSiteDataText.setText( + R.string.brave_shields_auto_shred_sites_data_text); + updateBraveShieldsAutoShredSiteDataSubText(autoShredSettingOption); + + ImageView braveShieldsAutoShredSiteDataShieldsIcon = + braveShieldsAutoShredItemLayout.findViewById(R.id.toggle_favicon); + braveShieldsAutoShredSiteDataShieldsIcon.setImageResource(R.drawable.ic_chevron_right); + braveShieldsAutoShredSiteDataShieldsIcon.setColorFilter( + mContext.getColor(R.color.default_icon_color_baseline)); + + // Auto shred options layout + TextView autoShredOptionTitle = mAutoShredOptionsLayout.findViewById(R.id.option_title); + autoShredOptionTitle.setText(R.string.brave_shields_auto_shred_sites_data_text); + TextView autoShredOptionText = mAutoShredOptionsLayout.findViewById(R.id.option_text); + autoShredOptionText.setVisibility(View.GONE); + ImageView mAutoShredOptionsBackButton = + mAutoShredOptionsLayout.findViewById(R.id.back_button); + mAutoShredOptionsBackButton.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + mAutoShredOptionsLayout.setVisibility(View.GONE); + mShredSiteDataLayout.setVisibility(View.VISIBLE); + } + }); + RadioButton autoShredOption1 = mAutoShredOptionsLayout.findViewById(R.id.option1); + autoShredOption1.setText(R.string.brave_shields_auto_shred_never_mode_text); + autoShredOption1.setChecked( + autoShredSettingOption.equals(BraveShieldsContentSettings.AUTO_SHRED_MODE_NEVER)); + RadioButton autoShredOption2 = mAutoShredOptionsLayout.findViewById(R.id.option2); + autoShredOption2.setText(R.string.brave_shields_auto_shred_site_tab_closed_mode_text); + autoShredOption2.setChecked( + autoShredSettingOption.equals( + BraveShieldsContentSettings.AUTO_SHRED_MODE_LAST_TAB_CLOSED)); + RadioButton autoShredOption3 = mAutoShredOptionsLayout.findViewById(R.id.option3); + autoShredOption3.setText(R.string.brave_shields_auto_shred_app_close_mode_text); + autoShredOption3.setChecked( + autoShredSettingOption.equals( + BraveShieldsContentSettings.AUTO_SHRED_MODE_APP_EXIT)); + + // Handle Auto shred options selection + RadioGroup autoShredOptionGroup = + mAutoShredOptionsLayout.findViewById(R.id.options_radio_group); + autoShredOptionGroup.setOnCheckedChangeListener( + new RadioGroup.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + RadioButton checkedRadioButton = + (RadioButton) group.findViewById(checkedId); + boolean isChecked = checkedRadioButton.isChecked(); + if (isChecked) { + if (checkedId == R.id.option1) { + BraveShieldsContentSettings.setShieldsValue( + mProfile, + mUrlSpec, + BraveShieldsContentSettings + .RESOURCE_IDENTIFIER_SHRED_SITE_DATA, + BraveShieldsContentSettings.AUTO_SHRED_MODE_NEVER, + false); + updateBraveShieldsAutoShredSiteDataSubText( + BraveShieldsContentSettings.AUTO_SHRED_MODE_NEVER); + } else if (checkedId == R.id.option2) { + BraveShieldsContentSettings.setShieldsValue( + mProfile, + mUrlSpec, + BraveShieldsContentSettings + .RESOURCE_IDENTIFIER_SHRED_SITE_DATA, + BraveShieldsContentSettings.AUTO_SHRED_MODE_LAST_TAB_CLOSED, + false); + updateBraveShieldsAutoShredSiteDataSubText( + BraveShieldsContentSettings + .AUTO_SHRED_MODE_LAST_TAB_CLOSED); + } else if (checkedId == R.id.option3) { + BraveShieldsContentSettings.setShieldsValue( + mProfile, + mUrlSpec, + BraveShieldsContentSettings + .RESOURCE_IDENTIFIER_SHRED_SITE_DATA, + BraveShieldsContentSettings.AUTO_SHRED_MODE_APP_EXIT, + false); + updateBraveShieldsAutoShredSiteDataSubText( + BraveShieldsContentSettings.AUTO_SHRED_MODE_APP_EXIT); + } + if (null != mMenuObserver) { + mMenuObserver.onMenuTopShieldsChanged(isChecked, false); + } + } + } + }); + } + private void setUpSwitchLayouts() { LinearLayout upgradeHttpsLayout = mSecondaryLayout.findViewById(R.id.brave_shields_upgrade_https_id); @@ -799,7 +1053,8 @@ public class BraveShieldsHandler implements BraveRewardsHelper.LargeIconReadyCal LinearLayout forgetFirstPartyStorageLayout = mSecondaryLayout.findViewById(R.id.brave_shields_forget_first_party_storage_id); - if (ChromeFeatureList.isEnabled(BraveFeatureList.BRAVE_FORGET_FIRST_PARTY_STORAGE)) { + if (ChromeFeatureList.isEnabled(BraveFeatureList.BRAVE_FORGET_FIRST_PARTY_STORAGE) + && !ChromeFeatureList.isEnabled(BraveFeatureList.BRAVE_SHRED)) { TextView forgetFirstPartyStorageText = forgetFirstPartyStorageLayout.findViewById(R.id.brave_shields_switch_text); forgetFirstPartyStorageText.setText(R.string.brave_forget_first_party_storage_switch); @@ -857,8 +1112,6 @@ public class BraveShieldsHandler implements BraveRewardsHelper.LargeIconReadyCal mOptionText.setVisibility(View.GONE); RadioGroup mOptionGroup = mAboutLayout.findViewById(R.id.options_radio_group); mOptionGroup.setVisibility(View.GONE); - Button mDoneButton = mAboutLayout.findViewById(R.id.done_button); - mDoneButton.setOnClickListener(mDoneClickListener); ImageView mBackButton = mAboutLayout.findViewById(R.id.back_button); mBackButton.setOnClickListener(new View.OnClickListener() { @Override @@ -1408,22 +1661,19 @@ public class BraveShieldsHandler implements BraveRewardsHelper.LargeIconReadyCal private void setFavIcon(Bitmap bmp) { if (bmp != null && mContext != null) { - ((Activity)mContext).runOnUiThread( - new Runnable() { - @Override - public void run() { - ImageView iv = (ImageView) mPopupView.findViewById(R.id.site_favicon); - if (iv != null) iv.setImageBitmap(BraveRewardsHelper.getCircularBitmap(bmp)); - } - }); + ((Activity) mContext) + .runOnUiThread( + new Runnable() { + @Override + public void run() { + ImageView iv = + (ImageView) mPopupView.findViewById(R.id.site_favicon); + if (iv != null) { + iv.setImageBitmap( + BraveRewardsHelper.getCircularBitmap(bmp)); + } + } + }); } } - - private final View.OnClickListener mDoneClickListener = - new View.OnClickListener() { - @Override - public void onClick(View view) { - hideBraveShieldsMenu(); - } - }; } diff --git a/browser/android/BUILD.gn b/browser/android/BUILD.gn index bf5c521619c..2690fa1be20 100644 --- a/browser/android/BUILD.gn +++ b/browser/android/BUILD.gn @@ -30,6 +30,7 @@ source_set("android_browser_process") { "//brave/components/brave_ads/browser", "//brave/components/brave_shields/content/browser", "//brave/components/brave_shields/core/browser", + "//brave/components/brave_shields/core/common", "//brave/components/brave_sync", "//brave/components/brave_sync:crypto", "//brave/components/brave_sync:prefs", diff --git a/browser/android/brave_shields_content_settings.cc b/browser/android/brave_shields_content_settings.cc index 66d042093b8..b7e558ae40d 100644 --- a/browser/android/brave_shields_content_settings.cc +++ b/browser/android/brave_shields_content_settings.cc @@ -15,6 +15,7 @@ #include "brave/components/brave_shields/content/browser/ad_block_service.h" #include "brave/components/brave_shields/core/browser/brave_shields_settings_service.h" #include "brave/components/brave_shields/core/browser/brave_shields_utils.h" +#include "brave/components/brave_shields/core/common/brave_shields_settings_values.h" #include "chrome/android/chrome_jni_headers/BraveShieldsContentSettings_jni.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/content_settings/cookie_settings_factory.h" @@ -330,5 +331,37 @@ void JNI_BraveShieldsContentSettings_SetAllowElementBlockerInPrivateModeEnabled( g_browser_process->local_state(), enabled); } +void JNI_BraveShieldsContentSettings_SetAutoShredMode( + JNIEnv* env, + jint mode, + const base::android::JavaParamRef& url, + const base::android::JavaParamRef& j_profile) { + std::optional maybe_mode = + brave_shields::traits::SettingTraits< + brave_shields::mojom::AutoShredMode>::From(mode); + CHECK(maybe_mode.has_value()); + + auto* brave_shields_settings = + BraveShieldsSettingsServiceFactory::GetForProfile( + Profile::FromJavaObject(j_profile)); + brave_shields_settings->SetAutoShredMode( + maybe_mode.value(), + GURL(base::android::ConvertJavaStringToUTF8(env, url))); +} + +jint JNI_BraveShieldsContentSettings_GetAutoShredMode( + JNIEnv* env, + const base::android::JavaParamRef& url, + const base::android::JavaParamRef& j_profile) { + auto* brave_shields_settings = + BraveShieldsSettingsServiceFactory::GetForProfile( + Profile::FromJavaObject(j_profile)); + const auto mode = brave_shields_settings->GetAutoShredMode( + GURL(base::android::ConvertJavaStringToUTF8(env, url))); + + return brave_shields::traits::SettingTraits< + brave_shields::mojom::AutoShredMode>::To(mode); +} + } // namespace android } // namespace chrome diff --git a/browser/brave_shields/android/BUILD.gn b/browser/brave_shields/android/BUILD.gn index 0cb4745e8cf..855edacfd31 100644 --- a/browser/brave_shields/android/BUILD.gn +++ b/browser/brave_shields/android/BUILD.gn @@ -19,6 +19,7 @@ android_library("java") { deps = [ ":java_resources", "//base:base_java", + "//chrome/browser/profiles/android:java", "//chrome/browser/tab:java", "//third_party/android_deps:com_airbnb_android_lottie_java", "//third_party/androidx:androidx_fragment_fragment_java", diff --git a/browser/brave_shields/android/brave_first_party_storage_cleaner_utils.cc b/browser/brave_shields/android/brave_first_party_storage_cleaner_utils.cc index 34de074f06f..5adb24fc4e4 100644 --- a/browser/brave_shields/android/brave_first_party_storage_cleaner_utils.cc +++ b/browser/brave_shields/android/brave_first_party_storage_cleaner_utils.cc @@ -6,10 +6,13 @@ #include #include "base/android/jni_android.h" +#include "base/android/jni_string.h" #include "brave/browser/brave_shields/android/jni_headers/BraveFirstPartyStorageCleanerUtils_jni.h" #include "brave/browser/brave_shields/brave_shields_tab_helper.h" +#include "brave/browser/ephemeral_storage/ephemeral_storage_service_factory.h" #include "brave/browser/ephemeral_storage/ephemeral_storage_tab_helper.h" #include "chrome/browser/android/tab_android.h" +#include "chrome/browser/profiles/profile.h" namespace brave_shields { @@ -39,4 +42,29 @@ static void JNI_BraveFirstPartyStorageCleanerUtils_CleanupTLDFirstPartyStorage( brave_shields_tab_helper->EnforceSiteDataCleanup(); } +static void +JNI_BraveFirstPartyStorageCleanerUtils_TriggerCurrentAppStateNotification( + JNIEnv* env, + const base::android::JavaParamRef& j_profile) { + CHECK(env); + Profile* profile = Profile::FromJavaObject(j_profile); + if (!profile) { + return; + } + + auto* ephemeral_storage_service = + EphemeralStorageServiceFactory::GetForContext( + static_cast(profile)); + if (!ephemeral_storage_service) { + return; + } + + ephemeral_storage_service->TriggerCurrentAppStateNotification(); +} + +bool IsAppInTaskStack() { + JNIEnv* env = base::android::AttachCurrentThread(); + return Java_BraveFirstPartyStorageCleanerUtils_isAppInTaskStack(env); +} + } // namespace brave_shields diff --git a/browser/brave_shields/android/brave_first_party_storage_cleaner_utils.h b/browser/brave_shields/android/brave_first_party_storage_cleaner_utils.h new file mode 100644 index 00000000000..c1ca5f32fa2 --- /dev/null +++ b/browser/brave_shields/android/brave_first_party_storage_cleaner_utils.h @@ -0,0 +1,15 @@ +/* Copyright (c) 2026 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_BRAVE_SHIELDS_ANDROID_BRAVE_FIRST_PARTY_STORAGE_CLEANER_UTILS_H_ +#define BRAVE_BROWSER_BRAVE_SHIELDS_ANDROID_BRAVE_FIRST_PARTY_STORAGE_CLEANER_UTILS_H_ + +namespace brave_shields { + +bool IsAppInTaskStack(); + +} // namespace brave_shields + +#endif // BRAVE_BROWSER_BRAVE_SHIELDS_ANDROID_BRAVE_FIRST_PARTY_STORAGE_CLEANER_UTILS_H_ diff --git a/browser/brave_shields/android/java/src/org/chromium/chrome/browser/brave_shields/BraveFirstPartyStorageCleanerUtils.java b/browser/brave_shields/android/java/src/org/chromium/chrome/browser/brave_shields/BraveFirstPartyStorageCleanerUtils.java index 3f9300d3ed9..c1cddc96985 100644 --- a/browser/brave_shields/android/java/src/org/chromium/chrome/browser/brave_shields/BraveFirstPartyStorageCleanerUtils.java +++ b/browser/brave_shields/android/java/src/org/chromium/chrome/browser/brave_shields/BraveFirstPartyStorageCleanerUtils.java @@ -5,23 +5,57 @@ package org.chromium.chrome.browser.brave_shields; +import android.app.ActivityManager; +import android.content.Context; + +import org.jni_zero.CalledByNative; import org.jni_zero.JNINamespace; import org.jni_zero.NativeMethods; +import org.chromium.base.ContextUtils; +import org.chromium.base.Log; import org.chromium.build.annotations.NullMarked; +import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.Tab; +import java.util.List; + @JNINamespace("brave_shields") @NullMarked public class BraveFirstPartyStorageCleanerUtils { - private static final String TAG = "FirstPartyStorageCleanerUtils"; + private static final String TAG = "FPSCleanerUtils"; public static void cleanupTLDFirstPartyStorage(Tab tab) { BraveFirstPartyStorageCleanerUtilsJni.get().cleanupTLDFirstPartyStorage(tab); } + public static void triggerCurrentAppStateNotification(Profile profile) { + BraveFirstPartyStorageCleanerUtilsJni.get().triggerCurrentAppStateNotification(profile); + } + + @CalledByNative + public static boolean isAppInTaskStack() { + try { + ActivityManager am = + (ActivityManager) + ContextUtils.getApplicationContext() + .getSystemService(Context.ACTIVITY_SERVICE); + if (am == null) { + return false; + } + + List appTasks = am.getAppTasks(); + return !appTasks.isEmpty(); + } catch (Exception e) { + Log.e(TAG, "Failed to check task stack", e); + return false; + } + } + @NativeMethods interface Natives { void cleanupTLDFirstPartyStorage(Tab tab); + + void triggerCurrentAppStateNotification(Profile profile); } } diff --git a/browser/brave_shields/sources.gni b/browser/brave_shields/sources.gni index a697c187c02..d67a1af6bc4 100644 --- a/browser/brave_shields/sources.gni +++ b/browser/brave_shields/sources.gni @@ -44,6 +44,7 @@ brave_browser_brave_shields_deps = [ if (is_android) { brave_browser_brave_shields_sources += [ "//brave/browser/brave_shields/android/brave_first_party_storage_cleaner_utils.cc", + "//brave/browser/brave_shields/android/brave_first_party_storage_cleaner_utils.h", "//brave/browser/brave_shields/android/filter_list_service_factory_android.cc", "//brave/browser/brave_shields/brave_shields_web_contents_observer_android.cc", ] diff --git a/browser/ephemeral_storage/application_state_observer.cc b/browser/ephemeral_storage/application_state_observer.cc index cb5fffcb02a..18ec57ef309 100644 --- a/browser/ephemeral_storage/application_state_observer.cc +++ b/browser/ephemeral_storage/application_state_observer.cc @@ -13,6 +13,8 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_list.h" +#else +#include "brave/browser/brave_shields/android/brave_first_party_storage_cleaner_utils.h" #endif namespace ephemeral_storage { @@ -59,12 +61,13 @@ void ApplicationStateObserver::TriggerCurrentAppStateNotification() { void ApplicationStateObserver::OnApplicationStateChange( base::android::ApplicationState new_state) { + auto app_in_stack = brave_shields::IsAppInTaskStack(); if (new_state == base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) { if (!has_notified_active_) { NotifyApplicationBecameActive(); has_notified_active_ = true; } - } else if (current_state_ != new_state && + } else if (!app_in_stack && current_state_ != new_state && current_state_ == base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) { NotifyApplicationBecameInactive(); diff --git a/browser/ephemeral_storage/ephemeral_storage_service_unittest.cc b/browser/ephemeral_storage/ephemeral_storage_service_unittest.cc index 1dcf20ccea5..97c084dd7a2 100644 --- a/browser/ephemeral_storage/ephemeral_storage_service_unittest.cc +++ b/browser/ephemeral_storage/ephemeral_storage_service_unittest.cc @@ -181,9 +181,8 @@ TEST_F(EphemeralStorageServiceTest, EphemeralCleanup) { const std::string ephemeral_domain = url.GetHost(); const auto storage_partition_config = content::StoragePartitionConfig::CreateDefault(&profile_); -#if BUILDFLAG(IS_ANDROID) - EXPECT_CALL(*mock_delegate_, TriggerCurrentAppStateNotification()); -#endif + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)) + .WillOnce(testing::Return(std::nullopt)); // Create tld ephemeral lifetime. service_->TLDEphemeralLifetimeCreated(ephemeral_domain, storage_partition_config); @@ -199,9 +198,8 @@ TEST_F(EphemeralStorageServiceTest, EphemeralCleanup) { task_environment_.FastForwardBy(base::Seconds(10)); } -#if BUILDFLAG(IS_ANDROID) - EXPECT_CALL(*mock_delegate_, TriggerCurrentAppStateNotification()); -#endif + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)) + .WillOnce(testing::Return(std::nullopt)); // Reopen tld ephemeral lifetime while the keepalive is active. service_->TLDEphemeralLifetimeCreated(ephemeral_domain, storage_partition_config); @@ -238,12 +236,13 @@ TEST_F(EphemeralStorageServiceTest, const auto second_storage_partition_config = content::StoragePartitionConfig::Create(&profile_, "partition_domain", "partition_name", false); -#if BUILDFLAG(IS_ANDROID) - EXPECT_CALL(*mock_delegate_, TriggerCurrentAppStateNotification()).Times(2); -#endif + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)) + .WillOnce(testing::Return(std::nullopt)); // Create tld ephemeral lifetime. service_->TLDEphemeralLifetimeCreated(ephemeral_domain, storage_partition_config); + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)) + .WillOnce(testing::Return(std::nullopt)); // Create tld ephemeral lifetime in a different storage partition. service_->TLDEphemeralLifetimeCreated(ephemeral_domain, second_storage_partition_config); @@ -297,9 +296,8 @@ TEST_F(EphemeralStorageServiceNoKeepAliveTest, ImmediateCleanup) { const std::string ephemeral_domain = url.GetHost(); const auto storage_partition_config = content::StoragePartitionConfig::CreateDefault(&profile_); -#if BUILDFLAG(IS_ANDROID) - EXPECT_CALL(*mock_delegate_, TriggerCurrentAppStateNotification()); -#endif + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)) + .WillOnce(testing::Return(std::nullopt)); // Create tld ephemeral lifetime. service_->TLDEphemeralLifetimeCreated(ephemeral_domain, storage_partition_config); @@ -365,9 +363,8 @@ TEST_F(EphemeralStorageServiceForgetFirstPartyTest, CleanupFirstPartyStorage) { test_case.forget_first_party ? CONTENT_SETTING_BLOCK : CONTENT_SETTING_ALLOW); -#if BUILDFLAG(IS_ANDROID) - EXPECT_CALL(*mock_delegate_, TriggerCurrentAppStateNotification()); -#endif + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)) + .WillOnce(testing::Return(std::nullopt)); service_->TLDEphemeralLifetimeCreated(ephemeral_domain, storage_partition_config); EXPECT_EQ( @@ -411,9 +408,8 @@ TEST_F(EphemeralStorageServiceForgetFirstPartyTest, CleanupOnRestart) { url, url, ContentSettingsType::BRAVE_REMEMBER_1P_STORAGE, CONTENT_SETTING_BLOCK); -#if BUILDFLAG(IS_ANDROID) - EXPECT_CALL(*mock_delegate_, TriggerCurrentAppStateNotification()); -#endif + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)) + .WillOnce(testing::Return(std::nullopt)); // Create tld ephemeral lifetime. service_->TLDEphemeralLifetimeCreated(ephemeral_domain, storage_partition_config); @@ -472,9 +468,8 @@ TEST_F(EphemeralStorageServiceForgetFirstPartyTest, url, url, ContentSettingsType::BRAVE_REMEMBER_1P_STORAGE, CONTENT_SETTING_BLOCK); -#if BUILDFLAG(IS_ANDROID) - EXPECT_CALL(*mock_delegate_, TriggerCurrentAppStateNotification()); -#endif + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)) + .WillOnce(testing::Return(std::nullopt)); // Create tld ephemeral lifetime. service_->TLDEphemeralLifetimeCreated(ephemeral_domain, storage_partition_config); @@ -497,12 +492,11 @@ TEST_F(EphemeralStorageServiceForgetFirstPartyTest, service_ = CreateEphemeralStorageService(&profile_, mock_delegate_, &mock_observer_); ScopedVerifyAndClearExpectations verify(mock_delegate_); -#if BUILDFLAG(IS_ANDROID) - EXPECT_CALL(*mock_delegate_, TriggerCurrentAppStateNotification()); -#endif EXPECT_EQ( profile_.GetPrefs()->GetList(kFirstPartyStorageOriginsToCleanup).size(), 1u); + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)) + .WillOnce(testing::Return(std::nullopt)); service_->TLDEphemeralLifetimeCreated(ephemeral_domain, storage_partition_config); EXPECT_EQ( @@ -531,9 +525,9 @@ TEST_F(EphemeralStorageServiceForgetFirstPartyTest, url, url, ContentSettingsType::BRAVE_REMEMBER_1P_STORAGE, CONTENT_SETTING_BLOCK); -#if BUILDFLAG(IS_ANDROID) - EXPECT_CALL(*mock_delegate_, TriggerCurrentAppStateNotification()).Times(2); -#endif + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)) + .Times(2) + .WillRepeatedly(testing::Return(std::nullopt)); // Create tld ephemeral lifetime. service_->TLDEphemeralLifetimeCreated(ephemeral_domain, storage_partition_config); @@ -564,9 +558,8 @@ TEST_F(EphemeralStorageServiceForgetFirstPartyTest, EXPECT_EQ( profile_.GetPrefs()->GetList(kFirstPartyStorageOriginsToCleanup).size(), 2u); -#if BUILDFLAG(IS_ANDROID) - EXPECT_CALL(*mock_delegate_, TriggerCurrentAppStateNotification()); -#endif + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)) + .WillOnce(testing::Return(std::nullopt)); service_->TLDEphemeralLifetimeCreated(ephemeral_domain, storage_partition_config); EXPECT_EQ( @@ -599,9 +592,8 @@ TEST_F(EphemeralStorageServiceForgetFirstPartyTest, url, url, ContentSettingsType::BRAVE_REMEMBER_1P_STORAGE, CONTENT_SETTING_BLOCK); -#if BUILDFLAG(IS_ANDROID) - EXPECT_CALL(*mock_delegate_, TriggerCurrentAppStateNotification()); -#endif + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)) + .WillOnce(testing::Return(std::nullopt)); // Create tld ephemeral lifetime. service_->TLDEphemeralLifetimeCreated(ephemeral_domain, storage_partition_config); @@ -676,9 +668,7 @@ TEST_F(EphemeralStorageServiceForgetFirstPartyTest, OffTheRecordSkipsPrefs) { url, url, ContentSettingsType::BRAVE_REMEMBER_1P_STORAGE, CONTENT_SETTING_BLOCK); -#if BUILDFLAG(IS_ANDROID) - EXPECT_CALL(*mock_delegate_, TriggerCurrentAppStateNotification()); -#endif + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)).Times(0); // Create tld ephemeral lifetime. otr_service->TLDEphemeralLifetimeCreated(ephemeral_domain, storage_partition_config); @@ -840,9 +830,8 @@ TEST_F(EphemeralStorageServiceAutoShredForgetFirstPartyTest, test_case.auto_shred_mode.value(), url); } -#if BUILDFLAG(IS_ANDROID) - EXPECT_CALL(*mock_delegate_, TriggerCurrentAppStateNotification()); -#endif + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)) + .WillOnce(testing::Return(std::nullopt)); service_->TLDEphemeralLifetimeCreated(ephemeral_domain, storage_partition_config); EXPECT_EQ( @@ -945,9 +934,8 @@ TEST_F(EphemeralStorageServiceAutoShredForgetFirstPartyTest, CleanupOnRestart) { test_case.auto_shred_mode.value(), url); } -#if BUILDFLAG(IS_ANDROID) - EXPECT_CALL(*mock_delegate_, TriggerCurrentAppStateNotification()); -#endif + EXPECT_CALL(*mock_delegate_, GetAutoShredMode(url)) + .WillOnce(testing::Return(test_case.auto_shred_mode)); // Create tld ephemeral lifetime. service_->TLDEphemeralLifetimeCreated(ephemeral_domain, storage_partition_config); diff --git a/browser/ui/android/strings/android_brave_strings.grd b/browser/ui/android/strings/android_brave_strings.grd index bbd44476253..2e4adaa8d7d 100644 --- a/browser/ui/android/strings/android_brave_strings.grd +++ b/browser/ui/android/strings/android_brave_strings.grd @@ -1445,6 +1445,27 @@ Are you sure you want to do this? Block element + + Shred site's data + + + Auto shred: %1$s + + + Shred site data now + + + Auto shred + + + Never + + + Site tab closed + + + App close + Report a broken site @@ -3592,7 +3613,7 @@ If you don't accept this request, VPN will not reconnect and your internet conne Shredding will delete site data and close all '%1$s' tabs. This cannot be undone. - Shred + Shred site data Shred data diff --git a/components/ephemeral_storage/ephemeral_storage_service.cc b/components/ephemeral_storage/ephemeral_storage_service.cc index 81914b2045f..7bbe18ae19f 100644 --- a/components/ephemeral_storage/ephemeral_storage_service.cc +++ b/components/ephemeral_storage/ephemeral_storage_service.cc @@ -221,11 +221,6 @@ void EphemeralStorageService::TLDEphemeralLifetimeCreated( const TLDEphemeralAreaKey key(ephemeral_domain, storage_partition_config); tld_ephemeral_areas_to_cleanup_.erase(key); FirstPartyStorageAreaInUse(ephemeral_domain, storage_partition_config); -#if BUILDFLAG(IS_ANDROID) - // Triggers notification of current app state on Android. We need to call it - // at the beginning of the TLD ephemeral lifetime. - delegate_->TriggerCurrentAppStateNotification(); -#endif } void EphemeralStorageService::TLDEphemeralLifetimeDestroyed( @@ -293,6 +288,12 @@ void EphemeralStorageService::RemoveObserver( observer_list_.RemoveObserver(observer); } +#if BUILDFLAG(IS_ANDROID) +void EphemeralStorageService::TriggerCurrentAppStateNotification() { + delegate_->TriggerCurrentAppStateNotification(); +} +#endif // BUILDFLAG(IS_ANDROID) + void EphemeralStorageService::CleanupTLDFirstPartyStorage( const GURL& url, const content::StoragePartitionConfig& storage_partition_config, @@ -328,6 +329,12 @@ void EphemeralStorageService::FirstPartyStorageAreaInUse( const GURL url(GetFirstPartyStorageURL(ephemeral_domain)); const base::Value value_to_cleanup = GetFirstPartyStorageValueToCleanup(url, storage_partition_config); + auto auto_shred_mode = delegate_->GetAutoShredMode(url); + if (auto_shred_mode.has_value() && + auto_shred_mode.value() == + brave_shields::mojom::AutoShredMode::APP_EXIT) { + return; + } ScopedListPrefUpdate pref_update(prefs_, kFirstPartyStorageOriginsToCleanup); pref_update->EraseValue(value_to_cleanup); diff --git a/components/ephemeral_storage/ephemeral_storage_service.h b/components/ephemeral_storage/ephemeral_storage_service.h index b7839d2af9e..50ffff2d7d3 100644 --- a/components/ephemeral_storage/ephemeral_storage_service.h +++ b/components/ephemeral_storage/ephemeral_storage_service.h @@ -90,6 +90,9 @@ class EphemeralStorageService : public KeyedService { const GURL& url, const content::StoragePartitionConfig& storage_partition_config, const bool enforced_by_user); +#if BUILDFLAG(IS_ANDROID) + void TriggerCurrentAppStateNotification(); +#endif // BUILDFLAG(IS_ANDROID) private: FRIEND_TEST_ALL_PREFIXES(EphemeralStorageServiceAutoShredForgetFirstPartyTest,