From c829a97f13ca004fe8bbc263cff05543f474238e Mon Sep 17 00:00:00 2001 From: Tsung-Mao Fang Date: Thu, 9 Sep 2021 12:18:34 +0800 Subject: [PATCH 1/6] Extends the criteria width of split mode Follow the ui spec, we extend criteria width from 600dp -> 720dp Test: Build apk and run on the device. Fix: 197716926 Change-Id: If963ebb1f51c71a1d225198a150e4cbc2f5ef35a --- .../settings/activityembedding/ActivityEmbeddingUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java b/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java index 96c37773fc1..fd6618716e0 100644 --- a/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java +++ b/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java @@ -28,7 +28,7 @@ import androidx.window.embedding.SplitController; public class ActivityEmbeddingUtils { public static final float SPLIT_RATIO = 0.5f; // The smallest value of current width of the window when the split should be used. - private static final float MIN_CURRENT_SCREEN_SPLIT_WIDTH_DP = 600f; + private static final float MIN_CURRENT_SCREEN_SPLIT_WIDTH_DP = 720f; // The smallest value of the smallest-width (sw) of the window in any rotation when // the split should be used. private static final float MIN_SMALLEST_SCREEN_SPLIT_WIDTH_DP = 600f; From 83b5a3e01b61a81d3a46b2ea36f577aa596869e1 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Wed, 8 Sep 2021 17:45:47 +0800 Subject: [PATCH 2/6] Update homepage icon of Safety & emergency Fixes: 197752821 Test: visual Change-Id: Ib303b66fba556f0779f1c11033bf3c901b98101e --- res/drawable/ic_settings_emergency.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/drawable/ic_settings_emergency.xml b/res/drawable/ic_settings_emergency.xml index 25d42723bb3..d331e3e4161 100644 --- a/res/drawable/ic_settings_emergency.xml +++ b/res/drawable/ic_settings_emergency.xml @@ -19,6 +19,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> From 22ce392b9161f85cee413ee27ea5bb0f2c9f1714 Mon Sep 17 00:00:00 2001 From: Arc Wang Date: Tue, 31 Aug 2021 18:11:57 +0800 Subject: [PATCH 3/6] 2 panes deep link for large screen devices This change supports deep link to Settings app internal pages and external pages outside Settings app. Apps need android.permission.ALLOW_TWO_PANES_DEEP_LINK_IN_SETTINGS to send the intent of Settings#ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK. Settings app will startActivity for the intent from Settings#EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI. Bug: 197048599 Test: build pass Change-Id: Idaf4a8be4603c1308f16fb4e378266c1e52acb40 --- AndroidManifest.xml | 13 +++ .../android/settings/SettingsActivity.java | 23 ++++++ .../ActivityEmbeddingRulesController.java | 50 ++++++------ .../homepage/SettingsHomepageActivity.java | 80 +++++++++++++++++++ 4 files changed, 142 insertions(+), 24 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d8bbe8f2de7..2c3eb057fc3 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -107,6 +107,8 @@ + + + + + + + + + + diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index f3cdd6c1640..178892e6f5f 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -58,6 +58,8 @@ import com.android.settings.core.SettingsBaseActivity; import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.gateway.SettingsGateway; import com.android.settings.dashboard.DashboardFeatureProvider; +import com.android.settings.activityembedding.ActivityEmbeddingUtils; +import com.android.settings.homepage.SettingsHomepageActivity; import com.android.settings.homepage.TopLevelSettings; import com.android.settings.overlay.FeatureFactory; import com.android.settings.wfd.WifiDisplaySettings; @@ -240,7 +242,22 @@ public class SettingsActivity extends SettingsBaseActivity // Should happen before any call to getIntent() getMetaData(); + // If it's a deep link intent, start the Activity from SettingsHomepageActivity for 2-pane. final Intent intent = getIntent(); + final boolean isFromSettingsHomepage = intent.getBooleanExtra( + SettingsHomepageActivity.EXTRA_IS_FROM_SETTINGS_HOMEPAGE, /* defaultValue */ false); + if (ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this) && !isFromSettingsHomepage + && isOnlyOneActivityInActivityStack()) { + final Intent trampolineIntent = + new Intent(android.provider.Settings.ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK); + trampolineIntent.putExtra( + android.provider.Settings.EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI, + intent.toUri(Intent.URI_INTENT_SCHEME)); + startActivity(trampolineIntent); + finish(); + return; + } + if (intent.hasExtra(EXTRA_UI_OPTIONS)) { getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0)); } @@ -347,6 +364,12 @@ public class SettingsActivity extends SettingsBaseActivity } } + private boolean isOnlyOneActivityInActivityStack() { + final ActivityManager activityManager = getSystemService(ActivityManager.class); + List taskList = activityManager.getRunningTasks(2); + return taskList.get(0).numActivities == 1; + } + /** Returns the initial fragment name that the activity will launch. */ @VisibleForTesting public String getInitialFragmentName(Intent intent) { diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java index f1a1ecd023b..994098083ba 100644 --- a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java +++ b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java @@ -60,12 +60,34 @@ public class ActivityEmbeddingRulesController { mSplitController.clearRegisteredRules(); // Set a placeholder for home page. - mSplitController.registerRule(getHomepagePlaceholderRule()); + registerHomepagePlaceholderRule(); // Set subsettings rule. - mSplitController.registerRule(getSubSettingsPairRule()); + registerTwoPanePairRule(mContext, + getComponentName(Settings.class), + getComponentName(SubSettings.class), + true /* finishPrimaryWithSecondary */, + true /* finishSecondaryWithPrimary */); } - private SplitPlaceholderRule getHomepagePlaceholderRule() { + /** Register a SplitPairRule for 2-pane. */ + public static void registerTwoPanePairRule(Context context, + ComponentName primary, ComponentName secondary, + boolean finishPrimaryWithSecondary, boolean finishSecondaryWithPrimary) { + final Set filters = new HashSet<>(); + filters.add(new SplitPairFilter(primary, secondary, + null /* secondaryActivityIntentAction */, + null /* secondaryActivityIntentCategory */)); + + new SplitController(context).registerRule(new SplitPairRule(filters, + finishPrimaryWithSecondary, + finishSecondaryWithPrimary, true /* clearTop */, + ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthPx(context), + ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthPx(context), + ActivityEmbeddingUtils.SPLIT_RATIO, + LayoutDirection.LOCALE)); + } + + private void registerHomepagePlaceholderRule() { final Set activityFilters = new HashSet<>(); activityFilters.add(new ActivityFilter(getComponentName(Settings.class))); final Intent intent = new Intent(); @@ -78,27 +100,7 @@ public class ActivityEmbeddingRulesController { ActivityEmbeddingUtils.SPLIT_RATIO, LayoutDirection.LOCALE); - return placeholderRule; - } - - private SplitPairRule getSubSettingsPairRule() { - final Set pairFilters = new HashSet<>(); - pairFilters.add(new SplitPairFilter( - getComponentName(Settings.class), - getComponentName(SubSettings.class), - null /* secondaryActivityIntentAction */, - null /* secondaryActivityIntentCategory */)); - final SplitPairRule rule = new SplitPairRule( - pairFilters, - true /* finishPrimaryWithSecondary */, - true /* finishSecondaryWithPrimary */, - true /* clearTop */, - ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthPx(mContext), - ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthPx(mContext), - ActivityEmbeddingUtils.SPLIT_RATIO, - LayoutDirection.LOCALE); - - return rule; + mSplitController.registerRule(placeholderRule); } @NonNull diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java index 1d7b5dc14b9..73f0abb1f2c 100644 --- a/src/com/android/settings/homepage/SettingsHomepageActivity.java +++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java @@ -18,8 +18,14 @@ package com.android.settings.homepage; import android.animation.LayoutTransition; import android.app.ActivityManager; +import android.app.PendingIntent; import android.app.settings.SettingsEnums; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.PackageManager; import android.os.Bundle; +import android.provider.Settings; +import android.text.TextUtils; import android.util.FeatureFlagUtils; import android.util.Log; import android.view.View; @@ -31,21 +37,33 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; +import androidx.window.embedding.SplitController; import com.android.settings.R; +import com.android.settings.Utils; import com.android.settings.accounts.AvatarViewMixin; import com.android.settings.core.CategoryMixin; import com.android.settings.core.FeatureFlags; +import com.android.settings.activityembedding.ActivityEmbeddingRulesController; +import com.android.settings.activityembedding.ActivityEmbeddingUtils; import com.android.settings.homepage.contextualcards.ContextualCardsFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin; +import java.net.URISyntaxException; + /** Settings homepage activity */ public class SettingsHomepageActivity extends FragmentActivity implements CategoryMixin.CategoryHandler { private static final String TAG = "SettingsHomepageActivity"; + // Put true value to the intent when startActivity for a deep link intent from this Activity. + public static final String EXTRA_IS_FROM_SETTINGS_HOMEPAGE = "is_from_settings_homepage"; + + // An alias class name of SettingsHomepageActivity. + private static final String ALIAS_DEEP_LINK = "com.android.settings.DeepLinkHomepageActivity"; + private static final long HOMEPAGE_LOADING_TIMEOUT_MS = 300; private View mHomepageView; @@ -105,6 +123,20 @@ public class SettingsHomepageActivity extends FragmentActivity implements showFragment(new TopLevelSettings(), R.id.main_content); ((FrameLayout) findViewById(R.id.main_content)) .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); + + // Launch the intent from deep link for large screen devices. + launchDeepLinkIntentToRight(); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + + // When it's large screen 2-pane and Settings app is in background. Receiving a Intent + // in this Activity will not finish nor onCreate. setIntent here for this case. + setIntent(intent); + // Launch the intent from deep link for large screen devices. + launchDeepLinkIntentToRight(); } private void showSuggestionFragment() { @@ -141,6 +173,54 @@ public class SettingsHomepageActivity extends FragmentActivity implements fragmentTransaction.commit(); } + private void launchDeepLinkIntentToRight() { + if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this)) { + return; + } + + final Intent intent = getIntent(); + if (intent == null || !TextUtils.equals(intent.getAction(), + Settings.ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK)) { + return; + } + + final String intentUriString = intent.getStringExtra( + Settings.EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI); + if (TextUtils.isEmpty(intentUriString)) { + Log.e(TAG, "No EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI to deep link"); + finish(); + return; + } + + final Intent targetIntent; + try { + targetIntent = Intent.parseUri(intentUriString, Intent.URI_INTENT_SCHEME); + } catch (URISyntaxException e) { + Log.e(TAG, "Failed to parse deep link intent: " + e); + finish(); + return; + } + + final ComponentName targetComponentName = targetIntent.resolveActivity(getPackageManager()); + if (targetComponentName == null) { + Log.e(TAG, "No valid target for the deep link intent: " + targetIntent); + finish(); + return; + } + + targetIntent.setFlags(targetIntent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); + + targetIntent.putExtra(EXTRA_IS_FROM_SETTINGS_HOMEPAGE, true); + + // Set 2-pane pair rule for the external deep link page. + ActivityEmbeddingRulesController.registerTwoPanePairRule(this, + new ComponentName(Utils.SETTINGS_PACKAGE_NAME, ALIAS_DEEP_LINK), + targetComponentName, + true /* finishPrimaryWithSecondary */, + true /* finishSecondaryWithPrimary */); + startActivity(targetIntent); + } + private void initHomepageContainer() { final View view = findViewById(R.id.homepage_container); // Prevent inner RecyclerView gets focus and invokes scrolling. From 597784e4558fdea631092bf199285009bb850f82 Mon Sep 17 00:00:00 2001 From: Arc Wang Date: Thu, 9 Sep 2021 15:59:01 +0800 Subject: [PATCH 4/6] Set 2-pane placeholder for all alias of SettingsHomepageActivity SettingsHomepageActivity may be launched by different alias, this change sets placeholder for all these alias to ensure placeholder is present. Bug: 199363383 Test: manual 1. Click Settings icon button in quick setting panel. 2. Launch Settings app deep link, then move it to background, and the launch Settings app. Change-Id: I9ba6dbc5cfa2bac51bf53bb21f8983193ba053b6 --- .../activityembedding/ActivityEmbeddingRulesController.java | 5 +++++ .../android/settings/homepage/SettingsHomepageActivity.java | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java index 994098083ba..a0f4aad4153 100644 --- a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java +++ b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java @@ -32,6 +32,8 @@ import androidx.window.embedding.SplitPlaceholderRule; import com.android.settings.Settings; import com.android.settings.SubSettings; +import com.android.settings.Utils; +import com.android.settings.homepage.SettingsHomepageActivity; import java.util.HashSet; import java.util.Set; @@ -89,7 +91,10 @@ public class ActivityEmbeddingRulesController { private void registerHomepagePlaceholderRule() { final Set activityFilters = new HashSet<>(); + activityFilters.add(new ActivityFilter(getComponentName(SettingsHomepageActivity.class))); activityFilters.add(new ActivityFilter(getComponentName(Settings.class))); + activityFilters.add(new ActivityFilter(new ComponentName(Utils.SETTINGS_PACKAGE_NAME, + SettingsHomepageActivity.ALIAS_DEEP_LINK))); final Intent intent = new Intent(); intent.setComponent(getComponentName(Settings.NetworkDashboardActivity.class)); final SplitPlaceholderRule placeholderRule = new SplitPlaceholderRule( diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java index 73f0abb1f2c..4a0a912323c 100644 --- a/src/com/android/settings/homepage/SettingsHomepageActivity.java +++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java @@ -62,7 +62,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements public static final String EXTRA_IS_FROM_SETTINGS_HOMEPAGE = "is_from_settings_homepage"; // An alias class name of SettingsHomepageActivity. - private static final String ALIAS_DEEP_LINK = "com.android.settings.DeepLinkHomepageActivity"; + public static final String ALIAS_DEEP_LINK = "com.android.settings.DeepLinkHomepageActivity"; private static final long HOMEPAGE_LOADING_TIMEOUT_MS = 300; From 600672c91d3c11c51a13412d8dba225b3c7e293b Mon Sep 17 00:00:00 2001 From: Tsung-Mao Fang Date: Thu, 9 Sep 2021 08:30:28 +0000 Subject: [PATCH 5/6] Revert "Extends the criteria width of split mode" This reverts commit c829a97f13ca004fe8bbc263cff05543f474238e. Reason for revert: Bug: 15786013 Change-Id: I58024f143f8c59f8b0cf50d5cd284f84600e36e6 --- .../settings/activityembedding/ActivityEmbeddingUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java b/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java index fd6618716e0..96c37773fc1 100644 --- a/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java +++ b/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java @@ -28,7 +28,7 @@ import androidx.window.embedding.SplitController; public class ActivityEmbeddingUtils { public static final float SPLIT_RATIO = 0.5f; // The smallest value of current width of the window when the split should be used. - private static final float MIN_CURRENT_SCREEN_SPLIT_WIDTH_DP = 720f; + private static final float MIN_CURRENT_SCREEN_SPLIT_WIDTH_DP = 600f; // The smallest value of the smallest-width (sw) of the window in any rotation when // the split should be used. private static final float MIN_SMALLEST_SCREEN_SPLIT_WIDTH_DP = 600f; From 083ace54d5cc29f9e22bec26fc2d9533aa13a7b9 Mon Sep 17 00:00:00 2001 From: "Wesley.CW Wang" Date: Thu, 2 Sep 2021 17:28:53 +0800 Subject: [PATCH 6/6] Enhance battery settings limit charge tip - Add dialog for limit charge tips - Add action button to make limit charge tips can bypass charge limited - Fix layout typo Screenshot: https://screenshot.googleplex.com/5ngtD2sJAKSQZD3.png Bug: 196315151 Bug: 197769934 Test: make SettingsRoboTests Change-Id: I0901c56ae93691c984376852da78cde9078caaf0 Merged-In: I0901c56ae93691c984376852da78cde9078caaf0 --- res/values/strings.xml | 6 ++ res/xml/smart_battery_detail.xml | 2 +- .../fuelgauge/PowerUsageFeatureProvider.java | 5 ++ .../PowerUsageFeatureProviderImpl.java | 5 ++ .../batterytip/BatteryTipDialogFragment.java | 34 +++++++++ .../actions/BatteryDefenderAction.java | 19 ++--- .../detectors/BatteryDefenderDetector.java | 3 +- .../batterytip/tips/BatteryDefenderTip.java | 2 +- .../PowerUsageFeatureProviderImplTest.java | 5 ++ .../BatteryTipDialogFragmentTest.java | 19 +++++ .../actions/BatteryDefenderActionTest.java | 70 ------------------- 11 files changed, 83 insertions(+), 87 deletions(-) delete mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/BatteryDefenderActionTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index c1832b5b637..0bce0bc52d3 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6158,6 +6158,12 @@ Charging temporarily limited To preserve your battery. Learn more. + + Resume charging + + In certain conditions, like high temperatures and long charging periods, charging may be limited to %1$s to help preserve battery health.\n\nWhen those conditions end, your phone will automatically charge normally. + + In certain conditions, like high temperatures and long charging periods, charging may be limited to %1$s to help preserve battery health.\n\nWhen those conditions end, your tablet will automatically charge normally. Because you’ve used your phone more than usual, your battery may run out sooner than it normally would.\n\nApps using most battery: diff --git a/res/xml/smart_battery_detail.xml b/res/xml/smart_battery_detail.xml index 9c0aac58318..97951252525 100644 --- a/res/xml/smart_battery_detail.xml +++ b/res/xml/smart_battery_detail.xml @@ -22,7 +22,7 @@ android:title="@string/smart_battery_manager_title" settings:keywords="@string/keywords_battery_adaptive_preferences"> - diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java index eff538fa644..bab88110bc6 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java @@ -138,6 +138,11 @@ public interface PowerUsageFeatureProvider { */ boolean isChartGraphSlotsEnabled(Context context); + /** + * Gets a intent for one time bypass charge limited to resume charging. + */ + Intent getResumeChargeIntent(); + /** * Returns battery history data with corresponding timestamp key. */ diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java index 7f1bef0ae23..208a478b41b 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java @@ -165,6 +165,11 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider return false; } + @Override + public Intent getResumeChargeIntent() { + return null; + } + @Override public Map> getBatteryHistory(Context context) { return null; diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java index 58038cd4bb4..e9e5d68c067 100644 --- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java +++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java @@ -20,6 +20,9 @@ import android.app.Dialog; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryManager; import android.os.Bundle; import android.view.LayoutInflater; @@ -40,6 +43,7 @@ import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip; import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip; +import java.text.NumberFormat; import java.util.List; /** @@ -50,6 +54,7 @@ public class BatteryTipDialogFragment extends InstrumentedDialogFragment impleme private static final String ARG_BATTERY_TIP = "battery_tip"; private static final String ARG_METRICS_KEY = "metrics_key"; + private static final double CHARGE_LIMIT_LEVEL = 0.8f; @VisibleForTesting BatteryTip mBatteryTip; @@ -138,6 +143,28 @@ public class BatteryTipDialogFragment extends InstrumentedDialogFragment impleme .setPositiveButton(R.string.battery_tip_unrestrict_app_dialog_ok, this) .setNegativeButton(R.string.battery_tip_unrestrict_app_dialog_cancel, null) .create(); + case BatteryTip.TipType.BATTERY_DEFENDER: + mMetricsFeatureProvider.action(context, + SettingsEnums.ACTION_TIP_BATTERY_DEFENDER, mMetricsKey); + final String percentage = + NumberFormat.getPercentInstance().format(CHARGE_LIMIT_LEVEL); + final String message = context.getString( + R.string.battery_tip_limited_temporarily_dialog_msg, percentage); + final boolean isPluggedIn = isPluggedIn(); + final AlertDialog.Builder dialogBuilder = + new AlertDialog.Builder(context) + .setTitle(R.string.battery_tip_limited_temporarily_title) + .setMessage(message); + if (isPluggedIn) { + dialogBuilder + .setPositiveButton( + R.string.battery_tip_limited_temporarily_dialog_resume_charge, + this) + .setNegativeButton(R.string.okay, null); + } else { + dialogBuilder.setPositiveButton(R.string.okay, null); + } + return dialogBuilder.create(); default: throw new IllegalArgumentException("unknown type " + mBatteryTip.getType()); } @@ -163,4 +190,11 @@ public class BatteryTipDialogFragment extends InstrumentedDialogFragment impleme lsn.onBatteryTipHandled(mBatteryTip); } + private boolean isPluggedIn() { + final Intent batteryIntent = getContext().registerReceiver(null /* receiver */, + new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + return batteryIntent != null && batteryIntent.getIntExtra( + BatteryManager.EXTRA_PLUGGED, 0) != 0; + } + } diff --git a/src/com/android/settings/fuelgauge/batterytip/actions/BatteryDefenderAction.java b/src/com/android/settings/fuelgauge/batterytip/actions/BatteryDefenderAction.java index 24cddcd809e..af16952aa5c 100644 --- a/src/com/android/settings/fuelgauge/batterytip/actions/BatteryDefenderAction.java +++ b/src/com/android/settings/fuelgauge/batterytip/actions/BatteryDefenderAction.java @@ -16,12 +16,11 @@ package com.android.settings.fuelgauge.batterytip.actions; -import android.app.settings.SettingsEnums; import android.content.Intent; -import com.android.settings.R; import com.android.settings.SettingsActivity; -import com.android.settingslib.HelpUtils; +import com.android.settings.overlay.FeatureFactory; +import android.os.AsyncTask; /** * Action to open the Support Center article @@ -34,19 +33,13 @@ public class BatteryDefenderAction extends BatteryTipAction { mSettingsActivity = settingsActivity; } - /** - * Handle the action when user clicks positive button - */ @Override public void handlePositiveAction(int metricsKey) { - mMetricsFeatureProvider.action(mContext, - SettingsEnums.ACTION_TIP_BATTERY_DEFENDER, metricsKey); - final Intent intent = HelpUtils.getHelpIntent( - mContext, - mContext.getString(R.string.help_url_battery_defender), - getClass().getName()); + final Intent intent = FeatureFactory.getFactory(mContext) + .getPowerUsageFeatureProvider(mContext).getResumeChargeIntent(); if (intent != null) { - mSettingsActivity.startActivityForResult(intent, 0); + // Post intent to background thread to avoid UI flaky + AsyncTask.execute(() -> mContext.sendBroadcast(intent)); } } } diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetector.java index dc33026c934..5befa330b0a 100644 --- a/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetector.java +++ b/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetector.java @@ -17,7 +17,6 @@ package com.android.settings.fuelgauge.batterytip.detectors; import com.android.settings.fuelgauge.BatteryInfo; -import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.batterytip.tips.BatteryDefenderTip; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; @@ -34,7 +33,7 @@ public class BatteryDefenderDetector implements BatteryTipDetector { @Override public BatteryTip detect() { final int state = - BatteryUtils.isBatteryDefenderOn(mBatteryInfo) + mBatteryInfo.isOverheated ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE; return new BatteryDefenderTip(state); diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java index 242be24b44e..a2890ad9b40 100644 --- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java +++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java @@ -29,7 +29,7 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; public class BatteryDefenderTip extends BatteryTip { public BatteryDefenderTip(@StateType int state) { - super(TipType.BATTERY_DEFENDER, state, false /* showDialog */); + super(TipType.BATTERY_DEFENDER, state, true /* showDialog */); } private BatteryDefenderTip(Parcel in) { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java index e8a9c5cb05a..c0b566a5f4b 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java @@ -155,4 +155,9 @@ public class PowerUsageFeatureProviderImplTest { assertThat(mPowerFeatureProvider.isSmartBatterySupported()).isFalse(); } + + @Test + public void testGetResumeChargeIntent_returnNull() { + assertThat(mPowerFeatureProvider.getResumeChargeIntent()).isNull(); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java index 331fb343d43..99fbaf3c812 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java @@ -31,6 +31,7 @@ import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.FragmentActivity; import com.android.settings.R; +import com.android.settings.fuelgauge.batterytip.tips.BatteryDefenderTip; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip; import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; @@ -74,6 +75,7 @@ public class BatteryTipDialogFragmentTest { private RestrictAppTip mRestrictTwoAppsTip; private UnrestrictAppTip mUnrestrictAppTip; private SummaryTip mSummaryTip; + private BatteryDefenderTip mDefenderTip; private AppInfo mAppInfo; private ShadowPackageManager mPackageManager; @@ -116,6 +118,7 @@ public class BatteryTipDialogFragmentTest { mUnrestrictAppTip = new UnrestrictAppTip(BatteryTip.StateType.NEW, mAppInfo); mSummaryTip = spy(new SummaryTip(BatteryTip.StateType.NEW, EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN)); + mDefenderTip = new BatteryDefenderTip(BatteryTip.StateType.NEW); } @After @@ -243,4 +246,20 @@ public class BatteryTipDialogFragmentTest { + "your phone will suggest actions you can take.\n\nYou can always turn" + " on Battery Saver if you’re running low on battery."); } + + @Test + public void testOnCreateDialog_defenderTip_fireDialog() { + mDialogFragment = BatteryTipDialogFragment.newInstance(mDefenderTip, METRICS_KEY); + + FragmentController.setupFragment(mDialogFragment, FragmentActivity.class, + 0 /* containerViewId */, null /* bundle */); + + final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog); + + assertThat(shadowDialog.getTitle()).isEqualTo( + mContext.getString(R.string.battery_tip_limited_temporarily_title)); + assertThat(shadowDialog.getMessage()).isEqualTo( + mContext.getString(R.string.battery_tip_limited_temporarily_dialog_msg, "80%")); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/BatteryDefenderActionTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/BatteryDefenderActionTest.java deleted file mode 100644 index ad5dc54ca9b..00000000000 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/BatteryDefenderActionTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.settings.fuelgauge.batterytip.actions; - - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -import android.app.settings.SettingsEnums; -import android.content.Context; - -import com.android.settings.R; -import com.android.settings.SettingsActivity; -import com.android.settings.testutils.FakeFeatureFactory; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -@RunWith(RobolectricTestRunner.class) -public final class BatteryDefenderActionTest { - - private Context mContext; - private FakeFeatureFactory mFeatureFactory; - private BatteryDefenderAction mBatteryDefenderAction; - private MetricsFeatureProvider mMetricsFeatureProvider; - - @Mock private SettingsActivity mSettingsActivity; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mFeatureFactory = FakeFeatureFactory.setupForTest(); - mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider; - mContext = spy(RuntimeEnvironment.application); - doReturn(mContext).when(mSettingsActivity).getApplicationContext(); - mBatteryDefenderAction = new BatteryDefenderAction(mSettingsActivity); - } - - @Test - public void testHandlePositiveAction_logMetric() { - final int metricKey = 10; - mBatteryDefenderAction.handlePositiveAction(metricKey); - - verify(mMetricsFeatureProvider).action(mContext, - SettingsEnums.ACTION_TIP_BATTERY_DEFENDER, metricKey); - } -}