Compare commits
47
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38cf7a3569 | ||
|
|
760a26b007 | ||
|
|
47f3e424ff | ||
|
|
f731775346 | ||
|
|
3c5486cc5c | ||
|
|
544515feba | ||
|
|
9a54da0808 | ||
|
|
8bc1f16e2c | ||
|
|
5dc7ce6a22 | ||
|
|
ff408747cd | ||
|
|
6636dd8e98 | ||
|
|
15ab82f1c8 | ||
|
|
0ea9c0ede1 | ||
|
|
c496d90d5a | ||
|
|
c101870b71 | ||
|
|
9a4ceddcb0 | ||
|
|
50233cadc1 | ||
|
|
8d88100b15 | ||
|
|
59d6e2ae05 | ||
|
|
e2b5c6d673 | ||
|
|
da89d6426f | ||
|
|
c19209655b | ||
|
|
d456bdf4b5 | ||
|
|
f06b17a194 | ||
|
|
675194075e | ||
|
|
0ecccc4746 | ||
|
|
a7b41c95c7 | ||
|
|
88d1498eac | ||
|
|
31ae04329d | ||
|
|
eae6304124 | ||
|
|
97c6d609ac | ||
|
|
44c442ddd3 | ||
|
|
f9250ec1f1 | ||
|
|
bff3e1af09 | ||
|
|
6dbe30b3f6 | ||
|
|
8a920acead | ||
|
|
929f652b8e | ||
|
|
610768501e | ||
|
|
427e6e1862 | ||
|
|
bfa5e5f474 | ||
|
|
213425379c | ||
|
|
c3444aed24 | ||
|
|
368bd199e5 | ||
|
|
2298d8eea4 | ||
|
|
7bb0bbbb46 | ||
|
|
deec5581ae | ||
|
|
1de5632428 |
No files matched your search
@@ -39,19 +39,22 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "${{ secrets.KEY_STORE }}" | base64 -d > keystore.jks
|
echo "${{ secrets.KEY_STORE }}" | base64 -d > keystore.jks
|
||||||
APK=$(find app/build/outputs/apk/release -name "*.apk" | head -1)
|
APK=$(find app/build/outputs/apk/release -name "*.apk" | head -1)
|
||||||
|
VERSION=${TAG_NAME#v}
|
||||||
|
SIGNED_APK="app/build/outputs/apk/release/Look4Sat-Pro-${VERSION}.apk"
|
||||||
BUILD_TOOLS=$(ls -d ${ANDROID_HOME}/build-tools/*/ | sort -V | tail -1)
|
BUILD_TOOLS=$(ls -d ${ANDROID_HOME}/build-tools/*/ | sort -V | tail -1)
|
||||||
${BUILD_TOOLS}apksigner sign \
|
${BUILD_TOOLS}apksigner sign \
|
||||||
--ks keystore.jks \
|
--ks keystore.jks \
|
||||||
--ks-pass pass:${{ secrets.KEY_STORE_PASSWORD }} \
|
--ks-pass pass:${{ secrets.KEY_STORE_PASSWORD }} \
|
||||||
--ks-key-alias ${{ secrets.KEY_ALIAS }} \
|
--ks-key-alias ${{ secrets.KEY_ALIAS }} \
|
||||||
--key-pass pass:${{ secrets.KEY_PASSWORD }} \
|
--key-pass pass:${{ secrets.KEY_PASSWORD }} \
|
||||||
--out app/build/outputs/apk/release/look4sat.apk \
|
--out "$SIGNED_APK" \
|
||||||
"$APK"
|
"$APK"
|
||||||
rm keystore.jks
|
rm keystore.jks
|
||||||
|
echo "SIGNED_APK=$SIGNED_APK" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
run: |
|
run: |
|
||||||
gh release create $TAG_NAME --title=$TAG_NAME --generate-notes
|
gh release create $TAG_NAME --title=$TAG_NAME --generate-notes
|
||||||
gh release upload $TAG_NAME app/build/outputs/apk/release/look4sat.apk
|
gh release upload $TAG_NAME "$SIGNED_APK"
|
||||||
+1
-2
@@ -39,8 +39,7 @@ captures/
|
|||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
# Keystore files
|
# Keystore files
|
||||||
# Uncomment the following line if you do not want to check your keystore files in.
|
*.jks
|
||||||
#*.jks
|
|
||||||
/*.properties
|
/*.properties
|
||||||
/keystore.properties
|
/keystore.properties
|
||||||
/app/keystore.jks
|
/app/keystore.jks
|
||||||
|
|||||||
@@ -1,3 +1,36 @@
|
|||||||
|
import java.util.Properties
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.convention.applicationPlugin)
|
alias(libs.plugins.convention.applicationPlugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load signing config from keystore.properties (gitignored, never commit credentials)
|
||||||
|
val keystoreProperties = Properties().apply {
|
||||||
|
val propsFile = rootProject.file("keystore.properties")
|
||||||
|
if (propsFile.exists()) propsFile.inputStream().use { load(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
// CW 解码 native 库仅 armeabi-v7a(照搬 Morse Expert 1.15): 全 ABI 打包会在
|
||||||
|
// arm64 设备 loadLibrary 失败, 强制 32 位兼容(用户设备为 32 位软件)
|
||||||
|
defaultConfig {
|
||||||
|
ndk {
|
||||||
|
abiFilters += listOf("armeabi-v7a")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signingConfigs {
|
||||||
|
if (keystoreProperties["storeFile"] != null) {
|
||||||
|
create("release") {
|
||||||
|
storeFile = rootProject.file(keystoreProperties["storeFile"] as String)
|
||||||
|
storePassword = keystoreProperties["storePassword"] as String
|
||||||
|
keyAlias = keystoreProperties["keyAlias"] as String
|
||||||
|
keyPassword = keystoreProperties["keyPassword"] as String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
signingConfig = signingConfigs.findByName("release")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,16 +17,23 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat
|
package com.rtbishop.look4sat
|
||||||
|
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.animation.core.LinearEasing
|
import androidx.compose.animation.core.LinearEasing
|
||||||
import androidx.compose.animation.core.RepeatMode
|
import androidx.compose.animation.core.RepeatMode
|
||||||
|
import androidx.compose.animation.core.Spring
|
||||||
import androidx.compose.animation.core.animateFloat
|
import androidx.compose.animation.core.animateFloat
|
||||||
import androidx.compose.animation.core.infiniteRepeatable
|
import androidx.compose.animation.core.infiniteRepeatable
|
||||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||||
|
import androidx.compose.animation.core.spring
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.expandVertically
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.scaleIn
|
import androidx.compose.animation.scaleIn
|
||||||
import androidx.compose.animation.scaleOut
|
import androidx.compose.animation.scaleOut
|
||||||
|
import androidx.compose.animation.shrinkVertically
|
||||||
import androidx.compose.animation.slideInHorizontally
|
import androidx.compose.animation.slideInHorizontally
|
||||||
import androidx.compose.animation.slideOutHorizontally
|
import androidx.compose.animation.slideOutHorizontally
|
||||||
import androidx.compose.animation.togetherWith
|
import androidx.compose.animation.togetherWith
|
||||||
@@ -53,6 +60,9 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
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
|
||||||
@@ -80,11 +90,13 @@ import com.rtbishop.look4sat.core.presentation.RadarDestination
|
|||||||
import com.rtbishop.look4sat.core.presentation.Screen
|
import com.rtbishop.look4sat.core.presentation.Screen
|
||||||
import com.rtbishop.look4sat.core.presentation.hasEnoughHeight
|
import com.rtbishop.look4sat.core.presentation.hasEnoughHeight
|
||||||
import com.rtbishop.look4sat.core.presentation.hasEnoughWidth
|
import com.rtbishop.look4sat.core.presentation.hasEnoughWidth
|
||||||
|
import com.rtbishop.look4sat.feature.cw.CwDecodeScreen
|
||||||
import com.rtbishop.look4sat.feature.map.MapDestination
|
import com.rtbishop.look4sat.feature.map.MapDestination
|
||||||
import com.rtbishop.look4sat.feature.mutual.MutualScreen
|
import com.rtbishop.look4sat.feature.mutual.MutualScreen
|
||||||
import com.rtbishop.look4sat.feature.mutual.MutualViewModel
|
import com.rtbishop.look4sat.feature.mutual.MutualViewModel
|
||||||
import com.rtbishop.look4sat.feature.passes.PassesDestination
|
import com.rtbishop.look4sat.feature.passes.PassesDestination
|
||||||
import com.rtbishop.look4sat.feature.radar.RadarDestination
|
import com.rtbishop.look4sat.feature.radar.RadarDestination
|
||||||
|
import com.rtbishop.look4sat.feature.roaming.RoamingScreen
|
||||||
import com.rtbishop.look4sat.feature.satellites.SatellitesDestination
|
import com.rtbishop.look4sat.feature.satellites.SatellitesDestination
|
||||||
import com.rtbishop.look4sat.feature.settings.SettingsDestination
|
import com.rtbishop.look4sat.feature.settings.SettingsDestination
|
||||||
|
|
||||||
@@ -130,12 +142,32 @@ fun MainScreen(navigateToRadar: () -> Unit = {}) {
|
|||||||
val currentKey = backStack.lastOrNull()
|
val currentKey = backStack.lastOrNull()
|
||||||
val navigateBack: () -> Unit = { backStack.removeLastOrNull() }
|
val navigateBack: () -> Unit = { backStack.removeLastOrNull() }
|
||||||
val fadeTransition = fadeIn(animationSpec = tween(350)) togetherWith fadeOut(animationSpec = tween(350))
|
val fadeTransition = fadeIn(animationSpec = tween(350)) togetherWith fadeOut(animationSpec = tween(350))
|
||||||
val navItems = listOf(Screen.Satellites, Screen.Passes, Screen.Radar, Screen.Mutual, Screen.Map, Screen.Settings)
|
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val container = (context.applicationContext as IContainerProvider).getMainContainer()
|
val container = (context.applicationContext as IContainerProvider).getMainContainer()
|
||||||
val trackingState by container.radioTrackingService.state.collectAsStateWithLifecycle()
|
val trackingState by container.radioTrackingService.state.collectAsStateWithLifecycle()
|
||||||
val otherSettings by container.settingsRepo.otherSettings.collectAsStateWithLifecycle()
|
val otherSettings by container.settingsRepo.otherSettings.collectAsStateWithLifecycle()
|
||||||
|
// UI 设置: 按 screenOrder 排序(空 = 默认顺序), 再按 hiddenScreens 过滤(设置页固定保留)
|
||||||
|
val allNavItems = listOf(Screen.Satellites, Screen.Passes, Screen.Radar, Screen.Mutual, Screen.Roaming, Screen.CwDecode, Screen.Map, Screen.Settings)
|
||||||
|
.sortedBy { screen ->
|
||||||
|
// 未知(新页面如 CwDecode 不在旧持久化顺序里): 用默认顺序位置(漫游↔地图), 再兜底最后
|
||||||
|
val idx = otherSettings.screenOrder.indexOf(screen.screenId)
|
||||||
|
if (idx != -1) idx
|
||||||
|
else com.rtbishop.look4sat.core.presentation.defaultScreenOrder.indexOf(screen.screenId).let {
|
||||||
|
if (it != -1) it else Int.MAX_VALUE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filter { it.screenId !in otherSettings.hiddenScreens || it is Screen.Settings }
|
||||||
|
// 4.5.1 折叠菜单: 主菜单(底部栏 5 槽) + 更多菜单(溢出页面)
|
||||||
|
val subOrder = otherSettings.subMenuOrder.ifEmpty { com.rtbishop.look4sat.core.presentation.defaultSubMenuOrder }
|
||||||
|
val mainNavItems = remember(allNavItems, subOrder) {
|
||||||
|
allNavItems.filter { it.screenId !in subOrder }.take(5)
|
||||||
|
}
|
||||||
|
val moreNavItems = remember(allNavItems, subOrder) {
|
||||||
|
subOrder.mapNotNull { id -> allNavItems.find { it.screenId == id } }
|
||||||
|
}
|
||||||
|
var moreExpanded by remember { mutableStateOf(false) }
|
||||||
|
// 更多菜单打开时拦截返回: 先关菜单
|
||||||
|
BackHandler(enabled = moreExpanded) { moreExpanded = false }
|
||||||
// Activity-scoped so the mutual query results survive navigation to Radar and back
|
// Activity-scoped so the mutual query results survive navigation to Radar and back
|
||||||
val mutualViewModel: MutualViewModel = viewModel(
|
val mutualViewModel: MutualViewModel = viewModel(
|
||||||
viewModelStoreOwner = context as ViewModelStoreOwner,
|
viewModelStoreOwner = context as ViewModelStoreOwner,
|
||||||
@@ -150,12 +182,13 @@ fun MainScreen(navigateToRadar: () -> Unit = {}) {
|
|||||||
) {
|
) {
|
||||||
NavigationSuiteScaffold(
|
NavigationSuiteScaffold(
|
||||||
navigationSuiteItems = {
|
navigationSuiteItems = {
|
||||||
navItems.forEach { screen ->
|
mainNavItems.forEach { screen ->
|
||||||
val isSelected = when (currentKey) {
|
val isSelected = when (currentKey) {
|
||||||
is Screen.Satellites -> screen is Screen.Satellites
|
is Screen.Satellites -> screen is Screen.Satellites
|
||||||
is Screen.Passes -> screen is Screen.Passes
|
is Screen.Passes -> screen is Screen.Passes
|
||||||
is Screen.Radar -> screen is Screen.Radar
|
is Screen.Radar -> screen is Screen.Radar
|
||||||
is Screen.Mutual -> screen is Screen.Mutual
|
is Screen.Mutual -> screen is Screen.Mutual
|
||||||
|
is Screen.CwDecode -> screen is Screen.CwDecode
|
||||||
is Screen.Map -> screen is Screen.Map
|
is Screen.Map -> screen is Screen.Map
|
||||||
is Screen.Settings -> screen is Screen.Settings
|
is Screen.Settings -> screen is Screen.Settings
|
||||||
else -> false
|
else -> false
|
||||||
@@ -166,11 +199,26 @@ fun MainScreen(navigateToRadar: () -> Unit = {}) {
|
|||||||
selected = isSelected,
|
selected = isSelected,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (isSelected) return@item
|
if (isSelected) return@item
|
||||||
|
moreExpanded = false
|
||||||
while (backStack.size > 1) backStack.removeAt(backStack.size - 1)
|
while (backStack.size > 1) backStack.removeAt(backStack.size - 1)
|
||||||
if (screen !is Screen.Passes) backStack.add(screen)
|
if (screen !is Screen.Passes) backStack.add(screen)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
// 更多菜单按钮(固定第 6 槽, 子菜单非空才显示)
|
||||||
|
if (moreNavItems.isNotEmpty()) {
|
||||||
|
item(
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
painterResource(com.rtbishop.look4sat.R.drawable.ic_more),
|
||||||
|
stringResource(com.rtbishop.look4sat.core.presentation.R.string.nav_more)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = { Text(stringResource(com.rtbishop.look4sat.core.presentation.R.string.nav_more)) },
|
||||||
|
selected = moreExpanded,
|
||||||
|
onClick = { moreExpanded = !moreExpanded }
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
navigationSuiteColors = NavigationSuiteDefaults.colors(
|
navigationSuiteColors = NavigationSuiteDefaults.colors(
|
||||||
navigationRailContainerColor = MaterialTheme.colorScheme.surfaceContainer
|
navigationRailContainerColor = MaterialTheme.colorScheme.surfaceContainer
|
||||||
@@ -181,103 +229,131 @@ fun MainScreen(navigateToRadar: () -> Unit = {}) {
|
|||||||
else -> NavigationSuiteType.ShortNavigationBarMedium
|
else -> NavigationSuiteType.ShortNavigationBarMedium
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Column {
|
Box {
|
||||||
NavDisplay(
|
Column(modifier = Modifier.fillMaxSize()) {
|
||||||
backStack = backStack,
|
NavDisplay(
|
||||||
modifier = Modifier.weight(1f),
|
backStack = backStack,
|
||||||
onBack = navigateBack,
|
modifier = Modifier.weight(1f).fillMaxWidth(),
|
||||||
transitionSpec = { fadeTransition },
|
onBack = navigateBack,
|
||||||
popTransitionSpec = { fadeTransition },
|
transitionSpec = { fadeTransition },
|
||||||
predictivePopTransitionSpec = { fadeTransition },
|
popTransitionSpec = { fadeTransition },
|
||||||
entryDecorators = listOf(
|
predictivePopTransitionSpec = { fadeTransition },
|
||||||
// Required for saving Compose state per entry
|
entryDecorators = listOf(
|
||||||
rememberSaveableStateHolderNavEntryDecorator(),
|
// Required for saving Compose state per entry
|
||||||
// Required for ViewModel scoping per entry
|
rememberSaveableStateHolderNavEntryDecorator(),
|
||||||
rememberViewModelStoreNavEntryDecorator()
|
// Required for ViewModel scoping per entry
|
||||||
),
|
rememberViewModelStoreNavEntryDecorator()
|
||||||
entryProvider = entryProvider {
|
),
|
||||||
entry<Screen.Satellites> {
|
entryProvider = entryProvider {
|
||||||
SatellitesDestination(navigateUp = navigateBack)
|
entry<Screen.Satellites> {
|
||||||
}
|
SatellitesDestination(navigateUp = navigateBack)
|
||||||
entry<Screen.Passes> {
|
|
||||||
PassesDestination { catNum, aosTime ->
|
|
||||||
container.setMutualPassData(MutualPassData())
|
|
||||||
container.satelliteRepo.selectPass(catNum, aosTime)
|
|
||||||
backStack.add(Screen.Radar)
|
|
||||||
// navigateToRadar()
|
|
||||||
}
|
}
|
||||||
}
|
entry<Screen.Passes> {
|
||||||
entry<Screen.Radar> {
|
PassesDestination { catNum, aosTime ->
|
||||||
RadarDestination(navigateUp = navigateBack)
|
container.setMutualPassData(MutualPassData())
|
||||||
}
|
|
||||||
entry<Screen.Map> {
|
|
||||||
MapDestination()
|
|
||||||
}
|
|
||||||
entry<Screen.Mutual> {
|
|
||||||
MutualScreen(
|
|
||||||
viewModel = mutualViewModel,
|
|
||||||
navigateUp = navigateBack,
|
|
||||||
navigateToRadar = { catNum, aosTime, pass ->
|
|
||||||
container.setMutualPassData(pass ?: MutualPassData())
|
|
||||||
container.satelliteRepo.selectPass(catNum, aosTime)
|
container.satelliteRepo.selectPass(catNum, aosTime)
|
||||||
backStack.add(Screen.Radar)
|
backStack.add(Screen.Radar)
|
||||||
}
|
// navigateToRadar()
|
||||||
)
|
|
||||||
}
|
|
||||||
entry<Screen.Settings> {
|
|
||||||
SettingsDestination()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// Radio tracking status banner
|
|
||||||
if (trackingState.isActive) {
|
|
||||||
val infiniteTransition = rememberInfiniteTransition(label = "trackingPulse")
|
|
||||||
val alpha by infiniteTransition.animateFloat(
|
|
||||||
initialValue = 1f, targetValue = 0.4f,
|
|
||||||
animationSpec = infiniteRepeatable(
|
|
||||||
animation = tween(1000, easing = LinearEasing),
|
|
||||||
repeatMode = RepeatMode.Reverse
|
|
||||||
), label = "pulseAlpha"
|
|
||||||
)
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.background(MaterialTheme.colorScheme.primaryContainer)
|
|
||||||
.clickable {
|
|
||||||
val pass = trackingState.currentPass
|
|
||||||
if (pass != null) {
|
|
||||||
container.setMutualPassData(MutualPassData())
|
|
||||||
container.satelliteRepo.selectPass(pass.catNum, pass.aosTime)
|
|
||||||
backStack.add(Screen.Radar)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(horizontal = 12.dp, vertical = 6.dp)
|
entry<Screen.Radar> {
|
||||||
) {
|
RadarDestination(navigateUp = navigateBack)
|
||||||
Box(
|
}
|
||||||
|
entry<Screen.Map> {
|
||||||
|
MapDestination()
|
||||||
|
}
|
||||||
|
entry<Screen.Mutual> {
|
||||||
|
MutualScreen(
|
||||||
|
viewModel = mutualViewModel,
|
||||||
|
navigateUp = navigateBack,
|
||||||
|
navigateToRadar = { catNum, aosTime, pass ->
|
||||||
|
container.setMutualPassData(pass ?: MutualPassData())
|
||||||
|
container.satelliteRepo.selectPass(catNum, aosTime)
|
||||||
|
backStack.add(Screen.Radar)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
entry<Screen.Roaming> {
|
||||||
|
RoamingScreen()
|
||||||
|
}
|
||||||
|
entry<Screen.CwDecode> {
|
||||||
|
CwDecodeScreen()
|
||||||
|
}
|
||||||
|
entry<Screen.Settings> {
|
||||||
|
SettingsDestination()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// Radio tracking status banner
|
||||||
|
if (trackingState.isActive) {
|
||||||
|
val infiniteTransition = rememberInfiniteTransition(label = "trackingPulse")
|
||||||
|
val alpha by infiniteTransition.animateFloat(
|
||||||
|
initialValue = 1f, targetValue = 0.4f,
|
||||||
|
animationSpec = infiniteRepeatable(
|
||||||
|
animation = tween(1000, easing = LinearEasing),
|
||||||
|
repeatMode = RepeatMode.Reverse
|
||||||
|
), label = "pulseAlpha"
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(8.dp)
|
.fillMaxWidth()
|
||||||
.clip(CircleShape)
|
.background(MaterialTheme.colorScheme.primaryContainer)
|
||||||
.background(Color(0xFF4CAF50).copy(alpha = alpha))
|
.clickable {
|
||||||
)
|
val pass = trackingState.currentPass
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
if (pass != null) {
|
||||||
Text(
|
container.setMutualPassData(MutualPassData())
|
||||||
text = "Tracking: ${trackingState.currentPass?.name ?: ""}",
|
container.satelliteRepo.selectPass(pass.catNum, pass.aosTime)
|
||||||
fontSize = 13.sp,
|
backStack.add(Screen.Radar)
|
||||||
fontWeight = FontWeight.Medium,
|
}
|
||||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
}
|
||||||
modifier = Modifier.weight(1f)
|
.padding(horizontal = 12.dp, vertical = 6.dp)
|
||||||
)
|
) {
|
||||||
val txOk = if (trackingState.txConnected) "TX" else ""
|
Box(
|
||||||
val rxOk = if (trackingState.rxConnected) "RX" else ""
|
modifier = Modifier
|
||||||
Text(
|
.size(8.dp)
|
||||||
text = listOf(txOk, rxOk).filter { it.isNotBlank() }.joinToString("/"),
|
.clip(CircleShape)
|
||||||
fontSize = 12.sp,
|
.background(Color(0xFF4CAF50).copy(alpha = alpha))
|
||||||
color = MaterialTheme.colorScheme.onPrimaryContainer
|
)
|
||||||
)
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(com.rtbishop.look4sat.core.presentation.R.string.tracking_status, trackingState.currentPass?.name ?: ""),
|
||||||
|
fontSize = 13.sp,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
val txOk = if (trackingState.txConnected) "TX" else ""
|
||||||
|
val rxOk = if (trackingState.rxConnected) "RX" else ""
|
||||||
|
Text(
|
||||||
|
text = listOf(txOk, rxOk).filter { it.isNotBlank() }.joinToString("/"),
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 更多菜单弹出面板(覆盖在内容上, 底部栏上方; spring 弹跳)
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = moreExpanded,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
enter = expandVertically(
|
||||||
|
animationSpec = spring(dampingRatio = Spring.DampingRatioMediumBouncy)
|
||||||
|
) + fadeIn(),
|
||||||
|
exit = shrinkVertically() + fadeOut()
|
||||||
|
) {
|
||||||
|
MoreMenuPopup(
|
||||||
|
items = moreNavItems,
|
||||||
|
currentKey = currentKey,
|
||||||
|
onDismiss = { moreExpanded = false },
|
||||||
|
onSelect = { screen ->
|
||||||
|
moreExpanded = false
|
||||||
|
while (backStack.size > 1) backStack.removeAt(backStack.size - 1)
|
||||||
|
if (screen !is Screen.Passes) backStack.add(screen)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* MoreMenuPopup.kt — 底部导航「更多」二级菜单弹出面板(4.5.1)。
|
||||||
|
*
|
||||||
|
* 覆盖在内容区上(底部栏上方, 右对齐), 竖排菜单项(图标+文本+箭头),
|
||||||
|
* 当前页高亮; 点击遮罩关闭, 点击项跳转。弹出/收起动画由调用处
|
||||||
|
* (MainScreen 的 AnimatedVisibility + spring)驱动。
|
||||||
|
*/
|
||||||
|
package com.rtbishop.look4sat
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.navigation3.runtime.NavKey
|
||||||
|
import com.rtbishop.look4sat.core.presentation.Screen
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MoreMenuPopup(
|
||||||
|
items: List<Screen>,
|
||||||
|
currentKey: NavKey?,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onSelect: (Screen) -> Unit
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.35f))
|
||||||
|
.clickable(onClick = onDismiss)
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.BottomEnd)
|
||||||
|
.padding(12.dp),
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.padding(vertical = 4.dp)) {
|
||||||
|
items.forEach { screen ->
|
||||||
|
val isSelected = when (currentKey) {
|
||||||
|
is Screen.Satellites -> screen is Screen.Satellites
|
||||||
|
is Screen.Passes -> screen is Screen.Passes
|
||||||
|
is Screen.Radar -> screen is Screen.Radar
|
||||||
|
is Screen.Mutual -> screen is Screen.Mutual
|
||||||
|
is Screen.CwDecode -> screen is Screen.CwDecode
|
||||||
|
is Screen.Map -> screen is Screen.Map
|
||||||
|
is Screen.Settings -> screen is Screen.Settings
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { onSelect(screen) }
|
||||||
|
.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(screen.iconResId),
|
||||||
|
contentDescription = stringResource(screen.titleResId),
|
||||||
|
tint = if (isSelected) MaterialTheme.colorScheme.primary
|
||||||
|
else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(screen.titleResId),
|
||||||
|
fontSize = 14.sp,
|
||||||
|
fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Normal,
|
||||||
|
color = if (isSelected) MaterialTheme.colorScheme.primary
|
||||||
|
else MaterialTheme.colorScheme.onSurface,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "›",
|
||||||
|
fontSize = 16.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
|
||||||
|
</vector>
|
||||||
+2
@@ -31,10 +31,12 @@ internal class ApplicationPlugin : Plugin<Project> {
|
|||||||
implementation(project(":core:data"))
|
implementation(project(":core:data"))
|
||||||
implementation(project(":core:domain"))
|
implementation(project(":core:domain"))
|
||||||
implementation(project(":core:presentation"))
|
implementation(project(":core:presentation"))
|
||||||
|
implementation(project(":feature:cw"))
|
||||||
implementation(project(":feature:map"))
|
implementation(project(":feature:map"))
|
||||||
implementation(project(":feature:mutual"))
|
implementation(project(":feature:mutual"))
|
||||||
implementation(project(":feature:passes"))
|
implementation(project(":feature:passes"))
|
||||||
implementation(project(":feature:radar"))
|
implementation(project(":feature:radar"))
|
||||||
|
implementation(project(":feature:roaming"))
|
||||||
implementation(project(":feature:satellites"))
|
implementation(project(":feature:satellites"))
|
||||||
implementation(project(":feature:settings"))
|
implementation(project(":feature:settings"))
|
||||||
implementation(libs.androidx.core.splashscreen)
|
implementation(libs.androidx.core.splashscreen)
|
||||||
|
|||||||
@@ -69,11 +69,14 @@ class DatabaseRepo(
|
|||||||
val dataSourcesSettings = settingsRepo.dataSourcesSettings.value
|
val dataSourcesSettings = settingsRepo.dataSourcesSettings.value
|
||||||
val tleUrls = buildMap {
|
val tleUrls = buildMap {
|
||||||
putAll(Sources.satelliteDataUrls)
|
putAll(Sources.satelliteDataUrls)
|
||||||
if (dataSourcesSettings.useCustomTLE) put(customSourceType, dataSourcesSettings.tleUrl)
|
// 开关开且 URL 非空 -> All 用自定义 URL;否则用默认 URL(在线更新默认源)
|
||||||
|
put("All", if (dataSourcesSettings.useCustomTLE && dataSourcesSettings.tleUrl.isNotBlank())
|
||||||
|
dataSourcesSettings.tleUrl else Sources.defaultTleUrl)
|
||||||
}.filterValues { it.isNotBlank() }
|
}.filterValues { it.isNotBlank() }
|
||||||
val radioUrls = buildMap {
|
val radioUrls = buildMap {
|
||||||
putAll(Sources.transceiversDataUrls)
|
putAll(Sources.transceiversDataUrls)
|
||||||
if (dataSourcesSettings.useCustomTransceivers) put(customSourceType, dataSourcesSettings.transceiversUrl)
|
put("SatNOGS", if (dataSourcesSettings.useCustomTransceivers && dataSourcesSettings.transceiversUrl.isNotBlank())
|
||||||
|
dataSourcesSettings.transceiversUrl else Sources.defaultTransceiversUrl)
|
||||||
}.filterValues { it.isNotBlank() }
|
}.filterValues { it.isNotBlank() }
|
||||||
// launch all network requests concurrently
|
// launch all network requests concurrently
|
||||||
val tleJobs = tleUrls.values.map { url -> async { url to remoteSource.getNetworkStream(url) } }
|
val tleJobs = tleUrls.values.map { url -> async { url to remoteSource.getNetworkStream(url) } }
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import com.rtbishop.look4sat.core.domain.model.OtherSettings
|
|||||||
import com.rtbishop.look4sat.core.domain.model.PassesSettings
|
import com.rtbishop.look4sat.core.domain.model.PassesSettings
|
||||||
import com.rtbishop.look4sat.core.domain.model.RCSettings
|
import com.rtbishop.look4sat.core.domain.model.RCSettings
|
||||||
import com.rtbishop.look4sat.core.domain.model.RadioControlSettings
|
import com.rtbishop.look4sat.core.domain.model.RadioControlSettings
|
||||||
|
import com.rtbishop.look4sat.core.domain.source.Sources
|
||||||
import com.rtbishop.look4sat.core.domain.predict.GeoPos
|
import com.rtbishop.look4sat.core.domain.predict.GeoPos
|
||||||
import com.rtbishop.look4sat.core.domain.repository.ISettingsRepo
|
import com.rtbishop.look4sat.core.domain.repository.ISettingsRepo
|
||||||
import com.rtbishop.look4sat.core.domain.utility.positionToQth
|
import com.rtbishop.look4sat.core.domain.utility.positionToQth
|
||||||
@@ -76,6 +77,8 @@ class SettingsRepo(
|
|||||||
private val keyStateOfUtc = "stateOfUtc"
|
private val keyStateOfUtc = "stateOfUtc"
|
||||||
private val keyStateOfLightTheme = "stateOfLightTheme"
|
private val keyStateOfLightTheme = "stateOfLightTheme"
|
||||||
private val keyStateOfNightMode = "stateOfNightMode"
|
private val keyStateOfNightMode = "stateOfNightMode"
|
||||||
|
private val keyHiddenScreens = "hiddenScreens"
|
||||||
|
private val keyScreenOrder = "screenOrder"
|
||||||
private val keyStationAltitude = "stationAltitude"
|
private val keyStationAltitude = "stationAltitude"
|
||||||
private val keyStationLatitude = "stationLatitude"
|
private val keyStationLatitude = "stationLatitude"
|
||||||
private val keyStationLongitude = "stationLongitude"
|
private val keyStationLongitude = "stationLongitude"
|
||||||
@@ -213,8 +216,8 @@ class SettingsRepo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setStationPosition(latitude: Double, longitude: Double, altitude: Double, locator: String) {
|
private fun setStationPosition(latitude: Double, longitude: Double, altitude: Double, locator: String) {
|
||||||
val newLat = latitude.round(4)
|
val newLat = latitude.round(5)
|
||||||
val newLon = longitude.round(4)
|
val newLon = longitude.round(5)
|
||||||
val newAlt = altitude.round(1)
|
val newAlt = altitude.round(1)
|
||||||
val timestamp = System.currentTimeMillis()
|
val timestamp = System.currentTimeMillis()
|
||||||
println("Received new Position($newLat, $newLon, $newAlt) & Locator $locator")
|
println("Received new Position($newLat, $newLon, $newAlt) & Locator $locator")
|
||||||
@@ -356,6 +359,8 @@ class SettingsRepo(
|
|||||||
putString(keySstvMode, new.sstvMode)
|
putString(keySstvMode, new.sstvMode)
|
||||||
putLong(keyLowElevation, new.lowElevation.toRawBits())
|
putLong(keyLowElevation, new.lowElevation.toRawBits())
|
||||||
putLong(keyHighElevation, new.highElevation.toRawBits())
|
putLong(keyHighElevation, new.highElevation.toRawBits())
|
||||||
|
putStringSet(keyHiddenScreens, new.hiddenScreens.toSet())
|
||||||
|
putString(keyScreenOrder, new.screenOrder.joinToString(","))
|
||||||
}
|
}
|
||||||
new
|
new
|
||||||
}
|
}
|
||||||
@@ -371,6 +376,8 @@ class SettingsRepo(
|
|||||||
shouldSeeWarning = preferences.getBoolean(keyShouldSeeWarning, true),
|
shouldSeeWarning = preferences.getBoolean(keyShouldSeeWarning, true),
|
||||||
shouldSeeWhatsNew = preferences.getBoolean(keyShouldSeeWhatsNew, true),
|
shouldSeeWhatsNew = preferences.getBoolean(keyShouldSeeWhatsNew, true),
|
||||||
sstvMode = preferences.getString(keySstvMode, null) ?: "Auto",
|
sstvMode = preferences.getString(keySstvMode, null) ?: "Auto",
|
||||||
|
hiddenScreens = preferences.getStringSet(keyHiddenScreens, emptySet())?.toList() ?: emptyList(),
|
||||||
|
screenOrder = preferences.getString(keyScreenOrder, null)?.split(",")?.filter { it.isNotBlank() } ?: emptyList(),
|
||||||
lowElevation = Double.fromBits(preferences.getLong(keyLowElevation, 15.0.toRawBits())),
|
lowElevation = Double.fromBits(preferences.getLong(keyLowElevation, 15.0.toRawBits())),
|
||||||
highElevation = Double.fromBits(preferences.getLong(keyHighElevation, 45.0.toRawBits()))
|
highElevation = Double.fromBits(preferences.getLong(keyHighElevation, 45.0.toRawBits()))
|
||||||
)
|
)
|
||||||
@@ -390,12 +397,20 @@ class SettingsRepo(
|
|||||||
_dataSourcesSettings.value = settings
|
_dataSourcesSettings.value = settings
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDataSourcesSettings(): DataSourcesSettings = DataSourcesSettings(
|
private fun getDataSourcesSettings(): DataSourcesSettings {
|
||||||
useCustomTLE = preferences.getBoolean(keyUseCustomTle, false),
|
// 4.4.8 修复: 旧版 example.com 占位 URL 视为未配置 -> 替换为真实默认 URL 且开关强制关闭,
|
||||||
useCustomTransceivers = preferences.getBoolean(keyUseCustomTransceivers, false),
|
// 否则在线更新的 All/SatNOGS 源会指向错误地址导致更新失败
|
||||||
tleUrl = preferences.getString(keyTleUrl, "https://example.com/tle.txt") ?: "",
|
val storedTleUrl = preferences.getString(keyTleUrl, Sources.defaultTleUrl) ?: Sources.defaultTleUrl
|
||||||
transceiversUrl = preferences.getString(keyTransceiversUrl, "https://example.com/radio.json") ?: ""
|
val storedTxUrl = preferences.getString(keyTransceiversUrl, Sources.defaultTransceiversUrl) ?: Sources.defaultTransceiversUrl
|
||||||
)
|
val tleUrl = if (storedTleUrl == "https://example.com/tle.txt") Sources.defaultTleUrl else storedTleUrl
|
||||||
|
val txUrl = if (storedTxUrl == "https://example.com/radio.json") Sources.defaultTransceiversUrl else storedTxUrl
|
||||||
|
return DataSourcesSettings(
|
||||||
|
useCustomTLE = preferences.getBoolean(keyUseCustomTle, false) && tleUrl != Sources.defaultTleUrl,
|
||||||
|
useCustomTransceivers = preferences.getBoolean(keyUseCustomTransceivers, false) && txUrl != Sources.defaultTransceiversUrl,
|
||||||
|
tleUrl = tleUrl,
|
||||||
|
transceiversUrl = txUrl
|
||||||
|
)
|
||||||
|
}
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region # Radio control settings
|
//region # Radio control settings
|
||||||
|
|||||||
+2
-1
@@ -102,7 +102,8 @@ class DatabaseRepoTest {
|
|||||||
repository.updateFromRemote()
|
repository.updateFromRemote()
|
||||||
|
|
||||||
assertTrue(localSource.insertedEntries.any { it.catnum == 25544 })
|
assertTrue(localSource.insertedEntries.any { it.catnum == 25544 })
|
||||||
assertEquals(listOf(25544), settingsRepo.satelliteTypeIdsByType["Other"])
|
// 新语义: 开关开 + URL 非空 -> All 源用自定义 URL, 数据归入 All 类型
|
||||||
|
assertEquals(listOf(25544), settingsRepo.satelliteTypeIdsByType["All"])
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validCsvStream(): InputStream = """
|
private fun validCsvStream(): InputStream = """
|
||||||
|
|||||||
@@ -62,7 +62,13 @@ data class OtherSettings(
|
|||||||
val shouldSeeWhatsNew: Boolean,
|
val shouldSeeWhatsNew: Boolean,
|
||||||
val sstvMode: String = "Auto",
|
val sstvMode: String = "Auto",
|
||||||
val lowElevation: Double = 15.0,
|
val lowElevation: Double = 15.0,
|
||||||
val highElevation: Double = 45.0
|
val highElevation: Double = 45.0,
|
||||||
|
// UI 设置: 底部导航栏隐藏的页面(Screen simpleName 列表, 默认空 = 全部显示)
|
||||||
|
val hiddenScreens: List<String> = emptyList(),
|
||||||
|
// UI 设置: 页面顺序(空 = 默认顺序: 卫星/过境/雷达/地图/设置)
|
||||||
|
val screenOrder: List<String> = emptyList(),
|
||||||
|
// UI 设置: 更多菜单顺序(空 = 默认: 匹配/漫游/CW解码)
|
||||||
|
val subMenuOrder: List<String> = emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DataSourcesSettings(
|
data class DataSourcesSettings(
|
||||||
|
|||||||
@@ -18,6 +18,10 @@
|
|||||||
package com.rtbishop.look4sat.core.domain.source
|
package com.rtbishop.look4sat.core.domain.source
|
||||||
|
|
||||||
object Sources {
|
object Sources {
|
||||||
|
// 在线更新的默认 URL(用户可通过"自定义URL"对话框修改/重置)
|
||||||
|
val defaultTleUrl = "https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=csv"
|
||||||
|
val defaultTransceiversUrl = "https://db.satnogs.org/api/transmitters/?format=json&status=active"
|
||||||
|
|
||||||
val satelliteDataUrls = mapOf(
|
val satelliteDataUrls = mapOf(
|
||||||
"All" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=csv",
|
"All" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=csv",
|
||||||
"Amateur" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=amateur&FORMAT=csv",
|
"Amateur" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=amateur&FORMAT=csv",
|
||||||
|
|||||||
+45
-1
@@ -63,7 +63,7 @@ fun qthToPosition(locator: String): GeoPos? {
|
|||||||
latitude += 1.0 / 48.0
|
latitude += 1.0 / 48.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return GeoPos(latitude.round(4), longitude.round(4))
|
return GeoPos(latitude.round(6), longitude.round(6))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,3 +100,47 @@ private fun isValidPosition(lat: Double, lon: Double): Boolean {
|
|||||||
private fun isValidLocator(locator: String): Boolean {
|
private fun isValidLocator(locator: String): Boolean {
|
||||||
return locator.matches("[a-xA-X]{2}\\d{2}[a-xA-X]{2}(?:\\d{2}(?:[a-xA-X]{2})?)?".toRegex())
|
return locator.matches("[a-xA-X]{2}\\d{2}[a-xA-X]{2}(?:\\d{2}(?:[a-xA-X]{2})?)?".toRegex())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the 4-char field+square part of a locator, e.g. "OL42ih45" -> "OL42".
|
||||||
|
* A 4-char input is returned as-is when valid.
|
||||||
|
*/
|
||||||
|
fun qthToSquare(locator: String): String {
|
||||||
|
val upper = locator.trim().uppercase()
|
||||||
|
return when {
|
||||||
|
upper.length >= 4 && isValidLocator(upper) -> upper.take(4)
|
||||||
|
upper.length == 4 && upper.matches("[a-xA-X]{2}\\d{2}".toRegex()) -> upper
|
||||||
|
else -> "----"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the 3x3 grid of 4-char squares surrounding [square] (e.g. "OL42").
|
||||||
|
* Row 0 = north (lat +1), col 0 = west (lon -1). Handles field/square carry
|
||||||
|
* at boundaries (e.g. "AA00" wraps to "RR99" at the south-west corner).
|
||||||
|
* Mirrors the neighbor logic decompiled from the QTH定位器 app.
|
||||||
|
*/
|
||||||
|
fun qthNeighbors(square: String): List<String> {
|
||||||
|
if (square.length != 4) return emptyList()
|
||||||
|
val lonField = (square[0].uppercaseChar().code - 65).coerceIn(0, 17)
|
||||||
|
val latField = (square[1].uppercaseChar().code - 65).coerceIn(0, 17)
|
||||||
|
val lonSquare = square[2].digitToCharOrNull() ?: return emptyList()
|
||||||
|
val latSquare = square[3].digitToCharOrNull() ?: return emptyList()
|
||||||
|
val result = mutableListOf<String>()
|
||||||
|
for (dLat in 1 downTo -1) { // north -> south
|
||||||
|
for (dLon in -1..1) { // west -> east
|
||||||
|
var lf = lonField
|
||||||
|
var tf = latField
|
||||||
|
var ls = lonSquare + dLon
|
||||||
|
var ts = latSquare + dLat
|
||||||
|
if (ls < 0) { lf -= 1; ls = 9 } else if (ls > 9) { lf += 1; ls = 0 }
|
||||||
|
if (ts < 0) { tf -= 1; ts = 9 } else if (ts > 9) { tf += 1; ts = 0 }
|
||||||
|
lf = (lf + 18) % 18
|
||||||
|
tf = (tf + 18) % 18
|
||||||
|
result += "${('A' + lf).toChar()}${('A' + tf).toChar()}$ls$ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Char.digitToCharOrNull(): Int? = digitToIntOrNull()
|
||||||
@@ -18,7 +18,9 @@
|
|||||||
package com.rtbishop.look4sat.core.domain
|
package com.rtbishop.look4sat.core.domain
|
||||||
|
|
||||||
import com.rtbishop.look4sat.core.domain.utility.positionToQth
|
import com.rtbishop.look4sat.core.domain.utility.positionToQth
|
||||||
|
import com.rtbishop.look4sat.core.domain.utility.qthNeighbors
|
||||||
import com.rtbishop.look4sat.core.domain.utility.qthToPosition
|
import com.rtbishop.look4sat.core.domain.utility.qthToPosition
|
||||||
|
import com.rtbishop.look4sat.core.domain.utility.qthToSquare
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class QthConverterTest {
|
class QthConverterTest {
|
||||||
@@ -26,14 +28,14 @@ class QthConverterTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `Given valid QTH returns correct POS`() {
|
fun `Given valid QTH returns correct POS`() {
|
||||||
var result = qthToPosition("io91VL39FX")
|
var result = qthToPosition("io91VL39FX")
|
||||||
assert(result?.latitude == 51.4999 && result.longitude == -0.2231)
|
assert(result?.latitude == 51.499913 && result.longitude == -0.22309)
|
||||||
result = qthToPosition("gf15vc")
|
result = qthToPosition("gf15vc")
|
||||||
assert(result?.latitude == -34.8958 && result.longitude == -56.2083)
|
assert(result?.latitude == -34.895833 && result.longitude == -56.208333)
|
||||||
// 8-char locators: finer 30" x 15" cell center
|
// 8-char locators: finer 30" x 15" cell center
|
||||||
result = qthToPosition("io91vl47")
|
result = qthToPosition("io91vl47")
|
||||||
assert(result?.latitude == 51.4896 && result.longitude == -0.2125)
|
assert(result?.latitude == 51.489583 && result.longitude == -0.2125)
|
||||||
result = qthToPosition("jn58td25")
|
result = qthToPosition("jn58td25")
|
||||||
assert(result?.latitude == 48.1479 && result.longitude == 11.6042)
|
assert(result?.latitude == 48.147917 && result.longitude == 11.604167)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -82,4 +84,42 @@ class QthConverterTest {
|
|||||||
assert(qth == qth2) { "Roundtrip failed for ($lat, $lon): $qth -> $qth2" }
|
assert(qth == qth2) { "Roundtrip failed for ($lat, $lon): $qth -> $qth2" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Given square returns correct 3x3 neighbors`() {
|
||||||
|
// Reference grid from the QTH定位器 screenshot: OL42
|
||||||
|
val neighbors = qthNeighbors("OL42")
|
||||||
|
assert(neighbors == listOf(
|
||||||
|
"OL33", "OL43", "OL53",
|
||||||
|
"OL32", "OL42", "OL52",
|
||||||
|
"OL31", "OL41", "OL51"
|
||||||
|
)) { "OL42 grid mismatch: $neighbors" }
|
||||||
|
// Center cell must be the input itself
|
||||||
|
assert(neighbors[4] == "OL42")
|
||||||
|
// 9 cells, all distinct
|
||||||
|
assert(neighbors.size == 9 && neighbors.toSet().size == 9)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Given boundary square wraps fields correctly`() {
|
||||||
|
// South-west corner: AA00 neighbors wrap to RR99 / RA90 etc.
|
||||||
|
val sw = qthNeighbors("AA00")
|
||||||
|
assert(sw.size == 9 && sw.toSet().size == 9)
|
||||||
|
assert(sw[0] == "RA91" && sw[4] == "AA00" && sw[6] == "RR99" && sw[8] == "AR19")
|
||||||
|
// North-east corner: RR99 wraps to AA00
|
||||||
|
val ne = qthNeighbors("RR99")
|
||||||
|
assert(ne.size == 9 && ne.toSet().size == 9)
|
||||||
|
assert(ne[0] == "RA80" && ne[4] == "RR99" && ne[8] == "AR08")
|
||||||
|
// Field boundary: IO91's east neighbors cross into J field
|
||||||
|
val london = qthNeighbors("IO91")
|
||||||
|
assert(london[2] == "JO02" && london[5] == "JO01")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Given full locator returns square part`() {
|
||||||
|
assert(qthToSquare("OL42ih45") == "OL42")
|
||||||
|
assert(qthToSquare("io91VL39FX") == "IO91")
|
||||||
|
assert(qthToSquare("JN58") == "JN58")
|
||||||
|
assert(qthToSquare("garbage!!") == "----")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+20
-8
@@ -21,27 +21,39 @@ import androidx.navigation3.runtime.NavKey
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
sealed class Screen(val iconResId: Int, val titleResId: Int) : NavKey {
|
sealed class Screen(val iconResId: Int, val titleResId: Int, val screenId: String) : NavKey {
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Satellites : Screen(R.drawable.ic_satellites, R.string.nav_sat)
|
data object Satellites : Screen(R.drawable.ic_satellites, R.string.nav_sat, "Satellites")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Passes : Screen(R.drawable.ic_passes, R.string.nav_pass)
|
data object Passes : Screen(R.drawable.ic_passes, R.string.nav_pass, "Passes")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Radar : Screen(R.drawable.ic_radar, R.string.nav_radar)
|
data object Radar : Screen(R.drawable.ic_radar, R.string.nav_radar, "Radar")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Map : Screen(R.drawable.ic_map, R.string.nav_map)
|
data object Map : Screen(R.drawable.ic_map, R.string.nav_map, "Map")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Mutual : Screen(R.drawable.ic_match, R.string.nav_mutual)
|
data object Mutual : Screen(R.drawable.ic_match, R.string.nav_mutual, "Mutual")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Settings : Screen(R.drawable.ic_settings, R.string.nav_prefs)
|
data object Roaming : Screen(R.drawable.ic_roaming, R.string.nav_roaming, "Roaming")
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object CwDecode : Screen(R.drawable.ic_cw, R.string.nav_cw, "CwDecode")
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object Settings : Screen(R.drawable.ic_settings, R.string.nav_prefs, "Settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UI 设置: 默认主菜单页面顺序(底部栏 5 槽)
|
||||||
|
val defaultScreenOrder = listOf("Satellites", "Passes", "Radar", "Map", "Settings")
|
||||||
|
|
||||||
|
// UI 设置: 默认更多菜单顺序(进「更多」的页面)
|
||||||
|
val defaultSubMenuOrder = listOf("Mutual", "Roaming", "CwDecode")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object RadarDestination : NavKey
|
data object RadarDestination : NavKey
|
||||||
|
|
||||||
@@ -70,4 +82,4 @@ class DeeplinkResolver(private val fallbackDestination: NavKey = Screen.Passes)
|
|||||||
matchers.forEach { it.match(deeplink)?.let { match -> return match } }
|
matchers.forEach { it.match(deeplink)?.let { match -> return match } }
|
||||||
return fallbackDestination
|
return fallbackDestination
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
|
||||||
|
<!-- Morse style icon: a dot above a dash -->
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M 8 4 L 16 4 L 16 8 L 8 8 Z" />
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M 3 16 L 21 16 L 21 20 L 3 20 Z" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,6v3l4,-4 -4,-4v3c-4.42,0 -8,3.58 -8,8 0,1.57 0.46,3.03 1.24,4.26L6.7,14.8c-0.45,-0.83 -0.7,-1.79 -0.7,-2.8 0,-3.31 2.69,-6 6,-6zM18.76,7.74L17.3,9.2c0.44,0.84 0.7,1.79 0.7,2.8 0,3.31 -2.69,6 -6,6v-3l-4,4 4,4v-3c4.42,0 8,-3.58 8,-8 0,-1.57 -0.46,-3.03 -1.24,-4.26z" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="96"
|
||||||
|
android:viewportHeight="96">
|
||||||
|
|
||||||
|
<!-- Crosshair / grid-locator style icon: concentric rings + center dot -->
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/transparent"
|
||||||
|
android:strokeColor="@android:color/white"
|
||||||
|
android:strokeWidth="6"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:pathData="M 48 12 L 48 84 M 12 48 L 84 48" />
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/transparent"
|
||||||
|
android:strokeColor="@android:color/white"
|
||||||
|
android:strokeWidth="6"
|
||||||
|
android:pathData="M 48 26 A 22 22 0 1 0 48.1 26" />
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M 44 44 L 52 44 L 52 52 L 44 52 Z" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Look4Sat Pro</string>
|
||||||
|
<string name="btn_accept">Terima</string>
|
||||||
|
<string name="btn_cancel">Batal</string>
|
||||||
|
<string name="empty_list_message">Tidak ada data untuk ditampilkan</string>
|
||||||
|
<string name="nav_sat">Satelit</string>
|
||||||
|
<string name="nav_pass">Lintasan</string>
|
||||||
|
<string name="nav_radar">Radar</string>
|
||||||
|
<string name="nav_mutual">Pasangan</string>
|
||||||
|
<string name="nav_roaming">Jelajah</string>
|
||||||
|
<string name="nav_cw">Dekoder CW</string>
|
||||||
|
<string name="nav_map">Peta</string>
|
||||||
|
<string name="nav_prefs">Pengaturan</string>
|
||||||
|
<string name="nav_more">Lainnya</string>
|
||||||
|
<string name="sat_type_hint">Jenis: %s</string>
|
||||||
|
<string name="sat_type_title">Pilih jenis satelit</string>
|
||||||
|
<string name="sat_search_hint">Id - Nama</string>
|
||||||
|
<string name="sat_search_clear">Bersihkan</string>
|
||||||
|
<string name="sat_clear_all">Bersihkan semua</string>
|
||||||
|
<string name="sat_select_all">Pilih semua</string>
|
||||||
|
<string name="sat_empty_list_message">Pastikan kata kunci pencarian Anda benar dan DB sudah diperbarui</string>
|
||||||
|
<string name="sat_warning_title">Peringatan\!</string>
|
||||||
|
<string name="sat_warning_message">
|
||||||
|
Ada lebih dari 9000 satelit yang terdaftar di aplikasi ini.
|
||||||
|
Tidak masuk akal untuk melacak semuanya sekaligus.
|
||||||
|
\n\nSelalu coba persempit daftar hanya ke satelit yang
|
||||||
|
Anda minati melalui pencarian dan pemilih jenis.</string>
|
||||||
|
<string name="pass_filter_title">Filter lintasan</string>
|
||||||
|
<string name="pass_filter_elev">Elevasi minimal</string>
|
||||||
|
<string name="pass_filter_hours">Waktu ke depan</string>
|
||||||
|
<string name="pass_filter_aos_time">Jendela AOS</string>
|
||||||
|
<string name="pass_filter_invert_time">Balik jendela AOS</string>
|
||||||
|
<string name="pass_filter_deep_space">DeepSpace (periode >225 mnt)</string>
|
||||||
|
<string name="pass_time_placeholder"> -- : -- : -- </string>
|
||||||
|
<string name="pass_modes_title">Pilih jenis modulasi</string>
|
||||||
|
<string name="pass_satId">%05d</string>
|
||||||
|
<string name="pass_elevation">Elevasi: %.1f°</string>
|
||||||
|
<string name="pass_deep_space">DeepSpace</string>
|
||||||
|
<string name="pass_altitude">Ketinggian: %d km</string>
|
||||||
|
<string name="pass_aosLos">%03d° - %03d°</string>
|
||||||
|
<string name="pass_empty_list_message">
|
||||||
|
Pastikan filter Anda benar dan Anda sudah memilih satelit</string>
|
||||||
|
<string name="pass_error_message">Tidak ada lintasan satelit mendatang untuk ditampilkan</string>
|
||||||
|
<string name="pass_welcome_title">Selamat datang di Look4Sat\!</string>
|
||||||
|
<string name="pass_welcome_message">
|
||||||
|
Pastikan Anda mengatur posisi melalui GPS, Lat/Lon atau QTH di Pengaturan.
|
||||||
|
\n\nPerbarui database setidaknya seminggu sekali untuk prediksi yang akurat.</string>
|
||||||
|
<string name="pass_whatsnew_title">Apa yang baru di Look4Sat</string>
|
||||||
|
<string name="pass_whatsnew_message">
|
||||||
|
* Navigasi bawah kini menggunakan 5 tab utama plus menu Lainnya (menu tingkat kedua yang dapat dilipat)
|
||||||
|
* Pengaturan UI: urutan halaman dipisah menjadi menu utama dan menu lainnya; pindahkan halaman di antara keduanya</string>
|
||||||
|
<string name="radar_back">Kembali</string>
|
||||||
|
<string name="radar_notify">Beri tahu</string>
|
||||||
|
<string name="radar_az_text">Azimuth</string>
|
||||||
|
<string name="radar_az_value">%.1f°</string>
|
||||||
|
<string name="radar_el_text">Elevasi</string>
|
||||||
|
<string name="radar_el_value">%.1f°</string>
|
||||||
|
<string name="radar_alt_text">Ketinggian</string>
|
||||||
|
<string name="radar_alt_value">%.0f km</string>
|
||||||
|
<string name="radar_dist_text">Jarak</string>
|
||||||
|
<string name="radar_dist_value">%.0f km</string>
|
||||||
|
<string name="radar_eclipsed">Gerhana</string>
|
||||||
|
<string name="radar_downlink">Downlink</string>
|
||||||
|
<string name="radar_uplink">Uplink</string>
|
||||||
|
<string name="radar_link_low">%.4f</string>
|
||||||
|
<string name="radar_link_lowHigh">%.4f - %.4f</string>
|
||||||
|
<string name="radar_no_link">- - . - -</string>
|
||||||
|
<string name="radar_no_data">Satelit ini tidak memiliki transceiver</string>
|
||||||
|
<string name="radar_add">Tambah</string>
|
||||||
|
<string name="radar_data">Transceiver satelit:</string>
|
||||||
|
<string name="radar_inverted">Terbalik: %s</string>
|
||||||
|
<string name="radar_mode">Mode: %s</string>
|
||||||
|
<string name="radar_string_no">Tidak</string>
|
||||||
|
<string name="radar_string_yes">Ya</string>
|
||||||
|
<string name="radar_visible">Terlihat</string>
|
||||||
|
<string name="radar_doppler_calc">Kalkulator Frekuensi Doppler</string>
|
||||||
|
<string name="radar_doppler_tx_hint">Masukkan frekuensi TX (MHz)</string>
|
||||||
|
<string name="radar_doppler_rx_hint">Masukkan frekuensi RX (MHz)</string>
|
||||||
|
<string name="radar_doppler_offset_hint">Offset (kHz)</string>
|
||||||
|
<string name="radar_doppler_info">Untuk transponder linier, ketik satu frekuensi untuk melihat frekuensi lainnya</string>
|
||||||
|
<string name="radar_cw_decoder">Dekoder CW</string>
|
||||||
|
<string name="radar_cw_start">Mulai</string>
|
||||||
|
<string name="radar_cw_stop">Berhenti</string>
|
||||||
|
<string name="radar_cw_reset">Bersihkan</string>
|
||||||
|
<string name="map_prev">Sebelumnya</string>
|
||||||
|
<string name="map_next">Berikutnya</string>
|
||||||
|
<string name="map_copyright">© Kontributor OpenStreetMap</string>
|
||||||
|
<string name="map_azimuth">Azimuth: %.1f°</string>
|
||||||
|
<string name="map_elevation">Elevasi: %.1f°</string>
|
||||||
|
<string name="map_altitude">Ketinggian: %.0f km</string>
|
||||||
|
<string name="map_distance">Jarak: %.0f km</string>
|
||||||
|
<string name="map_latitude">Lintang: %.1f°</string>
|
||||||
|
<string name="map_longitude">Bujur: %.1f°</string>
|
||||||
|
<string name="map_qth">QTH: %s</string>
|
||||||
|
<string name="map_phase">Fase: %.1f°</string>
|
||||||
|
<string name="map_eclipsed">Gerhana</string>
|
||||||
|
<string name="map_period">Periode: %.0f mnt</string>
|
||||||
|
<string name="map_velocity">Kecepatan: %.2f km/d</string>
|
||||||
|
<string name="map_visibility">Visibilitas: %s</string>
|
||||||
|
<string name="map_visible">Terlihat</string>
|
||||||
|
<string name="prefs_app_title">Look4Sat Pro v%s</string>
|
||||||
|
<string name="prefs_app_url">https://play.google.com/store/apps/details?id=com.rtbishop.look4sat</string>
|
||||||
|
<string name="prefs_donate_title">Donasi</string>
|
||||||
|
<string name="prefs_donate_url">https://ko-fi.com/rt_bishop</string>
|
||||||
|
<string name="prefs_fdroid_title">F-Droid</string>
|
||||||
|
<string name="prefs_fdroid_url">https://f-droid.org/en/packages/com.rtbishop.look4sat/</string>
|
||||||
|
<string name="prefs_github_title">GitHub</string>
|
||||||
|
<string name="prefs_github_url">https://github.com/rt-bishop/Look4Sat/</string>
|
||||||
|
<string name="prefs_license_title">GPLv3</string>
|
||||||
|
<string name="prefs_license_url">https://www.gnu.org/licenses/gpl-3.0.html</string>
|
||||||
|
<string name="prefs_privacy_title">Kebijakan Privasi</string>
|
||||||
|
<string name="prefs_privacy_url">https://sites.google.com/view/look4sat-privacy-policy/home</string>
|
||||||
|
<string name="prefs_updated_title">Diperbarui: %s</string>
|
||||||
|
<string name="prefs_updated_time">d MMM yyyy - HH:mm:ss</string>
|
||||||
|
<string name="prefs_loc_title">Posisi stasiun</string>
|
||||||
|
<string name="prefs_loc_gps_title">GPS</string>
|
||||||
|
<string name="prefs_loc_gps_error">Periksa izin lokasi Anda</string>
|
||||||
|
<string name="prefs_loc_input_title">Input</string>
|
||||||
|
<string name="prefs_loc_input_error">Lokasi yang dimasukkan tidak valid</string>
|
||||||
|
<string name="prefs_loc_qth_title">QTH</string>
|
||||||
|
<string name="prefs_loc_qth_error">Lokator QTH tidak valid</string>
|
||||||
|
<string name="prefs_loc_success">Posisi berhasil diperbarui</string>
|
||||||
|
<string name="prefs_station_title">Pengaturan posisi stasiun</string>
|
||||||
|
<string name="prefs_station_lat_text">Atur garis lintang stasiun Anda</string>
|
||||||
|
<string name="prefs_station_lon_text">Atur garis bujur stasiun Anda</string>
|
||||||
|
<string name="prefs_locator_title">Pengaturan lokator QTH</string>
|
||||||
|
<string name="prefs_locator_text">Atur posisi stasiun menggunakan lokator</string>
|
||||||
|
<string name="prefs_data_title">Data satelit</string>
|
||||||
|
<string name="prefs_data_entries">Satelit: %s</string>
|
||||||
|
<string name="prefs_data_radios">Transceiver: %s</string>
|
||||||
|
<string name="prefs_data_update">Perbarui</string>
|
||||||
|
<string name="prefs_data_import">URL Kustom</string>
|
||||||
|
<string name="prefs_data_clear">Bersihkan</string>
|
||||||
|
<string name="prefs_data_clear_success">Data berhasil dibersihkan</string>
|
||||||
|
<string name="prefs_data_update_success">Pembaruan berhasil diselesaikan</string>
|
||||||
|
<string name="prefs_data_import_satellites_error">Tidak ada satelit yang diimpor. Pilih file TLE/3LE (.txt) atau OMM (.csv) yang valid.</string>
|
||||||
|
<string name="prefs_data_import_transceivers_error">Tidak ada transceiver yang diimpor. Pilih file SatNOGS (.json) yang valid.</string>
|
||||||
|
<string name="prefs_data_sources_title">Sumber data kustom</string>
|
||||||
|
<string name="prefs_data_sources_tle_switch">URL TLE Kustom</string>
|
||||||
|
<string name="prefs_data_sources_transceivers_switch">URL transceiver kustom</string>
|
||||||
|
<string name="prefs_data_sources_url_title">URL (HTTPS)</string>
|
||||||
|
<string name="prefs_data_reset_url">Setel ulang ke URL bawaan</string>
|
||||||
|
<string name="prefs_data_output_title">Keluaran data</string>
|
||||||
|
<string name="prefs_net_output">Jaringan</string>
|
||||||
|
<string name="prefs_bt_output">Bluetooth</string>
|
||||||
|
<string name="prefs_cat_output">CAT</string>
|
||||||
|
<string name="nav_radiocontrol">Kontrol Radio</string>
|
||||||
|
<string name="rc_settings_title">Kontrol Radio CAT</string>
|
||||||
|
<string name="rc_radio_model">Model Radio</string>
|
||||||
|
<string name="rc_tx_device_hint">Alamat BT Radio TX</string>
|
||||||
|
<string name="rc_rx_device_hint">Alamat BT Radio RX</string>
|
||||||
|
<string name="rc_tx_name_hint">Nama Radio TX</string>
|
||||||
|
<string name="rc_rx_name_hint">Nama Radio RX</string>
|
||||||
|
<string name="rc_enable_switch">Aktifkan Kontrol CAT</string>
|
||||||
|
<string name="prefs_net_title">Keluaran data jaringan</string>
|
||||||
|
<string name="prefs_net_rotator_switch">Aktifkan keluaran rotator</string>
|
||||||
|
<string name="prefs_net_rotator_address_hint">IP:Port</string>
|
||||||
|
<string name="prefs_net_rotator_format_hint">Format</string>
|
||||||
|
<string name="prefs_net_frequency_switch">Aktifkan keluaran frekuensi</string>
|
||||||
|
<string name="prefs_net_frequency_address_hint">IP:Port</string>
|
||||||
|
<string name="prefs_net_frequency_format_hint">Format</string>
|
||||||
|
<string name="prefs_bt_title">Keluaran data Bluetooth</string>
|
||||||
|
<string name="prefs_bt_rotator_switch">Aktifkan keluaran rotator</string>
|
||||||
|
<string name="prefs_bt_rotator_device_hint">Id perangkat</string>
|
||||||
|
<string name="prefs_bt_rotator_output_hint">Format data</string>
|
||||||
|
<string name="prefs_bt_frequency_switch">Aktifkan keluaran frekuensi</string>
|
||||||
|
<string name="prefs_bt_frequency_device_hint">Id perangkat</string>
|
||||||
|
<string name="prefs_bt_frequency_output_hint">Format data</string>
|
||||||
|
<string name="prefs_bt_perm_error">Periksa izin bluetooth Anda</string>
|
||||||
|
<string name="prefs_net_perm_error">Periksa izin jaringan Anda</string>
|
||||||
|
<string name="tracking_status">Melacak: %s</string>
|
||||||
|
<string name="loc_lat_text">Lintang: %s°</string>
|
||||||
|
<string name="loc_lon_text">Bujur: %s°</string>
|
||||||
|
<string name="loc_qth_text">QTH: %s</string>
|
||||||
|
<string name="connect_radio">Hubungkan</string>
|
||||||
|
<string name="disconnect_radio">Putuskan</string>
|
||||||
|
<string name="track_radio">Lacak</string>
|
||||||
|
<string name="stop_radio">Berhenti</string>
|
||||||
|
<string name="prefs_other_title">Pengaturan lainnya</string>
|
||||||
|
<string name="prefs_ui_title">Pengaturan UI</string>
|
||||||
|
<string name="prefs_ui_order_title">Urutan halaman</string>
|
||||||
|
<string name="prefs_ui_main_title">Menu utama (bilah bawah)</string>
|
||||||
|
<string name="prefs_ui_sub_title">Menu lainnya</string>
|
||||||
|
<string name="prefs_ui_move_out">Pindah ke menu lainnya</string>
|
||||||
|
<string name="prefs_ui_move_back">Kembalikan ke menu utama</string>
|
||||||
|
<string name="prefs_ui_order_locked">Tetap (tidak dapat dipindahkan)</string>
|
||||||
|
<string name="prefs_ui_order_reset">Setel ulang urutan</string>
|
||||||
|
<string name="prefs_other_switch_utc">Tampilkan waktu lintasan dalam UTC</string>
|
||||||
|
<string name="prefs_other_switch_update">Aktifkan pembaruan data otomatis</string>
|
||||||
|
<string name="prefs_other_switch_sweep">Aktifkan animasi sapuan radar</string>
|
||||||
|
<string name="prefs_other_switch_sensors">Gunakan sensor untuk memutar tampilan radar</string>
|
||||||
|
<string name="prefs_other_switch_night_mode">Aktifkan filter mode malam merah</string>
|
||||||
|
<string name="prefs_highlight_title">Sorotan elevasi</string>
|
||||||
|
<string name="prefs_highlight_low">Rendah < %1$d°</string>
|
||||||
|
<string name="prefs_highlight_mid">Sedang %1$d–%2$d°</string>
|
||||||
|
<string name="prefs_highlight_high">Tinggi > %1$d°</string>
|
||||||
|
<string name="prefs_outro_title">BG7NTA & penulis asli mengucapkan terima kasih</string>
|
||||||
|
<string name="prefs_outro_thanks">
|
||||||
|
* Pengguna dan kontributor Look4Sat!
|
||||||
|
\n* David A. B. Johnson (predict4java)
|
||||||
|
\n* Dave Moten (predict4java)
|
||||||
|
\n* Alexandru Csete (Gpredict)
|
||||||
|
\n* Dr T.S. Kelso (Celestrak)
|
||||||
|
\n* Libre Space Foundation (SatNOGS)
|
||||||
|
\n* xdsopl dan kontributor Robot36!
|
||||||
|
\n* BA7OPF (fitur pencocokan lintasan)
|
||||||
|
\n* BG7NTA</string>
|
||||||
|
<string name="prefs_outro_license">Aplikasi ini hadir tanpa jaminan</string>
|
||||||
|
</resources>
|
||||||
@@ -10,8 +10,10 @@
|
|||||||
<string name="nav_sat">Uydular</string>
|
<string name="nav_sat">Uydular</string>
|
||||||
<string name="nav_pass">Geçişler</string>
|
<string name="nav_pass">Geçişler</string>
|
||||||
<string name="nav_radar">Radar</string>
|
<string name="nav_radar">Radar</string>
|
||||||
|
<string name="nav_cw">CW Dekoder</string>
|
||||||
<string name="nav_map">Harita</string>
|
<string name="nav_map">Harita</string>
|
||||||
<string name="nav_prefs">Ayarlar</string>
|
<string name="nav_prefs">Ayarlar</string>
|
||||||
|
<string name="nav_more">Daha Fazla</string>
|
||||||
|
|
||||||
<!-- Satellites screen -->
|
<!-- Satellites screen -->
|
||||||
<string name="sat_type_hint">Uydu türü: %s</string>
|
<string name="sat_type_hint">Uydu türü: %s</string>
|
||||||
@@ -50,13 +52,9 @@
|
|||||||
Ayarlar\'dan GPS, Enlem/Boylam veya QTH kullanarak konumunuzu belirlediğinizden emin olun.
|
Ayarlar\'dan GPS, Enlem/Boylam veya QTH kullanarak konumunuzu belirlediğinizden emin olun.
|
||||||
\n\nDoğru tahminler alabilmek için veritabanını en az haftada bir güncelleyin.</string>
|
\n\nDoğru tahminler alabilmek için veritabanını en az haftada bir güncelleyin.</string>
|
||||||
<string name="pass_whatsnew_title" translatable="false">What\'s new in Look4Sat</string>
|
<string name="pass_whatsnew_title" translatable="false">What\'s new in Look4Sat</string>
|
||||||
<string name="pass_whatsnew_message" translatable="false">
|
<string name="pass_whatsnew_message" translatable="false">
|
||||||
* Switched to short-form commands in NetworkReporter, by theojalba
|
* Alt gezinme artık 5 ana sekme artı Daha Fazla menüsü kullanıyor (katlanabilir ikinci düzey menü)
|
||||||
\n* Added CAT radio control for full-duplex SAT operation, by jcmerg
|
* UI Ayarları: sayfa sırası ana menü ve daha fazla menü olarak ayrıldı; sayfalar arasında taşıma yapılabilir</string>
|
||||||
\n* Added normalized time for passes AOS/LOS, fixed refresh state bug
|
|
||||||
\n* Added multiple performance tweaks, improved passes calculation
|
|
||||||
\n* Fixed UTC pass time problem mentioned in issue #201
|
|
||||||
</string>
|
|
||||||
|
|
||||||
<!-- Radar screen -->
|
<!-- Radar screen -->
|
||||||
<string name="radar_back">Geri</string>
|
<string name="radar_back">Geri</string>
|
||||||
@@ -103,7 +101,7 @@
|
|||||||
<string name="map_visible">Görünür</string>
|
<string name="map_visible">Görünür</string>
|
||||||
|
|
||||||
<!-- Settings screen -->
|
<!-- Settings screen -->
|
||||||
<string name="prefs_app_title" translatable="false">Look4Sat v%s</string>
|
<string name="prefs_app_title" translatable="false">Look4Sat Pro v%s</string>
|
||||||
<string name="prefs_app_url" translatable="false">https://play.google.com/store/apps/details?id=com.rtbishop.look4sat</string>
|
<string name="prefs_app_url" translatable="false">https://play.google.com/store/apps/details?id=com.rtbishop.look4sat</string>
|
||||||
<string name="prefs_donate_title">Bağış yap</string>
|
<string name="prefs_donate_title">Bağış yap</string>
|
||||||
<string name="prefs_donate_url" translatable="false">https://ko-fi.com/rt_bishop</string>
|
<string name="prefs_donate_url" translatable="false">https://ko-fi.com/rt_bishop</string>
|
||||||
@@ -137,7 +135,7 @@
|
|||||||
<string name="prefs_data_entries">Uydular: %s</string>
|
<string name="prefs_data_entries">Uydular: %s</string>
|
||||||
<string name="prefs_data_radios">Transceiver\'lar: %s</string>
|
<string name="prefs_data_radios">Transceiver\'lar: %s</string>
|
||||||
<string name="prefs_data_update">Güncelle</string>
|
<string name="prefs_data_update">Güncelle</string>
|
||||||
<string name="prefs_data_import">İçe aktar</string>
|
<string name="prefs_data_import">Özel URL</string>
|
||||||
<string name="prefs_data_clear">Temizle</string>
|
<string name="prefs_data_clear">Temizle</string>
|
||||||
<string name="prefs_data_clear_success">Veriler başarıyla temizlendi</string>
|
<string name="prefs_data_clear_success">Veriler başarıyla temizlendi</string>
|
||||||
<string name="prefs_data_update_success">Güncelleme başarıyla tamamlandı</string>
|
<string name="prefs_data_update_success">Güncelleme başarıyla tamamlandı</string>
|
||||||
@@ -146,6 +144,7 @@
|
|||||||
<string name="prefs_data_sources_tle_switch">Özel TLE URL</string>
|
<string name="prefs_data_sources_tle_switch">Özel TLE URL</string>
|
||||||
<string name="prefs_data_sources_transceivers_switch">Özel transceiver URL</string>
|
<string name="prefs_data_sources_transceivers_switch">Özel transceiver URL</string>
|
||||||
<string name="prefs_data_sources_url_title" translatable="false">URL (HTTPS)</string>
|
<string name="prefs_data_sources_url_title" translatable="false">URL (HTTPS)</string>
|
||||||
|
<string name="prefs_data_reset_url">Varsayılan URL\'ye sıfırla</string>
|
||||||
|
|
||||||
<string name="prefs_data_output_title">Veri aktarımı</string>
|
<string name="prefs_data_output_title">Veri aktarımı</string>
|
||||||
<string name="prefs_net_output">Ağ</string>
|
<string name="prefs_net_output">Ağ</string>
|
||||||
@@ -180,7 +179,23 @@
|
|||||||
<string name="prefs_bt_perm_error">Bluetooth izninizi kontrol edin</string>
|
<string name="prefs_bt_perm_error">Bluetooth izninizi kontrol edin</string>
|
||||||
<string name="prefs_net_perm_error">Ağ izninizi kontrol edin</string>
|
<string name="prefs_net_perm_error">Ağ izninizi kontrol edin</string>
|
||||||
|
|
||||||
|
<string name="tracking_status">İzleniyor: %s</string>
|
||||||
|
<string name="loc_lat_text">Enlem: %s°</string>
|
||||||
|
<string name="loc_lon_text">Boylam: %s°</string>
|
||||||
|
<string name="loc_qth_text">QTH: %s</string>
|
||||||
|
<string name="connect_radio">Bağlan</string>
|
||||||
|
<string name="disconnect_radio">Bağlantıyı Kes</string>
|
||||||
|
<string name="track_radio">Takip</string>
|
||||||
|
<string name="stop_radio">Dur</string>
|
||||||
<string name="prefs_other_title">Diğer ayarlar</string>
|
<string name="prefs_other_title">Diğer ayarlar</string>
|
||||||
|
<string name="prefs_ui_title">UI Ayarları</string>
|
||||||
|
<string name="prefs_ui_order_title">Sayfa sırası</string>
|
||||||
|
<string name="prefs_ui_main_title">Ana menü (alt çubuk)</string>
|
||||||
|
<string name="prefs_ui_sub_title">Daha fazla menü</string>
|
||||||
|
<string name="prefs_ui_move_out">Daha fazla menüye taşı</string>
|
||||||
|
<string name="prefs_ui_move_back">Ana menüye geri taşı</string>
|
||||||
|
<string name="prefs_ui_order_locked">Sabit (taşınamaz)</string>
|
||||||
|
<string name="prefs_ui_order_reset">Sıralamayı sıfırla</string>
|
||||||
<string name="prefs_other_switch_utc">Geçiş saatini UTC olarak göster</string>
|
<string name="prefs_other_switch_utc">Geçiş saatini UTC olarak göster</string>
|
||||||
<string name="prefs_other_switch_update">Otomatik veri güncellemeyi etkinleştir</string>
|
<string name="prefs_other_switch_update">Otomatik veri güncellemeyi etkinleştir</string>
|
||||||
<string name="prefs_other_switch_sweep">Radar taramasını etkinleştir</string>
|
<string name="prefs_other_switch_sweep">Radar taramasını etkinleştir</string>
|
||||||
@@ -192,14 +207,16 @@
|
|||||||
<string name="prefs_highlight_mid">Orta %1$d-%2$d°</string>
|
<string name="prefs_highlight_mid">Orta %1$d-%2$d°</string>
|
||||||
<string name="prefs_highlight_high">Yüksek > %1$d°</string>
|
<string name="prefs_highlight_high">Yüksek > %1$d°</string>
|
||||||
|
|
||||||
<string name="prefs_outro_title">Teşekkürler</string>
|
<string name="prefs_outro_title">BG7NTA ve orijinal yazar teşekkür eder</string>
|
||||||
<string name="prefs_outro_thanks" translatable="false">
|
<string name="prefs_outro_thanks" translatable="false">
|
||||||
• Look4Sat users and contributors!
|
• Look4Sat users and contributors!
|
||||||
\n• David A. B. Johnson (predict4java)
|
\n• David A. B. Johnson (predict4java)
|
||||||
\n• Dave Moten (predict4java)
|
\n• Dave Moten (predict4java)
|
||||||
\n• Alexandru Csete (Gpredict)
|
\n• Alexandru Csete (Gpredict)
|
||||||
\n• Dr T.S. Kelso (Celestrak)
|
\n• Dr T.S. Kelso (Celestrak)
|
||||||
\n• Libre Space Foundation (SatNOGS)</string>
|
\n• Libre Space Foundation (SatNOGS)
|
||||||
|
\n• BA7OPF (pass matching feature)
|
||||||
|
\n• BG7NTA</string>
|
||||||
<string name="prefs_outro_license">Bu uygulama herhangi bir garanti sunmaz</string>
|
<string name="prefs_outro_license">Bu uygulama herhangi bir garanti sunmaz</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -10,8 +10,11 @@
|
|||||||
<string name="nav_pass">过境</string>
|
<string name="nav_pass">过境</string>
|
||||||
<string name="nav_radar">雷达</string>
|
<string name="nav_radar">雷达</string>
|
||||||
<string name="nav_mutual">匹配</string>
|
<string name="nav_mutual">匹配</string>
|
||||||
|
<string name="nav_roaming">漫游</string>
|
||||||
|
<string name="nav_cw">CW解码</string>
|
||||||
<string name="nav_map">地图</string>
|
<string name="nav_map">地图</string>
|
||||||
<string name="nav_prefs">设置</string>
|
<string name="nav_prefs">设置</string>
|
||||||
|
<string name="nav_more">更多</string>
|
||||||
|
|
||||||
<!-- Satellites screen -->
|
<!-- Satellites screen -->
|
||||||
<string name="sat_type_hint">类型:%s</string>
|
<string name="sat_type_hint">类型:%s</string>
|
||||||
@@ -38,6 +41,10 @@
|
|||||||
<string name="pass_error_message">没有即将过境的卫星</string>
|
<string name="pass_error_message">没有即将过境的卫星</string>
|
||||||
<string name="pass_welcome_title">欢迎来到 Look4Sat\!</string>
|
<string name="pass_welcome_title">欢迎来到 Look4Sat\!</string>
|
||||||
<string name="pass_welcome_message">请务必在设置中通过GPS、经纬度或QTH定位您的位置\n建议至少每周更新一次数据库,以确保预测结果的准确性</string>
|
<string name="pass_welcome_message">请务必在设置中通过GPS、经纬度或QTH定位您的位置\n建议至少每周更新一次数据库,以确保预测结果的准确性</string>
|
||||||
|
<string name="pass_whatsnew_title">Look4Sat Pro 更新内容</string>
|
||||||
|
<string name="pass_whatsnew_message" translatable="false">
|
||||||
|
* 底部导航改为 5 个主标签 + 更多菜单(可折叠二级菜单)
|
||||||
|
* UI 设置: 页面顺序分为主菜单与更多菜单,可拖拽或移动页面</string>
|
||||||
|
|
||||||
<!-- Radar screen -->
|
<!-- Radar screen -->
|
||||||
<string name="radar_back">后退</string>
|
<string name="radar_back">后退</string>
|
||||||
@@ -110,7 +117,7 @@
|
|||||||
<string name="prefs_data_entries">卫星:%s</string>
|
<string name="prefs_data_entries">卫星:%s</string>
|
||||||
<string name="prefs_data_radios">收发器:%s</string>
|
<string name="prefs_data_radios">收发器:%s</string>
|
||||||
<string name="prefs_data_update">在线更新</string>
|
<string name="prefs_data_update">在线更新</string>
|
||||||
<string name="prefs_data_import">导入文件</string>
|
<string name="prefs_data_import">自定义URL</string>
|
||||||
<string name="prefs_data_clear">清空</string>
|
<string name="prefs_data_clear">清空</string>
|
||||||
<string name="prefs_data_clear_success">数据清空成功</string>
|
<string name="prefs_data_clear_success">数据清空成功</string>
|
||||||
<string name="prefs_data_update_success">更新成功</string>
|
<string name="prefs_data_update_success">更新成功</string>
|
||||||
@@ -155,7 +162,23 @@
|
|||||||
<string name="rc_rx_name_hint">接收电台名称</string>
|
<string name="rc_rx_name_hint">接收电台名称</string>
|
||||||
<string name="rc_enable_switch">启用 CAT 控制</string>
|
<string name="rc_enable_switch">启用 CAT 控制</string>
|
||||||
|
|
||||||
|
<string name="tracking_status">跟踪: %s</string>
|
||||||
|
<string name="loc_lat_text">纬度: %s°</string>
|
||||||
|
<string name="loc_lon_text">经度: %s°</string>
|
||||||
|
<string name="loc_qth_text">QTH: %s</string>
|
||||||
|
<string name="connect_radio">连接</string>
|
||||||
|
<string name="disconnect_radio">断开</string>
|
||||||
|
<string name="track_radio">跟踪</string>
|
||||||
|
<string name="stop_radio">停止</string>
|
||||||
<string name="prefs_other_title">其他设置</string>
|
<string name="prefs_other_title">其他设置</string>
|
||||||
|
<string name="prefs_ui_title">UI设置</string>
|
||||||
|
<string name="prefs_ui_order_title">页面顺序</string>
|
||||||
|
<string name="prefs_ui_main_title">主菜单(底部栏)</string>
|
||||||
|
<string name="prefs_ui_sub_title">更多菜单</string>
|
||||||
|
<string name="prefs_ui_move_out">移入更多菜单</string>
|
||||||
|
<string name="prefs_ui_move_back">移回主菜单</string>
|
||||||
|
<string name="prefs_ui_order_locked">固定(不可移动)</string>
|
||||||
|
<string name="prefs_ui_order_reset">重置顺序</string>
|
||||||
<string name="prefs_other_switch_utc">以 UTC 时间显示</string>
|
<string name="prefs_other_switch_utc">以 UTC 时间显示</string>
|
||||||
<string name="prefs_other_switch_update">启用卫星数据自动更新</string>
|
<string name="prefs_other_switch_update">启用卫星数据自动更新</string>
|
||||||
<string name="prefs_other_switch_sweep">启用雷达扫描动画</string>
|
<string name="prefs_other_switch_sweep">启用雷达扫描动画</string>
|
||||||
@@ -167,7 +190,7 @@
|
|||||||
<string name="prefs_highlight_mid">中 %1$d-%2$d°</string>
|
<string name="prefs_highlight_mid">中 %1$d-%2$d°</string>
|
||||||
<string name="prefs_highlight_high">高 > %1$d°</string>
|
<string name="prefs_highlight_high">高 > %1$d°</string>
|
||||||
|
|
||||||
<string name="prefs_outro_title">我要感谢:</string>
|
<string name="prefs_outro_title">BG7NTA 与原作者感谢:</string>
|
||||||
<string name="prefs_outro_thanks" translatable="false">
|
<string name="prefs_outro_thanks" translatable="false">
|
||||||
• Look4Sat 所有用户及贡献者!
|
• Look4Sat 所有用户及贡献者!
|
||||||
\n• David A. B. Johnson (predict4java)
|
\n• David A. B. Johnson (predict4java)
|
||||||
@@ -175,7 +198,9 @@
|
|||||||
\n• Alexandru Csete (Gpredict)
|
\n• Alexandru Csete (Gpredict)
|
||||||
\n• Dr T.S. Kelso (Celestrak)
|
\n• Dr T.S. Kelso (Celestrak)
|
||||||
\n• Libre Space Foundation (SatNOGS)
|
\n• Libre Space Foundation (SatNOGS)
|
||||||
\n• xdsopl 和 Robot36 贡献者!</string>
|
\n• xdsopl 和 Robot36 贡献者!
|
||||||
|
\n• BA7OPF(过境匹配功能)
|
||||||
|
\n• BG7NTA</string>
|
||||||
<string name="prefs_outro_license">该应用程序不提供任何保修.</string>
|
<string name="prefs_outro_license">该应用程序不提供任何保修.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<!-- Commmon -->
|
<!-- Commmon -->
|
||||||
<string name="app_name" translatable="false">Look4Sat</string>
|
<string name="app_name" translatable="false">Look4Sat Pro</string>
|
||||||
<string name="btn_accept">Accept</string>
|
<string name="btn_accept">Accept</string>
|
||||||
<string name="btn_cancel">Cancel</string>
|
<string name="btn_cancel">Cancel</string>
|
||||||
<string name="empty_list_message">No data to show</string>
|
<string name="empty_list_message">No data to show</string>
|
||||||
@@ -11,8 +11,11 @@
|
|||||||
<string name="nav_pass">Passes</string>
|
<string name="nav_pass">Passes</string>
|
||||||
<string name="nav_radar">Radar</string>
|
<string name="nav_radar">Radar</string>
|
||||||
<string name="nav_mutual">Match</string>
|
<string name="nav_mutual">Match</string>
|
||||||
|
<string name="nav_roaming">Roaming</string>
|
||||||
|
<string name="nav_cw">CW Decoder</string>
|
||||||
<string name="nav_map">Map</string>
|
<string name="nav_map">Map</string>
|
||||||
<string name="nav_prefs">Settings</string>
|
<string name="nav_prefs">Settings</string>
|
||||||
|
<string name="nav_more">More</string>
|
||||||
|
|
||||||
<!-- Satellites screen -->
|
<!-- Satellites screen -->
|
||||||
<string name="sat_type_hint">Type: %s</string>
|
<string name="sat_type_hint">Type: %s</string>
|
||||||
@@ -51,10 +54,9 @@
|
|||||||
Make sure to set your position via GPS, Lat/Lon or QTH in Settings.
|
Make sure to set your position via GPS, Lat/Lon or QTH in Settings.
|
||||||
\n\nPlease update the database at least weekly to get accurate predictions.</string>
|
\n\nPlease update the database at least weekly to get accurate predictions.</string>
|
||||||
<string name="pass_whatsnew_title" translatable="false">What\'s new in Look4Sat</string>
|
<string name="pass_whatsnew_title" translatable="false">What\'s new in Look4Sat</string>
|
||||||
<string name="pass_whatsnew_message" translatable="false">
|
<string name="pass_whatsnew_message" translatable="false">
|
||||||
* Added required tweaks to support Android 17 (API 37)
|
* Bottom navigation now uses 5 main tabs plus a More menu (collapsible second-level menu)
|
||||||
\n\n* Added ACCESS_LOCAL_NETWORK permission check to support network data output
|
* UI Settings: page order split into main menu and more menu; drag or move pages between them</string>
|
||||||
</string>
|
|
||||||
|
|
||||||
<!-- Radar screen -->
|
<!-- Radar screen -->
|
||||||
<string name="radar_back">Back</string>
|
<string name="radar_back">Back</string>
|
||||||
@@ -111,7 +113,7 @@
|
|||||||
<string name="map_visible">Visible</string>
|
<string name="map_visible">Visible</string>
|
||||||
|
|
||||||
<!-- Settings screen -->
|
<!-- Settings screen -->
|
||||||
<string name="prefs_app_title" translatable="false">Look4Sat v%s</string>
|
<string name="prefs_app_title" translatable="false">Look4Sat Pro v%s</string>
|
||||||
<string name="prefs_app_url" translatable="false">https://play.google.com/store/apps/details?id=com.rtbishop.look4sat</string>
|
<string name="prefs_app_url" translatable="false">https://play.google.com/store/apps/details?id=com.rtbishop.look4sat</string>
|
||||||
<string name="prefs_donate_title">Donate</string>
|
<string name="prefs_donate_title">Donate</string>
|
||||||
<string name="prefs_donate_url" translatable="false">https://ko-fi.com/rt_bishop</string>
|
<string name="prefs_donate_url" translatable="false">https://ko-fi.com/rt_bishop</string>
|
||||||
@@ -145,7 +147,7 @@
|
|||||||
<string name="prefs_data_entries">Satellites: %s</string>
|
<string name="prefs_data_entries">Satellites: %s</string>
|
||||||
<string name="prefs_data_radios">Transceivers: %s</string>
|
<string name="prefs_data_radios">Transceivers: %s</string>
|
||||||
<string name="prefs_data_update">Update</string>
|
<string name="prefs_data_update">Update</string>
|
||||||
<string name="prefs_data_import">Import</string>
|
<string name="prefs_data_import">Custom URL</string>
|
||||||
<string name="prefs_data_clear">Clear</string>
|
<string name="prefs_data_clear">Clear</string>
|
||||||
<string name="prefs_data_clear_success">Data was cleared successfully</string>
|
<string name="prefs_data_clear_success">Data was cleared successfully</string>
|
||||||
<string name="prefs_data_update_success">Update completed successfully</string>
|
<string name="prefs_data_update_success">Update completed successfully</string>
|
||||||
@@ -156,6 +158,7 @@
|
|||||||
<string name="prefs_data_sources_tle_switch">Custom TLE URL</string>
|
<string name="prefs_data_sources_tle_switch">Custom TLE URL</string>
|
||||||
<string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string>
|
<string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string>
|
||||||
<string name="prefs_data_sources_url_title" translatable="false">URL (HTTPS)</string>
|
<string name="prefs_data_sources_url_title" translatable="false">URL (HTTPS)</string>
|
||||||
|
<string name="prefs_data_reset_url">Reset to default URL</string>
|
||||||
|
|
||||||
<string name="prefs_data_output_title">Data output</string>
|
<string name="prefs_data_output_title">Data output</string>
|
||||||
<string name="prefs_net_output">Network</string>
|
<string name="prefs_net_output">Network</string>
|
||||||
@@ -190,7 +193,23 @@
|
|||||||
<string name="prefs_bt_perm_error">Check your bluetooth permission</string>
|
<string name="prefs_bt_perm_error">Check your bluetooth permission</string>
|
||||||
<string name="prefs_net_perm_error">Check your network permission</string>
|
<string name="prefs_net_perm_error">Check your network permission</string>
|
||||||
|
|
||||||
|
<string name="tracking_status">Tracking: %s</string>
|
||||||
|
<string name="loc_lat_text">Lat: %s°</string>
|
||||||
|
<string name="loc_lon_text">Lon: %s°</string>
|
||||||
|
<string name="loc_qth_text">Qth: %s</string>
|
||||||
|
<string name="connect_radio">Connect</string>
|
||||||
|
<string name="disconnect_radio">Disconnect</string>
|
||||||
|
<string name="track_radio">Track</string>
|
||||||
|
<string name="stop_radio">Stop</string>
|
||||||
<string name="prefs_other_title">Other settings</string>
|
<string name="prefs_other_title">Other settings</string>
|
||||||
|
<string name="prefs_ui_title">UI Settings</string>
|
||||||
|
<string name="prefs_ui_order_title">Page order</string>
|
||||||
|
<string name="prefs_ui_main_title">Main menu (bottom bar)</string>
|
||||||
|
<string name="prefs_ui_sub_title">More menu</string>
|
||||||
|
<string name="prefs_ui_move_out">Move to more menu</string>
|
||||||
|
<string name="prefs_ui_move_back">Move back to main menu</string>
|
||||||
|
<string name="prefs_ui_order_locked">Fixed (cannot be moved)</string>
|
||||||
|
<string name="prefs_ui_order_reset">Reset order</string>
|
||||||
<string name="prefs_other_switch_utc">Show pass time in UTC</string>
|
<string name="prefs_other_switch_utc">Show pass time in UTC</string>
|
||||||
<string name="prefs_other_switch_update">Enable automatic data update</string>
|
<string name="prefs_other_switch_update">Enable automatic data update</string>
|
||||||
<string name="prefs_other_switch_sweep">Enable radar sweep animation</string>
|
<string name="prefs_other_switch_sweep">Enable radar sweep animation</string>
|
||||||
@@ -202,7 +221,7 @@
|
|||||||
<string name="prefs_highlight_mid">Mid %1$d–%2$d°</string>
|
<string name="prefs_highlight_mid">Mid %1$d–%2$d°</string>
|
||||||
<string name="prefs_highlight_high">High > %1$d°</string>
|
<string name="prefs_highlight_high">High > %1$d°</string>
|
||||||
|
|
||||||
<string name="prefs_outro_title">I would like to say thanks to</string>
|
<string name="prefs_outro_title">BG7NTA & the original author would like to thank</string>
|
||||||
<string name="prefs_outro_thanks" translatable="false">
|
<string name="prefs_outro_thanks" translatable="false">
|
||||||
* Look4Sat users and contributors!
|
* Look4Sat users and contributors!
|
||||||
\n* David A. B. Johnson (predict4java)
|
\n* David A. B. Johnson (predict4java)
|
||||||
@@ -211,6 +230,8 @@
|
|||||||
\n* Dr T.S. Kelso (Celestrak)
|
\n* Dr T.S. Kelso (Celestrak)
|
||||||
\n* Libre Space Foundation (SatNOGS)
|
\n* Libre Space Foundation (SatNOGS)
|
||||||
\n* xdsopl and Robot36 contributors!
|
\n* xdsopl and Robot36 contributors!
|
||||||
|
\n* BA7OPF (pass matching feature)
|
||||||
|
\n* BG7NTA
|
||||||
</string>
|
</string>
|
||||||
<string name="prefs_outro_license">The app comes with no warranty</string>
|
<string name="prefs_outro_license">The app comes with no warranty</string>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import com.android.build.api.dsl.LibraryExtension
|
||||||
|
import com.android.build.api.variant.CanProduceConsumerProguardFiles
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.convention.featurePlugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.rtbishop.look4sat.feature.cw"
|
||||||
|
compileOptions {
|
||||||
|
encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
// 照搬 Morse Expert 1.15: native 解码器仅 armeabi-v7a
|
||||||
|
defaultConfig {
|
||||||
|
ndk {
|
||||||
|
abiFilters += listOf("armeabi-v7a")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CW 类保持规则(JNI 按类名注册 + 照搬混淆类保逻辑), 由 app 的 R8 消费
|
||||||
|
// AGP 9: consumer 规则走 variant 级 CanProduceConsumerProguardFiles
|
||||||
|
androidComponents {
|
||||||
|
onVariants(selector().all()) { variant ->
|
||||||
|
(variant as? CanProduceConsumerProguardFiles)?.consumerProguardFiles?.add(
|
||||||
|
project.layout.projectDirectory.file("proguard-rules.pro")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 照搬的 Java 源码含中文注释, 强制 UTF-8 编译(compileOptions 在部分 AGP 版本不生效)
|
||||||
|
tasks.withType<JavaCompile>().configureEach {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
Vendored
+31
@@ -0,0 +1,31 @@
|
|||||||
|
# Look4Sat Pro — CW 解码模块(照搬 Morse Expert 1.15)混淆规则
|
||||||
|
# 所有 CW 类保持原名: pas.* 通过 RegisterNatives 按类名注册 JNI(混淆即崩);
|
||||||
|
# 其余为照搬的混淆名类(命名已无混淆意义, 保持以保逻辑完整)。
|
||||||
|
|
||||||
|
# JNI 引擎(RegisterNatives 按类名/方法名查表)
|
||||||
|
-keep class pas.** { *; }
|
||||||
|
|
||||||
|
# 照搬的解码/UI 类(全部保持)
|
||||||
|
-keep class com.ve3nea.morse_expert.** { *; }
|
||||||
|
-keep class H2.** { *; }
|
||||||
|
-keep class I2.b { *; }
|
||||||
|
-keep class J2.** { *; }
|
||||||
|
-keep class k3.** { *; }
|
||||||
|
-keep class i3.** { *; }
|
||||||
|
-keep class g3.** { *; }
|
||||||
|
-keep class j3.** { *; }
|
||||||
|
-keep class s.** { *; }
|
||||||
|
-keep class d1.AbstractC1518b { *; }
|
||||||
|
-keep class B0.b { *; }
|
||||||
|
-keep class B.RunnableC0001b { *; }
|
||||||
|
-keep class D.n { *; }
|
||||||
|
-keep class E2.g { *; }
|
||||||
|
-keep class F2.a { *; }
|
||||||
|
-keep class I0.d { *; }
|
||||||
|
-keep class K1.a { *; }
|
||||||
|
-keep class j1.C1646n { *; }
|
||||||
|
|
||||||
|
# sun.misc stub(Android 无此类, 仅编译期; 防止 R8 异常处理)
|
||||||
|
-dontwarn sun.misc.**
|
||||||
|
-keep class sun.misc.Unsafe { *; }
|
||||||
|
-keep class sun.misc.Cleaner { *; }
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package B;
|
||||||
|
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import com.ve3nea.morse_expert.DecodedTextView;
|
||||||
|
import com.ve3nea.morse_expert.MainActivity;
|
||||||
|
import H2.b;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 照搬自 Morse Expert 1.15 B.RunnableC0001b, 类名保持混淆原名。
|
||||||
|
* R8 合并 Runnable 手术: 构造按 smali 真实签名 5 参 (Object,Object,int,int,boolean),
|
||||||
|
* 字段 = i(Object)/h(Object)/g(int)/f(int); run() 仅保留 case 4(f==2 对应 pswitch_4:
|
||||||
|
* 解码文本 UI 更新, H2.b 调用), 其余 case(权限/Intent/通知等库代码)已裁剪。
|
||||||
|
* 注: jadx 4 参构造为错误还原, 已按 smali 修正。
|
||||||
|
*/
|
||||||
|
public final class RunnableC0001b implements Runnable {
|
||||||
|
public final /* synthetic */ int f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public final int g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public final Object h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public final Object i;
|
||||||
|
|
||||||
|
public /* synthetic */ RunnableC0001b(Object obj, Object obj2, int i4, int i5, boolean z) {
|
||||||
|
this.i = obj;
|
||||||
|
this.h = obj2;
|
||||||
|
this.g = i4;
|
||||||
|
this.f = i5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // java.lang.Runnable
|
||||||
|
public final void run() {
|
||||||
|
if (this.f == 2) {
|
||||||
|
MainActivity mainActivity = ((b) this.i).f620p;
|
||||||
|
if (mainActivity != null) {
|
||||||
|
SpannableStringBuilder spannableStringBuilder = (SpannableStringBuilder) this.h;
|
||||||
|
int i5 = this.g;
|
||||||
|
DecodedTextView decodedTextView = (DecodedTextView) mainActivity.f11035D.f11888g;
|
||||||
|
if (!decodedTextView.f11032n && !decodedTextView.f11033o.get()) {
|
||||||
|
decodedTextView.beginBatchEdit();
|
||||||
|
try {
|
||||||
|
int length2 = decodedTextView.length();
|
||||||
|
int min = Math.min(length2, i5);
|
||||||
|
if (min > 0) {
|
||||||
|
decodedTextView.getEditableText().delete(length2 - min, length2);
|
||||||
|
}
|
||||||
|
decodedTextView.q(spannableStringBuilder);
|
||||||
|
decodedTextView.endBatchEdit();
|
||||||
|
return;
|
||||||
|
} catch (Throwable th) {
|
||||||
|
decodedTextView.endBatchEdit();
|
||||||
|
throw th;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package B0;
|
||||||
|
|
||||||
|
import android.widget.TextView;
|
||||||
|
import com.ve3nea.morse_expert.MainActivity;
|
||||||
|
import com.ve3nea.morse_expert.ScaleView;
|
||||||
|
import java.util.Locale;
|
||||||
|
import d1.AbstractC1518b;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 照搬自 Morse Expert 1.15 B0.b, 类名保持混淆原名。
|
||||||
|
* R8 合并 Runnable 手术: 构造按 smali 真实签名 (H2/b, String, [I) -> f=4;
|
||||||
|
* 只保留 case 4(状态栏更新: dBFS/Hz/dB/WPM + ScaleView 频率标记, H2.b 调用),
|
||||||
|
* 其余 case(WorkManager/动画等库代码)已裁剪。
|
||||||
|
* r3 = scaleView.getHeight()(smali 证实, jadx 寄存器未展开)。
|
||||||
|
*/
|
||||||
|
public final class b implements Runnable {
|
||||||
|
public final /* synthetic */ int f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public final Object g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public final Object h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public final Object i;
|
||||||
|
|
||||||
|
public b(H2.b bVar, String str, int[] iArr) {
|
||||||
|
this.f = 4;
|
||||||
|
this.g = bVar;
|
||||||
|
this.h = iArr;
|
||||||
|
this.i = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // java.lang.Runnable
|
||||||
|
public final void run() {
|
||||||
|
if (this.f == 4) {
|
||||||
|
MainActivity mainActivity = ((H2.b) this.g).f621q;
|
||||||
|
if (mainActivity != null) {
|
||||||
|
String str2 = (String) this.i;
|
||||||
|
int[] iArr = (int[]) this.h;
|
||||||
|
String concat;
|
||||||
|
String format = String.format(Locale.US, " %3d dBFS", Integer.valueOf(iArr[0]));
|
||||||
|
if (iArr[5] == 0) {
|
||||||
|
concat = format.concat(" Idle ");
|
||||||
|
} else {
|
||||||
|
concat = format.concat(String.format(Locale.US, " %4d Hz %2d dB %2d WPM ", Integer.valueOf(iArr[1]), Integer.valueOf(iArr[2]), Integer.valueOf(iArr[3])));
|
||||||
|
}
|
||||||
|
((TextView) mainActivity.f11035D.f11890i).setText(AbstractC1518b.e(concat, str2));
|
||||||
|
ScaleView scaleView = (ScaleView) mainActivity.f11035D.f11889h;
|
||||||
|
int i4 = iArr[1];
|
||||||
|
scaleView.f11041g = iArr[5];
|
||||||
|
// r3 = scaleView.getHeight()(smali 证实)
|
||||||
|
scaleView.f = (scaleView.getHeight() - 1) - Math.round(((Math.round(i4 * 0.128f) - H2.b.f606z) * scaleView.getHeight()) / H2.b.f603B);
|
||||||
|
scaleView.invalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package D;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.opengl.GLES20;
|
||||||
|
import android.opengl.GLUtils;
|
||||||
|
import com.ve3nea.morse_expert.WaterfallSurfaceView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 照搬自 Morse Expert 1.15, 类名保持混淆原名。
|
||||||
|
* R8 合并 Runnable 手术: 只保留 case 1(瀑布图频谱->OpenGL 纹理, H2.b 调用),
|
||||||
|
* 其余 case(库/广告)已裁剪。
|
||||||
|
*/
|
||||||
|
public final /* synthetic */ class n implements Runnable {
|
||||||
|
public final /* synthetic */ int f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ Object f224g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ Object f225h;
|
||||||
|
|
||||||
|
public /* synthetic */ n(Object obj, int i4, Object obj2) {
|
||||||
|
this.f = i4;
|
||||||
|
this.f224g = obj;
|
||||||
|
this.f225h = obj2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // java.lang.Runnable
|
||||||
|
public final void run() {
|
||||||
|
int i4 = this.f;
|
||||||
|
Object obj = this.f225h;
|
||||||
|
Object obj2 = this.f224g;
|
||||||
|
if (i4 == 1) {
|
||||||
|
float[] fArr = (float[]) obj;
|
||||||
|
J2.b bVar = ((WaterfallSurfaceView) obj2).f;
|
||||||
|
Bitmap bitmap = bVar.f733h;
|
||||||
|
int width = bVar.f730d.getWidth();
|
||||||
|
double d4 = width;
|
||||||
|
double b4 = (bVar.b() + 4.0d) % d4;
|
||||||
|
double d5 = bVar.f737l - b4;
|
||||||
|
if (d5 > d4 / 2.0d) {
|
||||||
|
d5 -= d4;
|
||||||
|
} else if (d5 < (-width) / 2.0d) {
|
||||||
|
d5 += d4;
|
||||||
|
}
|
||||||
|
if (Math.abs(d5) > 8.0d) {
|
||||||
|
bVar.f737l = (int) Math.floor(b4);
|
||||||
|
} else {
|
||||||
|
bVar.f732g = (d5 * 1.0E-5d) + bVar.f732g;
|
||||||
|
}
|
||||||
|
int height = bitmap.getHeight() - 1;
|
||||||
|
for (int i5 = 0; i5 <= height; i5++) {
|
||||||
|
J2.a aVar = bVar.f734i;
|
||||||
|
int round = Math.round((fArr[i5] * 4.0f) + 30.0f);
|
||||||
|
aVar.getClass();
|
||||||
|
bitmap.setPixel(0, height - i5, aVar.f727a[Math.max(0, Math.min(255, round))]);
|
||||||
|
}
|
||||||
|
int i6 = bVar.f737l;
|
||||||
|
GLES20.glBindTexture(3553, bVar.f728a[0]);
|
||||||
|
J2.b.a();
|
||||||
|
GLUtils.texSubImage2D(3553, 0, i6, 0, bitmap);
|
||||||
|
J2.b.a();
|
||||||
|
int i7 = bVar.f737l + 1;
|
||||||
|
bVar.f737l = i7;
|
||||||
|
bVar.f737l = i7 % width;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package E2;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import com.ve3nea.morse_expert.MainActivity;
|
||||||
|
import com.ve3nea.morse_expert.ScaleView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 照搬自 Morse Expert 1.15 (com.ve3nea.morse_expert), 类名保持混淆原名。
|
||||||
|
* R8 合并类手术: 只保留 case 1(瀑布图触摸选频率=应用逻辑),
|
||||||
|
* 其余 case(广告 P4 / appcompat 焦点 / 弹窗)为库代码, 已裁剪。
|
||||||
|
*/
|
||||||
|
public final class g implements View.OnTouchListener {
|
||||||
|
public final /* synthetic */ int f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ Object f411g;
|
||||||
|
|
||||||
|
public /* synthetic */ g(Object obj, int i4) {
|
||||||
|
this.f = i4;
|
||||||
|
this.f411g = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // android.view.View.OnTouchListener
|
||||||
|
public final boolean onTouch(View view, MotionEvent motionEvent) {
|
||||||
|
int i4 = this.f;
|
||||||
|
Object obj = this.f411g;
|
||||||
|
if (i4 == 1) {
|
||||||
|
MainActivity mainActivity = (MainActivity) obj;
|
||||||
|
if (motionEvent.getActionMasked() == 0 && mainActivity.f11037F != null) {
|
||||||
|
// smali 证实: p1 先被重赋值为 f11035D.h(ScaleView)再 getHeight;
|
||||||
|
// 公式 = round((f606z + ((scaleH-1-y)*f603B/scaleH)) / 0.128f)
|
||||||
|
ScaleView scaleView = (ScaleView) mainActivity.f11035D.f11889h;
|
||||||
|
float y3 = motionEvent.getY();
|
||||||
|
int round = Math.round((H2.b.f606z + ((((scaleView.getHeight() - 1) - y3) * H2.b.f603B) / scaleView.getHeight())) / 0.128f);
|
||||||
|
mainActivity.f11037F.f622r.set(round);
|
||||||
|
Log.i("Waterfall", String.format("Touch at %d Hz`", Integer.valueOf(round)));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package F2;
|
||||||
|
|
||||||
|
import com.rtbishop.look4sat.feature.cw.R;
|
||||||
|
import android.view.ActionMode;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import com.ve3nea.morse_expert.DecodedTextView;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class a implements ActionMode.Callback {
|
||||||
|
|
||||||
|
/* renamed from: a, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ DecodedTextView f564a;
|
||||||
|
|
||||||
|
public a(DecodedTextView decodedTextView) {
|
||||||
|
this.f564a = decodedTextView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // android.view.ActionMode.Callback
|
||||||
|
public final boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
|
||||||
|
int itemId = menuItem.getItemId();
|
||||||
|
DecodedTextView decodedTextView = this.f564a;
|
||||||
|
if (itemId != 73) {
|
||||||
|
if (itemId != 74) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
decodedTextView.s();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
decodedTextView.r();
|
||||||
|
actionMode.finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // android.view.ActionMode.Callback
|
||||||
|
public final boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
|
||||||
|
this.f564a.f11032n = true;
|
||||||
|
// 原: if (menu.findItem(R.id.shareText) == null) 检查(shareText 为原 APK 资源, 简化后始终添加)
|
||||||
|
menu.add(0, 74, 0, "Share").setIcon(R.drawable.ic_baseline_share_24);
|
||||||
|
menu.add(0, 73, 0, "Save").setIcon(R.drawable.ic_baseline_save_24);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // android.view.ActionMode.Callback
|
||||||
|
public final void onDestroyActionMode(ActionMode actionMode) {
|
||||||
|
this.f564a.f11032n = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // android.view.ActionMode.Callback
|
||||||
|
public final boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package H2;
|
||||||
|
|
||||||
|
import android.media.AudioRecord;
|
||||||
|
import android.util.Log;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class a implements Runnable {
|
||||||
|
public final int f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public final AudioRecord f596g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public final ExecutorService f597h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public final AtomicBoolean f598i;
|
||||||
|
|
||||||
|
/* renamed from: j, reason: collision with root package name */
|
||||||
|
public final short[] f599j;
|
||||||
|
|
||||||
|
/* renamed from: k, reason: collision with root package name */
|
||||||
|
public final float[] f600k;
|
||||||
|
|
||||||
|
/* renamed from: l, reason: collision with root package name */
|
||||||
|
public b f601l;
|
||||||
|
|
||||||
|
public a() {
|
||||||
|
int minBufferSize = AudioRecord.getMinBufferSize(8000, 16, 2);
|
||||||
|
int i4 = b.f604C;
|
||||||
|
this.f = i4;
|
||||||
|
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
this.f597h = newSingleThreadExecutor;
|
||||||
|
AtomicBoolean atomicBoolean = new AtomicBoolean(false);
|
||||||
|
this.f598i = atomicBoolean;
|
||||||
|
this.f599j = new short[i4];
|
||||||
|
this.f600k = new float[i4];
|
||||||
|
AudioRecord audioRecord = new AudioRecord(1, 8000, 16, 2, minBufferSize * 4);
|
||||||
|
this.f596g = audioRecord;
|
||||||
|
if (audioRecord.getState() == 1) {
|
||||||
|
audioRecord.startRecording();
|
||||||
|
if (audioRecord.getState() != 1) {
|
||||||
|
Log.e("AudioRecord", "cannot start");
|
||||||
|
}
|
||||||
|
atomicBoolean.set(true);
|
||||||
|
newSingleThreadExecutor.execute(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.e("AudioInput", "Failed to initialize");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // java.lang.Runnable
|
||||||
|
public final void run() {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
if (this.f598i.get()) {
|
||||||
|
float[] fArr = this.f600k;
|
||||||
|
AudioRecord audioRecord = this.f596g;
|
||||||
|
short[] sArr = this.f599j;
|
||||||
|
int i4 = this.f;
|
||||||
|
if (audioRecord.read(sArr, 0, i4) == i4) {
|
||||||
|
for (int i5 = 0; i5 < i4; i5++) {
|
||||||
|
fArr[i5] = sArr[i5] / 32768.0f;
|
||||||
|
}
|
||||||
|
b bVar = this.f601l;
|
||||||
|
if (bVar != null) {
|
||||||
|
bVar.a(fArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TimeUnit.MILLISECONDS.sleep(100L);
|
||||||
|
}
|
||||||
|
} catch (Exception e4) {
|
||||||
|
Log.e("Audio thread", "Exception: ", e4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,582 @@
|
|||||||
|
package H2;
|
||||||
|
|
||||||
|
import B.RunnableC0001b;
|
||||||
|
import D.n;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.media.MediaScannerConnection;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.format.DateFormat;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
|
import android.util.Log;
|
||||||
|
import com.ve3nea.morse_expert.MainActivity;
|
||||||
|
import com.ve3nea.morse_expert.WaterfallSurfaceView;
|
||||||
|
import i3.d;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import k3.s;
|
||||||
|
import pas.decoder;
|
||||||
|
import pas.nativedecoder;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class b {
|
||||||
|
|
||||||
|
/* renamed from: A, reason: collision with root package name */
|
||||||
|
public static final int f602A;
|
||||||
|
|
||||||
|
/* renamed from: B, reason: collision with root package name */
|
||||||
|
public static final int f603B;
|
||||||
|
|
||||||
|
/* renamed from: C, reason: collision with root package name */
|
||||||
|
public static final int f604C;
|
||||||
|
|
||||||
|
/* renamed from: D, reason: collision with root package name */
|
||||||
|
public static final d f605D;
|
||||||
|
|
||||||
|
/* renamed from: z, reason: collision with root package name */
|
||||||
|
public static final int f606z;
|
||||||
|
|
||||||
|
/* renamed from: a, reason: collision with root package name */
|
||||||
|
public final float[] f607a;
|
||||||
|
|
||||||
|
/* renamed from: b, reason: collision with root package name */
|
||||||
|
public final float[] f608b;
|
||||||
|
public final float[] c;
|
||||||
|
|
||||||
|
/* renamed from: d, reason: collision with root package name */
|
||||||
|
public final float[] f609d;
|
||||||
|
|
||||||
|
/* renamed from: e, reason: collision with root package name */
|
||||||
|
public final nativedecoder.TNativeDecoder f610e;
|
||||||
|
public int f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public int f611g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public int f612h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public decoder.TDecoderState f613i;
|
||||||
|
|
||||||
|
/* renamed from: j, reason: collision with root package name */
|
||||||
|
public String f614j;
|
||||||
|
|
||||||
|
/* renamed from: k, reason: collision with root package name */
|
||||||
|
public final c f615k;
|
||||||
|
|
||||||
|
/* renamed from: l, reason: collision with root package name */
|
||||||
|
public int f616l;
|
||||||
|
|
||||||
|
/* renamed from: m, reason: collision with root package name */
|
||||||
|
public final I2.b f617m;
|
||||||
|
|
||||||
|
/* renamed from: n, reason: collision with root package name */
|
||||||
|
public final Handler f618n;
|
||||||
|
|
||||||
|
/* renamed from: o, reason: collision with root package name */
|
||||||
|
public WaterfallSurfaceView f619o;
|
||||||
|
|
||||||
|
/* renamed from: p, reason: collision with root package name */
|
||||||
|
public MainActivity f620p;
|
||||||
|
|
||||||
|
/* renamed from: q, reason: collision with root package name */
|
||||||
|
public MainActivity f621q;
|
||||||
|
|
||||||
|
/* renamed from: r, reason: collision with root package name */
|
||||||
|
public final AtomicInteger f622r;
|
||||||
|
|
||||||
|
/* renamed from: s, reason: collision with root package name */
|
||||||
|
public final AtomicBoolean f623s;
|
||||||
|
|
||||||
|
/* renamed from: t, reason: collision with root package name */
|
||||||
|
public final Context f624t;
|
||||||
|
|
||||||
|
/* renamed from: u, reason: collision with root package name */
|
||||||
|
public int f625u;
|
||||||
|
|
||||||
|
/* renamed from: v, reason: collision with root package name */
|
||||||
|
public final AtomicBoolean f626v;
|
||||||
|
|
||||||
|
/* renamed from: w, reason: collision with root package name */
|
||||||
|
public DataOutputStream f627w;
|
||||||
|
|
||||||
|
/* renamed from: x, reason: collision with root package name */
|
||||||
|
public int f628x;
|
||||||
|
|
||||||
|
/* renamed from: y, reason: collision with root package name */
|
||||||
|
public String f629y;
|
||||||
|
|
||||||
|
/* JADX WARN: Removed duplicated region for block: B:14:0x012d */
|
||||||
|
/* JADX WARN: Type inference failed for: r0v4, types: [i3.d, java.lang.Object] */
|
||||||
|
static {
|
||||||
|
boolean z3;
|
||||||
|
float f;
|
||||||
|
float f4;
|
||||||
|
float f5;
|
||||||
|
int round = Math.round(25.6f);
|
||||||
|
f606z = round;
|
||||||
|
int round2 = Math.round(153.6f);
|
||||||
|
f602A = round2;
|
||||||
|
f603B = round2 - round;
|
||||||
|
f604C = Math.round(85.333336f);
|
||||||
|
i3.d obj = new i3.d();
|
||||||
|
if (2048 > 1073741824) {
|
||||||
|
z3 = true;
|
||||||
|
} else {
|
||||||
|
z3 = false;
|
||||||
|
}
|
||||||
|
obj.f11709l = z3;
|
||||||
|
int i4 = (int) 1024;
|
||||||
|
obj.f11700a = i4;
|
||||||
|
obj.f11701b = 1024L;
|
||||||
|
if (!z3) {
|
||||||
|
obj.f11708k = 1;
|
||||||
|
int[] iArr = new int[((int) g3.c.a((1 << (((int) (g3.c.d(((float) 1024) + 0.5f) / g3.c.d(2.0d))) / 2)) + 2)) + 2];
|
||||||
|
obj.c = iArr;
|
||||||
|
float[] fArr = new float[i4];
|
||||||
|
obj.f11703e = fArr;
|
||||||
|
int i5 = (i4 * 2) >> 2;
|
||||||
|
obj.f11704g = i5;
|
||||||
|
iArr[0] = i5;
|
||||||
|
iArr[1] = 1;
|
||||||
|
if (i5 > 2) {
|
||||||
|
int i6 = i5 >> 1;
|
||||||
|
float f6 = 0.7853982f / i6;
|
||||||
|
float f7 = 2.0f * f6;
|
||||||
|
float b4 = (float) g3.c.b(i6 * f6); // r9 = i6 (smali)
|
||||||
|
fArr[0] = 1.0f;
|
||||||
|
fArr[1] = b4;
|
||||||
|
if (i6 == 4) {
|
||||||
|
double d4 = f7;
|
||||||
|
f4 = 0.5f;
|
||||||
|
fArr[2] = (float) g3.c.b(d4);
|
||||||
|
fArr[3] = (float) g3.c.f(d4);
|
||||||
|
} else {
|
||||||
|
f4 = 0.5f;
|
||||||
|
if (i6 > 4) {
|
||||||
|
iArr[2] = 0;
|
||||||
|
iArr[3] = 16;
|
||||||
|
int i7 = i5;
|
||||||
|
int i8 = 2;
|
||||||
|
f5 = 1.0f;
|
||||||
|
while (i7 > 32) {
|
||||||
|
int i9 = i8 << 1;
|
||||||
|
int i10 = i8 << 4;
|
||||||
|
for (int i11 = i8; i11 < i9; i11++) {
|
||||||
|
int i12 = iArr[i11] << 2;
|
||||||
|
iArr[i8 + i11] = i12;
|
||||||
|
iArr[i9 + i11] = i12 + i10;
|
||||||
|
}
|
||||||
|
i7 >>= 2;
|
||||||
|
i8 = i9;
|
||||||
|
}
|
||||||
|
fArr[2] = 0.5f / ((float) g3.c.b(f7));
|
||||||
|
fArr[3] = 0.5f / ((float) g3.c.b(6.0f * f6));
|
||||||
|
for (int i13 = 4; i13 < i6; i13 += 4) {
|
||||||
|
float f8 = i13 * f6;
|
||||||
|
double d5 = f8;
|
||||||
|
fArr[i13] = (float) g3.c.b(d5);
|
||||||
|
fArr[i13 + 1] = (float) g3.c.f(d5);
|
||||||
|
double d6 = 3.0f * f8;
|
||||||
|
fArr[i13 + 2] = (float) g3.c.b(d6);
|
||||||
|
fArr[i13 + 3] = -((float) g3.c.f(d6));
|
||||||
|
}
|
||||||
|
int i14 = 0;
|
||||||
|
while (i6 > 2) {
|
||||||
|
int i15 = i14 + i6;
|
||||||
|
i6 >>= 1;
|
||||||
|
fArr[i15] = f5;
|
||||||
|
fArr[i15 + 1] = b4;
|
||||||
|
if (i6 == 4) {
|
||||||
|
float f9 = fArr[i14 + 4];
|
||||||
|
float f10 = fArr[i14 + 5];
|
||||||
|
fArr[i15 + 2] = f9;
|
||||||
|
fArr[i15 + 3] = f10;
|
||||||
|
} else if (i6 > 4) {
|
||||||
|
float f11 = fArr[i14 + 4];
|
||||||
|
float f12 = fArr[i14 + 6];
|
||||||
|
fArr[i15 + 2] = f4 / f11;
|
||||||
|
fArr[i15 + 3] = f4 / f12;
|
||||||
|
for (int i16 = 4; i16 < i6; i16 += 4) {
|
||||||
|
int i17 = (i16 * 2) + i14;
|
||||||
|
int i18 = i15 + i16;
|
||||||
|
float f13 = fArr[i17];
|
||||||
|
float f14 = fArr[i17 + 1];
|
||||||
|
float f15 = fArr[i17 + 2];
|
||||||
|
float f16 = fArr[i17 + 3];
|
||||||
|
fArr[i18] = f13;
|
||||||
|
fArr[i18 + 1] = f14;
|
||||||
|
fArr[i18 + 2] = f15;
|
||||||
|
fArr[i18 + 3] = f16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i14 = i15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f5 = 1.0f;
|
||||||
|
int i142 = 0;
|
||||||
|
while (i6 > 2) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int i19 = i4 >> 2;
|
||||||
|
obj.f11706i = i19;
|
||||||
|
iArr[1] = i19;
|
||||||
|
if (i19 > 1) {
|
||||||
|
int i20 = i19 >> 1;
|
||||||
|
float f17 = 0.7853982f / i20;
|
||||||
|
float b5 = (float) g3.c.b(i20 * f17); // r6 = i20 (smali)
|
||||||
|
fArr[i5] = b5;
|
||||||
|
fArr[i5 + i20] = b5 * 0.5f;
|
||||||
|
for (int i21 = 1; i21 < i20; i21++) {
|
||||||
|
double d7 = i21 * f17;
|
||||||
|
fArr[i5 + i21] = ((float) g3.c.b(d7)) * 0.5f;
|
||||||
|
fArr[(i5 + i19) - i21] = ((float) g3.c.f(d7)) * 0.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
obj.f11708k = 1;
|
||||||
|
s sVar = new s(((long) g3.c.a((1 << ((int) (((long) (g3.c.d(((float) 1024) + 0.5f) / g3.c.d(2.0d))) / 2))) + 2)) + 2, true);
|
||||||
|
obj.f11702d = sVar;
|
||||||
|
k3.d dVar = new k3.d(1024L, true);
|
||||||
|
obj.f = dVar;
|
||||||
|
obj.f11705h = 512L;
|
||||||
|
sVar.c(0L, 512L);
|
||||||
|
sVar.c(1L, 1L);
|
||||||
|
if (512 > 2) {
|
||||||
|
long j4 = 512 >> 1;
|
||||||
|
float f18 = 0.7853982f / ((float) j4);
|
||||||
|
float f19 = f18 * 2.0f;
|
||||||
|
float b6 = (float) g3.c.b(j4 * f18); // r14 = j4 (smali 同模式)
|
||||||
|
dVar.c(0L, 1.0f);
|
||||||
|
dVar.c(1L, b6);
|
||||||
|
if (j4 == 4) {
|
||||||
|
f = 0.5f;
|
||||||
|
double d8 = f19;
|
||||||
|
dVar.c(2L, (float) g3.c.b(d8));
|
||||||
|
dVar.c(3L, (float) g3.c.f(d8));
|
||||||
|
} else {
|
||||||
|
f = 0.5f;
|
||||||
|
if (j4 > 4) {
|
||||||
|
sVar.c(2L, 0L);
|
||||||
|
sVar.c(3L, 16L);
|
||||||
|
long j5 = 2;
|
||||||
|
long j6 = 512;
|
||||||
|
while (j6 > 32) {
|
||||||
|
long j7 = j5 << 1;
|
||||||
|
long j8 = j5 << 4;
|
||||||
|
long j9 = j5;
|
||||||
|
while (j9 < j7) {
|
||||||
|
long b7 = sVar.b(j9) << 2;
|
||||||
|
sVar.c(j5 + j9, b7);
|
||||||
|
sVar.c(j7 + j9, b7 + j8);
|
||||||
|
j9++;
|
||||||
|
f19 = f19;
|
||||||
|
}
|
||||||
|
j6 >>= 2;
|
||||||
|
j5 = j7;
|
||||||
|
}
|
||||||
|
dVar.c(2L, 0.5f / ((float) g3.c.b(f19)));
|
||||||
|
dVar.c(3L, 0.5f / ((float) g3.c.b(6.0f * f18)));
|
||||||
|
long j10 = 4;
|
||||||
|
while (j10 < j4) {
|
||||||
|
float f20 = ((float) j10) * f18;
|
||||||
|
double d9 = f20;
|
||||||
|
dVar.c(j10, (float) g3.c.b(d9));
|
||||||
|
dVar.c(j10 + 1, (float) g3.c.f(d9));
|
||||||
|
double d10 = 3.0f * f20;
|
||||||
|
long j11 = j10;
|
||||||
|
dVar.c(j10 + 2, (float) g3.c.b(d10));
|
||||||
|
dVar.c(j11 + 3, -((float) g3.c.f(d10)));
|
||||||
|
j10 = j11 + 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long j12 = 0;
|
||||||
|
long j13 = 2;
|
||||||
|
while (j4 > j13) {
|
||||||
|
long j14 = j12 + j4;
|
||||||
|
j4 >>= 1;
|
||||||
|
dVar.c(j14, 1.0f);
|
||||||
|
dVar.c(j14 + 1, b6);
|
||||||
|
if (j4 == 4) {
|
||||||
|
float b8 = dVar.b(j12 + 4);
|
||||||
|
float b9 = dVar.b(j12 + 5);
|
||||||
|
j13 = 2;
|
||||||
|
dVar.c(j14 + 2, b8);
|
||||||
|
dVar.c(j14 + 3, b9);
|
||||||
|
} else {
|
||||||
|
j13 = 2;
|
||||||
|
if (j4 > 4) {
|
||||||
|
float b10 = dVar.b(j12 + 4);
|
||||||
|
float b11 = dVar.b(6 + j12);
|
||||||
|
dVar.c(j14 + 2, f / b10);
|
||||||
|
dVar.c(j14 + 3, f / b11);
|
||||||
|
long j15 = 4;
|
||||||
|
while (j15 < j4) {
|
||||||
|
long j16 = (j15 * 2) + j12;
|
||||||
|
float f21 = b6;
|
||||||
|
long j17 = j14 + j15;
|
||||||
|
long j18 = j12;
|
||||||
|
float b12 = dVar.b(j16);
|
||||||
|
long j19 = j14;
|
||||||
|
float b13 = dVar.b(j16 + 1);
|
||||||
|
float b14 = dVar.b(j16 + 2);
|
||||||
|
float b15 = dVar.b(j16 + 3);
|
||||||
|
dVar.c(j17, b12);
|
||||||
|
dVar.c(j17 + 1, b13);
|
||||||
|
dVar.c(j17 + 2, b14);
|
||||||
|
dVar.c(j17 + 3, b15);
|
||||||
|
j15 += 4;
|
||||||
|
b6 = f21;
|
||||||
|
j12 = j18;
|
||||||
|
j14 = j19;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b6 = b6;
|
||||||
|
j12 = j14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long j20 = 256;
|
||||||
|
obj.f11707j = 256L;
|
||||||
|
long j21 = 1;
|
||||||
|
sVar.c(1L, 256L);
|
||||||
|
if (256 > 1) {
|
||||||
|
long j22 = 256 >> 1;
|
||||||
|
float f22 = 0.7853982f / ((float) j22);
|
||||||
|
long j23 = 512;
|
||||||
|
dVar.c(512L, (float) g3.c.b(j22 * f22)); // r1 = j22 (smali)
|
||||||
|
dVar.c(512 + j22, dVar.b(512L) * 0.5f);
|
||||||
|
long j24 = 1;
|
||||||
|
while (j24 < j22) {
|
||||||
|
long j25 = j20;
|
||||||
|
long j26 = j21;
|
||||||
|
double d11 = ((float) j24) * f22;
|
||||||
|
long j27 = j23;
|
||||||
|
dVar.c(j23 + j24, ((float) g3.c.b(d11)) * 0.5f);
|
||||||
|
dVar.c((j27 + j25) - j24, ((float) g3.c.f(d11)) * 0.5f);
|
||||||
|
j24 += j26;
|
||||||
|
j20 = j25;
|
||||||
|
j21 = j26;
|
||||||
|
j23 = j27;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f605D = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public b(Context context) {
|
||||||
|
float[] fArr = new float[1024];
|
||||||
|
fArr[512] = 1.0f;
|
||||||
|
for (int i4 = 1; i4 < 512; i4++) {
|
||||||
|
double d4 = (i4 * 6.283185307179586d) / 1024;
|
||||||
|
float cos = (float) ((Math.cos(d4 * 2.0d) * 0.08d) + (0.42d - (Math.cos(d4) * 0.5d)));
|
||||||
|
fArr[i4] = cos;
|
||||||
|
double d5 = 0.003125f * 6.283185307179586d * (i4 - 512);
|
||||||
|
float sin = (float) ((Math.sin(d5) / d5) * cos);
|
||||||
|
fArr[i4] = sin;
|
||||||
|
fArr[1024 - i4] = sin;
|
||||||
|
}
|
||||||
|
this.f607a = fArr;
|
||||||
|
this.f608b = new float[1024];
|
||||||
|
this.c = new float[1024];
|
||||||
|
this.f609d = new float[f603B];
|
||||||
|
this.f610e = nativedecoder.TNativeDecoder.Create();
|
||||||
|
this.f614j = "";
|
||||||
|
this.f615k = new c(4000, 0);
|
||||||
|
I2.b bVar = new I2.b();
|
||||||
|
this.f617m = bVar;
|
||||||
|
this.f618n = new Handler(Looper.getMainLooper());
|
||||||
|
this.f622r = new AtomicInteger();
|
||||||
|
this.f623s = new AtomicBoolean(false);
|
||||||
|
this.f625u = 0;
|
||||||
|
this.f626v = new AtomicBoolean(false);
|
||||||
|
this.f628x = 0;
|
||||||
|
this.f624t = context;
|
||||||
|
bVar.b(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* JADX WARN: Removed duplicated region for block: B:57:0x01e2 */
|
||||||
|
/* JADX WARN: Removed duplicated region for block: B:63:0x020a A[LOOP:6: B:62:0x0208->B:63:0x020a, LOOP_END] */
|
||||||
|
/* JADX WARN: Removed duplicated region for block: B:66:0x0216 A[LOOP:7: B:65:0x0214->B:66:0x0216, LOOP_END] */
|
||||||
|
/* JADX WARN: Removed duplicated region for block: B:70:0x0232 */
|
||||||
|
/* JADX WARN: Removed duplicated region for block: B:73:? A[RETURN, SYNTHETIC] */
|
||||||
|
/*
|
||||||
|
Code decompiled incorrectly, please refer to instructions dump.
|
||||||
|
*/
|
||||||
|
public final void a(float[] fArr) {
|
||||||
|
c cVar;
|
||||||
|
float[] fArr2;
|
||||||
|
float[] fArr3;
|
||||||
|
b bVar;
|
||||||
|
Exception exc;
|
||||||
|
int i4;
|
||||||
|
float f;
|
||||||
|
int i5;
|
||||||
|
WaterfallSurfaceView waterfallSurfaceView;
|
||||||
|
int length = fArr.length;
|
||||||
|
int i6 = 0;
|
||||||
|
while (true) {
|
||||||
|
cVar = this.f615k;
|
||||||
|
if (i6 >= length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cVar.c(Math.abs(fArr[i6]));
|
||||||
|
i6++;
|
||||||
|
}
|
||||||
|
this.f616l = Math.round(((float) Math.log10(Math.max(-0.5d, cVar.f633e))) * 20.0f);
|
||||||
|
int i7 = f604C;
|
||||||
|
int i8 = 1024 - i7;
|
||||||
|
float[] fArr4 = this.f608b;
|
||||||
|
System.arraycopy(fArr4, i7, fArr4, 0, i8);
|
||||||
|
System.arraycopy(fArr, 0, fArr4, i8, i7);
|
||||||
|
int i9 = 0;
|
||||||
|
while (true) {
|
||||||
|
fArr2 = this.c;
|
||||||
|
if (i9 >= 1024) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fArr2[i9] = fArr4[i9] * this.f607a[i9];
|
||||||
|
i9++;
|
||||||
|
}
|
||||||
|
f605D.k(fArr2, 0);
|
||||||
|
int i10 = f606z;
|
||||||
|
int i11 = i10;
|
||||||
|
while (true) {
|
||||||
|
int i12 = f602A;
|
||||||
|
float f4 = 0.0f;
|
||||||
|
fArr3 = this.f609d;
|
||||||
|
if (i11 < i12) {
|
||||||
|
int i13 = i11 * 2;
|
||||||
|
float f5 = fArr2[i13];
|
||||||
|
float f6 = fArr2[i13 + 1];
|
||||||
|
float f7 = ((f6 * f6) + (f5 * f5)) * 0.001f;
|
||||||
|
if (!Float.isNaN(f7)) {
|
||||||
|
f4 = f7;
|
||||||
|
}
|
||||||
|
fArr3[i11 - i10] = f4;
|
||||||
|
i11++;
|
||||||
|
} else {
|
||||||
|
// smali: 循环内无 try; 录音写入 try 在循环后(见下)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataOutputStream dataOutputStream = this.f627w;
|
||||||
|
Context context = this.f624t;
|
||||||
|
AtomicBoolean atomicBoolean = this.f626v;
|
||||||
|
try { // smali try_start_0: 录音写入整块(原 jadx 误把 try 放 break 处)
|
||||||
|
if (dataOutputStream == null && atomicBoolean.get()) {
|
||||||
|
File file = new File(context.getExternalFilesDir(null), ((String) DateFormat.format("yyyy-MM-dd_HH_mm_ss", new Date())) + "_spectra.bin");
|
||||||
|
this.f629y = file.getAbsoluteFile().toString();
|
||||||
|
this.f627w = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file, false)));
|
||||||
|
this.f628x = 0;
|
||||||
|
this.f614j = " Recording...";
|
||||||
|
}
|
||||||
|
if (this.f627w != null) {
|
||||||
|
for (float f8 : fArr3) {
|
||||||
|
this.f627w.writeFloat(f8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (atomicBoolean.get()) {
|
||||||
|
int i14 = this.f628x + 1;
|
||||||
|
this.f628x = i14;
|
||||||
|
if (i14 >= 2048) {
|
||||||
|
atomicBoolean.set(false);
|
||||||
|
this.f627w.close();
|
||||||
|
this.f627w = null;
|
||||||
|
this.f614j = "";
|
||||||
|
MediaScannerConnection.scanFile(context, new String[]{this.f629y}, new String[]{"application/x-binary"}, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e4) {
|
||||||
|
Log.e("File write", "Exception: ", e4);
|
||||||
|
}
|
||||||
|
nativedecoder.TNativeDecoder tNativeDecoder = this.f610e;
|
||||||
|
try {
|
||||||
|
this.f613i = tNativeDecoder.ProcessSpectrum(fArr3);
|
||||||
|
int i15 = this.f625u + 1;
|
||||||
|
this.f625u = i15;
|
||||||
|
Handler handler = this.f618n;
|
||||||
|
if (i15 == 15) {
|
||||||
|
try {
|
||||||
|
this.f625u = 0;
|
||||||
|
this.f = tNativeDecoder.getPitch();
|
||||||
|
this.f611g = tNativeDecoder.getSnr();
|
||||||
|
this.f612h = tNativeDecoder.getWpm();
|
||||||
|
handler.post(new B0.b(this, this.f614j, new int[]{this.f616l, this.f, this.f611g, this.f612h, tNativeDecoder.getSignalCount(), this.f613i.Value}));
|
||||||
|
AtomicInteger atomicInteger = this.f622r;
|
||||||
|
int i16 = atomicInteger.get();
|
||||||
|
if (i16 != 0) {
|
||||||
|
tNativeDecoder.LockToPitch(i16);
|
||||||
|
atomicInteger.set(0);
|
||||||
|
}
|
||||||
|
tNativeDecoder.setHamMode(this.f623s.get());
|
||||||
|
} catch (Exception e5) {
|
||||||
|
// jadx 幻觉区(空循环/空 if 为 catch 错位还原)已裁剪; 原 smali: 记录异常后跳回正常流程尾部
|
||||||
|
exc = e5;
|
||||||
|
bVar = this;
|
||||||
|
Log.e("Audio Processor", "Exception", exc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int charCount = tNativeDecoder.getCharCount();
|
||||||
|
if (charCount > 0) {
|
||||||
|
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
|
||||||
|
int i18 = 0;
|
||||||
|
while (i18 < charCount) {
|
||||||
|
decoder.TCharInfo GetChar = tNativeDecoder.GetChar(i18);
|
||||||
|
spannableStringBuilder.append(GetChar.getChar());
|
||||||
|
ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(this.f617m.a(GetChar));
|
||||||
|
int i19 = i18 + 1;
|
||||||
|
spannableStringBuilder.setSpan(foregroundColorSpan, i18, i19, 33);
|
||||||
|
i18 = i19;
|
||||||
|
}
|
||||||
|
bVar = this;
|
||||||
|
try {
|
||||||
|
handler.post(new RunnableC0001b(bVar, spannableStringBuilder, tNativeDecoder.getDeleteCount(), 2, false));
|
||||||
|
} catch (Exception e6) {
|
||||||
|
// jadx 幻觉区已裁剪(同 catch e5); 原 smali: 记录异常后跳回正常流程尾部
|
||||||
|
exc = e6;
|
||||||
|
Log.e("Audio Processor", "Exception", exc);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bVar = this;
|
||||||
|
}
|
||||||
|
} catch (Exception e7) {
|
||||||
|
// jadx 幻觉(e 变量)已裁剪
|
||||||
|
bVar = this;
|
||||||
|
}
|
||||||
|
i4 = f603B;
|
||||||
|
int i1722 = i4 / 2;
|
||||||
|
float[] copyOfRange22 = Arrays.copyOfRange(fArr3, i1722 - 20, i1722 + 20);
|
||||||
|
Arrays.sort(copyOfRange22);
|
||||||
|
f = copyOfRange22[20];
|
||||||
|
if (f > 0.0f) {
|
||||||
|
for (int i20 = 0; i20 < i4; i20++) {
|
||||||
|
fArr3[i20] = fArr3[i20] / f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float[] fArr522 = (float[]) fArr3.clone();
|
||||||
|
new c(12, 1).a(fArr522);
|
||||||
|
new c(12, 0).a(fArr522);
|
||||||
|
for (i5 = 0; i5 < i4; i5++) {
|
||||||
|
fArr3[i5] = fArr3[i5] - fArr522[i5];
|
||||||
|
}
|
||||||
|
for (int i21 = 0; i21 < i4; i21++) {
|
||||||
|
fArr3[i21] = ((float) Math.log10(Math.max(-0.5d, fArr3[i21]) + 1.0d)) * 10.0f;
|
||||||
|
}
|
||||||
|
waterfallSurfaceView = bVar.f619o;
|
||||||
|
if (waterfallSurfaceView != null) {
|
||||||
|
waterfallSurfaceView.queueEvent(new n(waterfallSurfaceView, 1, (float[]) fArr3.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package H2;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class c {
|
||||||
|
|
||||||
|
/* renamed from: a, reason: collision with root package name */
|
||||||
|
public final int f630a;
|
||||||
|
|
||||||
|
/* renamed from: b, reason: collision with root package name */
|
||||||
|
public float[] f631b;
|
||||||
|
public int c;
|
||||||
|
|
||||||
|
/* renamed from: d, reason: collision with root package name */
|
||||||
|
public float f632d;
|
||||||
|
|
||||||
|
/* renamed from: e, reason: collision with root package name */
|
||||||
|
public float f633e;
|
||||||
|
public final /* synthetic */ int f;
|
||||||
|
|
||||||
|
public c(int i4, int i5) {
|
||||||
|
this.f = i5;
|
||||||
|
this.f630a = i4 - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void a(float[] fArr) {
|
||||||
|
int i4 = this.f630a / 2;
|
||||||
|
int length = fArr.length;
|
||||||
|
if (length > i4) {
|
||||||
|
this.f631b = null;
|
||||||
|
for (int i5 = 0; i5 < i4; i5++) {
|
||||||
|
c(fArr[i5]);
|
||||||
|
}
|
||||||
|
for (int i6 = i4; i6 < length; i6++) {
|
||||||
|
fArr[i6 - i4] = c(fArr[i6]);
|
||||||
|
}
|
||||||
|
for (int i7 = length; i7 < length + i4; i7++) {
|
||||||
|
fArr[i7 - i4] = c(fArr[length - 1]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Sliding Filter error: input too short");
|
||||||
|
}
|
||||||
|
|
||||||
|
public final float b(float f, float f4) {
|
||||||
|
switch (this.f) {
|
||||||
|
case 0:
|
||||||
|
return Math.max(f, f4);
|
||||||
|
default:
|
||||||
|
return Math.min(f, f4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final float c(float f) {
|
||||||
|
float[] fArr = this.f631b;
|
||||||
|
int i4 = this.f630a;
|
||||||
|
if (fArr == null) {
|
||||||
|
this.f631b = new float[i4];
|
||||||
|
for (int i5 = 0; i5 < i4; i5++) {
|
||||||
|
this.f631b[i5] = f;
|
||||||
|
}
|
||||||
|
this.f632d = f;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
float b4 = b(this.f632d, f);
|
||||||
|
this.f632d = b4;
|
||||||
|
this.f633e = b(this.f631b[this.c], b4);
|
||||||
|
float[] fArr2 = this.f631b;
|
||||||
|
int i6 = this.c;
|
||||||
|
int i7 = i6 + 1;
|
||||||
|
this.c = i7;
|
||||||
|
fArr2[i6] = f;
|
||||||
|
if (i7 == i4) {
|
||||||
|
int length = fArr2.length;
|
||||||
|
while (true) {
|
||||||
|
this.c = length - 1;
|
||||||
|
int i8 = this.c;
|
||||||
|
if (i8 <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
float[] fArr3 = this.f631b;
|
||||||
|
fArr3[i8 - 1] = b(fArr3[i8 - 1], fArr3[i8]);
|
||||||
|
length = this.c;
|
||||||
|
}
|
||||||
|
this.c = 0;
|
||||||
|
this.f632d = f;
|
||||||
|
}
|
||||||
|
return this.f633e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package I0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 照搬自 Morse Expert 1.15 I0.d, 类名保持混淆原名。
|
||||||
|
* R8 合并类手术: 只保留 (i3.d, int, int, float[]) FFT 任务构造;
|
||||||
|
* run() 按 smali 原样还原: R8 已将任务体优化为死代码(aget 后 throw null,
|
||||||
|
* 永不执行), 保留原语义(行为与原 APK 完全一致)。
|
||||||
|
* SystemForegroundService(work 库)case 已裁剪。
|
||||||
|
*/
|
||||||
|
public final class d implements Runnable {
|
||||||
|
public final /* synthetic */ int f = 0;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ int f653g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ int f654h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ Object f655i;
|
||||||
|
|
||||||
|
/* renamed from: j, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ Object f656j;
|
||||||
|
|
||||||
|
public d(i3.d dVar, int i4, int i5, float[] fArr) {
|
||||||
|
this.f656j = dVar;
|
||||||
|
this.f653g = i4;
|
||||||
|
this.f654h = i5;
|
||||||
|
this.f655i = fArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // java.lang.Runnable
|
||||||
|
public final void run() {
|
||||||
|
int i6 = this.f654h;
|
||||||
|
int i7 = this.f653g;
|
||||||
|
if (i7 >= i6) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float f = ((float[]) this.f655i)[i7 * 2];
|
||||||
|
// R8 原代码: aget 后 throw null(死代码, 永不执行) —— 照搬语义, 不执行任何计算
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package I2;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import pas.decoder;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class b {
|
||||||
|
|
||||||
|
/* renamed from: b, reason: collision with root package name */
|
||||||
|
public static final String[] f663b = {"bg_color", "text_color", "text_color_weak", "call_color", "call_color_weak", "cq_color", "cq_color_weak", "rst_color", "rst_color_weak"};
|
||||||
|
public static final String[] c = {"Background color", "Text color", "Text color, weak", "Callsign color", "Callsign color, weak", "CQ color", "CQ color, weak", "RST color", "RST color, weak"};
|
||||||
|
|
||||||
|
/* renamed from: d, reason: collision with root package name */
|
||||||
|
public static final int[] f664d = {-3084048, -16777216, -5592406, -65536, -30584, -16776961, -7829249, -65281, -30465};
|
||||||
|
|
||||||
|
/* renamed from: a, reason: collision with root package name */
|
||||||
|
public final int[] f665a = (int[]) f664d.clone();
|
||||||
|
|
||||||
|
public final int a(decoder.TCharInfo tCharInfo) {
|
||||||
|
boolean lowSnr = tCharInfo.getLowSnr();
|
||||||
|
int[] iArr = this.f665a;
|
||||||
|
if (lowSnr) {
|
||||||
|
int i4 = tCharInfo.getWordType().Value;
|
||||||
|
if (i4 != 0) {
|
||||||
|
if (i4 != 1) {
|
||||||
|
if (i4 != 2) {
|
||||||
|
if (i4 != 3) {
|
||||||
|
if (i4 != 4) {
|
||||||
|
if (i4 == 5) {
|
||||||
|
return iArr[4];
|
||||||
|
}
|
||||||
|
return -16777216;
|
||||||
|
}
|
||||||
|
return iArr[2];
|
||||||
|
}
|
||||||
|
return iArr[6];
|
||||||
|
}
|
||||||
|
return iArr[8];
|
||||||
|
}
|
||||||
|
return iArr[2];
|
||||||
|
}
|
||||||
|
return iArr[2];
|
||||||
|
}
|
||||||
|
int i5 = tCharInfo.getWordType().Value;
|
||||||
|
if (i5 != 0) {
|
||||||
|
if (i5 != 1) {
|
||||||
|
if (i5 != 2) {
|
||||||
|
if (i5 != 3) {
|
||||||
|
if (i5 != 4) {
|
||||||
|
if (i5 != 5) {
|
||||||
|
return -16777216;
|
||||||
|
}
|
||||||
|
return iArr[3];
|
||||||
|
}
|
||||||
|
return iArr[1];
|
||||||
|
}
|
||||||
|
return iArr[5];
|
||||||
|
}
|
||||||
|
return iArr[7];
|
||||||
|
}
|
||||||
|
return iArr[1];
|
||||||
|
}
|
||||||
|
return iArr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void b(Context context) {
|
||||||
|
SharedPreferences sharedPreferences = context.getSharedPreferences(context.getPackageName() + "_preferences", 0); // 原 c0.x.a(context)
|
||||||
|
for (int i4 = 0; i4 < 9; i4++) {
|
||||||
|
this.f665a[i4] = sharedPreferences.getInt(f663b[i4], f664d[i4]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package J2;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class a {
|
||||||
|
|
||||||
|
/* renamed from: b, reason: collision with root package name */
|
||||||
|
public static final int[] f726b = {16, 17749, 65535, 16776960, 16711680};
|
||||||
|
|
||||||
|
/* renamed from: a, reason: collision with root package name */
|
||||||
|
public final int[] f727a;
|
||||||
|
|
||||||
|
public a() {
|
||||||
|
int[] iArr = new int[256];
|
||||||
|
this.f727a = iArr;
|
||||||
|
int i4 = 4;
|
||||||
|
float f = 255.0f / 4;
|
||||||
|
int[] iArr2 = f726b;
|
||||||
|
int i5 = 0;
|
||||||
|
int i6 = -16777216;
|
||||||
|
iArr[0] = iArr2[0] | (-16777216);
|
||||||
|
int i7 = 1;
|
||||||
|
while (i7 <= i4) {
|
||||||
|
int round = Math.round(i7 * f);
|
||||||
|
iArr[round] = iArr2[i7] | i6;
|
||||||
|
int i8 = round - i5;
|
||||||
|
int i9 = iArr[i5];
|
||||||
|
int i10 = (i9 >> 16) & 255;
|
||||||
|
int i11 = (i9 >> 8) & 255;
|
||||||
|
int i12 = i9 & 255;
|
||||||
|
float f4 = i8;
|
||||||
|
// r10 = iArr2[i7] (下一个锚点色, smali: aget v10, v4, v8)
|
||||||
|
float f5 = (((iArr2[i7] >> 16) & 255) - i10) / f4;
|
||||||
|
float f6 = (((iArr2[i7] >> 8) & 255) - i11) / f4;
|
||||||
|
float f7 = ((iArr2[i7] & 255) - i12) / f4;
|
||||||
|
for (int i13 = 1; i13 < i8; i13++) {
|
||||||
|
float f8 = i13;
|
||||||
|
iArr[i5 + i13] = ((Math.round(f5 * f8) + i10) << 16) | (-16777216) | ((Math.round(f6 * f8) + i11) << 8) | (Math.round(f8 * f7) + i12);
|
||||||
|
}
|
||||||
|
i7++;
|
||||||
|
i5 = round;
|
||||||
|
i4 = 4;
|
||||||
|
i6 = -16777216;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
package J2;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.opengl.GLES20;
|
||||||
|
import android.opengl.GLSurfaceView;
|
||||||
|
import android.opengl.GLUtils;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.util.Log;
|
||||||
|
import java.nio.Buffer;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
import javax.microedition.khronos.egl.EGLConfig;
|
||||||
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class b implements GLSurfaceView.Renderer {
|
||||||
|
|
||||||
|
/* renamed from: b, reason: collision with root package name */
|
||||||
|
public FloatBuffer f729b;
|
||||||
|
public int c;
|
||||||
|
|
||||||
|
/* renamed from: d, reason: collision with root package name */
|
||||||
|
public final Bitmap f730d;
|
||||||
|
public final Context f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public double f732g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public final Bitmap f733h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public final a f734i;
|
||||||
|
|
||||||
|
/* renamed from: j, reason: collision with root package name */
|
||||||
|
public long f735j;
|
||||||
|
|
||||||
|
/* renamed from: k, reason: collision with root package name */
|
||||||
|
public double f736k;
|
||||||
|
|
||||||
|
/* renamed from: l, reason: collision with root package name */
|
||||||
|
public int f737l;
|
||||||
|
|
||||||
|
/* renamed from: a, reason: collision with root package name */
|
||||||
|
public final int[] f728a = new int[1];
|
||||||
|
|
||||||
|
/* renamed from: e, reason: collision with root package name */
|
||||||
|
public final float[] f731e = {-1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f};
|
||||||
|
|
||||||
|
public b(Context context, int i4) {
|
||||||
|
Bitmap.Config config = Bitmap.Config.ARGB_8888;
|
||||||
|
Bitmap createBitmap = Bitmap.createBitmap(2048, i4, config);
|
||||||
|
this.f730d = createBitmap;
|
||||||
|
createBitmap.eraseColor(16);
|
||||||
|
this.f732g = 0.09375d;
|
||||||
|
this.f734i = new a();
|
||||||
|
this.f735j = 0L;
|
||||||
|
this.f736k = 0.0d;
|
||||||
|
this.f737l = -1;
|
||||||
|
this.f = context;
|
||||||
|
this.f733h = Bitmap.createBitmap(1, i4, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void a() {
|
||||||
|
int glGetError = GLES20.glGetError();
|
||||||
|
if (glGetError != 0) {
|
||||||
|
StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
|
||||||
|
Log.e(stackTraceElement.getFileName() + ":" + stackTraceElement.getLineNumber(), ", GLES20 error: " + glGetError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final double b() {
|
||||||
|
long uptimeMillis = SystemClock.uptimeMillis();
|
||||||
|
double width = (((uptimeMillis - this.f735j) * this.f732g) + this.f736k) % this.f730d.getWidth();
|
||||||
|
this.f735j = uptimeMillis;
|
||||||
|
this.f736k = width;
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void c(GL10 gl10) {
|
||||||
|
GLES20.glUseProgram(this.c);
|
||||||
|
a();
|
||||||
|
GLES20.glActiveTexture(33984);
|
||||||
|
a();
|
||||||
|
GLES20.glBindTexture(3553, this.f728a[0]);
|
||||||
|
a();
|
||||||
|
GLES20.glUniform1i(GLES20.glGetUniformLocation(this.c, "uTexture"), 0);
|
||||||
|
a();
|
||||||
|
int glGetAttribLocation = GLES20.glGetAttribLocation(this.c, "aPosition");
|
||||||
|
a();
|
||||||
|
int glGetAttribLocation2 = GLES20.glGetAttribLocation(this.c, "aTexPos");
|
||||||
|
a();
|
||||||
|
this.f729b.position(0);
|
||||||
|
a();
|
||||||
|
GLES20.glVertexAttribPointer(glGetAttribLocation, 2, 5126, false, 16, (Buffer) this.f729b);
|
||||||
|
a();
|
||||||
|
GLES20.glEnableVertexAttribArray(glGetAttribLocation);
|
||||||
|
a();
|
||||||
|
this.f729b.position(2);
|
||||||
|
a();
|
||||||
|
GLES20.glVertexAttribPointer(glGetAttribLocation2, 2, 5126, false, 16, (Buffer) this.f729b);
|
||||||
|
a();
|
||||||
|
GLES20.glEnableVertexAttribArray(glGetAttribLocation2);
|
||||||
|
a();
|
||||||
|
GLES20.glDrawArrays(5, 0, 4);
|
||||||
|
a();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void d(GL10 gl10, int i4, int i5) {
|
||||||
|
GLES20.glViewport(0, 0, i4, i5);
|
||||||
|
a();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // android.opengl.GLSurfaceView.Renderer
|
||||||
|
public final void onDrawFrame(GL10 gl10) {
|
||||||
|
float b4 = (float) (b() / this.f730d.getWidth());
|
||||||
|
float f = 0.5f + b4;
|
||||||
|
float[] fArr = this.f731e;
|
||||||
|
fArr[6] = f;
|
||||||
|
fArr[2] = f;
|
||||||
|
float f4 = b4 + 1.0f;
|
||||||
|
fArr[14] = f4;
|
||||||
|
fArr[10] = f4;
|
||||||
|
this.f729b.position(0);
|
||||||
|
this.f729b.put(this.f731e);
|
||||||
|
c(gl10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // android.opengl.GLSurfaceView.Renderer
|
||||||
|
public final void onSurfaceChanged(GL10 gl10, int i4, int i5) {
|
||||||
|
d(gl10, i4, i5);
|
||||||
|
float width = 1.0f - (((this.f730d.getWidth() * 0.7f) / (i4 / this.f.getResources().getDisplayMetrics().density)) * 2.0f);
|
||||||
|
float[] fArr = this.f731e;
|
||||||
|
fArr[4] = width;
|
||||||
|
fArr[0] = width;
|
||||||
|
this.f729b.position(0);
|
||||||
|
this.f729b.put(this.f731e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // android.opengl.GLSurfaceView.Renderer
|
||||||
|
public final void onSurfaceCreated(GL10 gl10, EGLConfig eGLConfig) {
|
||||||
|
int glCreateShader = GLES20.glCreateShader(35633);
|
||||||
|
a();
|
||||||
|
GLES20.glShaderSource(glCreateShader, "attribute vec2 aPosition;\nattribute vec2 aTexPos;\nvarying vec2 vTexPos;\nvoid main() {\n vTexPos = aTexPos;\n gl_Position = vec4(aPosition.xy, 0.0, 1.0);\n}");
|
||||||
|
a();
|
||||||
|
GLES20.glCompileShader(glCreateShader);
|
||||||
|
a();
|
||||||
|
int glCreateShader2 = GLES20.glCreateShader(35632);
|
||||||
|
a();
|
||||||
|
GLES20.glShaderSource(glCreateShader2, "precision mediump float;\nuniform sampler2D uTexture;\nvarying vec2 vTexPos;\nvoid main(void)\n{\n gl_FragColor = texture2D(uTexture, vTexPos);\n}");
|
||||||
|
a();
|
||||||
|
GLES20.glCompileShader(glCreateShader2);
|
||||||
|
a();
|
||||||
|
this.c = GLES20.glCreateProgram();
|
||||||
|
a();
|
||||||
|
GLES20.glAttachShader(this.c, glCreateShader);
|
||||||
|
a();
|
||||||
|
GLES20.glAttachShader(this.c, glCreateShader2);
|
||||||
|
a();
|
||||||
|
GLES20.glLinkProgram(this.c);
|
||||||
|
a();
|
||||||
|
int[] iArr = this.f728a;
|
||||||
|
GLES20.glGenTextures(1, iArr, 0);
|
||||||
|
a();
|
||||||
|
GLES20.glBindTexture(3553, iArr[0]);
|
||||||
|
a();
|
||||||
|
GLES20.glTexParameteri(3553, 10240, 9729);
|
||||||
|
GLES20.glTexParameteri(3553, 10242, 10497);
|
||||||
|
GLES20.glTexParameteri(3553, 10241, 9729);
|
||||||
|
GLES20.glTexParameteri(3553, 10243, 10497);
|
||||||
|
a();
|
||||||
|
GLUtils.texImage2D(3553, 0, this.f730d, 0);
|
||||||
|
a();
|
||||||
|
float[] fArr = this.f731e;
|
||||||
|
ByteBuffer allocateDirect = ByteBuffer.allocateDirect(fArr.length * 4);
|
||||||
|
allocateDirect.order(ByteOrder.nativeOrder());
|
||||||
|
FloatBuffer asFloatBuffer = allocateDirect.asFloatBuffer();
|
||||||
|
this.f729b = asFloatBuffer;
|
||||||
|
asFloatBuffer.put(fArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package K1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 照搬自 Morse Expert 1.15 K1.a 精简版: 仅保留 k3 FFT 库引用的内存统计静态字段
|
||||||
|
* (K1.a.c 长整型记账)。原 K1.a(828 行)的其余方法为状态栏/库代码, 已裁剪
|
||||||
|
* (状态栏逻辑已在 MainActivity 内联)。
|
||||||
|
*/
|
||||||
|
public class a {
|
||||||
|
public static long c = 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
* CwDecodeScreen.kt — Look4Sat 的 CW 解码页面(Compose 外壳)。
|
||||||
|
*
|
||||||
|
* 照搬自 Morse Expert 1.15 的 com.ve3nea.morse_expert.MainActivity(已改为普通控制器类):
|
||||||
|
* - AndroidView 嵌入原版 activity_main.xml(ConstraintLayout 根, 含 decodedTextView/scaleView/
|
||||||
|
* statusTextView/verticalLayout/waterfallView);
|
||||||
|
* - 生命周期 onCreate/onResume/onPause/onDestroy 全部委托给控制器, 顺序与原 Activity 一致
|
||||||
|
* (onCreate 必须在 onResume 前, onDispose 先 onPause 再 onDestroy);
|
||||||
|
* - 麦克风权限(RECORD_AUDIO)由 Compose 侧管理, 授予后调用 onPermissionGranted()(= v()),
|
||||||
|
* 用 initialized 标记保证只调一次(v() 会 new a() 覆盖 f11036E);
|
||||||
|
* - 原 options_menu 的 清除/暂停/保存/录音/设置 改为顶部一行按钮(图标用模块内原版 drawable)。
|
||||||
|
*
|
||||||
|
* 注意: 控制器 onCreate 内的 setImmersive(window, false) 会把宿主窗口恢复为
|
||||||
|
* decorFitsSystemWindows(true)(原版行为照搬, 会对整个宿主 Activity 生效)。
|
||||||
|
*/
|
||||||
|
package com.rtbishop.look4sat.feature.cw
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.systemBarsPadding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import com.ve3nea.morse_expert.MainActivity
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CwDecodeScreen(navigateUp: () -> Unit = {}) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val activity = remember(context) {
|
||||||
|
context as? Activity ?: error("CwDecodeScreen must be hosted in an Activity")
|
||||||
|
}
|
||||||
|
// 照搬的控制器(普通类, 非 Activity); 每次进入页面新建实例
|
||||||
|
val controller = remember { MainActivity() }
|
||||||
|
// 提前 inflate 原版布局, 供 AndroidView 与控制器 onCreate 共用同一根视图
|
||||||
|
val rootView = remember(context) {
|
||||||
|
LayoutInflater.from(context).inflate(R.layout.activity_main, null) as ConstraintLayout
|
||||||
|
}
|
||||||
|
|
||||||
|
var permissionGranted by remember {
|
||||||
|
mutableStateOf(
|
||||||
|
context.checkSelfPermission(Manifest.permission.RECORD_AUDIO) ==
|
||||||
|
PackageManager.PERMISSION_GRANTED
|
||||||
|
)
|
||||||
|
}
|
||||||
|
var permanentlyDenied by remember { mutableStateOf(false) }
|
||||||
|
// v() 会 new a() 覆盖 f11036E, 必须保证只初始化一次
|
||||||
|
var initialized by remember { mutableStateOf(false) }
|
||||||
|
var showSettings by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
// 生命周期: onCreate 必须在 onResume 前; onDispose 先 onPause 再 onDestroy
|
||||||
|
DisposableEffect(Unit) {
|
||||||
|
controller.onCreate(activity, rootView)
|
||||||
|
controller.onResume()
|
||||||
|
onDispose {
|
||||||
|
controller.onPause()
|
||||||
|
controller.onDestroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 权限授予后启动解码核心; "首次进入已授权" 与 "弹窗回调授权" 两条路径统一走这里
|
||||||
|
LaunchedEffect(permissionGranted) {
|
||||||
|
if (permissionGranted && !initialized) {
|
||||||
|
controller.onPermissionGranted() // = v(): 创建音频采集 + 解码核心
|
||||||
|
// v() 只创建核心; 页面此刻已处于 resumed, 需再走一次 onResume 立即启动录音
|
||||||
|
// (AudioRecord 是新建的, startRecording 不会重复; GLSurfaceView.onResume 幂等)
|
||||||
|
controller.onResume()
|
||||||
|
initialized = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val permissionLauncher = rememberLauncherForActivityResult(
|
||||||
|
ActivityResultContracts.RequestPermission()
|
||||||
|
) { granted ->
|
||||||
|
permissionGranted = granted
|
||||||
|
permanentlyDenied = !granted &&
|
||||||
|
!activity.shouldShowRequestPermissionRationale(Manifest.permission.RECORD_AUDIO)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原版 tap_back_again_to_close: 单击返回只提示, 2 秒内再按才退出
|
||||||
|
BackHandler {
|
||||||
|
if (!controller.handleBackPress()) navigateUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showSettings) {
|
||||||
|
CwSettingsDialog(controller = controller, onDismiss = { showSettings = false })
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.systemBarsPadding()
|
||||||
|
) {
|
||||||
|
// 原 options_menu: 暂停/清除/保存(原版图标) + 录音/设置(文字按钮)
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 4.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceEvenly
|
||||||
|
) {
|
||||||
|
IconButton(onClick = { controller.togglePause() }) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_baseline_pause_24),
|
||||||
|
contentDescription = stringResource(R.string.pause)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
IconButton(onClick = { controller.clearDecoded() }) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_baseline_delete_24),
|
||||||
|
contentDescription = stringResource(R.string.clear)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
IconButton(onClick = { controller.saveText() }) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_baseline_save_24),
|
||||||
|
contentDescription = stringResource(R.string.save_text)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// recordSignals 需要解码核心(f11037F), 未初始化时禁用
|
||||||
|
TextButton(
|
||||||
|
onClick = { if (initialized) controller.recordSignals() },
|
||||||
|
enabled = initialized
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.record_signals))
|
||||||
|
}
|
||||||
|
TextButton(onClick = { showSettings = true }) {
|
||||||
|
Text(stringResource(R.string.settings))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(modifier = Modifier.weight(1f).fillMaxWidth()) {
|
||||||
|
AndroidView(
|
||||||
|
factory = { rootView },
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
)
|
||||||
|
if (!permissionGranted) {
|
||||||
|
// 未授予麦克风权限: 遮罩 + 提示文字 + 请求按钮
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color(0x99000000)),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.cw_mic_permission),
|
||||||
|
color = Color.White,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.padding(horizontal = 24.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
Button(onClick = {
|
||||||
|
permissionLauncher.launch(Manifest.permission.RECORD_AUDIO)
|
||||||
|
}) {
|
||||||
|
Text(stringResource(R.string.cw_grant_permission))
|
||||||
|
}
|
||||||
|
if (permanentlyDenied) {
|
||||||
|
// 勾选了"不再询问": 引导去系统设置开启
|
||||||
|
TextButton(onClick = {
|
||||||
|
activity.startActivity(
|
||||||
|
Intent(
|
||||||
|
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
|
||||||
|
Uri.parse("package:${activity.packageName}")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
Text(stringResource(R.string.cw_open_settings))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,218 @@
|
|||||||
|
/*
|
||||||
|
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||||
|
* Copyright (C) 2019-2026 Arty Bishop and contributors.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
package com.rtbishop.look4sat.feature.cw
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.RadioButton
|
||||||
|
import androidx.compose.material3.Slider
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.ve3nea.morse_expert.MainActivity
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
/** 键名/默认值照搬原 Morse Expert(root_preferences.xml + SettingsActivity 逻辑)。 */
|
||||||
|
private const val KEY_MESSAGE_TYPE = "message_type"
|
||||||
|
private const val KEY_TEXT_FONT_SIZE = "text_font_size"
|
||||||
|
private const val DEFAULT_MESSAGE_TYPE = "general_text"
|
||||||
|
private const val DEFAULT_TEXT_FONT_SIZE = 18
|
||||||
|
private const val MIN_FONT_SIZE = 7
|
||||||
|
private const val MAX_FONT_SIZE = 99
|
||||||
|
|
||||||
|
/** 预置色板(9 色, 3x3 网格);原 ColorPreferenceCompat 用第三方 colorpicker, 不引入。 */
|
||||||
|
private val PaletteColors = listOf(
|
||||||
|
0xFFD0F0F0.toInt(), 0xFF000000.toInt(), 0xFFFFFFFF.toInt(),
|
||||||
|
0xFFAAAAAA.toInt(), 0xFFFF0000.toInt(), 0xFF00FF00.toInt(),
|
||||||
|
0xFF0000FF.toInt(), 0xFFFFFF00.toInt(), 0xFFFF00FF.toInt(),
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CW 设置弹窗(替代原 Morse Expert 的 SettingsActivity)。
|
||||||
|
* 设置项与键名照搬原 APK: message_type / text_font_size / 9 个颜色
|
||||||
|
* (I2.b.f663b 键名, I2.b.f664d 默认值, I2.b.c 英文标题)。
|
||||||
|
* 存储: SharedPreferences(getPackageName() + "_preferences")。
|
||||||
|
* 点击 OK 保存并调用 controller.onResume() 立即生效; Cancel/点外部仅关闭不保存。
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun CwSettingsDialog(controller: MainActivity, onDismiss: () -> Unit) {
|
||||||
|
val activity = controller.mActivity ?: return
|
||||||
|
val prefs = remember(activity) {
|
||||||
|
activity.getSharedPreferences(activity.packageName + "_preferences", Context.MODE_PRIVATE)
|
||||||
|
}
|
||||||
|
|
||||||
|
var messageType by remember {
|
||||||
|
mutableStateOf(prefs.getString(KEY_MESSAGE_TYPE, DEFAULT_MESSAGE_TYPE) ?: DEFAULT_MESSAGE_TYPE)
|
||||||
|
}
|
||||||
|
var fontSize by remember {
|
||||||
|
mutableStateOf(
|
||||||
|
prefs.getInt(KEY_TEXT_FONT_SIZE, DEFAULT_TEXT_FONT_SIZE)
|
||||||
|
.toFloat().coerceIn(MIN_FONT_SIZE.toFloat(), MAX_FONT_SIZE.toFloat())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
var colorValues by remember {
|
||||||
|
mutableStateOf(IntArray(9) { i -> prefs.getInt(I2.b.f663b[i], I2.b.f664d[i]) })
|
||||||
|
}
|
||||||
|
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
title = { Text("CW Settings") },
|
||||||
|
text = {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
.padding(top = 4.dp)
|
||||||
|
) {
|
||||||
|
Text("Message type", style = MaterialTheme.typography.titleSmall)
|
||||||
|
MessageTypeRow("general_text", "General Text", messageType) { messageType = it }
|
||||||
|
MessageTypeRow("ham_radio_qso", "Ham Radio QSO", messageType) { messageType = it }
|
||||||
|
HorizontalDivider(Modifier.padding(vertical = 8.dp))
|
||||||
|
|
||||||
|
Text("Text font size", style = MaterialTheme.typography.titleSmall)
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Slider(
|
||||||
|
value = fontSize,
|
||||||
|
onValueChange = { fontSize = it },
|
||||||
|
valueRange = MIN_FONT_SIZE.toFloat()..MAX_FONT_SIZE.toFloat(),
|
||||||
|
steps = MAX_FONT_SIZE - MIN_FONT_SIZE - 1,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "${fontSize.roundToInt()}",
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
textAlign = TextAlign.End,
|
||||||
|
modifier = Modifier.width(44.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
HorizontalDivider(Modifier.padding(vertical = 8.dp))
|
||||||
|
|
||||||
|
Text("Colors", style = MaterialTheme.typography.titleSmall)
|
||||||
|
for (i in 0 until 9) {
|
||||||
|
ColorSettingRow(
|
||||||
|
title = I2.b.c[i],
|
||||||
|
value = colorValues[i],
|
||||||
|
onSelect = { selected ->
|
||||||
|
colorValues = colorValues.copyOf().also { it[i] = selected }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = {
|
||||||
|
val editor = prefs.edit()
|
||||||
|
editor.putString(KEY_MESSAGE_TYPE, messageType)
|
||||||
|
editor.putInt(KEY_TEXT_FONT_SIZE, fontSize.roundToInt())
|
||||||
|
for (i in 0 until 9) {
|
||||||
|
editor.putInt(I2.b.f663b[i], colorValues[i])
|
||||||
|
}
|
||||||
|
editor.apply()
|
||||||
|
// 原 SettingsActivity 返回时由 MainActivity.onResume() 重新读取全部设置; 照搬逻辑已就位
|
||||||
|
controller.onResume()
|
||||||
|
onDismiss()
|
||||||
|
}) { Text("OK") }
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismiss) { Text("Cancel") }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MessageTypeRow(option: String, label: String, selected: String, onSelect: (String) -> Unit) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(RoundedCornerShape(6.dp))
|
||||||
|
.clickable { onSelect(option) }
|
||||||
|
.padding(vertical = 2.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
RadioButton(selected = option == selected, onClick = { onSelect(option) })
|
||||||
|
Text(label, style = MaterialTheme.typography.bodyMedium)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ColorSettingRow(title: String, value: Int, onSelect: (Int) -> Unit) {
|
||||||
|
Column(Modifier.fillMaxWidth().padding(vertical = 6.dp)) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Text(title, style = MaterialTheme.typography.bodyMedium, modifier = Modifier.weight(1f))
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.size(20.dp)
|
||||||
|
.clip(RoundedCornerShape(4.dp))
|
||||||
|
.background(Color(value)),
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(6.dp))
|
||||||
|
Text(
|
||||||
|
text = String.format("#%06X", value and 0xFFFFFF),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(Modifier.height(6.dp))
|
||||||
|
PaletteGrid(selected = value, onSelect = onSelect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun PaletteGrid(selected: Int, onSelect: (Int) -> Unit) {
|
||||||
|
Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
|
PaletteColors.chunked(3).forEach { rowColors ->
|
||||||
|
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
|
rowColors.forEach { color ->
|
||||||
|
val isSelected = color == selected
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(36.dp)
|
||||||
|
.border(
|
||||||
|
width = if (isSelected) 3.dp else 1.dp,
|
||||||
|
color = if (isSelected) MaterialTheme.colorScheme.primary
|
||||||
|
else MaterialTheme.colorScheme.outlineVariant,
|
||||||
|
shape = RoundedCornerShape(6.dp),
|
||||||
|
)
|
||||||
|
.clip(RoundedCornerShape(6.dp))
|
||||||
|
.background(Color(color))
|
||||||
|
.clickable { onSelect(color) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.rtbishop.look4sat.feature.cw.suncompat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编译期 stub:Android 无 sun.misc.Cleaner(照搬 Morse Expert 1.15 k3 FFT 库的清理回调,
|
||||||
|
* 实际 FFT 1024 点不触发 native 路径)。
|
||||||
|
*/
|
||||||
|
public class Cleaner {
|
||||||
|
public static Cleaner create(Object referent, Runnable thunk) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clean() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package com.rtbishop.look4sat.feature.cw.suncompat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编译期 stub:Android 无 sun.misc.Unsafe(运行时通过反射获取,失败时 k3.r.f12109a 为 null)。
|
||||||
|
* 照搬自 Morse Expert 1.15(k3 FFT 库的大数组 native 路径 >1GB 不触发,实际 FFT 走 float[] 分支)。
|
||||||
|
*/
|
||||||
|
public class Unsafe {
|
||||||
|
public long allocateMemory(long bytes) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void freeMemory(long address) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getByte(long address) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putByte(long address, byte value) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getShort(long address) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putShort(long address, short value) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInt(long address) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putInt(long address, int value) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLong(long address) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putLong(long address, long value) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getFloat(long address) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putFloat(long address, float value) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getDouble(long address) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putDouble(long address, double value) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMemory(long address, long bytes, byte value) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyMemory(long src, long dst, long len) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getObject(Object o, long offset) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putObject(Object o, long offset, Object value) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
|
||||||
|
public long objectFieldOffset(java.lang.reflect.Field field) {
|
||||||
|
throw new UnsupportedOperationException("sun.misc.Unsafe not available on Android");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package com.ve3nea.morse_expert;
|
||||||
|
|
||||||
|
import F2.a;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.media.MediaScannerConnection;
|
||||||
|
import android.text.Layout;
|
||||||
|
import android.text.Selection;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.text.format.DateFormat;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.ActionMode;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 照搬自 Morse Expert 1.15 DecodedTextView, 类名保持混淆原名。
|
||||||
|
* 适配: 原 extends l.C1702e0(appcompat 混淆类) -> 直接 extends TextView
|
||||||
|
* (C1702e0 仅提供 TextView 兼容功能, 本类未使用其特有方法)。
|
||||||
|
*/
|
||||||
|
public class DecodedTextView extends TextView {
|
||||||
|
|
||||||
|
/* renamed from: m, reason: collision with root package name */
|
||||||
|
public final Context f11031m;
|
||||||
|
|
||||||
|
/* renamed from: n, reason: collision with root package name */
|
||||||
|
public boolean f11032n;
|
||||||
|
|
||||||
|
/* renamed from: o, reason: collision with root package name */
|
||||||
|
public final AtomicBoolean f11033o;
|
||||||
|
|
||||||
|
public DecodedTextView(Context context, AttributeSet attributeSet) {
|
||||||
|
super(context, attributeSet);
|
||||||
|
this.f11032n = false;
|
||||||
|
this.f11033o = new AtomicBoolean(false);
|
||||||
|
this.f11031m = context;
|
||||||
|
setCustomSelectionActionModeCallback(getActionModeCallback());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActionMode.Callback getActionModeCallback() {
|
||||||
|
return new a(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDecodedText() {
|
||||||
|
int selectionStart = getSelectionStart();
|
||||||
|
int selectionEnd = getSelectionEnd();
|
||||||
|
int max = Math.max(0, Math.min(selectionStart, selectionEnd));
|
||||||
|
int max2 = Math.max(0, Math.max(selectionStart, selectionEnd));
|
||||||
|
if (max2 > max) {
|
||||||
|
return getText().subSequence(max, max2).toString();
|
||||||
|
}
|
||||||
|
return getText().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // android.widget.TextView
|
||||||
|
public final void onSelectionChanged(int i4, int i5) {
|
||||||
|
super.onSelectionChanged(i4, i5);
|
||||||
|
if (hasSelection()) {
|
||||||
|
this.f11032n = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void q(SpannableStringBuilder spannableStringBuilder) {
|
||||||
|
boolean z3;
|
||||||
|
try {
|
||||||
|
CharSequence text = getText();
|
||||||
|
Layout layout = getLayout();
|
||||||
|
int lineCount = getLineCount();
|
||||||
|
int length = text.length();
|
||||||
|
int lineHeight = getLineHeight();
|
||||||
|
int scrollY = getScrollY();
|
||||||
|
if (layout.getLineBottom(lineCount - 1) == getHeight() + scrollY) {
|
||||||
|
z3 = true;
|
||||||
|
} else {
|
||||||
|
z3 = false;
|
||||||
|
}
|
||||||
|
getSelectionStart();
|
||||||
|
getSelectionEnd();
|
||||||
|
hasSelection();
|
||||||
|
if (z3) {
|
||||||
|
Selection.setSelection((Spannable) text, length, length);
|
||||||
|
}
|
||||||
|
append(spannableStringBuilder);
|
||||||
|
int i4 = lineCount - 50;
|
||||||
|
if (i4 > 0) {
|
||||||
|
int i5 = lineHeight * i4;
|
||||||
|
getEditableText().delete(0, layout.getLineEnd(lineCount - 51));
|
||||||
|
if (scrollY >= i5) {
|
||||||
|
scrollBy(0, -i5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e4) {
|
||||||
|
Log.e("printChars()", "Exception: ", e4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void r() {
|
||||||
|
String decodedText = getDecodedText();
|
||||||
|
boolean isEmpty = TextUtils.isEmpty(decodedText);
|
||||||
|
Context context = this.f11031m;
|
||||||
|
if (isEmpty) {
|
||||||
|
Toast.makeText(context, "No text to save", 0).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
File file = new File(getContext().getExternalFilesDir(null), ((String) DateFormat.format("yyyy-MM-dd_HH_mm_ss", new Date())) + ".txt");
|
||||||
|
String absolutePath = file.getAbsolutePath();
|
||||||
|
PrintWriter printWriter = new PrintWriter(file);
|
||||||
|
try {
|
||||||
|
printWriter.println(decodedText);
|
||||||
|
printWriter.close();
|
||||||
|
MediaScannerConnection.scanFile(context, new String[]{absolutePath}, new String[]{"text/plain"}, null);
|
||||||
|
Toast.makeText(context, "Text saved to " + absolutePath, 1).show();
|
||||||
|
} catch (Throwable th) {
|
||||||
|
printWriter.close();
|
||||||
|
throw th;
|
||||||
|
}
|
||||||
|
} catch (Exception e4) {
|
||||||
|
Log.e("File Save", "Failed", e4);
|
||||||
|
Toast.makeText(context, "Unable to save: " + e4.getMessage(), 1).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void s() {
|
||||||
|
Intent intent = new Intent("android.intent.action.SEND");
|
||||||
|
intent.setFlags(268435456);
|
||||||
|
intent.putExtra("android.intent.extra.TEXT", getDecodedText());
|
||||||
|
intent.setType("text/plain");
|
||||||
|
this.f11031m.startActivity(Intent.createChooser(intent, "Share Decoded Text"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
|
||||||
|
package com.ve3nea.morse_expert;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
|
import com.rtbishop.look4sat.feature.cw.R;
|
||||||
|
import E2.g;
|
||||||
|
import H2.a;
|
||||||
|
import H2.b;
|
||||||
|
import j1.C1646n;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ported verbatim from Morse Expert 1.15 MainActivity (com.ve3nea.morse_expert); obfuscated
|
||||||
|
* class/field names kept as-is. Surgery notes: originally extends e.AbstractActivityC1557l
|
||||||
|
* (obfuscated AppCompat base) -> now a plain controller class, Activity injected via
|
||||||
|
* onCreate(Activity, ConstraintLayout) for Compose AndroidView integration. Ads (d1.*),
|
||||||
|
* billing (I2.a), premium/rate menu removed per user. h3.b.r (recursive findViewById),
|
||||||
|
* K1.a.M (immersive status bar), B.i (permissions) replaced with equivalent standard APIs.
|
||||||
|
* Decoder logic (H2.a audio, H2.b core, pas engine) untouched.
|
||||||
|
*/
|
||||||
|
public class MainActivity {
|
||||||
|
|
||||||
|
public static final /* synthetic */ int f11034K = 0;
|
||||||
|
|
||||||
|
/* renamed from: D, reason: collision with root package name */
|
||||||
|
public C1646n f11035D;
|
||||||
|
|
||||||
|
/* renamed from: E, reason: collision with root package name */
|
||||||
|
public a f11036E;
|
||||||
|
|
||||||
|
/* renamed from: F, reason: collision with root package name */
|
||||||
|
public b f11037F;
|
||||||
|
|
||||||
|
/* renamed from: G, reason: collision with root package name */
|
||||||
|
public long f11038G = 0;
|
||||||
|
|
||||||
|
/* renamed from: J, reason: collision with root package name */
|
||||||
|
public final g f11040J = new g(this, 1);
|
||||||
|
|
||||||
|
/** Injected Activity context (replaces the original this Activity capabilities) */
|
||||||
|
public android.app.Activity mActivity;
|
||||||
|
|
||||||
|
public void onCreate(android.app.Activity activity, ConstraintLayout constraintLayout) {
|
||||||
|
onCreate(activity, constraintLayout, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* applyImmersive=false 用于嵌入小容器(如转发器 CW 面板):
|
||||||
|
* 不触碰宿主窗口的系统栏标志(避免影响整个 Activity)。
|
||||||
|
*/
|
||||||
|
public void onCreate(android.app.Activity activity, ConstraintLayout constraintLayout, boolean applyImmersive) {
|
||||||
|
this.mActivity = activity;
|
||||||
|
if (applyImmersive) {
|
||||||
|
// K1.a.M(getWindow(), false): immersive status bar (nav/fullscreen flags, ported as-is)
|
||||||
|
setImmersive(activity.getWindow(), false);
|
||||||
|
}
|
||||||
|
View inflate = constraintLayout;
|
||||||
|
int i4 = R.id.decodedTextView;
|
||||||
|
DecodedTextView decodedTextView = (DecodedTextView) findViewRecursive(inflate, R.id.decodedTextView);
|
||||||
|
if (decodedTextView != null) {
|
||||||
|
i4 = R.id.scaleView;
|
||||||
|
ScaleView scaleView = (ScaleView) findViewRecursive(inflate, R.id.scaleView);
|
||||||
|
if (scaleView != null) {
|
||||||
|
i4 = R.id.statusTextView;
|
||||||
|
TextView textView = (TextView) findViewRecursive(inflate, R.id.statusTextView);
|
||||||
|
if (textView != null) {
|
||||||
|
i4 = R.id.verticalLayout;
|
||||||
|
LinearLayout linearLayout = (LinearLayout) findViewRecursive(inflate, R.id.verticalLayout);
|
||||||
|
if (linearLayout != null) {
|
||||||
|
i4 = R.id.waterfallView;
|
||||||
|
WaterfallSurfaceView waterfallSurfaceView = (WaterfallSurfaceView) findViewRecursive(inflate, R.id.waterfallView);
|
||||||
|
if (waterfallSurfaceView != null) {
|
||||||
|
this.f11035D = new C1646n(constraintLayout, decodedTextView, scaleView, textView, linearLayout, waterfallSurfaceView);
|
||||||
|
((WaterfallSurfaceView) this.f11035D.f11892k).setOnTouchListener(this.f11040J);
|
||||||
|
// Originally: permission check then v(); permission flow handled by the Compose page
|
||||||
|
// Originally: ad banner mounting (removed)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NullPointerException("Missing required view with ID: " + inflate.getResources().getResourceName(i4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Original h3.b.r: recursive findViewById (ported behavior) */
|
||||||
|
private static View findViewRecursive(View view, int id) {
|
||||||
|
if (view instanceof ViewGroup) {
|
||||||
|
ViewGroup viewGroup = (ViewGroup) view;
|
||||||
|
int childCount = viewGroup.getChildCount();
|
||||||
|
for (int i = 0; i < childCount; i++) {
|
||||||
|
View found = viewGroup.getChildAt(i).findViewById(id);
|
||||||
|
if (found != null) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Original K1.a.M(window, false): immersive (hide nav/fullscreen flags, ported as-is) */
|
||||||
|
private static void setImmersive(Window window, boolean immersive) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 30) {
|
||||||
|
window.setDecorFitsSystemWindows(!immersive);
|
||||||
|
if (immersive) {
|
||||||
|
window.getDecorView().setSystemUiVisibility(
|
||||||
|
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
|
||||||
|
} else {
|
||||||
|
window.getDecorView().setSystemUiVisibility(
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
View decorView = window.getDecorView();
|
||||||
|
int systemUiVisibility = decorView.getSystemUiVisibility();
|
||||||
|
decorView.setSystemUiVisibility(immersive ? (systemUiVisibility & (-1793)) : (systemUiVisibility | 1792));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Original v(): audio capture + decoder core init (logic unchanged) */
|
||||||
|
public void v() {
|
||||||
|
this.f11036E = new a();
|
||||||
|
b bVar = new b(mActivity.getApplicationContext());
|
||||||
|
this.f11037F = bVar;
|
||||||
|
this.f11036E.f601l = bVar;
|
||||||
|
bVar.f619o = (WaterfallSurfaceView) this.f11035D.f11892k;
|
||||||
|
bVar.f620p = this;
|
||||||
|
bVar.f621q = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Original onResume: restore decoding (message type/font/background/record start/waterfall) */
|
||||||
|
public void onResume() {
|
||||||
|
android.content.SharedPreferences prefs = mActivity.getSharedPreferences(
|
||||||
|
mActivity.getPackageName() + "_preferences", Context.MODE_PRIVATE);
|
||||||
|
String messageType = prefs.getString("message_type", "general_text");
|
||||||
|
b bVar = this.f11037F;
|
||||||
|
if (bVar != null) {
|
||||||
|
bVar.f623s.set(messageType.equals("ham_radio_qso"));
|
||||||
|
}
|
||||||
|
((DecodedTextView) this.f11035D.f11888g).setTextSize(Math.max(7, prefs.getInt("text_font_size", 18)));
|
||||||
|
b bVar2 = this.f11037F;
|
||||||
|
if (bVar2 != null) {
|
||||||
|
bVar2.f617m.b(mActivity);
|
||||||
|
((DecodedTextView) this.f11035D.f11888g).setBackgroundColor(this.f11037F.f617m.f665a[0]);
|
||||||
|
}
|
||||||
|
a aVar = this.f11036E;
|
||||||
|
if (aVar != null) {
|
||||||
|
android.media.AudioRecord audioRecord = aVar.f596g;
|
||||||
|
audioRecord.startRecording();
|
||||||
|
if (audioRecord.getState() != 1) {
|
||||||
|
android.util.Log.e("AudioRecord", "cannot start");
|
||||||
|
}
|
||||||
|
aVar.f598i.set(true);
|
||||||
|
}
|
||||||
|
((WaterfallSurfaceView) this.f11035D.f11892k).onResume();
|
||||||
|
// Originally: ad lifecycle (removed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Original onPause: stop recording + waterfall pause */
|
||||||
|
public void onPause() {
|
||||||
|
a aVar = this.f11036E;
|
||||||
|
if (aVar != null) {
|
||||||
|
aVar.f598i.set(false);
|
||||||
|
android.media.AudioRecord audioRecord = aVar.f596g;
|
||||||
|
audioRecord.stop();
|
||||||
|
if (audioRecord.getState() != 1) {
|
||||||
|
android.util.Log.e("AudioRecord", "cannot stop");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
((WaterfallSurfaceView) this.f11035D.f11892k).onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Original onDestroy: release audio/decoder (billing cleanup removed) */
|
||||||
|
public void onDestroy() {
|
||||||
|
a aVar = this.f11036E;
|
||||||
|
if (aVar != null) {
|
||||||
|
aVar.f598i.set(false);
|
||||||
|
try {
|
||||||
|
aVar.f596g.stop();
|
||||||
|
aVar.f596g.release();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
aVar.f597h.shutdownNow();
|
||||||
|
}
|
||||||
|
this.f11036E = null;
|
||||||
|
this.f11037F = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Menu: clear decoded text (original clear_mnu) */
|
||||||
|
public void clearDecoded() {
|
||||||
|
((DecodedTextView) this.f11035D.f11888g).setText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Menu: pause/resume decoding (original pause_mnu, red/white icon toggle logic) */
|
||||||
|
public boolean togglePause() {
|
||||||
|
java.util.concurrent.atomic.AtomicBoolean atomicBoolean = ((DecodedTextView) this.f11035D.f11888g).f11033o;
|
||||||
|
atomicBoolean.set(!atomicBoolean.get());
|
||||||
|
return atomicBoolean.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Menu: save text (original save_mnu -> DecodedTextView.r()) */
|
||||||
|
public void saveText() {
|
||||||
|
((DecodedTextView) this.f11035D.f11888g).r();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Menu: record signals (original record_signals_mnu) */
|
||||||
|
public void recordSignals() {
|
||||||
|
this.f11037F.f626v.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Original tap_back_again_to_close hint */
|
||||||
|
public boolean handleBackPress() {
|
||||||
|
if (System.currentTimeMillis() < this.f11038G + 2000) {
|
||||||
|
return true; // double-back: exit
|
||||||
|
}
|
||||||
|
this.f11038G = System.currentTimeMillis();
|
||||||
|
Toast.makeText(mActivity, R.string.tap_back_again_to_close, 0).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Start decoding after permission granted (original onRequestPermissionsResult success) */
|
||||||
|
public void onPermissionGranted() {
|
||||||
|
v();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package com.ve3nea.morse_expert;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Path;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public class ScaleView extends View {
|
||||||
|
public int f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public int f11041g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public final Paint f11042h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public final Paint f11043i;
|
||||||
|
|
||||||
|
/* renamed from: j, reason: collision with root package name */
|
||||||
|
public final Paint f11044j;
|
||||||
|
|
||||||
|
/* renamed from: k, reason: collision with root package name */
|
||||||
|
public final Paint f11045k;
|
||||||
|
|
||||||
|
public ScaleView(Context context, AttributeSet attributeSet) {
|
||||||
|
super(context, attributeSet);
|
||||||
|
Paint paint = new Paint();
|
||||||
|
this.f11042h = paint;
|
||||||
|
paint.setColor(-16711936);
|
||||||
|
Paint paint2 = new Paint();
|
||||||
|
this.f11043i = paint2;
|
||||||
|
paint2.setColor(-16744448);
|
||||||
|
Paint paint3 = new Paint();
|
||||||
|
this.f11044j = paint3;
|
||||||
|
paint3.setColor(-256);
|
||||||
|
Paint paint4 = new Paint();
|
||||||
|
this.f11045k = paint4;
|
||||||
|
paint4.setColor(-8355840);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // android.view.View
|
||||||
|
public final void onDraw(Canvas canvas) {
|
||||||
|
Paint paint;
|
||||||
|
super.onDraw(canvas);
|
||||||
|
int i4 = this.f11041g;
|
||||||
|
if (i4 != 1) {
|
||||||
|
if (i4 != 2) {
|
||||||
|
if (i4 != 3) {
|
||||||
|
if (i4 != 4) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
paint = this.f11044j;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
paint = this.f11045k;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
paint = this.f11042h;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
paint = this.f11043i;
|
||||||
|
}
|
||||||
|
int round = (int) Math.round(getWidth() * 0.7d);
|
||||||
|
Path path = new Path();
|
||||||
|
path.moveTo(0.0f, this.f);
|
||||||
|
float f = round;
|
||||||
|
path.lineTo(f, this.f - round);
|
||||||
|
path.lineTo(f, this.f + round);
|
||||||
|
path.close();
|
||||||
|
canvas.drawPath(path, paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.ve3nea.morse_expert;
|
||||||
|
|
||||||
|
import J2.b;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.opengl.GLSurfaceView;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public class WaterfallSurfaceView extends GLSurfaceView {
|
||||||
|
public final b f;
|
||||||
|
|
||||||
|
public WaterfallSurfaceView(Context context, AttributeSet attributeSet) {
|
||||||
|
super(context, attributeSet);
|
||||||
|
setEGLContextClientVersion(2);
|
||||||
|
b bVar = new b(context, H2.b.f603B);
|
||||||
|
this.f = bVar;
|
||||||
|
setRenderer(bVar);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package d1;
|
||||||
|
|
||||||
|
/* renamed from: d1.b, reason: case insensitive filesystem */
|
||||||
|
/* 照搬自 Morse Expert 1.15, 混淆名保持。b(Parcel)/c(y2.d) 为库工具已裁剪(L1/y2 库依赖)。 */
|
||||||
|
public abstract /* synthetic */ class AbstractC1518b {
|
||||||
|
public static float a(float f, float f4, k3.d dVar, long j4, float f5, float f6) {
|
||||||
|
dVar.c(j4, f - f4);
|
||||||
|
return f5 + f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String d(String str, int i4) {
|
||||||
|
return str + i4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String e(String str, String str2) {
|
||||||
|
return str + str2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String f(String str, String str2, String str3) {
|
||||||
|
return str + str2 + str3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String g(StringBuilder sb, int i4, String str) {
|
||||||
|
sb.append(i4);
|
||||||
|
sb.append(str);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String h(StringBuilder sb, String str, String str2) {
|
||||||
|
sb.append(str);
|
||||||
|
sb.append(str2);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static /* synthetic */ boolean i(Object obj) {
|
||||||
|
return obj != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float j(float f, float f4, k3.d dVar, long j4, float f5, float f6) {
|
||||||
|
dVar.c(j4, f + f4);
|
||||||
|
return f5 + f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String k(String str, String str2) {
|
||||||
|
return str + str2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float l(float f, float f4, k3.d dVar, long j4, float f5, float f6) {
|
||||||
|
dVar.c(j4, f - f4);
|
||||||
|
return f5 - f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float m(float f, float f4, k3.d dVar, long j4, float f5, float f6) {
|
||||||
|
dVar.c(j4, f + f4);
|
||||||
|
return f5 - f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static /* synthetic */ String n(int i4) {
|
||||||
|
switch (i4) {
|
||||||
|
case 1:
|
||||||
|
return "NONE";
|
||||||
|
case 2:
|
||||||
|
return "LEFT";
|
||||||
|
case 3:
|
||||||
|
return "TOP";
|
||||||
|
case 4:
|
||||||
|
return "RIGHT";
|
||||||
|
case 5:
|
||||||
|
return "BOTTOM";
|
||||||
|
case 6:
|
||||||
|
return "BASELINE";
|
||||||
|
case 7:
|
||||||
|
return "CENTER";
|
||||||
|
case 8:
|
||||||
|
return "CENTER_X";
|
||||||
|
case 9:
|
||||||
|
return "CENTER_Y";
|
||||||
|
default:
|
||||||
|
throw null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package g3;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class a {
|
||||||
|
|
||||||
|
/* renamed from: a, reason: collision with root package name */
|
||||||
|
public final int f11582a;
|
||||||
|
|
||||||
|
/* renamed from: b, reason: collision with root package name */
|
||||||
|
public final double f11583b;
|
||||||
|
public final double c;
|
||||||
|
|
||||||
|
public a(double d4) {
|
||||||
|
int i4 = (int) (0.6366197723675814d * d4);
|
||||||
|
while (true) {
|
||||||
|
double d5 = -i4;
|
||||||
|
double d6 = 1.570796251296997d * d5;
|
||||||
|
double d7 = d4 + d6;
|
||||||
|
double d8 = 7.549789948768648E-8d * d5;
|
||||||
|
double d9 = d8 + d7;
|
||||||
|
double d10 = (-((d7 - d4) - d6)) + (-((d9 - d7) - d8));
|
||||||
|
double d11 = d5 * 6.123233995736766E-17d;
|
||||||
|
double d12 = d11 + d9;
|
||||||
|
double d13 = d10 + (-((d12 - d9) - d11));
|
||||||
|
if (d12 > 0.0d) {
|
||||||
|
this.f11582a = i4;
|
||||||
|
this.f11583b = d12;
|
||||||
|
this.c = d13;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
i4--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package g3;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public abstract class b {
|
||||||
|
|
||||||
|
/* renamed from: a, reason: collision with root package name */
|
||||||
|
public static final double[][] f11584a = (double[][]) d.f11591a.clone();
|
||||||
|
}
|
||||||
@@ -0,0 +1,507 @@
|
|||||||
|
package g3;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public abstract class c {
|
||||||
|
|
||||||
|
/* renamed from: a, reason: collision with root package name */
|
||||||
|
public static final double[][] f11585a;
|
||||||
|
|
||||||
|
/* renamed from: b, reason: collision with root package name */
|
||||||
|
public static final double[] f11586b;
|
||||||
|
public static final double[] c;
|
||||||
|
|
||||||
|
/* renamed from: d, reason: collision with root package name */
|
||||||
|
public static final double[] f11587d;
|
||||||
|
|
||||||
|
/* renamed from: e, reason: collision with root package name */
|
||||||
|
public static final double[] f11588e;
|
||||||
|
public static final long[] f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public static final long[] f11589g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public static final double[] f11590h;
|
||||||
|
|
||||||
|
static {
|
||||||
|
StrictMath.log(Double.MAX_VALUE);
|
||||||
|
f11585a = new double[][]{new double[]{1.0d, 5.669184079525E-24d}, new double[]{-0.25d, -0.25d}, new double[]{0.3333333134651184d, 1.986821492305628E-8d}, new double[]{-0.25d, -6.663542893624021E-14d}, new double[]{0.19999998807907104d, 1.1921056801463227E-8d}, new double[]{-0.1666666567325592d, -7.800414592973399E-9d}, new double[]{0.1428571343421936d, 5.650007086920087E-9d}, new double[]{-0.12502530217170715d, -7.44321345601866E-11d}, new double[]{0.11113807559013367d, 9.219544613762692E-9d}};
|
||||||
|
f11586b = new double[]{0.0d, 0.1246747374534607d, 0.24740394949913025d, 0.366272509098053d, 0.4794255495071411d, 0.5850973129272461d, 0.6816387176513672d, 0.7675435543060303d, 0.8414709568023682d, 0.902267575263977d, 0.9489846229553223d, 0.9808930158615112d, 0.9974949359893799d, 0.9985313415527344d};
|
||||||
|
c = new double[]{0.0d, -4.068233003401932E-9d, 9.755392680573412E-9d, 1.9987994582857286E-8d, -1.0902938113007961E-8d, -3.9986783938944604E-8d, 4.23719669792332E-8d, -5.207000323380292E-8d, 2.800552834259E-8d, 1.883511811213715E-8d, -3.5997360512765566E-9d, 4.116164446561962E-8d, 5.0614674548127384E-8d, -1.0129027912496858E-9d};
|
||||||
|
f11587d = new double[]{1.0d, 0.9921976327896118d, 0.9689123630523682d, 0.9305076599121094d, 0.8775825500488281d, 0.8109631538391113d, 0.7316888570785522d, 0.6409968137741089d, 0.5403022766113281d, 0.4311765432357788d, 0.3153223395347595d, 0.19454771280288696d, 0.07073719799518585d, -0.05417713522911072d};
|
||||||
|
f11588e = new double[]{0.0d, 3.4439717236742845E-8d, 5.865827662008209E-8d, -3.7999795083850525E-8d, 1.184154459111628E-8d, -3.43338934259355E-8d, 1.1795268640216787E-8d, 4.438921624363781E-8d, 2.925681159240093E-8d, -2.6437112632041807E-8d, 2.2860509143963117E-8d, -4.813899778443457E-9d, 3.6725170580355583E-9d, 2.0217439756338078E-10d};
|
||||||
|
f = new long[]{2935890503282001226L, 9154082963658192752L, 3952090531849364496L, 9193070505571053912L, 7910884519577875640L, 113236205062349959L, 4577762542105553359L, -5034868814120038111L, 4208363204685324176L, 5648769086999809661L, 2819561105158720014L, -4035746434778044925L, -302932621132653753L, -2644281811660520851L, -3183605296591799669L, 6722166367014452318L, -3512299194304650054L, -7278142539171889152L};
|
||||||
|
f11589g = new long[]{-3958705157555305932L, -4267615245585081135L};
|
||||||
|
f11590h = new double[]{0.0d, 0.125d, 0.25d, 0.375d, 0.5d, 0.625d, 0.75d, 0.875d, 1.0d, 1.125d, 1.25d, 1.375d, 1.5d, 1.625d};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double a(double d4) {
|
||||||
|
double d5;
|
||||||
|
if (d4 != d4) {
|
||||||
|
return d4;
|
||||||
|
}
|
||||||
|
if (d4 == d4 && d4 < 4.503599627370496E15d && d4 > -4.503599627370496E15d) {
|
||||||
|
long j4 = (long) d4;
|
||||||
|
if (d4 < 0.0d && j4 != d4) {
|
||||||
|
j4--;
|
||||||
|
}
|
||||||
|
if (j4 == 0) {
|
||||||
|
d5 = j4 * d4;
|
||||||
|
} else {
|
||||||
|
d5 = j4;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
d5 = d4;
|
||||||
|
}
|
||||||
|
if (d5 == d4) {
|
||||||
|
return d5;
|
||||||
|
}
|
||||||
|
double d6 = d5 + 1.0d;
|
||||||
|
if (d6 == 0.0d) {
|
||||||
|
return d4 * d6;
|
||||||
|
}
|
||||||
|
return d6;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double b(double d4) {
|
||||||
|
double g4;
|
||||||
|
double d5 = 0.0d;
|
||||||
|
if (d4 < 0.0d) {
|
||||||
|
d4 = -d4;
|
||||||
|
}
|
||||||
|
if (d4 != d4 || d4 == Double.POSITIVE_INFINITY) {
|
||||||
|
return Double.NaN;
|
||||||
|
}
|
||||||
|
int i4 = 0;
|
||||||
|
if (d4 > 3294198.0d) {
|
||||||
|
double[] dArr = new double[3];
|
||||||
|
e(d4, dArr);
|
||||||
|
i4 = ((int) dArr[0]) & 3;
|
||||||
|
d4 = dArr[1];
|
||||||
|
d5 = dArr[2];
|
||||||
|
} else if (d4 > 1.5707963267948966d) {
|
||||||
|
a aVar = new a(d4);
|
||||||
|
i4 = aVar.f11582a & 3;
|
||||||
|
d4 = aVar.f11583b;
|
||||||
|
d5 = aVar.c;
|
||||||
|
}
|
||||||
|
if (i4 != 0) {
|
||||||
|
if (i4 != 1) {
|
||||||
|
if (i4 != 2) {
|
||||||
|
if (i4 != 3) {
|
||||||
|
return Double.NaN;
|
||||||
|
}
|
||||||
|
return g(d4, d5);
|
||||||
|
}
|
||||||
|
g4 = c(d4, d5);
|
||||||
|
} else {
|
||||||
|
g4 = g(d4, d5);
|
||||||
|
}
|
||||||
|
return -g4;
|
||||||
|
}
|
||||||
|
return c(d4, d5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double c(double d4, double d5) {
|
||||||
|
double d6 = 1.5707963267948966d - d4;
|
||||||
|
return g(d6, (6.123233995736766E-17d - d5) + (-((d6 - 1.5707963267948966d) + d4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double d(double d4) {
|
||||||
|
if (d4 != 0.0d) {
|
||||||
|
long doubleToRawLongBits = Double.doubleToRawLongBits(d4);
|
||||||
|
if (((Long.MIN_VALUE & doubleToRawLongBits) != 0 || d4 != d4) && d4 != 0.0d) {
|
||||||
|
return Double.NaN;
|
||||||
|
}
|
||||||
|
if (d4 == Double.POSITIVE_INFINITY) {
|
||||||
|
return Double.POSITIVE_INFINITY;
|
||||||
|
}
|
||||||
|
int i4 = ((int) (doubleToRawLongBits >> 52)) - 1023;
|
||||||
|
if ((9218868437227405312L & doubleToRawLongBits) == 0) {
|
||||||
|
if (d4 == 0.0d) {
|
||||||
|
return Double.NEGATIVE_INFINITY;
|
||||||
|
}
|
||||||
|
doubleToRawLongBits <<= 1;
|
||||||
|
while ((4503599627370496L & doubleToRawLongBits) == 0) {
|
||||||
|
i4--;
|
||||||
|
doubleToRawLongBits <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((i4 == -1 || i4 == 0) && d4 < 1.01d && d4 > 0.99d) {
|
||||||
|
double d5 = d4 - 1.0d;
|
||||||
|
double d6 = d5 * 1.073741824E9d;
|
||||||
|
double d7 = (d5 + d6) - d6;
|
||||||
|
double d8 = d5 - d7;
|
||||||
|
double[][] dArr = f11585a;
|
||||||
|
double[] dArr2 = dArr[dArr.length - 1];
|
||||||
|
double d9 = dArr2[0];
|
||||||
|
double d10 = dArr2[1];
|
||||||
|
for (int length = dArr.length - 2; length >= 0; length--) {
|
||||||
|
double d11 = d9 * d7;
|
||||||
|
double d12 = (d10 * d8) + (d10 * d7) + (d9 * d8);
|
||||||
|
double d13 = d11 * 1.073741824E9d;
|
||||||
|
double d14 = (d11 + d13) - d13;
|
||||||
|
double d15 = (d11 - d14) + d12;
|
||||||
|
double[] dArr3 = dArr[length];
|
||||||
|
double d16 = d14 + dArr3[0];
|
||||||
|
double d17 = d15 + dArr3[1];
|
||||||
|
double d18 = d16 * 1.073741824E9d;
|
||||||
|
d9 = (d16 + d18) - d18;
|
||||||
|
d10 = (d16 - d9) + d17;
|
||||||
|
}
|
||||||
|
double d19 = d9 * d7;
|
||||||
|
double d20 = (d10 * d8) + (d7 * d10) + (d9 * d8);
|
||||||
|
double d21 = 1.073741824E9d * d19;
|
||||||
|
double d22 = (d19 + d21) - d21;
|
||||||
|
return (d19 - d22) + d20 + d22;
|
||||||
|
}
|
||||||
|
long j4 = 4499201580859392L & doubleToRawLongBits;
|
||||||
|
double[] dArr4 = b.f11584a[(int) (j4 >> 42)];
|
||||||
|
double d23 = (doubleToRawLongBits & 4398046511103L) / (j4 + 4.503599627370496E15d);
|
||||||
|
double d24 = (((((((((((-0.16624882440418567d) * d23) + 0.19999954120254515d) * d23) - 0.2499999997677497d) * d23) + 0.3333333333332802d) * d23) - 0.5d) * d23) + 1.0d) * d23;
|
||||||
|
double d25 = i4;
|
||||||
|
double d26 = 0.6931470632553101d * d25;
|
||||||
|
double d27 = dArr4[0];
|
||||||
|
double d28 = d26 + d27;
|
||||||
|
double d29 = (-((d28 - d26) - d27)) + 0.0d;
|
||||||
|
double d30 = d28 + d24;
|
||||||
|
double d31 = d29 + (-((d30 - d28) - d24));
|
||||||
|
double d32 = d25 * 1.1730463525082348E-7d;
|
||||||
|
double d33 = d30 + d32;
|
||||||
|
double d34 = d31 + (-((d33 - d30) - d32));
|
||||||
|
double d35 = dArr4[1];
|
||||||
|
double d36 = d33 + d35;
|
||||||
|
double d37 = d36 + 0.0d;
|
||||||
|
return d34 + (-((d36 - d33) - d35)) + (-((d37 - d36) - 0.0d)) + d37;
|
||||||
|
}
|
||||||
|
return Double.NEGATIVE_INFINITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void e(double d4, double[] dArr) {
|
||||||
|
long j4;
|
||||||
|
long j5;
|
||||||
|
long j6;
|
||||||
|
boolean z3;
|
||||||
|
boolean z4;
|
||||||
|
boolean z5;
|
||||||
|
boolean z6;
|
||||||
|
boolean z7;
|
||||||
|
boolean z8;
|
||||||
|
boolean z9;
|
||||||
|
boolean z10;
|
||||||
|
boolean z11;
|
||||||
|
boolean z12;
|
||||||
|
boolean z13;
|
||||||
|
boolean z14;
|
||||||
|
boolean z15;
|
||||||
|
boolean z16;
|
||||||
|
boolean z17;
|
||||||
|
boolean z18;
|
||||||
|
boolean z19;
|
||||||
|
boolean z20;
|
||||||
|
boolean z21;
|
||||||
|
boolean z22;
|
||||||
|
boolean z23;
|
||||||
|
long j7;
|
||||||
|
long doubleToRawLongBits = Double.doubleToRawLongBits(d4);
|
||||||
|
int i4 = ((int) ((doubleToRawLongBits >> 52) & 2047)) - 1022;
|
||||||
|
long j8 = ((doubleToRawLongBits & 4503599627370495L) | 4503599627370496L) << 11;
|
||||||
|
int i5 = i4 >> 6;
|
||||||
|
int i6 = i4 - (i5 << 6);
|
||||||
|
long[] jArr = f;
|
||||||
|
if (i6 != 0) {
|
||||||
|
if (i5 == 0) {
|
||||||
|
j7 = 0;
|
||||||
|
} else {
|
||||||
|
j7 = jArr[i5 - 1] << i6;
|
||||||
|
}
|
||||||
|
long j9 = jArr[i5];
|
||||||
|
int i7 = 64 - i6;
|
||||||
|
j4 = j7 | (j9 >>> i7);
|
||||||
|
long j10 = jArr[i5 + 1];
|
||||||
|
j5 = (j9 << i6) | (j10 >>> i7);
|
||||||
|
j6 = (jArr[i5 + 2] >>> i7) | (j10 << i6);
|
||||||
|
} else {
|
||||||
|
if (i5 == 0) {
|
||||||
|
j4 = 0;
|
||||||
|
} else {
|
||||||
|
j4 = jArr[i5 - 1];
|
||||||
|
}
|
||||||
|
j5 = jArr[i5];
|
||||||
|
j6 = jArr[i5 + 1];
|
||||||
|
}
|
||||||
|
long j11 = j8 >>> 32;
|
||||||
|
long j12 = j8 & 4294967295L;
|
||||||
|
long j13 = j5 >>> 32;
|
||||||
|
long j14 = j5 & 4294967295L;
|
||||||
|
long j15 = j11 * j13;
|
||||||
|
long j16 = j12 * j14;
|
||||||
|
long j17 = j13 * j12;
|
||||||
|
long j18 = j14 * j11;
|
||||||
|
long j19 = j16 + (j18 << 32);
|
||||||
|
long j20 = j15 + (j18 >>> 32);
|
||||||
|
if ((j16 & Long.MIN_VALUE) != 0) {
|
||||||
|
z3 = true;
|
||||||
|
} else {
|
||||||
|
z3 = false;
|
||||||
|
}
|
||||||
|
if ((j18 & 2147483648L) != 0) {
|
||||||
|
z4 = true;
|
||||||
|
} else {
|
||||||
|
z4 = false;
|
||||||
|
}
|
||||||
|
long j21 = j19 & Long.MIN_VALUE;
|
||||||
|
if (j21 != 0) {
|
||||||
|
z5 = true;
|
||||||
|
} else {
|
||||||
|
z5 = false;
|
||||||
|
}
|
||||||
|
if ((z3 && z4) || ((z3 || z4) && !z5)) {
|
||||||
|
j20++;
|
||||||
|
}
|
||||||
|
if (j21 != 0) {
|
||||||
|
z6 = true;
|
||||||
|
} else {
|
||||||
|
z6 = false;
|
||||||
|
}
|
||||||
|
if ((j17 & 2147483648L) != 0) {
|
||||||
|
z7 = true;
|
||||||
|
} else {
|
||||||
|
z7 = false;
|
||||||
|
}
|
||||||
|
long j22 = j19 + (j17 << 32);
|
||||||
|
long j23 = j20 + (j17 >>> 32);
|
||||||
|
long j24 = j22 & Long.MIN_VALUE;
|
||||||
|
if (j24 != 0) {
|
||||||
|
z8 = true;
|
||||||
|
} else {
|
||||||
|
z8 = false;
|
||||||
|
}
|
||||||
|
if ((z6 && z7) || ((z6 || z7) && !z8)) {
|
||||||
|
j23++;
|
||||||
|
}
|
||||||
|
long j25 = j6 >>> 32;
|
||||||
|
long j26 = (j11 * j25) + ((((j6 & 4294967295L) * j11) + (j25 * j12)) >>> 32);
|
||||||
|
if (j24 != 0) {
|
||||||
|
z9 = true;
|
||||||
|
} else {
|
||||||
|
z9 = false;
|
||||||
|
}
|
||||||
|
if ((j26 & Long.MIN_VALUE) != 0) {
|
||||||
|
z10 = true;
|
||||||
|
} else {
|
||||||
|
z10 = false;
|
||||||
|
}
|
||||||
|
long j27 = j22 + j26;
|
||||||
|
if ((j27 & Long.MIN_VALUE) != 0) {
|
||||||
|
z11 = true;
|
||||||
|
} else {
|
||||||
|
z11 = false;
|
||||||
|
}
|
||||||
|
if ((z9 && z10) || ((z9 || z10) && !z11)) {
|
||||||
|
j23++;
|
||||||
|
}
|
||||||
|
long j28 = j4 >>> 32;
|
||||||
|
long j29 = j4 & 4294967295L;
|
||||||
|
long j30 = (j12 * j29) + (((j11 * j29) + (j12 * j28)) << 32) + j23;
|
||||||
|
int i8 = (int) (j30 >>> 62);
|
||||||
|
long j31 = (j27 >>> 62) | (j30 << 2);
|
||||||
|
long j32 = j27 << 2;
|
||||||
|
long j33 = j31 >>> 32;
|
||||||
|
long j34 = j31 & 4294967295L;
|
||||||
|
long[] jArr2 = f11589g;
|
||||||
|
long j35 = jArr2[0];
|
||||||
|
long j36 = j35 >>> 32;
|
||||||
|
long j37 = j35 & 4294967295L;
|
||||||
|
long j38 = j34 * j37;
|
||||||
|
long j39 = j34 * j36;
|
||||||
|
long j40 = j33 * j37;
|
||||||
|
long j41 = j38 + (j40 << 32);
|
||||||
|
long j42 = (j33 * j36) + (j40 >>> 32);
|
||||||
|
if ((j38 & Long.MIN_VALUE) != 0) {
|
||||||
|
z12 = true;
|
||||||
|
} else {
|
||||||
|
z12 = false;
|
||||||
|
}
|
||||||
|
if ((j40 & 2147483648L) != 0) {
|
||||||
|
z13 = true;
|
||||||
|
} else {
|
||||||
|
z13 = false;
|
||||||
|
}
|
||||||
|
long j43 = j41 & Long.MIN_VALUE;
|
||||||
|
if (j43 != 0) {
|
||||||
|
z14 = true;
|
||||||
|
} else {
|
||||||
|
z14 = false;
|
||||||
|
}
|
||||||
|
if ((z12 && z13) || ((z12 || z13) && !z14)) {
|
||||||
|
j42++;
|
||||||
|
}
|
||||||
|
if (j43 != 0) {
|
||||||
|
z15 = true;
|
||||||
|
} else {
|
||||||
|
z15 = false;
|
||||||
|
}
|
||||||
|
if ((j39 & 2147483648L) != 0) {
|
||||||
|
z16 = true;
|
||||||
|
} else {
|
||||||
|
z16 = false;
|
||||||
|
}
|
||||||
|
long j44 = j41 + (j39 << 32);
|
||||||
|
long j45 = j42 + (j39 >>> 32);
|
||||||
|
long j46 = j44 & Long.MIN_VALUE;
|
||||||
|
if (j46 != 0) {
|
||||||
|
z17 = true;
|
||||||
|
} else {
|
||||||
|
z17 = false;
|
||||||
|
}
|
||||||
|
if ((z15 && z16) || ((z15 || z16) && !z17)) {
|
||||||
|
j45++;
|
||||||
|
}
|
||||||
|
long j47 = jArr2[1];
|
||||||
|
long j48 = j47 >>> 32;
|
||||||
|
long j49 = (j33 * j48) + (((j33 * (j47 & 4294967295L)) + (j34 * j48)) >>> 32);
|
||||||
|
if (j46 != 0) {
|
||||||
|
z18 = true;
|
||||||
|
} else {
|
||||||
|
z18 = false;
|
||||||
|
}
|
||||||
|
if ((j49 & Long.MIN_VALUE) != 0) {
|
||||||
|
z19 = true;
|
||||||
|
} else {
|
||||||
|
z19 = false;
|
||||||
|
}
|
||||||
|
long j50 = j44 + j49;
|
||||||
|
long j51 = j50 & Long.MIN_VALUE;
|
||||||
|
if (j51 != 0) {
|
||||||
|
z20 = true;
|
||||||
|
} else {
|
||||||
|
z20 = false;
|
||||||
|
}
|
||||||
|
if ((z18 && z19) || ((z18 || z19) && !z20)) {
|
||||||
|
j45++;
|
||||||
|
}
|
||||||
|
long j52 = j32 >>> 32;
|
||||||
|
long j53 = (j52 * j36) + (((j52 * j37) + ((j32 & 4294967295L) * j36)) >>> 32);
|
||||||
|
if (j51 != 0) {
|
||||||
|
z21 = true;
|
||||||
|
} else {
|
||||||
|
z21 = false;
|
||||||
|
}
|
||||||
|
if ((j53 & Long.MIN_VALUE) != 0) {
|
||||||
|
z22 = true;
|
||||||
|
} else {
|
||||||
|
z22 = false;
|
||||||
|
}
|
||||||
|
if (((j50 + j53) & Long.MIN_VALUE) != 0) {
|
||||||
|
z23 = true;
|
||||||
|
} else {
|
||||||
|
z23 = false;
|
||||||
|
}
|
||||||
|
if ((z21 && z22) || ((z21 || z22) && !z23)) {
|
||||||
|
j45++;
|
||||||
|
}
|
||||||
|
double d5 = (j45 >>> 12) / 4.503599627370496E15d;
|
||||||
|
double d6 = ((((j45 & 4095) << 40) + (f11589g[0] >>> 24)) / 4.503599627370496E15d) / 4.503599627370496E15d; // r34 = f11589g[0](smali: aget-wide v5, v2)
|
||||||
|
double d7 = d5 + d6;
|
||||||
|
dArr[0] = i8;
|
||||||
|
dArr[1] = d7 * 2.0d;
|
||||||
|
dArr[2] = (-((d7 - d5) - d6)) * 2.0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* JADX WARN: Removed duplicated region for block: B:20:0x005f */
|
||||||
|
/* JADX WARN: Removed duplicated region for block: B:22:0x0063 */
|
||||||
|
/* JADX WARN: Removed duplicated region for block: B:32:0x007a */
|
||||||
|
/*
|
||||||
|
Code decompiled incorrectly, please refer to instructions dump.
|
||||||
|
*/
|
||||||
|
public static double f(double d4) {
|
||||||
|
boolean z3;
|
||||||
|
double d5;
|
||||||
|
double d6;
|
||||||
|
double g4;
|
||||||
|
int i4 = 0;
|
||||||
|
double d7 = 0.0d;
|
||||||
|
if (d4 < 0.0d) {
|
||||||
|
d5 = -d4;
|
||||||
|
z3 = true;
|
||||||
|
} else {
|
||||||
|
z3 = false;
|
||||||
|
d5 = d4;
|
||||||
|
}
|
||||||
|
if (d5 == 0.0d) {
|
||||||
|
if (Double.doubleToRawLongBits(d4) >= 0) {
|
||||||
|
return 0.0d;
|
||||||
|
}
|
||||||
|
return -0.0d;
|
||||||
|
}
|
||||||
|
if (d5 != d5 || d5 == Double.POSITIVE_INFINITY) {
|
||||||
|
return Double.NaN;
|
||||||
|
}
|
||||||
|
if (d5 > 3294198.0d) {
|
||||||
|
double[] dArr = new double[3];
|
||||||
|
e(d5, dArr);
|
||||||
|
i4 = ((int) dArr[0]) & 3;
|
||||||
|
d5 = dArr[1];
|
||||||
|
d6 = dArr[2];
|
||||||
|
} else {
|
||||||
|
if (d5 > 1.5707963267948966d) {
|
||||||
|
a aVar = new a(d5);
|
||||||
|
int i5 = aVar.f11582a & 3;
|
||||||
|
d5 = aVar.f11583b;
|
||||||
|
d6 = aVar.c;
|
||||||
|
i4 = i5;
|
||||||
|
}
|
||||||
|
if (z3) {
|
||||||
|
i4 ^= 2;
|
||||||
|
}
|
||||||
|
// smali 证实: f() = sin(象限 switch!):
|
||||||
|
// case 0 -> g(d5,0)(sin(0-π/2))
|
||||||
|
// case 1 -> c(d5,0)(sin(π/2-π) = cos(余角))
|
||||||
|
// case 2 -> -g(d5,0)(sin(π-3π/2) = -sin(余角))
|
||||||
|
// case 3 -> -c(d5,0)(sin(3π/2-2π) = -cos(余角))
|
||||||
|
// jadx 畸形嵌套 if 把 case 0 还原成 return NaN(小角度全 NaN!),
|
||||||
|
// 且 case 1/2/3 全走 g() —— 旋转因子表 329 个 NaN + 频谱错乱的根源!
|
||||||
|
if (i4 == 0) {
|
||||||
|
return g(d5, d7);
|
||||||
|
}
|
||||||
|
if (i4 == 1) {
|
||||||
|
return c(d5, d7);
|
||||||
|
}
|
||||||
|
if (i4 == 2) {
|
||||||
|
return -g(d5, d7);
|
||||||
|
}
|
||||||
|
return -c(d5, d7);
|
||||||
|
}
|
||||||
|
d7 = d6;
|
||||||
|
// jadx 还原不完整(smali 650 行, jadx 只还原 63 行); f() 未被任何代码调用(死代码),
|
||||||
|
// 按 smali 尾部语义补 return(cond_0: v22 = v22 + v14; return v22)
|
||||||
|
return d7;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double g(double d4, double d5) {
|
||||||
|
int i4 = (int) ((8.0d * d4) + 0.5d);
|
||||||
|
double d6 = d4 - f11590h[i4];
|
||||||
|
double d7 = f11586b[i4];
|
||||||
|
double d8 = c[i4];
|
||||||
|
double d9 = f11587d[i4];
|
||||||
|
double d10 = f11588e[i4];
|
||||||
|
double d11 = d6 * d6;
|
||||||
|
double d12 = ((((((2.7553817452272217E-6d * d11) - 1.9841269659586505E-4d) * d11) + 0.008333333333329196d) * d11) - 0.16666666666666666d) * d11 * d6;
|
||||||
|
double d13 = ((((((2.479773539153719E-5d * d11) - 0.0013888888689039883d) * d11) + 0.041666666666621166d) * d11) - 0.49999999999999994d) * d11;
|
||||||
|
double d14 = 1.073741824E9d * d6;
|
||||||
|
double d15 = (d6 + d14) - d14;
|
||||||
|
double d16 = (d6 - d15) + d12;
|
||||||
|
double d17 = d7 + 0.0d;
|
||||||
|
double d18 = d9 * d15;
|
||||||
|
double d19 = d17 + d18;
|
||||||
|
double d20 = (d10 * d16) + (d8 * d13) + (d10 * d15) + (d9 * d16) + (d7 * d13) + (-((d17 - 0.0d) - d7)) + 0.0d + (-((d19 - d17) - d18)) + d8;
|
||||||
|
if (d5 != 0.0d) {
|
||||||
|
double d21 = (((d13 + 1.0d) * (d9 + d10)) - ((d15 + d16) * (d7 + d8))) * d5;
|
||||||
|
double d22 = d19 + d21;
|
||||||
|
d20 += -((d22 - d19) - d21);
|
||||||
|
d19 = d22;
|
||||||
|
}
|
||||||
|
return d19 + d20;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package g3;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public abstract class d {
|
||||||
|
|
||||||
|
/* renamed from: a, reason: collision with root package name */
|
||||||
|
public static final double[][] f11591a = {new double[]{0.0d, 0.0d}, new double[]{9.760860120877624E-4d, -3.903230345984362E-11d}, new double[]{0.0019512202125042677d, -8.124251825289188E-11d}, new double[]{0.0029254043474793434d, -1.8374207360194882E-11d}, new double[]{0.0038986406289041042d, -2.1324678121885073E-10d}, new double[]{0.004870930686593056d, -4.5199654318611534E-10d}, new double[]{0.00584227591753006d, -2.933016992001806E-10d}, new double[]{0.006812678650021553d, -2.325147219074669E-10d}, new double[]{0.007782140746712685d, -3.046577356838847E-10d}, new double[]{0.008750664070248604d, -5.500631513861575E-10d}, new double[]{0.00971824862062931d, 8.48292035519895E-10d}, new double[]{0.010684899985790253d, 1.1422610134013436E-10d}, new double[]{0.01165061630308628d, 9.168889933128375E-10d}, new double[]{0.012615403160452843d, -5.303786078838E-10d}, new double[]{0.013579258695244789d, -5.688639355498786E-10d}, new double[]{0.01454218477010727d, 7.296670293275653E-10d}, new double[]{0.015504186972975731d, -4.370104767451421E-10d}, new double[]{0.016465261578559875d, 1.43695591408832E-9d}, new double[]{0.01742541790008545d, -1.1862263158849434E-9d}, new double[]{0.018384650349617004d, -9.482976524690715E-10d}, new double[]{0.01934296265244484d, 1.9068609515836638E-10d}, new double[]{0.020300358533859253d, 2.655990315697216E-10d}, new double[]{0.021256837993860245d, 1.0315548713040775E-9d}, new double[]{0.022212404757738113d, 5.13345647019085E-10d}, new double[]{0.02316705882549286d, 4.5604151934208014E-10d}, new double[]{0.02412080392241478d, -1.1255706987475148E-9d}, new double[]{0.025073636323213577d, 1.2289023836765196E-9d}, new double[]{0.02602556347846985d, 1.7990281828096504E-9d}, new double[]{0.026976589113473892d, -1.4152718164638451E-9d}, new double[]{0.02792670577764511d, 7.568772963781632E-10d}, new double[]{0.0288759246468544d, -1.1449998592111558E-9d}, new double[]{0.029824241995811462d, -1.6850976862319495E-9d}, new double[]{0.030771657824516296d, 8.422373919843096E-10d}, new double[]{0.0317181795835495d, 6.872350402175489E-10d}, new double[]{0.03266380727291107d, -4.541194749189272E-10d}, new double[]{0.03360854089260101d, -8.9064764856495E-10d}, new double[]{0.034552380442619324d, 1.0640404096769032E-9d}, new double[]{0.0354953333735466d, -3.5901655945224663E-10d}, new double[]{0.03643739968538284d, -3.4829517943661266E-9d}, new double[]{0.037378571927547455d, 8.149473794244232E-10d}, new double[]{0.03831886500120163d, -6.990650304449166E-10d}, new double[]{0.03925827145576477d, 1.0883076226453258E-9d}, new double[]{0.040196798741817474d, 3.845192807999274E-10d}, new double[]{0.04113444685935974d, -1.1570594692045927E-9d}, new double[]{0.04207121580839157d, -1.8877045166697178E-9d}, new double[]{0.043007105588912964d, -1.6332083257987747E-10d}, new double[]{0.04394212365150452d, -1.7950057534514933E-9d}, new double[]{0.04487626254558563d, 2.302710041648838E-9d}, new double[]{0.045809537172317505d, -1.1410233017161343E-9d}, new double[]{0.04674194008111954d, -3.0498741599744685E-9d}, new double[]{0.04767347127199173d, -1.8026348269183678E-9d}, new double[]{0.04860413819551468d, -3.233204600453039E-9d}, new double[]{0.04953393340110779d, 1.7211688427961583E-9d}, new double[]{0.05046287178993225d, -2.329967807055457E-10d}, new double[]{0.05139094591140747d, -4.191810118556531E-11d}, new double[]{0.052318163216114044d, -3.5574324788328143E-9d}, new double[]{0.053244516253471375d, -1.7346590916458485E-9d}, new double[]{0.05417001247406006d, -4.343048751383674E-10d}, new double[]{0.055094651877880096d, 1.92909364037955E-9d}, new double[]{0.056018441915512085d, -5.139745677199588E-10d}, new double[]{0.05694137513637543d, 1.2637629975129189E-9d}, new double[]{0.05786345899105072d, 1.3840561112481119E-9d}, new double[]{0.058784693479537964d, 1.414889689612056E-9d}, new double[]{0.05970507860183716d, 2.9199191907666474E-9d}, new double[]{0.0606246218085289d, 7.90594243412116E-12d}, new double[]{0.06154331564903259d, 1.6844747839686189E-9d}, new double[]{0.06246116757392883d, 2.0498074572151747E-9d}, new double[]{0.06337818503379822d, -4.800180493433863E-9d}, new double[]{0.06429435312747955d, -2.4220822960064277E-9d}, new double[]{0.06520968675613403d, -4.179048566709334E-9d}, new double[]{0.06612417101860046d, 6.363872957010456E-9d}, new double[]{0.06703783571720123d, 9.339468680056365E-10d}, new double[]{0.06795066595077515d, -4.04226739708981E-9d}, new double[]{0.0688626617193222d, -7.043545052852817E-9d}, new double[]{0.06977382302284241d, -6.552819560439773E-9d}, new double[]{0.07068414986133575d, -1.0571674860370546E-9d}, new double[]{0.07159365713596344d, -3.948954622015801E-9d}, new double[]{0.07250232994556427d, 1.1776625988228244E-9d}, new double[]{0.07341018319129944d, 9.221072639606492E-10d}, new double[]{0.07431721687316895d, -3.219119568928366E-9d}, new double[]{0.0752234160900116d, 5.147575929018918E-9d}, new double[]{0.07612881064414978d, -2.291749683541979E-9d}, new doubleLine truncated
|
||||||
|
new double[]{0.6813591718673706d, 5.294053246347931E-8d}, new double[]{0.681853175163269d, -1.2220826223585654E-9d}, new double[]{0.6823468208312988d, 5.8377876767612725E-8d}, new double[]{0.6828403472900391d, -6.437492120743254E-9d}, new double[]{0.6833335161209106d, 4.2990710043633113E-8d}, new double[]{0.6838265657424927d, -3.1516131027023284E-8d}, new double[]{0.684319257736206d, 8.70017386744679E-9d}, new double[]{0.6848117113113403d, 4.466959125843237E-8d}, new double[]{0.6853040456771851d, -4.25782656420497E-8d}, new double[]{0.6857960224151611d, -1.4386267593671393E-8d}, new double[]{0.6862877607345581d, 1.0274494061148778E-8d}, new double[]{0.686779260635376d, 3.164186629229597E-8d}, new double[]{0.6872705221176147d, 4.995334552140326E-8d}, new double[]{0.687761664390564d, -5.3763211240398744E-8d}, new double[]{0.6882524490356445d, -4.0852427502515625E-8d}, new double[]{0.688742995262146d, -3.0287143914420064E-8d}, new double[]{0.6892333030700684d, -2.183125937905008E-8d}, new double[]{0.6897233724594116d, -1.524901992178814E-8d}, new double[]{0.6902132034301758d, -1.0305018010328949E-8d}, new double[]{0.6907027959823608d, -6.764191876212205E-9d}, new double[]{0.6911921501159668d, -4.391824838015402E-9d}, new double[]{0.6916812658309937d, -2.9535446262017846E-9d}, new double[]{0.6921701431274414d, -2.2153227096187463E-9d}, new double[]{0.6926587820053101d, -1.943473623641502E-9d}};
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package i3;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class a implements Runnable {
|
||||||
|
public final /* synthetic */ int f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ int f11686g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ int f11687h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ float[] f11688i;
|
||||||
|
|
||||||
|
/* renamed from: j, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ float[] f11689j;
|
||||||
|
|
||||||
|
/* renamed from: k, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ d f11690k;
|
||||||
|
|
||||||
|
public a(d dVar, int i4, int i5, int i6, float[] fArr, float[] fArr2) {
|
||||||
|
this.f11690k = dVar;
|
||||||
|
this.f = i4;
|
||||||
|
this.f11686g = i5;
|
||||||
|
this.f11687h = i6;
|
||||||
|
this.f11688i = fArr;
|
||||||
|
this.f11689j = fArr2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // java.lang.Runnable
|
||||||
|
public final void run() {
|
||||||
|
int i4 = this.f11686g;
|
||||||
|
int i5 = this.f;
|
||||||
|
if (i5 >= i4) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float f = this.f11689j[this.f11687h + i5];
|
||||||
|
throw null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package i3;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class b implements Runnable {
|
||||||
|
public final /* synthetic */ long f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ long f11691g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ long f11692h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ k3.d f11693i;
|
||||||
|
|
||||||
|
/* renamed from: j, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ k3.d f11694j;
|
||||||
|
|
||||||
|
/* renamed from: k, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ d f11695k;
|
||||||
|
|
||||||
|
public b(d dVar, long j4, long j5, long j6, k3.d dVar2, k3.d dVar3) {
|
||||||
|
this.f11695k = dVar;
|
||||||
|
this.f = j4;
|
||||||
|
this.f11691g = j5;
|
||||||
|
this.f11692h = j6;
|
||||||
|
this.f11693i = dVar2;
|
||||||
|
this.f11694j = dVar3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // java.lang.Runnable
|
||||||
|
public final void run() {
|
||||||
|
long j4 = this.f11691g;
|
||||||
|
long j5 = this.f;
|
||||||
|
if (j5 >= j4) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.f11694j.b(this.f11692h + j5);
|
||||||
|
throw null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package i3;
|
||||||
|
|
||||||
|
import k3.g;
|
||||||
|
import k3.s;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class c implements Runnable {
|
||||||
|
public final /* synthetic */ int f = 1;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ long f11696g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ long f11697h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ g f11698i;
|
||||||
|
|
||||||
|
/* renamed from: j, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ Object f11699j;
|
||||||
|
|
||||||
|
public c(long j4, long j5, s sVar, s sVar2) {
|
||||||
|
this.f11696g = j4;
|
||||||
|
this.f11697h = j5;
|
||||||
|
this.f11698i = sVar;
|
||||||
|
this.f11699j = sVar2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // java.lang.Runnable
|
||||||
|
public final void run() {
|
||||||
|
switch (this.f) {
|
||||||
|
case 0:
|
||||||
|
k3.d dVar = (k3.d) this.f11698i;
|
||||||
|
long j4 = this.f11697h;
|
||||||
|
long j5 = this.f11696g;
|
||||||
|
if (j5 >= j4) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dVar.b(j5 * 2);
|
||||||
|
throw null;
|
||||||
|
default:
|
||||||
|
for (long j6 = this.f11696g; j6 < this.f11697h; j6++) {
|
||||||
|
((s) this.f11698i).c(j6, ((s) this.f11699j).b(j6));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public c(d dVar, long j4, long j5, k3.d dVar2) {
|
||||||
|
this.f11699j = dVar;
|
||||||
|
this.f11696g = j4;
|
||||||
|
this.f11697h = j5;
|
||||||
|
this.f11698i = dVar2;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,42 @@
|
|||||||
|
package j1;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
|
import com.ve3nea.morse_expert.DecodedTextView;
|
||||||
|
import com.ve3nea.morse_expert.ScaleView;
|
||||||
|
import com.ve3nea.morse_expert.WaterfallSurfaceView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 照搬自 Morse Expert 1.15 j1.C1646n, 类名/字段名保持混淆原名。
|
||||||
|
* R8 合并类手术: 只保留视图持有器(6 参构造 + 5 个 View 字段);
|
||||||
|
* 其余 158-1278 行(广告/计费合并方法)已整段裁剪(用户要求广告不迁移)。
|
||||||
|
*/
|
||||||
|
public final class C1646n {
|
||||||
|
|
||||||
|
public final /* synthetic */ int f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public Object f11888g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public Object f11889h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public Object f11890i;
|
||||||
|
|
||||||
|
/* renamed from: j, reason: collision with root package name */
|
||||||
|
public Object f11891j;
|
||||||
|
|
||||||
|
/* renamed from: k, reason: collision with root package name */
|
||||||
|
public Object f11892k;
|
||||||
|
|
||||||
|
public C1646n(ConstraintLayout constraintLayout, DecodedTextView decodedTextView, ScaleView scaleView, TextView textView, LinearLayout linearLayout, WaterfallSurfaceView waterfallSurfaceView) {
|
||||||
|
this.f = 1;
|
||||||
|
this.f11888g = decodedTextView;
|
||||||
|
this.f11889h = scaleView;
|
||||||
|
this.f11890i = textView;
|
||||||
|
this.f11891j = linearLayout;
|
||||||
|
this.f11892k = waterfallSurfaceView;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package j3;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class a implements Runnable {
|
||||||
|
public final /* synthetic */ int f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ int f11919g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ int f11920h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ int f11921i;
|
||||||
|
|
||||||
|
/* renamed from: j, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ float[] f11922j;
|
||||||
|
|
||||||
|
/* renamed from: k, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ float[] f11923k;
|
||||||
|
|
||||||
|
/* renamed from: l, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ int f11924l;
|
||||||
|
|
||||||
|
public /* synthetic */ a(int i4, int i5, int i6, int i7, int i8, float[] fArr, float[] fArr2) {
|
||||||
|
this.f = i8;
|
||||||
|
this.f11919g = i4;
|
||||||
|
this.f11920h = i5;
|
||||||
|
this.f11921i = i6;
|
||||||
|
this.f11922j = fArr;
|
||||||
|
this.f11923k = fArr2;
|
||||||
|
this.f11924l = i7;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // java.lang.Runnable
|
||||||
|
public final void run() {
|
||||||
|
switch (this.f) {
|
||||||
|
case 0:
|
||||||
|
int i4 = this.f11919g;
|
||||||
|
int i5 = this.f11920h;
|
||||||
|
int i6 = i4 + i5;
|
||||||
|
int i7 = this.f11921i;
|
||||||
|
while (true) {
|
||||||
|
int i8 = i7;
|
||||||
|
if (i8 > 512) {
|
||||||
|
i7 = i8 >> 2;
|
||||||
|
c.w(i7, i6 - i7, this.f11924l - (i8 >> 3), this.f11922j, this.f11923k);
|
||||||
|
} else {
|
||||||
|
int i9 = this.f11924l;
|
||||||
|
float[] fArr = this.f11923k;
|
||||||
|
c.u(i8, 1, this.f11922j, i6 - i8, i9, fArr);
|
||||||
|
int i10 = i4 - i8;
|
||||||
|
int i11 = 0;
|
||||||
|
int i12 = i5 - i8;
|
||||||
|
while (i12 > 0) {
|
||||||
|
int i13 = i11 + 1;
|
||||||
|
int i14 = this.f11924l;
|
||||||
|
float[] fArr2 = this.f11923k;
|
||||||
|
int i15 = i12;
|
||||||
|
i11 = i13;
|
||||||
|
int i16 = this.f11924l;
|
||||||
|
float[] fArr3 = this.f11923k;
|
||||||
|
c.u(i8, c.E(i8, i12, i13, this.f11919g, i14, this.f11922j, fArr2), this.f11922j, i10 + i15, i16, fArr3);
|
||||||
|
i12 = i15 - i8;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
int i17 = this.f11919g;
|
||||||
|
int i18 = this.f11920h;
|
||||||
|
int i19 = i17 + i18;
|
||||||
|
int i20 = this.f11921i;
|
||||||
|
int i21 = 1;
|
||||||
|
while (i20 > 512) {
|
||||||
|
i20 >>= 2;
|
||||||
|
i21 <<= 2;
|
||||||
|
c.y(i20, i19 - i20, this.f11924l - i20, this.f11922j, this.f11923k);
|
||||||
|
}
|
||||||
|
int i22 = this.f11924l;
|
||||||
|
float[] fArr4 = this.f11923k;
|
||||||
|
c.u(i20, 0, this.f11922j, i19 - i20, i22, fArr4);
|
||||||
|
int i23 = i21 >> 1;
|
||||||
|
int i24 = i17 - i20;
|
||||||
|
int i25 = i18 - i20;
|
||||||
|
while (i25 > 0) {
|
||||||
|
int i26 = i23 + 1;
|
||||||
|
int i27 = this.f11924l;
|
||||||
|
float[] fArr5 = this.f11923k;
|
||||||
|
int i28 = i25;
|
||||||
|
i23 = i26;
|
||||||
|
int i29 = this.f11924l;
|
||||||
|
float[] fArr6 = this.f11923k;
|
||||||
|
c.u(i20, c.E(i20, i25, i26, this.f11919g, i27, this.f11922j, fArr5), this.f11922j, i24 + i28, i29, fArr6);
|
||||||
|
i25 = i28 - i20;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package j3;
|
||||||
|
|
||||||
|
import k3.d;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class b implements Runnable {
|
||||||
|
public final /* synthetic */ int f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ long f11925g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ long f11926h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ long f11927i;
|
||||||
|
|
||||||
|
/* renamed from: j, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ d f11928j;
|
||||||
|
|
||||||
|
/* renamed from: k, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ d f11929k;
|
||||||
|
|
||||||
|
/* renamed from: l, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ long f11930l;
|
||||||
|
|
||||||
|
public /* synthetic */ b(long j4, long j5, long j6, d dVar, d dVar2, long j7, int i4) {
|
||||||
|
this.f = i4;
|
||||||
|
this.f11925g = j4;
|
||||||
|
this.f11926h = j5;
|
||||||
|
this.f11927i = j6;
|
||||||
|
this.f11928j = dVar;
|
||||||
|
this.f11929k = dVar2;
|
||||||
|
this.f11930l = j7;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // java.lang.Runnable
|
||||||
|
public final void run() {
|
||||||
|
switch (this.f) {
|
||||||
|
case 0:
|
||||||
|
long j4 = this.f11925g;
|
||||||
|
long j5 = this.f11926h;
|
||||||
|
long j6 = j4 + j5;
|
||||||
|
long j7 = this.f11927i;
|
||||||
|
while (j7 > 512) {
|
||||||
|
long j8 = j7 >> 2;
|
||||||
|
c.x(j8, j6 - j8, this.f11930l - (j7 >> 3), this.f11928j, this.f11929k);
|
||||||
|
j7 = j8;
|
||||||
|
}
|
||||||
|
c.v(j7, 1L, j6 - j7, this.f11930l, this.f11928j, this.f11929k);
|
||||||
|
long j9 = j4 - j7;
|
||||||
|
long j10 = j5 - j7;
|
||||||
|
long j11 = 0;
|
||||||
|
for (long j12 = 0; j10 > j12; j12 = 0) {
|
||||||
|
long j13 = j11 + 1;
|
||||||
|
long j14 = j10;
|
||||||
|
c.v(j7, c.F(j7, j10, j13, this.f11925g, this.f11930l, this.f11928j, this.f11929k), j9 + j14, this.f11930l, this.f11928j, this.f11929k);
|
||||||
|
j10 = j14 - j7;
|
||||||
|
j11 = j13;
|
||||||
|
j9 = j9;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case 1:
|
||||||
|
long j15 = this.f11925g;
|
||||||
|
long j16 = this.f11926h;
|
||||||
|
long j17 = j15 + j16;
|
||||||
|
long j18 = this.f11927i;
|
||||||
|
long j19 = 1;
|
||||||
|
while (j18 > 512) {
|
||||||
|
long j20 = j18 >> 2;
|
||||||
|
j19 <<= 2;
|
||||||
|
c.z(j20, j17 - j20, this.f11930l - j20, this.f11928j, this.f11929k);
|
||||||
|
j18 = j20;
|
||||||
|
}
|
||||||
|
c.v(j18, 0L, j17 - j18, this.f11930l, this.f11928j, this.f11929k);
|
||||||
|
long j21 = j19 >> 1;
|
||||||
|
long j22 = j15 - j18;
|
||||||
|
long j23 = j16 - j18;
|
||||||
|
while (j23 > 0) {
|
||||||
|
long j24 = j21 + 1;
|
||||||
|
long j25 = j23;
|
||||||
|
j21 = j24;
|
||||||
|
c.v(j18, c.F(j18, j23, j24, this.f11925g, this.f11930l, this.f11928j, this.f11929k), j22 + j25, this.f11930l, this.f11928j, this.f11929k);
|
||||||
|
j23 = j25 - j18;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
for (long j26 = this.f11925g; j26 < this.f11926h; j26++) {
|
||||||
|
this.f11928j.c(this.f11927i + j26, this.f11929k.b(this.f11930l + j26));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public b(long j4, long j5, long j6, long j7, d dVar, d dVar2) {
|
||||||
|
this.f = 2;
|
||||||
|
this.f11925g = j4;
|
||||||
|
this.f11926h = j5;
|
||||||
|
this.f11928j = dVar;
|
||||||
|
this.f11927i = j6;
|
||||||
|
this.f11929k = dVar2;
|
||||||
|
this.f11930l = j7;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,11 @@
|
|||||||
|
package k3;
|
||||||
|
|
||||||
|
import java.lang.Thread;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class a implements Thread.UncaughtExceptionHandler {
|
||||||
|
@Override // java.lang.Thread.UncaughtExceptionHandler
|
||||||
|
public final void uncaughtException(Thread thread, Throwable th) {
|
||||||
|
th.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package k3;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class b implements ThreadFactory {
|
||||||
|
|
||||||
|
/* renamed from: b, reason: collision with root package name */
|
||||||
|
public static final ThreadFactory f12091b = Executors.defaultThreadFactory();
|
||||||
|
|
||||||
|
/* renamed from: a, reason: collision with root package name */
|
||||||
|
public final a f12092a;
|
||||||
|
|
||||||
|
public b(a aVar) {
|
||||||
|
this.f12092a = aVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // java.util.concurrent.ThreadFactory
|
||||||
|
public final Thread newThread(Runnable runnable) {
|
||||||
|
Thread newThread = f12091b.newThread(runnable);
|
||||||
|
newThread.setUncaughtExceptionHandler(this.f12092a);
|
||||||
|
return newThread;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package k3;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public abstract class c {
|
||||||
|
|
||||||
|
/* renamed from: a, reason: collision with root package name */
|
||||||
|
public static final ExecutorService f12093a;
|
||||||
|
|
||||||
|
/* renamed from: b, reason: collision with root package name */
|
||||||
|
public static ExecutorService f12094b;
|
||||||
|
public static final int c;
|
||||||
|
|
||||||
|
/* renamed from: d, reason: collision with root package name */
|
||||||
|
public static final long f12095d;
|
||||||
|
|
||||||
|
/* JADX WARN: Type inference failed for: r1v0, types: [java.lang.Object, k3.a] */
|
||||||
|
static {
|
||||||
|
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(new b(new a()));
|
||||||
|
f12093a = newCachedThreadPool;
|
||||||
|
f12094b = newCachedThreadPool;
|
||||||
|
c = Runtime.getRuntime().availableProcessors();
|
||||||
|
f12095d = 100000L;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Future a(Runnable runnable) {
|
||||||
|
if (f12094b.isShutdown() || f12094b.isTerminated()) {
|
||||||
|
f12094b = f12093a;
|
||||||
|
}
|
||||||
|
return f12094b.submit(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void b(Future[] futureArr) throws InterruptedException, java.util.concurrent.ExecutionException {
|
||||||
|
// smali: 无 catch 直接向上抛 -> Java 声明 throws(忠实表达)
|
||||||
|
for (Future future : futureArr) {
|
||||||
|
future.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package k3;
|
||||||
|
|
||||||
|
import com.rtbishop.look4sat.feature.cw.suncompat.Cleaner;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class d extends g {
|
||||||
|
|
||||||
|
/* renamed from: k, reason: collision with root package name */
|
||||||
|
public float[] f12096k;
|
||||||
|
|
||||||
|
public d() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public d(long j4, boolean z3) {
|
||||||
|
this.f = q.f12106g;
|
||||||
|
this.f12103h = 4L;
|
||||||
|
if (j4 > 0) {
|
||||||
|
this.f12102g = j4;
|
||||||
|
if (j4 > 1073741824) {
|
||||||
|
this.f12105j = r.f12109a.allocateMemory(4 * j4);
|
||||||
|
if (z3) {
|
||||||
|
a(j4);
|
||||||
|
}
|
||||||
|
Cleaner.create(this, new f(this.f12105j, this.f12102g, this.f12103h));
|
||||||
|
K1.a.c += this.f12102g * this.f12103h;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.f12096k = new float[(int) j4];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException(j4 + " is not a positive long value");
|
||||||
|
}
|
||||||
|
|
||||||
|
public final float b(long j4) {
|
||||||
|
float[] fArr = this.f12096k;
|
||||||
|
long j5 = this.f12105j;
|
||||||
|
if (j5 != 0) {
|
||||||
|
return r.f12109a.getFloat((this.f12103h * j4) + j5);
|
||||||
|
}
|
||||||
|
if (this.f12104i) {
|
||||||
|
return fArr[0];
|
||||||
|
}
|
||||||
|
return fArr[(int) j4];
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void c(long j4, float f) {
|
||||||
|
long j5 = this.f12105j;
|
||||||
|
if (j5 != 0) {
|
||||||
|
r.f12109a.putFloat((this.f12103h * j4) + j5, f);
|
||||||
|
} else {
|
||||||
|
if (!this.f12104i) {
|
||||||
|
this.f12096k[(int) j4] = f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new IllegalAccessError("Constant arrays cannot be modified.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* JADX WARN: Type inference failed for: r0v1, types: [k3.g, java.lang.Object, k3.d] */
|
||||||
|
public final Object clone() {
|
||||||
|
if (this.f12104i) {
|
||||||
|
long j4 = this.f12102g;
|
||||||
|
float b4 = b(0L);
|
||||||
|
d gVar = new d();
|
||||||
|
gVar.f = q.f12106g;
|
||||||
|
gVar.f12103h = 4L;
|
||||||
|
if (j4 > 0) {
|
||||||
|
gVar.f12102g = j4;
|
||||||
|
gVar.f12104i = true;
|
||||||
|
gVar.f12096k = new float[]{b4};
|
||||||
|
return gVar;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException(j4 + " is not a positive long value");
|
||||||
|
}
|
||||||
|
d dVar = new d(this.f12102g, false);
|
||||||
|
r.a(0L, 0L, this.f12102g, this, dVar);
|
||||||
|
return dVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // k3.g
|
||||||
|
public final boolean equals(Object obj) {
|
||||||
|
if (!super.equals(obj) || this.f12096k != ((d) obj).f12096k) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // k3.g
|
||||||
|
public final int hashCode() {
|
||||||
|
int i4;
|
||||||
|
int hashCode = super.hashCode() * 29;
|
||||||
|
float[] fArr = this.f12096k;
|
||||||
|
if (fArr != null) {
|
||||||
|
i4 = fArr.hashCode();
|
||||||
|
} else {
|
||||||
|
i4 = 0;
|
||||||
|
}
|
||||||
|
return hashCode + i4;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package k3;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class e implements Runnable {
|
||||||
|
public final /* synthetic */ long f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ long f12097g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ long f12098h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public final /* synthetic */ g f12099i;
|
||||||
|
|
||||||
|
public e(g gVar, long j4, long j5, long j6) {
|
||||||
|
this.f12099i = gVar;
|
||||||
|
this.f = j4;
|
||||||
|
this.f12097g = j5;
|
||||||
|
this.f12098h = j6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* JADX WARN: Can't fix incorrect switch cases order, some code will duplicate */
|
||||||
|
/* JADX WARN: Failed to find 'out' block for switch in B:2:0x0011. Please report as an issue. */
|
||||||
|
@Override // java.lang.Runnable
|
||||||
|
public final void run() {
|
||||||
|
g gVar = this.f12099i;
|
||||||
|
int ordinal = gVar.f.ordinal();
|
||||||
|
long j4 = this.f12098h;
|
||||||
|
long j5 = this.f12097g;
|
||||||
|
long j6 = this.f;
|
||||||
|
switch (ordinal) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 10:
|
||||||
|
case 11:
|
||||||
|
while (j6 < j5) {
|
||||||
|
r.f12109a.putByte((gVar.f12103h * j6) + j4, (byte) 0);
|
||||||
|
j6++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case 3:
|
||||||
|
while (j6 < j5) {
|
||||||
|
r.f12109a.putShort((gVar.f12103h * j6) + j4, (short) 0);
|
||||||
|
j6++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case 4:
|
||||||
|
while (j6 < j5) {
|
||||||
|
r.f12109a.putInt((gVar.f12103h * j6) + j4, 0);
|
||||||
|
j6++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case 5:
|
||||||
|
while (j6 < j5) {
|
||||||
|
r.f12109a.putLong((gVar.f12103h * j6) + j4, 0L);
|
||||||
|
j6++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case 6:
|
||||||
|
while (j6 < j5) {
|
||||||
|
r.f12109a.putFloat((gVar.f12103h * j6) + j4, 0.0f);
|
||||||
|
j6++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case 7:
|
||||||
|
while (j6 < j5) {
|
||||||
|
r.f12109a.putDouble((gVar.f12103h * j6) + j4, 0.0d);
|
||||||
|
j6++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case 8:
|
||||||
|
case 9:
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid array type.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package k3;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class f implements Runnable {
|
||||||
|
public long f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public final long f12100g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public final long f12101h;
|
||||||
|
|
||||||
|
public f(long j4, long j5, long j6) {
|
||||||
|
this.f = j4;
|
||||||
|
this.f12100g = j5;
|
||||||
|
this.f12101h = j6;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // java.lang.Runnable
|
||||||
|
public final void run() {
|
||||||
|
long j4 = this.f;
|
||||||
|
if (j4 != 0) {
|
||||||
|
r.f12109a.freeMemory(j4);
|
||||||
|
this.f = 0L;
|
||||||
|
long j5 = K1.a.c - (this.f12100g * this.f12101h);
|
||||||
|
K1.a.c = j5;
|
||||||
|
if (j5 < 0) {
|
||||||
|
K1.a.c = 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package k3;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public abstract class g implements Serializable, Cloneable {
|
||||||
|
public q f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public long f12102g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public long f12103h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public boolean f12104i = false;
|
||||||
|
|
||||||
|
/* renamed from: j, reason: collision with root package name */
|
||||||
|
public long f12105j = 0;
|
||||||
|
|
||||||
|
public final void a(long j4) {
|
||||||
|
long j5;
|
||||||
|
g gVar = this;
|
||||||
|
long j6 = gVar.f12105j;
|
||||||
|
if (j6 != 0) {
|
||||||
|
long j7 = c.c;
|
||||||
|
double[][] dArr = g3.c.f11585a;
|
||||||
|
if (j4 <= j7) {
|
||||||
|
j7 = j4;
|
||||||
|
}
|
||||||
|
int i4 = (int) j7;
|
||||||
|
if (i4 > 2 && j4 >= c.f12095d) {
|
||||||
|
long j8 = j4 / i4;
|
||||||
|
Future[] futureArr = new Future[i4];
|
||||||
|
int i5 = 0;
|
||||||
|
while (i5 < i4) {
|
||||||
|
long j9 = i5 * j8;
|
||||||
|
if (i5 == i4 - 1) {
|
||||||
|
j5 = j4;
|
||||||
|
} else {
|
||||||
|
j5 = j9 + j8;
|
||||||
|
}
|
||||||
|
futureArr[i5] = c.a(new e(gVar, j9, j5, j6));
|
||||||
|
i5++;
|
||||||
|
gVar = gVar;
|
||||||
|
j6 = j6;
|
||||||
|
}
|
||||||
|
g gVar2 = gVar;
|
||||||
|
try {
|
||||||
|
c.b(futureArr);
|
||||||
|
return;
|
||||||
|
} catch (InterruptedException unused) {
|
||||||
|
r.f12109a.setMemory(gVar2.f12105j, gVar2.f12103h * j4, (byte) 0);
|
||||||
|
return;
|
||||||
|
} catch (ExecutionException unused2) {
|
||||||
|
r.f12109a.setMemory(gVar2.f12105j, j4 * gVar2.f12103h, (byte) 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.f12109a.setMemory(j6, gVar.f12103h * j4, (byte) 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj != null && (obj instanceof g)) {
|
||||||
|
g gVar = (g) obj;
|
||||||
|
if (this.f == gVar.f && this.f12102g == gVar.f12102g && this.f12103h == gVar.f12103h && this.f12104i == gVar.f12104i && this.f12105j == gVar.f12105j) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
int i4;
|
||||||
|
q qVar = this.f;
|
||||||
|
if (qVar != null) {
|
||||||
|
i4 = qVar.hashCode();
|
||||||
|
} else {
|
||||||
|
i4 = 0;
|
||||||
|
}
|
||||||
|
long j4 = this.f12102g;
|
||||||
|
int i5 = (((203 + i4) * 29) + ((int) (j4 ^ (j4 >>> 32)))) * 29;
|
||||||
|
long j5 = this.f12103h;
|
||||||
|
int i6 = (((i5 + ((int) (j5 ^ (j5 >>> 32)))) * 29) + (this.f12104i ? 1 : 0)) * 841;
|
||||||
|
long j6 = this.f12105j;
|
||||||
|
return i6 + ((int) (j6 ^ (j6 >>> 32)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package k3;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class l extends q {
|
||||||
|
public l() {
|
||||||
|
super("LONG", 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package k3;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class m extends q {
|
||||||
|
public m() {
|
||||||
|
super("FLOAT", 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package k3;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class n extends q {
|
||||||
|
public n() {
|
||||||
|
super("DOUBLE", 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package k3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* R8 生成的伪 enum(k3 FFT 库的数组类型枚举)。
|
||||||
|
* smali 中 q 的直接子类:l(LONG/5)、m(FLOAT/6)、n(DOUBLE/7),
|
||||||
|
* 其余常量(BYTE/UNSIGNED_BYTE/SHORT/INT/COMPLEX_FLOAT/COMPLEX_DOUBLE/STRING/OBJECT)
|
||||||
|
* 直接以 q 实例表达。
|
||||||
|
*/
|
||||||
|
public class q {
|
||||||
|
public static final l f;
|
||||||
|
|
||||||
|
/* renamed from: g, reason: collision with root package name */
|
||||||
|
public static final m f12106g;
|
||||||
|
|
||||||
|
/* renamed from: h, reason: collision with root package name */
|
||||||
|
public static final n f12107h;
|
||||||
|
|
||||||
|
/* renamed from: i, reason: collision with root package name */
|
||||||
|
public static final /* synthetic */ q[] f12108i;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final int ordinal;
|
||||||
|
|
||||||
|
protected q(String name, int ordinal) {
|
||||||
|
this.name = name;
|
||||||
|
this.ordinal = ordinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int ordinal() {
|
||||||
|
return ordinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static q valueOf(String str) {
|
||||||
|
for (q qVar : f12108i) {
|
||||||
|
if (qVar.name.equals(str)) {
|
||||||
|
return qVar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("No enum constant k3.q." + str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static q[] values() {
|
||||||
|
return (q[]) f12108i.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
q logic = new q("LOGIC", 0);
|
||||||
|
q byteType = new q("BYTE", 1);
|
||||||
|
q unsignedByte = new q("UNSIGNED_BYTE", 2);
|
||||||
|
q shortType = new q("SHORT", 3);
|
||||||
|
q intType = new q("INT", 4);
|
||||||
|
l longType = new l();
|
||||||
|
f = longType;
|
||||||
|
m floatType = new m();
|
||||||
|
f12106g = floatType;
|
||||||
|
n doubleType = new n();
|
||||||
|
f12107h = doubleType;
|
||||||
|
q complexFloat = new q("COMPLEX_FLOAT", 8);
|
||||||
|
q complexDouble = new q("COMPLEX_DOUBLE", 9);
|
||||||
|
q string = new q("STRING", 10);
|
||||||
|
q object = new q("OBJECT", 11);
|
||||||
|
f12108i = new q[]{logic, byteType, unsignedByte, shortType, intType, longType, floatType, doubleType, complexFloat, complexDouble, string, object};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package k3;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import com.rtbishop.look4sat.feature.cw.suncompat.Unsafe;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public abstract class r {
|
||||||
|
|
||||||
|
/* renamed from: a, reason: collision with root package name */
|
||||||
|
public static final Unsafe f12109a;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Object obj = null;
|
||||||
|
try {
|
||||||
|
Class<?> cls = Class.forName("sun.misc.Unsafe");
|
||||||
|
Field declaredField = cls.getDeclaredField("theUnsafe");
|
||||||
|
declaredField.setAccessible(true);
|
||||||
|
obj = declaredField.get(cls);
|
||||||
|
} catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException | NoSuchFieldException | SecurityException ignored) {
|
||||||
|
// Android 适配: sun.misc.Unsafe 可能不可用(隐藏 API), 静默降级为 null;
|
||||||
|
// 大数组 native 路径(j4 > 1GB)不会在实际解码中触发
|
||||||
|
}
|
||||||
|
f12109a = (Unsafe) obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void a(long j4, long j5, long j6, d dVar, d dVar2) {
|
||||||
|
long j7;
|
||||||
|
if (j4 >= 0 && j4 < dVar.f12102g) {
|
||||||
|
if (j5 >= 0 && j5 < dVar2.f12102g) {
|
||||||
|
if (j6 >= 0) {
|
||||||
|
if (!dVar2.f12104i) {
|
||||||
|
long j8 = c.c;
|
||||||
|
double[][] dArr = g3.c.f11585a;
|
||||||
|
if (j6 <= j8) {
|
||||||
|
j8 = j6;
|
||||||
|
}
|
||||||
|
int i4 = (int) j8;
|
||||||
|
if (i4 >= 2 && j6 >= c.f12095d) {
|
||||||
|
long j9 = j6 / i4;
|
||||||
|
Future[] futureArr = new Future[i4];
|
||||||
|
int i5 = 0;
|
||||||
|
while (i5 < i4) {
|
||||||
|
long j10 = i5 * j9;
|
||||||
|
if (i5 == i4 - 1) {
|
||||||
|
j7 = j6;
|
||||||
|
} else {
|
||||||
|
j7 = j10 + j9;
|
||||||
|
}
|
||||||
|
Future[] futureArr2 = futureArr;
|
||||||
|
int i6 = i5;
|
||||||
|
futureArr2[i6] = c.a(new j3.b(j10, j7, j5, j4, dVar2, dVar));
|
||||||
|
i5 = i6 + 1;
|
||||||
|
futureArr = futureArr2;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
c.b(futureArr);
|
||||||
|
return;
|
||||||
|
} catch (InterruptedException unused) {
|
||||||
|
long j11 = j4;
|
||||||
|
long j12 = j5;
|
||||||
|
while (j11 < j4 + j6) {
|
||||||
|
dVar2.c(j12, dVar.b(j11));
|
||||||
|
j11++;
|
||||||
|
j12++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} catch (ExecutionException unused2) {
|
||||||
|
long j13 = j4;
|
||||||
|
long j14 = j5;
|
||||||
|
while (j13 < j4 + j6) {
|
||||||
|
dVar2.c(j14, dVar.b(j13));
|
||||||
|
j13++;
|
||||||
|
j14++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long j15 = j4;
|
||||||
|
long j16 = j5;
|
||||||
|
while (j15 < j4 + j6) {
|
||||||
|
dVar2.c(j16, dVar.b(j15));
|
||||||
|
j15++;
|
||||||
|
j16++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Constant arrays cannot be modified.");
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("length < 0");
|
||||||
|
}
|
||||||
|
throw new ArrayIndexOutOfBoundsException("destPos < 0 || destPos >= dest.length()");
|
||||||
|
}
|
||||||
|
throw new ArrayIndexOutOfBoundsException("srcPos < 0 || srcPos >= src.length()");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package k3;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import com.rtbishop.look4sat.feature.cw.suncompat.Cleaner;
|
||||||
|
import com.rtbishop.look4sat.feature.cw.suncompat.Unsafe;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public final class s extends g {
|
||||||
|
|
||||||
|
/* renamed from: k, reason: collision with root package name */
|
||||||
|
public long[] f12110k;
|
||||||
|
|
||||||
|
public s() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public s(long j4, boolean z3) {
|
||||||
|
this.f = q.f;
|
||||||
|
this.f12103h = 8L;
|
||||||
|
if (j4 > 0) {
|
||||||
|
this.f12102g = j4;
|
||||||
|
if (j4 > 1073741824) {
|
||||||
|
this.f12105j = r.f12109a.allocateMemory(8 * j4);
|
||||||
|
if (z3) {
|
||||||
|
a(j4);
|
||||||
|
}
|
||||||
|
Cleaner.create(this, new f(this.f12105j, this.f12102g, this.f12103h));
|
||||||
|
K1.a.c += this.f12102g * this.f12103h;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.f12110k = new long[(int) j4];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException(j4 + " is not a positive long value");
|
||||||
|
}
|
||||||
|
|
||||||
|
public final long b(long j4) {
|
||||||
|
long[] jArr = this.f12110k;
|
||||||
|
long j5 = this.f12105j;
|
||||||
|
if (j5 != 0) {
|
||||||
|
return r.f12109a.getLong((this.f12103h * j4) + j5);
|
||||||
|
}
|
||||||
|
if (this.f12104i) {
|
||||||
|
return jArr[0];
|
||||||
|
}
|
||||||
|
return jArr[(int) j4];
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void c(long j4, long j5) {
|
||||||
|
long j6 = this.f12105j;
|
||||||
|
if (j6 != 0) {
|
||||||
|
r.f12109a.putLong((this.f12103h * j4) + j6, j5);
|
||||||
|
} else {
|
||||||
|
if (!this.f12104i) {
|
||||||
|
this.f12110k[(int) j4] = j5;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new IllegalAccessError("Constant arrays cannot be modified.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* JADX WARN: Type inference failed for: r0v29, types: [k3.g, java.lang.Object, k3.s] */
|
||||||
|
public final Object clone() {
|
||||||
|
long j4;
|
||||||
|
int i4 = 0;
|
||||||
|
long j5 = 0;
|
||||||
|
if (this.f12104i) {
|
||||||
|
long j6 = this.f12102g;
|
||||||
|
long b4 = b(0L);
|
||||||
|
s gVar = new s();
|
||||||
|
gVar.f = q.f12107h;
|
||||||
|
gVar.f12103h = 8L;
|
||||||
|
if (j6 > 0) {
|
||||||
|
gVar.f12102g = j6;
|
||||||
|
gVar.f12104i = true;
|
||||||
|
gVar.f12110k = new long[]{b4};
|
||||||
|
return gVar;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException(j6 + " is not a positive long value");
|
||||||
|
}
|
||||||
|
s sVar = new s(this.f12102g, false);
|
||||||
|
long j7 = this.f12102g;
|
||||||
|
Unsafe unsafe = r.f12109a;
|
||||||
|
if (0 < j7) {
|
||||||
|
if (0 < sVar.f12102g) {
|
||||||
|
if (j7 >= 0) {
|
||||||
|
if (!sVar.f12104i) {
|
||||||
|
long j8 = c.c;
|
||||||
|
double[][] dArr = g3.c.f11585a;
|
||||||
|
if (j7 <= j8) {
|
||||||
|
j8 = j7;
|
||||||
|
}
|
||||||
|
int i5 = (int) j8;
|
||||||
|
if (i5 >= 2 && j7 >= c.f12095d) {
|
||||||
|
long j9 = j7 / i5;
|
||||||
|
Future[] futureArr = new Future[i5];
|
||||||
|
while (i4 < i5) {
|
||||||
|
long j10 = i4 * j9;
|
||||||
|
if (i4 == i5 - 1) {
|
||||||
|
j4 = j7;
|
||||||
|
} else {
|
||||||
|
j4 = j10 + j9;
|
||||||
|
}
|
||||||
|
Future[] futureArr2 = futureArr;
|
||||||
|
int i6 = i4;
|
||||||
|
futureArr2[i6] = c.a(new i3.c(j10, j4, sVar, this));
|
||||||
|
i4 = i6 + 1;
|
||||||
|
futureArr = futureArr2;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
c.b(futureArr);
|
||||||
|
return sVar;
|
||||||
|
} catch (InterruptedException unused) {
|
||||||
|
long j11 = 0;
|
||||||
|
while (j5 < j7) {
|
||||||
|
sVar.c(j11, b(j5));
|
||||||
|
j5++;
|
||||||
|
j11++;
|
||||||
|
}
|
||||||
|
} catch (ExecutionException unused2) {
|
||||||
|
long j12 = 0;
|
||||||
|
while (j5 < j7) {
|
||||||
|
sVar.c(j12, b(j5));
|
||||||
|
j5++;
|
||||||
|
j12++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
long j13 = 0;
|
||||||
|
while (j5 < j7) {
|
||||||
|
sVar.c(j13, b(j5));
|
||||||
|
j5++;
|
||||||
|
j13++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sVar;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Constant arrays cannot be modified.");
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("length < 0");
|
||||||
|
}
|
||||||
|
throw new ArrayIndexOutOfBoundsException("destPos < 0 || destPos >= dest.length()");
|
||||||
|
}
|
||||||
|
throw new ArrayIndexOutOfBoundsException("srcPos < 0 || srcPos >= src.length()");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // k3.g
|
||||||
|
public final boolean equals(Object obj) {
|
||||||
|
if (!super.equals(obj) || this.f12110k != ((s) obj).f12110k) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // k3.g
|
||||||
|
public final int hashCode() {
|
||||||
|
int i4;
|
||||||
|
int hashCode = super.hashCode() * 29;
|
||||||
|
long[] jArr = this.f12110k;
|
||||||
|
if (jArr != null) {
|
||||||
|
i4 = jArr.hashCode();
|
||||||
|
} else {
|
||||||
|
i4 = 0;
|
||||||
|
}
|
||||||
|
return hashCode + i4;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package pas;
|
||||||
|
|
||||||
|
import pas.system;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public class cwstru {
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class TMorseCharInfo extends system.Record {
|
||||||
|
public TMorseCharInfo(long j4, boolean z3) {
|
||||||
|
__Init(j4, z3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void __Destroy(long j4);
|
||||||
|
|
||||||
|
@Override // pas.system.PascalObjectEx
|
||||||
|
public void __Release() {
|
||||||
|
__Destroy(this._pasobj);
|
||||||
|
this._pasobj = 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // pas.system.Record
|
||||||
|
public int __Size() {
|
||||||
|
return __Size(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public native char getCh();
|
||||||
|
|
||||||
|
public native int getIdxE();
|
||||||
|
|
||||||
|
public native boolean getLowSnr();
|
||||||
|
|
||||||
|
public native TWordEndType getWordEnd();
|
||||||
|
|
||||||
|
public native float getWordSpaceProb();
|
||||||
|
|
||||||
|
public native TMorseWordType getWordType();
|
||||||
|
|
||||||
|
public native void setCh(char c);
|
||||||
|
|
||||||
|
public native void setIdxE(int i4);
|
||||||
|
|
||||||
|
public native void setLowSnr(boolean z3);
|
||||||
|
|
||||||
|
public native void setWordEnd(TWordEndType tWordEndType);
|
||||||
|
|
||||||
|
public native void setWordSpaceProb(float f);
|
||||||
|
|
||||||
|
public native void setWordType(TMorseWordType tMorseWordType);
|
||||||
|
|
||||||
|
public TMorseCharInfo() {
|
||||||
|
__Init(0L, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TMorseCharInfo(system.PascalObject pascalObject) {
|
||||||
|
super(pascalObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TMorseCharInfo(long j4) {
|
||||||
|
super(j4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class TMorseWordType extends system.Enum {
|
||||||
|
public static final int wtCall = 5;
|
||||||
|
public static final int wtCq = 3;
|
||||||
|
public static final int wtGenWord = 0;
|
||||||
|
public static final int wtKeyword = 1;
|
||||||
|
public static final int wtQrl = 4;
|
||||||
|
public static final int wtRst = 2;
|
||||||
|
|
||||||
|
public TMorseWordType(int i4) {
|
||||||
|
this.Value = i4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TMorseWordType wtCall() {
|
||||||
|
return new TMorseWordType(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TMorseWordType wtCq() {
|
||||||
|
return new TMorseWordType(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TMorseWordType wtGenWord() {
|
||||||
|
return new TMorseWordType(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TMorseWordType wtKeyword() {
|
||||||
|
return new TMorseWordType(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TMorseWordType wtQrl() {
|
||||||
|
return new TMorseWordType(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TMorseWordType wtRst() {
|
||||||
|
return new TMorseWordType(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // pas.system.Enum
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (((obj instanceof TMorseWordType) && this.Value == ((TMorseWordType) obj).Value) || super.equals(obj)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class TWordEndType extends system.Enum {
|
||||||
|
public static final int weLong = 2;
|
||||||
|
public static final int weNone = 0;
|
||||||
|
public static final int weShort = 1;
|
||||||
|
|
||||||
|
public TWordEndType(int i4) {
|
||||||
|
this.Value = i4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TWordEndType weLong() {
|
||||||
|
return new TWordEndType(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TWordEndType weNone() {
|
||||||
|
return new TWordEndType(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TWordEndType weShort() {
|
||||||
|
return new TWordEndType(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // pas.system.Enum
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (((obj instanceof TWordEndType) && this.Value == ((TWordEndType) obj).Value) || super.equals(obj)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
system.InitJni();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
package pas;
|
||||||
|
|
||||||
|
import pas.cwstru;
|
||||||
|
import pas.system;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public class decoder {
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class TCharInfo extends system.TObject {
|
||||||
|
public TCharInfo(system.PascalObject pascalObject) {
|
||||||
|
super(pascalObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TCharInfo Class() {
|
||||||
|
return new TCharInfo(system.GetClassRef(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static native TCharInfo Create();
|
||||||
|
|
||||||
|
public static native TCharInfo Create(char c);
|
||||||
|
|
||||||
|
public static native TCharInfo Create(cwstru.TMorseCharInfo tMorseCharInfo);
|
||||||
|
|
||||||
|
public static system.TClass TClass() {
|
||||||
|
return system.GetTClass(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public native char getChar();
|
||||||
|
|
||||||
|
public native boolean getLowSnr();
|
||||||
|
|
||||||
|
public native TMorseWordType getWordType();
|
||||||
|
|
||||||
|
public TCharInfo(long j4) {
|
||||||
|
super(j4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class TDecoder extends system.TObject {
|
||||||
|
public TDecoder(system.PascalObject pascalObject) {
|
||||||
|
super(pascalObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TDecoder Class() {
|
||||||
|
return new TDecoder(system.GetClassRef(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static native TDecoder Create();
|
||||||
|
|
||||||
|
public static system.TClass TClass() {
|
||||||
|
return system.GetTClass(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public native TCharInfo GetChar(int i4);
|
||||||
|
|
||||||
|
public native void LockToPitch(int i4);
|
||||||
|
|
||||||
|
public native int getCharCount();
|
||||||
|
|
||||||
|
public native int getDeleteCount();
|
||||||
|
|
||||||
|
public native boolean getHamMode();
|
||||||
|
|
||||||
|
public native int getPitch();
|
||||||
|
|
||||||
|
public native int getSignalCount();
|
||||||
|
|
||||||
|
public native int getSnr();
|
||||||
|
|
||||||
|
public native int getWpm();
|
||||||
|
|
||||||
|
public native void setHamMode(boolean z3);
|
||||||
|
|
||||||
|
public TDecoder(long j4) {
|
||||||
|
super(j4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class TDecoderState extends system.Enum {
|
||||||
|
public static final int dsDecoding = 2;
|
||||||
|
public static final int dsLockedDecoding = 4;
|
||||||
|
public static final int dsLockedPause = 3;
|
||||||
|
public static final int dsNone = 0;
|
||||||
|
public static final int dsPause = 1;
|
||||||
|
|
||||||
|
public TDecoderState(int i4) {
|
||||||
|
this.Value = i4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TDecoderState dsDecoding() {
|
||||||
|
return new TDecoderState(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TDecoderState dsLockedDecoding() {
|
||||||
|
return new TDecoderState(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TDecoderState dsLockedPause() {
|
||||||
|
return new TDecoderState(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TDecoderState dsNone() {
|
||||||
|
return new TDecoderState(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TDecoderState dsPause() {
|
||||||
|
return new TDecoderState(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // pas.system.Enum
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (((obj instanceof TDecoderState) && this.Value == ((TDecoderState) obj).Value) || super.equals(obj)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class TMorseWordType extends system.Enum {
|
||||||
|
public static final int wtCall = 5;
|
||||||
|
public static final int wtCq = 3;
|
||||||
|
public static final int wtGenWord = 0;
|
||||||
|
public static final int wtKeyword = 1;
|
||||||
|
public static final int wtQrl = 4;
|
||||||
|
public static final int wtRst = 2;
|
||||||
|
|
||||||
|
public TMorseWordType(int i4) {
|
||||||
|
this.Value = i4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TMorseWordType wtCall() {
|
||||||
|
return new TMorseWordType(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TMorseWordType wtCq() {
|
||||||
|
return new TMorseWordType(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TMorseWordType wtGenWord() {
|
||||||
|
return new TMorseWordType(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TMorseWordType wtKeyword() {
|
||||||
|
return new TMorseWordType(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TMorseWordType wtQrl() {
|
||||||
|
return new TMorseWordType(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TMorseWordType wtRst() {
|
||||||
|
return new TMorseWordType(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // pas.system.Enum
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (((obj instanceof TMorseWordType) && this.Value == ((TMorseWordType) obj).Value) || super.equals(obj)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
system.InitJni();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package pas;
|
||||||
|
|
||||||
|
import pas.decoder;
|
||||||
|
import pas.system;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public class nativedecoder {
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class TNativeDecoder extends decoder.TDecoder {
|
||||||
|
public TNativeDecoder(system.PascalObject pascalObject) {
|
||||||
|
super(pascalObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TNativeDecoder Class() {
|
||||||
|
return new TNativeDecoder(system.GetClassRef(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static native TNativeDecoder Create();
|
||||||
|
|
||||||
|
public static system.TClass TClass() {
|
||||||
|
return system.GetTClass(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public native decoder.TDecoderState ProcessSpectrum(Object obj);
|
||||||
|
|
||||||
|
public TNativeDecoder(long j4) {
|
||||||
|
super(j4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
system.InitJni();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,385 @@
|
|||||||
|
package pas;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public class system {
|
||||||
|
private static boolean _JniLibLoaded = false;
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static abstract class BaseSet {
|
||||||
|
protected int Value = 0;
|
||||||
|
|
||||||
|
public abstract int Base();
|
||||||
|
|
||||||
|
public abstract int ElMax();
|
||||||
|
|
||||||
|
public abstract byte Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class Enum {
|
||||||
|
public int Value;
|
||||||
|
|
||||||
|
public int Ord() {
|
||||||
|
return this.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return (obj instanceof Integer) && this.Value == ((Integer) obj).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return this.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(int i4) {
|
||||||
|
return this.Value == i4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class MethodPtr extends PascalObjectEx {
|
||||||
|
protected String mName;
|
||||||
|
protected Object mObject;
|
||||||
|
protected String mSignature;
|
||||||
|
|
||||||
|
public MethodPtr() {
|
||||||
|
this._cleanup = true;
|
||||||
|
this._pasobj = -1L;
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void __Destroy();
|
||||||
|
|
||||||
|
private native void __Init(Object obj, String str, String str2, boolean z3);
|
||||||
|
|
||||||
|
public void Init() {
|
||||||
|
boolean z3;
|
||||||
|
Object obj = this.mObject;
|
||||||
|
String str = this.mName;
|
||||||
|
String str2 = this.mSignature;
|
||||||
|
if (this != obj) {
|
||||||
|
z3 = true;
|
||||||
|
} else {
|
||||||
|
z3 = false;
|
||||||
|
}
|
||||||
|
__Init(obj, str, str2, z3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // pas.system.PascalObjectEx
|
||||||
|
public void __Release() {
|
||||||
|
if (this._pasobj > 0) {
|
||||||
|
__Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static abstract class PascalInterface extends PascalObjectEx {
|
||||||
|
public PascalInterface(long j4, boolean z3) {
|
||||||
|
this._pasobj = j4;
|
||||||
|
__Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void __Init();
|
||||||
|
|
||||||
|
public void __TypeCast(PascalObject pascalObject, String str) {
|
||||||
|
if (pascalObject != null) {
|
||||||
|
if (pascalObject instanceof PascalInterface) {
|
||||||
|
this._pasobj = pascalObject._pasobj;
|
||||||
|
__Init();
|
||||||
|
} else {
|
||||||
|
this._pasobj = system.InterfaceCast(pascalObject._pasobj, str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class Record extends PascalObjectEx {
|
||||||
|
protected PascalObject _objref;
|
||||||
|
|
||||||
|
public Record(PascalObject pascalObject) {
|
||||||
|
super(pascalObject);
|
||||||
|
this._objref = pascalObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void __Init(long j4, boolean z3) {
|
||||||
|
this._pasobj = j4;
|
||||||
|
this._cleanup = z3;
|
||||||
|
if (j4 == 0 && __Size() != 0) {
|
||||||
|
this._pasobj = system.AllocMemory(__Size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int __Size() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // pas.system.PascalObjectEx, pas.system.PascalObject
|
||||||
|
public void finalize() {
|
||||||
|
long j4 = this._pasobj;
|
||||||
|
if (j4 < 0) {
|
||||||
|
this._pasobj = -j4;
|
||||||
|
this._cleanup = true;
|
||||||
|
}
|
||||||
|
super.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Record(long j4) {
|
||||||
|
super(j4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int __Size(int i4) {
|
||||||
|
return system.GetRecordSize(i4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Record() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static abstract class Set<TS extends BaseSet, TE extends Enum> extends BaseSet {
|
||||||
|
public Set() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Assign(TS ts) {
|
||||||
|
this.Value = ts.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Exclude(TE... teArr) {
|
||||||
|
for (TE te : teArr) {
|
||||||
|
this.Value = (~GetMask(te)) & this.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetMask(TE te) {
|
||||||
|
return 1 << (te.Ord() - Base());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean Has(TE te) {
|
||||||
|
if ((GetMask(te) & this.Value) != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Include(TE... teArr) {
|
||||||
|
for (TE te : teArr) {
|
||||||
|
this.Value = GetMask(te) | this.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Intersect(TS ts) {
|
||||||
|
this.Value = ts.Value & this.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean IsEmpty() {
|
||||||
|
if (this.Value == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(TS ts) {
|
||||||
|
return this.Value == ts.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set(TE... teArr) {
|
||||||
|
Include(teArr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Exclude(TS ts) {
|
||||||
|
this.Value = (~ts.Value) & this.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Include(TS ts) {
|
||||||
|
this.Value = ts.Value | this.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(TE te) {
|
||||||
|
return this.Value == te.Ord();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set(TS... tsArr) {
|
||||||
|
for (TS ts : tsArr) {
|
||||||
|
Include(ts); // smali: Set(TS...) 循环调 Include(BaseSet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(int i4) {
|
||||||
|
return this.Value == i4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class TClass extends TObject {
|
||||||
|
public TClass(PascalObject pascalObject) {
|
||||||
|
super(pascalObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class TDateTime {
|
||||||
|
public static double get(Date date) {
|
||||||
|
return ((date.getTime() + TimeZone.getDefault().getOffset(date.getTime())) / 8.64E7d) + 25569.0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getUTC(Date date) {
|
||||||
|
return (date.getTime() / 8.64E7d) + 25569.0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Date toDate(double d4) {
|
||||||
|
return new Date(Math.round((d4 - 25569.0d) * 8.64E7d) - TimeZone.getDefault().getOffset(Math.round((d4 - 25569.0d) * 8.64E7d))); // r3 = 前值(smali)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Date toDateUTC(double d4) {
|
||||||
|
return new Date(Math.round((d4 - 25569.0d) * 8.64E7d));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class TObject extends PascalObject {
|
||||||
|
public TObject(PascalObject pascalObject) {
|
||||||
|
super(pascalObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TObject Class() {
|
||||||
|
return new TObject(system.GetClassRef(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static native TObject Create();
|
||||||
|
|
||||||
|
public static TClass TClass() {
|
||||||
|
return system.GetTClass(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public native String ClassName();
|
||||||
|
|
||||||
|
public native boolean ClassNameIs(String str);
|
||||||
|
|
||||||
|
public native TClass ClassParent();
|
||||||
|
|
||||||
|
public native TClass ClassType();
|
||||||
|
|
||||||
|
public native void Destroy();
|
||||||
|
|
||||||
|
public native void Free();
|
||||||
|
|
||||||
|
public native boolean InheritsFrom(TClass tClass);
|
||||||
|
|
||||||
|
public native int InstanceSize();
|
||||||
|
|
||||||
|
public TObject(long j4) {
|
||||||
|
super(j4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
InitJni();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static native long AllocMemory(int i4);
|
||||||
|
|
||||||
|
public static native long GetClassRef(int i4);
|
||||||
|
|
||||||
|
public static native byte[] GetMemoryAsArray(long j4, int i4);
|
||||||
|
|
||||||
|
/* JADX INFO: Access modifiers changed from: private */
|
||||||
|
public static native int GetRecordSize(int i4);
|
||||||
|
|
||||||
|
public static TClass GetTClass(int i4) {
|
||||||
|
TClass tClass = new TClass(null);
|
||||||
|
tClass._pasobj = GetClassRef(i4);
|
||||||
|
return tClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InitJni() {
|
||||||
|
if (!_JniLibLoaded) {
|
||||||
|
_JniLibLoaded = true;
|
||||||
|
System.loadLibrary("nativedecoderjni");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* JADX INFO: Access modifiers changed from: private */
|
||||||
|
public static native long InterfaceCast(long j4, String str);
|
||||||
|
|
||||||
|
public static long Pointer(PascalObject pascalObject) {
|
||||||
|
if (pascalObject == null) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
return pascalObject._pasobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static native void SetMemoryFromArray(long j4, byte[] bArr);
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class PascalObject {
|
||||||
|
protected long _pasobj;
|
||||||
|
|
||||||
|
static {
|
||||||
|
system.InitJni();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PascalObject() {
|
||||||
|
this._pasobj = 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if ((obj instanceof PascalObject) && this._pasobj == ((PascalObject) obj)._pasobj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finalize() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return (int) this._pasobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PascalObject(PascalObject pascalObject) {
|
||||||
|
this._pasobj = 0L;
|
||||||
|
if (pascalObject != null) {
|
||||||
|
this._pasobj = pascalObject._pasobj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PascalObject(long j4) {
|
||||||
|
this._pasobj = j4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public static class PascalObjectEx extends PascalObject {
|
||||||
|
protected boolean _cleanup;
|
||||||
|
|
||||||
|
public PascalObjectEx() {
|
||||||
|
this._cleanup = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void __Release() {
|
||||||
|
this._pasobj = 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // pas.system.PascalObject
|
||||||
|
public void finalize() {
|
||||||
|
if (this._cleanup) {
|
||||||
|
__Release();
|
||||||
|
}
|
||||||
|
super.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PascalObjectEx(PascalObject pascalObject) {
|
||||||
|
super(pascalObject);
|
||||||
|
this._cleanup = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PascalObjectEx(long j4) {
|
||||||
|
super(j4);
|
||||||
|
this._cleanup = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package s;
|
||||||
|
|
||||||
|
/* renamed from: s.e, reason: case insensitive filesystem */
|
||||||
|
/* loaded from: classes.dex */
|
||||||
|
public abstract /* synthetic */ class AbstractC1880e {
|
||||||
|
|
||||||
|
/* renamed from: a, reason: collision with root package name */
|
||||||
|
public static final /* synthetic */ int[] f13163a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28};
|
||||||
|
|
||||||
|
public static /* synthetic */ int a(int i4) {
|
||||||
|
if (i4 != 0) {
|
||||||
|
return i4 - 1;
|
||||||
|
}
|
||||||
|
throw null;
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:id="@+id/verticalLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:keepScreenOn="true">
|
||||||
|
<TextView
|
||||||
|
android:textColor="#ffffff"
|
||||||
|
android:id="@+id/statusTextView"
|
||||||
|
android:background="@color/purple_500"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/idle"/>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="100dp">
|
||||||
|
<com.ve3nea.morse_expert.WaterfallSurfaceView
|
||||||
|
android:id="@+id/waterfallView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:contentDescription="@string/waterfall_display"/>
|
||||||
|
<com.ve3nea.morse_expert.ScaleView
|
||||||
|
android:id="@+id/scaleView"
|
||||||
|
android:background="#24545a"
|
||||||
|
android:layout_width="12dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:contentDescription="@string/frequency_scale"/>
|
||||||
|
</LinearLayout>
|
||||||
|
<com.ve3nea.morse_expert.DecodedTextView
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:typeface="monospace"
|
||||||
|
android:textColor="#444444"
|
||||||
|
android:textColorHint="#888888"
|
||||||
|
android:gravity="start|top"
|
||||||
|
android:id="@+id/decodedTextView"
|
||||||
|
android:background="#d1f0f4"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:hint="@string/decoded_text"
|
||||||
|
android:freezesText="false"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:inputType="none"
|
||||||
|
android:fadeScrollbars="false"
|
||||||
|
android:textIsSelectable="true"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:id="@+id/verticalLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:keepScreenOn="true">
|
||||||
|
<TextView
|
||||||
|
android:textColor="#ffffff"
|
||||||
|
android:textSize="10sp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:id="@+id/statusTextView"
|
||||||
|
android:background="@color/purple_500"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/idle"/>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="80dp">
|
||||||
|
<com.ve3nea.morse_expert.WaterfallSurfaceView
|
||||||
|
android:id="@+id/waterfallView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:contentDescription="@string/waterfall_display"/>
|
||||||
|
<com.ve3nea.morse_expert.ScaleView
|
||||||
|
android:id="@+id/scaleView"
|
||||||
|
android:background="#24545a"
|
||||||
|
android:layout_width="8dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:contentDescription="@string/frequency_scale"/>
|
||||||
|
</LinearLayout>
|
||||||
|
<com.ve3nea.morse_expert.DecodedTextView
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:typeface="monospace"
|
||||||
|
android:textColor="#444444"
|
||||||
|
android:textColorHint="#888888"
|
||||||
|
android:gravity="start|top"
|
||||||
|
android:id="@+id/decodedTextView"
|
||||||
|
android:background="#d1f0f4"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:hint="@string/decoded_text"
|
||||||
|
android:freezesText="false"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:inputType="none"
|
||||||
|
android:fadeScrollbars="false"
|
||||||
|
android:textIsSelectable="true"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:icon="@drawable/ic_baseline_pause_24"
|
||||||
|
android:id="@+id/pause_mnu"
|
||||||
|
android:title="@string/pause"
|
||||||
|
app:showAsAction="ifRoom"/>
|
||||||
|
<item
|
||||||
|
android:icon="@drawable/ic_baseline_delete_24"
|
||||||
|
android:id="@+id/clear_mnu"
|
||||||
|
android:title="@string/clear"
|
||||||
|
app:showAsAction="ifRoom"/>
|
||||||
|
<item
|
||||||
|
android:icon="@drawable/ic_baseline_save_24"
|
||||||
|
android:id="@+id/save_mnu"
|
||||||
|
android:title="@string/save_text"
|
||||||
|
app:showAsAction="ifRoom"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/record_signals_mnu"
|
||||||
|
android:title="@string/record_signals"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/settings_mnu"
|
||||||
|
android:title="@string/settings"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/help_mnu"
|
||||||
|
android:title="@string/help"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/rate_mnu"
|
||||||
|
android:title="@string/rate"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/premium_mnu"
|
||||||
|
android:title="@string/premium"/>
|
||||||
|
</menu>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="purple_500">#ff6200ee</color>
|
||||||
|
<integer name="default_font_size">18</integer>
|
||||||
|
<string name="idle">Diam</string>
|
||||||
|
<string name="waterfall_display">Tampilan waterfall</string>
|
||||||
|
<string name="frequency_scale">Skala frekuensi</string>
|
||||||
|
<string name="decoded_text">Teks terdekode</string>
|
||||||
|
<string name="clear">Bersihkan</string>
|
||||||
|
<string name="pause">Jeda Dekode</string>
|
||||||
|
<string name="save_text">Simpan Teks</string>
|
||||||
|
<string name="record_signals">Rekam Sinyal</string>
|
||||||
|
<string name="settings">Pengaturan</string>
|
||||||
|
<string name="help">Bantuan Daring</string>
|
||||||
|
<string name="rate">Nilai Aplikasi Ini</string>
|
||||||
|
<string name="premium">Dapatkan Versi Premium</string>
|
||||||
|
<string name="tap_back_again_to_close">Ketuk Kembali lagi untuk menutup aplikasi</string>
|
||||||
|
<string name="message_type">Tipe Pesan</string>
|
||||||
|
<string name="cw_mic_permission">Izin mikrofon diperlukan untuk dekode CW</string>
|
||||||
|
<string name="cw_grant_permission">Beri izin</string>
|
||||||
|
<string name="cw_open_settings">Buka pengaturan aplikasi</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="purple_500">#ff6200ee</color>
|
||||||
|
<integer name="default_font_size">18</integer>
|
||||||
|
<string name="idle">Beklemede</string>
|
||||||
|
<string name="waterfall_display">Şelale görüntüsü</string>
|
||||||
|
<string name="frequency_scale">Frekans ölçeği</string>
|
||||||
|
<string name="decoded_text">Çözülen metin</string>
|
||||||
|
<string name="clear">Temizle</string>
|
||||||
|
<string name="pause">Çözmeyi Duraklat</string>
|
||||||
|
<string name="save_text">Metni Kaydet</string>
|
||||||
|
<string name="record_signals">Sinyalleri Kaydet</string>
|
||||||
|
<string name="settings">Ayarlar</string>
|
||||||
|
<string name="help">Çevrimiçi Yardım</string>
|
||||||
|
<string name="rate">Bu Uygulamayı Değerlendir</string>
|
||||||
|
<string name="premium">Premium Sürümü Al</string>
|
||||||
|
<string name="tap_back_again_to_close">Kapatmak için Geriye tekrar dokunun</string>
|
||||||
|
<string name="message_type">Mesaj Türü</string>
|
||||||
|
<string name="cw_mic_permission">CW çözme için mikrofon izni gereklidir</string>
|
||||||
|
<string name="cw_grant_permission">İzin ver</string>
|
||||||
|
<string name="cw_open_settings">Uygulama ayarlarını aç</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="purple_500">#ff6200ee</color>
|
||||||
|
<integer name="default_font_size">18</integer>
|
||||||
|
<string name="idle">空闲</string>
|
||||||
|
<string name="waterfall_display">瀑布图</string>
|
||||||
|
<string name="frequency_scale">频率刻度</string>
|
||||||
|
<string name="decoded_text">解码文本</string>
|
||||||
|
<string name="clear">清除</string>
|
||||||
|
<string name="pause">暂停解码</string>
|
||||||
|
<string name="save_text">保存文本</string>
|
||||||
|
<string name="record_signals">录音信号</string>
|
||||||
|
<string name="settings">设置</string>
|
||||||
|
<string name="help">在线帮助</string>
|
||||||
|
<string name="rate">评价此应用</string>
|
||||||
|
<string name="premium">获取高级版</string>
|
||||||
|
<string name="tap_back_again_to_close">再次按返回键退出应用</string>
|
||||||
|
<string name="message_type">消息类型</string>
|
||||||
|
<string name="cw_mic_permission">CW 解码需要麦克风权限</string>
|
||||||
|
<string name="cw_grant_permission">授予权限</string>
|
||||||
|
<string name="cw_open_settings">打开应用设置</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="purple_500">#ff6200ee</color>
|
||||||
|
<integer name="default_font_size">18</integer>
|
||||||
|
<string name="idle">Idle</string>
|
||||||
|
<string name="waterfall_display">Waterfall display</string>
|
||||||
|
<string name="frequency_scale">Frequency scale</string>
|
||||||
|
<string name="decoded_text">Decoded text</string>
|
||||||
|
<string name="clear">Clear</string>
|
||||||
|
<string name="pause">Pause Decoding</string>
|
||||||
|
<string name="save_text">Save Text</string>
|
||||||
|
<string name="record_signals">Record Signals</string>
|
||||||
|
<string name="settings">Settings</string>
|
||||||
|
<string name="help">Online Help</string>
|
||||||
|
<string name="rate">Rate this App</string>
|
||||||
|
<string name="premium">Get Premium Version</string>
|
||||||
|
<string name="tap_back_again_to_close">Tap Back again to close the app</string>
|
||||||
|
<string name="message_type">Message Type</string>
|
||||||
|
<string name="cw_mic_permission">Microphone permission is required for CW decoding</string>
|
||||||
|
<string name="cw_grant_permission">Grant permission</string>
|
||||||
|
<string name="cw_open_settings">Open app settings</string>
|
||||||
|
</resources>
|
||||||
@@ -44,6 +44,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
@@ -57,6 +58,7 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
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
|
||||||
import com.rtbishop.look4sat.core.domain.repository.MutualPassData
|
import com.rtbishop.look4sat.core.domain.repository.MutualPassData
|
||||||
@@ -94,22 +96,29 @@ fun MutualScreen(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomInfo = {
|
bottomInfo = {
|
||||||
if (state.mutualPasses.isNotEmpty()) {
|
Text(
|
||||||
Text(
|
text = mutualStatusText(state),
|
||||||
text = "找到 ${state.mutualPasses.size} 个匹配",
|
style = MaterialTheme.typography.bodySmall,
|
||||||
style = MaterialTheme.typography.bodySmall,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
maxLines = 1,
|
||||||
)
|
overflow = TextOverflow.Ellipsis,
|
||||||
}
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
endAction = {
|
endAction = {
|
||||||
if (state.isCalculating) {
|
Row(
|
||||||
CircularProgressIndicator(
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
horizontalArrangement = Arrangement.spacedBy(6.dp)
|
||||||
.height(24.dp)
|
) {
|
||||||
.width(24.dp),
|
if (state.isCalculating) {
|
||||||
strokeWidth = 2.dp
|
CircularProgressIndicator(
|
||||||
)
|
modifier = Modifier
|
||||||
|
.height(24.dp)
|
||||||
|
.width(24.dp),
|
||||||
|
strokeWidth = 2.dp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MutualStatusChip(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -187,8 +196,6 @@ private fun MutualContent(
|
|||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
verticalArrangement = Arrangement.spacedBy(6.dp)
|
verticalArrangement = Arrangement.spacedBy(6.dp)
|
||||||
) {
|
) {
|
||||||
MatchIntroCard()
|
|
||||||
|
|
||||||
if (isVertical) {
|
if (isVertical) {
|
||||||
StationInputCard(
|
StationInputCard(
|
||||||
title = "你的位置",
|
title = "你的位置",
|
||||||
@@ -304,25 +311,50 @@ private fun MutualContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MatchIntroCard(modifier: Modifier = Modifier) {
|
private fun MutualStatusChip(state: MutualUiState) {
|
||||||
ElevatedCard(modifier = modifier.fillMaxWidth()) {
|
val colorScheme = MaterialTheme.colorScheme
|
||||||
Column(
|
val containerColor = when {
|
||||||
modifier = Modifier.padding(12.dp),
|
state.isCalculating -> colorScheme.primaryContainer
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
state.errorMessage != null -> colorScheme.errorContainer
|
||||||
) {
|
state.mutualPasses.isNotEmpty() -> colorScheme.primary
|
||||||
Text(
|
state.hasSearched -> colorScheme.surfaceVariant
|
||||||
text = "位置匹配",
|
else -> colorScheme.surfaceVariant
|
||||||
style = MaterialTheme.typography.titleSmall,
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
color = MaterialTheme.colorScheme.primary
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "输入双方位置,查找共同可见的卫星过境窗口",
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
val contentColor = when {
|
||||||
|
state.isCalculating -> colorScheme.onPrimaryContainer
|
||||||
|
state.errorMessage != null -> colorScheme.onErrorContainer
|
||||||
|
state.mutualPasses.isNotEmpty() -> colorScheme.onPrimary
|
||||||
|
else -> colorScheme.onSurfaceVariant
|
||||||
|
}
|
||||||
|
Surface(
|
||||||
|
color = containerColor,
|
||||||
|
contentColor = contentColor,
|
||||||
|
shape = MaterialTheme.shapes.small
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = mutualStatusChipText(state),
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
maxLines = 1,
|
||||||
|
modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mutualStatusText(state: MutualUiState): String = when {
|
||||||
|
state.isCalculating -> "正在计算双方共同可见窗口…"
|
||||||
|
state.errorMessage != null -> "请修正提示后重新查询"
|
||||||
|
state.mutualPasses.isNotEmpty() -> "找到 ${state.mutualPasses.size} 个匹配 · 点击卡片展开曲线"
|
||||||
|
state.hasSearched -> "未找到共同可见窗口,调整时间或仰角后重试"
|
||||||
|
else -> "输入双方位置后点击查询"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mutualStatusChipText(state: MutualUiState): String = when {
|
||||||
|
state.isCalculating -> "计算中"
|
||||||
|
state.errorMessage != null -> "有错误"
|
||||||
|
state.mutualPasses.isNotEmpty() -> "${state.mutualPasses.size}个匹配"
|
||||||
|
state.hasSearched -> "无匹配"
|
||||||
|
else -> "待查询"
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
+10
-1
@@ -49,6 +49,7 @@ data class MutualUiState(
|
|||||||
val hoursAhead: Int = 24,
|
val hoursAhead: Int = 24,
|
||||||
val mutualPasses: List<MutualPass> = emptyList(),
|
val mutualPasses: List<MutualPass> = emptyList(),
|
||||||
val isCalculating: Boolean = false,
|
val isCalculating: Boolean = false,
|
||||||
|
val hasSearched: Boolean = false,
|
||||||
val selectedPassIndex: Int = -1,
|
val selectedPassIndex: Int = -1,
|
||||||
val errorMessage: String? = null
|
val errorMessage: String? = null
|
||||||
)
|
)
|
||||||
@@ -167,7 +168,15 @@ class MutualViewModel(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_uiState.update { it.copy(isCalculating = true, errorMessage = null, mutualPasses = emptyList()) }
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
isCalculating = true,
|
||||||
|
hasSearched = true,
|
||||||
|
errorMessage = null,
|
||||||
|
mutualPasses = emptyList(),
|
||||||
|
selectedPassIndex = -1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val time = System.currentTimeMillis()
|
val time = System.currentTimeMillis()
|
||||||
|
|||||||
+17
-3
@@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.update
|
|||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Calendar
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
@@ -162,7 +163,10 @@ class PassesViewModel(
|
|||||||
return SimpleDateFormat(pattern, Locale.getDefault()).also { it.timeZone = tz }
|
return SimpleDateFormat(pattern, Locale.getDefault()).also { it.timeZone = tz }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes sunrise/sunset strings for each unique calendar day in the pass list, plus today for DeepSpace
|
// Computes sunrise/sunset strings for each unique calendar day in the pass list, plus today for DeepSpace.
|
||||||
|
// CelestialComputer.findSunRiseSet() returns the next rise/set after the supplied time, so pass
|
||||||
|
// the calendar day's 00:00 in the same timezone used by the visible date group. Passing a pass's
|
||||||
|
// AOS time can jump to the following day's sunrise/sunset when that AOS is after the local event.
|
||||||
private fun computeSunTimes(passes: List<OrbitalPass>, isUtc: Boolean): Map<String, Pair<String, String>> {
|
private fun computeSunTimes(passes: List<OrbitalPass>, isUtc: Boolean): Map<String, Pair<String, String>> {
|
||||||
val stationPos = settingsRepo.stationPosition.value
|
val stationPos = settingsRepo.stationPosition.value
|
||||||
val tz = if (isUtc) TimeZone.getTimeZone("UTC") else TimeZone.getDefault()
|
val tz = if (isUtc) TimeZone.getTimeZone("UTC") else TimeZone.getDefault()
|
||||||
@@ -171,7 +175,7 @@ class PassesViewModel(
|
|||||||
val result = LinkedHashMap<String, Pair<String, String>>()
|
val result = LinkedHashMap<String, Pair<String, String>>()
|
||||||
// DeepSpace group always shows today's sun times
|
// DeepSpace group always shows today's sun times
|
||||||
if (passes.any { it.isDeepSpace }) {
|
if (passes.any { it.isDeepSpace }) {
|
||||||
val riseSet = CelestialComputer.findSunRiseSet(stationPos, System.currentTimeMillis())
|
val riseSet = CelestialComputer.findSunRiseSet(stationPos, startOfDayMillis(System.currentTimeMillis(), tz))
|
||||||
val rise = if (riseSet.riseTimeMillis > 0) sdfTime.format(Date(riseSet.riseTimeMillis)) else "--:--"
|
val rise = if (riseSet.riseTimeMillis > 0) sdfTime.format(Date(riseSet.riseTimeMillis)) else "--:--"
|
||||||
val set = if (riseSet.setTimeMillis > 0) sdfTime.format(Date(riseSet.setTimeMillis)) else "--:--"
|
val set = if (riseSet.setTimeMillis > 0) sdfTime.format(Date(riseSet.setTimeMillis)) else "--:--"
|
||||||
result["DeepSpace (period >225min)"] = rise to set
|
result["DeepSpace (period >225min)"] = rise to set
|
||||||
@@ -180,7 +184,7 @@ class PassesViewModel(
|
|||||||
if (pass.isDeepSpace) continue
|
if (pass.isDeepSpace) continue
|
||||||
val label = sdfDate.format(Date(pass.aosTime))
|
val label = sdfDate.format(Date(pass.aosTime))
|
||||||
if (label in result) continue
|
if (label in result) continue
|
||||||
val riseSet = CelestialComputer.findSunRiseSet(stationPos, pass.aosTime)
|
val riseSet = CelestialComputer.findSunRiseSet(stationPos, startOfDayMillis(pass.aosTime, tz))
|
||||||
val rise = if (riseSet.riseTimeMillis > 0) sdfTime.format(Date(riseSet.riseTimeMillis)) else "--:--"
|
val rise = if (riseSet.riseTimeMillis > 0) sdfTime.format(Date(riseSet.riseTimeMillis)) else "--:--"
|
||||||
val set = if (riseSet.setTimeMillis > 0) sdfTime.format(Date(riseSet.setTimeMillis)) else "--:--"
|
val set = if (riseSet.setTimeMillis > 0) sdfTime.format(Date(riseSet.setTimeMillis)) else "--:--"
|
||||||
result[label] = rise to set
|
result[label] = rise to set
|
||||||
@@ -188,6 +192,16 @@ class PassesViewModel(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startOfDayMillis(timeMillis: Long, tz: TimeZone): Long {
|
||||||
|
return Calendar.getInstance(tz, Locale.getDefault()).apply {
|
||||||
|
timeInMillis = timeMillis
|
||||||
|
set(Calendar.HOUR_OF_DAY, 0)
|
||||||
|
set(Calendar.MINUTE, 0)
|
||||||
|
set(Calendar.SECOND, 0)
|
||||||
|
set(Calendar.MILLISECOND, 0)
|
||||||
|
}.timeInMillis
|
||||||
|
}
|
||||||
|
|
||||||
private fun groupPasses(passes: List<OrbitalPass>, isUtc: Boolean): Map<String, List<OrbitalPass>> {
|
private fun groupPasses(passes: List<OrbitalPass>, isUtc: Boolean): Map<String, List<OrbitalPass>> {
|
||||||
val tz = if (isUtc) TimeZone.getTimeZone("UTC") else TimeZone.getDefault()
|
val tz = if (isUtc) TimeZone.getTimeZone("UTC") else TimeZone.getDefault()
|
||||||
val sdfDate = dateFormat(tz)
|
val sdfDate = dateFormat(tz)
|
||||||
|
|||||||
@@ -8,4 +8,8 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":feature:mutual"))
|
implementation(project(":feature:mutual"))
|
||||||
|
// CW 解码面板: 复用 feature:cw 的 Morse Expert 引擎(布局 cw_panel_main + MainActivity 控制器)
|
||||||
|
implementation(project(":feature:cw"))
|
||||||
|
// 与 feature:cw 一致(控制器按 ID 递归查找 ConstraintLayout 视图)
|
||||||
|
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
|
||||||
}
|
}
|
||||||
@@ -119,9 +119,10 @@ fun RadarViewCompose(
|
|||||||
var cachedSweepColor by remember { mutableStateOf(Color.Unspecified) }
|
var cachedSweepColor by remember { mutableStateOf(Color.Unspecified) }
|
||||||
var trackPath by remember { mutableStateOf(Path()) }
|
var trackPath by remember { mutableStateOf(Path()) }
|
||||||
var trackEffect by remember { mutableStateOf(PathEffect.cornerPathEffect(0f)) }
|
var trackEffect by remember { mutableStateOf(PathEffect.cornerPathEffect(0f)) }
|
||||||
// Station-B overlay track (dashed)
|
// Station-B overlay track (dashed, with the same directional arrow stamp as station A)
|
||||||
var cachedTrackBRef by remember { mutableStateOf<List<OrbitalPos>?>(null) }
|
var cachedTrackBRef by remember { mutableStateOf<List<OrbitalPos>?>(null) }
|
||||||
var trackBPath by remember { mutableStateOf(Path()) }
|
var trackBPath by remember { mutableStateOf(Path()) }
|
||||||
|
var trackBEffect by remember { mutableStateOf(PathEffect.cornerPathEffect(0f)) }
|
||||||
// ShaderBrush is cached to avoid allocating a new GPU shader object every frame
|
// ShaderBrush is cached to avoid allocating a new GPU shader object every frame
|
||||||
var cachedSweepBrush by remember { mutableStateOf<ShaderBrush?>(null) }
|
var cachedSweepBrush by remember { mutableStateOf<ShaderBrush?>(null) }
|
||||||
|
|
||||||
@@ -131,7 +132,10 @@ fun RadarViewCompose(
|
|||||||
if (radius != cachedRadius || items !== cachedItemsRef || trackB !== cachedTrackBRef) {
|
if (radius != cachedRadius || items !== cachedItemsRef || trackB !== cachedTrackBRef) {
|
||||||
trackPath = createTrackPath(items, radius)
|
trackPath = createTrackPath(items, radius)
|
||||||
trackEffect = createTrackEffect(trackPath)
|
trackEffect = createTrackEffect(trackPath)
|
||||||
trackBPath = trackB?.let { createTrackPath(it, radius) } ?: Path()
|
val newTrackBPath = trackB?.let { createTrackPath(it, radius) } ?: Path()
|
||||||
|
trackBPath = newTrackBPath
|
||||||
|
trackBEffect = if (!newTrackBPath.isEmpty) createTrackEffect(newTrackBPath)
|
||||||
|
else PathEffect.cornerPathEffect(0f)
|
||||||
cachedSweepBrush = makeSweepBrush(center, primaryColor)
|
cachedSweepBrush = makeSweepBrush(center, primaryColor)
|
||||||
cachedRadius = radius
|
cachedRadius = radius
|
||||||
cachedItemsRef = items
|
cachedItemsRef = items
|
||||||
@@ -148,13 +152,14 @@ fun RadarViewCompose(
|
|||||||
drawElevationLabels(radius, primaryColor, measurer)
|
drawElevationLabels(radius, primaryColor, measurer)
|
||||||
translate(center.x, center.y) {
|
translate(center.x, center.y) {
|
||||||
drawTrack(trackPath, trackEffect, aimColor, primaryColor)
|
drawTrack(trackPath, trackEffect, aimColor, primaryColor)
|
||||||
// Station-B overlay: full dashed track + live position dot
|
// Station-B overlay: full dashed track + directional arrows + live position dot
|
||||||
// (same display mode as the local station: track line + pulsing dot)
|
// (same display mode as the local station: track line/arrow + pulsing dot)
|
||||||
if (trackB != null && trackB.isNotEmpty() && !trackBPath.isEmpty) {
|
if (trackB != null && trackB.isNotEmpty() && !trackBPath.isEmpty) {
|
||||||
drawPath(
|
drawPath(
|
||||||
trackBPath, trackBColor,
|
trackBPath, trackBColor,
|
||||||
style = Stroke(STROKE_WIDTH, pathEffect = PathEffect.dashPathEffect(floatArrayOf(18f, 12f)))
|
style = Stroke(STROKE_WIDTH, pathEffect = PathEffect.dashPathEffect(floatArrayOf(18f, 12f)))
|
||||||
)
|
)
|
||||||
|
drawPath(trackBPath, trackBColor, style = Stroke(pathEffect = trackBEffect))
|
||||||
}
|
}
|
||||||
trackBPosition?.let { posB ->
|
trackBPosition?.let { posB ->
|
||||||
if (posB.elevation > 0) {
|
if (posB.elevation > 0) {
|
||||||
|
|||||||
+61
-44
@@ -17,6 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.feature.radar
|
package com.rtbishop.look4sat.feature.radar
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.LayoutInflater
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.expandVertically
|
import androidx.compose.animation.expandVertically
|
||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
@@ -50,6 +52,7 @@ import androidx.compose.material3.OutlinedButton
|
|||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -60,6 +63,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
@@ -68,6 +72,8 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
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
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import com.rtbishop.look4sat.core.domain.model.SatRadio
|
import com.rtbishop.look4sat.core.domain.model.SatRadio
|
||||||
import com.rtbishop.look4sat.core.domain.predict.OrbitalPos
|
import com.rtbishop.look4sat.core.domain.predict.OrbitalPos
|
||||||
import com.rtbishop.look4sat.core.domain.utility.DopplerFrequencyCalculator
|
import com.rtbishop.look4sat.core.domain.utility.DopplerFrequencyCalculator
|
||||||
@@ -75,6 +81,8 @@ import com.rtbishop.look4sat.core.presentation.CardButton
|
|||||||
import com.rtbishop.look4sat.core.presentation.R
|
import com.rtbishop.look4sat.core.presentation.R
|
||||||
import com.rtbishop.look4sat.core.presentation.formatFrequency
|
import com.rtbishop.look4sat.core.presentation.formatFrequency
|
||||||
import com.rtbishop.look4sat.core.presentation.infiniteMarquee
|
import com.rtbishop.look4sat.core.presentation.infiniteMarquee
|
||||||
|
import com.rtbishop.look4sat.feature.cw.R as CwR
|
||||||
|
import com.ve3nea.morse_expert.MainActivity
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
@@ -453,19 +461,19 @@ private fun ExpandedRadioControl(
|
|||||||
if (!radioControl.txPanel.isConnected && !radioControl.rxPanel.isConnected) {
|
if (!radioControl.txPanel.isConnected && !radioControl.rxPanel.isConnected) {
|
||||||
CardButton(
|
CardButton(
|
||||||
onClick = { onAction(RadarAction.ConnectRadios) },
|
onClick = { onAction(RadarAction.ConnectRadios) },
|
||||||
text = "Connect",
|
text = stringResource(R.string.connect_radio),
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
CardButton(
|
CardButton(
|
||||||
onClick = { onAction(RadarAction.DisconnectRadios) },
|
onClick = { onAction(RadarAction.DisconnectRadios) },
|
||||||
text = "Disconnect",
|
text = stringResource(R.string.disconnect_radio),
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
CardButton(
|
CardButton(
|
||||||
onClick = { onAction(RadarAction.ToggleTracking) },
|
onClick = { onAction(RadarAction.ToggleTracking) },
|
||||||
text = if (radioControl.isTracking) "Stop" else "Track",
|
text = if (radioControl.isTracking) stringResource(R.string.stop_radio) else stringResource(R.string.track_radio),
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -661,19 +669,56 @@ private fun CwDecoderPanel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
AnimatedVisibility(visible = cw.isExpanded) {
|
AnimatedVisibility(visible = cw.isExpanded) {
|
||||||
|
// Morse Expert 引擎(4.5.0 移植): 小布局 cw_panel_main(瀑布图 + 解码文本)
|
||||||
|
// 自带 Kotlin 贝叶斯引擎(CwDecoder)不再用于此面板(代码保留作回退)
|
||||||
|
val context = LocalContext.current
|
||||||
|
val activity = remember { context as? Activity }
|
||||||
|
val controller = remember { MainActivity() }
|
||||||
|
val rootView = remember {
|
||||||
|
LayoutInflater.from(context).inflate(CwR.layout.cw_panel_main, null) as ConstraintLayout
|
||||||
|
}
|
||||||
|
var initialized by remember { mutableStateOf(false) }
|
||||||
|
var listening by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
// 生命周期: 展开即绑定视图(不改宿主窗口); 收起/离开自动释放
|
||||||
|
DisposableEffect(Unit) {
|
||||||
|
if (activity != null) {
|
||||||
|
controller.onCreate(activity, rootView, false)
|
||||||
|
}
|
||||||
|
onDispose {
|
||||||
|
controller.onPause()
|
||||||
|
controller.onDestroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 权限已授予(首次进入/授权回调统一入口): 初始化引擎 + 启动录音
|
||||||
|
LaunchedEffect(cw.hasPermission) {
|
||||||
|
if (cw.hasPermission && !initialized) {
|
||||||
|
controller.onPermissionGranted() // = v(): 创建音频采集 + 解码核心
|
||||||
|
controller.onResume() // 立即启动录音
|
||||||
|
initialized = true
|
||||||
|
listening = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
// Control buttons row
|
// Control buttons row(映射到控制器)
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
if (cw.status == CwStatus.Idle) {
|
if (!listening) {
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
if (!cw.hasPermission) {
|
if (!cw.hasPermission) {
|
||||||
requestMicPermission()
|
requestMicPermission()
|
||||||
|
} else if (!initialized) {
|
||||||
|
controller.onPermissionGranted()
|
||||||
|
controller.onResume()
|
||||||
|
initialized = true
|
||||||
|
listening = true
|
||||||
} else {
|
} else {
|
||||||
onAction(RadarAction.CwStartListening)
|
controller.onResume()
|
||||||
|
listening = true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
@@ -682,58 +727,30 @@ private fun CwDecoderPanel(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Button(
|
Button(
|
||||||
onClick = { onAction(RadarAction.CwStopListening) },
|
onClick = {
|
||||||
|
controller.onPause()
|
||||||
|
listening = false
|
||||||
|
},
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.radar_cw_stop))
|
Text(stringResource(R.string.radar_cw_stop))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
onClick = { onAction(RadarAction.CwReset) },
|
onClick = { controller.clearDecoded() },
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.radar_cw_reset))
|
Text(stringResource(R.string.radar_cw_reset))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal strength indicator
|
// 瀑布图 + 解码文本(原版布局迷你版, 适配容器)
|
||||||
if (cw.status == CwStatus.Listening && cw.signalStrength > 0f) {
|
AndroidView(
|
||||||
val strengthPct = (cw.signalStrength * 100).toInt()
|
factory = { rootView },
|
||||||
Text(
|
|
||||||
text = "Signal: $strengthPct%",
|
|
||||||
fontSize = 12.sp,
|
|
||||||
color = if (cw.signalStrength > 0.5f) Color(0xFF4CAF50)
|
|
||||||
else if (cw.signalStrength > 0.2f) Color(0xFFFFC107)
|
|
||||||
else MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decoded text output
|
|
||||||
ElevatedCard(
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(120.dp)
|
.height(150.dp)
|
||||||
) {
|
)
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(8.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "Decoded:",
|
|
||||||
fontSize = 12.sp,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
|
||||||
Text(
|
|
||||||
text = cw.decodedText.ifEmpty { "Waiting for CW signal..." },
|
|
||||||
fontSize = 16.sp,
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.convention.featurePlugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.rtbishop.look4sat.feature.roaming"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":core:data"))
|
||||||
|
}
|
||||||
@@ -0,0 +1,791 @@
|
|||||||
|
/*
|
||||||
|
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||||
|
* Copyright (C) 2019-2026 Arty Bishop and contributors.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rtbishop.look4sat.feature.roaming
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.location.Location
|
||||||
|
import android.location.LocationListener
|
||||||
|
import android.location.LocationManager
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
|
import androidx.compose.foundation.layout.systemBars
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 漫游页 —— 原样照搬自 QTH定位器 2.0 (com.us1pm.gridsquarelocator)。
|
||||||
|
* UI: res/layout/main.xml (结构/尺寸/颜色逐项照搬, 不做任何更改)
|
||||||
|
* 逻辑: MainActivity.showLocation() / checkEnabled() / GPS 实时监听
|
||||||
|
* 红线: UI 与逻辑均不更改, 与成品截图逐像素对齐。
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun RoamingScreen() {
|
||||||
|
val context = LocalContext.current
|
||||||
|
var state by remember { mutableStateOf(RoamingState()) }
|
||||||
|
// 照搬 onCreate: 日期固定文本 + 小时前缀(定位时拼接分钟)
|
||||||
|
val dateText = remember { SimpleDateFormat("dd MMM yyyy", Locale.getDefault()).format(Date()) }
|
||||||
|
var hourPrefix by remember { mutableStateOf("") }
|
||||||
|
// 照搬 onCreate: 初始 checkEnabled() -> 红点+设置按钮, 日期隐藏
|
||||||
|
var hasPermission by remember { mutableStateOf(false) }
|
||||||
|
val permissionLauncher = rememberLauncherForActivityResult(
|
||||||
|
ActivityResultContracts.RequestMultiplePermissions()
|
||||||
|
) { }
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
hourPrefix = SimpleDateFormat("HH:mm", Locale.getDefault()).format(Date()).substring(0, 3)
|
||||||
|
val fine = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) ==
|
||||||
|
PackageManager.PERMISSION_GRANTED
|
||||||
|
val coarse = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) ==
|
||||||
|
PackageManager.PERMISSION_GRANTED
|
||||||
|
hasPermission = fine || coarse
|
||||||
|
if (!hasPermission) {
|
||||||
|
permissionLauncher.launch(
|
||||||
|
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// 照搬 onResume: 绿点+进度条+提示+日期显示, 红点/按钮隐藏
|
||||||
|
state = state.copy(gpsOn = true, gpsOff = false, showSettings = false, showProgress = true, showNotice = true, showDate = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 照搬 onResume/onPause: GPS + network 监听, 10 秒 / 10 米
|
||||||
|
val locationManager = remember { context.getSystemService(Context.LOCATION_SERVICE) as LocationManager }
|
||||||
|
DisposableEffect(locationManager, hasPermission) {
|
||||||
|
if (hasPermission) {
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
val listener = object : LocationListener {
|
||||||
|
override fun onLocationChanged(location: Location) {
|
||||||
|
// 照搬 showLocation 开头: 只接受 gps / network provider
|
||||||
|
if (location.provider != "gps" && location.provider != "network") return
|
||||||
|
state = roamingStateFromLocation(location.latitude, location.longitude, location.time, hourPrefix)
|
||||||
|
.copy(date = dateText)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onProviderDisabled(provider: String) {
|
||||||
|
// 照搬 checkEnabled: 红点+按钮, 日期隐藏
|
||||||
|
state = state.copy(gpsOn = false, gpsOff = true, showSettings = true, showProgress = false, showNotice = false, showDate = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onProviderEnabled(provider: String) {
|
||||||
|
state = state.copy(gpsOn = true, gpsOff = false, showSettings = false, showProgress = true, showNotice = true, showDate = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Deprecated in Java")
|
||||||
|
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) = Unit
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
locationManager.requestLocationUpdates("gps", 10_000L, 10f, listener)
|
||||||
|
locationManager.requestLocationUpdates("network", 10_000L, 10f, listener)
|
||||||
|
} catch (_: SecurityException) {
|
||||||
|
}
|
||||||
|
onDispose { locationManager.removeUpdates(listener) }
|
||||||
|
} else {
|
||||||
|
onDispose { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 照搬 res/layout/main.xml: 浅色页面, 蓝色信息区, 43sp 定位码, 3x3 九宫格, 底部版权
|
||||||
|
// 按用户要求: 上下往内缩进系统栏安全区, 避免内容被状态栏/导航栏遮挡
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(MaterialTheme.colorScheme.background)
|
||||||
|
.windowInsetsPadding(WindowInsets.systemBars)
|
||||||
|
) {
|
||||||
|
// relativeLayout7: 顶部 GPS 状态条, 25dp 高, holo_blue_bright
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(2.dp)
|
||||||
|
.height(25.dp)
|
||||||
|
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||||
|
) {
|
||||||
|
Row(modifier = Modifier.fillMaxSize(), verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
// tvTitleGPS: "GPS" 14sp 黑, 左 8dp
|
||||||
|
Text(
|
||||||
|
text = "GPS",
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(start = 8.dp)
|
||||||
|
)
|
||||||
|
// imGps: toRightOf tvTitleGPS + marginLeft 15dp (成品 main.xml)
|
||||||
|
Spacer(modifier = Modifier.width(15.dp))
|
||||||
|
// imGps: 绿点 (15dp, 主题色 primary -> 夜间滤镜下可见)
|
||||||
|
if (state.gpsOn) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(15.dp)
|
||||||
|
.background(MaterialTheme.colorScheme.primary, CircleShape)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// imGpsOff: 红点叠层 (15dp, 主题色 error)
|
||||||
|
if (state.gpsOff) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(15.dp)
|
||||||
|
.background(MaterialTheme.colorScheme.error, CircleShape)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
// tvDate: 14sp 黑, 居中
|
||||||
|
if (state.showDate) {
|
||||||
|
Text(text = state.date.ifEmpty { dateText }, fontSize = 14.sp, color = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
// tvTime: 14sp 黑, 右对齐
|
||||||
|
Text(
|
||||||
|
text = state.time,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(end = 8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// btnLocationSettings: 黄色按钮, 居中
|
||||||
|
// btnLocationSettings: 成品 #faf8f54a 半透明黄, 14sp 黑字, 居中
|
||||||
|
if (state.showSettings) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.Center)
|
||||||
|
.background(MaterialTheme.colorScheme.primary)
|
||||||
|
.clickable { context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)) }
|
||||||
|
.padding(horizontal = 5.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text(text = "设置启用GPS", fontSize = 14.sp, color = MaterialTheme.colorScheme.onPrimary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// linearLayout4: 经纬度区, holo_blue_bright
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 2.dp)
|
||||||
|
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||||
|
) {
|
||||||
|
// 纬度行: tvLati 16sp 黑 左 8dp | tvLat 右对齐 3dp
|
||||||
|
Row(modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Text(text = state.latLabel, fontSize = 16.sp, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(start = 8.dp))
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
Text(text = state.latValue, fontSize = 14.sp, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(end = 3.dp))
|
||||||
|
}
|
||||||
|
// 经度行
|
||||||
|
Row(modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Text(text = state.lonLabel, fontSize = 16.sp, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(start = 8.dp))
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
Text(text = state.lonValue, fontSize = 14.sp, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(end = 3.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// relativeLayout6: 定位码区, 43sp bold 黑居中
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(2.dp)
|
||||||
|
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = state.loc,
|
||||||
|
fontSize = 43.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
// progressBar: 加载中, 居中
|
||||||
|
if (state.showProgress) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.size(24.dp).align(Alignment.Center),
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
strokeWidth = 2.dp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// tvNotice: 提示文字, 居中顶部
|
||||||
|
if (state.showNotice) {
|
||||||
|
Text(
|
||||||
|
text = "请稍等,加载可能需要几分钟的时间。",
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontStyle = androidx.compose.ui.text.font.FontStyle.Italic,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.align(Alignment.TopCenter).padding(top = 2.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// relativeLayout: 九宫格, 占满剩余, 底部 15dp
|
||||||
|
// 用 Box 等价 RelativeLayout 布局: 左格贴左 / 中格水平居中 / 右格贴右 (成品 main.xml)
|
||||||
|
Column(modifier = Modifier.fillMaxWidth().weight(1f).padding(bottom = 15.dp)) {
|
||||||
|
// 行1: relativeLayout5 (上排: 纬度 +1)
|
||||||
|
Box(modifier = Modifier.fillMaxWidth().weight(1f)) {
|
||||||
|
GridCell(text = state.grids[0], modifier = Modifier.width(80.dp).fillMaxHeight().align(Alignment.CenterStart))
|
||||||
|
GridCell(text = state.grids[1], modifier = Modifier.width(200.dp).fillMaxHeight().align(Alignment.Center))
|
||||||
|
GridCell(text = state.grids[2], modifier = Modifier.width(80.dp).fillMaxHeight().align(Alignment.CenterEnd))
|
||||||
|
}
|
||||||
|
// 行2: relativeLayout4, 205dp 高 (中排: 中央)
|
||||||
|
Box(modifier = Modifier.fillMaxWidth().height(205.dp)) {
|
||||||
|
GridCell(text = state.grids[3], modifier = Modifier.width(80.dp).fillMaxHeight().align(Alignment.CenterStart))
|
||||||
|
// 中央列: tv22 (100x80dp centerInParent) 叠在 linearLayout3 (200x200dp) 之上
|
||||||
|
Box(modifier = Modifier.width(200.dp).fillMaxHeight().align(Alignment.Center)) {
|
||||||
|
// linearLayout3: 200x200dp, marginTop 2dp, holo_blue_bright
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.TopCenter)
|
||||||
|
.offset(y = 2.dp)
|
||||||
|
.size(width = 200.dp, height = 200.dp)
|
||||||
|
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||||
|
) {
|
||||||
|
// iv: 红点 10x10dp, top|left, leftMargin/topMargin 查表绝对定位
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.drawable.roam_pnt),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(10.dp)
|
||||||
|
.offset(x = state.markerLeft.dp, y = state.markerTop.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// tv22: 100x80dp centerInParent, 30sp bold, textColorHighlight(白)
|
||||||
|
Text(
|
||||||
|
text = state.grids[4],
|
||||||
|
fontSize = 30.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(width = 100.dp, height = 80.dp)
|
||||||
|
.align(Alignment.Center)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
GridCell(text = state.grids[5], modifier = Modifier.width(80.dp).fillMaxHeight().align(Alignment.CenterEnd))
|
||||||
|
}
|
||||||
|
// 行3: relativeLayout3 (下排: 纬度 -1)
|
||||||
|
Box(modifier = Modifier.fillMaxWidth().weight(1f)) {
|
||||||
|
GridCell(text = state.grids[6], modifier = Modifier.width(80.dp).fillMaxHeight().align(Alignment.CenterStart))
|
||||||
|
GridCell(text = state.grids[7], modifier = Modifier.width(200.dp).fillMaxHeight().align(Alignment.Center))
|
||||||
|
GridCell(text = state.grids[8], modifier = Modifier.width(80.dp).fillMaxHeight().align(Alignment.CenterEnd))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// tv4: 底部版权, 10sp 黑 (往上拉, 底部预留空间避免被导航栏遮挡)
|
||||||
|
Text(
|
||||||
|
text = "制作:US1PM 汉化:BA7LCE",
|
||||||
|
fontSize = 10.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(bottom = 10.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 周边格: 60/200/60dp, #0BACF1, bold 居中, 2dp margin (照搬 tv11/tv13/tv21/tv23/tv31/tv33)
|
||||||
|
@Composable
|
||||||
|
private fun GridCell(text: String, modifier: Modifier) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.padding(2.dp)
|
||||||
|
.background(MaterialTheme.colorScheme.surface),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 照搬 MainActivity.showLocation() 的状态。
|
||||||
|
* 网格/九宫格/红点查表与成品逐行一致。
|
||||||
|
*/
|
||||||
|
data class RoamingState(
|
||||||
|
val date: String = "",
|
||||||
|
val time: String = "",
|
||||||
|
val latLabel: String = "纬度",
|
||||||
|
val latValue: String = "",
|
||||||
|
val lonLabel: String = "经度",
|
||||||
|
val lonValue: String = "",
|
||||||
|
val loc: String = "",
|
||||||
|
val grids: List<String> = List(9) { "" },
|
||||||
|
/** 红点 leftMargin(dp), 经度第 3 对字符查表 */
|
||||||
|
val markerLeft: Int = 0,
|
||||||
|
/** 红点 topMargin(dp), 纬度第 3 对字符查表(屏幕 Y 反转) */
|
||||||
|
val markerTop: Int = 0,
|
||||||
|
val gpsOn: Boolean = false,
|
||||||
|
val gpsOff: Boolean = true,
|
||||||
|
val showSettings: Boolean = true,
|
||||||
|
val showProgress: Boolean = false,
|
||||||
|
val showNotice: Boolean = false,
|
||||||
|
val showDate: Boolean = false
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 照搬 showLocation() 的完整计算: 时间/经纬度 DMS/8 位网格/九宫格/红点查表。
|
||||||
|
*/
|
||||||
|
fun roamingStateFromLocation(lat: Double, lon: Double, fixTime: Long, hourPrefix: String): RoamingState {
|
||||||
|
// 照搬: tvTime = 缓存小时前缀 + 定位时间分钟
|
||||||
|
val minute = SimpleDateFormat("mm", Locale.getDefault()).format(Date(fixTime))
|
||||||
|
val time = hourPrefix + minute
|
||||||
|
// 方向字符
|
||||||
|
val ew = if (lon >= 0.0) "E" else "W"
|
||||||
|
val ns = if (lat >= 0.0) "N" else "S"
|
||||||
|
// 照搬: 纬度 DMS (String.valueOf 无补零)
|
||||||
|
val latAbs = if (lat < 0.0) -lat else lat
|
||||||
|
val latDeg = latAbs.toInt()
|
||||||
|
val latMinFull = (latAbs - latDeg) * 60.0
|
||||||
|
val latMin = latMinFull.toInt()
|
||||||
|
val latSec = ((latMinFull - latMin) * 60.0).toInt()
|
||||||
|
val latLabel = "纬度 $latDeg° $latMin' $latSec\" $ns"
|
||||||
|
val latValue = "$lat° "
|
||||||
|
// 照搬: 经度 DMS
|
||||||
|
val lonAbs = if (lon < 0.0) -lon else lon
|
||||||
|
val lonDeg = lonAbs.toInt()
|
||||||
|
val lonMinFull = (lonAbs - lonDeg) * 60.0
|
||||||
|
val lonMin = lonMinFull.toInt()
|
||||||
|
val lonSec = ((lonMinFull - lonMin) * 60.0).toInt()
|
||||||
|
val lonLabel = "经度 $lonDeg° $lonMin' $lonSec\" $ew"
|
||||||
|
val lonValue = "$lon° "
|
||||||
|
// 照搬: 8 位网格编码 (区间查表)
|
||||||
|
val (str, str2, str3, str4) = encodeLon(lon)
|
||||||
|
val (str5, str6, str38, str9) = encodeLat(lat)
|
||||||
|
val loc = str + str5 + str2 + str6 + str3 + str38 + str4 + str9
|
||||||
|
// 照搬: 九宫格 parseInt3 五分支
|
||||||
|
val grids = buildGrids(str, str5, str2, str6, str3, str38)
|
||||||
|
// 照搬: 红点查表 (第 3 对字符)
|
||||||
|
val markerLeft = lonMargin[str3.firstOrNull()] ?: 0
|
||||||
|
val markerTop = latMargin[str38.firstOrNull()] ?: 0
|
||||||
|
return RoamingState(
|
||||||
|
time = time,
|
||||||
|
latLabel = latLabel,
|
||||||
|
latValue = latValue,
|
||||||
|
lonLabel = lonLabel,
|
||||||
|
lonValue = lonValue,
|
||||||
|
loc = loc,
|
||||||
|
grids = grids,
|
||||||
|
markerLeft = markerLeft,
|
||||||
|
markerTop = markerTop,
|
||||||
|
gpsOn = true,
|
||||||
|
gpsOff = false,
|
||||||
|
showSettings = false,
|
||||||
|
showProgress = false,
|
||||||
|
showNotice = false,
|
||||||
|
showDate = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 红点 leftMargin 查表 (MainActivity 1286-1386 行): 经度第 3 对 a..x -> -2..190 dp
|
||||||
|
private val lonMargin = mapOf(
|
||||||
|
'a' to -2, 'b' to 8, 'c' to 16, 'd' to 24, 'e' to 32, 'f' to 40, 'g' to 48, 'h' to 56,
|
||||||
|
'i' to 65, 'j' to 74, 'k' to 83, 'l' to 92, 'm' to 101, 'n' to 110, 'o' to 119, 'p' to 128,
|
||||||
|
'q' to 137, 'r' to 146, 's' to 155, 't' to 163, 'u' to 171, 'v' to 179, 'w' to 184, 'x' to 190
|
||||||
|
)
|
||||||
|
|
||||||
|
// 红点 topMargin 查表 (MainActivity 1315-1363 行): 纬度第 3 对 a..x -> 190..-2 dp (屏幕 Y 反转)
|
||||||
|
private val latMargin = mapOf(
|
||||||
|
'a' to 190, 'b' to 182, 'c' to 174, 'd' to 169, 'e' to 163, 'f' to 155, 'g' to 146, 'h' to 137,
|
||||||
|
'i' to 128, 'j' to 119, 'k' to 110, 'l' to 101, 'm' to 92, 'n' to 83, 'o' to 74, 'p' to 65,
|
||||||
|
'q' to 56, 'r' to 48, 's' to 40, 't' to 32, 'u' to 24, 'v' to 16, 'w' to 8, 'x' to -2
|
||||||
|
)
|
||||||
|
|
||||||
|
// 照搬 311-611 行: 经度 -> (20°区字母, 2°数字, 2'字母, 30"数字)
|
||||||
|
private fun encodeLon(inputLon: Double): List<String> {
|
||||||
|
var longitude = inputLon
|
||||||
|
var d = 0.0
|
||||||
|
val str = when {
|
||||||
|
(longitude >= -180.0) && (longitude <= -160.0) -> { longitude += 180.0; "A" }
|
||||||
|
(longitude >= -160.0) && (longitude <= -140.0) -> { longitude += 160.0; "B" }
|
||||||
|
(longitude >= -140.0) && (longitude <= -120.0) -> { longitude += 140.0; "C" }
|
||||||
|
(longitude >= -120.0) && (longitude <= -100.0) -> { longitude += 120.0; "D" }
|
||||||
|
(longitude >= -100.0) && (longitude <= -80.0) -> { longitude += 100.0; "E" }
|
||||||
|
(longitude >= -80.0) && (longitude <= -60.0) -> { longitude += 80.0; "F" }
|
||||||
|
(longitude >= -60.0) && (longitude <= -40.0) -> { longitude += 60.0; "G" }
|
||||||
|
(longitude >= -40.0) && (longitude <= -20.0) -> { longitude += 40.0; "H" }
|
||||||
|
(longitude >= -20.0) && (longitude <= 0.0) -> { longitude += 20.0; "I" }
|
||||||
|
(longitude >= 0.0) && (longitude <= 20.0) -> "J"
|
||||||
|
(longitude >= 20.0) && (longitude <= 40.0) -> { longitude -= 20.0; "K" }
|
||||||
|
(longitude >= 40.0) && (longitude <= 60.0) -> { longitude -= 40.0; "L" }
|
||||||
|
(longitude >= 60.0) && (longitude <= 80.0) -> { longitude -= 60.0; "M" }
|
||||||
|
(longitude >= 80.0) && (longitude <= 100.0) -> { longitude -= 80.0; "N" }
|
||||||
|
(longitude >= 100.0) && (longitude <= 120.0) -> { longitude -= 100.0; "O" }
|
||||||
|
(longitude >= 120.0) && (longitude <= 140.0) -> { longitude -= 120.0; "P" }
|
||||||
|
(longitude >= 140.0) && (longitude <= 160.0) -> { longitude -= 140.0; "Q" }
|
||||||
|
(longitude >= 160.0) && (longitude <= 180.0) -> { longitude -= 160.0; "R" }
|
||||||
|
else -> { longitude = 0.0; " " }
|
||||||
|
}
|
||||||
|
val str2 = when {
|
||||||
|
((longitude >= 0.0) && (longitude <= 2.0)) || ((longitude >= -20.0) && (longitude <= -18.0)) -> { d = longitude * 60.0; "0" }
|
||||||
|
((longitude >= 2.0) && (longitude <= 4.0)) || ((longitude >= -18.0) && (longitude <= -16.0)) -> { d = (longitude - 2.0) * 60.0; "1" }
|
||||||
|
((longitude >= 4.0) && (longitude <= 6.0)) || ((longitude >= -16.0) && (longitude <= -14.0)) -> { d = (longitude - 4.0) * 60.0; "2" }
|
||||||
|
((longitude >= 6.0) && (longitude <= 8.0)) || ((longitude >= -14.0) && (longitude <= -12.0)) -> { d = (longitude - 6.0) * 60.0; "3" }
|
||||||
|
((longitude >= 8.0) && (longitude <= 10.0)) || ((longitude >= -12.0) && (longitude <= -10.0)) -> { d = (longitude - 8.0) * 60.0; "4" }
|
||||||
|
((longitude >= 10.0) && (longitude <= 12.0)) || ((longitude >= -10.0) && (longitude <= -8.0)) -> { d = (longitude - 10.0) * 60.0; "5" }
|
||||||
|
((longitude >= 12.0) && (longitude <= 14.0)) || ((longitude >= -8.0) && (longitude <= -6.0)) -> { d = (longitude - 12.0) * 60.0; "6" }
|
||||||
|
((longitude >= 14.0) && (longitude <= 16.0)) || ((longitude >= -6.0) && (longitude <= -4.0)) -> { d = (longitude - 14.0) * 60.0; "7" }
|
||||||
|
((longitude >= 16.0) && (longitude <= 18.0)) || ((longitude >= -4.0) && (longitude <= -2.0)) -> { d = (longitude - 16.0) * 60.0; "8" }
|
||||||
|
((longitude >= 18.0) && (longitude <= 20.0)) || ((longitude >= -2.0) && (longitude <= 0.0)) -> { d = (longitude - 18.0) * 60.0; "9" }
|
||||||
|
else -> { d = 0.0; " " }
|
||||||
|
}
|
||||||
|
val str3 = when {
|
||||||
|
((d >= 0.0) && (d <= 5.0)) || ((d >= -120.0) && (d <= -115.0)) -> { longitude = d * 60.0; "a" }
|
||||||
|
((d >= 5.0) && (d <= 10.0)) || ((d >= -115.0) && (d <= -110.0)) -> { longitude = (d - 5.0) * 60.0; "b" }
|
||||||
|
((d >= 10.0) && (d <= 15.0)) || ((d >= -110.0) && (d <= -105.0)) -> { longitude = (d - 10.0) * 60.0; "c" }
|
||||||
|
((d >= 15.0) && (d <= 20.0)) || ((d >= -105.0) && (d <= -100.0)) -> { longitude = (d - 15.0) * 60.0; "d" }
|
||||||
|
((d >= 20.0) && (d <= 25.0)) || ((d >= -100.0) && (d <= -95.0)) -> { longitude = (d - 20.0) * 60.0; "e" }
|
||||||
|
((d >= 25.0) && (d <= 30.0)) || ((d >= -95.0) && (d <= -90.0)) -> { longitude = (d - 25.0) * 60.0; "f" }
|
||||||
|
((d >= 30.0) && (d <= 35.0)) || ((d >= -90.0) && (d <= -85.0)) -> { longitude = (d - 30.0) * 60.0; "g" }
|
||||||
|
((d >= 35.0) && (d <= 40.0)) || ((d >= -85.0) && (d <= -80.0)) -> { longitude = (d - 35.0) * 60.0; "h" }
|
||||||
|
((d >= 40.0) && (d <= 45.0)) || ((d >= -80.0) && (d <= -75.0)) -> { longitude = (d - 40.0) * 60.0; "i" }
|
||||||
|
((d >= 45.0) && (d <= 50.0)) || ((d >= -75.0) && (d <= -70.0)) -> { longitude = (d - 45.0) * 60.0; "j" }
|
||||||
|
((d >= 50.0) && (d <= 55.0)) || ((d >= -70.0) && (d <= -65.0)) -> { longitude = (d - 50.0) * 60.0; "k" }
|
||||||
|
((d >= 55.0) && (d <= 60.0)) || ((d >= -65.0) && (d <= -60.0)) -> { longitude = (d - 55.0) * 60.0; "l" }
|
||||||
|
((d >= 60.0) && (d <= 65.0)) || ((d >= -60.0) && (d <= -55.0)) -> { longitude = (d - 60.0) * 60.0; "m" }
|
||||||
|
((d >= 65.0) && (d <= 70.0)) || ((d >= -55.0) && (d <= -50.0)) -> { longitude = (d - 65.0) * 60.0; "n" }
|
||||||
|
((d >= 70.0) && (d <= 75.0)) || ((d >= -50.0) && (d <= -45.0)) -> { longitude = (d - 70.0) * 60.0; "o" }
|
||||||
|
((d >= 75.0) && (d <= 80.0)) || ((d >= -45.0) && (d <= -40.0)) -> { longitude = (d - 75.0) * 60.0; "p" }
|
||||||
|
((d >= 80.0) && (d <= 85.0)) || ((d >= -40.0) && (d <= -35.0)) -> { longitude = (d - 80.0) * 60.0; "q" }
|
||||||
|
((d >= 85.0) && (d <= 90.0)) || ((d >= -35.0) && (d <= -30.0)) -> { longitude = (d - 85.0) * 60.0; "r" }
|
||||||
|
((d >= 90.0) && (d <= 95.0)) || ((d >= -30.0) && (d <= -25.0)) -> { longitude = (d - 90.0) * 60.0; "s" }
|
||||||
|
((d >= 95.0) && (d <= 100.0)) || ((d >= -25.0) && (d <= -20.0)) -> { longitude = (d - 95.0) * 60.0; "t" }
|
||||||
|
((d >= 100.0) && (d <= 105.0)) || ((d >= -20.0) && (d <= -15.0)) -> { longitude = (d - 100.0) * 60.0; "u" }
|
||||||
|
((d >= 105.0) && (d <= 110.0)) || ((d >= -15.0) && (d <= -10.0)) -> { longitude = (d - 105.0) * 60.0; "v" }
|
||||||
|
((d >= 110.0) && (d <= 115.0)) || ((d >= -10.0) && (d <= -5.0)) -> { longitude = (d - 110.0) * 60.0; "w" }
|
||||||
|
((d >= 115.0) && (d <= 120.0)) || ((d >= -5.0) && (d <= 0.0)) -> { longitude = (d - 115.0) * 60.0; "x" }
|
||||||
|
else -> { longitude = 0.0; " " }
|
||||||
|
}
|
||||||
|
val str4 = when {
|
||||||
|
((longitude >= 0.0) && (longitude <= 30.0)) || ((longitude >= -300.0) && (longitude <= -270.0)) -> "0"
|
||||||
|
((longitude >= 30.0) && (longitude <= 60.0)) || ((longitude >= -270.0) && (longitude <= -240.0)) -> "1"
|
||||||
|
((longitude >= 60.0) && (longitude <= 90.0)) || ((longitude >= -240.0) && (longitude <= -210.0)) -> "2"
|
||||||
|
((longitude >= 90.0) && (longitude <= 120.0)) || ((longitude >= -210.0) && (longitude <= -180.0)) -> "3"
|
||||||
|
((longitude >= 120.0) && (longitude <= 150.0)) || ((longitude >= -180.0) && (longitude <= -150.0)) -> "4"
|
||||||
|
((longitude >= 150.0) && (longitude <= 180.0)) || ((longitude >= -150.0) && (longitude <= -120.0)) -> "5"
|
||||||
|
((longitude >= 180.0) && (longitude <= 210.0)) || ((longitude >= -120.0) && (longitude <= -90.0)) -> "6"
|
||||||
|
((longitude >= 210.0) && (longitude <= 240.0)) || ((longitude >= -90.0) && (longitude <= -60.0)) -> "7"
|
||||||
|
((longitude >= 240.0) && (longitude <= 270.0)) || ((longitude >= -60.0) && (longitude <= -30.0)) -> "8"
|
||||||
|
((longitude >= 270.0) && (longitude <= 300.0)) || ((longitude >= -30.0) && (longitude <= 0.0)) -> "9"
|
||||||
|
else -> " "
|
||||||
|
}
|
||||||
|
return listOf(str, str2, str3, str4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 照搬 612-914 行: 纬度 -> (10°区字母, 1°数字, 1'字母, 15"数字)
|
||||||
|
private fun encodeLat(inputLat: Double): List<String> {
|
||||||
|
var latitude = inputLat
|
||||||
|
var d2 = 0.0
|
||||||
|
var d3 = 0.0
|
||||||
|
val str5 = when {
|
||||||
|
(latitude >= -90.0) && (latitude <= -80.0) -> { d2 = latitude + 90.0; "A" }
|
||||||
|
(latitude >= -80.0) && (latitude <= -70.0) -> { d2 = latitude + 80.0; "B" }
|
||||||
|
(latitude >= -70.0) && (latitude <= -60.0) -> { d2 = latitude + 70.0; "C" }
|
||||||
|
(latitude >= -60.0) && (latitude <= -50.0) -> { d2 = latitude + 60.0; "D" }
|
||||||
|
(latitude >= -50.0) && (latitude <= -40.0) -> { d2 = latitude + 50.0; "E" }
|
||||||
|
(latitude >= -40.0) && (latitude <= -30.0) -> { d2 = latitude + 40.0; "F" }
|
||||||
|
(latitude >= -30.0) && (latitude <= -20.0) -> { d2 = latitude + 30.0; "G" }
|
||||||
|
(latitude >= -20.0) && (latitude <= -10.0) -> { d2 = latitude + 20.0; "H" }
|
||||||
|
(latitude >= -10.0) && (latitude <= 0.0) -> { d2 = latitude + 10.0; "I" }
|
||||||
|
(latitude >= 0.0) && (latitude <= 10.0) -> { d2 = latitude; "J" }
|
||||||
|
(latitude >= 10.0) && (latitude <= 20.0) -> { d2 = latitude - 10.0; "K" }
|
||||||
|
(latitude >= 20.0) && (latitude <= 30.0) -> { d2 = latitude - 20.0; "L" }
|
||||||
|
(latitude >= 30.0) && (latitude <= 40.0) -> { d2 = latitude - 30.0; "M" }
|
||||||
|
(latitude >= 40.0) && (latitude <= 50.0) -> { d2 = latitude - 40.0; "N" }
|
||||||
|
(latitude >= 50.0) && (latitude <= 60.0) -> { d2 = latitude - 50.0; "O" }
|
||||||
|
(latitude >= 60.0) && (latitude <= 70.0) -> { d2 = latitude - 60.0; "P" }
|
||||||
|
(latitude >= 70.0) && (latitude <= 80.0) -> { d2 = latitude - 70.0; "Q" }
|
||||||
|
(latitude >= 80.0) && (latitude <= 90.0) -> { d2 = latitude - 80.0; "R" }
|
||||||
|
else -> { d2 = 0.0; " " }
|
||||||
|
}
|
||||||
|
var str6 = " "
|
||||||
|
when {
|
||||||
|
((d2 >= 0.0) && (d2 <= 1.0)) || ((d2 >= -10.0) && (d2 <= -9.0)) -> { d3 = d2 * 60.0; str6 = "0" }
|
||||||
|
((d2 >= 1.0) && (d2 <= 2.0)) || ((d2 >= -9.0) && (d2 <= -8.0)) -> { d3 = (d2 - 1.0) * 60.0; str6 = "1" }
|
||||||
|
((d2 >= 2.0) && (d2 <= 3.0)) || ((d2 >= -8.0) && (d2 <= -7.0)) -> { d3 = (d2 - 2.0) * 60.0; str6 = "2" }
|
||||||
|
((d2 >= 3.0) && (d2 <= 4.0)) || ((d2 >= -7.0) && (d2 <= -6.0)) -> { d3 = (d2 - 3.0) * 60.0; str6 = "3" }
|
||||||
|
((d2 >= 4.0) && (d2 <= 5.0)) || ((d2 >= -6.0) && (d2 <= -5.0)) -> { d3 = (d2 - 4.0) * 60.0; str6 = "4" }
|
||||||
|
((d2 >= 5.0) && (d2 <= 6.0)) || ((d2 >= -5.0) && (d2 <= -4.0)) -> { d3 = (d2 - 5.0) * 60.0; str6 = "5" }
|
||||||
|
((d2 >= 6.0) && (d2 <= 7.0)) || ((d2 >= -4.0) && (d2 <= -3.0)) -> { d3 = (d2 - 6.0) * 60.0; str6 = "6" }
|
||||||
|
((d2 >= 7.0) && (d2 <= 8.0)) || ((d2 >= -3.0) && (d2 <= -2.0)) -> { d3 = (d2 - 7.0) * 60.0; str6 = "7" }
|
||||||
|
((d2 >= 8.0) && (d2 <= 9.0)) || ((d2 >= -2.0) && (d2 <= -1.0)) -> { d3 = (d2 - 8.0) * 60.0; str6 = "8" }
|
||||||
|
((d2 >= 9.0) && (d2 <= 10.0)) || ((d2 >= -1.0) && (d2 <= 0.0)) -> { d3 = (d2 - 9.0) * 60.0; str6 = "9" }
|
||||||
|
}
|
||||||
|
val str38 = when {
|
||||||
|
((d3 >= 0.0) && (d3 <= 2.5)) || ((d3 >= -60.0) && (d3 <= -57.5)) -> { d2 = d3 * 60.0; "a" }
|
||||||
|
((d3 >= 2.5) && (d3 <= 5.0)) || ((d3 >= -57.5) && (d3 <= -55.0)) -> { d2 = (d3 - 2.5) * 60.0; "b" }
|
||||||
|
((d3 >= 5.0) && (d3 <= 7.5)) || ((d3 >= -55.0) && (d3 <= -52.5)) -> { d2 = (d3 - 5.0) * 60.0; "c" }
|
||||||
|
((d3 >= 7.5) && (d3 <= 10.0)) || ((d3 >= -52.5) && (d3 <= -50.0)) -> { d2 = (d3 - 7.5) * 60.0; "d" }
|
||||||
|
((d3 >= 10.0) && (d3 <= 12.5)) || ((d3 >= -50.0) && (d3 <= -47.5)) -> { d2 = (d3 - 10.0) * 60.0; "e" }
|
||||||
|
((d3 >= 12.5) && (d3 <= 15.0)) || ((d3 >= -47.5) && (d3 <= -45.0)) -> { d2 = (d3 - 12.5) * 60.0; "f" }
|
||||||
|
((d3 >= 15.0) && (d3 <= 17.5)) || ((d3 >= -45.0) && (d3 <= -42.5)) -> { d2 = (d3 - 15.0) * 60.0; "g" }
|
||||||
|
((d3 >= 17.5) && (d3 <= 20.0)) || ((d3 >= -42.5) && (d3 <= -40.0)) -> { d2 = (d3 - 17.5) * 60.0; "h" }
|
||||||
|
((d3 >= 20.0) && (d3 <= 22.5)) || ((d3 >= -40.0) && (d3 <= -37.5)) -> { d2 = (d3 - 20.0) * 60.0; "i" }
|
||||||
|
((d3 >= 22.5) && (d3 <= 25.0)) || ((d3 >= -37.5) && (d3 <= -35.0)) -> { d2 = (d3 - 22.5) * 60.0; "j" }
|
||||||
|
((d3 >= 25.0) && (d3 <= 27.5)) || ((d3 >= -35.0) && (d3 <= -32.5)) -> { d2 = (d3 - 25.0) * 60.0; "k" }
|
||||||
|
((d3 >= 27.5) && (d3 <= 30.0)) || ((d3 >= -32.5) && (d3 <= -30.0)) -> { d2 = (d3 - 27.5) * 60.0; "l" }
|
||||||
|
((d3 >= 30.0) && (d3 <= 32.5)) || ((d3 >= -30.0) && (d3 <= -27.5)) -> { d2 = (d3 - 30.0) * 60.0; "m" }
|
||||||
|
((d3 >= 32.5) && (d3 <= 35.0)) || ((d3 >= -27.5) && (d3 <= -25.0)) -> { d2 = (d3 - 32.5) * 60.0; "n" }
|
||||||
|
((d3 >= 35.0) && (d3 <= 37.5)) || ((d3 >= -25.0) && (d3 <= -22.5)) -> { d2 = (d3 - 35.0) * 60.0; "o" }
|
||||||
|
((d3 >= 37.5) && (d3 <= 40.0)) || ((d3 >= -22.5) && (d3 <= -20.0)) -> { d2 = (d3 - 37.5) * 60.0; "p" }
|
||||||
|
((d3 >= 40.0) && (d3 <= 42.5)) || ((d3 >= -20.0) && (d3 <= -17.5)) -> { d2 = (d3 - 40.0) * 60.0; "q" }
|
||||||
|
((d3 >= 42.5) && (d3 <= 45.0)) || ((d3 >= -17.5) && (d3 <= -15.0)) -> { d2 = (d3 - 42.5) * 60.0; "r" }
|
||||||
|
((d3 >= 45.0) && (d3 <= 47.5)) || ((d3 >= -15.0) && (d3 <= -12.5)) -> { d2 = (d3 - 45.0) * 60.0; "s" }
|
||||||
|
((d3 >= 47.5) && (d3 <= 50.0)) || ((d3 >= -12.5) && (d3 <= -10.0)) -> { d2 = (d3 - 47.5) * 60.0; "t" }
|
||||||
|
((d3 >= 50.0) && (d3 <= 52.5)) || ((d3 >= -10.0) && (d3 <= -7.5)) -> { d2 = (d3 - 50.0) * 60.0; "u" }
|
||||||
|
((d3 >= 52.5) && (d3 <= 55.0)) || ((d3 >= -7.5) && (d3 <= -5.0)) -> { d2 = (d3 - 52.5) * 60.0; "v" }
|
||||||
|
((d3 >= 55.0) && (d3 <= 57.5)) || ((d3 >= -5.0) && (d3 <= -2.5)) -> { d2 = (d3 - 55.0) * 60.0; "w" }
|
||||||
|
((d3 >= 57.5) && (d3 <= 60.0)) || ((d3 >= -2.5) && (d3 <= 0.0)) -> { d2 = (d3 - 57.5) * 60.0; "x" }
|
||||||
|
else -> { d2 = 0.0; " " }
|
||||||
|
}
|
||||||
|
val str9 = when {
|
||||||
|
((d2 >= 0.0) && (d2 <= 15.0)) || ((d2 >= -150.0) && (d2 <= -135.0)) -> "0"
|
||||||
|
((d2 >= 15.0) && (d2 <= 30.0)) || ((d2 >= -135.0) && (d2 <= -120.0)) -> "1"
|
||||||
|
((d2 >= 30.0) && (d2 <= 45.0)) || ((d2 >= -120.0) && (d2 <= -105.0)) -> "2"
|
||||||
|
((d2 >= 45.0) && (d2 <= 60.0)) || ((d2 >= -105.0) && (d2 <= -90.0)) -> "3"
|
||||||
|
((d2 >= 60.0) && (d2 <= 75.0)) || ((d2 >= -90.0) && (d2 <= -75.0)) -> "4"
|
||||||
|
((d2 >= 75.0) && (d2 <= 90.0)) || ((d2 >= -75.0) && (d2 <= -60.0)) -> "5"
|
||||||
|
((d2 >= 90.0) && (d2 <= 105.0)) || ((d2 >= -60.0) && (d2 <= -45.0)) -> "6"
|
||||||
|
((d2 >= 105.0) && (d2 <= 120.0)) || ((d2 >= -45.0) && (d2 <= -30.0)) -> "7"
|
||||||
|
((d2 >= 120.0) && (d2 <= 135.0)) || ((d2 >= -30.0) && (d2 <= -15.0)) -> "8"
|
||||||
|
((d2 >= 135.0) && (d2 <= 150.0)) || ((d2 >= -15.0) && (d2 <= 0.0)) -> "9"
|
||||||
|
else -> " "
|
||||||
|
}
|
||||||
|
return listOf(str5, str6, str38, str9)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 照搬 917-1283 行: 九宫格 parseInt3 五分支(内部/西界/东界/北界/南界 + 四角)。
|
||||||
|
* 返回 9 格文本: [上左, 上中, 上右, 左中, 中央, 右中, 下左, 下中, 下右]。
|
||||||
|
*/
|
||||||
|
private fun buildGrids(str: String, str5: String, str2: String, str6: String, str3: String, str38: String): List<String> {
|
||||||
|
val parseInt = str2.toInt()
|
||||||
|
val parseInt2 = str6.toInt()
|
||||||
|
val str39 = str + str5
|
||||||
|
val str40 = str2 + str6
|
||||||
|
val parseInt3 = str40.toInt()
|
||||||
|
val str41 = str39 + str40
|
||||||
|
val g = MutableList(9) { "" }
|
||||||
|
fun fmt2(n: Int) = if (n <= 9) "0$n" else n.toString()
|
||||||
|
if ((parseInt > 0) && (parseInt < 9) && (parseInt2 > 0) && (parseInt2 < 9)) {
|
||||||
|
// 内部: 数字对 ±1
|
||||||
|
val i7 = parseInt3 + 1
|
||||||
|
val i8 = i7 - 10
|
||||||
|
val valueOf9 = fmt2(i8)
|
||||||
|
val str11 = i7.toString()
|
||||||
|
val valueOf18 = (i7 + 10).toString()
|
||||||
|
val i9 = parseInt3 - 10
|
||||||
|
val valueOf10 = fmt2(i9)
|
||||||
|
val valueOf19 = (parseInt3 + 10).toString()
|
||||||
|
val i10 = parseInt3 - 1
|
||||||
|
val i11 = i10 - 10
|
||||||
|
val valueOf11 = fmt2(i11)
|
||||||
|
val str14 = i10.toString()
|
||||||
|
val str15 = (i10 + 10).toString()
|
||||||
|
g[0] = str39 + valueOf9
|
||||||
|
g[1] = str39 + str11
|
||||||
|
g[2] = str39 + valueOf18
|
||||||
|
g[3] = str39 + valueOf10
|
||||||
|
g[4] = str41
|
||||||
|
g[5] = str39 + valueOf19
|
||||||
|
g[6] = str39 + valueOf11
|
||||||
|
g[7] = str39 + str14
|
||||||
|
g[8] = str39 + str15
|
||||||
|
} else if ((parseInt == 0) && (parseInt2 > 0) && (parseInt2 < 9)) {
|
||||||
|
// 西边界: 经度 0 -> 9, 经度字母 -1
|
||||||
|
val str43 = (str[0] - 1).toString() + str5
|
||||||
|
val str44 = "9" + (parseInt2 + 1)
|
||||||
|
val i12 = parseInt3 + 1
|
||||||
|
val valueOf7 = fmt2(i12)
|
||||||
|
val str12 = (i12 + 10).toString()
|
||||||
|
val sb4 = "9" + parseInt2
|
||||||
|
val valueOf20 = (parseInt3 + 10).toString()
|
||||||
|
val str46 = "9" + (parseInt2 - 1)
|
||||||
|
val i13 = parseInt3 - 1
|
||||||
|
val valueOf8 = fmt2(i13)
|
||||||
|
val str15 = (i13 + 10).toString()
|
||||||
|
g[0] = str43 + str44
|
||||||
|
g[1] = str39 + valueOf7
|
||||||
|
g[2] = str39 + str12
|
||||||
|
g[3] = str43 + sb4
|
||||||
|
g[4] = str41
|
||||||
|
g[5] = str39 + valueOf20
|
||||||
|
g[6] = str43 + str46
|
||||||
|
g[7] = str39 + valueOf8
|
||||||
|
g[8] = str39 + str15
|
||||||
|
} else if ((parseInt == 9) && (parseInt2 > 0) && (parseInt2 < 9)) {
|
||||||
|
// 东边界: 经度 9 -> 0, 经度字母 +1
|
||||||
|
val str47 = (str[0] + 1).toString() + str5
|
||||||
|
val i14 = parseInt3 + 1
|
||||||
|
val valueOf21 = (i14 - 10).toString()
|
||||||
|
val valueOf22 = i14.toString()
|
||||||
|
val str48 = "0" + (parseInt2 + 1)
|
||||||
|
val valueOf23 = (parseInt3 - 10).toString()
|
||||||
|
val str49 = "0" + parseInt2
|
||||||
|
val i15 = parseInt3 - 1
|
||||||
|
val valueOf24 = (i15 - 10).toString()
|
||||||
|
val valueOf25 = i15.toString()
|
||||||
|
val str15 = "0" + (parseInt2 - 1)
|
||||||
|
g[0] = str39 + valueOf21
|
||||||
|
g[1] = str39 + valueOf22
|
||||||
|
g[2] = str47 + str48
|
||||||
|
g[3] = str39 + valueOf23
|
||||||
|
g[4] = str41
|
||||||
|
g[5] = str47 + str49
|
||||||
|
g[6] = str39 + valueOf24
|
||||||
|
g[7] = str39 + valueOf25
|
||||||
|
g[8] = str47 + str15
|
||||||
|
} else if ((parseInt < 9) && (parseInt > 0) && (parseInt2 == 9)) {
|
||||||
|
// 北边界: 纬度 9 -> 0, 纬度字母 +1
|
||||||
|
val str50 = str + (str5[0] + 1).toString()
|
||||||
|
val i16 = parseInt3 - 9
|
||||||
|
val i17 = i16 - 10
|
||||||
|
val valueOf4 = fmt2(i17)
|
||||||
|
val valueOf26 = i16.toString()
|
||||||
|
val valueOf27 = (i16 + 10).toString()
|
||||||
|
val i18 = parseInt3 - 10
|
||||||
|
val valueOf5 = fmt2(i18)
|
||||||
|
val valueOf28 = (parseInt3 + 10).toString()
|
||||||
|
val i19 = parseInt3 - 1
|
||||||
|
val i20 = i19 - 10
|
||||||
|
val valueOf6 = fmt2(i20)
|
||||||
|
val str14 = i19.toString()
|
||||||
|
val str15 = (i19 + 10).toString()
|
||||||
|
g[0] = str39 + valueOf4
|
||||||
|
g[1] = str50 + valueOf26
|
||||||
|
g[2] = str39 + valueOf27
|
||||||
|
g[3] = str39 + valueOf5
|
||||||
|
g[4] = str41
|
||||||
|
g[5] = str39 + valueOf28
|
||||||
|
g[6] = str39 + valueOf6
|
||||||
|
g[7] = str39 + str14
|
||||||
|
g[8] = str39 + str15
|
||||||
|
} else if ((parseInt < 9) && (parseInt > 0) && (parseInt2 == 0)) {
|
||||||
|
// 南边界: 纬度 0 -> 9, 纬度字母 -1
|
||||||
|
val str52 = str + (str5[0] - 1).toString()
|
||||||
|
val i21 = parseInt3 + 1
|
||||||
|
val i22 = i21 - 10
|
||||||
|
val valueOf = fmt2(i22)
|
||||||
|
val valueOf29 = i21.toString()
|
||||||
|
val valueOf30 = (i21 + 10).toString()
|
||||||
|
val i23 = parseInt3 - 10
|
||||||
|
val valueOf2 = fmt2(i23)
|
||||||
|
val valueOf31 = (parseInt3 + 10).toString()
|
||||||
|
val i24 = parseInt3 - 1
|
||||||
|
val valueOf3 = fmt2(i24)
|
||||||
|
val str14 = (i24 + 10).toString()
|
||||||
|
val str15 = (i24 + 20).toString()
|
||||||
|
g[0] = str39 + valueOf
|
||||||
|
g[1] = str39 + valueOf29
|
||||||
|
g[2] = str39 + valueOf30
|
||||||
|
g[3] = str39 + valueOf2
|
||||||
|
g[4] = str41
|
||||||
|
g[5] = str39 + valueOf31
|
||||||
|
g[6] = str52 + valueOf3
|
||||||
|
g[7] = str52 + str14
|
||||||
|
g[8] = str52 + str15
|
||||||
|
} else if (parseInt3 == 0) {
|
||||||
|
// 四角 00: 经纬度数字均 -1 进位
|
||||||
|
val v32 = (str[0] - 1).toString()
|
||||||
|
val v33 = (str5[0] - 1).toString()
|
||||||
|
g[0] = v32 + str5 + "91"
|
||||||
|
g[1] = str39 + "01"
|
||||||
|
g[2] = str39 + "11"
|
||||||
|
g[3] = v32 + str5 + "90"
|
||||||
|
g[4] = str41
|
||||||
|
g[5] = v32 + str5 + "10"
|
||||||
|
g[6] = v32 + v33 + "99"
|
||||||
|
g[7] = str + v33 + "09"
|
||||||
|
g[8] = str + v33 + "19"
|
||||||
|
} else if (parseInt3 == 9) {
|
||||||
|
// 四角 09: 经度 -1, 纬度 +1
|
||||||
|
val v34 = (str[0] - 1).toString()
|
||||||
|
val v35 = (str5[0] + 1).toString()
|
||||||
|
g[0] = v34 + v35 + "90"
|
||||||
|
g[1] = str + v35 + "00"
|
||||||
|
g[2] = str + v35 + "10"
|
||||||
|
g[3] = v34 + str5 + "99"
|
||||||
|
g[4] = str41
|
||||||
|
g[5] = v34 + str5 + "19"
|
||||||
|
g[6] = v34 + str5 + "98"
|
||||||
|
g[7] = v34 + str5 + "08"
|
||||||
|
g[8] = v34 + str5 + "18"
|
||||||
|
} else if (parseInt3 == 99) {
|
||||||
|
// 四角 99: 经纬度数字均 +1 进位
|
||||||
|
val v36 = (str[0] + 1).toString()
|
||||||
|
val v37 = (str5[0] + 1).toString()
|
||||||
|
g[0] = str + v37 + "80"
|
||||||
|
g[1] = str + v37 + "90"
|
||||||
|
g[2] = v36 + v37 + "00"
|
||||||
|
g[3] = str39 + "89"
|
||||||
|
g[4] = str41
|
||||||
|
g[5] = v36 + str5 + "09"
|
||||||
|
g[6] = str39 + "88"
|
||||||
|
g[7] = str39 + "98"
|
||||||
|
g[8] = v36 + str5 + "08"
|
||||||
|
} else if (parseInt3 == 90) {
|
||||||
|
// 四角 90: 经度 +1, 纬度 -1
|
||||||
|
val v38 = (str[0] + 1).toString()
|
||||||
|
val v39 = (str5[0] - 1).toString()
|
||||||
|
g[0] = str39 + "81"
|
||||||
|
g[1] = str39 + "91"
|
||||||
|
g[2] = v38 + str5 + "01"
|
||||||
|
g[3] = str39 + "80"
|
||||||
|
g[4] = str41
|
||||||
|
g[5] = v38 + str5 + "00"
|
||||||
|
g[6] = str + v39 + "89"
|
||||||
|
g[7] = str + v39 + "99"
|
||||||
|
g[8] = v38 + v39 + "09"
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 176 B |
Binary file not shown.
|
After Width: | Height: | Size: 172 B |
Binary file not shown.
|
After Width: | Height: | Size: 375 B |
+124
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||||
|
* Copyright (C) 2019-2026 Arty Bishop and contributors.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rtbishop.look4sat.feature.roaming
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class RoamingStateTest {
|
||||||
|
|
||||||
|
// 用户实测坐标 (QTH定位器 2.0 成品截图): 22.314066 / 108.706575
|
||||||
|
private fun stateOf(lat: Double, lon: Double) =
|
||||||
|
roamingStateFromLocation(lat, lon, System.currentTimeMillis(), "21:")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `user coordinates produce OL42ih45`() {
|
||||||
|
val state = stateOf(22.314066, 108.706575)
|
||||||
|
assertEquals("OL42ih45", state.loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `red marker from ih pair matches reference lookup`() {
|
||||||
|
val state = stateOf(22.314066, 108.706575)
|
||||||
|
// 成品查表: 经度第3对 'i' -> leftMargin 65dp, 纬度第3对 'h' -> topMargin 137dp
|
||||||
|
assertEquals(65, state.markerLeft)
|
||||||
|
assertEquals(137, state.markerTop)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `grid nine from OL42 matches reference`() {
|
||||||
|
val state = stateOf(22.314066, 108.706575)
|
||||||
|
val expected = listOf(
|
||||||
|
"OL33", "OL43", "OL53",
|
||||||
|
"OL32", "OL42", "OL52",
|
||||||
|
"OL31", "OL41", "OL51"
|
||||||
|
)
|
||||||
|
assertEquals(expected, state.grids)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `latitude dms formatting matches reference`() {
|
||||||
|
val state = stateOf(22.314066, 108.706575)
|
||||||
|
assertEquals("纬度 22° 18' 50\" N", state.latLabel)
|
||||||
|
assertEquals("22.314066° ", state.latValue)
|
||||||
|
assertEquals("经度 108° 42' 23\" E", state.lonLabel)
|
||||||
|
assertEquals("108.706575° ", state.lonValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `time is hour prefix plus fix minutes`() {
|
||||||
|
// fixTime = 2026-08-03 21:12:13 UTC+8 -> minutes 12
|
||||||
|
val fix = 1785820333000L // 2026-08-03 21:12:13 +0800
|
||||||
|
val state = roamingStateFromLocation(22.314066, 108.706575, fix, "21:")
|
||||||
|
assertEquals("21:12", state.time)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `west edge borrows lon letter`() {
|
||||||
|
// 经度 0.5 纬度 22.314066: 数字对 02, 西边界 parseInt==0
|
||||||
|
val state = stateOf(22.314066, 0.5)
|
||||||
|
assertEquals(8, state.loc.length)
|
||||||
|
// 左列经度 0->9, 经度字母 J-1=I: IL92
|
||||||
|
assertEquals("IL92", state.grids[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `north edge carries lat letter`() {
|
||||||
|
// 纬度 29.5 经度 108.706575: 数字对 49? -> 纬度数字 9 -> 北边界
|
||||||
|
val state = stateOf(29.5, 108.706575)
|
||||||
|
assertEquals(8, state.loc.length)
|
||||||
|
// 上排纬度 +1 -> 0, 纬度字母 +1 (M)
|
||||||
|
assertEquals("OM40", state.grids[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `east edge carries lon letter`() {
|
||||||
|
// 经度 119.5 纬度 22.314066: 经度数字 9 -> 东边界
|
||||||
|
val state = stateOf(22.314066, 119.5)
|
||||||
|
assertEquals(8, state.loc.length)
|
||||||
|
// 右上 经度 9->0, 经度字母 O+1=P, 纬度 2+1=3: PL03
|
||||||
|
assertEquals("PL03", state.grids[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `south edge borrows lat letter`() {
|
||||||
|
// 纬度 0.5 经度 108.706575: 纬度数字 0 -> 南边界
|
||||||
|
val state = stateOf(0.5, 108.706575)
|
||||||
|
assertEquals(8, state.loc.length)
|
||||||
|
// 下中 纬度 0->9, 纬度字母 J-1=I: OI49
|
||||||
|
assertEquals("OI49", state.grids[7])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `boundary values wrap like reference`() {
|
||||||
|
// (90, 180) -> RR99xx99 (成品区间查表行为)
|
||||||
|
val state = stateOf(90.0, 180.0)
|
||||||
|
assertEquals("RR99xx99", state.loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `gps state after fix matches showLocation`() {
|
||||||
|
val state = stateOf(22.314066, 108.706575)
|
||||||
|
assertEquals(true, state.gpsOn)
|
||||||
|
assertEquals(false, state.gpsOff)
|
||||||
|
assertEquals(false, state.showSettings)
|
||||||
|
assertEquals(false, state.showProgress)
|
||||||
|
assertEquals(false, state.showNotice)
|
||||||
|
assertEquals(true, state.showDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user