mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-28 12:19:54 +00:00
mobile: call settings, request camera on iOS on call start (#701)
* mobile: call settings, request camera on iOS on call start * refactor preferences * fix typo
This commit is contained in:
parent
79d9e90ab7
commit
da13e6614b
28 changed files with 686 additions and 513 deletions
5
apps/android/.idea/codeStyles/Project.xml
generated
5
apps/android/.idea/codeStyles/Project.xml
generated
|
@ -1,5 +1,8 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<ComposeCustomCodeStyleSettings>
|
||||
<option name="USE_CUSTOM_FORMATTING_FOR_MODIFIERS" value="false" />
|
||||
</ComposeCustomCodeStyleSettings>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="SPACE_BEFORE_EXTEND_COLON" value="false" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="3" />
|
||||
|
@ -123,9 +126,11 @@
|
|||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
<option name="RIGHT_MARGIN" value="140" />
|
||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
||||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="0" />
|
||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="CALL_PARAMETERS_WRAP" value="0" />
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="0" />
|
||||
<option name="EXTENDS_LIST_WRAP" value="0" />
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
|
@ -96,8 +96,7 @@ const processCommand = (function () {
|
|||
const pc = new RTCPeerConnection(config.peerConnectionConfig);
|
||||
const remoteStream = new MediaStream();
|
||||
const localCamera = VideoCamera.User;
|
||||
const constraints = callMediaConstraints(mediaType, localCamera);
|
||||
const localStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
const localStream = await getLocalMediaStream(mediaType, localCamera);
|
||||
const iceCandidates = getIceCandidates(pc, config);
|
||||
const call = { connection: pc, iceCandidates, localMedia: mediaType, localCamera, localStream, remoteStream, aesKey, useWorker };
|
||||
await setupMediaStreams(call);
|
||||
|
@ -156,11 +155,17 @@ const processCommand = (function () {
|
|||
try {
|
||||
switch (command.type) {
|
||||
case "capabilities":
|
||||
console.log("starting outgoing call - capabilities");
|
||||
if (activeCall)
|
||||
endCall();
|
||||
// This request for local media stream is made to prompt for camera/mic permissions on call start
|
||||
if (command.media)
|
||||
await getLocalMediaStream(command.media, VideoCamera.User);
|
||||
const encryption = supportsInsertableStreams(command.useWorker);
|
||||
resp = { type: "capabilities", capabilities: { encryption } };
|
||||
break;
|
||||
case "start": {
|
||||
console.log("starting call");
|
||||
console.log("starting incoming call - create webrtc session");
|
||||
if (activeCall)
|
||||
endCall();
|
||||
const { media, useWorker, iceServers, relay } = command;
|
||||
|
@ -394,8 +399,7 @@ const processCommand = (function () {
|
|||
for (const t of call.localStream.getTracks())
|
||||
t.stop();
|
||||
call.localCamera = camera;
|
||||
const constraints = callMediaConstraints(call.localMedia, camera);
|
||||
const localStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
const localStream = await getLocalMediaStream(call.localMedia, camera);
|
||||
replaceTracks(pc, localStream.getVideoTracks());
|
||||
replaceTracks(pc, localStream.getAudioTracks());
|
||||
call.localStream = localStream;
|
||||
|
@ -430,6 +434,10 @@ const processCommand = (function () {
|
|||
console.log(`no ${operation}`);
|
||||
}
|
||||
}
|
||||
function getLocalMediaStream(mediaType, facingMode) {
|
||||
const constraints = callMediaConstraints(mediaType, facingMode);
|
||||
return navigator.mediaDevices.getUserMedia(constraints);
|
||||
}
|
||||
function callMediaConstraints(mediaType, facingMode) {
|
||||
switch (mediaType) {
|
||||
case CallMediaType.Audio:
|
||||
|
|
|
@ -72,7 +72,7 @@ class MainActivity: FragmentActivity(), LifecycleEventObserver {
|
|||
val m = vm.chatModel
|
||||
val lastLAVal = lastLA.value
|
||||
if (
|
||||
m.controller.getPerformLA()
|
||||
m.controller.prefPerformLA.get()
|
||||
&& (lastLAVal == null || (System.nanoTime() - lastLAVal >= 30 * 1e+9))
|
||||
) {
|
||||
userAuthorized.value = false
|
||||
|
@ -91,7 +91,7 @@ class MainActivity: FragmentActivity(), LifecycleEventObserver {
|
|||
LAResult.Unavailable -> {
|
||||
userAuthorized.value = true
|
||||
m.performLA.value = false
|
||||
m.controller.setPerformLA(false)
|
||||
m.controller.prefPerformLA.set(false)
|
||||
laUnavailableTurningOffAlert()
|
||||
}
|
||||
}
|
||||
|
@ -106,13 +106,13 @@ class MainActivity: FragmentActivity(), LifecycleEventObserver {
|
|||
}
|
||||
|
||||
private fun schedulePeriodicServiceRestartWorker() {
|
||||
val workerVersion = chatController.getAutoRestartWorkerVersion()
|
||||
val workerVersion = chatController.prefAutoRestartWorkerVersion.get()
|
||||
val workPolicy = if (workerVersion == SimplexService.SERVICE_START_WORKER_VERSION) {
|
||||
Log.d(TAG, "ServiceStartWorker version matches: choosing KEEP as existing work policy")
|
||||
ExistingPeriodicWorkPolicy.KEEP
|
||||
} else {
|
||||
Log.d(TAG, "ServiceStartWorker version DOES NOT MATCH: choosing REPLACE as existing work policy")
|
||||
chatController.setAutoRestartWorkerVersion(SimplexService.SERVICE_START_WORKER_VERSION)
|
||||
chatController.prefAutoRestartWorkerVersion.set(SimplexService.SERVICE_START_WORKER_VERSION)
|
||||
ExistingPeriodicWorkPolicy.REPLACE
|
||||
}
|
||||
val work = PeriodicWorkRequestBuilder<SimplexService.ServiceStartWorker>(SimplexService.SERVICE_START_WORKER_INTERVAL_MINUTES, TimeUnit.MINUTES)
|
||||
|
@ -126,7 +126,7 @@ class MainActivity: FragmentActivity(), LifecycleEventObserver {
|
|||
private fun setPerformLA(on: Boolean) {
|
||||
val m = vm.chatModel
|
||||
if (on) {
|
||||
m.controller.setLANoticeShown(true)
|
||||
m.controller.prefLANoticeShown.set(true)
|
||||
authenticate(
|
||||
generalGetString(R.string.auth_enable),
|
||||
generalGetString(R.string.auth_confirm_credential),
|
||||
|
@ -135,24 +135,24 @@ class MainActivity: FragmentActivity(), LifecycleEventObserver {
|
|||
when (laResult) {
|
||||
LAResult.Success -> {
|
||||
m.performLA.value = true
|
||||
m.controller.setPerformLA(true)
|
||||
m.controller.prefPerformLA.set(true)
|
||||
userAuthorized.value = true
|
||||
lastLA.value = System.nanoTime()
|
||||
laTurnedOnAlert()
|
||||
}
|
||||
is LAResult.Error -> {
|
||||
m.performLA.value = false
|
||||
m.controller.setPerformLA(false)
|
||||
m.controller.prefPerformLA.set(false)
|
||||
laErrorToast(applicationContext, laResult.errString)
|
||||
}
|
||||
LAResult.Failed -> {
|
||||
m.performLA.value = false
|
||||
m.controller.setPerformLA(false)
|
||||
m.controller.prefPerformLA.set(false)
|
||||
laFailedToast(applicationContext)
|
||||
}
|
||||
LAResult.Unavailable -> {
|
||||
m.performLA.value = false
|
||||
m.controller.setPerformLA(false)
|
||||
m.controller.prefPerformLA.set(false)
|
||||
laUnavailableInstructionAlert()
|
||||
}
|
||||
}
|
||||
|
@ -167,21 +167,21 @@ class MainActivity: FragmentActivity(), LifecycleEventObserver {
|
|||
when (laResult) {
|
||||
LAResult.Success -> {
|
||||
m.performLA.value = false
|
||||
m.controller.setPerformLA(false)
|
||||
m.controller.prefPerformLA.set(false)
|
||||
}
|
||||
is LAResult.Error -> {
|
||||
m.performLA.value = true
|
||||
m.controller.setPerformLA(true)
|
||||
m.controller.prefPerformLA.set(true)
|
||||
laErrorToast(applicationContext, laResult.errString)
|
||||
}
|
||||
LAResult.Failed -> {
|
||||
m.performLA.value = true
|
||||
m.controller.setPerformLA(true)
|
||||
m.controller.prefPerformLA.set(true)
|
||||
laFailedToast(applicationContext)
|
||||
}
|
||||
LAResult.Unavailable -> {
|
||||
m.performLA.value = false
|
||||
m.controller.setPerformLA(false)
|
||||
m.controller.prefPerformLA.set(false)
|
||||
laUnavailableTurningOffAlert()
|
||||
}
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ fun MainPage(
|
|||
var showAdvertiseLAAlert by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(showAdvertiseLAAlert) {
|
||||
if (
|
||||
!chatModel.controller.getLANoticeShown()
|
||||
!chatModel.controller.prefLANoticeShown.get()
|
||||
&& showAdvertiseLAAlert
|
||||
&& chatModel.onboardingStage.value == OnboardingStage.OnboardingComplete
|
||||
&& chatModel.chats.isNotEmpty()
|
||||
|
|
|
@ -61,7 +61,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
|||
withApi {
|
||||
when (event) {
|
||||
Lifecycle.Event.ON_STOP ->
|
||||
if (!chatController.getRunServiceInBackground()) SimplexService.stop(applicationContext)
|
||||
if (!chatController.prefRunServiceInBackground.get()) SimplexService.stop(applicationContext)
|
||||
Lifecycle.Event.ON_START ->
|
||||
SimplexService.start(applicationContext)
|
||||
Lifecycle.Event.ON_RESUME ->
|
||||
|
|
|
@ -50,9 +50,18 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
|
|||
var chatModel = ChatModel(this)
|
||||
private val sharedPreferences: SharedPreferences = appContext.getSharedPreferences(SHARED_PREFS_ID, Context.MODE_PRIVATE)
|
||||
|
||||
val prefRunServiceInBackground = mkBoolPreference(SHARED_PREFS_RUN_SERVICE_IN_BACKGROUND, true)
|
||||
val prefBackgroundServiceNoticeShown = mkBoolPreference(SHARED_PREFS_SERVICE_NOTICE_SHOWN, false)
|
||||
private val prefBackgroundServiceBatteryNoticeShown = mkBoolPreference(SHARED_PREFS_SERVICE_BATTERY_NOTICE_SHOWN, false)
|
||||
val prefAutoRestartWorkerVersion = mkIntPreference(SHARED_PREFS_AUTO_RESTART_WORKER_VERSION, 0)
|
||||
val prefWebrtcPolicyRelay = mkBoolPreference(SHARED_PREFS_WEBRTC_POLICY_RELAY, true)
|
||||
val prefAcceptCallsFromLockScreen = mkBoolPreference(SHARED_PREFS_WEBRTC_ACCEPT_CALLS_FROM_LOCK_SCREEN, false)
|
||||
val prefPerformLA = mkBoolPreference(SHARED_PREFS_PERFORM_LA, false)
|
||||
val prefLANoticeShown = mkBoolPreference(SHARED_PREFS_LA_NOTICE_SHOWN, false)
|
||||
|
||||
init {
|
||||
chatModel.runServiceInBackground.value = getRunServiceInBackground()
|
||||
chatModel.performLA.value = getPerformLA()
|
||||
chatModel.runServiceInBackground.value = prefRunServiceInBackground.get()
|
||||
chatModel.performLA.value = prefPerformLA.get()
|
||||
}
|
||||
|
||||
suspend fun startChat(user: User) {
|
||||
|
@ -529,7 +538,7 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
|
|||
if (invitation != null) {
|
||||
chatModel.callManager.reportCallRemoteEnded(invitation = invitation)
|
||||
}
|
||||
withCall(r, r.contact) { call ->
|
||||
withCall(r, r.contact) { _ ->
|
||||
chatModel.callCommand.value = WCallCommand.End
|
||||
withApi {
|
||||
chatModel.activeCall.value = null
|
||||
|
@ -583,7 +592,7 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
|
|||
|
||||
fun showBackgroundServiceNoticeIfNeeded() {
|
||||
Log.d(TAG, "showBackgroundServiceNoticeIfNeeded")
|
||||
if (!getBackgroundServiceNoticeShown()) {
|
||||
if (!prefBackgroundServiceNoticeShown.get()) {
|
||||
// the branch for the new users who has never seen service notice
|
||||
if (isIgnoringBatteryOptimizations(appContext)) {
|
||||
showBGServiceNotice()
|
||||
|
@ -591,20 +600,20 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
|
|||
showBGServiceNoticeIgnoreOptimization()
|
||||
}
|
||||
// set both flags, so that if the user doesn't allow ignoring optimizations, the service will be disabled without additional notice
|
||||
setBackgroundServiceNoticeShown(true)
|
||||
setBackgroundServiceBatteryNoticeShown(true)
|
||||
} else if (!isIgnoringBatteryOptimizations(appContext) && getRunServiceInBackground()) {
|
||||
prefBackgroundServiceNoticeShown.set(true)
|
||||
prefBackgroundServiceBatteryNoticeShown.set(true)
|
||||
} else if (!isIgnoringBatteryOptimizations(appContext) && prefRunServiceInBackground.get()) {
|
||||
// the branch for users who have app installed, and have seen the service notice,
|
||||
// but the battery optimization for the app is on (Android 12) AND the service is running
|
||||
if (getBackgroundServiceBatteryNoticeShown()) {
|
||||
if (prefBackgroundServiceBatteryNoticeShown.get()) {
|
||||
// users have been presented with battery notice before - they did not allow ignoring optimizitions -> disable service
|
||||
showDisablingServiceNotice()
|
||||
setRunServiceInBackground(false)
|
||||
prefRunServiceInBackground.set(false)
|
||||
chatModel.runServiceInBackground.value = false
|
||||
} else {
|
||||
// show battery optimization notice
|
||||
showBGServiceNoticeIgnoreOptimization()
|
||||
setBackgroundServiceBatteryNoticeShown(true)
|
||||
prefBackgroundServiceBatteryNoticeShown.set(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -695,8 +704,8 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
|
|||
|
||||
fun showLANotice(activity: FragmentActivity) {
|
||||
Log.d(TAG, "showLANotice")
|
||||
if (!getLANoticeShown()) {
|
||||
setLANoticeShown(true)
|
||||
if (!prefLANoticeShown.get()) {
|
||||
prefLANoticeShown.set(true)
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = generalGetString(R.string.la_notice_title),
|
||||
text = generalGetString(R.string.la_notice_text),
|
||||
|
@ -710,22 +719,22 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
|
|||
when (laResult) {
|
||||
LAResult.Success -> {
|
||||
chatModel.performLA.value = true
|
||||
setPerformLA(true)
|
||||
prefPerformLA.set(true)
|
||||
laTurnedOnAlert()
|
||||
}
|
||||
is LAResult.Error -> {
|
||||
chatModel.performLA.value = false
|
||||
setPerformLA(false)
|
||||
prefPerformLA.set(false)
|
||||
laErrorToast(appContext, laResult.errString)
|
||||
}
|
||||
LAResult.Failed -> {
|
||||
chatModel.performLA.value = false
|
||||
setPerformLA(false)
|
||||
prefPerformLA.set(false)
|
||||
laFailedToast(appContext)
|
||||
}
|
||||
LAResult.Unavailable -> {
|
||||
chatModel.performLA.value = false
|
||||
setPerformLA(false)
|
||||
prefPerformLA.set(false)
|
||||
chatModel.showAdvertiseLAUnavailableAlert.value = true
|
||||
}
|
||||
}
|
||||
|
@ -736,34 +745,6 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
|
|||
}
|
||||
}
|
||||
|
||||
fun getAutoRestartWorkerVersion(): Int = sharedPreferences.getInt(SHARED_PREFS_AUTO_RESTART_WORKER_VERSION, 0)
|
||||
|
||||
fun setAutoRestartWorkerVersion(version: Int) =
|
||||
sharedPreferences.edit()
|
||||
.putInt(SHARED_PREFS_AUTO_RESTART_WORKER_VERSION, version)
|
||||
.apply()
|
||||
|
||||
fun getRunServiceInBackground(): Boolean = sharedPreferences.getBoolean(SHARED_PREFS_RUN_SERVICE_IN_BACKGROUND, true)
|
||||
|
||||
fun setRunServiceInBackground(runService: Boolean) =
|
||||
sharedPreferences.edit()
|
||||
.putBoolean(SHARED_PREFS_RUN_SERVICE_IN_BACKGROUND, runService)
|
||||
.apply()
|
||||
|
||||
private fun getBackgroundServiceNoticeShown(): Boolean = sharedPreferences.getBoolean(SHARED_PREFS_SERVICE_NOTICE_SHOWN, false)
|
||||
|
||||
fun setBackgroundServiceNoticeShown(shown: Boolean) =
|
||||
sharedPreferences.edit()
|
||||
.putBoolean(SHARED_PREFS_SERVICE_NOTICE_SHOWN, shown)
|
||||
.apply()
|
||||
|
||||
private fun getBackgroundServiceBatteryNoticeShown(): Boolean = sharedPreferences.getBoolean(SHARED_PREFS_SERVICE_BATTERY_NOTICE_SHOWN, false)
|
||||
|
||||
fun setBackgroundServiceBatteryNoticeShown(shown: Boolean) =
|
||||
sharedPreferences.edit()
|
||||
.putBoolean(SHARED_PREFS_SERVICE_BATTERY_NOTICE_SHOWN, shown)
|
||||
.apply()
|
||||
|
||||
fun isIgnoringBatteryOptimizations(context: Context): Boolean {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return true
|
||||
val powerManager = context.getSystemService(Application.POWER_SERVICE) as PowerManager
|
||||
|
@ -783,19 +764,17 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
|
|||
}
|
||||
}
|
||||
|
||||
fun getPerformLA(): Boolean = sharedPreferences.getBoolean(SHARED_PREFS_PERFORM_LA, false)
|
||||
private fun mkIntPreference(prefName: String, default: Int) =
|
||||
Preference(
|
||||
get = fun() = sharedPreferences.getInt(prefName, default),
|
||||
set = fun(value) = sharedPreferences.edit().putInt(prefName, value).apply()
|
||||
)
|
||||
|
||||
fun setPerformLA(performLA: Boolean) =
|
||||
sharedPreferences.edit()
|
||||
.putBoolean(SHARED_PREFS_PERFORM_LA, performLA)
|
||||
.apply()
|
||||
|
||||
fun getLANoticeShown(): Boolean = sharedPreferences.getBoolean(SHARED_PREFS_LA_NOTICE_SHOWN, false)
|
||||
|
||||
fun setLANoticeShown(shown: Boolean) =
|
||||
sharedPreferences.edit()
|
||||
.putBoolean(SHARED_PREFS_LA_NOTICE_SHOWN, shown)
|
||||
.apply()
|
||||
private fun mkBoolPreference(prefName: String, default: Boolean) =
|
||||
Preference(
|
||||
get = fun() = sharedPreferences.getBoolean(prefName, default),
|
||||
set = fun(value) = sharedPreferences.edit().putBoolean(prefName, value).apply()
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val SHARED_PREFS_ID = "chat.simplex.app.SIMPLEX_APP_PREFS"
|
||||
|
@ -803,11 +782,15 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
|
|||
private const val SHARED_PREFS_RUN_SERVICE_IN_BACKGROUND = "RunServiceInBackground"
|
||||
private const val SHARED_PREFS_SERVICE_NOTICE_SHOWN = "BackgroundServiceNoticeShown"
|
||||
private const val SHARED_PREFS_SERVICE_BATTERY_NOTICE_SHOWN = "BackgroundServiceBatteryNoticeShown"
|
||||
private const val SHARED_PREFS_WEBRTC_POLICY_RELAY = "WebrtcPolicyRelay"
|
||||
private const val SHARED_PREFS_WEBRTC_ACCEPT_CALLS_FROM_LOCK_SCREEN = "AcceptCallsFromLockScreen"
|
||||
private const val SHARED_PREFS_PERFORM_LA = "PerformLA"
|
||||
private const val SHARED_PREFS_LA_NOTICE_SHOWN = "LANoticeShown"
|
||||
}
|
||||
}
|
||||
|
||||
class Preference<T>(val get: () -> T, val set: (T) -> Unit)
|
||||
|
||||
// ChatCommand
|
||||
sealed class CC {
|
||||
class Console(val cmd: String): CC()
|
||||
|
|
|
@ -156,7 +156,7 @@ private fun ActiveCallOverlayLayout(
|
|||
ControlButton(call, Icons.Filled.FlipCameraAndroid, R.string.icon_descr_flip_camera, flipCamera)
|
||||
ControlButton(call, Icons.Filled.Videocam, R.string.icon_descr_video_off, toggleVideo)
|
||||
} else {
|
||||
Spacer(Modifier.size(40.dp))
|
||||
Spacer(Modifier.size(48.dp))
|
||||
ControlButton(call, Icons.Outlined.VideocamOff, R.string.icon_descr_video_on, toggleVideo)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import androidx.activity.ComponentActivity
|
|||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
|
@ -25,14 +24,15 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.*
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.model.Contact
|
||||
import chat.simplex.app.model.NtfManager.Companion.OpenChatAction
|
||||
import chat.simplex.app.ui.theme.*
|
||||
import chat.simplex.app.views.helpers.ProfileImage
|
||||
import chat.simplex.app.views.onboarding.SimpleXLogo
|
||||
|
@ -93,7 +93,7 @@ fun IncomingCallActivityView(m: ChatModel, activity: IncomingCallActivity) {
|
|||
activity.finish()
|
||||
}
|
||||
}
|
||||
SimpleXTheme(darkTheme = true) {
|
||||
SimpleXTheme {
|
||||
Surface(
|
||||
Modifier
|
||||
.background(MaterialTheme.colors.background)
|
||||
|
@ -104,33 +104,48 @@ fun IncomingCallActivityView(m: ChatModel, activity: IncomingCallActivity) {
|
|||
if (invitation != null) IncomingCallAlertView(invitation, m)
|
||||
}
|
||||
} else if (invitation != null) {
|
||||
IncomingCallLockScreenAlert(invitation, m)
|
||||
IncomingCallLockScreenAlert(invitation, m, activity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun IncomingCallLockScreenAlert(invitation: CallInvitation, chatModel: ChatModel) {
|
||||
fun IncomingCallLockScreenAlert(invitation: CallInvitation, chatModel: ChatModel, activity: IncomingCallActivity) {
|
||||
val cm = chatModel.callManager
|
||||
val cxt = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
var acceptCallsFromLockScreen by remember { mutableStateOf(chatModel.controller.prefAcceptCallsFromLockScreen.get()) }
|
||||
LaunchedEffect(true) { SoundPlayer.shared.start(cxt, scope, sound = true) }
|
||||
DisposableEffect(true) { onDispose { SoundPlayer.shared.stop() } }
|
||||
IncomingCallLockScreenAlertLayout(
|
||||
invitation,
|
||||
acceptCallsFromLockScreen,
|
||||
rejectCall = { cm.endCall(invitation = invitation) },
|
||||
ignoreCall = { chatModel.activeCallInvitation.value = null },
|
||||
acceptCall = { cm.acceptIncomingCall(invitation = invitation) }
|
||||
acceptCall = { cm.acceptIncomingCall(invitation = invitation) },
|
||||
openApp = {
|
||||
SoundPlayer.shared.stop()
|
||||
var intent = Intent(activity, MainActivity::class.java)
|
||||
.setAction(OpenChatAction)
|
||||
.putExtra("chatId", invitation.contact.id)
|
||||
activity.startActivity(intent)
|
||||
activity.finish()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
getKeyguardManager(activity).requestDismissKeyguard(activity, null)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun IncomingCallLockScreenAlertLayout(
|
||||
invitation: CallInvitation,
|
||||
acceptCallsFromLockScreen: Boolean,
|
||||
rejectCall: () -> Unit,
|
||||
ignoreCall: () -> Unit,
|
||||
acceptCall: () -> Unit
|
||||
acceptCall: () -> Unit,
|
||||
openApp: () -> Unit
|
||||
) {
|
||||
Column(
|
||||
Modifier
|
||||
|
@ -139,22 +154,24 @@ fun IncomingCallLockScreenAlertLayout(
|
|||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
IncomingCallInfo(invitation)
|
||||
Spacer(
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(1f))
|
||||
ProfileImage(size = 192.dp, image = invitation.contact.profile.image)
|
||||
Text(invitation.contact.chatViewName, style = MaterialTheme.typography.h2)
|
||||
Spacer(
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(1f))
|
||||
Row {
|
||||
LockScreenCallButton(stringResource(R.string.reject), Icons.Filled.CallEnd, Color.Red, rejectCall)
|
||||
Spacer(Modifier.size(48.dp))
|
||||
LockScreenCallButton(stringResource(R.string.ignore), Icons.Filled.Close, MaterialTheme.colors.primary, ignoreCall)
|
||||
Spacer(Modifier.size(48.dp))
|
||||
LockScreenCallButton(stringResource(R.string.accept), Icons.Filled.Check, SimplexGreen, acceptCall)
|
||||
Spacer(Modifier.fillMaxHeight().weight(1f))
|
||||
if (acceptCallsFromLockScreen) {
|
||||
ProfileImage(size = 192.dp, image = invitation.contact.profile.image)
|
||||
Text(invitation.contact.chatViewName, style = MaterialTheme.typography.h2)
|
||||
Spacer(Modifier.fillMaxHeight().weight(1f))
|
||||
Row {
|
||||
LockScreenCallButton(stringResource(R.string.reject), Icons.Filled.CallEnd, Color.Red, rejectCall)
|
||||
Spacer(Modifier.size(48.dp))
|
||||
LockScreenCallButton(stringResource(R.string.ignore), Icons.Filled.Close, MaterialTheme.colors.primary, ignoreCall)
|
||||
Spacer(Modifier.size(48.dp))
|
||||
LockScreenCallButton(stringResource(R.string.accept), Icons.Filled.Check, SimplexGreen, acceptCall)
|
||||
}
|
||||
} else {
|
||||
SimpleXLogo()
|
||||
Text(stringResource(R.string.open_simplex_chat_to_accept_call), textAlign = TextAlign.Center, lineHeight = 22.sp)
|
||||
Text(stringResource(R.string.allow_accepting_calls_from_lock_screen), textAlign = TextAlign.Center, style = MaterialTheme.typography.body2, lineHeight = 22.sp)
|
||||
Spacer(Modifier.fillMaxHeight().weight(1f))
|
||||
SimpleButton(text = stringResource(R.string.open_verb), icon = Icons.Filled.Check, click = openApp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -166,7 +183,9 @@ private fun LockScreenCallButton(text: String, icon: ImageVector, color: Color,
|
|||
color = Color.Transparent
|
||||
) {
|
||||
Column(
|
||||
Modifier.defaultMinSize(minWidth = 50.dp).padding(4.dp),
|
||||
Modifier
|
||||
.defaultMinSize(minWidth = 50.dp)
|
||||
.padding(4.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
IconButton(action) {
|
||||
|
@ -185,16 +204,21 @@ private fun LockScreenCallButton(text: String, icon: ImageVector, color: Color,
|
|||
@Composable
|
||||
fun PreviewIncomingCallLockScreenAlert() {
|
||||
SimpleXTheme(true) {
|
||||
Surface(Modifier.background(MaterialTheme.colors.background).fillMaxSize()) {
|
||||
Surface(
|
||||
Modifier
|
||||
.background(MaterialTheme.colors.background)
|
||||
.fillMaxSize()) {
|
||||
IncomingCallLockScreenAlertLayout(
|
||||
invitation = CallInvitation(
|
||||
contact = Contact.sampleData,
|
||||
peerMedia = CallMediaType.Audio,
|
||||
sharedKey = null
|
||||
),
|
||||
acceptCallsFromLockScreen = false,
|
||||
rejectCall = {},
|
||||
ignoreCall = {},
|
||||
acceptCall = {}
|
||||
acceptCall = {},
|
||||
openApp = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package chat.simplex.app.views.usersettings
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
|
||||
@Composable
|
||||
fun CallSettingsView(m: ChatModel) {
|
||||
CallSettingsLayout(
|
||||
webrtcPolicyRelay = m.controller.prefWebrtcPolicyRelay,
|
||||
acceptCallsFromLockScreen = m.controller.prefAcceptCallsFromLockScreen
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CallSettingsLayout(
|
||||
webrtcPolicyRelay: Preference<Boolean>,
|
||||
acceptCallsFromLockScreen: Preference<Boolean>,
|
||||
) {
|
||||
Column(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.call_settings),
|
||||
Modifier.padding(bottom = 24.dp),
|
||||
style = MaterialTheme.typography.h1
|
||||
)
|
||||
SharedPreferenceToggle(stringResource(R.string.connect_calls_via_relay), webrtcPolicyRelay)
|
||||
SharedPreferenceToggle(stringResource(R.string.accept_calls_from_lock_screen), acceptCallsFromLockScreen)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SharedPreferenceToggle(
|
||||
text: String,
|
||||
preference: Preference<Boolean>
|
||||
) {
|
||||
var preferenceState by remember { mutableStateOf(preference.get()) }
|
||||
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(text, Modifier.padding(end = 24.dp))
|
||||
Spacer(Modifier.fillMaxWidth().weight(1f))
|
||||
Switch(
|
||||
checked = preferenceState,
|
||||
onCheckedChange = {
|
||||
preference.set(it)
|
||||
preferenceState = it
|
||||
},
|
||||
colors = SwitchDefaults.colors(
|
||||
checkedThumbColor = MaterialTheme.colors.primary,
|
||||
uncheckedThumbColor = HighOrLowlight
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
package chat.simplex.app.views.usersettings
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.scrollable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
|
@ -12,6 +13,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.platform.UriHandler
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
|
@ -32,9 +34,9 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit) {
|
|||
val user = chatModel.currentUser.value
|
||||
|
||||
fun setRunServiceInBackground(on: Boolean) {
|
||||
chatModel.controller.setRunServiceInBackground(on)
|
||||
chatModel.controller.prefRunServiceInBackground.set(on)
|
||||
if (on && !chatModel.controller.isIgnoringBatteryOptimizations(chatModel.controller.appContext)) {
|
||||
chatModel.controller.setBackgroundServiceNoticeShown(false)
|
||||
chatModel.controller.prefBackgroundServiceNoticeShown.set(false)
|
||||
}
|
||||
chatModel.controller.showBackgroundServiceNoticeIfNeeded()
|
||||
chatModel.runServiceInBackground.value = on
|
||||
|
@ -75,6 +77,7 @@ fun SettingsLayout(
|
|||
Modifier
|
||||
.background(MaterialTheme.colors.background)
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Column(
|
||||
Modifier
|
||||
|
@ -83,6 +86,7 @@ fun SettingsLayout(
|
|||
.padding(8.dp)
|
||||
.padding(top = 16.dp)
|
||||
) {
|
||||
@Composable fun divider() = Divider(Modifier.padding(horizontal = 8.dp))
|
||||
Text(
|
||||
stringResource(R.string.your_settings),
|
||||
style = MaterialTheme.typography.h1,
|
||||
|
@ -93,150 +97,219 @@ fun SettingsLayout(
|
|||
SettingsSectionView(showCustomModal { chatModel, close -> UserProfileView(chatModel, close) }, 80.dp) {
|
||||
ProfilePreview(profile)
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
SettingsSectionView(showModal { UserAddressView(it) }) {
|
||||
Icon(
|
||||
Icons.Outlined.QrCode,
|
||||
contentDescription = stringResource(R.string.icon_descr_address),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(stringResource(R.string.your_simplex_contact_address))
|
||||
}
|
||||
divider()
|
||||
UserAddressSection(showModal)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
SettingsSectionView(showModal { HelpView(it) }) {
|
||||
Icon(
|
||||
Icons.Outlined.HelpOutline,
|
||||
contentDescription = stringResource(R.string.icon_descr_help),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(stringResource(R.string.how_to_use_simplex_chat))
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
SettingsSectionView(showModal { SimpleXInfo(it, onboarding = false) }) {
|
||||
Icon(
|
||||
Icons.Outlined.Info,
|
||||
contentDescription = stringResource(R.string.icon_descr_help),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(stringResource(R.string.about_simplex_chat))
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
SettingsSectionView(showModal { MarkdownHelpView() }) {
|
||||
Icon(
|
||||
Icons.Outlined.TextFormat,
|
||||
contentDescription = stringResource(R.string.markdown_help),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(stringResource(R.string.markdown_in_messages))
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
SettingsSectionView({ uriHandler.openUri(simplexTeamUri) }) {
|
||||
Icon(
|
||||
Icons.Outlined.Tag,
|
||||
contentDescription = stringResource(R.string.icon_descr_simplex_team),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(
|
||||
stringResource(R.string.chat_with_the_founder),
|
||||
color = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
SettingsSectionView({ uriHandler.openUri("mailto:chat@simplex.chat") }) {
|
||||
Icon(
|
||||
Icons.Outlined.Email,
|
||||
contentDescription = stringResource(R.string.icon_descr_email),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(
|
||||
stringResource(R.string.send_us_an_email),
|
||||
color = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
CallSettingsSection(showModal)
|
||||
divider()
|
||||
ChatLockSection(performLA, setPerformLA)
|
||||
divider()
|
||||
PrivateNotificationsSection(runServiceInBackground, setRunServiceInBackground)
|
||||
divider()
|
||||
SMPServersSection(showModal)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
SettingsSectionView(showModal { SMPServersView(it) }) {
|
||||
Icon(
|
||||
Icons.Outlined.Dns,
|
||||
contentDescription = stringResource(R.string.smp_servers),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(stringResource(R.string.smp_servers))
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
SettingsSectionView() {
|
||||
Icon(
|
||||
Icons.Outlined.Bolt,
|
||||
contentDescription = stringResource(R.string.private_notifications),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(
|
||||
stringResource(R.string.private_notifications), Modifier
|
||||
.padding(end = 24.dp)
|
||||
.fillMaxWidth()
|
||||
.weight(1F)
|
||||
)
|
||||
Switch(
|
||||
checked = runServiceInBackground.value,
|
||||
onCheckedChange = { setRunServiceInBackground(it) },
|
||||
colors = SwitchDefaults.colors(
|
||||
checkedThumbColor = MaterialTheme.colors.primary,
|
||||
uncheckedThumbColor = HighOrLowlight
|
||||
),
|
||||
modifier = Modifier.padding(end = 8.dp)
|
||||
)
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
SettingsSectionView() {
|
||||
Icon(
|
||||
Icons.Outlined.Lock,
|
||||
contentDescription = stringResource(R.string.chat_lock),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(
|
||||
stringResource(R.string.chat_lock), Modifier
|
||||
.padding(end = 24.dp)
|
||||
.fillMaxWidth()
|
||||
.weight(1F)
|
||||
)
|
||||
Switch(
|
||||
checked = performLA.value,
|
||||
onCheckedChange = { setPerformLA(it) },
|
||||
colors = SwitchDefaults.colors(
|
||||
checkedThumbColor = MaterialTheme.colors.primary,
|
||||
uncheckedThumbColor = HighOrLowlight
|
||||
),
|
||||
modifier = Modifier.padding(end = 8.dp)
|
||||
)
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
SettingsSectionView(showTerminal) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_outline_terminal),
|
||||
contentDescription = stringResource(R.string.chat_console),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(stringResource(R.string.chat_console))
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
SettingsSectionView({ uriHandler.openUri("https://github.com/simplex-chat/simplex-chat") }) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_github),
|
||||
contentDescription = "GitHub",
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(annotatedStringResource(R.string.install_simplex_chat_for_terminal))
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
// SettingsSectionView(showVideoChatPrototype) {
|
||||
SettingsSectionView() {
|
||||
Text("v${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
|
||||
}
|
||||
HelpViewSection(showModal)
|
||||
divider()
|
||||
SimpleXInfoSection(showModal)
|
||||
divider()
|
||||
MarkdownHelpSection(showModal)
|
||||
divider()
|
||||
ConnectToDevelopersSection(uriHandler)
|
||||
divider()
|
||||
SendEmailSection(uriHandler)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
ChatConsoleSection(showTerminal)
|
||||
divider()
|
||||
InstallTerminalAppSection(uriHandler)
|
||||
divider()
|
||||
AppVersionSection()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun UserAddressSection(showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit)) {
|
||||
SettingsSectionView(showModal { UserAddressView(it) }) {
|
||||
Icon(
|
||||
Icons.Outlined.QrCode,
|
||||
contentDescription = stringResource(R.string.icon_descr_address),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(stringResource(R.string.your_simplex_contact_address))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun CallSettingsSection(showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit)) {
|
||||
SettingsSectionView(showModal { CallSettingsView(it) }) {
|
||||
Icon(
|
||||
Icons.Outlined.Videocam,
|
||||
contentDescription = stringResource(R.string.call_settings),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(stringResource(R.string.call_settings))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun HelpViewSection(showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit)) {
|
||||
SettingsSectionView(showModal { HelpView(it) }) {
|
||||
Icon(
|
||||
Icons.Outlined.HelpOutline,
|
||||
contentDescription = stringResource(R.string.icon_descr_help),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(stringResource(R.string.how_to_use_simplex_chat))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun SimpleXInfoSection(showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit)) {
|
||||
SettingsSectionView(showModal { SimpleXInfo(it, onboarding = false) }) {
|
||||
Icon(
|
||||
Icons.Outlined.Info,
|
||||
contentDescription = stringResource(R.string.icon_descr_help),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(stringResource(R.string.about_simplex_chat))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun MarkdownHelpSection(showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit)) {
|
||||
SettingsSectionView(showModal { MarkdownHelpView() }) {
|
||||
Icon(
|
||||
Icons.Outlined.TextFormat,
|
||||
contentDescription = stringResource(R.string.markdown_help),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(stringResource(R.string.markdown_in_messages))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun ConnectToDevelopersSection(uriHandler: UriHandler) {
|
||||
SettingsSectionView({ uriHandler.openUri(simplexTeamUri) }) {
|
||||
Icon(
|
||||
Icons.Outlined.Tag,
|
||||
contentDescription = stringResource(R.string.icon_descr_simplex_team),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(
|
||||
stringResource(R.string.chat_with_the_founder),
|
||||
color = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun SendEmailSection(uriHandler: UriHandler) {
|
||||
SettingsSectionView({ uriHandler.openUri("mailto:chat@simplex.chat") }) {
|
||||
Icon(
|
||||
Icons.Outlined.Email,
|
||||
contentDescription = stringResource(R.string.icon_descr_email),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(
|
||||
stringResource(R.string.send_us_an_email),
|
||||
color = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun SMPServersSection(showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit)) {
|
||||
SettingsSectionView(showModal { SMPServersView(it) }) {
|
||||
Icon(
|
||||
Icons.Outlined.Dns,
|
||||
contentDescription = stringResource(R.string.smp_servers),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(stringResource(R.string.smp_servers))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun PrivateNotificationsSection(
|
||||
runServiceInBackground: MutableState<Boolean>,
|
||||
setRunServiceInBackground: (Boolean) -> Unit
|
||||
) {
|
||||
SettingsSectionView() {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
Icons.Outlined.Bolt,
|
||||
contentDescription = stringResource(R.string.private_notifications),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(
|
||||
stringResource(R.string.private_notifications),
|
||||
Modifier
|
||||
.padding(end = 24.dp)
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
)
|
||||
Switch(
|
||||
checked = runServiceInBackground.value,
|
||||
onCheckedChange = { setRunServiceInBackground(it) },
|
||||
colors = SwitchDefaults.colors(
|
||||
checkedThumbColor = MaterialTheme.colors.primary,
|
||||
uncheckedThumbColor = HighOrLowlight
|
||||
),
|
||||
modifier = Modifier.padding(end = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun ChatLockSection(performLA: MutableState<Boolean>, setPerformLA: (Boolean) -> Unit) {
|
||||
SettingsSectionView() {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
Icons.Outlined.Lock,
|
||||
contentDescription = stringResource(R.string.chat_lock),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(
|
||||
stringResource(R.string.chat_lock), Modifier
|
||||
.padding(end = 24.dp)
|
||||
.fillMaxWidth()
|
||||
.weight(1F)
|
||||
)
|
||||
Switch(
|
||||
checked = performLA.value,
|
||||
onCheckedChange = { setPerformLA(it) },
|
||||
colors = SwitchDefaults.colors(
|
||||
checkedThumbColor = MaterialTheme.colors.primary,
|
||||
uncheckedThumbColor = HighOrLowlight
|
||||
),
|
||||
modifier = Modifier.padding(end = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun ChatConsoleSection(showTerminal: () -> Unit) {
|
||||
SettingsSectionView(showTerminal) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_outline_terminal),
|
||||
contentDescription = stringResource(R.string.chat_console),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(stringResource(R.string.chat_console))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun InstallTerminalAppSection(uriHandler: UriHandler) {
|
||||
SettingsSectionView({ uriHandler.openUri("https://github.com/simplex-chat/simplex-chat") }) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_github),
|
||||
contentDescription = "GitHub",
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(annotatedStringResource(R.string.install_simplex_chat_for_terminal))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun AppVersionSection() {
|
||||
SettingsSectionView() {
|
||||
Text("v${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
|
||||
}
|
||||
}
|
||||
|
||||
@Composable fun ProfilePreview(profileOf: NamedChat, size: Dp = 60.dp, color: Color = MaterialTheme.colors.secondary) {
|
||||
ProfileImage(size = size, image = profileOf.image, color = color)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
|
|
|
@ -377,6 +377,16 @@
|
|||
<string name="icon_descr_video_call">видеозвонок</string>
|
||||
<string name="icon_descr_audio_call">аудиозвонок</string>
|
||||
|
||||
<!-- Call settings -->
|
||||
<string name="call_settings">Настройки звонков</string>
|
||||
<string name="connect_calls_via_relay">Соединяться через сервер (relay)</string>
|
||||
<string name="accept_calls_from_lock_screen">Принимать с экрана блокировки</string>
|
||||
|
||||
<!-- Call Lock Screen -->
|
||||
<string name="open_simplex_chat_to_accept_call">Open <xliff:g id="appNameFull">SimpleX Chat</xliff:g>\nto accept call</string>
|
||||
<string name="allow_accepting_calls_from_lock_screen">You can allow accepting calls from lock screen via Settings.</string>
|
||||
<string name="open_verb">Open</string>
|
||||
|
||||
<!-- Call overlay -->
|
||||
<string name="status_e2e_encrypted">e2e зашифровано</string>
|
||||
<string name="status_no_e2e_encryption">нет e2e шифрования</string>
|
||||
|
|
|
@ -379,6 +379,16 @@
|
|||
<string name="icon_descr_video_call">video call</string>
|
||||
<string name="icon_descr_audio_call">audio call</string>
|
||||
|
||||
<!-- Call settings -->
|
||||
<string name="call_settings">Call settings</string>
|
||||
<string name="connect_calls_via_relay">Connect via relay</string>
|
||||
<string name="accept_calls_from_lock_screen">Accept calls from lock screen</string>
|
||||
|
||||
<!-- Call Lock Screen -->
|
||||
<string name="open_simplex_chat_to_accept_call">Open <xliff:g id="appNameFull">SimpleX Chat</xliff:g> to accept call</string>
|
||||
<string name="allow_accepting_calls_from_lock_screen">Enable calls from lock screen via Settings.</string>
|
||||
<string name="open_verb">Open</string>
|
||||
|
||||
<!-- Call overlay -->
|
||||
<string name="status_e2e_encrypted">e2e encrypted</string>
|
||||
<string name="status_no_e2e_encryption">no e2e encryption</string>
|
||||
|
|
|
@ -660,7 +660,9 @@ func processReceivedMsg(_ res: ChatResponse) {
|
|||
call.callState = .offerReceived
|
||||
call.peerMedia = callType.media
|
||||
call.sharedKey = sharedKey
|
||||
m.callCommand = .offer(offer: offer.rtcSession, iceCandidates: offer.rtcIceCandidates, media: callType.media, aesKey: sharedKey, useWorker: true)
|
||||
let useRelay = UserDefaults.standard.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY)
|
||||
logger.debug(".callOffer useRelay \(useRelay)")
|
||||
m.callCommand = .offer(offer: offer.rtcSession, iceCandidates: offer.rtcIceCandidates, media: callType.media, aesKey: sharedKey, useWorker: true, relay: useRelay)
|
||||
}
|
||||
case let .callAnswer(contact, answer):
|
||||
withCall(contact) { call in
|
||||
|
|
|
@ -21,6 +21,7 @@ struct SimpleXApp: App {
|
|||
|
||||
init() {
|
||||
hs_init(0, nil)
|
||||
UserDefaults.standard.register(defaults: appDefaults)
|
||||
BGManager.shared.register()
|
||||
NtfManager.shared.registerCategories()
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class CallManager {
|
|||
let m = ChatModel.shared
|
||||
if let call = m.activeCall, call.callkitUUID == callUUID {
|
||||
m.showCallView = true
|
||||
m.callCommand = .capabilities(useWorker: true)
|
||||
m.callCommand = .capabilities(media: call.localMedia, useWorker: true)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -45,7 +45,9 @@ class CallManager {
|
|||
sharedKey: invitation.sharedKey
|
||||
)
|
||||
m.showCallView = true
|
||||
m.callCommand = .start(media: invitation.peerMedia, aesKey: invitation.sharedKey, useWorker: true)
|
||||
let useRelay = UserDefaults.standard.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY)
|
||||
logger.debug("answerIncomingCall useRelay \(useRelay)")
|
||||
m.callCommand = .start(media: invitation.peerMedia, aesKey: invitation.sharedKey, useWorker: true, relay: useRelay)
|
||||
}
|
||||
|
||||
func endCall(callUUID: UUID, completed: @escaping (Bool) -> Void) {
|
||||
|
|
|
@ -102,7 +102,7 @@ struct WVAPIMessage: Equatable, Decodable, Encodable {
|
|||
}
|
||||
|
||||
enum WCallCommand: Equatable, Encodable, Decodable {
|
||||
case capabilities(useWorker: Bool? = nil)
|
||||
case capabilities(media: CallMediaType, useWorker: Bool? = nil)
|
||||
case start(media: CallMediaType, aesKey: String? = nil, useWorker: Bool? = nil, iceServers: [RTCIceServer]? = nil, relay: Bool? = nil)
|
||||
case offer(offer: String, iceCandidates: String, media: CallMediaType, aesKey: String? = nil, useWorker: Bool? = nil, iceServers: [RTCIceServer]? = nil, relay: Bool? = nil)
|
||||
case answer(answer: String, iceCandidates: String)
|
||||
|
@ -143,8 +143,9 @@ enum WCallCommand: Equatable, Encodable, Decodable {
|
|||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
switch self {
|
||||
case let .capabilities(useWorker):
|
||||
case let .capabilities(media, useWorker):
|
||||
try container.encode("capabilities", forKey: .type)
|
||||
try container.encode(media, forKey: .media)
|
||||
try container.encode(useWorker, forKey: .useWorker)
|
||||
case let .start(media, aesKey, useWorker, iceServers, relay):
|
||||
try container.encode("start", forKey: .type)
|
||||
|
@ -186,8 +187,9 @@ enum WCallCommand: Equatable, Encodable, Decodable {
|
|||
let type = try container.decode(String.self, forKey: CodingKeys.type)
|
||||
switch type {
|
||||
case "capabilities":
|
||||
let media = try container.decode(CallMediaType.self, forKey: CodingKeys.media)
|
||||
let useWorker = try container.decode((Bool?).self, forKey: CodingKeys.useWorker)
|
||||
self = .capabilities(useWorker: useWorker)
|
||||
self = .capabilities(media: media, useWorker: useWorker)
|
||||
case "start":
|
||||
let media = try container.decode(CallMediaType.self, forKey: CodingKeys.media)
|
||||
let aesKey = try? container.decode(String.self, forKey: CodingKeys.aesKey)
|
||||
|
|
25
apps/ios/Shared/Views/UserSettings/CallSettings.swift
Normal file
25
apps/ios/Shared/Views/UserSettings/CallSettings.swift
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// CallSettings.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Evgeny on 27/05/2022.
|
||||
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct CallSettings: View {
|
||||
@AppStorage(DEFAULT_WEBRTC_POLICY_RELAY) private var webrtcPolicyRelay = true
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Toggle("Connect via relay", isOn: $webrtcPolicyRelay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CallSettings_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
CallSettings()
|
||||
}
|
||||
}
|
|
@ -16,6 +16,13 @@ let appBuild = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as?
|
|||
|
||||
let DEFAULT_USE_NOTIFICATIONS = "useNotifications"
|
||||
let DEFAULT_PENDING_CONNECTIONS = "pendingConnections"
|
||||
let DEFAULT_WEBRTC_POLICY_RELAY = "webrtcPolicyRelay"
|
||||
|
||||
let appDefaults: [String:Any] = [
|
||||
DEFAULT_USE_NOTIFICATIONS: false,
|
||||
DEFAULT_PENDING_CONNECTIONS: true,
|
||||
DEFAULT_WEBRTC_POLICY_RELAY: true
|
||||
]
|
||||
|
||||
private var indent: CGFloat = 36
|
||||
|
||||
|
@ -59,6 +66,12 @@ struct SettingsView: View {
|
|||
} label: {
|
||||
settingsRow("server.rack") { Text("SMP servers") }
|
||||
}
|
||||
NavigationLink {
|
||||
CallSettings()
|
||||
.navigationTitle("Call settings")
|
||||
} label: {
|
||||
settingsRow("video") { Text("Call settings") }
|
||||
}
|
||||
}
|
||||
|
||||
Section("Help") {
|
||||
|
|
|
@ -20,11 +20,6 @@
|
|||
<target> (can be copied)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=" wants to connect with you via " xml:space="preserve">
|
||||
<source> wants to connect with you via </source>
|
||||
<target> wants to connect with you via </target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="!1 colored!" xml:space="preserve">
|
||||
<source>!1 colored!</source>
|
||||
<target>!1 colored!</target>
|
||||
|
@ -133,7 +128,8 @@
|
|||
<trans-unit id="Accept" xml:space="preserve">
|
||||
<source>Accept</source>
|
||||
<target>Accept</target>
|
||||
<note>accept contact request via notification</note>
|
||||
<note>accept contact request via notification
|
||||
accept incoming call via notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept contact" xml:space="preserve">
|
||||
<source>Accept contact</source>
|
||||
|
@ -165,11 +161,6 @@
|
|||
<target>Already connected?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Answer" xml:space="preserve">
|
||||
<source>Answer</source>
|
||||
<target>Answer</target>
|
||||
<note>accept incoming call via notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Answer call" xml:space="preserve">
|
||||
<source>Answer call</source>
|
||||
<target>Answer call</target>
|
||||
|
@ -185,16 +176,16 @@
|
|||
<target>Call already ended!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Call settings" xml:space="preserve">
|
||||
<source>Call settings</source>
|
||||
<target>Call settings</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cancel" xml:space="preserve">
|
||||
<source>Cancel</source>
|
||||
<target>Cancel</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Capabilities" xml:space="preserve">
|
||||
<source>Capabilities</source>
|
||||
<target>Capabilities</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat console" xml:space="preserve">
|
||||
<source>Chat console</source>
|
||||
<target>Chat console</target>
|
||||
|
@ -275,6 +266,11 @@
|
|||
<target>Connect via one-time link?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via relay" xml:space="preserve">
|
||||
<source>Connect via relay</source>
|
||||
<target>Connect via relay</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect with the developers" xml:space="preserve">
|
||||
<source>Connect with the developers</source>
|
||||
<target>Connect with the developers</target>
|
||||
|
@ -460,11 +456,6 @@
|
|||
<target>Enable notifications? (BETA)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="End" xml:space="preserve">
|
||||
<source>End</source>
|
||||
<target>End</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter one SMP server per line:" xml:space="preserve">
|
||||
<source>Enter one SMP server per line:</source>
|
||||
<target>Enter one SMP server per line:</target>
|
||||
|
@ -540,11 +531,6 @@
|
|||
<target>How to use markdown</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ICE" xml:space="preserve">
|
||||
<source>ICE</source>
|
||||
<target>ICE</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you can't meet in person, **show QR code in the video call**, or share the link." xml:space="preserve">
|
||||
<source>If you can't meet in person, **show QR code in the video call**, or share the link.</source>
|
||||
<target>If you can't meet in person, **show QR code in the video call**, or share the link.</target>
|
||||
|
@ -558,7 +544,7 @@
|
|||
<trans-unit id="Ignore" xml:space="preserve">
|
||||
<source>Ignore</source>
|
||||
<target>Ignore</target>
|
||||
<note>ignore incoming call via notification</note>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Image will be received when your contact is online, please wait or check later!" xml:space="preserve">
|
||||
<source>Image will be received when your contact is online, please wait or check later!</source>
|
||||
|
@ -748,7 +734,7 @@
|
|||
<trans-unit id="Reject" xml:space="preserve">
|
||||
<source>Reject</source>
|
||||
<target>Reject</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>reject incoming call via notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reject contact (sender NOT notified)" xml:space="preserve">
|
||||
<source>Reject contact (sender NOT notified)</source>
|
||||
|
@ -795,11 +781,6 @@
|
|||
<target>Scan contact's QR code</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Send" xml:space="preserve">
|
||||
<source>Send</source>
|
||||
<target>Send</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Server connected" xml:space="preserve">
|
||||
<source>Server connected</source>
|
||||
<target>Server connected</target>
|
||||
|
@ -830,11 +811,6 @@
|
|||
<target>Show pending connections</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start" xml:space="preserve">
|
||||
<source>Start</source>
|
||||
<target>Start</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Take picture" xml:space="preserve">
|
||||
<source>Take picture</source>
|
||||
<target>Take picture</target>
|
||||
|
@ -1113,9 +1089,9 @@ SimpleX servers cannot see your profile.</target>
|
|||
<target>above, then choose:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="accepted" xml:space="preserve">
|
||||
<source>accepted</source>
|
||||
<target>accepted</target>
|
||||
<trans-unit id="accepted call" xml:space="preserve">
|
||||
<source>accepted call</source>
|
||||
<target>accepted call</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="audio call (not e2e encrypted)" xml:space="preserve">
|
||||
|
@ -1128,6 +1104,16 @@ SimpleX servers cannot see your profile.</target>
|
|||
<target>bold</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="call error" xml:space="preserve">
|
||||
<source>call error</source>
|
||||
<target>call error</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="call in progress" xml:space="preserve">
|
||||
<source>call in progress</source>
|
||||
<target>call in progress</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="calling…" xml:space="preserve">
|
||||
<source>calling…</source>
|
||||
<target>calling…</target>
|
||||
|
@ -1148,11 +1134,15 @@ SimpleX servers cannot see your profile.</target>
|
|||
<target>connected</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connecting call…" xml:space="preserve">
|
||||
<source>connecting call…</source>
|
||||
<target>connecting call…</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connecting…" xml:space="preserve">
|
||||
<source>connecting…</source>
|
||||
<target>connecting…</target>
|
||||
<note>call status
|
||||
chat list item title</note>
|
||||
<note>chat list item title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connection established" xml:space="preserve">
|
||||
<source>connection established</source>
|
||||
|
@ -1184,19 +1174,14 @@ SimpleX servers cannot see your profile.</target>
|
|||
<target>e2e encrypted</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ended %@" xml:space="preserve">
|
||||
<source>ended %@</source>
|
||||
<target>ended %@</target>
|
||||
<note>call status</note>
|
||||
<trans-unit id="ended" xml:space="preserve">
|
||||
<source>ended</source>
|
||||
<target>ended</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="error" xml:space="preserve">
|
||||
<source>error</source>
|
||||
<target>error</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="in progress" xml:space="preserve">
|
||||
<source>in progress</source>
|
||||
<target>in progress</target>
|
||||
<trans-unit id="ended call %@" xml:space="preserve">
|
||||
<source>ended call %@</source>
|
||||
<target>ended call %@</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="invited to connect" xml:space="preserve">
|
||||
|
@ -1209,9 +1194,9 @@ SimpleX servers cannot see your profile.</target>
|
|||
<target>italic</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="missed" xml:space="preserve">
|
||||
<source>missed</source>
|
||||
<target>missed</target>
|
||||
<trans-unit id="missed call" xml:space="preserve">
|
||||
<source>missed call</source>
|
||||
<target>missed call</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="no e2e encryption" xml:space="preserve">
|
||||
|
@ -1234,9 +1219,14 @@ SimpleX servers cannot see your profile.</target>
|
|||
<target>received answer…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="rejected" xml:space="preserve">
|
||||
<source>rejected</source>
|
||||
<target>rejected</target>
|
||||
<trans-unit id="received confirmation…" xml:space="preserve">
|
||||
<source>received confirmation…</source>
|
||||
<target>received confirmation…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="rejected call" xml:space="preserve">
|
||||
<source>rejected call</source>
|
||||
<target>rejected call</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="secret" xml:space="preserve">
|
||||
|
@ -1299,11 +1289,6 @@ SimpleX servers cannot see your profile.</target>
|
|||
<target>wants to connect to you!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="with e2e encryption" xml:space="preserve">
|
||||
<source>with e2e encryption</source>
|
||||
<target>with e2e encryption</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you shared one-time link" xml:space="preserve">
|
||||
<source>you shared one-time link</source>
|
||||
<target>you shared one-time link</target>
|
||||
|
@ -1410,9 +1395,9 @@ SimpleX servers cannot see your profile.</target>
|
|||
<target>You can now send messages to %@</target>
|
||||
<note>notification body</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="accepted" xml:space="preserve">
|
||||
<source>accepted</source>
|
||||
<target>accepted</target>
|
||||
<trans-unit id="accepted call" xml:space="preserve">
|
||||
<source>accepted call</source>
|
||||
<target>accepted call</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="audio call (not e2e encrypted)" xml:space="preserve">
|
||||
|
@ -1420,16 +1405,30 @@ SimpleX servers cannot see your profile.</target>
|
|||
<target>audio call (not e2e encrypted)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="call error" xml:space="preserve">
|
||||
<source>call error</source>
|
||||
<target>call error</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="call in progress" xml:space="preserve">
|
||||
<source>call in progress</source>
|
||||
<target>call in progress</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="calling…" xml:space="preserve">
|
||||
<source>calling…</source>
|
||||
<target>calling…</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connecting call…" xml:space="preserve">
|
||||
<source>connecting call…</source>
|
||||
<target>connecting call…</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connecting…" xml:space="preserve">
|
||||
<source>connecting…</source>
|
||||
<target>connecting…</target>
|
||||
<note>call status
|
||||
chat list item title</note>
|
||||
<note>chat list item title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connection established" xml:space="preserve">
|
||||
<source>connection established</source>
|
||||
|
@ -1446,19 +1445,9 @@ SimpleX servers cannot see your profile.</target>
|
|||
<target>deleted</target>
|
||||
<note>deleted chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ended %@" xml:space="preserve">
|
||||
<source>ended %@</source>
|
||||
<target>ended %@</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="error" xml:space="preserve">
|
||||
<source>error</source>
|
||||
<target>error</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="in progress" xml:space="preserve">
|
||||
<source>in progress</source>
|
||||
<target>in progress</target>
|
||||
<trans-unit id="ended call %@" xml:space="preserve">
|
||||
<source>ended call %@</source>
|
||||
<target>ended call %@</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="invited to connect" xml:space="preserve">
|
||||
|
@ -1466,19 +1455,14 @@ SimpleX servers cannot see your profile.</target>
|
|||
<target>invited to connect</target>
|
||||
<note>chat list item title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="missed" xml:space="preserve">
|
||||
<source>missed</source>
|
||||
<target>missed</target>
|
||||
<trans-unit id="missed call" xml:space="preserve">
|
||||
<source>missed call</source>
|
||||
<target>missed call</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="no e2e encryption" xml:space="preserve">
|
||||
<source>no e2e encryption</source>
|
||||
<target>no e2e encryption</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="rejected" xml:space="preserve">
|
||||
<source>rejected</source>
|
||||
<target>rejected</target>
|
||||
<trans-unit id="rejected call" xml:space="preserve">
|
||||
<source>rejected call</source>
|
||||
<target>rejected call</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="via contact address link" xml:space="preserve">
|
||||
|
@ -1496,11 +1480,6 @@ SimpleX servers cannot see your profile.</target>
|
|||
<target>video call (not e2e encrypted)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="with e2e encryption" xml:space="preserve">
|
||||
<source>with e2e encryption</source>
|
||||
<target>with e2e encryption</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you shared one-time link" xml:space="preserve">
|
||||
<source>you shared one-time link</source>
|
||||
<target>you shared one-time link</target>
|
||||
|
|
Binary file not shown.
|
@ -20,11 +20,6 @@
|
|||
<target> (можно скопировать)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=" wants to connect with you via " xml:space="preserve">
|
||||
<source> wants to connect with you via </source>
|
||||
<target> хочет связаться с вами через </target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="!1 colored!" xml:space="preserve">
|
||||
<source>!1 colored!</source>
|
||||
<target>!1 цвет!</target>
|
||||
|
@ -133,7 +128,8 @@
|
|||
<trans-unit id="Accept" xml:space="preserve">
|
||||
<source>Accept</source>
|
||||
<target>Принять</target>
|
||||
<note>accept contact request via notification</note>
|
||||
<note>accept contact request via notification
|
||||
accept incoming call via notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept contact" xml:space="preserve">
|
||||
<source>Accept contact</source>
|
||||
|
@ -165,11 +161,6 @@
|
|||
<target>Соединение уже установлено?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Answer" xml:space="preserve">
|
||||
<source>Answer</source>
|
||||
<target>Ответить</target>
|
||||
<note>accept incoming call via notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Answer call" xml:space="preserve">
|
||||
<source>Answer call</source>
|
||||
<target>Принять звонок</target>
|
||||
|
@ -185,16 +176,16 @@
|
|||
<target>Звонок уже завершен!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Call settings" xml:space="preserve">
|
||||
<source>Call settings</source>
|
||||
<target>Настройки звонков</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cancel" xml:space="preserve">
|
||||
<source>Cancel</source>
|
||||
<target>Отменить</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Capabilities" xml:space="preserve">
|
||||
<source>Capabilities</source>
|
||||
<target>Возможности</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat console" xml:space="preserve">
|
||||
<source>Chat console</source>
|
||||
<target>Консоль</target>
|
||||
|
@ -275,6 +266,11 @@
|
|||
<target>Соединиться через одноразовую ссылку?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via relay" xml:space="preserve">
|
||||
<source>Connect via relay</source>
|
||||
<target>Соединяться через сервер (relay)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect with the developers" xml:space="preserve">
|
||||
<source>Connect with the developers</source>
|
||||
<target>Соединиться с разработчиками</target>
|
||||
|
@ -460,11 +456,6 @@
|
|||
<target>Включить уведомления? (БЕТА)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="End" xml:space="preserve">
|
||||
<source>End</source>
|
||||
<target>Завершить</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter one SMP server per line:" xml:space="preserve">
|
||||
<source>Enter one SMP server per line:</source>
|
||||
<target>Введите SMP серверы, каждый на отдельной строке:</target>
|
||||
|
@ -540,11 +531,6 @@
|
|||
<target>Как форматировать</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ICE" xml:space="preserve">
|
||||
<source>ICE</source>
|
||||
<target>ICE</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you can't meet in person, **show QR code in the video call**, or share the link." xml:space="preserve">
|
||||
<source>If you can't meet in person, **show QR code in the video call**, or share the link.</source>
|
||||
<target>Если вы не можете встретиться лично, вы можете **показать QR код во время видеозвонка**, или поделиться ссылкой.</target>
|
||||
|
@ -558,7 +544,7 @@
|
|||
<trans-unit id="Ignore" xml:space="preserve">
|
||||
<source>Ignore</source>
|
||||
<target>Не отвечать</target>
|
||||
<note>ignore incoming call via notification</note>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Image will be received when your contact is online, please wait or check later!" xml:space="preserve">
|
||||
<source>Image will be received when your contact is online, please wait or check later!</source>
|
||||
|
@ -748,7 +734,7 @@
|
|||
<trans-unit id="Reject" xml:space="preserve">
|
||||
<source>Reject</source>
|
||||
<target>Отклонить</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>reject incoming call via notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reject contact (sender NOT notified)" xml:space="preserve">
|
||||
<source>Reject contact (sender NOT notified)</source>
|
||||
|
@ -795,11 +781,6 @@
|
|||
<target>Сосканировать QR код контакта</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Send" xml:space="preserve">
|
||||
<source>Send</source>
|
||||
<target>Отправить</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Server connected" xml:space="preserve">
|
||||
<source>Server connected</source>
|
||||
<target>Установлено соединение с сервером</target>
|
||||
|
@ -830,11 +811,6 @@
|
|||
<target>Показать ожидаемые соединения</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start" xml:space="preserve">
|
||||
<source>Start</source>
|
||||
<target>Начать</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Take picture" xml:space="preserve">
|
||||
<source>Take picture</source>
|
||||
<target>Сделать фото</target>
|
||||
|
@ -1113,9 +1089,9 @@ SimpleX серверы не могут получить доступ к ваше
|
|||
<target>наверху, затем выберите:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="accepted" xml:space="preserve">
|
||||
<source>accepted</source>
|
||||
<target>принятый звонок</target>
|
||||
<trans-unit id="accepted call" xml:space="preserve">
|
||||
<source>accepted call</source>
|
||||
<target> принятый звонок</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="audio call (not e2e encrypted)" xml:space="preserve">
|
||||
|
@ -1128,6 +1104,16 @@ SimpleX серверы не могут получить доступ к ваше
|
|||
<target>жирный</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="call error" xml:space="preserve">
|
||||
<source>call error</source>
|
||||
<target>ошибка звонка</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="call in progress" xml:space="preserve">
|
||||
<source>call in progress</source>
|
||||
<target>активный звонок</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="calling…" xml:space="preserve">
|
||||
<source>calling…</source>
|
||||
<target>входящий звонок…</target>
|
||||
|
@ -1148,11 +1134,15 @@ SimpleX серверы не могут получить доступ к ваше
|
|||
<target>соединение установлено</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connecting call…" xml:space="preserve">
|
||||
<source>connecting call…</source>
|
||||
<target>звонок соединяется…</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connecting…" xml:space="preserve">
|
||||
<source>connecting…</source>
|
||||
<target>соединяется…</target>
|
||||
<note>call status
|
||||
chat list item title</note>
|
||||
<note>chat list item title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connection established" xml:space="preserve">
|
||||
<source>connection established</source>
|
||||
|
@ -1184,19 +1174,14 @@ SimpleX серверы не могут получить доступ к ваше
|
|||
<target>e2e зашифровано</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ended %@" xml:space="preserve">
|
||||
<source>ended %@</source>
|
||||
<target>завершен %@</target>
|
||||
<note>call status</note>
|
||||
<trans-unit id="ended" xml:space="preserve">
|
||||
<source>ended</source>
|
||||
<target>завершён</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="error" xml:space="preserve">
|
||||
<source>error</source>
|
||||
<target>ошибка соединения</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="in progress" xml:space="preserve">
|
||||
<source>in progress</source>
|
||||
<target>активный звонок</target>
|
||||
<trans-unit id="ended call %@" xml:space="preserve">
|
||||
<source>ended call %@</source>
|
||||
<target>завершённый звонок %@</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="invited to connect" xml:space="preserve">
|
||||
|
@ -1209,8 +1194,8 @@ SimpleX серверы не могут получить доступ к ваше
|
|||
<target>курсив</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="missed" xml:space="preserve">
|
||||
<source>missed</source>
|
||||
<trans-unit id="missed call" xml:space="preserve">
|
||||
<source>missed call</source>
|
||||
<target>пропущенный звонок</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
|
@ -1234,9 +1219,14 @@ SimpleX серверы не могут получить доступ к ваше
|
|||
<target>получен ответ…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="rejected" xml:space="preserve">
|
||||
<source>rejected</source>
|
||||
<target>отклоненный звонок</target>
|
||||
<trans-unit id="received confirmation…" xml:space="preserve">
|
||||
<source>received confirmation…</source>
|
||||
<target>получено подтверждение…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="rejected call" xml:space="preserve">
|
||||
<source>rejected call</source>
|
||||
<target>отклонённый звонок</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="secret" xml:space="preserve">
|
||||
|
@ -1299,11 +1289,6 @@ SimpleX серверы не могут получить доступ к ваше
|
|||
<target>хочет соединиться с вами!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="with e2e encryption" xml:space="preserve">
|
||||
<source>with e2e encryption</source>
|
||||
<target>e2e зашифровано</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you shared one-time link" xml:space="preserve">
|
||||
<source>you shared one-time link</source>
|
||||
<target>вы создали ссылку</target>
|
||||
|
@ -1410,9 +1395,9 @@ SimpleX серверы не могут получить доступ к ваше
|
|||
<target>Вы можете отправлять сообщения %@</target>
|
||||
<note>notification body</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="accepted" xml:space="preserve">
|
||||
<source>accepted</source>
|
||||
<target>принятый звонок</target>
|
||||
<trans-unit id="accepted call" xml:space="preserve">
|
||||
<source>accepted call</source>
|
||||
<target>принятный звонок</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="audio call (not e2e encrypted)" xml:space="preserve">
|
||||
|
@ -1420,16 +1405,30 @@ SimpleX серверы не могут получить доступ к ваше
|
|||
<target>аудиозвонок (не e2e зашифрованный)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="call error" xml:space="preserve">
|
||||
<source>call error</source>
|
||||
<target>ошибка звонка</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="call in progress" xml:space="preserve">
|
||||
<source>call in progress</source>
|
||||
<target>активный звонок</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="calling…" xml:space="preserve">
|
||||
<source>calling…</source>
|
||||
<target>входящий звонок…</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connecting call…" xml:space="preserve">
|
||||
<source>connecting call…</source>
|
||||
<target>звонок соединяется…</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connecting…" xml:space="preserve">
|
||||
<source>connecting…</source>
|
||||
<target>соединяется…</target>
|
||||
<note>call status
|
||||
chat list item title</note>
|
||||
<note>chat list item title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connection established" xml:space="preserve">
|
||||
<source>connection established</source>
|
||||
|
@ -1446,19 +1445,9 @@ SimpleX серверы не могут получить доступ к ваше
|
|||
<target>удалено</target>
|
||||
<note>deleted chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ended %@" xml:space="preserve">
|
||||
<source>ended %@</source>
|
||||
<target>завершен %@</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="error" xml:space="preserve">
|
||||
<source>error</source>
|
||||
<target>ошибка соединения</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="in progress" xml:space="preserve">
|
||||
<source>in progress</source>
|
||||
<target>активный звонок</target>
|
||||
<trans-unit id="ended call %@" xml:space="preserve">
|
||||
<source>ended call %@</source>
|
||||
<target>завершённый звонок %@</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="invited to connect" xml:space="preserve">
|
||||
|
@ -1466,19 +1455,14 @@ SimpleX серверы не могут получить доступ к ваше
|
|||
<target>приглашение соединиться</target>
|
||||
<note>chat list item title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="missed" xml:space="preserve">
|
||||
<source>missed</source>
|
||||
<trans-unit id="missed call" xml:space="preserve">
|
||||
<source>missed call</source>
|
||||
<target>пропущенный звонок</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="no e2e encryption" xml:space="preserve">
|
||||
<source>no e2e encryption</source>
|
||||
<target>нет e2e шифрования</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="rejected" xml:space="preserve">
|
||||
<source>rejected</source>
|
||||
<target>отклоненный звонок</target>
|
||||
<trans-unit id="rejected call" xml:space="preserve">
|
||||
<source>rejected call</source>
|
||||
<target>отклонённый звонок</target>
|
||||
<note>call status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="via contact address link" xml:space="preserve">
|
||||
|
@ -1496,11 +1480,6 @@ SimpleX серверы не могут получить доступ к ваше
|
|||
<target>видеозвонок (не e2e зашифрованный)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="with e2e encryption" xml:space="preserve">
|
||||
<source>with e2e encryption</source>
|
||||
<target>e2e зашифровано</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you shared one-time link" xml:space="preserve">
|
||||
<source>you shared one-time link</source>
|
||||
<target>вы создали одноразовую ссылку</target>
|
||||
|
|
Binary file not shown.
|
@ -14,16 +14,24 @@
|
|||
"Accept contact request from %@?" = "Принять запрос на соединение от %@?";
|
||||
|
||||
/* call status */
|
||||
"accepted" = "принятый звонок";
|
||||
"accepted call" = "принятный звонок";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"audio call (not e2e encrypted)" = "аудиозвонок (не e2e зашифрованный)";
|
||||
|
||||
/* call status */
|
||||
"call error" = "ошибка звонка";
|
||||
|
||||
/* call status */
|
||||
"call in progress" = "активный звонок";
|
||||
|
||||
/* call status */
|
||||
"calling…" = "входящий звонок…";
|
||||
|
||||
/* call status
|
||||
chat list item title */
|
||||
/* call status */
|
||||
"connecting call…" = "звонок соединяется…";
|
||||
|
||||
/* chat list item title */
|
||||
"connecting…" = "соединяется…";
|
||||
|
||||
/* chat list item title (it should not be shown */
|
||||
|
@ -36,13 +44,7 @@
|
|||
"deleted" = "удалено";
|
||||
|
||||
/* call status */
|
||||
"ended %@" = "завершен %@";
|
||||
|
||||
/* call status */
|
||||
"error" = "ошибка соединения";
|
||||
|
||||
/* call status */
|
||||
"in progress" = "активный звонок";
|
||||
"ended call %@" = "завершённый звонок %@";
|
||||
|
||||
/* notification */
|
||||
"Incoming audio call" = "Входящий аудиозвонок";
|
||||
|
@ -54,13 +56,10 @@
|
|||
"invited to connect" = "приглашение соединиться";
|
||||
|
||||
/* call status */
|
||||
"missed" = "пропущенный звонок";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"no e2e encryption" = "нет e2e шифрования";
|
||||
"missed call" = "пропущенный звонок";
|
||||
|
||||
/* call status */
|
||||
"rejected" = "отклоненный звонок";
|
||||
"rejected call" = "отклонённый звонок";
|
||||
|
||||
/* chat list item description */
|
||||
"via contact address link" = "через ссылку-контакт";
|
||||
|
@ -71,9 +70,6 @@
|
|||
/* No comment provided by engineer. */
|
||||
"video call (not e2e encrypted)" = "видеозвонок (не e2e зашифрованный)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"with e2e encryption" = "e2e зашифровано";
|
||||
|
||||
/* notification body */
|
||||
"You can now send messages to %@" = "Вы можете отправлять сообщения %@";
|
||||
|
||||
|
|
|
@ -23,10 +23,8 @@
|
|||
<false/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
<string>fetch</string>
|
||||
<string>remote-notification</string>
|
||||
<string>voip</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
3CDBCF4827FF621E00354CDD /* CILinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4727FF621E00354CDD /* CILinkView.swift */; };
|
||||
5C029EA82837DBB3004A9677 /* CICallItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C029EA72837DBB3004A9677 /* CICallItemView.swift */; };
|
||||
5C029EAA283942EA004A9677 /* CallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C029EA9283942EA004A9677 /* CallController.swift */; };
|
||||
5C05DF532840AA1D00C683F9 /* CallSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C05DF522840AA1D00C683F9 /* CallSettings.swift */; };
|
||||
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; };
|
||||
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
|
||||
5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C13730A28156D2700F43030 /* ContactConnectionView.swift */; };
|
||||
|
@ -138,6 +139,7 @@
|
|||
3CDBCF4727FF621E00354CDD /* CILinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CILinkView.swift; sourceTree = "<group>"; };
|
||||
5C029EA72837DBB3004A9677 /* CICallItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CICallItemView.swift; sourceTree = "<group>"; };
|
||||
5C029EA9283942EA004A9677 /* CallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallController.swift; sourceTree = "<group>"; };
|
||||
5C05DF522840AA1D00C683F9 /* CallSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallSettings.swift; sourceTree = "<group>"; };
|
||||
5C063D2627A4564100AEC577 /* ChatPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatPreviewView.swift; sourceTree = "<group>"; };
|
||||
5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactRequestView.swift; sourceTree = "<group>"; };
|
||||
5C13730A28156D2700F43030 /* ContactConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionView.swift; sourceTree = "<group>"; };
|
||||
|
@ -463,6 +465,7 @@
|
|||
children = (
|
||||
5CB924D327A853F100ACCCDD /* SettingsButton.swift */,
|
||||
5CB924D627A8563F00ACCCDD /* SettingsView.swift */,
|
||||
5C05DF522840AA1D00C683F9 /* CallSettings.swift */,
|
||||
5CB924E327A8683A00ACCCDD /* UserAddress.swift */,
|
||||
5CB924E027A867BA00ACCCDD /* UserProfile.swift */,
|
||||
5C577F7C27C83AA10006112D /* MarkdownHelp.swift */,
|
||||
|
@ -624,7 +627,6 @@
|
|||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
ru,
|
||||
);
|
||||
mainGroup = 5CA059BD279559F40002BEB4;
|
||||
|
@ -717,6 +719,7 @@
|
|||
649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */,
|
||||
5CA059ED279559F40002BEB4 /* ContentView.swift in Sources */,
|
||||
5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */,
|
||||
5C05DF532840AA1D00C683F9 /* CallSettings.swift in Sources */,
|
||||
5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */,
|
||||
5C3A88CE27DF50170060F1C2 /* DetermineWidth.swift in Sources */,
|
||||
5CB0BA962827143500B3292C /* MakeConnection.swift in Sources */,
|
||||
|
|
|
@ -49,8 +49,7 @@
|
|||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
showNonLocalizedStrings = "YES">
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
|
|
|
@ -7,9 +7,6 @@
|
|||
/* No comment provided by engineer. */
|
||||
" (can be copied)" = " (можно скопировать)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
" wants to connect with you via " = " хочет связаться с вами через ";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"_italic_" = "\\_курсив_";
|
||||
|
||||
|
@ -88,7 +85,8 @@
|
|||
/* No comment provided by engineer. */
|
||||
"above, then choose:" = "наверху, затем выберите:";
|
||||
|
||||
/* accept contact request via notification */
|
||||
/* accept contact request via notification
|
||||
accept incoming call via notification */
|
||||
"Accept" = "Принять";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
|
@ -98,7 +96,7 @@
|
|||
"Accept contact request from %@?" = "Принять запрос на соединение от %@?";
|
||||
|
||||
/* call status */
|
||||
"accepted" = "принятый звонок";
|
||||
"accepted call" = " принятый звонок";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Add contact to start a new chat" = "Добавьте контакт, чтобы начать разговор";
|
||||
|
@ -112,9 +110,6 @@
|
|||
/* No comment provided by engineer. */
|
||||
"Already connected?" = "Соединение уже установлено?";
|
||||
|
||||
/* accept incoming call via notification */
|
||||
"Answer" = "Ответить";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Answer call" = "Принять звонок";
|
||||
|
||||
|
@ -130,15 +125,21 @@
|
|||
/* No comment provided by engineer. */
|
||||
"Call already ended!" = "Звонок уже завершен!";
|
||||
|
||||
/* call status */
|
||||
"call error" = "ошибка звонка";
|
||||
|
||||
/* call status */
|
||||
"call in progress" = "активный звонок";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Call settings" = "Настройки звонков";
|
||||
|
||||
/* call status */
|
||||
"calling…" = "входящий звонок…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Cancel" = "Отменить";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Capabilities" = "Возможности";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Chat console" = "Консоль";
|
||||
|
||||
|
@ -193,20 +194,25 @@
|
|||
/* No comment provided by engineer. */
|
||||
"Connect via one-time link?" = "Соединиться через одноразовую ссылку?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect via relay" = "Соединяться через сервер (relay)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect with the developers" = "Соединиться с разработчиками";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connected" = "соединение установлено";
|
||||
|
||||
/* call status */
|
||||
"connecting call…" = "звонок соединяется…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server…" = "Устанавливается соединение с сервером…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server… (error: %@)" = "Устанавливается соединение с сервером… (ошибка: %@)";
|
||||
|
||||
/* call status
|
||||
chat list item title */
|
||||
/* chat list item title */
|
||||
"connecting…" = "соединяется…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
|
@ -330,17 +336,14 @@
|
|||
"Enable notifications? (BETA)" = "Включить уведомления? (БЕТА)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"End" = "Завершить";
|
||||
"ended" = "завершён";
|
||||
|
||||
/* call status */
|
||||
"ended %@" = "завершен %@";
|
||||
"ended call %@" = "завершённый звонок %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter one SMP server per line:" = "Введите SMP серверы, каждый на отдельной строке:";
|
||||
|
||||
/* call status */
|
||||
"error" = "ошибка соединения";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Error deleting token" = "Ошибка удаления токена";
|
||||
|
||||
|
@ -383,16 +386,13 @@
|
|||
/* No comment provided by engineer. */
|
||||
"How to use markdown" = "Как форматировать";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"ICE" = "ICE";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"If you can't meet in person, **show QR code in the video call**, or share the link." = "Если вы не можете встретиться лично, вы можете **показать QR код во время видеозвонка**, или поделиться ссылкой.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." = "Если вы не можете встретиться лично, вы можете **сосканировать QR код во время видеозвонка**, или ваш контакт может отправить вам ссылку.";
|
||||
|
||||
/* ignore incoming call via notification */
|
||||
/* No comment provided by engineer. */
|
||||
"Ignore" = "Не отвечать";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
|
@ -404,9 +404,6 @@
|
|||
/* No comment provided by engineer. */
|
||||
"In person or via a video call – the most secure way to connect." = "При встрече или в видеозвонке – самый безопасный способ установить соединение";
|
||||
|
||||
/* call status */
|
||||
"in progress" = "активный звонок";
|
||||
|
||||
/* notification */
|
||||
"Incoming audio call" = "Входящий аудиозвонок";
|
||||
|
||||
|
@ -453,7 +450,7 @@
|
|||
"Message delivery error" = "Ошибка доставки сообщения";
|
||||
|
||||
/* call status */
|
||||
"missed" = "пропущенный звонок";
|
||||
"missed call" = "пропущенный звонок";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Most likely this contact has deleted the connection with you." = "Скорее всего, этот контакт удалил соединение с вами.";
|
||||
|
@ -531,6 +528,9 @@
|
|||
"received answer…" = "получен ответ…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"received confirmation…" = "получено подтверждение…";
|
||||
|
||||
/* reject incoming call via notification */
|
||||
"Reject" = "Отклонить";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
|
@ -540,7 +540,7 @@
|
|||
"Reject contact request" = "Отклонить запрос";
|
||||
|
||||
/* call status */
|
||||
"rejected" = "отклоненный звонок";
|
||||
"rejected call" = "отклонённый звонок";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Reply" = "Ответить";
|
||||
|
@ -563,9 +563,6 @@
|
|||
/* No comment provided by engineer. */
|
||||
"secret" = "секрет";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Send" = "Отправить";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Server connected" = "Установлено соединение с сервером";
|
||||
|
||||
|
@ -587,9 +584,6 @@
|
|||
/* No comment provided by engineer. */
|
||||
"SMP servers" = "SMP серверы";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Start" = "Начать";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"starting…" = "инициализация…";
|
||||
|
||||
|
@ -698,9 +692,6 @@
|
|||
/* No comment provided by engineer. */
|
||||
"Welcome %@!" = "Здравствуйте %@!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"with e2e encryption" = "e2e зашифровано";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You" = "Вы";
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ interface IWCallResponse {
|
|||
|
||||
interface WCCapabilities extends IWCallCommand {
|
||||
type: "capabilities"
|
||||
media?: CallMediaType
|
||||
useWorker?: boolean
|
||||
}
|
||||
|
||||
|
@ -289,8 +290,7 @@ const processCommand = (function () {
|
|||
const pc = new RTCPeerConnection(config.peerConnectionConfig)
|
||||
const remoteStream = new MediaStream()
|
||||
const localCamera = VideoCamera.User
|
||||
const constraints = callMediaConstraints(mediaType, localCamera)
|
||||
const localStream = await navigator.mediaDevices.getUserMedia(constraints)
|
||||
const localStream = await getLocalMediaStream(mediaType, localCamera)
|
||||
const iceCandidates = getIceCandidates(pc, config)
|
||||
const call = {connection: pc, iceCandidates, localMedia: mediaType, localCamera, localStream, remoteStream, aesKey, useWorker}
|
||||
await setupMediaStreams(call)
|
||||
|
@ -352,11 +352,15 @@ const processCommand = (function () {
|
|||
try {
|
||||
switch (command.type) {
|
||||
case "capabilities":
|
||||
console.log("starting outgoing call - capabilities")
|
||||
if (activeCall) endCall()
|
||||
// This request for local media stream is made to prompt for camera/mic permissions on call start
|
||||
if (command.media) await getLocalMediaStream(command.media, VideoCamera.User)
|
||||
const encryption = supportsInsertableStreams(command.useWorker)
|
||||
resp = {type: "capabilities", capabilities: {encryption}}
|
||||
break
|
||||
case "start": {
|
||||
console.log("starting call")
|
||||
console.log("starting incoming call - create webrtc session")
|
||||
if (activeCall) endCall()
|
||||
const {media, useWorker, iceServers, relay} = command
|
||||
const encryption = supportsInsertableStreams(useWorker)
|
||||
|
@ -582,8 +586,7 @@ const processCommand = (function () {
|
|||
const pc = call.connection
|
||||
for (const t of call.localStream.getTracks()) t.stop()
|
||||
call.localCamera = camera
|
||||
const constraints = callMediaConstraints(call.localMedia, camera)
|
||||
const localStream = await navigator.mediaDevices.getUserMedia(constraints)
|
||||
const localStream = await getLocalMediaStream(call.localMedia, camera)
|
||||
replaceTracks(pc, localStream.getVideoTracks())
|
||||
replaceTracks(pc, localStream.getAudioTracks())
|
||||
call.localStream = localStream
|
||||
|
@ -621,6 +624,11 @@ const processCommand = (function () {
|
|||
}
|
||||
}
|
||||
|
||||
function getLocalMediaStream(mediaType: CallMediaType, facingMode: VideoCamera): Promise<MediaStream> {
|
||||
const constraints = callMediaConstraints(mediaType, facingMode)
|
||||
return navigator.mediaDevices.getUserMedia(constraints)
|
||||
}
|
||||
|
||||
function callMediaConstraints(mediaType: CallMediaType, facingMode: VideoCamera): MediaStreamConstraints {
|
||||
switch (mediaType) {
|
||||
case CallMediaType.Audio:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue