Rename app to Castarr (dev.castarr.tv), add JDK toolchain resolution

Package/applicationId com.nodecast.tv -> dev.castarr.tv, all branding in app
and remote page renamed, remote localStorage key changed. Foojay toolchain
resolver + jvmToolchain(17) so hosts with only a JRE can build.

Closes #1

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
be-nj
2026-08-25 00:17:59 +02:00
parent a11b5a68c0
commit e2822d6f6f
14 changed files with 39 additions and 28 deletions

View File

@@ -4,11 +4,11 @@ plugins {
} }
android { android {
namespace = "com.nodecast.tv" namespace = "dev.castarr.tv"
compileSdk = 35 compileSdk = 35
defaultConfig { defaultConfig {
applicationId = "com.nodecast.tv" applicationId = "dev.castarr.tv"
minSdk = 23 minSdk = 23
targetSdk = 35 targetSdk = 35
versionCode = 1 versionCode = 1
@@ -32,6 +32,7 @@ android {
allWarningsAsErrors = true allWarningsAsErrors = true
} }
lint { lint {
// Media3's UI classes are @UnstableApi; the opt-in lint check would // Media3's UI classes are @UnstableApi; the opt-in lint check would
// otherwise fail release builds. // otherwise fail release builds.
@@ -49,3 +50,9 @@ dependencies {
implementation("org.nanohttpd:nanohttpd-websocket:2.3.1") implementation("org.nanohttpd:nanohttpd-websocket:2.3.1")
implementation("com.google.zxing:core:3.5.3") implementation("com.google.zxing:core:3.5.3")
} }
// Full JDK for javac via toolchain (host may only have a JRE); resolved by
// the foojay convention plugin in settings.
kotlin {
jvmToolchain(17)
}

View File

@@ -15,7 +15,7 @@
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
android:theme="@style/Theme.NodeCast"> android:theme="@style/Theme.Castarr">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"

View File

@@ -4,7 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="theme-color" content="#0a0b0d"> <meta name="theme-color" content="#0a0b0d">
<title>NodeCast Remote</title> <title>Castarr Remote</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600&display=swap"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600&display=swap">
<style> <style>
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; } * { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
@@ -149,7 +149,7 @@
<header> <header>
<div class="wordmark"> <div class="wordmark">
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#5fd4c4" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="5" width="20" height="13" rx="2"/><path d="M8.5 22h7"/></svg> <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#5fd4c4" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="5" width="20" height="13" rx="2"/><path d="M8.5 22h7"/></svg>
NodeCast Castarr
</div> </div>
<div class="tvchip"><span class="dot off" id="conn-dot"></span><span id="tv-name">Getrennt</span></div> <div class="tvchip"><span class="dot off" id="conn-dot"></span><span id="tv-name">Getrennt</span></div>
</header> </header>
@@ -248,10 +248,10 @@
// --- pairing credentials --- // --- pairing credentials ---
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
if (params.get('t')) { if (params.get('t')) {
localStorage.setItem('nodecast_token', params.get('t')); localStorage.setItem('castarr_token', params.get('t'));
history.replaceState(null, '', location.pathname); history.replaceState(null, '', location.pathname);
} }
const getToken = () => localStorage.getItem('nodecast_token') || ''; const getToken = () => localStorage.getItem('castarr_token') || '';
let manualCode = ''; let manualCode = '';
const deviceName = (() => { const deviceName = (() => {
@@ -312,7 +312,7 @@
case 'welcome': case 'welcome':
state.authorized = true; state.authorized = true;
state.retryDelay = 1000; state.retryDelay = 1000;
if (msg.token) localStorage.setItem('nodecast_token', msg.token); if (msg.token) localStorage.setItem('castarr_token', msg.token);
$('tv-name').textContent = msg.device || 'TV'; $('tv-name').textContent = msg.device || 'TV';
if (msg.status) { state.status = msg.status; } if (msg.status) { state.status = msg.status; }
if (msg.channels) { state.channels = msg.channels; } if (msg.channels) { state.channels = msg.channels; }
@@ -336,7 +336,7 @@
break; break;
case 'error': case 'error':
if (msg.error === 'bad_code' || msg.error === 'rate_limited') { if (msg.error === 'bad_code' || msg.error === 'rate_limited') {
localStorage.removeItem('nodecast_token'); localStorage.removeItem('castarr_token');
manualCode = ''; manualCode = '';
$('pair-error').textContent = msg.error === 'rate_limited' $('pair-error').textContent = msg.error === 'rate_limited'
? 'Zu viele Versuche — kurz warten.' : 'Falscher Code.'; ? 'Zu viele Versuche — kurz warten.' : 'Falscher Code.';

View File

@@ -1,4 +1,4 @@
package com.nodecast.tv package dev.castarr.tv
import android.app.Activity import android.app.Activity
import android.graphics.Color import android.graphics.Color
@@ -11,12 +11,12 @@ import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.media3.ui.PlayerView import androidx.media3.ui.PlayerView
import com.nodecast.tv.pairing.Pairing import dev.castarr.tv.pairing.Pairing
import com.nodecast.tv.pairing.Qr import dev.castarr.tv.pairing.Qr
import com.nodecast.tv.player.PlayerController import dev.castarr.tv.player.PlayerController
import com.nodecast.tv.playlist.Channel import dev.castarr.tv.playlist.Channel
import com.nodecast.tv.playlist.PlaylistRepository import dev.castarr.tv.playlist.PlaylistRepository
import com.nodecast.tv.server.ControlServer import dev.castarr.tv.server.ControlServer
import org.json.JSONObject import org.json.JSONObject
class MainActivity : Activity(), ControlServer.Listener { class MainActivity : Activity(), ControlServer.Listener {

View File

@@ -1,4 +1,4 @@
package com.nodecast.tv.pairing package dev.castarr.tv.pairing
import android.content.Context import android.content.Context
import java.net.Inet4Address import java.net.Inet4Address

View File

@@ -1,4 +1,4 @@
package com.nodecast.tv.pairing package dev.castarr.tv.pairing
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color

View File

@@ -1,4 +1,4 @@
package com.nodecast.tv.player package dev.castarr.tv.player
import android.content.Context import android.content.Context
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem

View File

@@ -1,4 +1,4 @@
package com.nodecast.tv.playlist package dev.castarr.tv.playlist
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject

View File

@@ -1,4 +1,4 @@
package com.nodecast.tv.playlist package dev.castarr.tv.playlist
object M3uParser { object M3uParser {

View File

@@ -1,4 +1,4 @@
package com.nodecast.tv.playlist package dev.castarr.tv.playlist
import android.content.Context import android.content.Context
import org.json.JSONArray import org.json.JSONArray

View File

@@ -1,11 +1,11 @@
package com.nodecast.tv.server package dev.castarr.tv.server
import android.content.Context import android.content.Context
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import com.nodecast.tv.pairing.Pairing import dev.castarr.tv.pairing.Pairing
import com.nodecast.tv.playlist.Channel import dev.castarr.tv.playlist.Channel
import fi.iki.elonen.NanoHTTPD import fi.iki.elonen.NanoHTTPD
import fi.iki.elonen.NanoWSD import fi.iki.elonen.NanoWSD
import org.json.JSONObject import org.json.JSONObject

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">NodeCast</string> <string name="app_name">Castarr</string>
<string name="pair_title">Pair with your phone</string> <string name="pair_title">Pair with your phone</string>
<string name="pair_subtitle">Point your camera at the code — the remote opens right in your browser.</string> <string name="pair_subtitle">Point your camera at the code — the remote opens right in your browser.</string>
<string name="ready_to_pair">Ready to pair</string> <string name="ready_to_pair">Ready to pair</string>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="Theme.NodeCast" parent="android:Theme.Material.NoActionBar"> <style name="Theme.Castarr" parent="android:Theme.Material.NoActionBar">
<item name="android:windowBackground">@color/bg</item> <item name="android:windowBackground">@color/bg</item>
<item name="android:colorBackground">@color/bg</item> <item name="android:colorBackground">@color/bg</item>
<item name="android:textColorPrimary">@color/fg</item> <item name="android:textColorPrimary">@color/fg</item>

View File

@@ -6,6 +6,10 @@ pluginManagement {
} }
} }
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
}
dependencyResolutionManagement { dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories { repositories {
@@ -14,5 +18,5 @@ dependencyResolutionManagement {
} }
} }
rootProject.name = "NodeCast" rootProject.name = "Castarr"
include(":app") include(":app")