Settings: Add charging control preferences
Change-Id: I936add4df464eb9818258de4955b651266c67ec3
diff --git a/res/drawable/ic_settings_backup_restore.xml b/res/drawable/ic_settings_backup_restore.xml
new file mode 100644
index 0000000..245b0fc
--- /dev/null
+++ b/res/drawable/ic_settings_backup_restore.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2017 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:pathData="M14,12c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2 0.9,2 2,2 2,-0.9 2,-2zM12,3c-4.97,0 -9,4.03 -9,9L0,12l4,4 4,-4L5,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.51,0 -2.91,-0.49 -4.06,-1.3l-1.42,1.44C8.04,20.3 9.94,21 12,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/res/layout/dialog_time.xml b/res/layout/dialog_time.xml
new file mode 100644
index 0000000..3572317
--- /dev/null
+++ b/res/layout/dialog_time.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 The LineageOS 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <TimePicker
+ android:id="@+id/time_picker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingTop="8dp" />
+
+</LinearLayout>
diff --git a/res/layout/preference_charging_limit.xml b/res/layout/preference_charging_limit.xml
new file mode 100644
index 0000000..9c2b4fe
--- /dev/null
+++ b/res/layout/preference_charging_limit.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 The LineageOS 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:paddingVertical="6dip"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="vertical">
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorPrimary"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+
+ <TextView
+ android:id="@+id/value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:singleLine="true" />
+
+ </RelativeLayout>
+
+ <SeekBar
+ android:id="@+id/seekbar_widget"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingVertical="15dip"
+ android:min="70"
+ android:max="100" />
+
+</LinearLayout>
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml
index da8a840..6c9e6a9 100644
--- a/res/values/cm_strings.xml
+++ b/res/values/cm_strings.xml
@@ -87,4 +87,22 @@
<!-- Backup Transport selection settings menu and activity title -->
<string name="backup_transport_setting_label">Change backup provider</string>
<string name="backup_transport_title">Select backup provider</string>
+
+ <!-- Charging control settings -->
+ <string name="charging_control_title">Charging control</string>
+ <string name="charging_control_summary">Customize charging schedule and limits for battery</string>
+ <string name="charging_control_enable_title">Enable charging control</string>
+ <string name="charging_control_mode_title">Charging mode</string>
+ <string name="charging_control_mode_auto_title">Automatic schedule</string>
+ <string name="charging_control_mode_auto_summary">Automatically determine when to start charging based on alarms set</string>
+ <string name="charging_control_mode_custom_title">Custom schedule</string>
+ <string name="charging_control_mode_custom_summary">Set a target time to full charge</string>
+ <string name="charging_control_mode_limit_title">Limit charging</string>
+ <string name="charging_control_mode_limit_summary">Limit charging to a certain percentage</string>
+ <string name="charging_control_start_time_title">Start time</string>
+ <string name="charging_control_start_time_summary">Charging control activates when you start charging after %s</string>
+ <string name="charging_control_target_time_title">Target time to full charge</string>
+ <string name="charging_control_target_time_summary">Battery will be fully charged by %s</string>
+ <string name="charging_control_limit_title">Limit</string>
+ <string name="reset">Reset</string>
</resources>
diff --git a/res/values/leaf_arrays.xml b/res/values/leaf_arrays.xml
index db338eb..d541341 100644
--- a/res/values/leaf_arrays.xml
+++ b/res/values/leaf_arrays.xml
@@ -74,4 +74,24 @@
<item>1</item>
<item>2</item>
</string-array>
+
+ <!-- Values for the charging control modes -->
+ <string-array name="charging_control_mode_entries" translatable="false">
+ <item>@string/charging_control_mode_auto_title</item>
+ <item>@string/charging_control_mode_custom_title</item>
+ </string-array>
+
+ <string-array name="charging_control_mode_values" translatable="false">
+ <item>1</item>
+ <item>2</item>
+ </string-array>
+
+ <!-- Values for the additional charging control modes if fine-grained control is supported -->
+ <string-array name="charging_control_mode_entries_fine_grained_control" translatable="false">
+ <item>@string/charging_control_mode_limit_title</item>
+ </string-array>
+
+ <string-array name="charging_control_mode_values_fine_grained_control" translatable="false">
+ <item>3</item>
+ </string-array>
</resources>
diff --git a/res/xml/charging_control_settings.xml b/res/xml/charging_control_settings.xml
new file mode 100644
index 0000000..4ffcfbf
--- /dev/null
+++ b/res/xml/charging_control_settings.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 The LineageOS 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ android:key="charging_control"
+ android:title="@string/charging_control_title">
+
+ <com.android.settingslib.widget.MainSwitchPreference
+ android:key="charging_control_enabled"
+ android:title="@string/charging_control_enable_title" />
+
+ <ListPreference
+ android:key="charging_control_mode"
+ android:title="@string/charging_control_mode_title"
+ android:entries="@array/charging_control_mode_entries"
+ android:entryValues="@array/charging_control_mode_values"
+ android:dependency="charging_control_enabled" />
+
+ <com.android.settings.lineage.health.StartTimePreference
+ android:key="charging_control_start_time"
+ android:title="@string/charging_control_start_time_title"
+ android:dependency="charging_control_enabled" />
+
+ <com.android.settings.lineage.health.TargetTimePreference
+ android:key="charging_control_target_time"
+ android:title="@string/charging_control_target_time_title"
+ android:dependency="charging_control_enabled" />
+
+ <com.android.settings.lineage.health.ChargingLimitPreference
+ android:key="charging_control_charging_limit"
+ android:title="@string/charging_control_limit_title"
+ android:dependency="charging_control_enabled" />
+
+</PreferenceScreen>
diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml
index 34df467..527d67e 100644
--- a/res/xml/power_usage_summary.xml
+++ b/res/xml/power_usage_summary.xml
@@ -63,6 +63,13 @@
android:summary="@string/fast_charging_summary"
settings:controller="com.android.settings.fuelgauge.FastChargingPreferenceController"/>
+ <Preference
+ android:key="charging_control"
+ android:title="@string/charging_control_title"
+ android:summary="@string/charging_control_summary"
+ android:fragment="com.android.settings.lineage.health.ChargingControlSettings"
+ settings:controller="com.android.settings.lineage.health.ChargingControlPreferenceController" />
+
<com.android.settingslib.widget.FooterPreference
android:key="power_usage_footer"
android:title="@string/battery_footer_summary"
diff --git a/src/com/android/settings/lineage/CustomDialogPreference.java b/src/com/android/settings/lineage/CustomDialogPreference.java
new file mode 100644
index 0000000..5c62c41
--- /dev/null
+++ b/src/com/android/settings/lineage/CustomDialogPreference.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2015 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.lineage;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.PreferenceDialogFragmentCompat;
+import androidx.preference.DialogPreference;
+
+public class CustomDialogPreference<T extends DialogInterface> extends DialogPreference {
+
+ private CustomPreferenceDialogFragment mFragment;
+
+ public CustomDialogPreference(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public CustomDialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public CustomDialogPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CustomDialogPreference(Context context) {
+ super(context);
+ }
+
+ public boolean isDialogOpen() {
+ return getDialog() != null && getDialog() instanceof Dialog && ((Dialog)getDialog()).isShowing();
+ }
+
+ public T getDialog() {
+ return (T) (mFragment != null ? mFragment.getDialog() : null);
+ }
+
+ protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
+ DialogInterface.OnClickListener listener) {
+ }
+
+ protected void onDialogClosed(boolean positiveResult) {
+ }
+
+ protected void onClick(T dialog, int which) {
+ }
+
+ protected void onBindDialogView(View view) {
+ }
+
+ protected void onStart() {
+ }
+
+ protected void onStop() {
+ }
+
+ protected void onPause() {
+ }
+
+ protected void onResume() {
+ }
+
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return null;
+ }
+
+ protected View onCreateDialogView(Context context) {
+ return null;
+ }
+
+ private void setFragment(CustomPreferenceDialogFragment fragment) {
+ mFragment = fragment;
+ }
+
+ protected boolean onDismissDialog(T dialog, int which) {
+ return true;
+ }
+
+ public static class CustomPreferenceDialogFragment extends PreferenceDialogFragmentCompat {
+
+ public static CustomPreferenceDialogFragment newInstance(String key) {
+ final CustomPreferenceDialogFragment fragment = new CustomPreferenceDialogFragment();
+ final Bundle b = new Bundle(1);
+ b.putString(ARG_KEY, key);
+ fragment.setArguments(b);
+ return fragment;
+ }
+
+ private CustomDialogPreference getCustomizablePreference() {
+ return (CustomDialogPreference) getPreference();
+ }
+
+ private class OnDismissListener implements View.OnClickListener {
+ private final int mWhich;
+ private final DialogInterface mDialog;
+
+ public OnDismissListener(DialogInterface dialog, int which) {
+ mWhich = which;
+ mDialog = dialog;
+ }
+
+ @Override
+ public void onClick(View view) {
+ CustomPreferenceDialogFragment.this.onClick(mDialog, mWhich);
+ if (getCustomizablePreference().onDismissDialog(mDialog, mWhich)) {
+ mDialog.dismiss();
+ }
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (getDialog() instanceof AlertDialog) {
+ AlertDialog a = (AlertDialog)getDialog();
+ if (a.getButton(Dialog.BUTTON_NEUTRAL) != null) {
+ a.getButton(Dialog.BUTTON_NEUTRAL).setOnClickListener(
+ new OnDismissListener(a, Dialog.BUTTON_NEUTRAL));
+ }
+ if (a.getButton(Dialog.BUTTON_POSITIVE) != null) {
+ a.getButton(Dialog.BUTTON_POSITIVE).setOnClickListener(
+ new OnDismissListener(a, Dialog.BUTTON_POSITIVE));
+ }
+ if (a.getButton(Dialog.BUTTON_NEGATIVE) != null) {
+ a.getButton(Dialog.BUTTON_NEGATIVE).setOnClickListener(
+ new OnDismissListener(a, Dialog.BUTTON_NEGATIVE));
+ }
+ }
+ getCustomizablePreference().onStart();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ getCustomizablePreference().onStop();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ getCustomizablePreference().onPause();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ getCustomizablePreference().onResume();
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+ super.onPrepareDialogBuilder(builder);
+ getCustomizablePreference().setFragment(this);
+ getCustomizablePreference().onPrepareDialogBuilder(builder, this);
+ }
+
+ @Override
+ public void onDialogClosed(boolean positiveResult) {
+ getCustomizablePreference().onDialogClosed(positiveResult);
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+ getCustomizablePreference().onBindDialogView(view);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ super.onClick(dialog, which);
+ getCustomizablePreference().onClick(dialog, which);
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ getCustomizablePreference().setFragment(this);
+ final Dialog sub = getCustomizablePreference().onCreateDialog(savedInstanceState);
+ if (sub == null) {
+ return super.onCreateDialog(savedInstanceState);
+ }
+ return sub;
+ }
+
+ @Override
+ protected View onCreateDialogView(Context context) {
+ final View v = getCustomizablePreference().onCreateDialogView(context);
+ if (v == null) {
+ return super.onCreateDialogView(context);
+ }
+ return v;
+ }
+ }
+}
diff --git a/src/com/android/settings/lineage/health/ChargingControlPreferenceController.java b/src/com/android/settings/lineage/health/ChargingControlPreferenceController.java
new file mode 100644
index 0000000..e41ea52
--- /dev/null
+++ b/src/com/android/settings/lineage/health/ChargingControlPreferenceController.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The PixelExperience Project
+ * Copyright (C) 2023 The lineage Foundation
+ *
+ * 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.lineage.health;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.ServiceManager;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.R;
+
+public class ChargingControlPreferenceController extends BasePreferenceController {
+
+ public static final String KEY = "charging_control";
+
+ private Context mContext;
+
+ public ChargingControlPreferenceController(Context context, String key) {
+ super(context, key);
+
+ mContext = context;
+ }
+
+ public ChargingControlPreferenceController(Context context) {
+ this(context, KEY);
+
+ mContext = context;
+ }
+
+ private boolean isNegated(String key) {
+ return key != null && key.startsWith("!");
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ String rService = "lineagehealth";
+ boolean negated = isNegated(rService);
+ if (negated) {
+ rService = rService.substring(1);
+ }
+ IBinder value = ServiceManager.getService(rService);
+ boolean available = value != null;
+ if (available == negated) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
+ return AVAILABLE;
+ }
+
+}
diff --git a/src/com/android/settings/lineage/health/ChargingControlSettings.java b/src/com/android/settings/lineage/health/ChargingControlSettings.java
new file mode 100644
index 0000000..5a4d7ef
--- /dev/null
+++ b/src/com/android/settings/lineage/health/ChargingControlSettings.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2023 The LineageOS 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.lineage.health;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+import androidx.fragment.app.DialogFragment;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Stream;
+
+import com.android.internal.lineage.health.HealthInterface;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+import com.android.settings.lineage.CustomDialogPreference;
+import androidx.preference.ListPreference;
+import com.android.settingslib.widget.MainSwitchPreference;
+
+import static com.android.internal.lineage.health.HealthInterface.MODE_AUTO;
+import static com.android.internal.lineage.health.HealthInterface.MODE_MANUAL;
+import static com.android.internal.lineage.health.HealthInterface.MODE_LIMIT;
+
+@SearchIndexable
+public class ChargingControlSettings extends SettingsPreferenceFragment implements
+ Preference.OnPreferenceChangeListener {
+ private static final String TAG = ChargingControlSettings.class.getSimpleName();
+
+ private static final String CHARGING_CONTROL_PREF = "charging_control";
+ private static final String CHARGING_CONTROL_ENABLED_PREF = "charging_control_enabled";
+ private static final String CHARGING_CONTROL_MODE_PREF = "charging_control_mode";
+ private static final String CHARGING_CONTROL_START_TIME_PREF = "charging_control_start_time";
+ private static final String CHARGING_CONTROL_TARGET_TIME_PREF = "charging_control_target_time";
+ private static final String CHARGING_CONTROL_LIMIT_PREF = "charging_control_charging_limit";
+
+ private MainSwitchPreference mChargingControlEnabledPref;
+ private ListPreference mChargingControlModePref;
+ private StartTimePreference mChargingControlStartTimePref;
+ private TargetTimePreference mChargingControlTargetTimePref;
+ private ChargingLimitPreference mChargingControlLimitPref;
+
+ private HealthInterface mHealthInterface;
+
+ private static final int MENU_RESET = Menu.FIRST;
+
+ @Override
+ public void onActivityCreated(final Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ final Resources res = getResources();
+
+ addPreferencesFromResource(R.xml.charging_control_settings);
+ getActivity().getActionBar().setTitle(R.string.charging_control_title);
+
+ mHealthInterface = HealthInterface.getInstance(getActivity());
+
+ final PreferenceScreen prefSet = getPreferenceScreen();
+
+ mChargingControlEnabledPref = prefSet.findPreference(CHARGING_CONTROL_ENABLED_PREF);
+ mChargingControlEnabledPref.setOnPreferenceChangeListener(this);
+ mChargingControlModePref = prefSet.findPreference(CHARGING_CONTROL_MODE_PREF);
+ mChargingControlModePref.setOnPreferenceChangeListener(this);
+ mChargingControlStartTimePref = prefSet.findPreference(CHARGING_CONTROL_START_TIME_PREF);
+ mChargingControlTargetTimePref = prefSet.findPreference(CHARGING_CONTROL_TARGET_TIME_PREF);
+ mChargingControlLimitPref = prefSet.findPreference(CHARGING_CONTROL_LIMIT_PREF);
+
+ if (mChargingControlLimitPref != null) {
+ if (mHealthInterface.allowFineGrainedSettings()) {
+ mChargingControlModePref.setEntries(concatStringArrays(
+ mChargingControlModePref.getEntries(),
+ res.getStringArray(
+ R.array.charging_control_mode_entries_fine_grained_control)));
+ mChargingControlModePref.setEntryValues(concatStringArrays(
+ mChargingControlModePref.getEntryValues(),
+ res.getStringArray(
+ R.array.charging_control_mode_values_fine_grained_control)));
+ }
+ }
+
+ setHasOptionsMenu(true);
+
+ refreshValues();
+
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ refreshUi();
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return -1;
+ }
+
+ @Override
+ public void onDisplayPreferenceDialog(Preference preference) {
+ if (preference.getKey() == null) {
+ // Auto-key preferences that don't have a key, so the dialog can find them.
+ preference.setKey(UUID.randomUUID().toString());
+ }
+ DialogFragment f = null;
+ if (preference instanceof CustomDialogPreference) {
+ f = CustomDialogPreference.CustomPreferenceDialogFragment
+ .newInstance(preference.getKey());
+ } else {
+ super.onDisplayPreferenceDialog(preference);
+ return;
+ }
+ f.setTargetFragment(this, 0);
+ f.show(getFragmentManager(), "dialog_preference");
+ onDialogShowing();
+ }
+
+ private void refreshValues() {
+ if (mChargingControlEnabledPref != null) {
+ mChargingControlEnabledPref.setChecked(mHealthInterface.getEnabled());
+ }
+
+ if (mChargingControlModePref != null) {
+ final int chargingControlMode = mHealthInterface.getMode();
+ mChargingControlModePref.setValue(Integer.toString(chargingControlMode));
+ refreshUi();
+ }
+
+ if (mChargingControlStartTimePref != null) {
+ mChargingControlStartTimePref.setValue(
+ mChargingControlStartTimePref.getTimeSetting());
+ }
+
+ if (mChargingControlTargetTimePref != null) {
+ mChargingControlTargetTimePref.setValue(
+ mChargingControlTargetTimePref.getTimeSetting());
+ }
+
+ if (mChargingControlLimitPref != null) {
+ mChargingControlLimitPref.setValue(
+ mChargingControlLimitPref.getSetting());
+ }
+ }
+
+ private void refreshUi() {
+ final int chargingControlMode = mHealthInterface.getMode();
+
+ refreshUi(chargingControlMode);
+ }
+
+ private void refreshUi(final int chargingControlMode) {
+ String summary = null;
+ boolean isChargingControlStartTimePrefVisible = false;
+ boolean isChargingControlTargetTimePrefVisible = false;
+ boolean isChargingControlLimitPrefVisible = false;
+
+ final Resources res = getResources();
+
+ switch (chargingControlMode) {
+ case MODE_AUTO:
+ summary = res.getString(R.string.charging_control_mode_auto_summary);
+ break;
+ case MODE_MANUAL:
+ summary = res.getString(R.string.charging_control_mode_custom_summary);
+ isChargingControlStartTimePrefVisible = true;
+ isChargingControlTargetTimePrefVisible = true;
+ break;
+ case MODE_LIMIT:
+ summary = res.getString(R.string.charging_control_mode_limit_summary);
+ isChargingControlLimitPrefVisible = true;
+ break;
+ default:
+ return;
+ }
+
+ mChargingControlModePref.setSummary(summary);
+
+ if (mChargingControlStartTimePref != null) {
+ mChargingControlStartTimePref.setVisible(isChargingControlStartTimePrefVisible);
+ }
+
+ if (mChargingControlTargetTimePref != null) {
+ mChargingControlTargetTimePref.setVisible(isChargingControlTargetTimePrefVisible);
+ }
+
+ if (mChargingControlLimitPref != null) {
+ mChargingControlLimitPref.setVisible(isChargingControlLimitPrefVisible);
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
+ menu.add(0, MENU_RESET, 0, R.string.reset)
+ .setIcon(R.drawable.ic_settings_backup_restore)
+ .setAlphabeticShortcut('r')
+ .setShowAsActionFlags(
+ MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (item.getItemId() == MENU_RESET) {
+ resetToDefaults();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onPreferenceChange(final Preference preference, final Object objValue) {
+ if (preference == mChargingControlEnabledPref) {
+ mHealthInterface.setEnabled((Boolean) objValue);
+ } else if (preference == mChargingControlModePref) {
+ final int chargingControlMode = Integer.parseInt((String) objValue);
+ mHealthInterface.setMode(chargingControlMode);
+ refreshUi(chargingControlMode);
+ }
+ return true;
+ }
+
+ private void resetToDefaults() {
+ mHealthInterface.reset();
+
+ refreshValues();
+ }
+
+ private CharSequence[] concatStringArrays(CharSequence[] array1, CharSequence[] array2) {
+ return Stream.concat(Arrays.stream(array1), Arrays.stream(array2)).toArray(size ->
+ (CharSequence[]) Array.newInstance(CharSequence.class, size));
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider() {
+ @Override
+ public List<String> getNonIndexableKeys(Context context) {
+ final List<String> result = new ArrayList<String>();
+ if (!HealthInterface.isChargingControlSupported(context)) {
+ result.add(CHARGING_CONTROL_PREF);
+ result.add(CHARGING_CONTROL_ENABLED_PREF);
+ result.add(CHARGING_CONTROL_MODE_PREF);
+ result.add(CHARGING_CONTROL_START_TIME_PREF);
+ result.add(CHARGING_CONTROL_TARGET_TIME_PREF);
+ result.add(CHARGING_CONTROL_LIMIT_PREF);
+ }
+ return result;
+ }
+ };
+
+}
diff --git a/src/com/android/settings/lineage/health/ChargingLimitPreference.java b/src/com/android/settings/lineage/health/ChargingLimitPreference.java
new file mode 100644
index 0000000..8577fb3
--- /dev/null
+++ b/src/com/android/settings/lineage/health/ChargingLimitPreference.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The LineageOS 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.lineage.health;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.internal.lineage.health.HealthInterface;
+
+import com.android.settings.R;
+
+public class ChargingLimitPreference extends Preference
+ implements SeekBar.OnSeekBarChangeListener {
+ private static final String TAG = ChargingLimitPreference.class.getSimpleName();
+
+ private TextView mChargingLimitValue;
+ private SeekBar mChargingLimitBar;
+
+ private final HealthInterface mHealthInterface;
+
+ public ChargingLimitPreference(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+
+ setLayoutResource(R.layout.preference_charging_limit);
+
+ mHealthInterface = HealthInterface.getInstance(context);
+ }
+
+ @Override
+ public void onBindViewHolder(final PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ mChargingLimitValue = (TextView) holder.findViewById(R.id.value);
+
+ mChargingLimitBar = (SeekBar) holder.findViewById(R.id.seekbar_widget);
+ mChargingLimitBar.setOnSeekBarChangeListener(this);
+
+ int currLimit = getSetting();
+ mChargingLimitBar.setProgress(currLimit);
+ updateValue(currLimit);
+ }
+
+ @Override
+ public void onStartTrackingTouch(final SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(final SeekBar seekBar) {
+ setSetting(seekBar.getProgress());
+ }
+
+ @Override
+ public void onProgressChanged(final SeekBar seekBar, final int progress,
+ final boolean fromUser) {
+ updateValue(progress);
+ }
+
+ public void setValue(final int value) {
+ if (mChargingLimitBar != null) {
+ mChargingLimitBar.setProgress(value);
+ }
+ updateValue(value);
+ }
+
+ protected int getSetting() {
+ return mHealthInterface.getLimit();
+ }
+
+ protected void setSetting(final int chargingLimit) {
+ mHealthInterface.setLimit(chargingLimit);
+ }
+
+ private void updateValue(final int value) {
+ if (mChargingLimitValue != null) {
+ mChargingLimitValue.setText(String.format("%d%%", value));
+ }
+ }
+}
diff --git a/src/com/android/settings/lineage/health/StartTimePreference.java b/src/com/android/settings/lineage/health/StartTimePreference.java
new file mode 100644
index 0000000..6d12c665
--- /dev/null
+++ b/src/com/android/settings/lineage/health/StartTimePreference.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The LineageOS 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.lineage.health;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.settings.R;
+
+public class StartTimePreference extends TimePreference {
+ private static final String TAG = StartTimePreference.class.getSimpleName();
+
+ public StartTimePreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected int getSummaryResourceId() {
+ return R.string.charging_control_start_time_summary;
+ }
+
+ @Override
+ protected int getTimeSetting() {
+ return mHealthInterface.getStartTime();
+ }
+
+ @Override
+ protected void setTimeSetting(int secondOfDay) {
+ mHealthInterface.setStartTime(secondOfDay);
+ }
+}
diff --git a/src/com/android/settings/lineage/health/TargetTimePreference.java b/src/com/android/settings/lineage/health/TargetTimePreference.java
new file mode 100644
index 0000000..87351e4
--- /dev/null
+++ b/src/com/android/settings/lineage/health/TargetTimePreference.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The LineageOS 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.lineage.health;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.settings.R;
+
+public class TargetTimePreference extends TimePreference {
+ private static final String TAG = TargetTimePreference.class.getSimpleName();
+
+ public TargetTimePreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected int getSummaryResourceId() {
+ return R.string.charging_control_target_time_summary;
+ }
+
+ @Override
+ protected int getTimeSetting() {
+ return mHealthInterface.getTargetTime();
+ }
+
+ @Override
+ protected void setTimeSetting(int secondOfDay) {
+ mHealthInterface.setTargetTime(secondOfDay);
+ }
+}
diff --git a/src/com/android/settings/lineage/health/TimePreference.java b/src/com/android/settings/lineage/health/TimePreference.java
new file mode 100644
index 0000000..aae2d7c
--- /dev/null
+++ b/src/com/android/settings/lineage/health/TimePreference.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 The LineageOS 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.lineage.health;
+
+import static java.time.format.FormatStyle.SHORT;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TimePicker;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.lineage.CustomDialogPreference;
+import com.android.settings.R;
+
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+
+import com.android.internal.lineage.health.HealthInterface;
+
+public abstract class TimePreference extends CustomDialogPreference<AlertDialog> {
+ private static final String TAG = TimePreference.class.getSimpleName();
+ private static final DateTimeFormatter mFormatter = DateTimeFormatter.ofLocalizedTime(SHORT);
+
+ private TimePicker mTimePicker;
+ private LocalTime mLocalTime;
+
+ protected HealthInterface mHealthInterface;
+
+ public TimePreference(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+
+ setDialogLayoutResource(R.layout.dialog_time);
+ mHealthInterface = HealthInterface.getInstance(context);
+ }
+
+ @Override
+ public void onBindViewHolder(final PreferenceViewHolder holder) {
+ mLocalTime = LocalTime.ofSecondOfDay(getTimeSetting());
+ super.onBindViewHolder(holder);
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(final AlertDialog.Builder builder,
+ final DialogInterface.OnClickListener listener) {
+ super.onPrepareDialogBuilder(builder, listener);
+
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.dlg_ok, null);
+ }
+
+ @Override
+ protected void onDialogClosed(final boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+
+ if (positiveResult) {
+ mLocalTime = LocalTime.of(mTimePicker.getHour(),
+ mTimePicker.getMinute());
+ setTimeSetting(mLocalTime.toSecondOfDay());
+ setSummary(getSummary());
+ }
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+
+ mTimePicker = view.findViewById(R.id.time_picker);
+ mTimePicker.setHour(mLocalTime.getHour());
+ mTimePicker.setMinute(mLocalTime.getMinute());
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return String.format(getContext().getString(getSummaryResourceId()),
+ mLocalTime.format(mFormatter));
+ }
+
+ public void setValue(final int value) {
+ mLocalTime = LocalTime.ofSecondOfDay(value);
+ setSummary(getSummary());
+ }
+
+ protected abstract int getSummaryResourceId();
+
+ protected abstract int getTimeSetting();
+
+ protected abstract void setTimeSetting(int secondOfDay);
+}