Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
01eb37d
docs: explain F-Droid release channels
Priveetee Aug 28, 2026
bf53457
docs: refresh README and onboarding screenshots
Priveetee Aug 28, 2026
86ca85b
docs: refresh all Android screenshots
Priveetee Aug 28, 2026
6d15798
docs: restore Android screenshot set
Priveetee Aug 28, 2026
96adff1
docs: refresh Android screenshots
Priveetee Aug 28, 2026
2a41b8d
docs: update onboarding screenshots
Priveetee Aug 28, 2026
48d6848
fix: keep Manga theme contrast readable
Priveetee Aug 28, 2026
c17fb53
docs: complete Android 1.6.0 release notes
Priveetee Aug 28, 2026
9ab5ec8
fix: coalesce repeated playback seeks
Priveetee Aug 29, 2026
a30fd2d
chore: bump Android version to 1.6.1
Priveetee Aug 29, 2026
3eba19f
docs: record Android 1.6.1 validation
Priveetee Aug 29, 2026
bc0fcbb
feat: add Android TV foundation
Priveetee Aug 31, 2026
9ca60ee
feat: add Android TV interface
Priveetee Aug 31, 2026
cd729af
ci: verify Android TV module
Priveetee Aug 31, 2026
a40319f
ci: handle private SDK checkout
Priveetee Aug 31, 2026
056b7d4
fix: restore Android CI configuration
Priveetee Aug 31, 2026
6ff389c
fix: remove Android TV CI wiring
Priveetee Aug 31, 2026
a14b058
fix: remove Android TV interface
Priveetee Aug 31, 2026
65af4c0
fix: remove Android TV foundation
Priveetee Aug 31, 2026
158b9b5
feat: wire Android TV module
Priveetee Aug 31, 2026
21bcf44
feat: add Android TV application shell
Priveetee Aug 31, 2026
0be0879
feat: add Android TV runtime
Priveetee Aug 31, 2026
2013ffa
feat: add Android TV artifact storage
Priveetee Aug 31, 2026
454e14a
feat: add Android TV collection browsing
Priveetee Aug 31, 2026
b6e0077
feat: add Android TV home data
Priveetee Aug 31, 2026
6dcdca3
feat: add Android TV download state
Priveetee Aug 31, 2026
6a0b467
feat: add Android TV library actions
Priveetee Aug 31, 2026
5a5b24d
feat: add Android TV account sessions
Priveetee Aug 31, 2026
1bbd3c9
feat: add Android TV search and playback data
Priveetee Aug 31, 2026
d8b0b13
feat: add Android TV content actions
Priveetee Aug 31, 2026
f5938ae
feat: add Android TV view model
Priveetee Aug 31, 2026
e3fa003
feat: add Android TV playback policy
Priveetee Aug 31, 2026
d8f6455
feat: add Android TV playback requests
Priveetee Aug 31, 2026
aed37c5
feat: add Android TV playback service
Priveetee Aug 31, 2026
c6c6982
feat: add Android TV account appearance UI
Priveetee Aug 31, 2026
3656ddb
feat: add Android TV playback overlays
Priveetee Aug 31, 2026
d7edd61
feat: add Android TV cinematic components
Priveetee Aug 31, 2026
3c02fdb
feat: add Android TV collection screens
Priveetee Aug 31, 2026
d616ff9
feat: add Android TV details comments
Priveetee Aug 31, 2026
650d05b
feat: add Android TV details hero
Priveetee Aug 31, 2026
8bde5cc
feat: add Android TV details playback
Priveetee Aug 31, 2026
6924d7b
feat: add Android TV details screen
Priveetee Aug 31, 2026
37e88e7
feat: add Android TV download panel
Priveetee Aug 31, 2026
9c3fd37
feat: add Android TV featured shelf
Priveetee Aug 31, 2026
136ad71
feat: add Android TV home navigation
Priveetee Aug 31, 2026
95722b5
feat: add Android TV library screen
Priveetee Aug 31, 2026
c994589
feat: add Android TV login UI
Priveetee Aug 31, 2026
40c041e
feat: add Android TV content preferences
Priveetee Aug 31, 2026
c53a11b
feat: add Android TV playback controls
Priveetee Aug 31, 2026
5f961e4
feat: add Android TV player controls
Priveetee Aug 31, 2026
3490336
feat: add Android TV player overlays
Priveetee Aug 31, 2026
dafa1bf
feat: add Android TV player screen
Priveetee Aug 31, 2026
35ecdb1
feat: add Android TV session profile UI
Priveetee Aug 31, 2026
0c6ddf1
feat: add Android TV shelf state UI
Priveetee Aug 31, 2026
c7d6cc8
feat: add Android TV search controls
Priveetee Aug 31, 2026
052d54c
feat: add Android TV search screen
Priveetee Aug 31, 2026
570890e
feat: add Android TV settings controls
Priveetee Aug 31, 2026
a19dac5
feat: add Android TV SponsorBlock preferences
Priveetee Aug 31, 2026
b23c49a
feat: add Android TV SponsorBlock status
Priveetee Aug 31, 2026
50c08ef
feat: add Android TV subscription manager
Priveetee Aug 31, 2026
6b8baa0
feat: add Android TV subscription dialogs
Priveetee Aug 31, 2026
d85f61f
feat: add Android TV overlay navigation
Priveetee Aug 31, 2026
43210c5
feat: add Android TV root screen
Priveetee Aug 31, 2026
15359a2
feat: add Android TV top navigation
Priveetee Aug 31, 2026
d370ad8
feat: add Android TV theme system
Priveetee Aug 31, 2026
e477ba5
feat: add Android TV resources
Priveetee Aug 31, 2026
9b31a99
test: cover Android TV data contracts
Priveetee Aug 31, 2026
399a14d
test: cover Android TV playback UI
Priveetee Aug 31, 2026
ee15f55
feat: add Android TV error models
Priveetee Aug 31, 2026
bd42d13
fix: harden Android TV playback recovery
Priveetee Aug 31, 2026
7bafdf5
fix: restore TV recommendation fallback
Priveetee Aug 31, 2026
51967c5
fix: avoid blocking TV startup
Priveetee Aug 31, 2026
d3c7d0a
fix: reset TV navigation context between videos
Priveetee Aug 31, 2026
26f4c5a
fix: enrich TV YouTube streams progressively
Priveetee Aug 31, 2026
2dd6529
feat: add TV comment timestamp seeking
Priveetee Aug 31, 2026
4885383
fix: preserve dynamic color with OLED surfaces
Priveetee Aug 31, 2026
b5d1924
fix: preserve paused player surface after backgrounding
Priveetee Aug 31, 2026
fc14d2b
Merge dev into beta
Priveetee Aug 31, 2026
ff4dd33
chore: bump beta to 1.7.0-beta.6
Priveetee Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 44 additions & 15 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ env:
ANDROID_SDK_ROOT: /opt/android-sdk
JAVA_HOME: /usr/lib/jvm/java-26-openjdk
GRADLE_USER_HOME: /var/tmp/typetype-android-gradle-user-home
TYPETYPE_SDK_PATH: .ci/TypeType-SDK

jobs:
verify:
Expand Down Expand Up @@ -69,20 +70,34 @@ jobs:
- name: Verify Android project
timeout-minutes: 25
run: |
./gradlew --build-cache --stacktrace \
:baseline-profile:assemble \
:player:testDebugUnitTest \
:app:testDebugUnitTest \
:player:lintDebug \
:app:lintDebug \
:app:assembleDebug \
set -euo pipefail
tasks=(
:baseline-profile:assemble
:player:testDebugUnitTest
:app:testDebugUnitTest
:player:lintDebug
:app:lintDebug
:app:assembleDebug
:app:assembleRelease
)
if [[ -f "$TYPETYPE_SDK_PATH/settings.gradle.kts" ]]; then
tasks+=(
:tv:testDebugUnitTest
:tv:lintDebug
:tv:assembleDebug
)
else
echo "::notice::TV verification skipped because the private TypeType-SDK checkout is unavailable."
fi
./gradlew --build-cache --stacktrace "${tasks[@]}"

- name: Upload debug APK
uses: actions/upload-artifact@v7.0.1
with:
name: typetype-debug-apk
path: app/build/outputs/apk/debug/*.apk
path: |
app/build/outputs/apk/debug/*.apk
tv/build/outputs/apk/debug/*.apk
if-no-files-found: error
compression-level: 0
retention-days: 14
Expand All @@ -95,6 +110,7 @@ jobs:
path: |
app/build/reports/lint-results-debug.html
player/build/reports/lint-results-debug.html
tv/build/reports/lint-results-debug.html
if-no-files-found: ignore
retention-days: 14

Expand Down Expand Up @@ -156,14 +172,26 @@ jobs:
- name: Verify Android project and build release
timeout-minutes: 25
run: |
./gradlew --build-cache --stacktrace \
:baseline-profile:assemble \
:player:testDebugUnitTest \
:app:testDebugUnitTest \
:player:lintDebug \
:app:lintDebug \
:app:assembleDebug \
set -euo pipefail
tasks=(
:baseline-profile:assemble
:player:testDebugUnitTest
:app:testDebugUnitTest
:player:lintDebug
:app:lintDebug
:app:assembleDebug
:app:assembleRelease
)
if [[ -f "$TYPETYPE_SDK_PATH/settings.gradle.kts" ]]; then
tasks+=(
:tv:testDebugUnitTest
:tv:lintDebug
:tv:assembleDebug
)
else
echo "::notice::TV verification skipped because the private TypeType-SDK checkout is unavailable."
fi
./gradlew --build-cache --stacktrace "${tasks[@]}"

- name: Upload lint report
if: ${{ always() }}
Expand All @@ -173,6 +201,7 @@ jobs:
path: |
app/build/reports/lint-results-debug.html
player/build/reports/lint-results-debug.html
tv/build/reports/lint-results-debug.html
if-no-files-found: ignore
retention-days: 14

Expand Down
13 changes: 13 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Gradle wrapper.
```sh
git switch dev
./gradlew :app:assembleDebug
# Android TV
./gradlew :tv:assembleDebug
```

On first launch, the app asks for a TypeType instance. Use your own test
Expand Down Expand Up @@ -67,6 +69,8 @@ the contract advertised by the selected TypeType instance.
| `player` | TypeType playback integration for Media3 |
| `app/src/test` | JVM unit and repository tests |
| `app/src/androidTest` | Room, Compose, platform, and device tests |
| `tv/src/main` | Native Android TV UI, navigation, focus, and playback |
| `tv/src/test` | Android TV JVM unit tests |

Most changes only touch one or two of these areas. As a rule of thumb,
composables render state, repositories decide how data is loaded and cached,
Expand Down Expand Up @@ -121,6 +125,15 @@ Before opening a pull request, run the same core checks as CI:
:app:assembleRelease
```

For TV changes, also run:

```sh
./gradlew --no-daemon \
:tv:testDebugUnitTest \
:tv:lintDebug \
:tv:assembleDebug
```

Add the checks that make sense for your change. You are not expected to own
every Android version or device; if something could not be tested, mention it
in the pull request so another contributor can help. Useful checks include:
Expand Down
121 changes: 54 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,73 +21,79 @@

</div>

TypeType Android is a native client for
TypeType Android is the native client for
[TypeType](https://github.com/TypeType-Video/TypeType), a self-hosted video
platform. It provides browsing, account synchronization, downloads, and native
playback on Android phones and tablets.
platform. The app is in beta, supports Android 6.0 and newer, and does not
require Google Play Services.

The Android app is currently in beta and receives frequent updates.
TypeType Android uses the TypeType Server selected during setup for extraction,
playback sessions, recommendations, synchronization, and downloads.

The app communicates exclusively with the TypeType instance selected during
setup. Extraction, playback sessions, recommendations, synchronization, and
server-side downloads remain server responsibilities.
The Android TV client is part of this repository in the `tv` module. It is a
separate native TV application that uses the same TypeType SDK and server
contracts as the mobile client.

## Screenshots

| Home and Continue Watching | Search | Subscriptions |
| Welcome | Add an instance | Home |
| --- | --- | --- |
| ![TypeType home feed and Continue Watching on Android](assets/screenshots/android-home.png) | ![TypeType video search on Android](assets/screenshots/android-search.png) | ![TypeType subscriptions feed on Android](assets/screenshots/android-subscriptions.png) |
| ![TypeType Android welcome screen](assets/screenshots/android-welcome.png) | ![TypeType Android add instance screen](assets/screenshots/android-add-instance.png) | ![TypeType Android home feed](assets/screenshots/android-home.png) |

| Library and history | Native player | Comments |
| Search | Native player | Settings |
| --- | --- | --- |
| ![TypeType library and history on Android](assets/screenshots/android-library.png) | ![TypeType native video player on Android](assets/screenshots/android-player.png) | ![TypeType video comments on Android](assets/screenshots/android-comments.png) |

| Notifications | Profile | Settings |
| --- | --- | --- |
| ![TypeType notifications on Android](assets/screenshots/android-notifications.png) | ![TypeType profile and animated avatar support on Android](assets/screenshots/android-profile.png) | ![TypeType settings on Android](assets/screenshots/android-settings.png) |
| ![TypeType Android search screen](assets/screenshots/android-search.png) | ![TypeType Android native video player](assets/screenshots/android-player.png) | ![TypeType Android settings screen](assets/screenshots/android-settings.png) |

## Features

- Resume unfinished videos from Continue Watching.
- Search videos, channels, playlists, music, and other supported content.
- Browse subscriptions with labels for live, premiere, and special videos.
- Access history, favorites, Watch Later, playlists, and notifications.
- Use background audio, Picture in Picture, the mini-player, and audio-only
playback.
- Browse and search videos, channels, playlists, music, and subscriptions.
- Resume videos, manage history, favorites, Watch Later, playlists, and
notifications.
- Play in the background, in Picture in Picture, or with the mini-player.
- Choose quality, codec, audio track, captions, playback speed, and image mode.
- Use chapters, SponsorBlock, the playback queue, comments, related videos, and
the sleep timer.
- Download supported videos and retain cached pages during temporary network
interruptions.
- Import existing data and manage multiple TypeType instances and accounts.
- Use chapters, SponsorBlock, comments, related videos, the queue, and the
sleep timer.
- Download supported videos and import existing data.

## Android TV

TypeType Android supports Android 6.0 and newer, and does not require Google
Play Services.
Build the TV client with the included Gradle wrapper:

```sh
./gradlew :tv:assembleDebug
```

The TV module targets Android TV devices from API 23 onward and keeps its TV
navigation, focus behavior, layouts, and playback presentation independent from
the mobile UI. Local builds automatically use a sibling `TypeType-SDK`
checkout; set `TYPETYPE_SDK_PATH` when the SDK is stored elsewhere.

## Install

### F-Droid

Use the official TypeType F-Droid repository to install the app and receive
stable updates:
TypeType Android is one application with two F-Droid channels:

- **Stable** is the recommended channel for normal use.
- **Beta** is for testing prerelease builds.

Both channels use the same application package and signing certificate, so they
update the same installation. Add only Stable for normal use, or add both
repositories to receive Beta updates. Choose a channel from the
[TypeType F-Droid setup page](https://typetype.video/fdroid/):

1. Open the [TypeType F-Droid setup page](https://typetype.video/fdroid/) on
your Android device, or scan its QR code.
2. Tap **Open in F-Droid**, add the repository, then wait for the catalog to
refresh.
2. Tap **Open in F-Droid**, add the repository, and refresh the catalog.
3. Search for **TypeType** and install it.

### Signed APK

1. Open the
1. Download the signed APK from the
[latest Release](https://github.com/TypeType-Video/TypeType-Android/releases/latest).
2. Download the signed `TypeType-vX.Y.Z.apk`. A matching SHA-256 file is
available if you want to verify the download.
3. Open the APK and allow installation from your browser or file manager when
Android asks.
4. Launch TypeType, enter the address of your TypeType instance, then sign in
with a local account or OIDC. Guest access appears when the instance supports
it.
2. Verify it with the matching SHA-256 file if desired.
3. Open the APK and allow installation when Android asks.
4. Enter your TypeType instance address and sign in, use OIDC, or continue as a
guest when supported.

Installing a newer signed Release over an existing Release keeps the
application data. If Android reports an incompatible signature, remove any
Expand All @@ -99,17 +105,6 @@ Debug build before installing the signed APK.
> access to a compatible TypeType instance. If you want to host one, start with
> the [self-hosting guide](https://typetype-video.github.io/Docs-TypeType/self-hosting/introduction).

## Offline behavior and diagnostics

The app keeps available local cache entries visible while refreshing and
resumes progressive feeds after a temporary interruption. Playback, downloads,
and account operations still depend on the selected instance and its upstream
providers.

When something fails, open **Settings > Diagnostics** to review a redacted,
local request history before sharing it. Diagnostics do not include raw
credentials, tokens, cookies, or private response bodies.

## Help and feedback

- Read the [TypeType user guide](https://typetype-video.github.io/Docs-TypeType/guide/).
Expand All @@ -118,25 +113,17 @@ credentials, tokens, cookies, or private response bodies.
- Report bugs and request features in the
[TypeType Android issue tracker](https://github.com/TypeType-Video/TypeType-Android/issues).

When reporting a playback or network problem, include the Android version, app
version, TypeType instance version, video URL, the action that failed, and the
redacted diagnostics export when available.

## TypeType ecosystem

- [TypeType](https://github.com/TypeType-Video/TypeType), stack installation,
releases, and coordination
- [TypeType-Server](https://github.com/TypeType-Video/TypeType-Server), API,
extraction, playback sessions, and private user data
- [TypeType-Frontend](https://github.com/TypeType-Video/TypeType-Frontend),
browser client
- [Docs-TypeType](https://github.com/TypeType-Video/Docs-TypeType), user and
self-hosting documentation
When reporting a problem, include the Android version, app version, TypeType
Server version, the action that failed, and the redacted diagnostics export
when available.

## Acknowledgements

TypeType Android is an independent client. The following GPL v3 projects have
provided useful technical and product references:
We warmly thank the teams and contributors behind these projects. Their work,
ideas, and hard-earned lessons have been valuable references while building
TypeType Android, especially for Android compatibility, media playback, and
user experience. TypeType Android remains an independent client and is not
affiliated with these projects.

- [PipePipe](https://github.com/InfinityLoop1308/PipePipe) and
[PipePipeClient](https://github.com/InfinityLoop1308/PipePipeClient), for
Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ android {
applicationId = "dev.typetype.android"
minSdk = 23
targetSdk = 37
versionCode = 10704
versionName = "1.7.0-beta.5"
versionCode = 10705
versionName = "1.7.0-beta.6"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
resValue("string", "app_name", "TypeType")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package dev.typetype.android.core.ui.theme

import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule
import androidx.activity.ComponentActivity
import androidx.compose.runtime.mutableStateOf
import dev.typetype.android.domain.preferences.AccentColor
import dev.typetype.android.domain.preferences.AppearanceMode
import dev.typetype.android.domain.preferences.AppearancePersonality
import dev.typetype.android.domain.preferences.AppearanceTheme
import dev.typetype.android.domain.preferences.AppPreferences
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test

class TypeTypeThemeOrderTest {
@get:Rule
val composeRule = createAndroidComposeRule<ComponentActivity>()
private val preferences = mutableStateOf(AppPreferences())
private var scheme: ColorScheme? = null

@Test
fun oledKeepsDynamicPaletteAndReplacesOnlySurfaces() {
preferences.value = AppPreferences(
accentColor = AccentColor.System,
appearancePersonality = AppearancePersonality.Classic,
appearanceMode = AppearanceMode.Dark,
appearanceTheme = AppearanceTheme.Dynamic,
)
composeRule.setContent {
TypeTypeTheme(preferences = preferences.value) {
scheme = MaterialTheme.colorScheme
}
}
val dynamic = requireNotNull(scheme)
val oled = colorSchemeFor(
preferences.value.copy(appearanceAmoled = true),
)
assertEquals(dynamic.primary, oled.primary)
assertEquals(dynamic.secondaryContainer, oled.secondaryContainer)
assertEquals(dynamic.tertiary, oled.tertiary)
assertEquals(dynamic.error, oled.error)
assertEquals(dynamic.surfaceVariant, oled.surfaceVariant)
assertEquals(dynamic.outline, oled.outline)
assertEquals(Color.Black, oled.background)
assertEquals(Color.Black, oled.surface)
}

private fun colorSchemeFor(preferences: AppPreferences): ColorScheme {
scheme = null
composeRule.runOnIdle {
this.preferences.value = preferences
}
composeRule.waitForIdle()
return requireNotNull(scheme)
}
}
Loading