mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-05-04 20:05:18 +00:00
android: fix island not closing
This commit is contained in:
@@ -38,6 +38,7 @@ import androidx.compose.foundation.layout.width
|
|||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
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.automirrored.filled.KeyboardArrowLeft
|
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
|
||||||
@@ -49,11 +50,15 @@ import androidx.compose.material3.CenterAlignedTopAppBar
|
|||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
import androidx.compose.material3.SliderDefaults
|
import androidx.compose.material3.SliderDefaults
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.TextField
|
||||||
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -76,6 +81,8 @@ import androidx.compose.ui.text.TextStyle
|
|||||||
import androidx.compose.ui.text.font.Font
|
import androidx.compose.ui.text.font.Font
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
@@ -88,10 +95,13 @@ import dev.chrisbanes.haze.materials.CupertinoMaterials
|
|||||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||||
import me.kavishdevar.librepods.R
|
import me.kavishdevar.librepods.R
|
||||||
import me.kavishdevar.librepods.composables.StyledSwitch
|
import me.kavishdevar.librepods.composables.StyledSwitch
|
||||||
|
import me.kavishdevar.librepods.utils.AACPManager
|
||||||
import me.kavishdevar.librepods.utils.RadareOffsetFinder
|
import me.kavishdevar.librepods.utils.RadareOffsetFinder
|
||||||
|
import kotlin.io.encoding.Base64
|
||||||
|
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class, ExperimentalEncodingApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AppSettingsScreen(navController: NavController) {
|
fun AppSettingsScreen(navController: NavController) {
|
||||||
val sharedPreferences = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
val sharedPreferences = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||||
@@ -103,6 +113,35 @@ fun AppSettingsScreen(navController: NavController) {
|
|||||||
val hazeState = remember { HazeState() }
|
val hazeState = remember { HazeState() }
|
||||||
|
|
||||||
var showResetDialog by remember { mutableStateOf(false) }
|
var showResetDialog by remember { mutableStateOf(false) }
|
||||||
|
var showIrkDialog by remember { mutableStateOf(false) }
|
||||||
|
var showEncKeyDialog by remember { mutableStateOf(false) }
|
||||||
|
var irkValue by remember { mutableStateOf("") }
|
||||||
|
var encKeyValue by remember { mutableStateOf("") }
|
||||||
|
var irkError by remember { mutableStateOf<String?>(null) }
|
||||||
|
var encKeyError by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
val savedIrk = sharedPreferences.getString(AACPManager.Companion.ProximityKeyType.IRK.name, null)
|
||||||
|
val savedEncKey = sharedPreferences.getString(AACPManager.Companion.ProximityKeyType.ENC_KEY.name, null)
|
||||||
|
|
||||||
|
if (savedIrk != null) {
|
||||||
|
try {
|
||||||
|
val decoded = Base64.decode(savedIrk)
|
||||||
|
irkValue = decoded.joinToString("") { "%02x".format(it) }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
irkValue = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (savedEncKey != null) {
|
||||||
|
try {
|
||||||
|
val decoded = Base64.decode(savedEncKey)
|
||||||
|
encKeyValue = decoded.joinToString("") { "%02x".format(it) }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
encKeyValue = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var showPhoneBatteryInWidget by remember {
|
var showPhoneBatteryInWidget by remember {
|
||||||
mutableStateOf(sharedPreferences.getBoolean("show_phone_battery_in_widget", true))
|
mutableStateOf(sharedPreferences.getBoolean("show_phone_battery_in_widget", true))
|
||||||
@@ -142,6 +181,11 @@ fun AppSettingsScreen(navController: NavController) {
|
|||||||
|
|
||||||
var mDensity by remember { mutableFloatStateOf(0f) }
|
var mDensity by remember { mutableFloatStateOf(0f) }
|
||||||
|
|
||||||
|
fun validateHexInput(input: String): Boolean {
|
||||||
|
val hexPattern = Regex("^[0-9a-fA-F]{32}$")
|
||||||
|
return hexPattern.matches(input)
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
@@ -659,7 +703,6 @@ fun AppSettingsScreen(navController: NavController) {
|
|||||||
modifier = Modifier.padding(top = 12.dp, bottom = 4.dp)
|
modifier = Modifier.padding(top = 12.dp, bottom = 4.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Disconnected
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -701,7 +744,6 @@ fun AppSettingsScreen(navController: NavController) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Idle
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -743,7 +785,6 @@ fun AppSettingsScreen(navController: NavController) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Music
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -785,7 +826,6 @@ fun AppSettingsScreen(navController: NavController) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -837,7 +877,6 @@ fun AppSettingsScreen(navController: NavController) {
|
|||||||
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp)
|
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ringing Call
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -879,7 +918,6 @@ fun AppSettingsScreen(navController: NavController) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Media Start
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -944,6 +982,64 @@ fun AppSettingsScreen(navController: NavController) {
|
|||||||
)
|
)
|
||||||
.padding(horizontal = 16.dp, vertical = 4.dp)
|
.padding(horizontal = 16.dp, vertical = 4.dp)
|
||||||
) {
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable {
|
||||||
|
showIrkDialog = true
|
||||||
|
},
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(vertical = 8.dp)
|
||||||
|
.padding(end = 4.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Set Identity Resolving Key (IRK)",
|
||||||
|
fontSize = 16.sp,
|
||||||
|
color = textColor
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
Text(
|
||||||
|
text = "Manually set the IRK value used for resolving BLE random addresses",
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = textColor.copy(0.6f),
|
||||||
|
lineHeight = 16.sp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable {
|
||||||
|
showEncKeyDialog = true
|
||||||
|
},
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(vertical = 8.dp)
|
||||||
|
.padding(end = 4.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Set Encryption Key",
|
||||||
|
fontSize = 16.sp,
|
||||||
|
color = textColor
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
Text(
|
||||||
|
text = "Manually set the ENC_KEY value used for decrypting BLE advertisements",
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = textColor.copy(0.6f),
|
||||||
|
lineHeight = 16.sp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -1073,6 +1169,184 @@ fun AppSettingsScreen(navController: NavController) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showIrkDialog) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showIrkDialog = false },
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
"Set Identity Resolving Key (IRK)",
|
||||||
|
fontFamily = FontFamily(Font(R.font.sf_pro)),
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
"Enter 16-byte IRK as hex string (32 characters):",
|
||||||
|
fontFamily = FontFamily(Font(R.font.sf_pro)),
|
||||||
|
modifier = Modifier.padding(bottom = 8.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = irkValue,
|
||||||
|
onValueChange = {
|
||||||
|
irkValue = it.lowercase().filter { char -> char.isDigit() || char in 'a'..'f' }
|
||||||
|
irkError = null
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
isError = irkError != null,
|
||||||
|
keyboardOptions = KeyboardOptions(
|
||||||
|
keyboardType = KeyboardType.Ascii,
|
||||||
|
capitalization = KeyboardCapitalization.None
|
||||||
|
),
|
||||||
|
colors = OutlinedTextFieldDefaults.colors(
|
||||||
|
focusedBorderColor = if (isDarkTheme) Color(0xFF007AFF) else Color(0xFF3C6DF5),
|
||||||
|
unfocusedBorderColor = if (isDarkTheme) Color.Gray else Color.LightGray
|
||||||
|
),
|
||||||
|
supportingText = {
|
||||||
|
if (irkError != null) {
|
||||||
|
Text(irkError!!, color = MaterialTheme.colorScheme.error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label = { Text("IRK Hex Value") }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
if (!validateHexInput(irkValue)) {
|
||||||
|
irkError = "Must be exactly 32 hex characters"
|
||||||
|
return@TextButton
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val hexBytes = ByteArray(16)
|
||||||
|
for (i in 0 until 16) {
|
||||||
|
val hexByte = irkValue.substring(i * 2, i * 2 + 2)
|
||||||
|
hexBytes[i] = hexByte.toInt(16).toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
val base64Value = Base64.encode(hexBytes)
|
||||||
|
sharedPreferences.edit().putString(AACPManager.Companion.ProximityKeyType.IRK.name, base64Value).apply()
|
||||||
|
|
||||||
|
Toast.makeText(context, "IRK has been set successfully", Toast.LENGTH_SHORT).show()
|
||||||
|
showIrkDialog = false
|
||||||
|
} catch (e: Exception) {
|
||||||
|
irkError = "Error converting hex: ${e.message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
"Save",
|
||||||
|
fontFamily = FontFamily(Font(R.font.sf_pro)),
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = { showIrkDialog = false }
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
"Cancel",
|
||||||
|
fontFamily = FontFamily(Font(R.font.sf_pro)),
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showEncKeyDialog) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showEncKeyDialog = false },
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
"Set Encryption Key",
|
||||||
|
fontFamily = FontFamily(Font(R.font.sf_pro)),
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
"Enter 16-byte ENC_KEY as hex string (32 characters):",
|
||||||
|
fontFamily = FontFamily(Font(R.font.sf_pro)),
|
||||||
|
modifier = Modifier.padding(bottom = 8.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = encKeyValue,
|
||||||
|
onValueChange = {
|
||||||
|
encKeyValue = it.lowercase().filter { char -> char.isDigit() || char in 'a'..'f' }
|
||||||
|
encKeyError = null
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
isError = encKeyError != null,
|
||||||
|
keyboardOptions = KeyboardOptions(
|
||||||
|
keyboardType = KeyboardType.Ascii,
|
||||||
|
capitalization = KeyboardCapitalization.None
|
||||||
|
),
|
||||||
|
colors = OutlinedTextFieldDefaults.colors(
|
||||||
|
focusedBorderColor = if (isDarkTheme) Color(0xFF007AFF) else Color(0xFF3C6DF5),
|
||||||
|
unfocusedBorderColor = if (isDarkTheme) Color.Gray else Color.LightGray
|
||||||
|
),
|
||||||
|
supportingText = {
|
||||||
|
if (encKeyError != null) {
|
||||||
|
Text(encKeyError!!, color = MaterialTheme.colorScheme.error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label = { Text("ENC_KEY Hex Value") }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
if (!validateHexInput(encKeyValue)) {
|
||||||
|
encKeyError = "Must be exactly 32 hex characters"
|
||||||
|
return@TextButton
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val hexBytes = ByteArray(16)
|
||||||
|
for (i in 0 until 16) {
|
||||||
|
val hexByte = encKeyValue.substring(i * 2, i * 2 + 2)
|
||||||
|
hexBytes[i] = hexByte.toInt(16).toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
val base64Value = Base64.encode(hexBytes)
|
||||||
|
sharedPreferences.edit().putString(AACPManager.Companion.ProximityKeyType.ENC_KEY.name, base64Value).apply()
|
||||||
|
|
||||||
|
Toast.makeText(context, "Encryption key has been set successfully", Toast.LENGTH_SHORT).show()
|
||||||
|
showEncKeyDialog = false
|
||||||
|
} catch (e: Exception) {
|
||||||
|
encKeyError = "Error converting hex: ${e.message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
"Save",
|
||||||
|
fontFamily = FontFamily(Font(R.font.sf_pro)),
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = { showEncKeyDialog = false }
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
"Cancel",
|
||||||
|
fontFamily = FontFamily(Font(R.font.sf_pro)),
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,6 +263,9 @@ class IslandWindow(private val context: Context) {
|
|||||||
if (abs(deltaY) > 5 || isBeingDragged) {
|
if (abs(deltaY) > 5 || isBeingDragged) {
|
||||||
isBeingDragged = true
|
isBeingDragged = true
|
||||||
|
|
||||||
|
// Cancel auto close timer when dragging starts
|
||||||
|
autoCloseHandler?.removeCallbacks(autoCloseRunnable ?: return@setOnTouchListener false)
|
||||||
|
|
||||||
val dampedDeltaY = if (deltaY > 0) {
|
val dampedDeltaY = if (deltaY > 0) {
|
||||||
initialY + (deltaY * 0.6f)
|
initialY + (deltaY * 0.6f)
|
||||||
} else {
|
} else {
|
||||||
@@ -417,6 +420,7 @@ class IslandWindow(private val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun resetAutoCloseTimer() {
|
private fun resetAutoCloseTimer() {
|
||||||
|
autoCloseHandler?.removeCallbacks(autoCloseRunnable ?: return)
|
||||||
autoCloseHandler = Handler(Looper.getMainLooper())
|
autoCloseHandler = Handler(Looper.getMainLooper())
|
||||||
autoCloseRunnable = Runnable { close() }
|
autoCloseRunnable = Runnable { close() }
|
||||||
autoCloseHandler?.postDelayed(autoCloseRunnable!!, 4500)
|
autoCloseHandler?.postDelayed(autoCloseRunnable!!, 4500)
|
||||||
@@ -501,7 +505,7 @@ class IslandWindow(private val context: Context) {
|
|||||||
}
|
}
|
||||||
flingAnimator.addListener(object : AnimatorListenerAdapter() {
|
flingAnimator.addListener(object : AnimatorListenerAdapter() {
|
||||||
override fun onAnimationEnd(animation: Animator) {
|
override fun onAnimationEnd(animation: Animator) {
|
||||||
close()
|
forceClose()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -556,7 +560,7 @@ class IslandWindow(private val context: Context) {
|
|||||||
normalizeAnimator.addListener(object : AnimatorListenerAdapter() {
|
normalizeAnimator.addListener(object : AnimatorListenerAdapter() {
|
||||||
override fun onAnimationEnd(animation: Animator) {
|
override fun onAnimationEnd(animation: Animator) {
|
||||||
ServiceManager.getService()?.startMainActivity()
|
ServiceManager.getService()?.startMainActivity()
|
||||||
close()
|
forceClose()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -611,7 +615,12 @@ class IslandWindow(private val context: Context) {
|
|||||||
resetStretchEffects(0f)
|
resetStretchEffects(0f)
|
||||||
|
|
||||||
val videoView = islandView.findViewById<VideoView>(R.id.island_video_view)
|
val videoView = islandView.findViewById<VideoView>(R.id.island_video_view)
|
||||||
videoView.stopPlayback()
|
try {
|
||||||
|
videoView.stopPlayback()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
val scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, containerView.scaleX, 0.5f)
|
val scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, containerView.scaleX, 0.5f)
|
||||||
val scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, containerView.scaleY, 0.5f)
|
val scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, containerView.scaleY, 0.5f)
|
||||||
val translationY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, containerView.translationY, -200f)
|
val translationY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, containerView.translationY, -200f)
|
||||||
@@ -620,19 +629,56 @@ class IslandWindow(private val context: Context) {
|
|||||||
interpolator = AnticipateOvershootInterpolator()
|
interpolator = AnticipateOvershootInterpolator()
|
||||||
addListener(object : AnimatorListenerAdapter() {
|
addListener(object : AnimatorListenerAdapter() {
|
||||||
override fun onAnimationEnd(animation: Animator) {
|
override fun onAnimationEnd(animation: Animator) {
|
||||||
containerView.visibility = View.GONE
|
cleanupAndRemoveView()
|
||||||
try {
|
|
||||||
windowManager.removeView(containerView)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e("IslandWindow", "Error removing view: $e")
|
|
||||||
}
|
|
||||||
isClosing = false
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
start()
|
start()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
// Even if animation fails, ensure we cleanup
|
||||||
|
cleanupAndRemoveView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanupAndRemoveView() {
|
||||||
|
containerView.visibility = View.GONE
|
||||||
|
try {
|
||||||
|
if (containerView.parent != null) {
|
||||||
|
windowManager.removeView(containerView)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e("IslandWindow", "Error removing view: $e")
|
||||||
|
}
|
||||||
|
isClosing = false
|
||||||
|
// Make sure all animations are canceled
|
||||||
|
springAnimation.cancel()
|
||||||
|
flingAnimator.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun forceClose() {
|
||||||
|
try {
|
||||||
|
if (isClosing) return
|
||||||
|
isClosing = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
context.unregisterReceiver(batteryReceiver)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Silent catch - receiver might already be unregistered
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceManager.getService()?.islandOpen = false
|
||||||
|
autoCloseHandler?.removeCallbacks(autoCloseRunnable ?: return)
|
||||||
|
|
||||||
|
// Cancel all ongoing animations
|
||||||
|
springAnimation.cancel()
|
||||||
|
flingAnimator.cancel()
|
||||||
|
|
||||||
|
// Immediately remove the view without animations
|
||||||
|
cleanupAndRemoveView()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
isClosing = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user