|
|
|
|
@ -56,6 +56,8 @@ import com.utun.chat.data.LogManager
|
|
|
|
|
import com.utun.chat.data.Message |
|
|
|
|
import com.utun.chat.data.ConversationTarget |
|
|
|
|
import com.utun.chat.data.NativeLib |
|
|
|
|
import com.utun.chat.data.FileLocation |
|
|
|
|
import com.utun.chat.data.MemberFilesProtocol |
|
|
|
|
import com.utun.chat.data.RadioOverlayService |
|
|
|
|
import com.utun.chat.data.RadioAudioEngine |
|
|
|
|
import com.utun.chat.data.RadioPttSource |
|
|
|
|
@ -64,14 +66,16 @@ import com.utun.chat.ui.theme.VibeChatTheme
|
|
|
|
|
import com.utun.chat.ui.components.ActiveCallBanner |
|
|
|
|
import com.utun.chat.ui.screens.* |
|
|
|
|
import com.utun.chat.viewmodel.ChatViewModel |
|
|
|
|
import com.utun.chat.viewmodel.MemberFilesViewModel |
|
|
|
|
import kotlinx.coroutines.launch |
|
|
|
|
import kotlinx.coroutines.flow.collect |
|
|
|
|
|
|
|
|
|
class MainActivity : AppCompatActivity() { |
|
|
|
|
enum class Screen { ChannelList, Chat, MemberList, Settings, Logs, VideoRecord, PhotoRecord, Call, Dm } |
|
|
|
|
enum class Screen { ChannelList, Chat, MemberList, Files, Settings, Logs, VideoRecord, PhotoRecord, Call, Dm } |
|
|
|
|
|
|
|
|
|
private var overLock = false |
|
|
|
|
private var inputScreen = "setup" |
|
|
|
|
private var requestedFiles by mutableStateOf<FileLocation?>(null) |
|
|
|
|
override fun dispatchTouchEvent(event: MotionEvent): Boolean { |
|
|
|
|
val started = SystemClock.uptimeMillis() |
|
|
|
|
try { |
|
|
|
|
@ -141,6 +145,7 @@ class MainActivity : AppCompatActivity() {
|
|
|
|
|
|
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) { |
|
|
|
|
super.onCreate(savedInstanceState) |
|
|
|
|
readFilesIntent(intent) |
|
|
|
|
lifecycleScope.launch { |
|
|
|
|
ChatApplication.instance.configProvider.radioVolumePttEnabled.collect { enabled -> |
|
|
|
|
if (!enabled) releaseVolumePtt("option-disabled") |
|
|
|
|
@ -189,6 +194,7 @@ class MainActivity : AppCompatActivity() {
|
|
|
|
|
return@VibeChatTheme |
|
|
|
|
} |
|
|
|
|
val vm: ChatViewModel = viewModel(factory = ChatViewModel.Factory()) |
|
|
|
|
val filesVm: MemberFilesViewModel = viewModel(factory = MemberFilesViewModel.Factory) |
|
|
|
|
|
|
|
|
|
/* Pause GUI polling (health-check, recording/playback timers) when backgrounded, |
|
|
|
|
refresh statuses on resume. */ |
|
|
|
|
@ -198,6 +204,17 @@ class MainActivity : AppCompatActivity() {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var screen by rememberSaveable { mutableStateOf(Screen.ChannelList) } |
|
|
|
|
var filesReturnScreen by rememberSaveable { mutableStateOf(Screen.Chat) } |
|
|
|
|
val fileChannels by vm.channels.collectAsState() |
|
|
|
|
LaunchedEffect(requestedFiles, fileChannels) { |
|
|
|
|
requestedFiles?.let { target -> |
|
|
|
|
val channel = fileChannels.find { it.id == target.group.toULong().toString() } |
|
|
|
|
if (channel != null) { |
|
|
|
|
requestedFiles = null; vm.selectChannel(channel); filesVm.openLocation(target) |
|
|
|
|
filesReturnScreen = Screen.Chat; screen = Screen.Files |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
var showJoinDialog by rememberSaveable { mutableStateOf(false) } |
|
|
|
|
var inviteChannelId by rememberSaveable { mutableStateOf<String?>(null) } |
|
|
|
|
var videoMessage by remember { mutableStateOf<Message?>(null) } |
|
|
|
|
@ -227,7 +244,7 @@ class MainActivity : AppCompatActivity() {
|
|
|
|
|
inputScreen = screen.name |
|
|
|
|
LogManager.addLog("INFO", "MAIN", "UI screen=$screen") |
|
|
|
|
val unavailable = when (screen) { |
|
|
|
|
Screen.Chat, Screen.MemberList, Screen.PhotoRecord -> vm.currentChannel.value == null |
|
|
|
|
Screen.Chat, Screen.MemberList, Screen.Files, Screen.PhotoRecord -> vm.currentChannel.value == null |
|
|
|
|
Screen.Dm -> vm.currentDm.value == null |
|
|
|
|
Screen.VideoRecord -> videoTarget == null |
|
|
|
|
Screen.Call -> callState == null |
|
|
|
|
@ -291,7 +308,7 @@ class MainActivity : AppCompatActivity() {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
BackHandler(enabled = screen != Screen.ChannelList) { |
|
|
|
|
BackHandler(enabled = screen != Screen.ChannelList && screen != Screen.Files) { |
|
|
|
|
when (screen) { |
|
|
|
|
Screen.MemberList -> screen = Screen.Chat |
|
|
|
|
Screen.VideoRecord -> screen = if (videoTarget is ConversationTarget.Pm) Screen.Dm else Screen.Chat |
|
|
|
|
@ -368,6 +385,9 @@ class MainActivity : AppCompatActivity() {
|
|
|
|
|
viewModel = vm, |
|
|
|
|
onSendMessage = { vm.sendMessage(it) }, |
|
|
|
|
onMembersClick = { screen = Screen.MemberList }, |
|
|
|
|
onFiles = { owner -> |
|
|
|
|
filesVm.open(channel.id, owner); filesReturnScreen = Screen.Chat; screen = Screen.Files |
|
|
|
|
}, |
|
|
|
|
onInvite = { showInvite(channel) }, |
|
|
|
|
onBack = { screen = Screen.ChannelList }, |
|
|
|
|
onPlayVideo = { msg -> videoMessage = msg }, |
|
|
|
|
@ -393,10 +413,17 @@ class MainActivity : AppCompatActivity() {
|
|
|
|
|
members = members, |
|
|
|
|
selectedMember = selectedMember, |
|
|
|
|
viewModel = vm, |
|
|
|
|
onFiles = { owner -> |
|
|
|
|
filesVm.open(channel.id, owner); filesReturnScreen = Screen.MemberList; screen = Screen.Files |
|
|
|
|
}, |
|
|
|
|
onBack = { screen = Screen.Chat } |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Screen.Files -> { |
|
|
|
|
val channel = vm.currentChannel.collectAsState().value |
|
|
|
|
if (channel != null) MemberFilesScreen(channel, filesVm) { screen = filesReturnScreen } |
|
|
|
|
} |
|
|
|
|
Screen.Dm -> { |
|
|
|
|
val dmMessages by vm.dmMessages.collectAsState() |
|
|
|
|
val dm = vm.currentDm.collectAsState().value |
|
|
|
|
@ -552,6 +579,7 @@ class MainActivity : AppCompatActivity() {
|
|
|
|
|
override fun onNewIntent(intent: Intent) { |
|
|
|
|
super.onNewIntent(intent) |
|
|
|
|
setIntent(intent) |
|
|
|
|
readFilesIntent(intent) |
|
|
|
|
if (intent.getBooleanExtra(MicrophonePermission.EXTRA_REQUEST, false)) { |
|
|
|
|
intent.removeExtra(MicrophonePermission.EXTRA_REQUEST) |
|
|
|
|
requestMicrophone("PTT") { |
|
|
|
|
@ -568,6 +596,20 @@ class MainActivity : AppCompatActivity() {
|
|
|
|
|
private fun incomingCallId(intent: Intent?): Long = |
|
|
|
|
intent?.getLongExtra(IncomingCallManager.EXTRA_CALL_ID, 0L) ?: 0L |
|
|
|
|
|
|
|
|
|
private fun readFilesIntent(intent: Intent) { |
|
|
|
|
val group = intent.getLongExtra("files_group", 0) |
|
|
|
|
if (group == 0L) return |
|
|
|
|
val server = intent.getLongExtra("files_server", 0) |
|
|
|
|
val owner = intent.getLongExtra("files_owner", 0) |
|
|
|
|
val path = intent.getStringExtra("files_path") ?: "" |
|
|
|
|
intent.removeExtra("files_group") |
|
|
|
|
if (server == 0L || !MemberFilesProtocol.validPath(path) || owner == 0L && path.isNotEmpty()) { |
|
|
|
|
LogManager.addLog("ERROR", "Files", "Invalid notification target group=$group server=$server owner=$owner path=$path"); return |
|
|
|
|
} |
|
|
|
|
requestedFiles = FileLocation(group, server, owner, path) |
|
|
|
|
LogManager.addLog("INFO", "Files", "Notification opened location=$requestedFiles") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Показать активити поверх блокировки и включить экран (входящий звонок). */ |
|
|
|
|
private fun showOverLockScreen() { |
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { |
|
|
|
|
|