Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ replay_pid*
*.DS_Store
*app/release/

Build/
Build/

local.properties
16 changes: 15 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,20 @@
</intent-filter>
</service>

<service
android:name=".services.tiles.RefreshRateTileService"
android:exported="true"
android:label="@string/tile_refresh_rate"
android:icon="@drawable/rounded_shutter_speed_24"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
<meta-data
android:name="android.service.quicksettings.TILE_CATEGORY"
android:value="android.service.quicksettings.CATEGORY_DISPLAY" />
</service>

</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import com.sameerasw.essentials.ui.composables.configs.MapsPowerSavingSettingsUI
import com.sameerasw.essentials.ui.composables.configs.NotificationLightingSettingsUI
import com.sameerasw.essentials.ui.composables.configs.OtherCustomizationsSettingsUI
import com.sameerasw.essentials.ui.composables.configs.QuickSettingsTilesSettingsUI
import com.sameerasw.essentials.ui.composables.configs.RefreshRateSettingsUI
import com.sameerasw.essentials.ui.composables.configs.ScreenLockedSecuritySettingsUI
import com.sameerasw.essentials.ui.composables.configs.ScreenOffWidgetSettingsUI
import com.sameerasw.essentials.ui.composables.configs.SnoozeNotificationsSettingsUI
Expand Down Expand Up @@ -207,6 +208,7 @@ class FeatureSettingsActivity : AppCompatActivity() {
val isNotificationLightingAccessibilityEnabled by viewModel.isNotificationLightingAccessibilityEnabled
val isNotificationListenerEnabled by viewModel.isNotificationListenerEnabled
val isReadPhoneStateEnabled by viewModel.isReadPhoneStateEnabled
val isShizukuPermissionGranted by viewModel.isShizukuPermissionGranted

// FAB State for Notification Lighting
var fabExpanded by remember { mutableStateOf(true) }
Expand Down Expand Up @@ -236,7 +238,8 @@ class FeatureSettingsActivity : AppCompatActivity() {
isOverlayPermissionGranted,
isNotificationLightingAccessibilityEnabled,
isNotificationListenerEnabled,
isReadPhoneStateEnabled
isReadPhoneStateEnabled,
isShizukuPermissionGranted
) {
val hasMissingPermissions = when (featureId) {
"Screen off widget" -> !isAccessibilityEnabled
Expand All @@ -253,6 +256,7 @@ class FeatureSettingsActivity : AppCompatActivity() {

"Location reached" -> !viewModel.isLocationPermissionGranted.value || !viewModel.isBackgroundLocationPermissionGranted.value
"Quick settings tiles" -> !viewModel.isWriteSettingsEnabled.value
"Screen refresh rate" -> !viewModel.isShizukuPermissionGranted.value
// Top level checks for other features (rarely hit if they are children, but safe to add)
"Ambient music glance" -> !isAccessibilityEnabled || !isNotificationListenerEnabled
"Call vibrations" -> !isReadPhoneStateEnabled || !isNotificationListenerEnabled
Expand Down Expand Up @@ -402,6 +406,7 @@ class FeatureSettingsActivity : AppCompatActivity() {
"Caffeinate" -> !viewModel.isPostNotificationsEnabled.value
"Battery notification" -> !viewModel.isPostNotificationsEnabled.value || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !viewModel.isBluetoothPermissionGranted.value)
"Text and animations" -> !viewModel.isWriteSettingsEnabled.value || !isWriteSecureSettingsEnabled
"Screen refresh rate" -> !viewModel.isShizukuPermissionGranted.value
else -> false
}

Expand Down Expand Up @@ -628,6 +633,14 @@ class FeatureSettingsActivity : AppCompatActivity() {
)
}

"Screen refresh rate" -> {
RefreshRateSettingsUI(
viewModel = viewModel,
modifier = Modifier.padding(top = 16.dp),
highlightSetting = highlightSetting
)
}

"Always on Display" -> {
AlwaysOnDisplaySettingsUI(
viewModel = viewModel,
Expand Down Expand Up @@ -672,4 +685,4 @@ class FeatureSettingsActivity : AppCompatActivity() {
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ class SettingsRepository(private val context: Context) {
const val KEY_SCALE_ANIMATIONS_MODE = "scale_animations_mode"
const val KEY_SCALE_ANIMATIONS_DEFAULT_PROFILE = "scale_animations_default_profile"
const val KEY_SCALE_ANIMATIONS_GLOVE_PROFILE = "scale_animations_glove_profile"
const val KEY_REFRESH_RATE_MODE = "refresh_rate_mode"
const val KEY_REFRESH_RATE_FIXED = "refresh_rate_fixed"
const val KEY_REFRESH_RATE_MIN = "refresh_rate_min"
const val KEY_REFRESH_RATE_PEAK = "refresh_rate_peak"
const val KEY_REFRESH_RATE_DEFAULT_PEAK_INFINITY = "refresh_rate_default_peak_infinity"
}

// Observe changes
Expand Down Expand Up @@ -908,6 +913,24 @@ class SettingsRepository(private val context: Context) {
if (contains(KEY_WINDOW_ANIMATION_SCALE)) {
setAnimationScale(android.provider.Settings.Global.WINDOW_ANIMATION_SCALE, getFloat(KEY_WINDOW_ANIMATION_SCALE, 1.0f))
}
if (contains(KEY_REFRESH_RATE_FIXED) || contains(KEY_REFRESH_RATE_MIN) || contains(KEY_REFRESH_RATE_PEAK)) {
val mode = getRefreshRateMode()
val fixed = getFloat(KEY_REFRESH_RATE_FIXED, 0f)
val min = getFloat(KEY_REFRESH_RATE_MIN, 0f)
val peak = getFloat(KEY_REFRESH_RATE_PEAK, 0f)

if (fixed <= 0f && min <= 0f && peak <= 0f) {
com.sameerasw.essentials.utils.RefreshRateUtils.resetRefreshRate(
shouldRestoreInfinityPeakOnRefreshRateReset()
)
} else if (mode == com.sameerasw.essentials.utils.RefreshRateUtils.MODE_RANGE && min > 0f && peak > 0f) {
com.sameerasw.essentials.utils.RefreshRateUtils.applyRangeRefreshRate(min, peak)
} else if (fixed > 0f || peak > 0f) {
com.sameerasw.essentials.utils.RefreshRateUtils.applyFixedRefreshRate(
if (fixed > 0f) fixed else peak
)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
Expand Down Expand Up @@ -968,6 +991,25 @@ class SettingsRepository(private val context: Context) {
fun getScaleAnimationsMode(): String = getString(KEY_SCALE_ANIMATIONS_MODE, "default") ?: "default"
fun setScaleAnimationsMode(mode: String) = putString(KEY_SCALE_ANIMATIONS_MODE, mode)

fun getRefreshRateMode(): String =
getString(KEY_REFRESH_RATE_MODE, com.sameerasw.essentials.utils.RefreshRateUtils.MODE_FIXED)
?: com.sameerasw.essentials.utils.RefreshRateUtils.MODE_FIXED

fun setRefreshRateMode(mode: String) = putString(KEY_REFRESH_RATE_MODE, mode)

fun saveRefreshRateState(mode: String, fixed: Float, min: Float, peak: Float) {
putString(KEY_REFRESH_RATE_MODE, mode)
putFloat(KEY_REFRESH_RATE_FIXED, fixed)
putFloat(KEY_REFRESH_RATE_MIN, min)
putFloat(KEY_REFRESH_RATE_PEAK, peak)
}

fun shouldRestoreInfinityPeakOnRefreshRateReset(): Boolean =
getBoolean(KEY_REFRESH_RATE_DEFAULT_PEAK_INFINITY, false)

fun setRestoreInfinityPeakOnRefreshRateReset(enabled: Boolean) =
putBoolean(KEY_REFRESH_RATE_DEFAULT_PEAK_INFINITY, enabled)

fun getScaleAnimationsProfile(mode: String): ScaleAnimationsProfile {
val key = if (mode == "glove") KEY_SCALE_ANIMATIONS_GLOVE_PROFILE else KEY_SCALE_ANIMATIONS_DEFAULT_PROFILE
val json = prefs.getString(key, null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,43 @@ object FeatureRegistry {
override fun isEnabled(viewModel: MainViewModel) = true
override fun onToggle(viewModel: MainViewModel, context: Context, enabled: Boolean) {}
},
object : Feature(
id = "Screen refresh rate",
title = R.string.feat_screen_refresh_rate_title,
iconRes = R.drawable.rounded_shutter_speed_24,
category = R.string.cat_interface,
description = R.string.feat_screen_refresh_rate_desc,
aboutDescription = R.string.about_desc_screen_refresh_rate,
permissionKeys = listOf("SHIZUKU"),
searchableSettings = listOf(
SearchSetting(
R.string.search_refresh_rate_mode_title,
R.string.search_refresh_rate_mode_desc,
"refresh_rate_mode"
),
SearchSetting(
R.string.search_refresh_rate_fixed_title,
R.string.search_refresh_rate_fixed_desc,
"refresh_rate_fixed"
),
SearchSetting(
R.string.search_refresh_rate_range_title,
R.string.search_refresh_rate_range_desc,
"refresh_rate_range"
),
SearchSetting(
R.string.search_refresh_rate_reset_title,
R.string.search_refresh_rate_reset_desc,
"refresh_rate_reset",
R.array.keywords_restore_default
)
),
showToggle = false,
parentFeatureId = "Display"
) {
override fun isEnabled(viewModel: MainViewModel) = true
override fun onToggle(viewModel: MainViewModel, context: Context, enabled: Boolean) {}
},
object : Feature(
id = "Watch",
title = R.string.feat_watch_title,
Expand Down Expand Up @@ -601,6 +638,13 @@ object FeatureRegistry {
"USB Debugging",
R.array.keywords_adb_debug,
R.string.feat_qs_tiles_title
),
SearchSetting(
R.string.search_qs_refresh_rate_title,
R.string.search_qs_refresh_rate_desc,
"Refresh Rate",
R.array.keywords_visual_style,
R.string.feat_qs_tiles_title
)
)
) {
Expand Down Expand Up @@ -1065,4 +1109,4 @@ object FeatureRegistry {
override fun onToggle(viewModel: MainViewModel, context: Context, enabled: Boolean) {}
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ fun initPermissionRegistry() {
PermissionRegistry.register("SHIZUKU", R.string.feat_freeze_title)
PermissionRegistry.register("SHIZUKU", R.string.feat_maps_power_saving_title)
PermissionRegistry.register("SHIZUKU", R.string.feat_screen_locked_security_title)
PermissionRegistry.register("SHIZUKU", R.string.feat_screen_refresh_rate_title)
PermissionRegistry.register("SHIZUKU", R.string.tile_refresh_rate)
PermissionRegistry.register("USAGE_STATS", R.string.feat_freeze_title)
PermissionRegistry.register("USAGE_STATS", R.string.feat_app_lock_title)
PermissionRegistry.register("USAGE_STATS", R.string.feat_dynamic_night_light_title)
Expand Down Expand Up @@ -89,4 +91,4 @@ fun initPermissionRegistry() {

// Default browser permission
PermissionRegistry.register("DEFAULT_BROWSER", R.string.feat_link_actions_title)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,9 @@ class InputEventListenerService : Service() {
android.media.AudioManager.ADJUST_RAISE
else
android.media.AudioManager.ADJUST_LOWER
am.adjustStreamVolume(
android.media.AudioManager.STREAM_MUSIC,
am.adjustSuggestedStreamVolume(
direction,
android.media.AudioManager.USE_DEFAULT_STREAM_TYPE,
android.media.AudioManager.FLAG_SHOW_UI
)
}
Expand All @@ -196,9 +196,9 @@ class InputEventListenerService : Service() {
android.media.AudioManager.ADJUST_RAISE
else
android.media.AudioManager.ADJUST_LOWER
am.adjustStreamVolume(
android.media.AudioManager.STREAM_MUSIC,
am.adjustSuggestedStreamVolume(
dirKey,
android.media.AudioManager.USE_DEFAULT_STREAM_TYPE,
android.media.AudioManager.FLAG_SHOW_UI
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@ class ButtonRemapHandler(
val am = service.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val direction =
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) AudioManager.ADJUST_RAISE else AudioManager.ADJUST_LOWER
am.adjustStreamVolume(
AudioManager.STREAM_MUSIC,
am.adjustSuggestedStreamVolume(
direction,
AudioManager.USE_DEFAULT_STREAM_TYPE,
AudioManager.FLAG_SHOW_UI
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.sameerasw.essentials.services.tiles

import android.content.Intent
import android.graphics.drawable.Icon
import android.os.Build
import android.service.quicksettings.Tile
import androidx.annotation.RequiresApi
import com.sameerasw.essentials.FeatureSettingsActivity
import com.sameerasw.essentials.R
import com.sameerasw.essentials.data.repository.SettingsRepository
import com.sameerasw.essentials.utils.RefreshRateUtils
import com.sameerasw.essentials.utils.ShizukuUtils

@RequiresApi(Build.VERSION_CODES.N)
class RefreshRateTileService : BaseTileService() {

override fun onClick() {
if (!hasFeaturePermission()) {
val intent = Intent(this, FeatureSettingsActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
putExtra("feature", "Quick settings tiles")
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
val pendingIntent = android.app.PendingIntent.getActivity(
this,
0,
intent,
android.app.PendingIntent.FLAG_UPDATE_CURRENT or android.app.PendingIntent.FLAG_IMMUTABLE
)
startActivityAndCollapse(pendingIntent)
} else {
@Suppress("DEPRECATION")
startActivityAndCollapse(intent)
}
return
}
super.onClick()
}

override fun onTileClick() {
val nextPreset = RefreshRateUtils.getNextPreset(this)
if (nextPreset <= 0) {
val settingsRepository = SettingsRepository(this)
RefreshRateUtils.resetRefreshRate(
settingsRepository.shouldRestoreInfinityPeakOnRefreshRateReset()
)
} else {
RefreshRateUtils.applyFixedRefreshRate(nextPreset.toFloat())
}
}

override fun getTileLabel(): String = getString(R.string.tile_refresh_rate)

override fun getTileSubtitle(): String = RefreshRateUtils.getDisplaySubtitle(this)

override fun hasFeaturePermission(): Boolean = ShizukuUtils.hasPermission()

override fun getTileIcon(): Icon {
return Icon.createWithResource(this, R.drawable.rounded_shutter_speed_24)
}

override fun getTileState(): Int {
return if (RefreshRateUtils.hasCustomRefreshRate(this)) {
Tile.STATE_ACTIVE
} else {
Tile.STATE_INACTIVE
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class QSPreferencesActivity : ComponentActivity() {
"com.sameerasw.essentials.services.tiles.StayAwakeTileService" -> "Quick settings tiles"
"com.sameerasw.essentials.services.tiles.NfcTileService" -> "NFC"
"com.sameerasw.essentials.services.tiles.AdaptiveBrightnessTileService" -> "Quick settings tiles"
"com.sameerasw.essentials.services.tiles.RefreshRateTileService" -> "Screen refresh rate"
"com.sameerasw.essentials.services.tiles.MapsPowerSavingTileService" -> "Maps power saving mode"
"com.sameerasw.essentials.services.tiles.UsbDebuggingTileService" -> "Quick settings tiles"
"com.sameerasw.essentials.services.tiles.BatteryNotificationTileService" -> "Battery notification"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.sameerasw.essentials.ui.components.pickers

import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
Expand All @@ -12,11 +13,13 @@ import androidx.compose.material3.Text
import androidx.compose.material3.ToggleButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.sameerasw.essentials.R
import com.sameerasw.essentials.domain.HapticFeedbackType

Expand Down Expand Up @@ -62,7 +65,7 @@ fun HapticFeedbackPicker(
else -> ButtonGroupDefaults.connectedMiddleButtonShapes()
},
) {
Text(stringResource(label))
Text(stringResource(label), fontSize = dimensionResource(R.dimen.font_small).value.sp, modifier= Modifier.basicMarquee(), maxLines = 1)
}
}
}
Expand Down
Loading