Open Android file browser from channels and members
This commit is contained in:
1 parent
6a3def3c9a
commit
4abfbc3e9c
3 files changed
+56
-3
No files matched your search
@@ -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) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.utun.chat.ui.screens
|
||||
|
||||
import com.utun.chat.data.NativeLib
|
||||
|
||||
import com.utun.chat.R
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import android.os.SystemClock
|
||||
@@ -80,6 +82,7 @@ fun ChatScreen(
|
||||
viewModel: ChatViewModel,
|
||||
onSendMessage: (String) -> Unit,
|
||||
onMembersClick: () -> Unit,
|
||||
onFiles: (Long) -> Unit = {},
|
||||
onInvite: () -> Unit,
|
||||
onBack: () -> Unit,
|
||||
onPlayVideo: (Message) -> Unit = {},
|
||||
@@ -181,6 +184,10 @@ fun ChatScreen(
|
||||
Box {
|
||||
IconButton(onClick = { menu = true }) { Icon(Icons.Filled.MoreVert, stringResource(R.string.options)) }
|
||||
DropdownMenu(expanded = menu, onDismissRequest = { menu = false }) {
|
||||
DropdownMenuItem(text = { Text(stringResource(R.string.files_title)) },
|
||||
onClick = { menu = false; onFiles(0) })
|
||||
DropdownMenuItem(text = { Text(stringResource(R.string.files_mine)) },
|
||||
onClick = { menu = false; onFiles(NativeLib.getMyNodeId()) })
|
||||
DropdownMenuItem(text = { Text(stringResource(R.string.poll_create)) }, onClick = { menu = false; createPoll = true })
|
||||
DropdownMenuItem(text = { Text(stringResource(R.string.invite)) },
|
||||
leadingIcon = { Icon(Icons.Filled.Link, null) }, onClick = { menu = false; onInvite() })
|
||||
|
||||
@@ -59,6 +59,7 @@ fun MemberListScreen(
|
||||
members: List<ChatMember>,
|
||||
selectedMember: MemberDetail?,
|
||||
viewModel: ChatViewModel,
|
||||
onFiles: (Long) -> Unit = {},
|
||||
onBack: () -> Unit
|
||||
) {
|
||||
var showEditDialog by remember { mutableStateOf(false) }
|
||||
@@ -124,6 +125,7 @@ fun MemberListScreen(
|
||||
localSocketStates = localSockets,
|
||||
isSelf = member?.isSelf == true,
|
||||
canEdit = member != null,
|
||||
onFiles = { onFiles(detail.nodeId) },
|
||||
onConnect = {
|
||||
Log.i("utun-gui", "Member detail connect: channel=${channel.id} node=${detail.nodeId}")
|
||||
viewModel.connectMemberNode(channel.id, detail.nodeId)
|
||||
@@ -291,6 +293,7 @@ private fun MemberDetailCard(
|
||||
localSocketStates: List<LocalSocketState>,
|
||||
isSelf: Boolean = false,
|
||||
canEdit: Boolean,
|
||||
onFiles: () -> Unit,
|
||||
onConnect: () -> Unit,
|
||||
onEdit: () -> Unit
|
||||
) {
|
||||
@@ -363,6 +366,7 @@ private fun MemberDetailCard(
|
||||
}
|
||||
if (!isSelf) TextButton(onClick = onConnect, enabled = !detail.directUp) { Text(stringResource(R.string.connect)) }
|
||||
TextButton(onClick = onEdit, enabled = canEdit) { Text(stringResource(R.string.edit)) }
|
||||
TextButton(onClick = onFiles) { Text(stringResource(R.string.files_member)) }
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(4.dp))
|
||||
|
||||
Reference in new issue
Block a user