stone: parts: introduce haptic feedback level adjustement

and adjust path to /sys/class/leds/vibrator/brightness
since its the path for prebuilt stone kernel

Signed-off-by: Arijit78 <sahaarijit2007@gmail.com>
This commit is contained in:
Nauval Rizky
2024-06-15 20:01:41 +02:00
committed by Arijit78
parent 375c4e312d
commit e97bc751bf
11 changed files with 636 additions and 0 deletions

View File

@@ -25,6 +25,7 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-sdk
android:minSdkVersion="24"
@@ -49,6 +50,20 @@
android:authorities="${applicationId}.androidx-startup"
tools:replace="android:authorities"/>
<activity
android:name=".haptic.HapticLevelActivity"
android:label="@string/haptic_title"
android:exported="true"
android:theme="@style/Theme.SubSettingsBase">
<intent-filter>
<action android:name="com.android.settings.action.IA_SETTINGS" />
</intent-filter>
<meta-data android:name="com.android.settings.category"
android:value="com.android.settings.category.ia.sound" />
<meta-data android:name="com.android.settings.summary"
android:resource="@string/haptic_summary" />
</activity>
<activity
android:name=".dirac.DiracActivity"
android:label="@string/dirac_title"

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2014, 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.
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="match_parent"
android:layout_width="match_parent"/>

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
(C) 2018-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.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<FrameLayout
android:id="@android:id/icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/summary">
<ImageView
android:id="@android:id/icon"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginEnd="12dp"
android:layout_gravity="start|center_vertical" />
</FrameLayout>
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@android:id/icon_frame"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorPrimary" />
<TextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_toEndOf="@android:id/icon_frame"
android:maxLines="4"
android:paddingTop="8dp"
android:paddingBottom="5dp"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary" />
<SeekBar
android:id="@+id/seekbar"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_below="@android:id/summary"
android:layout_gravity="center_vertical"
android:layout_toEndOf="@android:id/icon_frame"
android:tickMark="@null"
android:paddingStart="0dp"
android:paddingEnd="12dp"
style="@android:style/Widget.Material.SeekBar.Discrete" />
</RelativeLayout>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2022 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.
-->
<resources>
<!-- Haptic Feedback Levels -->
<integer name="haptic_level_default">25</integer>
<integer name="haptic_level_max">100</integer>
</resources>

View File

@@ -126,4 +126,12 @@
<string name="hbm_disable_time_title"></string>
<string name="hbm_disable_time_summary">disable delay (seconds)</string>
<string name="hbm_mode_warning">Long time usage of High brightness mode may damage your display</string>
<!-- Haptic Level Settings -->
<string name="haptic_title">Haptic feedback</string>
<string name="haptic_warning">Setting-up haptic level too high may cause permanent hardware damage, use at your own risk</string>
<string name="haptic_summary">Customize vibration intensity</string>
<string name="haptic_level_title">Level</string>
<string name="haptic_level_summary">Amount of vibration intensity</string>
<string name="haptic_level_summary_incompatible">Feature not supported</string>
</resources>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 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:title="@string/haptic_title">
<org.lineageos.settings.widget.SeekBarPreference
android:key="haptic_level_pref"
android:title="@string/haptic_level_title"
android:summary="@string/haptic_level_summary"
android:layout="@layout/preference_slider"
android:defaultValue="@integer/haptic_level_default"
android:max="@integer/haptic_level_max" />
<com.android.settingslib.widget.FooterPreference
android:key="haptic_footer_preference"
android:title="@string/haptic_warning"
android:selectable="false"
android:layout="@layout/preference_footer" />
</PreferenceScreen>

View File

@@ -29,6 +29,7 @@ import org.lineageos.settings.thermal.ThermalUtils;
import org.lineageos.settings.refreshrate.RefreshUtils;
import org.lineageos.settings.dirac.DiracUtils;
import org.lineageos.settings.utils.FileUtils;
import org.lineageos.settings.haptic.HapticUtils;
public class BootCompletedReceiver extends BroadcastReceiver {
private static final boolean DEBUG = false;
@@ -39,6 +40,7 @@ public class BootCompletedReceiver extends BroadcastReceiver {
if (DEBUG)
Log.d(TAG, "Received boot completed intent");
HapticUtils.restoreLevel(context);
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
// Dirac

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2022 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.haptic;
import android.os.Bundle;
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
public class HapticLevelActivity extends CollapsingToolbarBaseActivity {
private final String TAG_HAPTIC = "haptic_level";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(
com.android.settingslib.collapsingtoolbar.R.id.content_frame,
new HapticLevelFragment(), TAG_HAPTIC)
.commit();
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2022 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.haptic;
import android.content.Context;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceFragment;
import org.lineageos.settings.R;
import org.lineageos.settings.utils.FileUtils;
import org.lineageos.settings.widget.SeekBarPreference;
public class HapticLevelFragment extends PreferenceFragment implements OnPreferenceChangeListener {
private Vibrator mVibrator;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.haptic_settings);
final SeekBarPreference mHapticLevel = (SeekBarPreference) findPreference(HapticUtils.PREF_LEVEL);
if (FileUtils.fileExists(HapticUtils.PATH_LEVEL)) {
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (mVibrator == null || !mVibrator.hasVibrator()) {
mVibrator = null;
}
mHapticLevel.setEnabled(true);
mHapticLevel.setOnPreferenceChangeListener(this);
} else {
mHapticLevel.setSummary(R.string.haptic_level_summary_incompatible);
mHapticLevel.setEnabled(false);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View view = LayoutInflater.from(getContext()).inflate(R.layout.haptic, container, false);
((ViewGroup) view).addView(super.onCreateView(inflater, container, savedInstanceState));
return view;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (HapticUtils.PREF_LEVEL.equals(preference.getKey())) {
HapticUtils.applyLevel(getContext(), (int) newValue, true);
doHapticFeedback();
}
return true;
}
private void doHapticFeedback() {
if (mVibrator == null) {
return;
}
mVibrator.vibrate(VibrationEffect.createOneShot(500,
VibrationEffect.DEFAULT_AMPLITUDE));
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2022 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.haptic;
import android.content.Context;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings;
import java.lang.Math;
import org.lineageos.settings.utils.FileUtils;
public final class HapticUtils {
final static String PREF_LEVEL = "haptic_level_pref";
final static String PATH_LEVEL = "/sys/class/leds/vibrator/brightness";
final static int MIN_LEVEL = 1;
final static int MAX_LEVEL = 100;
public static void applyLevel(Context context, int value, boolean test) {
if (FileUtils.fileExists(PATH_LEVEL)) {
double level = value / 100.0 * (MAX_LEVEL - MIN_LEVEL) + MIN_LEVEL;
int newValue = (int) Math.round(level);
FileUtils.writeLine(PATH_LEVEL, String.valueOf(newValue));
if (test) {
Vibrator dev = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
if (dev.hasVibrator()) {
dev.vibrate(VibrationEffect.createOneShot(15, VibrationEffect.DEFAULT_AMPLITUDE));
}
}
}
}
public static void restoreLevel(Context context) {
applyLevel(context, Settings.Secure.getInt(
context.getContentResolver(), PREF_LEVEL, 25), false);
}
}

View File

@@ -0,0 +1,280 @@
/*
* Copyright (C) 2011 The Android Open Source Project
* 2017-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.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.RestrictedPreference;
import org.lineageos.settings.R;
/**
* Based on android.preference.SeekBarPreference, but uses support preference as base.
*/
public class SeekBarPreference extends RestrictedPreference
implements OnSeekBarChangeListener, View.OnKeyListener {
private int mProgress;
private int mMax;
private boolean mTrackingTouch;
private ImageView mIconView;
private Drawable mIcon;
public SeekBarPreference(
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.ProgressBar, defStyleAttr, defStyleRes);
setMax(a.getInt(com.android.internal.R.styleable.ProgressBar_max, mMax));
a.recycle();
a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.SeekBarPreference, defStyleAttr, defStyleRes);
final int layoutResId = a.getResourceId(
com.android.internal.R.styleable.SeekBarPreference_layout,
com.android.internal.R.layout.preference_widget_seekbar);
a.recycle();
setLayoutResource(layoutResId);
}
public SeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public SeekBarPreference(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.seekBarPreferenceStyle);
}
public SeekBarPreference(Context context) {
this(context, null);
}
@Override
public void onBindViewHolder(PreferenceViewHolder view) {
super.onBindViewHolder(view);
mIconView = (ImageView) view.findViewById(R.id.icon);
if (mIcon != null) {
mIconView.setImageDrawable(mIcon);
}
view.itemView.setOnKeyListener(this);
SeekBar seekBar = (SeekBar) view.findViewById(R.id.seekbar);
seekBar.setOnSeekBarChangeListener(this);
seekBar.setMax(mMax);
seekBar.setProgress(mProgress);
seekBar.setEnabled(isEnabled());
}
public ImageView getIconView() {
return mIconView;
}
public void setIconDrawable(Drawable drawable) {
if (mIconView != null) {
mIconView.setImageDrawable(drawable);
}
mIcon = drawable;
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setProgress(restoreValue ? getPersistedInt(mProgress)
: (Integer) defaultValue);
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getInt(index, 0);
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() != KeyEvent.ACTION_DOWN) {
return false;
}
SeekBar seekBar = (SeekBar) v.findViewById(R.id.seekbar);
if (seekBar == null) {
return false;
}
return seekBar.onKeyDown(keyCode, event);
}
public void setMax(int max) {
if (max != mMax) {
mMax = max;
notifyChanged();
}
}
public void setProgress(int progress) {
setProgress(progress, true);
}
private void setProgress(int progress, boolean notifyChanged) {
if (progress > mMax) {
progress = mMax;
}
if (progress < 0) {
progress = 0;
}
if (progress != mProgress) {
mProgress = progress;
persistInt(progress);
if (notifyChanged) {
notifyChanged();
}
}
}
public int getProgress() {
return mProgress;
}
/**
* Persist the seekBar's progress value if callChangeListener
* returns true, otherwise set the seekBar's progress to the stored value
*/
void syncProgress(SeekBar seekBar) {
int progress = seekBar.getProgress();
if (progress != mProgress) {
if (callChangeListener(progress)) {
setProgress(progress, false);
} else {
seekBar.setProgress(mProgress);
}
}
}
@Override
public void onProgressChanged(
SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser && !mTrackingTouch) {
syncProgress(seekBar);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
mTrackingTouch = true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mTrackingTouch = false;
if (seekBar.getProgress() != mProgress) {
syncProgress(seekBar);
}
}
@Override
protected Parcelable onSaveInstanceState() {
/*
* Suppose a client uses this preference type without persisting. We
* must save the instance state so it is able to, for example, survive
* orientation changes.
*/
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) {
// No need to save instance state since it's persistent
return superState;
}
// Save the instance state
final SavedState myState = new SavedState(superState);
myState.progress = mProgress;
myState.max = mMax;
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (!state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
// Restore the instance state
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
mProgress = myState.progress;
mMax = myState.max;
notifyChanged();
}
/**
* SavedState, a subclass of {@link BaseSavedState}, will store the state
* of MyPreference, a subclass of Preference.
* <p>
* It is important to always call through to super methods.
*/
private static class SavedState extends BaseSavedState {
int progress;
int max;
public SavedState(Parcel source) {
super(source);
// Restore the click counter
progress = source.readInt();
max = source.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
// Save the click counter
dest.writeInt(progress);
dest.writeInt(max);
}
public SavedState(Parcelable superState) {
super(superState);
}
@SuppressWarnings("unused")
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}