diff --git a/app/build.gradle b/app/build.gradle
index a7c526e..d52af13 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -9,8 +9,8 @@ android {
applicationId = 'kr.co.rito.ritosip'
minSdkVersion 29
targetSdkVersion 35
- versionCode = 100
- versionName = '1.0.0'
+ versionCode = 104
+ versionName = '1.0.4'
externalNativeBuild {
cmake {
cFlags '-DHAVE_INTTYPES_H -lstdc++'
@@ -45,6 +45,7 @@ android {
// minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
+ signingConfig signingConfigs.debug
}
}
android.applicationVariants.all { variant ->
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5daf56d..da656b9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -23,7 +23,7 @@
android:maxSdkVersion="32" />
diff --git a/app/src/main/assets/config.static b/app/src/main/assets/config.static
index 0a795dc..ea0ec0c 100644
--- a/app/src/main/assets/config.static
+++ b/app/src/main/assets/config.static
@@ -29,6 +29,7 @@ module vp8.so
module vp9.so
module av1.so
module snapshot.so
+module fakevideo.so
module stun.so
module turn.so
module ice.so
@@ -37,11 +38,13 @@ module dtls_srtp.so
module gzrtp.so
module uuid.so
module vumeter.so
+module webrtc_aecm.so
module_app account.so
module_app debug_cmd.so
module_app mwi.so
avcodec_h264enc h264_mediacodec
video_fps 30
+mic_volume 20
evdev_device /dev/input/event0
opus_samplerate 48000
opus_stereo no
diff --git a/app/src/main/cpp/baresip.c b/app/src/main/cpp/baresip.c
index 36f4616..851f3e0 100644
--- a/app/src/main/cpp/baresip.c
+++ b/app/src/main/cpp/baresip.c
@@ -1785,6 +1785,18 @@ JNIEXPORT jint JNICALL Java_com_tutpro_baresip_plus_Api_call_1set_1video_1source
return err;
}
+JNIEXPORT jint JNICALL Java_com_tutpro_baresip_plus_Api_call_1set_1video_1fake(
+ JNIEnv *env, jobject obj, jlong call)
+{
+ (void)env;
+ (void)obj;
+ int err;
+ re_thread_enter();
+ err = video_set_source(call_video((struct call *)call), "fakevideo", NULL);
+ re_thread_leave();
+ return err;
+}
+
JNIEXPORT void JNICALL Java_com_tutpro_baresip_plus_Api_call_1destroy(
JNIEnv *env, jobject obj, jlong call)
{
@@ -1808,6 +1820,36 @@ JNIEXPORT jint JNICALL Java_com_tutpro_baresip_plus_Api_cmd_1exec(
return res;
}
+
+JNIEXPORT jstring JNICALL Java_com_tutpro_baresip_plus_Api_set_1audio_1source(JNIEnv *env, jobject obj)
+{
+ (void)obj;
+ struct list *aucodecl = baresip_ausrcl();
+ struct le *le;
+ char codec_buf[256];
+ char *start = &(codec_buf[0]);
+ unsigned int left = sizeof codec_buf;
+ int len;
+ for (le = list_head(aucodecl); le != NULL; le = le->next) {
+ const struct ausrc *ac = le->data;
+ if (start == &(codec_buf[0]))
+ len = re_snprintf(start, left, "<%s>", ac->name);
+ else
+ len = re_snprintf(start, left, ",<%s>", ac->name);
+ if (len == -1) {
+ LOGE("failed to print codec to buffer\n");
+ codec_buf[0] = '\0';
+ return (*env)->NewStringUTF(env, codec_buf);
+ }
+ start = start + len;
+ left = left - len;
+ }
+ *start = '\0';
+ return (*env)->NewStringUTF(env, codec_buf);
+}
+
+
+
JNIEXPORT jstring JNICALL Java_com_tutpro_baresip_plus_Api_audio_1codecs(JNIEnv *env, jobject obj)
{
(void)obj;
diff --git a/app/src/main/kotlin/com/tutpro/baresip/plus/Api.kt b/app/src/main/kotlin/com/tutpro/baresip/plus/Api.kt
index 0a64fe6..0ba903b 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/plus/Api.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/plus/Api.kt
@@ -98,6 +98,7 @@ object Api {
external fun call_state(callp: Long): Int
external fun call_has_video(callp: Long): Boolean
external fun call_set_video_source(callp: Long, front: Boolean): Int
+ external fun call_set_video_fake(callp: Long): Int // added by ritoseo
external fun call_set_video_direction(callp: Long, dir: Int)
external fun call_set_video_mute(callp: Long, mute: Boolean) // added by ritoseo
external fun call_set_media_direction(callp: Long, adir: Int, vdir: Int)
@@ -113,6 +114,7 @@ object Api {
external fun message_send(uap: Long, peer_uri: String, message: String, time: String): Int
+ external fun set_audio_source(): String
external fun audio_codecs(): String
external fun video_codecs(): String
diff --git a/app/src/main/kotlin/com/tutpro/baresip/plus/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/plus/BaresipService.kt
index 89c989f..25561b9 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/plus/BaresipService.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/plus/BaresipService.kt
@@ -35,14 +35,12 @@ import android.telecom.TelecomManager
import android.text.Spannable
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
-import android.util.JsonReader
import android.util.Size
import android.view.LayoutInflater
import android.view.View
import android.widget.RemoteViews
import android.widget.TextView
import android.widget.Toast
-import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.ColorRes
import androidx.annotation.Keep
import androidx.annotation.StringRes
@@ -63,11 +61,8 @@ import org.json.JSONObject
import java.io.File
import java.io.IOException
import java.net.InetAddress
-import java.nio.ByteBuffer
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
-import java.time.Clock
-import java.time.LocalDate
import java.time.LocalDateTime
import java.util.*
import kotlin.concurrent.schedule
@@ -269,7 +264,6 @@ class BaresipService: Service() {
}
}
-
sipReqeustReceiver = object : BroadcastReceiver() {
fun sendContactList() {
val resArr : JSONArray = JSONArray()
@@ -570,11 +564,17 @@ class BaresipService: Service() {
if(param != null) {
val json = JSONObject(param)
val device_name = json.getString("device_name")
+ val mic_volume = json.getString("mic_volume")
val display_type = json.getString("display_type")
Config.replaceVariable("device_name", device_name)
BaresipService.deviceName = device_name
+ val mic_volume_int = mic_volume.toInt()
+ if(mic_volume_int >= 0 && mic_volume_int <= 24) {
+ Utils.setMicVolumeByMix(mic_volume_int)
+ }
+
Config.replaceVariable("far_view_display_id", display_type)
BaresipService.farViewDisplayId = display_type.toInt()
Config.save()
@@ -759,14 +759,28 @@ class BaresipService: Service() {
}
val scope = CoroutineScope(Dispatchers.Default)
- var captureCount = 0
+ var captureCount : Long = 0
var running = true
val job = scope.launch {
println("camera capture manager is running...")
Utils.deleteFiles("/mnt/obb", "jpg")
var updateDate = LocalDateTime.now()
+ var cameraState = -1
while (running) {
+
+// var devices = am.getDevices(AudioManager.GET_DEVICES_INPUTS)
+// for (device in devices) {
+// var typeName = "모름 ${device.type}"
+// if(device.type == AudioDeviceInfo.TYPE_USB_HEADSET) {
+// typeName = "USB헤드셋(${device.id})"
+// } else if(device.type == AudioDeviceInfo.TYPE_BUILTIN_MIC) {
+// typeName = "내장마이크(${device.id})"
+// }
+// println("[입력장치] ${typeName} : ${device.productName}")
+// }
+ updateAudioSourceDevice(applicationContext)
+
if(File("/mnt/obb/camera_near.rgb565.done").exists() && !File("/mnt/obb/camera_near.jpg").exists()) {
try {
val nearBuf = Utils.readFileToByteBuffer("/mnt/obb/camera_near.rgb565")
@@ -970,25 +984,88 @@ class BaresipService: Service() {
val camera1 = Utils.checkCameraConnection(0)
val camera2 = Utils.checkCameraConnection(1)
+ val prevCamera1 = Utils.propertyGet("sys.ritosip.camera1.status")
Utils.propertySet("sys.ritosip.camera1.status", camera1)
//call.setVideoSource(!BaresipService.cameraFront)
val prevCamera2 = Utils.propertyGet("sys.ritosip.camera2.status")
Utils.propertySet("sys.ritosip.camera2.status", camera2)
- if(camera2 != prevCamera2) {
+ if(camera1 != "plugin" && camera2 != "plugin" && cameraState != 0) {
if(uas.size > 0) {
val ua = uas[0]
val call = ua.currentCall()
if(call != null) {
- if(camera2 == "plugin") {
- BaresipService.cameraFront = false; // Contents Input
- call.setVideoSource(false)
- } else {
- BaresipService.cameraFront = true; // Camera Input
- call.setVideoSource(true)
- }
+ call.setVideoFake()
+ cameraState = 0
}
}
+ } else if(camera1 == "plugin" && camera2 != "plugin" && (cameraState == 0 || cameraState == 2)) {
+ if(uas.size > 0) {
+ val ua = uas[0]
+ val call = ua.currentCall()
+
+ if(call != null) {
+ Log.d(TAG, "RITO camera1 detect!")
+ //val process = Runtime.getRuntime().exec("ritosysc shell-order=killall android.hardware.camera.provider@2.4-service android.hardware.camera.provider@2.4-external-service cameraserver android.hardware.tv.input@1.0-service")
+ if(cameraState == 0) {
+ val process = Runtime.getRuntime()
+ .exec("ritosysc shell-order=killall cameraserver")
+ process.waitFor()
+ process.destroy()
+ delay(1500) // 1초마다 실행
+ }
+ BaresipService.cameraFront = true;
+ call.setVideoSource(true)
+ cameraState = 1
+ //delay(3000) // 1초마다 실행
+ }
+ }
+ } else if(camera2 == "plugin" && cameraState != 2) {
+ if(uas.size > 0) {
+ val ua = uas[0]
+ val call = ua.currentCall()
+
+ if(call != null) {
+ Log.d(TAG, "RITO camera2 detect!")
+ //val process = Runtime.getRuntime().exec("ritosysc shell-order=killall android.hardware.camera.provider@2.4-service android.hardware.camera.provider@2.4-external-service cameraserver android.hardware.tv.input@1.0-service")
+// val process = Runtime.getRuntime().exec("ritosysc shell-order=killall cameraserver")
+// process.waitFor()
+// process.destroy()
+// delay(1500) // 1초마다 실행
+ BaresipService.cameraFront = false;
+ call.setVideoSource(false)
+ cameraState = 2
+ //delay(3000) // 1초마다 실행
+ }
+ }
+ }
+// else if(camera2 != prevCamera2) {
+// if(uas.size > 0) {
+// val ua = uas[0]
+// val call = ua.currentCall()
+//
+// if(call != null) {
+// cameraState = 2
+// if(camera2 == "plugin") {
+// BaresipService.cameraFront = false; // Contents Input
+// call.setVideoSource(false)
+// } else {
+// BaresipService.cameraFront = true; // Camera Input
+// call.setVideoSource(true)
+// }
+// }
+// }
+// }
+
+ if(uas.size > 0) {
+ val ua = uas[0]
+ val call = ua.currentCall()
+
+ if(call == null) {
+ cameraState = -1
+ }
+ } else {
+ cameraState = -1
}
if(BaresipService.cameraFront) {
@@ -1000,15 +1077,17 @@ class BaresipService: Service() {
val usbMicExist = Utils.checkUsbMicrophone()
if(usbMicExist) {
Utils.propertySet("sys.ritosip.usb.microphone", "connected")
- if(audioInputDevice == "usb") {
- Utils.propertySet("sys.rito.audio.input.device", "usb")
- } else {
- Utils.propertySet("sys.rito.audio.input.device", "codec")
- }
+// if(audioInputDevice == "usb") {
+// Utils.propertySet("sys.rito.audio.input.device", "usb")
+// } else {
+// Utils.propertySet("sys.rito.audio.input.device", "codec")
+// }
} else {
Utils.propertySet("sys.ritosip.usb.microphone", "disconnected")
}
+ Utils.setAudioSourceDevice(audioInputDevice)
+
delay(100) // 1초마다 실행
val now = LocalDateTime.now()
@@ -1051,7 +1130,7 @@ class BaresipService: Service() {
if (farFilePath.length > 0)
Utils.propertySet("sys.ritosip.far_screen_file", farFilePath)
} else {
- Utils.propertySet("sys.ritosip.near_screen_file", "")
+ //Utils.propertySet("sys.ritosip.near_screen_file", "")
Utils.propertySet("sys.ritosip.far_screen_file", "")
}
}
@@ -1068,6 +1147,54 @@ class BaresipService: Service() {
Utils.propertySet("sys.ritosip.display_split_mode", BaresipService.displaySplitMode.toString())
Utils.propertySet("sys.ritosip.sip.total_duration", BaresipService.totalDurationSeconds.toString())
+ // Call 중이 아닌 경우 Local Camera 화면 캡쳐
+ val camera1 = Utils.checkCameraConnection(0)
+ if(cameraState == -1) {
+ if(camera1 == "plugin") {
+ //Utils.runShellOrderSuper("killall -9 v4l2-ctl")
+ Utils.runShellOrderSuper("timeout 2s v4l2-ctl --device=/dev/video20 --stream-mmap=3 --stream-to=/mnt/obb/camera.rgb888 --stream-count=1 && chmod 666 /mnt/obb/camera.rgb888")
+
+ try {
+ val nearBuf = Utils.readFileToByteBuffer("/mnt/obb/camera.rgb888")
+ var bufSize = nearBuf.capacity()
+ var width : Int = 1280
+ var height : Int = 720
+ if(bufSize == 1280 * 720 * 3) {
+ width = 1280
+ height = 720
+ } else if(bufSize == 1920 * 1080 * 3) {
+ width = 1920
+ height = 1080
+ }
+
+ if(bufSize > 0) {
+ var nearFilePath = "/mnt/obb/near_$captureCount.jpg"
+ Utils.saveRGB888AsJPG(
+ applicationContext,
+ nearBuf,
+ width,
+ height,
+ nearFilePath
+ )
+
+ val destFile = File(nearFilePath)
+ destFile.setReadable(true, false)
+ Utils.propertySet("sys.ritosip.near_screen_file", nearFilePath)
+ }
+ } catch(e : Exception) {
+ //e.printStackTrace()
+ }
+ } else {
+ var nearFilePath = "/mnt/obb/near_$captureCount.jpg"
+ Utils.saveDrawableAsJpg(applicationContext, R.drawable.nocamera, nearFilePath)
+
+ val destFile = File(nearFilePath)
+ destFile.setReadable(true, false)
+ Utils.propertySet("sys.ritosip.near_screen_file", nearFilePath)
+ }
+
+ }
+
captureCount++
delay(100)
@@ -1161,7 +1288,7 @@ class BaresipService: Service() {
CallHistoryNew.save()
}
- val value = Utils.readNumberFromFile("/sdcard/Documents/sip_total_duration")
+ val value = Utils.readNumberFromFile("/sdcard/Documents/sip_total_duration.txt")
if(value != null) {
BaresipService.totalDurationSeconds = value
}
@@ -1225,6 +1352,8 @@ class BaresipService: Service() {
applyTransportConfiguration()
+ Utils.setMicVolumeByMix(BaresipService.micVolume)
+
}
"Start Content Observer" -> {
@@ -1826,7 +1955,9 @@ class BaresipService: Service() {
/* Added by ritoseo */
val duration = call.duration()
BaresipService.totalDurationSeconds += duration
- Utils.saveNumberToFile("/sdcard/Documents/sip_total_duration", BaresipService.totalDurationSeconds)
+ Utils.saveNumberToFile("/sdcard/Documents/sip_total_duration.txt", BaresipService.totalDurationSeconds)
+ //Utils.saveNumberToFile(File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "sip_total_duration").absolutePath, BaresipService.totalDurationSeconds)
+
/* ---------------- */
if (!Utils.isVisible()) {
@@ -2267,11 +2398,15 @@ class BaresipService: Service() {
}
private fun setCallVolume() {
+ println("Call volume setting!")
+ callVolume = 0
if (callVolume != 0)
for (streamType in listOf(AudioManager.STREAM_MUSIC, AudioManager.STREAM_VOICE_CALL)) {
origVolume[streamType] = am.getStreamVolume(streamType)
val maxVolume = am.getStreamMaxVolume(streamType)
am.setStreamVolume(streamType, (callVolume * 0.1 * maxVolume).roundToInt(), 0)
+ println("Orig/new/max $streamType volume is " +
+ "${origVolume[streamType]}/${am.getStreamVolume(streamType)}/$maxVolume")
Log.d(TAG, "Orig/new/max $streamType volume is " +
"${origVolume[streamType]}/${am.getStreamVolume(streamType)}/$maxVolume")
}
@@ -2548,6 +2683,26 @@ class BaresipService: Service() {
var nearViewDisplayId = 2
var farViewLayout : Rect = Rect(0, 0, 1920, 1080)
var nearViewLayout : Rect = Rect(0, 0, 1920, 1080)
+ var audioDeviceUsbId = -1
+ var audioDeviceCodecId = -1
+ var micVolume = 20
+
+ fun updateAudioSourceDevice(ctx: Context) {
+ val am = ctx.getSystemService(Context.AUDIO_SERVICE) as AudioManager
+ var devices = am.getDevices(AudioManager.GET_DEVICES_INPUTS)
+ var usbId = -1
+ var codecId = -1
+ for (device in devices) {
+ if(device.type == AudioDeviceInfo.TYPE_USB_HEADSET) {
+ usbId = device.id
+ } else if(device.type == AudioDeviceInfo.TYPE_BUILTIN_MIC) {
+ codecId = device.id
+ }
+ }
+
+ audioDeviceUsbId = usbId
+ audioDeviceCodecId = codecId
+ }
fun requestAudioFocus(ctx: Context): Boolean {
Log.d(TAG, "Requesting audio focus")
diff --git a/app/src/main/kotlin/com/tutpro/baresip/plus/Call.kt b/app/src/main/kotlin/com/tutpro/baresip/plus/Call.kt
index 260bf24..b36b1df 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/plus/Call.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/plus/Call.kt
@@ -46,6 +46,10 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
return Api.call_set_video_source(callp, front)
}
+ fun setVideoFake(): Int {
+ return Api.call_set_video_fake(callp)
+ }
+
fun hold(): Boolean {
return Api.call_hold(callp, true) == 0
}
diff --git a/app/src/main/kotlin/com/tutpro/baresip/plus/Config.kt b/app/src/main/kotlin/com/tutpro/baresip/plus/Config.kt
index 0466c4c..9d1e06e 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/plus/Config.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/plus/Config.kt
@@ -211,6 +211,14 @@ object Config {
*/
/* Added by ritoseo */
+ val micVolume = previousVariable("mic_volume")
+ if (micVolume != "") {
+ config = "${config}mic_volume $micVolume\n"
+ BaresipService.micVolume = micVolume.toInt()
+ } else {
+ config = "${config}mic_volume ${BaresipService.micVolume}\n"
+ }
+
for (size in defaultSizes)
videoSizes.add(size.toString())
/********************/
@@ -225,12 +233,15 @@ object Config {
Size(videoSize.substringBefore("x").toInt(),
videoSize.substringAfter("x").toInt())
}
+
+ BaresipService.videoSize = Size(1280, 720)
+
//BaresipService.videoSize = Size(640, 480)
config = "${config}video_size " +
"${BaresipService.videoSize.width}x${BaresipService.videoSize.height}\n"
/* Added by ritoseo */
- config = "${config}video_bitrate 60000000\n"
+ config = "${config}video_bitrate 100000000\n"
config = "${config}rtp_video_tos 184\n"
config = "${config}rtp_bandwidth 512-8192\n"
/********************/
diff --git a/app/src/main/kotlin/com/tutpro/baresip/plus/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/plus/MainActivity.kt
index 9c05c62..bef9d4b 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/plus/MainActivity.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/plus/MainActivity.kt
@@ -11,6 +11,7 @@ import android.content.*
import android.content.Intent.ACTION_CALL
import android.content.Intent.ACTION_DIAL
import android.content.Intent.ACTION_VIEW
+import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Rect
@@ -19,10 +20,8 @@ import android.media.AudioManager
import android.media.MediaActionSound
import android.net.Uri
import android.os.*
-import android.os.StrictMode.VmPolicy
import android.provider.DocumentsContract
import android.provider.MediaStore
-import android.provider.MediaStore.Audio.Radio
import android.text.InputType
import android.text.TextWatcher
import android.util.TypedValue
@@ -48,14 +47,18 @@ import androidx.lifecycle.Observer
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
+import com.tutpro.baresip.plus.Utils.getAppVersion
import com.tutpro.baresip.plus.Utils.showSnackBar
import com.tutpro.baresip.plus.databinding.ActivityMainBinding
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import org.json.JSONObject
import java.io.File
import java.text.SimpleDateFormat
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.*
-import kotlin.collections.HashMap
import kotlin.system.exitProcess
@@ -138,6 +141,11 @@ class MainActivity : AppCompatActivity() {
private lateinit var callStartButton: AppCompatButton
private lateinit var callHistoryButton: AppCompatButton
private lateinit var settingButton: AppCompatButton
+ private lateinit var layoutButton: AppCompatButton
+ private lateinit var serverButton: AppCompatButton
+ private lateinit var volumeButton: AppCompatButton
+ private lateinit var networkButton: AppCompatButton
+ private lateinit var backwardButton: AppCompatButton
private var callHandler: Handler = Handler(Looper.getMainLooper())
private var callRunnable: Runnable? = null
@@ -191,6 +199,9 @@ class MainActivity : AppCompatActivity() {
lateinit var navUpList : HashMap
lateinit var navDownList : HashMap
+ lateinit var navUpServerList : HashMap
+ lateinit var navDownServerList : HashMap
+
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
if(!isKeyboardVisible) {
val currentFocusView = currentFocus
@@ -214,6 +225,24 @@ class MainActivity : AppCompatActivity() {
return false
}
}
+
+ if(navUpServerList.contains(currentFocusView)) {
+ val view = navUpServerList.get(currentFocusView)!!
+ if(view.isVisible) {
+ view.requestFocus()
+ return false
+ } else {
+ if(currentFocusView == binding.btnApply) {
+ binding.radioDhcp.requestFocus()
+ return false
+ }
+ }
+ } else {
+ if(currentFocusView == binding.aorSpinner) {
+ callStartButton.requestFocus()
+ return false
+ }
+ }
}
else if(event.keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
if(navDownList.contains(currentFocusView)) {
@@ -223,12 +252,22 @@ class MainActivity : AppCompatActivity() {
return false
}
}
-
+ if(navDownServerList.contains(currentFocusView)) {
+ val view = navDownServerList.get(currentFocusView)!!
+ if(view.isVisible) {
+ view.requestFocus()
+ return false
+ }
+ }
} else if(event.keyCode == KeyEvent.KEYCODE_ENTER) {
if(currentFocusView == binding.editIp ||
currentFocusView == binding.editGateway ||
currentFocusView == binding.editNetmask ||
- currentFocusView == binding.editDns) {
+ currentFocusView == binding.editDns ||
+ currentFocusView == binding.editDisplayName ||
+ currentFocusView == binding.editSipId ||
+ currentFocusView == binding.editSipPassword
+ ) {
if(imm.isActive) {
imm.showSoftInput(currentFocusView, InputMethodManager.SHOW_IMPLICIT)
imm.restartInput(currentFocusView)
@@ -242,6 +281,9 @@ class MainActivity : AppCompatActivity() {
if(navUpList.contains(currentFocusView) || navDownList.contains(currentFocusView)) {
found = true
}
+ if(navUpServerList.contains(currentFocusView) || navDownServerList.contains(currentFocusView)) {
+ found = true
+ }
if(found) {
return true
@@ -253,6 +295,12 @@ class MainActivity : AppCompatActivity() {
currentFocusView == binding.editDns) {
return true
}
+
+ if(currentFocusView == binding.editDisplayName ||
+ currentFocusView == binding.editSipId ||
+ currentFocusView == binding.editSipPassword) {
+ return true
+ }
}
}
} else {
@@ -389,6 +437,8 @@ class MainActivity : AppCompatActivity() {
setupKeyboardVisibilityListener()
+ Utils.propertySet("sys.ritosip.version", getAppVersion(this))
+
// Must be done after view has been created
this.setShowWhenLocked(true)
this.setTurnScreenOn( true)
@@ -453,10 +503,213 @@ class MainActivity : AppCompatActivity() {
// binding.baseButtonLayout.visibility = View.INVISIBLE
// binding.mainActivityLayout.visibility = View.INVISIBLE
// binding.defaultLayout.visibility = View.INVISIBLE
+
+// binding.networkSettingLayout.visibility = View.VISIBLE
+// binding.radioDhcp.requestFocus()
+
+ binding.settingLayout.visibility = View.VISIBLE
+ binding.dialogButtonLayout.requestFocus()
+ }
+
+ networkButton = binding.dialogButtonNetwork
+ networkButton.setOnClickListener {
+ binding.networkSettingLayout.bringToFront()
binding.networkSettingLayout.visibility = View.VISIBLE
binding.radioDhcp.requestFocus()
}
+ layoutButton = binding.dialogButtonLayout
+ layoutButton.setOnClickListener {
+ binding.layoutSettingLayout.bringToFront()
+ binding.layoutSettingLayout.visibility = View.VISIBLE
+ var splitMode = Utils.propertyGet("sys.ritosip.display_split_mode")
+ if(splitMode == "2") {
+ binding.layoutType2.requestFocus()
+ } else if(splitMode == "3") {
+ binding.layoutType3.requestFocus()
+ } else if(splitMode == "4") {
+ binding.layoutType4.requestFocus()
+ } else if(splitMode == "5") {
+ binding.layoutType5.requestFocus()
+ } else if(splitMode == "6") {
+ binding.layoutType6.requestFocus()
+ } else if(splitMode == "7") {
+ binding.layoutType7.requestFocus()
+ } else if(splitMode == "8") {
+ binding.layoutType8.requestFocus()
+ } else if(splitMode == "9") {
+ binding.layoutType9.requestFocus()
+ } else if(splitMode == "10") {
+ binding.layoutType10.requestFocus()
+ } else {
+ binding.layoutType1.requestFocus()
+ }
+ }
+
+ binding.layoutType1.setOnClickListener {
+ sendSettingByBroadcast("layout_setting", "{\"layout\":\"layout1\"}")
+ }
+ binding.layoutType2.setOnClickListener {
+ sendSettingByBroadcast("layout_setting", "{\"layout\":\"layout2\"}")
+ }
+ binding.layoutType3.setOnClickListener {
+ sendSettingByBroadcast("layout_setting", "{\"layout\":\"layout3\"}")
+ }
+ binding.layoutType4.setOnClickListener {
+ sendSettingByBroadcast("layout_setting", "{\"layout\":\"layout4\"}")
+ }
+ binding.layoutType5.setOnClickListener {
+ sendSettingByBroadcast("layout_setting", "{\"layout\":\"layout5\"}")
+ }
+ binding.layoutType6.setOnClickListener {
+ sendSettingByBroadcast("layout_setting", "{\"layout\":\"layout6\"}")
+ }
+
+ binding.layoutType7.setOnClickListener {
+ sendSettingByBroadcast("layout_setting", "{\"layout\":\"layout7\"}")
+ }
+ binding.layoutType8.setOnClickListener {
+ sendSettingByBroadcast("layout_setting", "{\"layout\":\"layout8\"}")
+ }
+ binding.layoutType9.setOnClickListener {
+ sendSettingByBroadcast("layout_setting", "{\"layout\":\"layout9\"}")
+ }
+ binding.layoutType10.setOnClickListener {
+ sendSettingByBroadcast("layout_setting", "{\"layout\":\"layout10\"}")
+ }
+
+
+ binding.dialogButtonLayoutInCall.setOnClickListener {
+ binding.layoutSettingLayout.bringToFront()
+ binding.layoutSettingLayout.visibility = View.VISIBLE
+ var splitMode = Utils.propertyGet("sys.ritosip.display_split_mode")
+ if(splitMode == "2") {
+ binding.layoutType2.requestFocus()
+ } else if(splitMode == "3") {
+ binding.layoutType3.requestFocus()
+ } else if(splitMode == "4") {
+ binding.layoutType4.requestFocus()
+ } else if(splitMode == "5") {
+ binding.layoutType5.requestFocus()
+ } else if(splitMode == "6") {
+ binding.layoutType6.requestFocus()
+ } else if(splitMode == "7") {
+ binding.layoutType7.requestFocus()
+ } else if(splitMode == "8") {
+ binding.layoutType8.requestFocus()
+ } else if(splitMode == "9") {
+ binding.layoutType9.requestFocus()
+ } else if(splitMode == "10") {
+ binding.layoutType10.requestFocus()
+ } else {
+ binding.layoutType1.requestFocus()
+ }
+ }
+
+ serverButton = binding.dialogButtonServer
+ serverButton.setOnClickListener {
+ binding.serverSettingLayout.bringToFront()
+ binding.serverSettingLayout.visibility = View.VISIBLE
+ binding.editDisplayName.setText(Utils.propertyGet("sys.ritosip.account.display_name"))
+ binding.editSipId.setText(Utils.propertyGet("sys.ritosip.account.account_name"))
+ binding.editSipServer.setText(Utils.propertyGet("sys.ritosip.account.server_address"))
+ var transport = Utils.propertyGet("sys.ritosip.account.prefer_transport")
+ if(transport == "tcp") {
+ binding.radioTransportTcp.isChecked = true
+ } else if(transport == "tls") {
+ binding.radioTransportTls.isChecked = true
+ } else {
+ binding.radioTransportUdp.isChecked = true
+ }
+ var encryption = Utils.propertyGet("sys.ritosip.account.media_encryption")
+ if(encryption == "srtp") {
+ binding.radioEncryptSrtp.isChecked = true
+ } else {
+ binding.radioEncryptNone.isChecked = true
+ }
+ binding.editDisplayName.requestFocus()
+ }
+
+ volumeButton = binding.dialogButtonVolume
+ volumeButton.setOnClickListener {
+ binding.volumeSettingLayout.bringToFront()
+ binding.volumeSettingLayout.visibility = View.VISIBLE
+ binding.seekbarMicVolume.requestFocus()
+
+ binding.seekbarMicVolume.progress = BaresipService.micVolume
+ val currentValue = binding.seekbarMicVolume.progress
+ binding.textMicVolume.setText(currentValue.toString())
+ }
+
+ binding.dialogButtonVolumeInCall.setOnClickListener {
+ binding.volumeSettingLayout.bringToFront()
+ binding.volumeSettingLayout.visibility = View.VISIBLE
+ binding.seekbarMicVolume.requestFocus()
+
+ binding.seekbarMicVolume.progress = BaresipService.micVolume
+ val currentValue = binding.seekbarMicVolume.progress
+ binding.textMicVolume.setText(currentValue.toString())
+ }
+
+ binding.seekbarMicVolume.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
+ override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+ // progress: 현재 값
+ // fromUser: 사용자가 직접 움직였는지 여부
+ val currentValue = progress
+ println("현재 값: $currentValue")
+ binding.textMicVolume.setText(currentValue.toString())
+ Utils.setMicVolumeByMix(currentValue)
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar?) {
+ // 터치 시작 시 호출
+
+ }
+
+ override fun onStopTrackingTouch(seekBar: SeekBar?) {
+ // 터치 끝날 때 호출
+ }
+ })
+
+ binding.btnDoneVolume.setOnClickListener {
+ binding.volumeSettingLayout.visibility = View.INVISIBLE
+ if(binding.settingLayout.visibility == View.VISIBLE) {
+ binding.dialogButtonVolume.requestFocus()
+ } else if(binding.settingLayoutInCall.visibility == View.VISIBLE) {
+ binding.dialogButtonVolumeInCall.requestFocus()
+ }
+ }
+
+ backwardButton = binding.dialogButtonBackward
+ backwardButton.setOnClickListener {
+ if(binding.settingLayout.visibility == View.VISIBLE) {
+ binding.settingLayout.visibility = View.INVISIBLE
+ binding.settingBtn.requestFocus()
+ }
+ }
+
+ binding.dialogButtonHangUp.setOnClickListener {
+ if(binding.settingLayoutInCall.visibility == View.VISIBLE) {
+ hangupButton.performClick()
+ binding.settingLayoutInCall.visibility = View.INVISIBLE
+ }
+ }
+
+ binding.dialogButtonBackwardInCall.setOnClickListener {
+ if(binding.settingLayoutInCall.visibility == View.VISIBLE) {
+ binding.settingLayoutInCall.visibility = View.INVISIBLE
+ }
+ }
+
+ binding.layoutBackward.setOnClickListener {
+ binding.layoutSettingLayout.visibility = View.INVISIBLE
+ if(binding.settingLayout.visibility == View.VISIBLE) {
+ binding.dialogButtonLayout.requestFocus()
+ } else if(binding.settingLayoutInCall.visibility == View.VISIBLE) {
+ binding.dialogButtonLayoutInCall.requestFocus()
+ }
+ }
+
BaresipService.supportedCameras = Utils.supportedCameras(applicationContext).isNotEmpty()
@@ -530,12 +783,87 @@ class MainActivity : AppCompatActivity() {
showCustomToast(applicationContext, "STATIC 적용되었습니다.")
binding.networkSettingLayout.visibility = View.INVISIBLE
}
+ binding.dialogButtonNetwork.requestFocus()
}
+
binding.btnCancel.setOnClickListener { _ ->
binding.networkSettingLayout.visibility = View.INVISIBLE
+ binding.dialogButtonNetwork.requestFocus()
}
+
+ binding.btnApplyServer.setOnClickListener { _ ->
+ showCustomToast(applicationContext, "적용되었습니다.")
+ var param : JSONObject = JSONObject()
+ param.put("display_name", binding.editDisplayName.text)
+ param.put("account_name", binding.editSipId.text)
+ var password = binding.editSipPassword.text.toString()
+ if(password.isEmpty()) {
+ if (File(filesDir.absolutePath + "/accounts").exists()) {
+ val accounts = String(
+ Utils.getFileContents(filesDir.absolutePath + "/accounts")!!,
+ Charsets.UTF_8
+ ).lines().toMutableList()
+
+ password = getPassword(accounts)
+ }
+ }
+ param.put("password", password)
+ param.put("server_address", binding.editSipServer.text)
+ var transport = "udp"
+ if(binding.radioTransportTcp.isChecked) {
+ transport = "tcp"
+ } else if(binding.radioTransportTls.isChecked) {
+ transport = "tls"
+ }
+ param.put("transport", transport)
+ var encrypt = "none"
+ if(binding.radioEncryptSrtp.isChecked) {
+ encrypt = "srtpo"
+ }
+ param.put("media_encryption", encrypt)
+ println("PARAM >> " + param.toString())
+ sendSettingByBroadcast("set_account", param.toString())
+ binding.serverSettingLayout.visibility = View.INVISIBLE
+ binding.dialogButtonServer.requestFocus()
+ }
+
+ binding.btnCancelServer.setOnClickListener { _ ->
+ binding.serverSettingLayout.visibility = View.INVISIBLE
+ binding.dialogButtonServer.requestFocus()
+ }
+
+ navUpServerList = HashMap()
+ navDownServerList = HashMap()
+
+ navUpServerList.put(binding.editDisplayName, binding.btnApplyServer)
+ navUpServerList.put(binding.editSipId, binding.editDisplayName)
+ navUpServerList.put(binding.editSipPassword, binding.editSipId)
+ navUpServerList.put(binding.editSipServer, binding.editSipPassword)
+ navUpServerList.put(binding.radioTransportUdp, binding.editSipServer)
+ navUpServerList.put(binding.radioTransportTcp, binding.editSipServer)
+ navUpServerList.put(binding.radioTransportTls, binding.editSipServer)
+ navUpServerList.put(binding.radioEncryptNone, binding.radioTransportUdp)
+ navUpServerList.put(binding.radioEncryptSrtp, binding.radioTransportUdp)
+ navUpServerList.put(binding.btnApplyServer, binding.radioEncryptNone)
+ navUpServerList.put(binding.btnCancelServer, binding.radioEncryptNone)
+ //navUpList.put(binding.btnApply, binding.radioDhcp)
+
+ navDownServerList.put(binding.editDisplayName, binding.editSipId)
+ navDownServerList.put(binding.editSipId, binding.editSipPassword)
+ navDownServerList.put(binding.editSipPassword, binding.editSipServer)
+ navDownServerList.put(binding.editSipServer, binding.radioTransportUdp)
+ navDownServerList.put(binding.radioTransportUdp, binding.radioEncryptNone)
+ navDownServerList.put(binding.radioTransportTcp, binding.radioEncryptNone)
+ navDownServerList.put(binding.radioTransportTls, binding.radioEncryptNone)
+ navDownServerList.put(binding.radioEncryptNone, binding.btnApplyServer)
+ navDownServerList.put(binding.radioEncryptSrtp, binding.btnApplyServer)
+ navDownServerList.put(binding.btnApplyServer, binding.editDisplayName)
+ navDownServerList.put(binding.btnCancelServer, binding.editDisplayName)
+
+
+
updateFieldsVisibility()
val radioGroup = findViewById(R.id.radioGroup)
@@ -1179,11 +1507,12 @@ class MainActivity : AppCompatActivity() {
else
getString(R.string.audio_permissions)
) { requestPermissionsLauncher.launch(permissions) }
- else
- startBaresip()
+// else
+// startBaresip()
}
}
+ startBaresip()
addVideoLayoutViews()
if (!BaresipService.isServiceRunning) {
@@ -1192,7 +1521,7 @@ class MainActivity : AppCompatActivity() {
Utils.getFileContents(filesDir.absolutePath + "/accounts")!!,
Charsets.UTF_8
).lines().toMutableList()
- askPasswords(accounts)
+ //askPasswords(accounts)
} else {
// Baresip is started for the first time
requestPermissionsLauncher.launch(permissions)
@@ -1201,6 +1530,14 @@ class MainActivity : AppCompatActivity() {
} // OnCreate
+ fun sendSettingByBroadcast(req : String, param : String) {
+ val responseIntent = Intent("kr.co.rito.ritosip.REQUEST")
+ responseIntent.putExtra("request", req)
+ responseIntent.putExtra("param", param)
+ responseIntent.setPackage("kr.co.rito.ritosip");
+ sendBroadcast(responseIntent)
+ }
+
override fun onStart() {
super.onStart()
Log.d(TAG, "Main onStart")
@@ -2055,6 +2392,7 @@ class MainActivity : AppCompatActivity() {
hangupButton.performClick()
}
videoLayout.addView(hb)
+ hb.visibility = View.INVISIBLE // by ritoseo
// Info Button
val ib = ImageButton(this)
@@ -2354,13 +2692,50 @@ class MainActivity : AppCompatActivity() {
}
when (event?.scanCode) {
SCANCODE_OPTION -> {
- val dialog = findViewById(R.id.dialogLayout)
- if(dialog.isVisible) {
- dialog.visibility = View.INVISIBLE
+// val dialog = findViewById(R.id.dialogLayout)
+// if(dialog.isVisible) {
+// dialog.visibility = View.INVISIBLE
+// } else {
+// dialog.visibility = View.VISIBLE
+// findViewById(R.id.dialogButtonImg1).requestFocus()
+// }
+
+// val dialog = findViewById(R.id.settingLayout)
+// if (dialog.isVisible) {
+// dialog.visibility = View.INVISIBLE
+// } else {
+// dialog.bringToFront()
+// dialog.visibility = View.VISIBLE
+// findViewById(R.id.dialogButtonLayout).requestFocus()
+// }
+ if(binding.layoutSettingLayout.visibility == View.VISIBLE ||
+ binding.networkSettingLayout.visibility == View.VISIBLE ||
+ binding.serverSettingLayout.visibility == View.VISIBLE ||
+ binding.volumeSettingLayout.visibility == View.VISIBLE)
+ return true
+
+ if (Call.inCall()) {
+ val dialog = findViewById(R.id.settingLayoutInCall)
+ if (dialog.isVisible) {
+ dialog.visibility = View.INVISIBLE
+ println("settingLayoutInCall 비활성화")
+ } else {
+ dialog.bringToFront()
+ dialog.visibility = View.VISIBLE
+ findViewById(R.id.dialogButtonLayoutInCall).requestFocus()
+ println("settingLayoutInCall 활성화")
+ }
} else {
- dialog.visibility = View.VISIBLE
- findViewById(R.id.dialogButtonImg1).requestFocus()
+ val dialog = findViewById(R.id.settingLayout)
+ if (dialog.isVisible) {
+ dialog.visibility = View.INVISIBLE
+ } else {
+ dialog.bringToFront()
+ dialog.visibility = View.VISIBLE
+ findViewById(R.id.dialogButtonLayout).requestFocus()
+ }
}
+
// val dialog = findViewById(R.id.dialogBase)
// val customButton = LayoutInflater.from(this).inflate(R.layout.image_button, null)
// dialog.addView(customButton)
@@ -3097,6 +3472,19 @@ class MainActivity : AppCompatActivity() {
}
}
+ private fun getPassword(accounts: MutableList) : String {
+ if (accounts.isNotEmpty()) {
+ val account = accounts.removeAt(0)
+ val params = account.substringAfter(">")
+ if ((Utils.paramValue(params, "auth_user") != "") &&
+ (Utils.paramValue(params, "auth_pass") != "")) {
+ return Utils.paramValue(params, "auth_pass").trim('"')
+ }
+ }
+
+ return ""
+ }
+
private fun askPasswords(accounts: MutableList) {
if (accounts.isNotEmpty()) {
val account = accounts.removeAt(0)
@@ -3315,8 +3703,8 @@ class MainActivity : AppCompatActivity() {
callVideoButton.visibility = View.INVISIBLE
callVideoButton.isEnabled = false
switchVideoLayout(true)
- hangupButton.visibility = View.VISIBLE
- hangupButton.isEnabled = true
+ hangupButton.visibility = View.INVISIBLE
+ hangupButton.isEnabled = false
if (Build.VERSION.SDK_INT < 31) {
Log.d(TAG, "Setting audio mode to MODE_IN_COMMUNICATION")
am.mode = AudioManager.MODE_IN_COMMUNICATION
@@ -3470,7 +3858,10 @@ class MainActivity : AppCompatActivity() {
//callVideoButton.visibility = View.VISIBLE
callVideoButton.visibility = View.INVISIBLE // modified by ritoseo
callVideoButton.isEnabled = true
+
+ binding.callBackground.visibility = View.GONE
hangupButton.visibility = View.INVISIBLE
+ binding.callBackground.visibility = View.GONE
answerButton.visibility = View.INVISIBLE
answerVideoButton.visibility = View.INVISIBLE
rejectButton.visibility = View.INVISIBLE
@@ -3501,8 +3892,10 @@ class MainActivity : AppCompatActivity() {
securityButton.visibility = View.INVISIBLE
diverter.visibility = View.GONE
callButton.visibility = View.INVISIBLE
- hangupButton.visibility = View.VISIBLE
- hangupButton.isEnabled = true
+
+ binding.callBackground.visibility = View.VISIBLE
+ hangupButton.visibility = View.INVISIBLE
+ hangupButton.isEnabled = false
answerButton.visibility = View.INVISIBLE
answerVideoButton.visibility = View.INVISIBLE
rejectButton.visibility = View.INVISIBLE
@@ -3528,6 +3921,8 @@ class MainActivity : AppCompatActivity() {
callButton.visibility = View.INVISIBLE
callVideoButton.visibility = View.INVISIBLE
switchVideoLayout(true)
+
+ binding.callBackground.visibility = View.GONE
hangupButton.visibility = View.INVISIBLE
answerButton.visibility = View.VISIBLE
answerButton.isEnabled = true
@@ -3603,8 +3998,9 @@ class MainActivity : AppCompatActivity() {
switchVideoLayout(true)
- hangupButton.visibility = View.VISIBLE
- hangupButton.isEnabled = true
+ binding.callBackground.visibility = View.VISIBLE
+ hangupButton.visibility = View.INVISIBLE
+ hangupButton.isEnabled = false
answerButton.visibility = View.INVISIBLE
answerVideoButton.visibility = View.INVISIBLE
rejectButton.visibility = View.INVISIBLE
diff --git a/app/src/main/kotlin/com/tutpro/baresip/plus/Utils.kt b/app/src/main/kotlin/com/tutpro/baresip/plus/Utils.kt
index 31e1da0..64ac2ef 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/plus/Utils.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/plus/Utils.kt
@@ -14,6 +14,8 @@ import android.graphics.Bitmap.createScaledBitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
import android.hardware.camera2.CameraAccessException
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
@@ -49,6 +51,9 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
import java.io.*
import java.lang.reflect.Method
import java.net.Inet4Address
@@ -1175,6 +1180,62 @@ object Utils {
}
}
+ fun rgb888ToBitmap(rgbData: ByteArray, width: Int, height: Int): Bitmap? {
+ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+ val pixels = IntArray(width * height)
+ for (i in pixels.indices) {
+ val b = rgbData[i * 3].toInt() and 0xFF
+ val g = rgbData[i * 3 + 1].toInt() and 0xFF
+ val r = rgbData[i * 3 + 2].toInt() and 0xFF
+ pixels[i] = 0xFF shl 24 or (r shl 16) or (g shl 8) or b
+ }
+ bitmap.setPixels(pixels, 0, width, 0, 0, width, height)
+ return bitmap
+ }
+ fun saveRGB888AsJPG(context: Context, rgb565Data: ByteBuffer, width: Int, height: Int, fileName: String) {
+ // RGB565 포맷의 빈 Bitmap 생성
+ val bitmap = rgb888ToBitmap(rgb565Data.array(), width, height)
+ if(bitmap == null)
+ return
+
+ try {
+ // 저장할 파일 경로 설정
+ val file = File(fileName)
+
+ try {
+ FileOutputStream(file).use { fos ->
+ // Bitmap을 JPG 형식으로 저장 (품질 90%)
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos)
+ fos.flush()
+ }
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ } catch(e: java.lang.Exception) {
+
+ }
+ }
+
+ fun saveDrawableAsJpg(context: Context, drawableResId: Int, filename: String): Boolean {
+ val drawable: Drawable? = ContextCompat.getDrawable(context, drawableResId)
+ if (drawable == null || drawable !is BitmapDrawable) {
+ return false
+ }
+
+ val bitmap: Bitmap = drawable.bitmap
+
+ val file = File(filename)
+ return try {
+ FileOutputStream(file).use { out ->
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)
+ }
+ true
+ } catch (e: Exception) {
+ e.printStackTrace()
+ false
+ }
+ }
+
fun copyFile(sourcePath: String, destinationPath: String): Boolean {
return try {
FileInputStream(sourcePath).use { input ->
@@ -1259,7 +1320,7 @@ object Utils {
}
}
- fun setAudioSourceDevice(dev : String) {
+ fun setAudioSourceDeviceV1(dev : String) {
propertySet("sys.rito.audio.input.device", dev)
//val process = Runtime.getRuntime().exec("ritosysc shell-order=killall audioserver")
val process = Runtime.getRuntime().exec("ritosysc shell-order=killall android.hardware.audio.service")
@@ -1267,6 +1328,22 @@ object Utils {
process.destroy()
}
+ fun setAudioSourceDevice(dev : String) {
+ propertySet("sys.ritosip.audio.input.device", dev)
+ if(dev == "usb") {
+ if(BaresipService.audioDeviceUsbId == -1) {
+ propertySet("sys.ritosip.audio.source", BaresipService.audioDeviceCodecId.toString())
+ propertySet("sys.ritosip.audio.source.volume", "0")
+ } else {
+ propertySet("sys.ritosip.audio.source", BaresipService.audioDeviceUsbId.toString())
+ propertySet("sys.ritosip.audio.source.volume", "100")
+ }
+ } else if(dev == "codec") {
+ propertySet("sys.ritosip.audio.source", BaresipService.audioDeviceCodecId.toString())
+ propertySet("sys.ritosip.audio.source.volume", "100")
+ }
+ }
+
fun checkNetworkIpType() {
val process = Runtime.getRuntime().exec("ritosysc shell-order=rm /mnt/obb/ip_static;grep STATIC /data/misc/ethernet/ipconfig.txt && touch /mnt/obb/ip_static")
process.waitFor()
@@ -1279,6 +1356,28 @@ object Utils {
}
}
+ fun runShellOrder(order : String) {
+ val process = Runtime.getRuntime().exec(order)
+ process.waitFor()
+ process.destroy()
+ }
+
+ fun setMicVolumeByMix(volume : Int) {
+ CoroutineScope(Dispatchers.Default).launch {
+ Utils.runShellOrderSuper("tinymix -D 3 \"Mic Capture Volume\" ${volume}")
+ BaresipService.micVolume = volume
+ Config.replaceVariable("mic_volume", volume.toString())
+ Config.save()
+ propertySet("sys.ritosip.mic.volume", volume.toString())
+ }
+ }
+
+ fun runShellOrderSuper(order : String) {
+ val process = Runtime.getRuntime().exec("ritosysc shell-order=" + order)
+ process.waitFor()
+ process.destroy()
+ }
+
fun save(dev : String) {
propertySet("sys.rito.audio.input.device", dev)
//val process = Runtime.getRuntime().exec("ritosysc shell-order=killall audioserver")
@@ -1315,7 +1414,17 @@ object Utils {
val file = File("/d/hdmirx/status") // 파일 경로 설정
file.bufferedReader().use { reader ->
val line = reader.readLine() // 첫 번째 줄 읽기
- return line.split(":")[1].trim()
+ val plugStatus = line.split(":")[1].trim()
+ if(plugStatus == "plugin") {
+ val line2 = reader.readLine() // 두 번째 줄 읽기
+ val lockStatus = line2.split(":")[1].trim().startsWith("Lock")
+
+ if (lockStatus) {
+ return "plugin"
+ }
+ }
+
+ return "plugout"
}
} else if(idx == 1) {
val file = File("/sys/module/lt6911uxc/parameters/status") // 파일 경로 설정
@@ -1334,13 +1443,21 @@ object Utils {
fun saveNumberToFile(path: String, number: Int) {
val file = File(path)
try {
+ //println("파일 존재 여부: ${file.exists()}, 디렉토리 여부: ${file.isDirectory}, 파일 여부: ${file.isFile}")
+ if(!file.exists()) {
+ file.createNewFile()
+ }
+
file.writeText(number.toString())
+
Log.d("FileSave", "숫자 저장 완료: $number")
RandomAccessFile(path, "rw").use { raf ->
raf.fd.sync() // eMMC에 확실히 기록
raf.close()
}
+
+ file.setWritable(true, false)
} catch (e: IOException) {
Log.e("FileSave", "파일 저장 실패 : " + e.toString())
}
diff --git a/app/src/main/res/drawable/backward.png b/app/src/main/res/drawable/backward.png
new file mode 100644
index 0000000..647e450
Binary files /dev/null and b/app/src/main/res/drawable/backward.png differ
diff --git a/app/src/main/res/drawable/bg_button_selector_2.xml b/app/src/main/res/drawable/bg_button_selector_2.xml
new file mode 100644
index 0000000..adf6267
--- /dev/null
+++ b/app/src/main/res/drawable/bg_button_selector_2.xml
@@ -0,0 +1,24 @@
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/hangup_big.xml b/app/src/main/res/drawable/hangup_big.xml
new file mode 100644
index 0000000..5095ea9
--- /dev/null
+++ b/app/src/main/res/drawable/hangup_big.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/layout.png b/app/src/main/res/drawable/layout.png
new file mode 100644
index 0000000..a3640cc
Binary files /dev/null and b/app/src/main/res/drawable/layout.png differ
diff --git a/app/src/main/res/drawable/layout1.png b/app/src/main/res/drawable/layout1.png
new file mode 100644
index 0000000..7ab492a
Binary files /dev/null and b/app/src/main/res/drawable/layout1.png differ
diff --git a/app/src/main/res/drawable/layout10.png b/app/src/main/res/drawable/layout10.png
new file mode 100644
index 0000000..406af34
Binary files /dev/null and b/app/src/main/res/drawable/layout10.png differ
diff --git a/app/src/main/res/drawable/layout2.png b/app/src/main/res/drawable/layout2.png
new file mode 100644
index 0000000..b1b7297
Binary files /dev/null and b/app/src/main/res/drawable/layout2.png differ
diff --git a/app/src/main/res/drawable/layout3.png b/app/src/main/res/drawable/layout3.png
new file mode 100644
index 0000000..c46ca07
Binary files /dev/null and b/app/src/main/res/drawable/layout3.png differ
diff --git a/app/src/main/res/drawable/layout4.png b/app/src/main/res/drawable/layout4.png
new file mode 100644
index 0000000..63351a6
Binary files /dev/null and b/app/src/main/res/drawable/layout4.png differ
diff --git a/app/src/main/res/drawable/layout5.png b/app/src/main/res/drawable/layout5.png
new file mode 100644
index 0000000..f4c3a1a
Binary files /dev/null and b/app/src/main/res/drawable/layout5.png differ
diff --git a/app/src/main/res/drawable/layout6.png b/app/src/main/res/drawable/layout6.png
new file mode 100644
index 0000000..4f29557
Binary files /dev/null and b/app/src/main/res/drawable/layout6.png differ
diff --git a/app/src/main/res/drawable/layout7.png b/app/src/main/res/drawable/layout7.png
new file mode 100644
index 0000000..02c82ae
Binary files /dev/null and b/app/src/main/res/drawable/layout7.png differ
diff --git a/app/src/main/res/drawable/layout8.png b/app/src/main/res/drawable/layout8.png
new file mode 100644
index 0000000..9586f54
Binary files /dev/null and b/app/src/main/res/drawable/layout8.png differ
diff --git a/app/src/main/res/drawable/layout9.png b/app/src/main/res/drawable/layout9.png
new file mode 100644
index 0000000..7515053
Binary files /dev/null and b/app/src/main/res/drawable/layout9.png differ
diff --git a/app/src/main/res/drawable/network.png b/app/src/main/res/drawable/network.png
new file mode 100644
index 0000000..337baa5
Binary files /dev/null and b/app/src/main/res/drawable/network.png differ
diff --git a/app/src/main/res/drawable/nocamera.png b/app/src/main/res/drawable/nocamera.png
new file mode 100644
index 0000000..56c77d2
Binary files /dev/null and b/app/src/main/res/drawable/nocamera.png differ
diff --git a/app/src/main/res/drawable/server.png b/app/src/main/res/drawable/server.png
new file mode 100644
index 0000000..bfb189e
Binary files /dev/null and b/app/src/main/res/drawable/server.png differ
diff --git a/app/src/main/res/drawable/volume.png b/app/src/main/res/drawable/volume.png
new file mode 100644
index 0000000..a5ccbd7
Binary files /dev/null and b/app/src/main/res/drawable/volume.png differ
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index e12b0c4..10c9540 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -19,6 +19,15 @@
app:srcCompat="@drawable/osvc_splash" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -771,10 +1004,549 @@
android:layout_margin="16dp"
android:paddingHorizontal="20dp"
android:paddingVertical="10dp"
+ android:nextFocusLeft="@id/btnApply"
+ android:nextFocusRight="@id/btnCancel"
android:text="취소하기"
android:textSize="24sp" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/distribution.video/baresip/lib/arm64-v8a/libbaresip.a b/distribution.video/baresip/lib/arm64-v8a/libbaresip.a
index fc97e5e..25e58a1 100644
Binary files a/distribution.video/baresip/lib/arm64-v8a/libbaresip.a and b/distribution.video/baresip/lib/arm64-v8a/libbaresip.a differ
diff --git a/distribution.video/ffmpeg/lib/arm64-v8a/libavdevice.so b/distribution.video/ffmpeg/lib/arm64-v8a/libavdevice.so
index 9812c5d..deac600 100644
Binary files a/distribution.video/ffmpeg/lib/arm64-v8a/libavdevice.so and b/distribution.video/ffmpeg/lib/arm64-v8a/libavdevice.so differ
diff --git a/distribution.video/webrtc/lib/arm64-v8a/libwebrtc.a b/distribution.video/webrtc/lib/arm64-v8a/libwebrtc.a
index 1e32d34..d0fe32e 100644
Binary files a/distribution.video/webrtc/lib/arm64-v8a/libwebrtc.a and b/distribution.video/webrtc/lib/arm64-v8a/libwebrtc.a differ