mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-08-03 14:50:49 +00:00
Added support for connecting to FCast via QR code.
This commit is contained in:
parent
23d1085755
commit
f90290c4ec
6 changed files with 234 additions and 78 deletions
|
@ -61,6 +61,14 @@
|
||||||
|
|
||||||
<data android:scheme="grayjay" />
|
<data android:scheme="grayjay" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="fcast" />
|
||||||
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.futo.platformplayer
|
package com.futo.platformplayer
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
@ -337,6 +338,10 @@ class UIDialogs {
|
||||||
} else {
|
} else {
|
||||||
val dialog = ConnectCastingDialog(context);
|
val dialog = ConnectCastingDialog(context);
|
||||||
registerDialogOpened(dialog);
|
registerDialogOpened(dialog);
|
||||||
|
val c = context
|
||||||
|
if (c is Activity) {
|
||||||
|
dialog.setOwnerActivity(c);
|
||||||
|
}
|
||||||
dialog.setOnDismissListener { registerDialogClosed(dialog) };
|
dialog.setOnDismissListener { registerDialogClosed(dialog) };
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import android.content.pm.ActivityInfo
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.preference.PreferenceManager
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -25,11 +24,9 @@ import androidx.fragment.app.FragmentContainerView
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.futo.platformplayer.*
|
import com.futo.platformplayer.*
|
||||||
import com.futo.platformplayer.api.media.PlatformID
|
|
||||||
import com.futo.platformplayer.api.media.models.channels.SerializedChannel
|
|
||||||
import com.futo.platformplayer.casting.StateCasting
|
import com.futo.platformplayer.casting.StateCasting
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
import com.futo.platformplayer.constructs.Event3
|
import com.futo.platformplayer.dialogs.ConnectCastingDialog
|
||||||
import com.futo.platformplayer.fragment.mainactivity.bottombar.MenuBottomBarFragment
|
import com.futo.platformplayer.fragment.mainactivity.bottombar.MenuBottomBarFragment
|
||||||
import com.futo.platformplayer.fragment.mainactivity.main.*
|
import com.futo.platformplayer.fragment.mainactivity.main.*
|
||||||
import com.futo.platformplayer.fragment.mainactivity.topbar.AddTopBarFragment
|
import com.futo.platformplayer.fragment.mainactivity.topbar.AddTopBarFragment
|
||||||
|
@ -45,6 +42,7 @@ import com.futo.platformplayer.stores.FragmentedStorage
|
||||||
import com.futo.platformplayer.stores.SubscriptionStorage
|
import com.futo.platformplayer.stores.SubscriptionStorage
|
||||||
import com.futo.platformplayer.stores.v2.ManagedStore
|
import com.futo.platformplayer.stores.v2.ManagedStore
|
||||||
import com.google.gson.JsonParser
|
import com.google.gson.JsonParser
|
||||||
|
import com.google.zxing.integration.android.IntentIntegrator
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
@ -124,6 +122,24 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
private var _isVisible = true;
|
private var _isVisible = true;
|
||||||
private var _wasStopped = false;
|
private var _wasStopped = false;
|
||||||
|
|
||||||
|
private val _urlQrCodeResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
|
val scanResult = IntentIntegrator.parseActivityResult(result.resultCode, result.data)
|
||||||
|
scanResult?.let {
|
||||||
|
val content = it.contents
|
||||||
|
if (content == null) {
|
||||||
|
UIDialogs.toast(this, getString(R.string.failed_to_scan_qr_code))
|
||||||
|
return@let
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
handleUrlAll(content)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Logger.i(TAG, "Failed to handle URL.", e)
|
||||||
|
UIDialogs.toast(this, "Failed to handle URL: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constructor() : super() {
|
constructor() : super() {
|
||||||
Thread.setDefaultUncaughtExceptionHandler { _, throwable ->
|
Thread.setDefaultUncaughtExceptionHandler { _, throwable ->
|
||||||
val writer = StringWriter();
|
val writer = StringWriter();
|
||||||
|
@ -409,6 +425,23 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
UIDialogs.toast(this, "No external file permissions\nExporting and auto backups will not work");
|
UIDialogs.toast(this, "No external file permissions\nExporting and auto backups will not work");
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
fun showUrlQrCodeScanner() {
|
||||||
|
try {
|
||||||
|
val integrator = IntentIntegrator(this)
|
||||||
|
integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE)
|
||||||
|
integrator.setPrompt(getString(R.string.scan_a_qr_code))
|
||||||
|
integrator.setOrientationLocked(true);
|
||||||
|
integrator.setCameraId(0)
|
||||||
|
integrator.setBeepEnabled(false)
|
||||||
|
integrator.setBarcodeImageEnabled(true)
|
||||||
|
integrator.captureActivity = QRCaptureActivity::class.java
|
||||||
|
_urlQrCodeResultLauncher.launch(integrator.createScanIntent())
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Logger.i(TAG, "Failed to handle show QR scanner.", e)
|
||||||
|
UIDialogs.toast(this, "Failed to show QR scanner: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
Logger.v(TAG, "onResume")
|
Logger.v(TAG, "onResume")
|
||||||
|
@ -496,10 +529,20 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (targetData != null) {
|
if (targetData != null) {
|
||||||
when(intent.scheme) {
|
handleUrlAll(targetData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(ex: Throwable) {
|
||||||
|
UIDialogs.showGeneralErrorDialog(this, getString(R.string.failed_to_handle_file), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleUrlAll(url: String) {
|
||||||
|
val uri = Uri.parse(url)
|
||||||
|
when (uri.scheme) {
|
||||||
"grayjay" -> {
|
"grayjay" -> {
|
||||||
if(targetData.startsWith("grayjay://license/")) {
|
if(url.startsWith("grayjay://license/")) {
|
||||||
if(StatePayment.instance.setPaymentLicenseUrl(targetData))
|
if(StatePayment.instance.setPaymentLicenseUrl(url))
|
||||||
{
|
{
|
||||||
UIDialogs.showDialogOk(this, R.drawable.ic_check, getString(R.string.your_license_key_has_been_set_an_app_restart_might_be_required));
|
UIDialogs.showDialogOk(this, R.drawable.ic_check, getString(R.string.your_license_key_has_been_set_an_app_restart_might_be_required));
|
||||||
|
|
||||||
|
@ -510,68 +553,73 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
UIDialogs.toast(getString(R.string.invalid_license_format));
|
UIDialogs.toast(getString(R.string.invalid_license_format));
|
||||||
|
|
||||||
}
|
}
|
||||||
else if(targetData.startsWith("grayjay://plugin/")) {
|
else if(url.startsWith("grayjay://plugin/")) {
|
||||||
val intent = Intent(this, AddSourceActivity::class.java).apply {
|
val intent = Intent(this, AddSourceActivity::class.java).apply {
|
||||||
data = Uri.parse(targetData.substring("grayjay://plugin/".length));
|
data = Uri.parse(url.substring("grayjay://plugin/".length));
|
||||||
};
|
};
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
else if(targetData.startsWith("grayjay://video/")) {
|
else if(url.startsWith("grayjay://video/")) {
|
||||||
val videoUrl = targetData.substring("grayjay://video/".length);
|
val videoUrl = url.substring("grayjay://video/".length);
|
||||||
navigate(_fragVideoDetail, videoUrl);
|
navigate(_fragVideoDetail, videoUrl);
|
||||||
}
|
}
|
||||||
else if(targetData.startsWith("grayjay://channel/")) {
|
else if(url.startsWith("grayjay://channel/")) {
|
||||||
val channelUrl = targetData.substring("grayjay://channel/".length);
|
val channelUrl = url.substring("grayjay://channel/".length);
|
||||||
navigate(_fragMainChannel, channelUrl);
|
navigate(_fragMainChannel, channelUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"content" -> {
|
"content" -> {
|
||||||
if(!handleContent(targetData, intent.type)) {
|
if(!handleContent(url, intent.type)) {
|
||||||
UIDialogs.showSingleButtonDialog(
|
UIDialogs.showSingleButtonDialog(
|
||||||
this,
|
this,
|
||||||
R.drawable.ic_play,
|
R.drawable.ic_play,
|
||||||
getString(R.string.unknown_content_format) + " [${targetData}]",
|
getString(R.string.unknown_content_format) + " [${url}]",
|
||||||
"Ok",
|
"Ok",
|
||||||
{ });
|
{ });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"file" -> {
|
"file" -> {
|
||||||
if(!handleFile(targetData)) {
|
if(!handleFile(url)) {
|
||||||
UIDialogs.showSingleButtonDialog(
|
UIDialogs.showSingleButtonDialog(
|
||||||
this,
|
this,
|
||||||
R.drawable.ic_play,
|
R.drawable.ic_play,
|
||||||
getString(R.string.unknown_file_format) + " [${targetData}]",
|
getString(R.string.unknown_file_format) + " [${url}]",
|
||||||
"Ok",
|
"Ok",
|
||||||
{ });
|
{ });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"polycentric" -> {
|
"polycentric" -> {
|
||||||
if(!handlePolycentric(targetData)) {
|
if(!handlePolycentric(url)) {
|
||||||
UIDialogs.showSingleButtonDialog(
|
UIDialogs.showSingleButtonDialog(
|
||||||
this,
|
this,
|
||||||
R.drawable.ic_play,
|
R.drawable.ic_play,
|
||||||
getString(R.string.unknown_polycentric_format) + " [${targetData}]",
|
getString(R.string.unknown_polycentric_format) + " [${url}]",
|
||||||
|
"Ok",
|
||||||
|
{ });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"fcast" -> {
|
||||||
|
if(!handleFCast(url)) {
|
||||||
|
UIDialogs.showSingleButtonDialog(
|
||||||
|
this,
|
||||||
|
R.drawable.ic_cast,
|
||||||
|
"Unknown FCast format [${url}]",
|
||||||
"Ok",
|
"Ok",
|
||||||
{ });
|
{ });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if (!handleUrl(targetData)) {
|
if (!handleUrl(url)) {
|
||||||
UIDialogs.showSingleButtonDialog(
|
UIDialogs.showSingleButtonDialog(
|
||||||
this,
|
this,
|
||||||
R.drawable.ic_play,
|
R.drawable.ic_play,
|
||||||
getString(R.string.unknown_url_format) + " [${targetData}]",
|
getString(R.string.unknown_url_format) + " [${url}]",
|
||||||
"Ok",
|
"Ok",
|
||||||
{ });
|
{ });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch(ex: Throwable) {
|
|
||||||
UIDialogs.showGeneralErrorDialog(this, getString(R.string.failed_to_handle_file), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun handleUrl(url: String): Boolean {
|
fun handleUrl(url: String): Boolean {
|
||||||
Logger.i(TAG, "handleUrl(url=$url)")
|
Logger.i(TAG, "handleUrl(url=$url)")
|
||||||
|
@ -719,6 +767,20 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
startActivity(Intent(this, PolycentricImportProfileActivity::class.java).apply { putExtra("url", url) })
|
startActivity(Intent(this, PolycentricImportProfileActivity::class.java).apply { putExtra("url", url) })
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun handleFCast(url: String): Boolean {
|
||||||
|
Logger.i(TAG, "handleFCast");
|
||||||
|
|
||||||
|
try {
|
||||||
|
StateCasting.instance.handleUrl(this, url)
|
||||||
|
return true;
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Log.e(TAG, "Failed to parse FCast URL '${url}'.", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
private fun readSharedContent(contentPath: String): ByteArray {
|
private fun readSharedContent(contentPath: String): ByteArray {
|
||||||
return contentResolver.openInputStream(Uri.parse(contentPath))?.use {
|
return contentResolver.openInputStream(Uri.parse(contentPath))?.use {
|
||||||
return it.readBytes();
|
return it.readBytes();
|
||||||
|
|
|
@ -2,8 +2,11 @@ package com.futo.platformplayer.casting
|
||||||
|
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.util.Base64
|
||||||
import com.futo.platformplayer.*
|
import com.futo.platformplayer.*
|
||||||
|
import com.futo.platformplayer.activities.MainActivity
|
||||||
import com.futo.platformplayer.api.http.ManagedHttpClient
|
import com.futo.platformplayer.api.http.ManagedHttpClient
|
||||||
import com.futo.platformplayer.api.http.server.ManagedHttpServer
|
import com.futo.platformplayer.api.http.server.ManagedHttpServer
|
||||||
import com.futo.platformplayer.api.http.server.handlers.*
|
import com.futo.platformplayer.api.http.server.handlers.*
|
||||||
|
@ -27,6 +30,9 @@ import javax.jmdns.ServiceListener
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
import com.futo.platformplayer.stores.CastingDeviceInfoStorage
|
import com.futo.platformplayer.stores.CastingDeviceInfoStorage
|
||||||
import com.futo.platformplayer.stores.FragmentedStorage
|
import com.futo.platformplayer.stores.FragmentedStorage
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import javax.jmdns.ServiceTypeListener
|
import javax.jmdns.ServiceTypeListener
|
||||||
|
|
||||||
class StateCasting {
|
class StateCasting {
|
||||||
|
@ -147,6 +153,32 @@ class StateCasting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun handleUrl(context: Context, url: String) {
|
||||||
|
val uri = Uri.parse(url)
|
||||||
|
if (uri.scheme != "fcast") {
|
||||||
|
throw Exception("Expected scheme to be FCast")
|
||||||
|
}
|
||||||
|
|
||||||
|
val type = uri.host
|
||||||
|
if (type != "r") {
|
||||||
|
throw Exception("Expected type r")
|
||||||
|
}
|
||||||
|
|
||||||
|
val connectionInfo = uri.pathSegments[0]
|
||||||
|
val json = Base64.decode(connectionInfo, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP).toString(Charsets.UTF_8)
|
||||||
|
val networkConfig = Json.decodeFromString<FCastNetworkConfig>(json)
|
||||||
|
val tcpService = networkConfig.services.first { v -> v.type == 0 }
|
||||||
|
|
||||||
|
addRememberedDevice(CastingDeviceInfo(
|
||||||
|
name = networkConfig.name,
|
||||||
|
type = CastProtocolType.FCAST,
|
||||||
|
addresses = networkConfig.addresses.toTypedArray(),
|
||||||
|
port = tcpService.port
|
||||||
|
))
|
||||||
|
|
||||||
|
UIDialogs.toast(context,"FCast device '${networkConfig.name}' added")
|
||||||
|
}
|
||||||
|
|
||||||
fun onStop() {
|
fun onStop() {
|
||||||
val ad = activeDevice ?: return;
|
val ad = activeDevice ?: return;
|
||||||
Logger.i(TAG, "Stopping active device because of onStop.");
|
Logger.i(TAG, "Stopping active device because of onStop.");
|
||||||
|
@ -1167,6 +1199,19 @@ class StateCasting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class FCastNetworkConfig(
|
||||||
|
val name: String,
|
||||||
|
val addresses: List<String>,
|
||||||
|
val services: List<FCastService>
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class FCastService(
|
||||||
|
val port: Int,
|
||||||
|
val type: Int
|
||||||
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val instance: StateCasting = StateCasting();
|
val instance: StateCasting = StateCasting();
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,33 @@
|
||||||
package com.futo.platformplayer.dialogs
|
package com.futo.platformplayer.dialogs
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.graphics.drawable.Animatable
|
import android.graphics.drawable.Animatable
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.UIDialogs
|
import com.futo.platformplayer.UIDialogs
|
||||||
|
import com.futo.platformplayer.activities.AddSourceActivity
|
||||||
|
import com.futo.platformplayer.activities.MainActivity
|
||||||
|
import com.futo.platformplayer.activities.QRCaptureActivity
|
||||||
import com.futo.platformplayer.casting.CastConnectionState
|
import com.futo.platformplayer.casting.CastConnectionState
|
||||||
import com.futo.platformplayer.casting.CastingDevice
|
import com.futo.platformplayer.casting.CastingDevice
|
||||||
import com.futo.platformplayer.casting.StateCasting
|
import com.futo.platformplayer.casting.StateCasting
|
||||||
import com.futo.platformplayer.states.StateApp
|
import com.futo.platformplayer.states.StateApp
|
||||||
import com.futo.platformplayer.views.adapters.DeviceAdapter
|
import com.futo.platformplayer.views.adapters.DeviceAdapter
|
||||||
|
import com.google.zxing.integration.android.IntentIntegrator
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -28,6 +37,7 @@ class ConnectCastingDialog(context: Context?) : AlertDialog(context) {
|
||||||
private lateinit var _imageLoader: ImageView;
|
private lateinit var _imageLoader: ImageView;
|
||||||
private lateinit var _buttonClose: Button;
|
private lateinit var _buttonClose: Button;
|
||||||
private lateinit var _buttonAdd: Button;
|
private lateinit var _buttonAdd: Button;
|
||||||
|
private lateinit var _buttonScanQR: Button;
|
||||||
private lateinit var _textNoDevicesFound: TextView;
|
private lateinit var _textNoDevicesFound: TextView;
|
||||||
private lateinit var _textNoDevicesRemembered: TextView;
|
private lateinit var _textNoDevicesRemembered: TextView;
|
||||||
private lateinit var _recyclerDevices: RecyclerView;
|
private lateinit var _recyclerDevices: RecyclerView;
|
||||||
|
@ -44,6 +54,7 @@ class ConnectCastingDialog(context: Context?) : AlertDialog(context) {
|
||||||
_imageLoader = findViewById(R.id.image_loader);
|
_imageLoader = findViewById(R.id.image_loader);
|
||||||
_buttonClose = findViewById(R.id.button_close);
|
_buttonClose = findViewById(R.id.button_close);
|
||||||
_buttonAdd = findViewById(R.id.button_add);
|
_buttonAdd = findViewById(R.id.button_add);
|
||||||
|
_buttonScanQR = findViewById(R.id.button_scan_qr);
|
||||||
_recyclerDevices = findViewById(R.id.recycler_devices);
|
_recyclerDevices = findViewById(R.id.recycler_devices);
|
||||||
_recyclerRememberedDevices = findViewById(R.id.recycler_remembered_devices);
|
_recyclerRememberedDevices = findViewById(R.id.recycler_remembered_devices);
|
||||||
_textNoDevicesFound = findViewById(R.id.text_no_devices_found);
|
_textNoDevicesFound = findViewById(R.id.text_no_devices_found);
|
||||||
|
@ -77,6 +88,17 @@ class ConnectCastingDialog(context: Context?) : AlertDialog(context) {
|
||||||
UIDialogs.showCastingAddDialog(context);
|
UIDialogs.showCastingAddDialog(context);
|
||||||
dismiss();
|
dismiss();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
val c = ownerActivity
|
||||||
|
if (c is MainActivity) {
|
||||||
|
_buttonScanQR.visibility = View.VISIBLE
|
||||||
|
_buttonScanQR.setOnClickListener {
|
||||||
|
c.showUrlQrCodeScanner()
|
||||||
|
dismiss()
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
_buttonScanQR.visibility = View.GONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun show() {
|
override fun show() {
|
||||||
|
|
|
@ -89,18 +89,32 @@
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:fontFamily="@font/inter_regular" />
|
android:fontFamily="@font/inter_regular" />
|
||||||
|
|
||||||
<Space android:layout_width="0dp"
|
<Button
|
||||||
android:layout_height="match_parent"
|
android:id="@+id/button_scan_qr"
|
||||||
android:layout_weight="1" />
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1.7"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/scan_qr"
|
||||||
|
android:textSize="14dp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:layout_marginEnd="2dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:fontFamily="@font/inter_regular"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:background="@color/transparent" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/button_add"
|
android:id="@+id/button_add"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/add"
|
android:text="@string/add"
|
||||||
android:textSize="14dp"
|
android:textSize="14dp"
|
||||||
android:textAlignment="textEnd"
|
android:textAlignment="textEnd"
|
||||||
android:layout_marginEnd="2dp"
|
android:layout_marginEnd="2dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
android:fontFamily="@font/inter_regular"
|
android:fontFamily="@font/inter_regular"
|
||||||
android:textColor="@color/colorPrimary"
|
android:textColor="@color/colorPrimary"
|
||||||
android:background="@color/transparent" />
|
android:background="@color/transparent" />
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue