mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-05-15 13:09:10 +00:00
Compare commits
6 Commits
nightly-d1
...
nightly-04
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
044aff731f | ||
|
|
216c97f9ca | ||
|
|
fd3774b513 | ||
|
|
b7336940e6 | ||
|
|
b2ba830a80 | ||
|
|
f08769e62f |
@@ -1,6 +1,6 @@
|
|||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
|
||||||
val appVersionName = "0.2.8"
|
val appVersionName = "0.2.9"
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
@@ -28,9 +28,8 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "me.kavishdevar.librepods"
|
applicationId = "me.kavishdevar.librepods"
|
||||||
minSdk = 33
|
|
||||||
targetSdk = 37
|
targetSdk = 37
|
||||||
versionCode = 49
|
versionCode = 52
|
||||||
versionName = appVersionName
|
versionName = appVersionName
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -47,21 +46,29 @@ android {
|
|||||||
}
|
}
|
||||||
buildConfigField("Boolean", "PLAY_BUILD", "false")
|
buildConfigField("Boolean", "PLAY_BUILD", "false")
|
||||||
signingConfig = signingConfigs.getByName("release")
|
signingConfig = signingConfigs.getByName("release")
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = 33
|
||||||
|
}
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
buildConfigField("Boolean", "PLAY_BUILD", "false")
|
buildConfigField("Boolean", "PLAY_BUILD", "false")
|
||||||
signingConfig = signingConfigs.getByName("release")
|
signingConfig = signingConfigs.getByName("release")
|
||||||
versionNameSuffix = "-debug"
|
versionNameSuffix = "-debug"
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = 33
|
||||||
|
}
|
||||||
}
|
}
|
||||||
create("playRelease") {
|
}
|
||||||
initWith(getByName("release"))
|
productFlavors {
|
||||||
|
create("foss") {
|
||||||
|
dimension = "env"
|
||||||
|
buildConfigField("Boolean", "PLAY_BUILD", "false")
|
||||||
|
}
|
||||||
|
create("play") {
|
||||||
|
dimension = "env"
|
||||||
buildConfigField("Boolean", "PLAY_BUILD", "true")
|
buildConfigField("Boolean", "PLAY_BUILD", "true")
|
||||||
versionNameSuffix = "-play"
|
versionNameSuffix = "-play"
|
||||||
}
|
minSdk = 36
|
||||||
create("playDebug") {
|
|
||||||
initWith(getByName("debug"))
|
|
||||||
buildConfigField("Boolean", "PLAY_BUILD", "true")
|
|
||||||
versionNameSuffix = "-youshouldnothavethis"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
@@ -91,25 +98,6 @@ android {
|
|||||||
ndkVersion = "30.0.14904198"
|
ndkVersion = "30.0.14904198"
|
||||||
|
|
||||||
flavorDimensions += "env"
|
flavorDimensions += "env"
|
||||||
|
|
||||||
productFlavors {
|
|
||||||
create("normal") {
|
|
||||||
dimension = "env"
|
|
||||||
externalNativeBuild {
|
|
||||||
cmake {
|
|
||||||
arguments += "-DIS_XPOSED=OFF"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
create("xposed") {
|
|
||||||
dimension = "env"
|
|
||||||
externalNativeBuild {
|
|
||||||
cmake {
|
|
||||||
arguments += "-DIS_XPOSED=ON"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -139,9 +127,10 @@ dependencies {
|
|||||||
implementation(libs.backdrop)
|
implementation(libs.backdrop)
|
||||||
// implementation(libs.hilt)
|
// implementation(libs.hilt)
|
||||||
// implementation(libs.hilt.compiler)
|
// implementation(libs.hilt.compiler)
|
||||||
add("xposedCompileOnly", libs.libxposed.api)
|
compileOnly(libs.libxposed.api)
|
||||||
add("xposedImplementation", libs.libxposed.service)
|
implementation(libs.libxposed.service)
|
||||||
add("playReleaseImplementation", libs.billing)
|
implementation(libs.play.review)
|
||||||
|
implementation(libs.play.review.ktx)
|
||||||
}
|
}
|
||||||
|
|
||||||
aboutLibraries {
|
aboutLibraries {
|
||||||
@@ -184,14 +173,14 @@ fun registerRootModuleZipTask(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val zipRelease = registerRootModuleZipTask(
|
val zipRelease = registerRootModuleZipTask(
|
||||||
"zipXposedReleaseModule",
|
"zipReleaseModule",
|
||||||
"xposed",
|
"foss",
|
||||||
"release"
|
"release"
|
||||||
)
|
)
|
||||||
|
|
||||||
val zipDebug = registerRootModuleZipTask(
|
val zipDebug = registerRootModuleZipTask(
|
||||||
"zipXposedDebugModule",
|
"zipDebugModule",
|
||||||
"xposed",
|
"foss",
|
||||||
"debug"
|
"debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -200,22 +189,22 @@ val collect = tasks.register<Copy>("collectReleaseArtifacts") {
|
|||||||
dependsOn(
|
dependsOn(
|
||||||
zipRelease,
|
zipRelease,
|
||||||
zipDebug,
|
zipDebug,
|
||||||
"bundleXposedPlayRelease"
|
"bundlePlayRelease"
|
||||||
)
|
)
|
||||||
|
|
||||||
into(releaseDir)
|
into(releaseDir)
|
||||||
|
|
||||||
from(layout.buildDirectory.dir("outputs/apk/xposed/release")) {
|
from(layout.buildDirectory.dir("outputs/apk/foss/release")) {
|
||||||
include("*.apk")
|
include("*.apk")
|
||||||
rename(".*", "LibrePods-FOSS-v$appVersionName-release.apk")
|
rename(".*", "LibrePods-FOSS-v$appVersionName-release.apk")
|
||||||
}
|
}
|
||||||
|
|
||||||
from(layout.buildDirectory.dir("outputs/apk/xposed/debug")) {
|
from(layout.buildDirectory.dir("outputs/apk/foss/debug")) {
|
||||||
include("*.apk")
|
include("*.apk")
|
||||||
rename(".*", "LibrePods-FOSS-v$appVersionName-debug.apk")
|
rename(".*", "LibrePods-FOSS-v$appVersionName-debug.apk")
|
||||||
}
|
}
|
||||||
|
|
||||||
from(layout.buildDirectory.dir("outputs/bundle/xposedPlayRelease")) {
|
from(layout.buildDirectory.dir("outputs/bundle/playRelease")) {
|
||||||
include("*.aab")
|
include("*.aab")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ cmake_minimum_required(VERSION 3.22.1)
|
|||||||
project("l2c_fcr_hook")
|
project("l2c_fcr_hook")
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
|
||||||
option(IS_XPOSED "Build Xposed components" OFF)
|
|
||||||
|
|
||||||
add_library(bluetooth_socket SHARED
|
add_library(bluetooth_socket SHARED
|
||||||
bluetooth_socket.cpp
|
bluetooth_socket.cpp
|
||||||
)
|
)
|
||||||
@@ -24,40 +22,36 @@ target_link_libraries(bluetooth_socket
|
|||||||
log
|
log
|
||||||
)
|
)
|
||||||
|
|
||||||
if(IS_XPOSED)
|
|
||||||
|
|
||||||
set(XPOSED_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../xposed/cpp)
|
set(XPOSED_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../xposed/cpp)
|
||||||
|
|
||||||
add_library(l2c_fcr_hook SHARED
|
add_library(l2c_fcr_hook SHARED
|
||||||
${XPOSED_SRC_DIR}/l2c_fcr_hook.cpp
|
l2c_fcr_hook.cpp
|
||||||
|
|
||||||
${XPOSED_SRC_DIR}/xz/xz_crc32.c
|
xz/xz_crc32.c
|
||||||
${XPOSED_SRC_DIR}/xz/xz_crc64.c
|
xz/xz_crc64.c
|
||||||
${XPOSED_SRC_DIR}/xz/xz_sha256.c
|
xz/xz_sha256.c
|
||||||
${XPOSED_SRC_DIR}/xz/xz_dec_stream.c
|
xz/xz_dec_stream.c
|
||||||
${XPOSED_SRC_DIR}/xz/xz_dec_lzma2.c
|
xz/xz_dec_lzma2.c
|
||||||
${XPOSED_SRC_DIR}/xz/xz_dec_bcj.c
|
xz/xz_dec_bcj.c
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(l2c_fcr_hook PRIVATE
|
target_include_directories(l2c_fcr_hook PRIVATE
|
||||||
${XPOSED_SRC_DIR}
|
xz
|
||||||
${XPOSED_SRC_DIR}/xz
|
)
|
||||||
)
|
|
||||||
|
|
||||||
target_compile_definitions(l2c_fcr_hook PRIVATE
|
target_compile_definitions(l2c_fcr_hook PRIVATE
|
||||||
XZ_DEC_X86
|
XZ_DEC_X86
|
||||||
XZ_DEC_ARM
|
XZ_DEC_ARM
|
||||||
XZ_DEC_ARMTHUMB
|
XZ_DEC_ARMTHUMB
|
||||||
XZ_DEC_ARM64
|
XZ_DEC_ARM64
|
||||||
XZ_DEC_ANY_CHECK
|
XZ_DEC_ANY_CHECK
|
||||||
XZ_USE_CRC64
|
XZ_USE_CRC64
|
||||||
XZ_USE_SHA256
|
XZ_USE_SHA256
|
||||||
XZ_DEC_CONCATENATED
|
XZ_DEC_CONCATENATED
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(l2c_fcr_hook
|
target_link_libraries(l2c_fcr_hook
|
||||||
android
|
android
|
||||||
log
|
log
|
||||||
)
|
)
|
||||||
|
|
||||||
endif()
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import me.kavishdevar.librepods.utils.XposedServiceHolder
|
|||||||
import me.kavishdevar.librepods.utils.XposedState
|
import me.kavishdevar.librepods.utils.XposedState
|
||||||
|
|
||||||
class LibrePodsApplication: Application(), XposedServiceHelper.OnServiceListener, DefaultLifecycleObserver {
|
class LibrePodsApplication: Application(), XposedServiceHelper.OnServiceListener, DefaultLifecycleObserver {
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
XposedServiceHelper.registerListener(this)
|
XposedServiceHelper.registerListener(this)
|
||||||
BillingManager.provider = BillingProviderFactory.create(this)
|
BillingManager.provider = BillingProviderFactory.create(this)
|
||||||
@@ -24,6 +24,7 @@ package me.kavishdevar.librepods
|
|||||||
// import me.kavishdevar.librepods.utils.RadareOffsetFinder
|
// import me.kavishdevar.librepods.utils.RadareOffsetFinder
|
||||||
//import dagger.hilt.android.AndroidEntryPoint
|
//import dagger.hilt.android.AndroidEntryPoint
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Activity
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -65,7 +66,6 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Notifications
|
import androidx.compose.material.icons.filled.Notifications
|
||||||
@@ -87,13 +87,11 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.draw.scale
|
import androidx.compose.ui.draw.scale
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.drawscope.rotate
|
import androidx.compose.ui.graphics.drawscope.rotate
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
|
||||||
import androidx.compose.ui.platform.LocalWindowInfo
|
import androidx.compose.ui.platform.LocalWindowInfo
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.res.vectorResource
|
import androidx.compose.ui.res.vectorResource
|
||||||
@@ -114,6 +112,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
|||||||
import com.google.accompanist.permissions.MultiplePermissionsState
|
import com.google.accompanist.permissions.MultiplePermissionsState
|
||||||
import com.google.accompanist.permissions.isGranted
|
import com.google.accompanist.permissions.isGranted
|
||||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||||
|
import com.google.android.play.core.review.ReviewManagerFactory
|
||||||
import com.kyant.backdrop.backdrops.layerBackdrop
|
import com.kyant.backdrop.backdrops.layerBackdrop
|
||||||
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
|
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
|
||||||
import dev.chrisbanes.haze.hazeSource
|
import dev.chrisbanes.haze.hazeSource
|
||||||
@@ -122,14 +121,8 @@ import dev.chrisbanes.haze.rememberHazeState
|
|||||||
import me.kavishdevar.librepods.data.AirPodsNotifications
|
import me.kavishdevar.librepods.data.AirPodsNotifications
|
||||||
import me.kavishdevar.librepods.data.ControlCommandRepository
|
import me.kavishdevar.librepods.data.ControlCommandRepository
|
||||||
import me.kavishdevar.librepods.presentation.components.AppInfoCard
|
import me.kavishdevar.librepods.presentation.components.AppInfoCard
|
||||||
import me.kavishdevar.librepods.presentation.components.ConfirmationDialog
|
|
||||||
import me.kavishdevar.librepods.presentation.components.DeviceInfoCard
|
import me.kavishdevar.librepods.presentation.components.DeviceInfoCard
|
||||||
import me.kavishdevar.librepods.presentation.components.SelectItem
|
|
||||||
import me.kavishdevar.librepods.presentation.components.StyledBottomSheet
|
|
||||||
import me.kavishdevar.librepods.presentation.components.StyledButton
|
|
||||||
import me.kavishdevar.librepods.presentation.components.StyledIconButton
|
import me.kavishdevar.librepods.presentation.components.StyledIconButton
|
||||||
import me.kavishdevar.librepods.presentation.components.StyledInputField
|
|
||||||
import me.kavishdevar.librepods.presentation.components.StyledSelectList
|
|
||||||
import me.kavishdevar.librepods.presentation.screens.AccessibilitySettingsScreen
|
import me.kavishdevar.librepods.presentation.screens.AccessibilitySettingsScreen
|
||||||
import me.kavishdevar.librepods.presentation.screens.AdaptiveStrengthScreen
|
import me.kavishdevar.librepods.presentation.screens.AdaptiveStrengthScreen
|
||||||
import me.kavishdevar.librepods.presentation.screens.AirPodsSettingsScreen
|
import me.kavishdevar.librepods.presentation.screens.AirPodsSettingsScreen
|
||||||
@@ -159,6 +152,7 @@ import kotlin.io.encoding.ExperimentalEncodingApi
|
|||||||
|
|
||||||
lateinit var serviceConnection: ServiceConnection
|
lateinit var serviceConnection: ServiceConnection
|
||||||
lateinit var connectionStatusReceiver: BroadcastReceiver
|
lateinit var connectionStatusReceiver: BroadcastReceiver
|
||||||
|
lateinit var testReviewReceiver: BroadcastReceiver
|
||||||
|
|
||||||
//@AndroidEntryPoint
|
//@AndroidEntryPoint
|
||||||
@ExperimentalMaterial3Api
|
@ExperimentalMaterial3Api
|
||||||
@@ -225,8 +219,6 @@ fun Main() {
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val sharedPreferences = context.getSharedPreferences("settings", MODE_PRIVATE)
|
val sharedPreferences = context.getSharedPreferences("settings", MODE_PRIVATE)
|
||||||
if (!isSupported(sharedPreferences) && !XposedState.bluetoothScopeEnabled) {
|
if (!isSupported(sharedPreferences) && !XposedState.bluetoothScopeEnabled) {
|
||||||
val showDialog = remember { mutableStateOf(false) }
|
|
||||||
val showPlayBypassVisible = remember { mutableStateOf(false) }
|
|
||||||
val hazeState = rememberHazeState()
|
val hazeState = rememberHazeState()
|
||||||
val backdrop = rememberLayerBackdrop()
|
val backdrop = rememberLayerBackdrop()
|
||||||
val isDarkTheme = isSystemInDarkTheme()
|
val isDarkTheme = isSystemInDarkTheme()
|
||||||
@@ -243,7 +235,7 @@ fun Main() {
|
|||||||
.background(if (isDarkTheme) Color.Black else Color(0xFFF2F2F7)),
|
.background(if (isDarkTheme) Color.Black else Color(0xFFF2F2F7)),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Column (
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
.verticalScroll(scrollState),
|
.verticalScroll(scrollState),
|
||||||
@@ -288,173 +280,25 @@ fun Main() {
|
|||||||
.padding(horizontal = 12.dp, vertical = 16.dp)
|
.padding(horizontal = 12.dp, vertical = 16.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
StyledButton(
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
onClick = { showDialog.value = true },
|
Text(
|
||||||
backdrop = rememberLayerBackdrop(),
|
text = stringResource(R.string.enable_app_in_xposed_or_update_device),
|
||||||
|
style = TextStyle(
|
||||||
|
fontFamily = FontFamily(Font(R.font.sf_pro)),
|
||||||
|
fontWeight = FontWeight.Light,
|
||||||
|
color = if (isDarkTheme) Color.White else Color.Black,
|
||||||
|
fontSize = 14.sp
|
||||||
|
),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth()
|
||||||
isInteractive = false,
|
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||||
surfaceColor = if (isDarkTheme) Color(0xFF862424) else Color(0xFFC94646)
|
)
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.bypass_compatibility_check),
|
|
||||||
style = TextStyle(
|
|
||||||
fontFamily = FontFamily(Font(R.font.sf_pro)),
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
color = Color.White,
|
|
||||||
fontSize = 16.sp
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
|
||||||
DeviceInfoCard()
|
DeviceInfoCard()
|
||||||
AppInfoCard()
|
AppInfoCard()
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(48.dp))
|
Spacer(modifier = Modifier.height(48.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfirmationDialog(
|
|
||||||
showDialog = showDialog,
|
|
||||||
title = stringResource(R.string.bypass_compatibility_check),
|
|
||||||
message = stringResource(R.string.bypass_compatiblity_check_confirmation),
|
|
||||||
confirmText = stringResource(R.string.yes),
|
|
||||||
dismissText = stringResource(R.string.no),
|
|
||||||
onConfirm = {
|
|
||||||
showDialog.value = false
|
|
||||||
if (BuildConfig.PLAY_BUILD) {
|
|
||||||
showPlayBypassVisible.value = true
|
|
||||||
} else {
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putBoolean("bypass_device_check.v2", true)
|
|
||||||
}
|
|
||||||
val intent = Intent(context, MainActivity::class.java)
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
|
||||||
context.startActivity(intent)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDismiss = {
|
|
||||||
showDialog.value = false
|
|
||||||
},
|
|
||||||
backdrop = backdrop
|
|
||||||
// hazeState = hazeState
|
|
||||||
)
|
|
||||||
|
|
||||||
if (BuildConfig.PLAY_BUILD) {
|
|
||||||
StyledBottomSheet(
|
|
||||||
visible = showPlayBypassVisible.value,
|
|
||||||
onDismiss = {
|
|
||||||
showPlayBypassVisible.value = false
|
|
||||||
showDialog.value = true
|
|
||||||
},
|
|
||||||
backdrop = backdrop
|
|
||||||
) { innerBackdrop, _ ->
|
|
||||||
val contentColor = if (isDarkTheme) Color.White else Color.Black
|
|
||||||
|
|
||||||
var acknowledged by remember { mutableStateOf(false) }
|
|
||||||
val inputState = rememberTextFieldState("")
|
|
||||||
|
|
||||||
val isValid = acknowledged && inputState.text.trim() == "OK"
|
|
||||||
|
|
||||||
val sfPro = FontFamily(Font(R.font.sf_pro))
|
|
||||||
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.bypass_compatibility_check),
|
|
||||||
style = TextStyle(
|
|
||||||
fontFamily = sfPro,
|
|
||||||
fontWeight = FontWeight.SemiBold,
|
|
||||||
fontSize = 18.sp,
|
|
||||||
color = contentColor
|
|
||||||
),
|
|
||||||
modifier = Modifier.padding(horizontal = 12.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.compatibility_play_dialog_confirmation),
|
|
||||||
style = TextStyle(
|
|
||||||
fontFamily = sfPro,
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
fontSize = 14.sp,
|
|
||||||
color = contentColor
|
|
||||||
),
|
|
||||||
modifier = Modifier.padding(horizontal = 12.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
StyledSelectList(
|
|
||||||
items = listOf(
|
|
||||||
SelectItem(
|
|
||||||
name = stringResource(R.string.read_compatibility_requirements),
|
|
||||||
selected = acknowledged,
|
|
||||||
onClick = { acknowledged = !acknowledged }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val focusRequester = remember { FocusRequester() }
|
|
||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
focusRequester.requestFocus()
|
|
||||||
keyboardController?.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledInputField(
|
|
||||||
inputState = inputState,
|
|
||||||
focusRequester = focusRequester,
|
|
||||||
placeholder = stringResource(R.string.type_ok_to_continue, "OK")
|
|
||||||
)
|
|
||||||
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(24.dp)
|
|
||||||
) {
|
|
||||||
StyledButton(
|
|
||||||
onClick = { showPlayBypassVisible.value = false },
|
|
||||||
backdrop = innerBackdrop,
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.no),
|
|
||||||
style = TextStyle(
|
|
||||||
fontFamily = sfPro,
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
fontSize = 14.sp,
|
|
||||||
color = contentColor
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
StyledButton(
|
|
||||||
onClick = {
|
|
||||||
showPlayBypassVisible.value = false
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putBoolean("bypass_device_check.v2", true)
|
|
||||||
val intent = Intent(context, MainActivity::class.java)
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
|
||||||
context.startActivity(intent)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
backdrop = innerBackdrop,
|
|
||||||
isInteractive = isValid,
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
enabled = isValid,
|
|
||||||
surfaceColor = if (isDarkTheme) Color(0xFF0091FF) else Color(0xFF0088FF)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.proceed),
|
|
||||||
style = TextStyle(
|
|
||||||
fontFamily = sfPro,
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
fontSize = 14.sp,
|
|
||||||
color = if (isValid) contentColor else contentColor.copy(alpha = 0.4f)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,6 +359,31 @@ fun Main() {
|
|||||||
|
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
if (BuildConfig.PLAY_BUILD) {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
val firstConn =
|
||||||
|
sharedPreferences.getLong("first_connection_successful_time", 0L)
|
||||||
|
|
||||||
|
val alreadyPrompted =
|
||||||
|
sharedPreferences.getBoolean("review_prompted", false)
|
||||||
|
|
||||||
|
val oneDay = 24 * 60 * 60 * 1000L
|
||||||
|
|
||||||
|
if (
|
||||||
|
firstConn != 0L &&
|
||||||
|
!alreadyPrompted &&
|
||||||
|
(now - firstConn) > oneDay
|
||||||
|
) {
|
||||||
|
triggerReviewFlow(context as? Activity ?: return@LaunchedEffect)
|
||||||
|
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putBoolean("review_prompted", true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
@@ -652,6 +521,12 @@ fun Main() {
|
|||||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||||
val binder = service as AirPodsService.LocalBinder
|
val binder = service as AirPodsService.LocalBinder
|
||||||
airPodsService.value = binder.getService()
|
airPodsService.value = binder.getService()
|
||||||
|
|
||||||
|
if (!sharedPreferences.contains("first_connection_successful_time")) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putLong("first_connection_successful_time", System.currentTimeMillis())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceDisconnected(name: ComponentName?) {
|
override fun onServiceDisconnected(name: ComponentName?) {
|
||||||
@@ -677,6 +552,17 @@ fun Main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun triggerReviewFlow(activity: Activity) {
|
||||||
|
val manager = ReviewManagerFactory.create(activity)
|
||||||
|
val request = manager.requestReviewFlow()
|
||||||
|
request.addOnCompleteListener { task ->
|
||||||
|
if (task.isSuccessful) {
|
||||||
|
val reviewInfo = task.result
|
||||||
|
manager.launchReviewFlow(activity, reviewInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalPermissionsApi::class, ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalPermissionsApi::class, ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PermissionsScreen(
|
fun PermissionsScreen(
|
||||||
|
|||||||
@@ -109,7 +109,8 @@ class AACPManager {
|
|||||||
EAR_DETECTION_CONFIG(0x0A), AUTOMATIC_CONNECTION_CONFIG(0x20), OWNS_CONNECTION(0x06), PPE_TOGGLE_CONFIG(
|
EAR_DETECTION_CONFIG(0x0A), AUTOMATIC_CONNECTION_CONFIG(0x20), OWNS_CONNECTION(0x06), PPE_TOGGLE_CONFIG(
|
||||||
0x37
|
0x37
|
||||||
),
|
),
|
||||||
PPE_CAP_LEVEL_CONFIG(0x38);
|
PPE_CAP_LEVEL_CONFIG(0x38),
|
||||||
|
DYNAMIC_END_OF_CHARGE(0x3B);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromByte(byte: Byte): ControlCommandIdentifiers? =
|
fun fromByte(byte: Byte): ControlCommandIdentifiers? =
|
||||||
|
|||||||
@@ -131,7 +131,6 @@ fun AirPodsSettingsScreen(viewModel: AirPodsViewModel, navController: NavControl
|
|||||||
viewModel.refreshInitialData()
|
viewModel.refreshInitialData()
|
||||||
}
|
}
|
||||||
|
|
||||||
isSystemInDarkTheme()
|
|
||||||
val hazeStateS = remember { mutableStateOf(HazeState()) }
|
val hazeStateS = remember { mutableStateOf(HazeState()) }
|
||||||
|
|
||||||
StyledScaffold(
|
StyledScaffold(
|
||||||
@@ -398,6 +397,16 @@ fun AirPodsSettingsScreen(viewModel: AirPodsViewModel, navController: NavControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item(key = "spacer_dynamic_end_of_charge") { Spacer(modifier = Modifier.height(16.dp)) }
|
||||||
|
item(key = "dynamic_end_of_charge") {
|
||||||
|
StyledToggle(
|
||||||
|
label = stringResource(R.string.optimized_charging),
|
||||||
|
description = stringResource(R.string.optimized_charging_description),
|
||||||
|
checked = state.dynamicEndOfCharge,
|
||||||
|
onCheckedChange = viewModel::setDynamicEndOfCharge
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
item(key = "spacer_accessibility") { Spacer(modifier = Modifier.height(16.dp)) }
|
item(key = "spacer_accessibility") { Spacer(modifier = Modifier.height(16.dp)) }
|
||||||
item(key = "accessibility") {
|
item(key = "accessibility") {
|
||||||
NavigationButton(
|
NavigationButton(
|
||||||
@@ -542,19 +551,22 @@ fun AirPodsSettingsScreen(viewModel: AirPodsViewModel, navController: NavControl
|
|||||||
}
|
}
|
||||||
Spacer(Modifier.height(16.dp))
|
Spacer(Modifier.height(16.dp))
|
||||||
}
|
}
|
||||||
StyledButton(
|
if (state.connectionSuccessful) {
|
||||||
onClick = {
|
StyledButton(
|
||||||
viewModel.reconnectFromSavedMac()
|
onClick = {
|
||||||
}, backdrop = backdrop, modifier = Modifier.fillMaxWidth(0.9f)
|
viewModel.reconnectFromSavedMac()
|
||||||
) {
|
}, backdrop = backdrop, modifier = Modifier.fillMaxWidth(0.9f)
|
||||||
Text(
|
) {
|
||||||
text = stringResource(R.string.reconnect_to_last_device), style = TextStyle(
|
Text(
|
||||||
fontSize = 16.sp,
|
text = stringResource(R.string.reconnect_to_last_device),
|
||||||
fontWeight = FontWeight.Medium,
|
style = TextStyle(
|
||||||
fontFamily = FontFamily(Font(R.font.sf_pro)),
|
fontSize = 16.sp,
|
||||||
color = if (isSystemInDarkTheme()) Color.White else Color.Black
|
fontWeight = FontWeight.Medium,
|
||||||
|
fontFamily = FontFamily(Font(R.font.sf_pro)),
|
||||||
|
color = if (isSystemInDarkTheme()) Color.White else Color.Black
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,11 @@ data class AirPodsUiState(
|
|||||||
val hearingAidData: ByteArray = byteArrayOf(),
|
val hearingAidData: ByteArray = byteArrayOf(),
|
||||||
|
|
||||||
val isPremium: Boolean = false,
|
val isPremium: Boolean = false,
|
||||||
val vendorIdHook: Boolean = false
|
val vendorIdHook: Boolean = false,
|
||||||
|
|
||||||
|
val dynamicEndOfCharge: Boolean = false,
|
||||||
|
|
||||||
|
val connectionSuccessful: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
class AirPodsViewModel(
|
class AirPodsViewModel(
|
||||||
@@ -268,9 +272,16 @@ class AirPodsViewModel(
|
|||||||
val current = state.controlStates[identifier]
|
val current = state.controlStates[identifier]
|
||||||
if (current?.contentEquals(value) == true) return@update state
|
if (current?.contentEquals(value) == true) return@update state
|
||||||
|
|
||||||
state.copy(
|
if (identifier == ControlCommandIdentifiers.DYNAMIC_END_OF_CHARGE) {
|
||||||
controlStates = state.controlStates + (identifier to value)
|
state.copy(
|
||||||
)
|
dynamicEndOfCharge = value[0] == 0x01.toByte(),
|
||||||
|
controlStates = state.controlStates + (identifier to value)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
state.copy(
|
||||||
|
controlStates = state.controlStates + (identifier to value)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,6 +316,7 @@ class AirPodsViewModel(
|
|||||||
ControlCommandIdentifiers.AUTOMATIC_CONNECTION_CONFIG,
|
ControlCommandIdentifiers.AUTOMATIC_CONNECTION_CONFIG,
|
||||||
ControlCommandIdentifiers.OWNS_CONNECTION,
|
ControlCommandIdentifiers.OWNS_CONNECTION,
|
||||||
ControlCommandIdentifiers.PPE_TOGGLE_CONFIG,
|
ControlCommandIdentifiers.PPE_TOGGLE_CONFIG,
|
||||||
|
ControlCommandIdentifiers.DYNAMIC_END_OF_CHARGE
|
||||||
)
|
)
|
||||||
for (identifier in identifiersList) {
|
for (identifier in identifiersList) {
|
||||||
observeControl(identifier)
|
observeControl(identifier)
|
||||||
@@ -342,6 +354,9 @@ class AirPodsViewModel(
|
|||||||
) ?: "CYCLE_NOISE_CONTROL_MODES"
|
) ?: "CYCLE_NOISE_CONTROL_MODES"
|
||||||
)
|
)
|
||||||
val vendorIdHook = xposedRemotePref.getBoolean("vendor_id_hook", false)
|
val vendorIdHook = xposedRemotePref.getBoolean("vendor_id_hook", false)
|
||||||
|
val dynamicEndOfCharge = sharedPreferences.getBoolean("dynamic_end_of_charge", false)
|
||||||
|
|
||||||
|
val connectionSuccessful = sharedPreferences.getBoolean("connection_successful", false)
|
||||||
|
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
@@ -351,7 +366,9 @@ class AirPodsViewModel(
|
|||||||
headGesturesEnabled = headGesturesEnabled,
|
headGesturesEnabled = headGesturesEnabled,
|
||||||
leftAction = leftAction,
|
leftAction = leftAction,
|
||||||
rightAction = rightAction,
|
rightAction = rightAction,
|
||||||
vendorIdHook = vendorIdHook
|
vendorIdHook = vendorIdHook,
|
||||||
|
dynamicEndOfCharge = dynamicEndOfCharge,
|
||||||
|
connectionSuccessful = connectionSuccessful
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -371,6 +388,14 @@ class AirPodsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setDynamicEndOfCharge(enabled: Boolean) {
|
||||||
|
service.aacpManager.sendControlCommand(ControlCommandIdentifiers.DYNAMIC_END_OF_CHARGE.value, enabled)
|
||||||
|
sharedPreferences.edit { putBoolean("dynamic_end_of_charge", enabled) }
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(dynamicEndOfCharge = enabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadControlList() {
|
private fun loadControlList() {
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ import me.kavishdevar.librepods.utils.SystemApisUtils.METADATA_UNTETHERED_RIGHT_
|
|||||||
import me.kavishdevar.librepods.utils.SystemApisUtils.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD
|
import me.kavishdevar.librepods.utils.SystemApisUtils.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
|
import java.time.LocalDateTime
|
||||||
import kotlin.io.encoding.Base64
|
import kotlin.io.encoding.Base64
|
||||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||||
|
|
||||||
@@ -526,7 +527,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
|
|
||||||
initializeConfig()
|
initializeConfig()
|
||||||
|
|
||||||
ancModeReceiver = object : BroadcastReceiver() {
|
externalBroadcastReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
if (intent?.action == "me.kavishdevar.librepods.SET_ANC_MODE") {
|
if (intent?.action == "me.kavishdevar.librepods.SET_ANC_MODE") {
|
||||||
if (intent.hasExtra("mode")) {
|
if (intent.hasExtra("mode")) {
|
||||||
@@ -555,15 +556,23 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
"Cycling ANC mode from $currentMode to $nextMode"
|
"Cycling ANC mode from $currentMode to $nextMode"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else if (intent?.action == "me.kavishdevar.librepods.CONVO_DETECT") {
|
||||||
|
if (intent.hasExtra("enabled")) {
|
||||||
|
val enabled = intent.getBooleanExtra("enabled", false)
|
||||||
|
aacpManager.sendControlCommand(
|
||||||
|
AACPManager.Companion.ControlCommandIdentifiers.CONVERSATION_DETECT_CONFIG.value,
|
||||||
|
enabled
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
registerReceiver(ancModeReceiver, ancModeFilter, RECEIVER_EXPORTED)
|
registerReceiver(externalBroadcastReceiver, externalBroadcastFilter, RECEIVER_EXPORTED)
|
||||||
} else {
|
} else {
|
||||||
@Suppress("UnspecifiedRegisterReceiverFlag") registerReceiver(
|
@Suppress("UnspecifiedRegisterReceiverFlag") registerReceiver(
|
||||||
ancModeReceiver, ancModeFilter
|
externalBroadcastReceiver, externalBroadcastFilter
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val audioManager = this@AirPodsService.getSystemService(AUDIO_SERVICE) as AudioManager
|
val audioManager = this@AirPodsService.getSystemService(AUDIO_SERVICE) as AudioManager
|
||||||
@@ -2397,8 +2406,11 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val ancModeFilter = IntentFilter("me.kavishdevar.librepods.SET_ANC_MODE")
|
val externalBroadcastFilter = IntentFilter().apply {
|
||||||
var ancModeReceiver: BroadcastReceiver? = null
|
addAction("me.kavishdevar.librepods.SET_ANC_MODE")
|
||||||
|
addAction("me.kavishdevar.librepods.CONVO_DETECT")
|
||||||
|
}
|
||||||
|
var externalBroadcastReceiver: BroadcastReceiver? = null
|
||||||
|
|
||||||
@SuppressLint("InlinedApi", "MissingPermission", "UnspecifiedRegisterReceiverFlag")
|
@SuppressLint("InlinedApi", "MissingPermission", "UnspecifiedRegisterReceiverFlag")
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
@@ -2701,6 +2713,14 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
)
|
)
|
||||||
Log.d(TAG, "<LogCollector:Complete:Success> Socket connected")
|
Log.d(TAG, "<LogCollector:Complete:Success> Socket connected")
|
||||||
sharedPreferences.edit { putBoolean("connection_successful", true) }
|
sharedPreferences.edit { putBoolean("connection_successful", true) }
|
||||||
|
if (!sharedPreferences.contains("first_connection_successful_time")) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putLong(
|
||||||
|
"first_connection_successful_time",
|
||||||
|
System.currentTimeMillis()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_L2CAP_CONNECTED))
|
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_L2CAP_CONNECTED))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// sharedPreferences.edit { putBoolean("connection_successful", false) }
|
// sharedPreferences.edit { putBoolean("connection_successful", false) }
|
||||||
@@ -3104,7 +3124,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
unregisterReceiver(ancModeReceiver)
|
unregisterReceiver(externalBroadcastReceiver)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import android.os.Build
|
|||||||
|
|
||||||
fun isSupported(sharedPreferences: SharedPreferences): Boolean {
|
fun isSupported(sharedPreferences: SharedPreferences): Boolean {
|
||||||
val isPixel = Build.MANUFACTURER.lowercase() == "google"
|
val isPixel = Build.MANUFACTURER.lowercase() == "google"
|
||||||
val isOppoOrOnePlus = Build.MANUFACTURER.lowercase() in listOf("oneplus", "oppo")
|
val isOppoFamily = Build.MANUFACTURER.lowercase() in listOf("oneplus", "oppo", "realme")
|
||||||
val isBypassFlagActive = sharedPreferences.getBoolean("bypass_device_check.v2", false)
|
val isBypassFlagActive = sharedPreferences.getBoolean("bypass_device_check.v2", false)
|
||||||
|
|
||||||
if (isBypassFlagActive) return true
|
if (isBypassFlagActive) return true
|
||||||
@@ -31,14 +31,14 @@ fun isSupported(sharedPreferences: SharedPreferences): Boolean {
|
|||||||
if (isPixel) {
|
if (isPixel) {
|
||||||
when (Build.VERSION.SDK_INT) {
|
when (Build.VERSION.SDK_INT) {
|
||||||
36 -> {
|
36 -> {
|
||||||
return Build.ID == "CP1A.260305.018" || Build.ID == "CP1A.260405.005"
|
return Build.ID.startsWith("CP1A")
|
||||||
}
|
}
|
||||||
|
|
||||||
37 -> {
|
37 -> {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (isOppoOrOnePlus) {
|
} else if (isOppoFamily) {
|
||||||
return Build.VERSION.SDK_INT >= 36
|
return Build.VERSION.SDK_INT >= 36
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -252,7 +252,8 @@
|
|||||||
\n• Google Pixel® running 17 Beta 3 and above
|
\n• Google Pixel® running 17 Beta 3 and above
|
||||||
\n• OnePlus devices running OxygenOS 16 or later
|
\n• OnePlus devices running OxygenOS 16 or later
|
||||||
\n• Oppo devices running ColorOS 16 or later
|
\n• Oppo devices running ColorOS 16 or later
|
||||||
\n\nFor details, see the project documentation.</string>
|
\n\nFor details, see the project documentation.
|
||||||
|
</string>
|
||||||
<string name="name_your_own_price">(Name your own price)</string>
|
<string name="name_your_own_price">(Name your own price)</string>
|
||||||
<string name="compatibility_play_dialog_confirmation">
|
<string name="compatibility_play_dialog_confirmation">
|
||||||
This device may not be supported due to platform limitations and requires an Xposed framework. Tick the checkbox below and type OK to continue.
|
This device may not be supported due to platform limitations and requires an Xposed framework. Tick the checkbox below and type OK to continue.
|
||||||
@@ -272,4 +273,7 @@
|
|||||||
<string name="app_enabled_in_xposed">App enabled in Xposed</string>
|
<string name="app_enabled_in_xposed">App enabled in Xposed</string>
|
||||||
<string name="subject">Subject</string>
|
<string name="subject">Subject</string>
|
||||||
<string name="describe_your_issue">Describe your issue</string>
|
<string name="describe_your_issue">Describe your issue</string>
|
||||||
|
<string name="optimized_charging">Optimized Charge Limit</string>
|
||||||
|
<string name="optimized_charging_description">AirPods can learn from your daily usage and determine when to charge to an optmized limit and when to allow or full charge. This limit adapts to your daily usage and preserves your battery lifespan over time.\nThis setting may not affect unsupported AirPods, or AirPods on an older firmware version.</string>
|
||||||
|
<string name="enable_app_in_xposed_or_update_device">Enable LibrePods in Xposed or update your device to proceed.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
package me.kavishdevar.librepods
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
|
||||||
import me.kavishdevar.librepods.billing.BillingManager
|
|
||||||
import me.kavishdevar.librepods.billing.BillingProviderFactory
|
|
||||||
|
|
||||||
class LibrePodsApplication: Application(), DefaultLifecycleObserver {
|
|
||||||
override fun onCreate() {
|
|
||||||
BillingManager.provider = BillingProviderFactory.create(this)
|
|
||||||
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
|
||||||
|
|
||||||
super<Application>.onCreate()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume(owner: LifecycleOwner) {
|
|
||||||
BillingManager.provider.queryPurchases()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package me.kavishdevar.librepods.data
|
|
||||||
|
|
||||||
class XposedRemotePrefImpl: XposedRemotePref {
|
|
||||||
override fun isAvailable(): Boolean { return false }
|
|
||||||
|
|
||||||
override fun getBoolean(key: String, def: Boolean): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun putBoolean(key: String, value: Boolean) { }
|
|
||||||
}
|
|
||||||
@@ -1,24 +1,25 @@
|
|||||||
[versions]
|
[versions]
|
||||||
accompanistPermissions = "0.37.3"
|
accompanistPermissions = "0.37.3"
|
||||||
agp = "9.1.0"
|
agp = "9.1.1"
|
||||||
kotlin = "2.3.20"
|
kotlin = "2.3.21"
|
||||||
coreKtx = "1.18.0"
|
coreKtx = "1.18.0"
|
||||||
lifecycleRuntimeKtx = "2.10.0"
|
lifecycleRuntimeKtx = "2.10.0"
|
||||||
activityCompose = "1.13.0"
|
activityCompose = "1.13.0"
|
||||||
composeBom = "2026.03.01"
|
composeBom = "2026.05.00"
|
||||||
annotations = "26.1.0"
|
annotations = "26.1.0"
|
||||||
navigationCompose = "2.9.7"
|
navigationCompose = "2.9.8"
|
||||||
constraintlayout = "2.2.1"
|
constraintlayout = "2.2.1"
|
||||||
haze = "1.7.2"
|
haze = "1.7.2"
|
||||||
hazeMaterials = "1.7.2"
|
hazeMaterials = "1.7.2"
|
||||||
dynamicanimation = "1.1.0"
|
dynamicanimation = "1.1.0"
|
||||||
aboutLibraries = "14.0.1"
|
aboutLibraries = "14.2.0"
|
||||||
materialIconsCore = "1.7.8"
|
materialIconsCore = "1.7.8"
|
||||||
backdrop = "2.0.0-alpha03"
|
backdrop = "2.0.0-alpha03"
|
||||||
billing = "8.3.0"
|
billing = "8.3.0"
|
||||||
hilt = "2.59.2"
|
hilt = "2.59.2"
|
||||||
xposed = "101.0.0"
|
xposed = "101.0.0"
|
||||||
lifecycleProcess = "2.10.0"
|
lifecycleProcess = "2.10.0"
|
||||||
|
play = "2.0.2"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" }
|
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" }
|
||||||
@@ -49,6 +50,8 @@ hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.r
|
|||||||
libxposed-api = { group = "io.github.libxposed", name = "api", version.ref = "xposed" }
|
libxposed-api = { group = "io.github.libxposed", name = "api", version.ref = "xposed" }
|
||||||
libxposed-service = { group = "io.github.libxposed", name = "service", version.ref = "xposed" }
|
libxposed-service = { group = "io.github.libxposed", name = "service", version.ref = "xposed" }
|
||||||
androidx-lifecycle-process = { group = "androidx.lifecycle", name = "lifecycle-process", version.ref = "lifecycleProcess" }
|
androidx-lifecycle-process = { group = "androidx.lifecycle", name = "lifecycle-process", version.ref = "lifecycleProcess" }
|
||||||
|
play-review = { group = "com.google.android.play", name="review", version.ref = "play" }
|
||||||
|
play-review-ktx = { group = "com.google.android.play", name="review-ktx", version.ref = "play" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|||||||
Reference in New Issue
Block a user