diff --git a/parts/AndroidManifest.xml b/parts/AndroidManifest.xml
index adb5814..4e77b67 100644
--- a/parts/AndroidManifest.xml
+++ b/parts/AndroidManifest.xml
@@ -1,7 +1,7 @@
+ Black Shark settings
+ Additional device specific tweaks
+
+
+ Rear LED
+ Mode
+ Enabled
+ Change the behaviour of rear LED logo
+ Breathing RGB
+ Manual
+ Red
+ Green
+ Blue
+
+
+ Speaker tweaks
+ Enable built-in EQ or game mode
+ Enabled
+ Equalizer
+ 256 Hz
+ 512 Hz
+ 1024 Hz
+ 2048 Hz
+ 4096 Hz
+ Game mode
+ Disabled
+ Default
+ Shooter
+ If game mode is enabled, EQ will not be working. To apply changes immediately, you can restart playback, if something was playing.
+
Thermal profiles
Adjust per-app thermal profiles for optimum performance
diff --git a/parts/res/xml/logo.xml b/parts/res/xml/logo.xml
new file mode 100644
index 0000000..196df33
--- /dev/null
+++ b/parts/res/xml/logo.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/parts/res/xml/sharkparts.xml b/parts/res/xml/sharkparts.xml
new file mode 100644
index 0000000..5f582f7
--- /dev/null
+++ b/parts/res/xml/sharkparts.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
diff --git a/parts/res/xml/shortcuts.xml b/parts/res/xml/shortcuts.xml
new file mode 100644
index 0000000..0042b55
--- /dev/null
+++ b/parts/res/xml/shortcuts.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
diff --git a/parts/res/xml/speaker.xml b/parts/res/xml/speaker.xml
new file mode 100644
index 0000000..f204f73
--- /dev/null
+++ b/parts/res/xml/speaker.xml
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/parts/src/org/lineageos/settings/BootCompletedReceiver.java b/parts/src/org/lineageos/settings/BootCompletedReceiver.java
index 0728d09..f350cf7 100644
--- a/parts/src/org/lineageos/settings/BootCompletedReceiver.java
+++ b/parts/src/org/lineageos/settings/BootCompletedReceiver.java
@@ -20,12 +20,20 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
import android.util.Log;
import org.lineageos.settings.dirac.DiracUtils;
import org.lineageos.settings.doze.DozeUtils;
import org.lineageos.settings.thermal.ThermalUtils;
+import org.lineageos.settings.logo.LogoFragment;
+import org.lineageos.settings.utils.SettingsUtils;
+
+import org.lineageos.settings.logo.LogoUtil;
+import org.lineageos.settings.speaker.SpeakerUtil;
+
public class BootCompletedReceiver extends BroadcastReceiver {
private static final boolean DEBUG = false;
@@ -37,5 +45,23 @@ public void onReceive(final Context context, Intent intent) {
DiracUtils.initialize();
DozeUtils.checkDozeService(context);
ThermalUtils.startService(context);
+
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+ int intValue = Integer.parseInt(sharedPreferences.getString(LogoFragment.KEY_LOGO_MODE, String.valueOf(LogoFragment.LOGO_MODE_BREATH)));
+
+ if (SettingsUtils.getEnabled(context, LogoFragment.KEY_LOGO_ENABLE)) {
+ if (intValue == LogoFragment.LOGO_MODE_BREATH) {
+ LogoUtil.enableBreathingEffect();
+ } else if (intValue == LogoFragment.LOGO_MODE_MANUAL) {
+ final int r = SettingsUtils.getInt(context, LogoFragment.KEY_LOGO_MODE_MANUAL_RED, 1);
+ final int g = SettingsUtils.getInt(context, LogoFragment.KEY_LOGO_MODE_MANUAL_GREEN, 1);
+ final int b = SettingsUtils.getInt(context, LogoFragment.KEY_LOGO_MODE_MANUAL_BLUE, 1);
+ LogoUtil.setRGBStill(r, g, b);
+ }
+ } else {
+ LogoUtil.turnOff();
+ }
+
+ SpeakerUtil.initInjector(context);
}
}
diff --git a/parts/src/org/lineageos/settings/KeyHandler.java b/parts/src/org/lineageos/settings/KeyHandler.java
new file mode 100644
index 0000000..ebd6300
--- /dev/null
+++ b/parts/src/org/lineageos/settings/KeyHandler.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 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 org.lineageos.settings;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.view.KeyEvent;
+import android.util.Log;
+
+import com.android.internal.os.DeviceKeyHandler;
+
+public class KeyHandler implements DeviceKeyHandler {
+ private static final String TAG = KeyHandler.class.getSimpleName();
+
+ private static final int KEY_SLIDER_OFF = 250;
+ private static final int KEY_SLIDER_ON = 251;
+
+ private final Context mContext;
+ private final NotificationManager mNotificationManager;
+ private final Vibrator mVibrator;
+
+ public KeyHandler(Context context) {
+ mContext = context;
+
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
+ mVibrator = mContext.getSystemService(Vibrator.class);
+ }
+
+ public KeyEvent handleKeyEvent(KeyEvent event) {
+ int scanCode = event.getScanCode();
+
+ switch(scanCode) {
+ case KEY_SLIDER_OFF:
+ mNotificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+ mNotificationManager.setNotificationPolicy(
+ new NotificationManager.Policy(
+ NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA
+ | NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS, 0, 0
+ )
+ );
+ break;
+ case KEY_SLIDER_ON:
+ mNotificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+ break;
+ default:
+ return event;
+ }
+
+ doHapticFeedback();
+
+ return null;
+ }
+
+ private void doHapticFeedback() {
+ if (mVibrator != null && mVibrator.hasVibrator()) {
+ mVibrator.vibrate(VibrationEffect.createOneShot(50,
+ VibrationEffect.DEFAULT_AMPLITUDE));
+ }
+ }
+}
diff --git a/parts/src/org/lineageos/settings/SharkParts.java b/parts/src/org/lineageos/settings/SharkParts.java
new file mode 100644
index 0000000..c8ec6d2
--- /dev/null
+++ b/parts/src/org/lineageos/settings/SharkParts.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 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 org.lineageos.settings;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.content.Intent;
+import android.os.Bundle;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.Preference;
+import androidx.preference.ListPreference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+import androidx.preference.TwoStatePreference;
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+
+import org.lineageos.settings.logo.LogoActivity;
+import org.lineageos.settings.speaker.SpeakerActivity;
+
+public class SharkParts extends CollapsingToolbarBaseActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Fragment fragment = getFragmentManager().findFragmentById(com.android.settingslib.collapsingtoolbar.R.id.content_frame);
+ SharkPartsFragment mSharkPartsFragment;
+ if (fragment == null) {
+ mSharkPartsFragment = new SharkPartsFragment();
+ getFragmentManager().beginTransaction()
+ .add(com.android.settingslib.collapsingtoolbar.R.id.content_frame, mSharkPartsFragment)
+ .commit();
+ }
+ }
+
+ public static class SharkPartsFragment extends PreferenceFragment implements
+ Preference.OnPreferenceChangeListener {
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ setPreferencesFromResource(R.xml.sharkparts, rootKey);
+ Preference mLogoPref = findPreference("logo");
+ mLogoPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ Intent intent = new Intent(getContext(), LogoActivity.class);
+ startActivity(intent);
+ return true;
+ }
+ });
+
+ Preference mSpeakerPref = findPreference("speaker");
+ mSpeakerPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ Intent intent = new Intent(getContext(), SpeakerActivity.class);
+ startActivity(intent);
+ return true;
+ }
+ });
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final String key = preference.getKey();
+ return true;
+ }
+ }
+}
diff --git a/parts/src/org/lineageos/settings/logo/LogoActivity.java b/parts/src/org/lineageos/settings/logo/LogoActivity.java
new file mode 100644
index 0000000..0d5bb28
--- /dev/null
+++ b/parts/src/org/lineageos/settings/logo/LogoActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020-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 org.lineageos.settings.logo;
+
+import android.os.Bundle;
+
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+
+public class LogoActivity extends CollapsingToolbarBaseActivity {
+
+ private static final String TAG_LOGO = "logo";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getFragmentManager().beginTransaction()
+ .replace(com.android.settingslib.collapsingtoolbar.R.id.content_frame, new LogoFragment(), TAG_LOGO)
+ .commit();
+ }
+}
diff --git a/parts/src/org/lineageos/settings/logo/LogoFragment.java b/parts/src/org/lineageos/settings/logo/LogoFragment.java
new file mode 100644
index 0000000..fdae127
--- /dev/null
+++ b/parts/src/org/lineageos/settings/logo/LogoFragment.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2020 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 org.lineageos.settings.logo;
+
+import android.content.res.Resources;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.widget.CompoundButton;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.SwitchPreference;
+import androidx.preference.SeekBarPreference;
+import androidx.preference.ListPreference;
+
+import com.android.settingslib.widget.MainSwitchPreference;
+
+import org.lineageos.settings.R;
+
+import org.lineageos.settings.utils.SettingsUtils;
+
+import org.lineageos.settings.logo.LogoUtil;
+
+public class LogoFragment extends PreferenceFragment implements
+ Preference.OnPreferenceChangeListener, CompoundButton.OnCheckedChangeListener,
+ SharedPreferences.OnSharedPreferenceChangeListener {
+ public static final String KEY_LOGO_ENABLE = "logo_control_enable";
+ public static final String KEY_LOGO_MODE = "logo_control_mode";
+ public static final String KEY_LOGO_MODE_MANUAL_RED = "logo_control_manual_red";
+ public static final String KEY_LOGO_MODE_MANUAL_GREEN = "logo_control_manual_green";
+ public static final String KEY_LOGO_MODE_MANUAL_BLUE = "logo_control_manual_blue";
+ public static final String KEY_LOGO_MODE_BREATH = "logo_control_breath";
+
+ public static final int LOGO_MODE_BREATH = 1;
+ public static final int LOGO_MODE_MANUAL = 2;
+ private static final int LOGO_DISABLED_VALUE = 0;
+ private static final int LOGO_MIN_VALUE = 1;
+ private static final int LOGO_MAX_VALUE = 255;
+
+ private MainSwitchPreference mSwitchBar;
+ private ListPreference mLogoControlMode;
+ private SeekBarPreference mLogoManualBarRed;
+ private SeekBarPreference mLogoManualBarGreen;
+ private SeekBarPreference mLogoManualBarBlue;
+
+ private void setSlidersVisibility(boolean state) {
+ mLogoManualBarRed.setVisible(state);
+ mLogoManualBarGreen.setVisible(state);
+ mLogoManualBarBlue.setVisible(state);
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.logo);
+
+ mSwitchBar = (MainSwitchPreference) findPreference(KEY_LOGO_ENABLE);
+ mSwitchBar.setChecked(SettingsUtils.getEnabled(getActivity(), KEY_LOGO_ENABLE));
+ mSwitchBar.addOnSwitchChangeListener(this);
+
+ mLogoControlMode = (ListPreference) findPreference(KEY_LOGO_MODE);
+
+ mLogoManualBarRed = (SeekBarPreference) findPreference(KEY_LOGO_MODE_MANUAL_RED);
+ mLogoManualBarRed.setValue(SettingsUtils.getInt(getActivity(), KEY_LOGO_MODE_MANUAL_RED, 1));
+ mLogoManualBarGreen = (SeekBarPreference) findPreference(KEY_LOGO_MODE_MANUAL_GREEN);
+ mLogoManualBarGreen.setValue(SettingsUtils.getInt(getActivity(), KEY_LOGO_MODE_MANUAL_GREEN, 1));
+ mLogoManualBarBlue = (SeekBarPreference) findPreference(KEY_LOGO_MODE_MANUAL_BLUE);
+ mLogoManualBarBlue.setValue(SettingsUtils.getInt(getActivity(), KEY_LOGO_MODE_MANUAL_BLUE, 1));
+
+ mLogoManualBarRed.setOnPreferenceChangeListener(this);
+ mLogoManualBarGreen.setOnPreferenceChangeListener(this);
+ mLogoManualBarBlue.setOnPreferenceChangeListener(this);
+
+ if (mLogoControlMode.getValue() == null) {
+ mLogoControlMode.setValue("1");
+ }
+
+ final int mode = Integer.parseInt((String) mLogoControlMode.getValue());
+ if (mode == LOGO_MODE_BREATH) {
+ mLogoControlMode.setSummary(getResources().getString(R.string.logo_control_breath_title));
+ setSlidersVisibility(false);
+ } else if (mode == LOGO_MODE_MANUAL) {
+ mLogoControlMode.setSummary(getResources().getString(R.string.logo_control_manual_title));
+ setSlidersVisibility(true);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
+ sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
+ sharedPreferences.registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean enabled) {
+ SettingsUtils.setEnabled(getActivity(), KEY_LOGO_ENABLE, enabled);
+
+ LogoUtil.turnOff();
+ if (!enabled) return;
+
+ int mode = Integer.parseInt((String) mLogoControlMode.getValue());
+ if (mode == LOGO_MODE_BREATH) {
+ enableBreathingMode();
+ } else if (mode == LOGO_MODE_MANUAL) {
+ enableManualMode();
+ }
+ }
+
+ private void enableManualMode() {
+ mLogoControlMode.setSummary(getResources().getString(R.string.logo_control_manual_title));
+ setSlidersVisibility(true);
+
+ final int r = SettingsUtils.getInt(getActivity(), KEY_LOGO_MODE_MANUAL_RED, 1);
+ final int g = SettingsUtils.getInt(getActivity(), KEY_LOGO_MODE_MANUAL_GREEN, 1);
+ final int b = SettingsUtils.getInt(getActivity(), KEY_LOGO_MODE_MANUAL_BLUE, 1);
+ LogoUtil.setRGBStill(r, g, b);
+ }
+
+ private void enableBreathingMode() {
+ mLogoControlMode.setSummary(getResources().getString(R.string.logo_control_breath_title));
+ setSlidersVisibility(false);
+ LogoUtil.enableBreathingEffect();
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object value) {
+ final String key = preference.getKey();
+ if (KEY_LOGO_MODE_MANUAL_RED.equals(key)) {
+ final int r = (Integer)value;
+ SettingsUtils.putInt(getActivity(), KEY_LOGO_MODE_MANUAL_RED, r);
+ LogoUtil.setStillRed(r);
+ } else if (KEY_LOGO_MODE_MANUAL_GREEN.equals(key)) {
+ final int g = (Integer)value;
+ SettingsUtils.putInt(getActivity(), KEY_LOGO_MODE_MANUAL_GREEN, g);
+ LogoUtil.setStillGreen(g);
+ } else if (KEY_LOGO_MODE_MANUAL_BLUE.equals(key)) {
+ final int b = (Integer)value;
+ SettingsUtils.putInt(getActivity(), KEY_LOGO_MODE_MANUAL_BLUE, b);
+ LogoUtil.setStillBlue(b);
+ }
+ return true;
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if (!KEY_LOGO_MODE.equals(key)) return;
+ int mode = Integer.parseInt(sharedPreferences.getString(key, String.valueOf(LOGO_MODE_BREATH)));
+ mLogoControlMode.setValue(String.valueOf(mode));
+
+ // Reset everything before switching mode
+ LogoUtil.turnOff();
+
+ if (mode == LOGO_MODE_BREATH) {
+ enableBreathingMode();
+ } else if (mode == LOGO_MODE_MANUAL) {
+ enableManualMode();
+ }
+ }
+}
diff --git a/parts/src/org/lineageos/settings/logo/LogoUtil.java b/parts/src/org/lineageos/settings/logo/LogoUtil.java
new file mode 100644
index 0000000..9a4d3e2
--- /dev/null
+++ b/parts/src/org/lineageos/settings/logo/LogoUtil.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2020 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 org.lineageos.settings.logo;
+
+import org.lineageos.settings.utils.FileUtils;
+import android.os.Handler;
+
+public class LogoUtil {
+ public static final String RED_LED = "/sys/rgb/leds/green_1/brightness";
+ public static final String GREEN_LED = "/sys/rgb/leds/red_1/brightness";
+ public static final String BLUE_LED = "/sys/rgb/leds/blue_1/brightness";
+
+ public static final String RED_LED_BLINK = "/sys/rgb/leds/green_1/blink";
+ public static final String GREEN_LED_BLINK = "/sys/rgb/leds/red_1/blink";
+ public static final String BLUE_LED_BLINK = "/sys/rgb/leds/blue_1/blink";
+
+ public static void turnOff() {
+ FileUtils.writeLine(RED_LED_BLINK, "0");
+ FileUtils.writeLine(GREEN_LED_BLINK, "0");
+ FileUtils.writeLine(BLUE_LED_BLINK, "0");
+ FileUtils.writeLine(RED_LED, "0");
+ FileUtils.writeLine(GREEN_LED, "0");
+ FileUtils.writeLine(BLUE_LED, "0");
+ }
+
+ public static void setRGBStill(int r, int g, int b) {
+ FileUtils.writeLine(RED_LED, String.valueOf(r));
+ FileUtils.writeLine(GREEN_LED, String.valueOf(g));
+ FileUtils.writeLine(BLUE_LED, String.valueOf(b));
+ }
+
+ public static void setStillRed(int value) {
+ FileUtils.writeLine(RED_LED, String.valueOf(value));
+ }
+
+ public static void setStillGreen(int value) {
+ FileUtils.writeLine(GREEN_LED, String.valueOf(value));
+ }
+
+ public static void setStillBlue(int value) {
+ FileUtils.writeLine(BLUE_LED, String.valueOf(value));
+ }
+
+ public static void setBlinkRed(boolean state) {
+ FileUtils.writeLine(RED_LED_BLINK, state ? "1" : "0");
+ }
+
+ public static void setBlinkBlue(boolean state) {
+ FileUtils.writeLine(BLUE_LED_BLINK, state ? "1" : "0");
+ }
+
+ public static void setBlinkGreen(boolean state) {
+ FileUtils.writeLine(GREEN_LED_BLINK, state ? "1" : "0");
+ }
+
+ public static void enableBreathingEffect() {
+ final Handler handler = new Handler();
+ setBlinkRed(true);
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ setBlinkGreen(true);
+ }
+ }, 1000);
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ setBlinkBlue(true);
+ }
+ }, 2000);
+ }
+}
diff --git a/parts/src/org/lineageos/settings/logo/QsService.java b/parts/src/org/lineageos/settings/logo/QsService.java
new file mode 100644
index 0000000..237ec4c
--- /dev/null
+++ b/parts/src/org/lineageos/settings/logo/QsService.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 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 org.lineageos.settings.logo;
+
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.os.Bundle;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Icon;
+import android.os.Build;
+import android.service.quicksettings.Tile;
+import android.service.quicksettings.TileService;
+
+import org.lineageos.settings.logo.LogoFragment;
+import org.lineageos.settings.logo.LogoUtil;
+import org.lineageos.settings.R;
+import org.lineageos.settings.utils.SettingsUtils;
+
+public class QsService extends TileService {
+ private void updateState() {
+ final boolean enabled = SettingsUtils.getEnabled(getApplicationContext(), LogoFragment.KEY_LOGO_ENABLE);
+ getQsTile().setState(enabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
+ getQsTile().setIcon(Icon.createWithResource(getApplicationContext(), R.drawable.logo));
+ getQsTile().setLabel(getString(R.string.logo_control_title));
+ getQsTile().updateTile();
+ }
+
+ @Override
+ public void onStartListening() {
+ super.onStartListening();
+ updateState();
+ }
+
+ @Override
+ public void onClick() {
+ super.onClick();
+ final Context context = getApplicationContext();
+
+ boolean enabled = !SettingsUtils.getEnabled(context, LogoFragment.KEY_LOGO_ENABLE);
+ SettingsUtils.setEnabled(context, LogoFragment.KEY_LOGO_ENABLE, enabled);
+
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+ int intValue = Integer.parseInt(sharedPreferences.getString(LogoFragment.KEY_LOGO_MODE, String.valueOf(LogoFragment.LOGO_MODE_BREATH)));
+ if (enabled) {
+ if (intValue == LogoFragment.LOGO_MODE_BREATH) {
+ LogoUtil.enableBreathingEffect();
+ } else if (intValue == LogoFragment.LOGO_MODE_MANUAL) {
+ final int r = SettingsUtils.getInt(context, LogoFragment.KEY_LOGO_MODE_MANUAL_RED, 1);
+ final int g = SettingsUtils.getInt(context, LogoFragment.KEY_LOGO_MODE_MANUAL_GREEN, 1);
+ final int b = SettingsUtils.getInt(context, LogoFragment.KEY_LOGO_MODE_MANUAL_BLUE, 1);
+ LogoUtil.setRGBStill(r, g, b);
+ }
+ } else {
+ LogoUtil.turnOff();
+ }
+
+ updateState();
+ }
+}
diff --git a/parts/src/org/lineageos/settings/speaker/SpeakerActivity.java b/parts/src/org/lineageos/settings/speaker/SpeakerActivity.java
new file mode 100644
index 0000000..dd76fd7
--- /dev/null
+++ b/parts/src/org/lineageos/settings/speaker/SpeakerActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020-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 org.lineageos.settings.speaker;
+
+import android.os.Bundle;
+
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+
+public class SpeakerActivity extends CollapsingToolbarBaseActivity {
+
+ private static final String TAG_SPEAKER = "speaker";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getFragmentManager().beginTransaction()
+ .replace(com.android.settingslib.collapsingtoolbar.R.id.content_frame, new SpeakerFragment(), TAG_SPEAKER)
+ .commit();
+ }
+}
diff --git a/parts/src/org/lineageos/settings/speaker/SpeakerFragment.java b/parts/src/org/lineageos/settings/speaker/SpeakerFragment.java
new file mode 100644
index 0000000..2beb937
--- /dev/null
+++ b/parts/src/org/lineageos/settings/speaker/SpeakerFragment.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2020 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 org.lineageos.settings.speaker;
+
+import android.content.res.Resources;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.widget.CompoundButton;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.SwitchPreference;
+import androidx.preference.SeekBarPreference;
+import androidx.preference.ListPreference;
+
+import com.android.settingslib.widget.MainSwitchPreference;
+
+import org.lineageos.settings.R;
+
+import org.lineageos.settings.utils.SettingsUtils;
+
+import org.lineageos.settings.speaker.SpeakerUtil;
+
+public class SpeakerFragment extends PreferenceFragment implements
+ Preference.OnPreferenceChangeListener, CompoundButton.OnCheckedChangeListener,
+ SharedPreferences.OnSharedPreferenceChangeListener {
+ public static final String KEY_SPEAKER_TWEAKS_ENABLE = "speaker_tweaks_enable";
+ public static final String KEY_SPEAKER_EQ_BASE = "speaker_control_eq_band_";
+ public static final String KEY_SPEAKER_GAMEMODE = "speaker_gamemode_val";
+
+ public static final String EQ_DEFAULT = "3";
+ public static final String GAMEMODE_DISABLED = "0";
+ public static final String GAMEMODE_DEFAULT = "1";
+ public static final String GAMEMODE_EATENCHICKEN = "2";
+
+ private MainSwitchPreference mSwitchBar;
+ private ListPreference mGameMode;
+ private SeekBarPreference mEqBand0;
+ private SeekBarPreference mEqBand1;
+ private SeekBarPreference mEqBand2;
+ private SeekBarPreference mEqBand3;
+ private SeekBarPreference mEqBand4;
+
+ private void updateGameModeSummary() {
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
+ final String val = sharedPreferences.getString(KEY_SPEAKER_GAMEMODE, GAMEMODE_DISABLED);
+ if (val.equals("0")) {
+ mGameMode.setSummary(R.string.speaker_control_gamemode_0);
+ } else if (val.equals("1")) {
+ mGameMode.setSummary(R.string.speaker_control_gamemode_1);
+ } else if (val.equals("2")) {
+ mGameMode.setSummary(R.string.speaker_control_gamemode_2);
+ } else {
+ mGameMode.setSummary(null);
+ }
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.speaker);
+
+ mSwitchBar = (MainSwitchPreference) findPreference(KEY_SPEAKER_TWEAKS_ENABLE);
+ mSwitchBar.setChecked(SettingsUtils.getEnabled(getActivity(), KEY_SPEAKER_TWEAKS_ENABLE));
+ mSwitchBar.addOnSwitchChangeListener(this);
+
+ mGameMode = (ListPreference) findPreference(KEY_SPEAKER_GAMEMODE);
+
+ mEqBand0 = (SeekBarPreference) findPreference(KEY_SPEAKER_EQ_BASE + "0");
+ mEqBand1 = (SeekBarPreference) findPreference(KEY_SPEAKER_EQ_BASE + "1");
+ mEqBand2 = (SeekBarPreference) findPreference(KEY_SPEAKER_EQ_BASE + "2");
+ mEqBand3 = (SeekBarPreference) findPreference(KEY_SPEAKER_EQ_BASE + "3");
+ mEqBand4 = (SeekBarPreference) findPreference(KEY_SPEAKER_EQ_BASE + "4");
+
+ mEqBand0.setValue(SettingsUtils.getInt(getActivity(), KEY_SPEAKER_EQ_BASE + "0", 3));
+ mEqBand1.setValue(SettingsUtils.getInt(getActivity(), KEY_SPEAKER_EQ_BASE + "1", 3));
+ mEqBand2.setValue(SettingsUtils.getInt(getActivity(), KEY_SPEAKER_EQ_BASE + "2", 3));
+ mEqBand3.setValue(SettingsUtils.getInt(getActivity(), KEY_SPEAKER_EQ_BASE + "3", 3));
+ mEqBand4.setValue(SettingsUtils.getInt(getActivity(), KEY_SPEAKER_EQ_BASE + "4", 3));
+
+ mEqBand0.setOnPreferenceChangeListener(this);
+ mEqBand1.setOnPreferenceChangeListener(this);
+ mEqBand2.setOnPreferenceChangeListener(this);
+ mEqBand3.setOnPreferenceChangeListener(this);
+ mEqBand4.setOnPreferenceChangeListener(this);
+
+ if (mGameMode.getValue() == null) {
+ mGameMode.setValue(GAMEMODE_DISABLED);
+ }
+
+ updateGameModeSummary();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
+ sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
+ sharedPreferences.registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean enabled) {
+ SettingsUtils.setEnabled(getActivity(), KEY_SPEAKER_TWEAKS_ENABLE, enabled);
+
+ SpeakerUtil.enabled = enabled;
+ SpeakerUtil.updateParameters(getActivity());
+ updateGameModeSummary();
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object value) {
+ final String key = preference.getKey();
+ SettingsUtils.putInt(getActivity(), key, (Integer)value);
+ SpeakerUtil.updateParameters(getActivity());
+ return true;
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if (!KEY_SPEAKER_GAMEMODE.equals(key)) return;
+ String mode = sharedPreferences.getString(key, GAMEMODE_DISABLED);
+ mGameMode.setValue(mode);
+ updateGameModeSummary();
+ SpeakerUtil.updateParameters(getActivity());
+ }
+}
diff --git a/parts/src/org/lineageos/settings/speaker/SpeakerUtil.java b/parts/src/org/lineageos/settings/speaker/SpeakerUtil.java
new file mode 100644
index 0000000..b8db1fd
--- /dev/null
+++ b/parts/src/org/lineageos/settings/speaker/SpeakerUtil.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020-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 org.lineageos.settings.speaker;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.media.AudioManager;
+import android.media.AudioPlaybackConfiguration;
+import android.media.AudioDeviceInfo;
+import android.os.Handler;
+import android.preference.PreferenceManager;
+import android.util.Log;
+
+import java.lang.String;
+import java.lang.StringBuilder;
+import java.util.List;
+
+import org.lineageos.settings.speaker.SpeakerFragment;
+import org.lineageos.settings.utils.SettingsUtils;
+
+public class SpeakerUtil {
+ private static final String TAG = "SpeakerUtil";
+ private static final boolean DEBUG = false;
+
+ private static AudioManager audioManager = null;
+ private static SharedPreferences sharedPreferences = null;
+ private static Handler handler = new Handler();
+
+ public static boolean enabled = false;
+
+ public static void initInjector(Context context) {
+ enabled = SettingsUtils.getEnabled(context, SpeakerFragment.KEY_SPEAKER_TWEAKS_ENABLE);
+ audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+
+ audioManager.registerAudioPlaybackCallback(new AudioManager.AudioPlaybackCallback() {
+ @Override
+ public void onPlaybackConfigChanged(List configs) {
+ super.onPlaybackConfigChanged(configs);
+
+ for (AudioPlaybackConfiguration config : configs) {
+ if (config == null) continue;
+ if (config.getAudioDeviceInfo() == null) continue;
+ if (config.getAudioDeviceInfo().getType() != AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
+ continue;
+ } else {
+ updateParameters(context);
+ break;
+ }
+ }
+ }
+ }, handler);
+ }
+
+ public static void updateParameters(Context context) {
+ if (!enabled) return;
+
+ StringBuilder builder = new StringBuilder("eq=");
+ builder.append(String.valueOf(SettingsUtils.getInt(context, SpeakerFragment.KEY_SPEAKER_EQ_BASE + "0", 3)));
+ builder.append(String.valueOf(SettingsUtils.getInt(context, SpeakerFragment.KEY_SPEAKER_EQ_BASE + "1", 3)));
+ builder.append(String.valueOf(SettingsUtils.getInt(context, SpeakerFragment.KEY_SPEAKER_EQ_BASE + "2", 3)));
+ builder.append(String.valueOf(SettingsUtils.getInt(context, SpeakerFragment.KEY_SPEAKER_EQ_BASE + "3", 3)));
+ builder.append(String.valueOf(SettingsUtils.getInt(context, SpeakerFragment.KEY_SPEAKER_EQ_BASE + "4", 3)));
+ builder.append(";gamemode=");
+ builder.append(sharedPreferences.getString(SpeakerFragment.KEY_SPEAKER_GAMEMODE, SpeakerFragment.GAMEMODE_DISABLED));
+ String parameters = builder.toString();
+
+ if (DEBUG) Log.d(TAG, "Setting speaker parameters to " + parameters);
+ audioManager.setParameters(parameters);
+ }
+}
diff --git a/parts/src/org/lineageos/settings/utils/FileUtils.java b/parts/src/org/lineageos/settings/utils/FileUtils.java
new file mode 100644
index 0000000..ffaf591
--- /dev/null
+++ b/parts/src/org/lineageos/settings/utils/FileUtils.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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 org.lineageos.settings.utils;
+
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.NullPointerException;
+import java.lang.SecurityException;
+
+public final class FileUtils {
+ private static final String TAG = "FileUtils";
+
+ private FileUtils() {
+ // This class is not supposed to be instantiated
+ }
+
+ /**
+ * Reads the first line of text from the given file.
+ * Reference {@link BufferedReader#readLine()} for clarification on what a line is
+ *
+ * @return the read line contents, or null on failure
+ */
+ public static String readOneLine(String fileName) {
+ String line = null;
+ BufferedReader reader = null;
+
+ try {
+ reader = new BufferedReader(new FileReader(fileName), 512);
+ line = reader.readLine();
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "No such file " + fileName + " for reading", e);
+ } catch (IOException e) {
+ Log.e(TAG, "Could not read from file " + fileName, e);
+ } finally {
+ try {
+ if (reader != null) {
+ reader.close();
+ }
+ } catch (IOException e) {
+ // Ignored, not much we can do anyway
+ }
+ }
+
+ return line;
+ }
+
+ /**
+ * Writes the given value into the given file
+ *
+ * @return true on success, false on failure
+ */
+ public static boolean writeLine(String fileName, String value) {
+ BufferedWriter writer = null;
+
+ try {
+ writer = new BufferedWriter(new FileWriter(fileName));
+ writer.write(value);
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "No such file " + fileName + " for writing", e);
+ return false;
+ } catch (IOException e) {
+ Log.e(TAG, "Could not write to file " + fileName, e);
+ return false;
+ } finally {
+ try {
+ if (writer != null) {
+ writer.close();
+ }
+ } catch (IOException e) {
+ // Ignored, not much we can do anyway
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks whether the given file exists
+ *
+ * @return true if exists, false if not
+ */
+ public static boolean fileExists(String fileName) {
+ final File file = new File(fileName);
+ return file.exists();
+ }
+
+ /**
+ * Checks whether the given file is readable
+ *
+ * @return true if readable, false if not
+ */
+ public static boolean isFileReadable(String fileName) {
+ final File file = new File(fileName);
+ return file.exists() && file.canRead();
+ }
+
+ /**
+ * Checks whether the given file is writable
+ *
+ * @return true if writable, false if not
+ */
+ public static boolean isFileWritable(String fileName) {
+ final File file = new File(fileName);
+ return file.exists() && file.canWrite();
+ }
+
+ /**
+ * Deletes an existing file
+ *
+ * @return true if the delete was successful, false if not
+ */
+ public static boolean delete(String fileName) {
+ final File file = new File(fileName);
+ boolean ok = false;
+ try {
+ ok = file.delete();
+ } catch (SecurityException e) {
+ Log.w(TAG, "SecurityException trying to delete " + fileName, e);
+ }
+ return ok;
+ }
+
+ /**
+ * Renames an existing file
+ *
+ * @return true if the rename was successful, false if not
+ */
+ public static boolean rename(String srcPath, String dstPath) {
+ final File srcFile = new File(srcPath);
+ final File dstFile = new File(dstPath);
+ boolean ok = false;
+ try {
+ ok = srcFile.renameTo(dstFile);
+ } catch (SecurityException e) {
+ Log.w(TAG, "SecurityException trying to rename " + srcPath + " to " + dstPath, e);
+ } catch (NullPointerException e) {
+ Log.e(TAG, "NullPointerException trying to rename " + srcPath + " to " + dstPath, e);
+ }
+ return ok;
+ }
+}
diff --git a/parts/src/org/lineageos/settings/utils/SettingsUtils.java b/parts/src/org/lineageos/settings/utils/SettingsUtils.java
new file mode 100644
index 0000000..89bbc2d
--- /dev/null
+++ b/parts/src/org/lineageos/settings/utils/SettingsUtils.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 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 org.lineageos.settings.utils;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+
+public class SettingsUtils {
+ public static final String PREFERENCES = "SettingsUtilsPreferences";
+
+ /**
+ * Saves given value into given preference
+ * @return puts the value in place
+ */
+ public static boolean setEnabled(Context context, String string, boolean enabled) {
+ return putInt(context, string, enabled ? 1 : 0);
+ }
+
+ /**
+ * Retrieves a value from a desired string
+ * @return the value
+ */
+ public static boolean getEnabled(Context context, String string) {
+ return getInt(context, string, 0) == 1;
+ }
+
+ public static boolean putInt(Context context, String name, int value) {
+ SharedPreferences settings = context.getSharedPreferences(PREFERENCES, 0);
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putInt(name, value);
+ return editor.commit();
+ }
+
+ public static int getInt(Context context, String name, int def) {
+ SharedPreferences settings = context.getSharedPreferences(PREFERENCES, 0);
+ return settings.getInt(name, def);
+ }
+};