chore: prepare Android app for release
This commit is contained in:
@@ -6,7 +6,9 @@
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:allowBackup="false"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
@@ -26,18 +28,60 @@
|
||||
|
||||
<activity
|
||||
android:name=".ShareActivity"
|
||||
android:exported="true"
|
||||
android:exported="false"
|
||||
android:excludeFromRecents="true"
|
||||
android:finishOnTaskLaunch="true"
|
||||
android:taskAffinity=""
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@android:style/Theme.NoDisplay"/>
|
||||
|
||||
<activity-alias
|
||||
android:name=".ShareQueueAlias"
|
||||
android:targetActivity=".ShareActivity"
|
||||
android:exported="true"
|
||||
android:enabled="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:finishOnTaskLaunch="true"
|
||||
android:taskAffinity=""
|
||||
android:launchMode="singleTask"
|
||||
android:label="@string/share_queue_label"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
<meta-data
|
||||
android:name="pro.relaytv.SHARE_ENDPOINT_PATH"
|
||||
android:value="/smart" />
|
||||
<meta-data
|
||||
android:name="pro.relaytv.SHARE_SUCCESS_TEMPLATE"
|
||||
android:value="@string/share_queue_toast" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</activity-alias>
|
||||
|
||||
<activity-alias
|
||||
android:name=".SharePlayNowAlias"
|
||||
android:targetActivity=".ShareActivity"
|
||||
android:exported="true"
|
||||
android:enabled="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:finishOnTaskLaunch="true"
|
||||
android:taskAffinity=""
|
||||
android:launchMode="singleTask"
|
||||
android:label="@string/share_play_now_label"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
<meta-data
|
||||
android:name="pro.relaytv.SHARE_ENDPOINT_PATH"
|
||||
android:value="/play_now" />
|
||||
<meta-data
|
||||
android:name="pro.relaytv.SHARE_SUCCESS_TEMPLATE"
|
||||
android:value="@string/share_play_now_toast" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package pro.relaytv
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
@@ -25,7 +27,6 @@ import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import okhttp3.Call
|
||||
@@ -44,7 +45,6 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var web: WebView
|
||||
private lateinit var toolbar: MaterialToolbar
|
||||
private lateinit var swipeRefresh: SwipeRefreshLayout
|
||||
|
||||
private var activeBaseUrl: String? = null
|
||||
private var consecutiveHealthFailures = 0
|
||||
@@ -83,7 +83,6 @@ class MainActivity : AppCompatActivity() {
|
||||
val openServers = intent.getBooleanExtra("open_servers", false)
|
||||
toolbar = findViewById(R.id.toolbar)
|
||||
web = findViewById(R.id.web)
|
||||
swipeRefresh = findViewById(R.id.swipeRefresh)
|
||||
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
if (this@MainActivity::web.isInitialized && web.canGoBack()) {
|
||||
@@ -106,29 +105,25 @@ class MainActivity : AppCompatActivity() {
|
||||
loadActiveServer(forcePickerOnFailure = false, manualRefresh = true)
|
||||
true
|
||||
}
|
||||
R.id.action_privacy -> {
|
||||
openPrivacyPolicy()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
web.settings.javaScriptEnabled = true
|
||||
web.settings.domStorageEnabled = true
|
||||
web.settings.mediaPlaybackRequiresUserGesture = false
|
||||
web.settings.userAgentString = web.settings.userAgentString + " RelayTV/1.1.0"
|
||||
swipeRefresh.setOnRefreshListener {
|
||||
loadActiveServer(forcePickerOnFailure = false, manualRefresh = true)
|
||||
}
|
||||
configureWebView()
|
||||
|
||||
web.webChromeClient = WebChromeClient()
|
||||
web.webViewClient = object : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView, url: String) {
|
||||
mainFrameFailed = false
|
||||
swipeRefresh.isRefreshing = false
|
||||
}
|
||||
|
||||
override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceError) {
|
||||
if (request.isForMainFrame) {
|
||||
mainFrameFailed = true
|
||||
swipeRefresh.isRefreshing = false
|
||||
Toast.makeText(this@MainActivity, "Connection lost. Retrying…", Toast.LENGTH_SHORT).show()
|
||||
scheduleHeartbeat(1_500)
|
||||
}
|
||||
@@ -147,6 +142,26 @@ class MainActivity : AppCompatActivity() {
|
||||
loadServerBase(base.trimEnd('/'), forcePickerOnFailure = true, manualRefresh = false)
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
private fun configureWebView() {
|
||||
val versionName = runCatching {
|
||||
packageManager.getPackageInfo(packageName, 0).versionName
|
||||
}.getOrNull() ?: "dev"
|
||||
web.settings.javaScriptEnabled = true
|
||||
web.settings.domStorageEnabled = true
|
||||
web.settings.mediaPlaybackRequiresUserGesture = false
|
||||
web.settings.userAgentString = web.settings.userAgentString + " RelayTV/$versionName"
|
||||
}
|
||||
|
||||
private fun openPrivacyPolicy() {
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.privacy_policy_url)))
|
||||
runCatching {
|
||||
startActivity(browserIntent)
|
||||
}.onFailure {
|
||||
Toast.makeText(this, getString(R.string.privacy_open_failed), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
isInForeground = true
|
||||
@@ -173,13 +188,9 @@ class MainActivity : AppCompatActivity() {
|
||||
private fun loadServerBase(base: String, forcePickerOnFailure: Boolean, manualRefresh: Boolean) {
|
||||
val normalized = base.trimEnd('/')
|
||||
if (normalized.isBlank()) {
|
||||
swipeRefresh.isRefreshing = false
|
||||
return
|
||||
}
|
||||
activeBaseUrl = normalized
|
||||
if (manualRefresh || !initialLoadComplete) {
|
||||
swipeRefresh.isRefreshing = true
|
||||
}
|
||||
probeServerHealth(
|
||||
base = normalized,
|
||||
forcePickerOnFailure = forcePickerOnFailure,
|
||||
@@ -191,7 +202,6 @@ class MainActivity : AppCompatActivity() {
|
||||
private fun loadActiveServer(forcePickerOnFailure: Boolean, manualRefresh: Boolean) {
|
||||
val base = activeBaseUrl ?: HostStore.getActiveBaseUrl(this)?.trimEnd('/')
|
||||
if (base.isNullOrBlank()) {
|
||||
swipeRefresh.isRefreshing = false
|
||||
showServerPicker(force = true)
|
||||
return
|
||||
}
|
||||
@@ -248,7 +258,6 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
mainFrameFailed = false
|
||||
initialLoadComplete = true
|
||||
swipeRefresh.isRefreshing = false
|
||||
scheduleHeartbeat(HEARTBEAT_OK_MS)
|
||||
}
|
||||
}
|
||||
@@ -257,7 +266,6 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
private fun onServerHealthFailed(forcePickerOnFailure: Boolean, manualRefresh: Boolean) {
|
||||
consecutiveHealthFailures += 1
|
||||
swipeRefresh.isRefreshing = false
|
||||
|
||||
if (forcePickerOnFailure && !initialLoadComplete) {
|
||||
Toast.makeText(this, "Server not reachable. Switch servers.", Toast.LENGTH_LONG).show()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package pro.relaytv
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
@@ -10,6 +11,11 @@ import androidx.work.WorkManager
|
||||
|
||||
class ShareActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
private const val META_ENDPOINT_PATH = "pro.relaytv.SHARE_ENDPOINT_PATH"
|
||||
private const val META_SUCCESS_TEMPLATE = "pro.relaytv.SHARE_SUCCESS_TEMPLATE"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -26,10 +32,13 @@ class ShareActivity : AppCompatActivity() {
|
||||
|
||||
val shared = intent.getStringExtra(Intent.EXTRA_TEXT) ?: ""
|
||||
val url = extractUrl(shared) ?: run { finish(); return }
|
||||
val endpointPath = resolveMetaString(META_ENDPOINT_PATH).ifBlank { "/smart" }
|
||||
val successTemplate = resolveMetaString(META_SUCCESS_TEMPLATE).ifBlank { "Sent to \"%s\"" }
|
||||
|
||||
val input = Data.Builder()
|
||||
.putString(ShareWorker.KEY_BASE, base)
|
||||
.putString(ShareWorker.KEY_URL, url)
|
||||
.putString(ShareWorker.KEY_ENDPOINT_PATH, endpointPath)
|
||||
.build()
|
||||
|
||||
val req = OneTimeWorkRequestBuilder<ShareWorker>()
|
||||
@@ -38,10 +47,20 @@ class ShareActivity : AppCompatActivity() {
|
||||
|
||||
WorkManager.getInstance(this).enqueue(req)
|
||||
val serverName = HostStore.getActiveHost(this)?.name?.ifBlank { "Server" } ?: "Server"
|
||||
Toast.makeText(this, "Sent to \"$serverName\"", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, successTemplate.format(serverName), Toast.LENGTH_SHORT).show()
|
||||
finishAndRemoveTask()
|
||||
}
|
||||
|
||||
private fun resolveMetaString(key: String): String {
|
||||
return try {
|
||||
val flags = PackageManager.GET_META_DATA
|
||||
val info = packageManager.getActivityInfo(componentName, flags)
|
||||
info.metaData?.getString(key).orEmpty()
|
||||
} catch (_: Exception) {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractUrl(text: String): String? {
|
||||
val m = Regex("""https?://\S+""").find(text)
|
||||
return m?.value?.trim()?.trimEnd(')', ']', '>', '"', '\'')
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
@@ -16,6 +15,7 @@ class ShareWorker(appContext: Context, params: WorkerParameters) : Worker(appCon
|
||||
companion object {
|
||||
const val KEY_BASE = "base"
|
||||
const val KEY_URL = "url"
|
||||
const val KEY_ENDPOINT_PATH = "endpoint_path"
|
||||
private const val CHANNEL_ID = "relaytv_silent"
|
||||
private const val NOTIF_ID = 4242
|
||||
}
|
||||
@@ -23,9 +23,10 @@ class ShareWorker(appContext: Context, params: WorkerParameters) : Worker(appCon
|
||||
override fun doWork(): Result {
|
||||
val base = inputData.getString(KEY_BASE)?.trim()?.trimEnd('/') ?: return Result.failure()
|
||||
val url = inputData.getString(KEY_URL) ?: return Result.failure()
|
||||
val endpointPath = inputData.getString(KEY_ENDPOINT_PATH)?.trim()?.ifBlank { "/smart" } ?: "/smart"
|
||||
return try {
|
||||
val payload = JSONObject().put("url", url).toString()
|
||||
val req = Net.postJson(base + "/smart", payload)
|
||||
val req = Net.postJson(base + endpointPath, payload)
|
||||
|
||||
Net.client.newCall(req).execute().use { resp ->
|
||||
val body = resp.body?.string().orEmpty()
|
||||
@@ -57,10 +58,8 @@ class ShareWorker(appContext: Context, params: WorkerParameters) : Worker(appCon
|
||||
private fun postNotification(title: String, text: String, tapToOpen: Boolean) {
|
||||
val nm = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val ch = NotificationChannel(CHANNEL_ID, "RelayTV", NotificationManager.IMPORTANCE_LOW)
|
||||
nm.createNotificationChannel(ch)
|
||||
}
|
||||
val ch = NotificationChannel(CHANNEL_ID, "RelayTV", NotificationManager.IMPORTANCE_LOW)
|
||||
nm.createNotificationChannel(ch)
|
||||
|
||||
val builder = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
|
||||
.setSmallIcon(android.R.drawable.ic_media_play)
|
||||
@@ -78,7 +77,7 @@ class ShareWorker(appContext: Context, params: WorkerParameters) : Worker(appCon
|
||||
applicationContext,
|
||||
0,
|
||||
openIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or (if (Build.VERSION.SDK_INT >= 23) PendingIntent.FLAG_IMMUTABLE else 0)
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
builder.setContentIntent(pending)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="120"
|
||||
android:viewportHeight="120">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M12.98,22.42v74.54c0,5.48 2.19,9.87 6.58,12.06 4.38,2.19 8.77,2.19 13.15,0l70.15,-37.27c4.38,-2.19 6.58,-6.58 6.58,-12.06 0,-5.48 -2.19,-9.87 -6.58,-12.06L32.71,10.36C23.47,5.74 12.98,12.99 12.98,22.42z" />
|
||||
<path
|
||||
android:fillColor="#1B2439"
|
||||
android:pathData="M16.98,25.69v68c0,5 2,9 6,11 4,2 8,2 12,0l64,-34c4,-2 6,-6 6,-11 0,-5 -2,-9 -6,-11l-64,-34c-8.43,-4.22 -18,2.41 -18,11z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M64.89,39.13C61.32,36.63 38.94,24.09 34.01,21.83c-5.76,-2.64 -10.27,1.85 -9.88,5.88 0.17,1.77 0.18,57.55 0.21,63.09 0.04,8.03 5.25,8.1 8.46,7.16 5.14,-1.41 28.58,-15.18 29.63,-15.78"
|
||||
android:strokeColor="#36D9F4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="5" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M66.28,72.15C65.1,71.98 52.37,69.37 50.4,69.24M67.69,50.59C65.1,51.84 49.2,61.51 49.2,61.51"
|
||||
android:strokeColor="#2D89FF"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeWidth="4" />
|
||||
<path
|
||||
android:fillColor="#2D89FF"
|
||||
android:pathData="M73.01,66.99a6.97,6.97 0 1,1 0,13.93a6.97,6.97 0 1,1 0,-13.93z" />
|
||||
<path
|
||||
android:fillColor="#36D9F4"
|
||||
android:pathData="M44.86,55.91a9.96,9.96 0 1,1 0,19.92a9.96,9.96 0 1,1 0,-19.92z" />
|
||||
<path
|
||||
android:fillColor="#2D89FF"
|
||||
android:pathData="M74.24,41.12a7,7 0 1,1 0,14a7,7 0 1,1 0,-14z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="120"
|
||||
android:viewportHeight="120">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M16.98,25.69v68c0,5 2,9 6,11 4,2 8,2 12,0l64,-34c4,-2 6,-6 6,-11 0,-5 -2,-9 -6,-11l-64,-34c-8.43,-4.22 -18,2.41 -18,11z"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="6" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M64.89,39.13C61.32,36.63 38.94,24.09 34.01,21.83c-5.76,-2.64 -10.27,1.85 -9.88,5.88 0.17,1.77 0.18,57.55 0.21,63.09 0.04,8.03 5.25,8.1 8.46,7.16 5.14,-1.41 28.58,-15.18 29.63,-15.78"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="5" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M66.28,72.15C65.1,71.98 52.37,69.37 50.4,69.24M67.69,50.59C65.1,51.84 49.2,61.51 49.2,61.51"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeWidth="4" />
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M73.01,66.99a6.97,6.97 0 1,1 0,13.93a6.97,6.97 0 1,1 0,-13.93z" />
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M44.86,55.91a9.96,9.96 0 1,1 0,19.92a9.96,9.96 0 1,1 0,-19.92z" />
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M74.24,41.12a7,7 0 1,1 0,14a7,7 0 1,1 0,-14z" />
|
||||
</vector>
|
||||
@@ -2,8 +2,7 @@
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/relaytv_surface">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -18,16 +17,10 @@
|
||||
app:menu="@menu/main_menu" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefresh"
|
||||
<WebView
|
||||
android:id="@+id/web"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<WebView
|
||||
android:id="@+id/web"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="RelayTV Server"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold"
|
||||
android:paddingBottom="10dp"/>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Base URL (HTTP or HTTPS). Example: http://nuc.lan:8787"
|
||||
android:textSize="14sp"
|
||||
android:paddingBottom="8dp"/>
|
||||
<EditText
|
||||
android:id="@+id/editBaseUrl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="http://nuc.lan:8787"
|
||||
android:inputType="textUri"
|
||||
android:singleLine="true"/>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="14dp">
|
||||
<Button
|
||||
android:id="@+id/btnSave"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Save"/>
|
||||
<Space
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="wrap_content"/>
|
||||
<Button
|
||||
android:id="@+id/btnDetect"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Auto-detect"/>
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:id="@+id/txtStatus"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textSize="13sp"
|
||||
android:paddingTop="10dp"/>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Tip: Share a link from BravePipe → RelayTV. It sends to /smart in the background and shows a notification (tap it to open UI)."
|
||||
android:textSize="13sp"
|
||||
android:paddingTop="16dp"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -11,4 +11,8 @@
|
||||
android:title="@string/reload"
|
||||
android:icon="@android:drawable/ic_popup_sync"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/action_privacy"
|
||||
android:title="@string/privacy_policy"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/relaytv_surface" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/relaytv_surface" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
@@ -21,4 +21,11 @@
|
||||
<string name="status_offline">Offline</string>
|
||||
<string name="status_checking">Checking…</string>
|
||||
<string name="status_active_prefix">Active</string>
|
||||
<string name="share_queue_label">RelayTV Queue</string>
|
||||
<string name="share_play_now_label">RelayTV Play</string>
|
||||
<string name="share_queue_toast">Queued on "%s"</string>
|
||||
<string name="share_play_now_toast">Playing now on "%s"</string>
|
||||
<string name="privacy_policy">Privacy policy</string>
|
||||
<string name="privacy_policy_url">https://github.com/mcgeezy/relaytv-android/blob/main/docs/PRIVACY_POLICY.md</string>
|
||||
<string name="privacy_open_failed">No browser available to open the privacy policy.</string>
|
||||
</resources>
|
||||
|
||||
@@ -10,12 +10,4 @@
|
||||
|
||||
</style>
|
||||
<style name="Theme.RelayTV.NoActionBar" parent="Theme.Material3.DayNight.NoActionBar" />
|
||||
|
||||
<style name="Theme.RelayTV.Share" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:colorBackground">@android:color/transparent</item>
|
||||
<item name="android:backgroundDimEnabled">false</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<full-backup-content>
|
||||
<exclude domain="root" path="." />
|
||||
</full-backup-content>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<exclude domain="root" path="." />
|
||||
</cloud-backup>
|
||||
<device-transfer>
|
||||
<exclude domain="root" path="." />
|
||||
</device-transfer>
|
||||
</data-extraction-rules>
|
||||
@@ -1,4 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config cleartextTrafficPermitted="true" />
|
||||
<network-security-config xmlns:tools="http://schemas.android.com/tools">
|
||||
<base-config
|
||||
cleartextTrafficPermitted="true"
|
||||
tools:ignore="InsecureBaseConfiguration" />
|
||||
</network-security-config>
|
||||
|
||||
Reference in New Issue
Block a user