Bootstrap Castarr from NodeCast prototype
Imported the native TV app (Kotlin, ExoPlayer, embedded remote server, QR pairing) plus CONTEXT.md and ADRs 0001-0005. Rename, Compose for TV UI and the Dispatcharr integration follow as tracked issues. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
6
app/src/main/res/drawable/bg_chip.xml
Normal file
6
app/src/main/res/drawable/bg_chip.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<solid android:color="@color/chip_bg" />
|
||||
<stroke android:width="1dp" android:color="@color/chip_stroke" />
|
||||
<corners android:radius="100dp" />
|
||||
</shape>
|
||||
5
app/src/main/res/drawable/bg_qr_card.xml
Normal file
5
app/src/main/res/drawable/bg_qr_card.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<solid android:color="@color/card" />
|
||||
<corners android:radius="16dp" />
|
||||
</shape>
|
||||
4
app/src/main/res/drawable/dot_accent.xml
Normal file
4
app/src/main/res/drawable/dot_accent.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||
<solid android:color="@color/accent" />
|
||||
</shape>
|
||||
4
app/src/main/res/drawable/dot_live.xml
Normal file
4
app/src/main/res/drawable/dot_live.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||
<solid android:color="@color/live" />
|
||||
</shape>
|
||||
17
app/src/main/res/drawable/ic_cast.xml
Normal file
17
app/src/main/res/drawable/ic_cast.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M3,6.5 A1.5,1.5 0 0 1 4.5,5 L19.5,5 A1.5,1.5 0 0 1 21,6.5 L21,16.5 A1.5,1.5 0 0 1 19.5,18 L4.5,18 A1.5,1.5 0 0 1 3,16.5 Z"
|
||||
android:strokeColor="#5FD4C4"
|
||||
android:strokeWidth="1.6"
|
||||
android:strokeLineCap="round" />
|
||||
<path
|
||||
android:pathData="M8.5,21.5 L15.5,21.5"
|
||||
android:strokeColor="#5FD4C4"
|
||||
android:strokeWidth="1.6"
|
||||
android:strokeLineCap="round" />
|
||||
</vector>
|
||||
23
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
23
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?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="108"
|
||||
android:viewportHeight="108">
|
||||
<group
|
||||
android:scaleX="1.8"
|
||||
android:scaleY="1.8"
|
||||
android:pivotX="54"
|
||||
android:pivotY="54">
|
||||
<path
|
||||
android:pathData="M42,48.5 A1.5,1.5 0 0 1 43.5,47 L64.5,47 A1.5,1.5 0 0 1 66,48.5 L66,59.5 A1.5,1.5 0 0 1 64.5,61 L43.5,61 A1.5,1.5 0 0 1 42,59.5 Z"
|
||||
android:strokeColor="#5FD4C4"
|
||||
android:strokeWidth="1.8"
|
||||
android:strokeLineCap="round" />
|
||||
<path
|
||||
android:pathData="M50,64.5 L58,64.5"
|
||||
android:strokeColor="#5FD4C4"
|
||||
android:strokeWidth="1.8"
|
||||
android:strokeLineCap="round" />
|
||||
</group>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/progress_line.xml
Normal file
5
app/src/main/res/drawable/progress_line.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<solid android:color="@color/accent" />
|
||||
<corners android:radius="1dp" />
|
||||
</shape>
|
||||
7
app/src/main/res/drawable/scrim_bottom.xml
Normal file
7
app/src/main/res/drawable/scrim_bottom.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<gradient
|
||||
android:angle="90"
|
||||
android:startColor="#EB05060A"
|
||||
android:endColor="#0005060A" />
|
||||
</shape>
|
||||
26
app/src/main/res/drawable/tv_banner.xml
Normal file
26
app/src/main/res/drawable/tv_banner.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/bg" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:gravity="center" android:width="80dp" android:height="80dp">
|
||||
<vector
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M3,6.5 A1.5,1.5 0 0 1 4.5,5 L19.5,5 A1.5,1.5 0 0 1 21,6.5 L21,16.5 A1.5,1.5 0 0 1 19.5,18 L4.5,18 A1.5,1.5 0 0 1 3,16.5 Z"
|
||||
android:strokeColor="#5FD4C4"
|
||||
android:strokeWidth="1.6"
|
||||
android:strokeLineCap="round" />
|
||||
<path
|
||||
android:pathData="M8.5,21.5 L15.5,21.5"
|
||||
android:strokeColor="#5FD4C4"
|
||||
android:strokeWidth="1.6"
|
||||
android:strokeLineCap="round" />
|
||||
</vector>
|
||||
</item>
|
||||
</layer-list>
|
||||
BIN
app/src/main/res/font/space_grotesk.ttf
Normal file
BIN
app/src/main/res/font/space_grotesk.ttf
Normal file
Binary file not shown.
236
app/src/main/res/layout/activity_main.xml
Normal file
236
app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,236 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/bg">
|
||||
|
||||
<!-- Pairing screen -->
|
||||
<FrameLayout
|
||||
android:id="@+id/pairing_screen"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/ic_cast" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:text="@string/app_name"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/fg"
|
||||
android:textSize="16sp"
|
||||
android:letterSpacing="0.22" />
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="30dp"
|
||||
android:padding="16dp"
|
||||
android:background="@drawable/bg_qr_card">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/qr_image"
|
||||
android:layout_width="170dp"
|
||||
android:layout_height="170dp"
|
||||
android:importantForAccessibility="no" />
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="30dp"
|
||||
android:text="@string/pair_title"
|
||||
android:textColor="@color/fg"
|
||||
android:textSize="26sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:maxWidth="440dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/pair_subtitle"
|
||||
android:textColor="@color/muted"
|
||||
android:textSize="14sp"
|
||||
android:lineSpacingMultiplier="1.3" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="36dp"
|
||||
android:paddingEnd="36dp"
|
||||
android:paddingBottom="26dp">
|
||||
|
||||
<View
|
||||
android:layout_width="7dp"
|
||||
android:layout_height="7dp"
|
||||
android:background="@drawable/dot_accent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pairing_status"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="@string/ready_to_pair"
|
||||
android:textColor="@color/faint"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pairing_url"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="monospace"
|
||||
android:textColor="@color/faint"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginStart="14dp"
|
||||
android:layout_marginEnd="14dp"
|
||||
android:background="@color/line" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pairing_code"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/muted"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<!-- Player screen -->
|
||||
<FrameLayout
|
||||
android:id="@+id/player_screen"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
android:background="@color/bg_deep">
|
||||
|
||||
<androidx.media3.ui.PlayerView
|
||||
android:id="@+id/player_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="@drawable/scrim_bottom" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/device_chip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top|end"
|
||||
android:layout_marginTop="22dp"
|
||||
android:layout_marginEnd="30dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingEnd="14dp"
|
||||
android:paddingTop="7dp"
|
||||
android:paddingBottom="7dp"
|
||||
android:background="@drawable/bg_chip"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/device_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/muted"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<View
|
||||
android:layout_width="5dp"
|
||||
android:layout_height="5dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/dot_accent" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="36dp"
|
||||
android:paddingEnd="36dp"
|
||||
android:paddingBottom="30dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/overlay_live_dot"
|
||||
android:layout_width="6dp"
|
||||
android:layout_height="6dp"
|
||||
android:background="@drawable/dot_live" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/overlay_live_label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="8dp"
|
||||
android:textColor="@color/muted"
|
||||
android:textSize="11sp"
|
||||
android:letterSpacing="0.18" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/overlay_state"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/muted"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/overlay_channel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="@color/fg"
|
||||
android:textSize="29sp" />
|
||||
|
||||
<View
|
||||
android:id="@+id/overlay_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="2dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/progress_line" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</FrameLayout>
|
||||
</FrameLayout>
|
||||
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/bg" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
14
app/src/main/res/values-de/strings.xml
Normal file
14
app/src/main/res/values-de/strings.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pair_title">Mit dem Handy koppeln</string>
|
||||
<string name="pair_subtitle">Richte die Kamera auf den Code — die Fernbedienung öffnet sich direkt im Browser.</string>
|
||||
<string name="ready_to_pair">Bereit zum Koppeln</string>
|
||||
<string name="paired_with">Gekoppelt mit %1$s</string>
|
||||
<string name="pairing_code">Code %1$s</string>
|
||||
<string name="no_network">Keine Netzwerkverbindung</string>
|
||||
<string name="playing">Wird abgespielt</string>
|
||||
<string name="paused">Pausiert</string>
|
||||
<string name="buffering">Lädt…</string>
|
||||
<string name="playback_error">Wiedergabefehler (%1$s)</string>
|
||||
<string name="playlist_error">Playlist konnte nicht geladen werden</string>
|
||||
</resources>
|
||||
14
app/src/main/res/values/colors.xml
Normal file
14
app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="bg">#0A0B0D</color>
|
||||
<color name="bg_deep">#05060A</color>
|
||||
<color name="fg">#F2F3F5</color>
|
||||
<color name="muted">#9AA0A8</color>
|
||||
<color name="faint">#6B717A</color>
|
||||
<color name="accent">#5FD4C4</color>
|
||||
<color name="card">#FBFCFD</color>
|
||||
<color name="live">#E5484D</color>
|
||||
<color name="line">#23262B</color>
|
||||
<color name="chip_stroke">#14FFFFFF</color>
|
||||
<color name="chip_bg">#8C0A0B0D</color>
|
||||
</resources>
|
||||
16
app/src/main/res/values/strings.xml
Normal file
16
app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">NodeCast</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="ready_to_pair">Ready to pair</string>
|
||||
<string name="paired_with">Paired with %1$s</string>
|
||||
<string name="pairing_code">Code %1$s</string>
|
||||
<string name="no_network">No network connection</string>
|
||||
<string name="live">LIVE</string>
|
||||
<string name="playing">Playing</string>
|
||||
<string name="paused">Paused</string>
|
||||
<string name="buffering">Buffering…</string>
|
||||
<string name="playback_error">Playback error (%1$s)</string>
|
||||
<string name="playlist_error">Could not load playlist</string>
|
||||
</resources>
|
||||
12
app/src/main/res/values/themes.xml
Normal file
12
app/src/main/res/values/themes.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Theme.NodeCast" parent="android:Theme.Material.NoActionBar">
|
||||
<item name="android:windowBackground">@color/bg</item>
|
||||
<item name="android:colorBackground">@color/bg</item>
|
||||
<item name="android:textColorPrimary">@color/fg</item>
|
||||
<item name="android:textColorSecondary">@color/muted</item>
|
||||
<item name="android:colorAccent">@color/accent</item>
|
||||
<item name="android:fontFamily">@font/space_grotesk</item>
|
||||
<item name="android:windowAllowReturnTransitionOverlap">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user