Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4085e9d
Merge pull request #32 from TypeType-Video/dev
Priveetee Aug 22, 2026
d194d36
Merge pull request #39 from TypeType-Video/dev
Priveetee Aug 28, 2026
cc0ba0f
Merge pull request #41 from TypeType-Video/dev
Priveetee Aug 29, 2026
de6386a
refactor: split player surface responsibilities
Priveetee Sep 7, 2026
a2fddcd
feat: refine player controls and gestures
Priveetee Sep 7, 2026
cde2860
fix: tune player layout and seeking
Priveetee Sep 7, 2026
dd8f1b2
feat: adapt app shell for wide screens
Priveetee Sep 7, 2026
658d522
feat: adapt content layouts for tablets
Priveetee Sep 7, 2026
8499b97
feat: improve sharing and appearance themes
Priveetee Sep 7, 2026
cb8b5c1
test: cover player control layouts
Priveetee Sep 7, 2026
95900fa
test: cover adaptive navigation layouts
Priveetee Sep 7, 2026
0a36575
test: cover settings and tab navigation
Priveetee Sep 7, 2026
b8d5de2
test: cover sharing and theme ordering
Priveetee Sep 7, 2026
8daaa7a
build: prepare Android 1.7.0 release
Priveetee Sep 7, 2026
e21b078
Merge pull request #61 from TypeType-Video/release/1.7.0-mobile
Priveetee Sep 7, 2026
11b92c1
feat: refine search services and filters
Priveetee Sep 18, 2026
c9264bf
fix: preserve subscription pages during refresh
Priveetee Sep 18, 2026
2e468ed
fix: reconcile push registration on startup
Priveetee Sep 18, 2026
8fece92
build: prepare Android 1.8.0 release
Priveetee Sep 18, 2026
d5d2141
build: align release with main
Priveetee Sep 18, 2026
4729016
Merge pull request #83 from TypeType-Video/release/1.8.0
Priveetee Sep 18, 2026
dfa034f
fix: target release tag at dispatch commit
Priveetee Sep 18, 2026
53c3c13
Merge pull request #84 from TypeType-Video/fix/stable-release-tag-target
Priveetee Sep 18, 2026
2e51df8
build: publish mobile beta 1.8.0-beta.20
Priveetee Sep 18, 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
1 change: 1 addition & 0 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ jobs:
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ env.RELEASE_TAG }}
target_commitish: ${{ github.sha }}
name: ${{ env.RELEASE_TAG }}
body_path: release-notes/${{ env.RELEASE_TAG }}.md
files: |
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 = 10819
versionName = "1.8.0-beta.19"
versionCode = 10821
versionName = "1.8.0-beta.20"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
resValue("string", "app_name", "TypeType")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class PlayerControlsLayoutTest {
.padding(top = 200.dp),
)
}
}

composeRule.onNodeWithContentDescription(
composeRule.activity.getString(R.string.player_timeline),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import dev.typetype.android.core.ui.components.LocalAnimatedStatePlayback
import dev.typetype.android.core.ui.theme.TypeTypeTheme
import dev.typetype.android.domain.search.SearchFilterOption
import dev.typetype.android.feature.menu.VideoMenuScope
import org.junit.Rule
import org.junit.Test
Expand Down Expand Up @@ -61,6 +62,32 @@ class SearchScreenStateTest {
composeRule.onAllNodesWithText("Compose").assertCountEquals(2)
}

@Test
fun filtersStayHiddenUntilSearchFinishesSubmitting() {
show(searchState())

composeRule.onNodeWithContentDescription("Content type").assertDoesNotExist()
}

@Test
fun filtersAppearOnlyWithResultsState() {
composeRule.setContent {
TypeTypeTheme {
SearchFilterBar(
contentFilters = searchState().contentFilters,
filterGroups = emptyList(),
selectedContent = null,
selectedFilters = emptyList(),
onContentSelect = {},
onFilterToggle = { _, _ -> },
onResetFilters = {},
)
}
}

composeRule.onNodeWithContentDescription("Content type").assertIsDisplayed()
}

@Test
fun completedSearchHasAnExplicitEmptyState() {
composeRule.setContent {
Expand Down Expand Up @@ -106,4 +133,11 @@ class SearchScreenStateTest {
watchLater = emptySet(),
onAction = { _, _ -> },
)

private fun searchState() = SearchState(
query = "kotlin",
contentFilters = listOf(
SearchFilterOption(value = "video", label = "video", isDefault = false),
),
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package dev.typetype.android.feature.search

import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import dev.typetype.android.core.ui.theme.TypeTypeTheme
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test

class SearchServiceSelectorTest {
@get:Rule
val composeRule = createComposeRule()

@Test
fun selectingAServiceReportsItsAccountId() {
var selectedService = -1
composeRule.setContent {
TypeTypeTheme {
SearchServiceSelector(service = 0) { service ->
selectedService = service
}
}
}

composeRule.onNodeWithText("NicoNico").performClick()

assertEquals(6, selectedService)
}
}
4 changes: 4 additions & 0 deletions app/src/main/java/dev/typetype/android/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import dev.typetype.android.domain.subscriptions.SubscriptionsRepository
import dev.typetype.android.domain.usersettings.UserSettings
import dev.typetype.android.domain.usersettings.UserSettingsRepository
import dev.typetype.android.feature.player.host.PlayerHostController
import dev.typetype.android.services.push.PushRegistrationManager
import javax.inject.Inject
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
Expand Down Expand Up @@ -63,6 +64,7 @@ class MainViewModel @Inject constructor(
private val subscriptionsRepository: SubscriptionsRepository,
private val libraryRepository: LibraryRepository,
private val activeAccountScope: ActiveAccountScope,
private val pushRegistrationManager: PushRegistrationManager,
private val startupLandingStore: StartupLandingStore,
private val playbackResumeRepository: PlaybackResumeRepository,
private val playbackQueueRepository: PlaybackQueueRepository,
Expand Down Expand Up @@ -182,6 +184,7 @@ class MainViewModel @Inject constructor(
launch { profileRepository.refresh() }
launch { subscriptionsRepository.refresh() }
launch { libraryRepository.resumePendingWrites() }
launch { pushRegistrationManager.reconcileRegistration() }
}
}

Expand Down Expand Up @@ -221,6 +224,7 @@ class MainViewModel @Inject constructor(
launch { profileRepository.refresh() }
launch { subscriptionsRepository.refresh() }
launch { libraryRepository.resumePendingWrites() }
launch { pushRegistrationManager.reconcileRegistration() }
launch {
restorePlaybackUnlessExternalRequestArrives()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.res.stringResource
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ sealed interface SearchAction {
data object OnSearch : SearchAction
data class OnSuggestionClick(val query: String) : SearchAction
data object OnClearQuery : SearchAction
data class OnServiceSelect(val service: Int) : SearchAction
data class OnContentFilterSelect(val value: String?) : SearchAction
data class OnFilterToggle(val groupKey: String, val optionValue: String) : SearchAction
data object OnResetFilters : SearchAction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,17 +106,23 @@ fun SearchScreen(
onNavigateBack = onNavigateBack,
focusRequester = focusRequester,
)
SearchFilterBar(
contentFilters = state.contentFilters,
filterGroups = state.filterGroups,
selectedContent = state.selectedContentFilter,
selectedFilters = state.selectedFilters,
onContentSelect = { onAction(SearchAction.OnContentFilterSelect(it)) },
onFilterToggle = { groupKey, value ->
onAction(SearchAction.OnFilterToggle(groupKey, value))
},
onResetFilters = { onAction(SearchAction.OnResetFilters) },
SearchServiceSelector(
service = state.service,
onServiceSelect = { onAction(SearchAction.OnServiceSelect(it)) },
)
if (state.hasSearched) {
SearchFilterBar(
contentFilters = state.contentFilters,
filterGroups = state.filterGroups,
selectedContent = state.selectedContentFilter,
selectedFilters = state.selectedFilters,
onContentSelect = { onAction(SearchAction.OnContentFilterSelect(it)) },
onFilterToggle = { groupKey, value ->
onAction(SearchAction.OnFilterToggle(groupKey, value))
},
onResetFilters = { onAction(SearchAction.OnResetFilters) },
)
}

when {
state.isLoading -> Box(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package dev.typetype.android.feature.search

import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material3.FilterChip
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import dev.typetype.android.R

@Immutable
private data class SearchService(
val id: Int,
val labelRes: Int,
val iconRes: Int,
val brandColor: Color,
)

private val SEARCH_SERVICES = listOf(
SearchService(
id = 0,
labelRes = R.string.settings_default_service_youtube,
iconRes = R.drawable.ic_service_youtube,
brandColor = Color(0xFFFF0000),
),
SearchService(
id = 6,
labelRes = R.string.settings_default_service_niconico,
iconRes = R.drawable.ic_service_niconico,
brandColor = Color(0xFF231815),
),
SearchService(
id = 5,
labelRes = R.string.settings_default_service_bilibili,
iconRes = R.drawable.ic_service_bilibili,
brandColor = Color(0xFF00A1D6),
),
)

@Composable
internal fun SearchServiceSelector(
service: Int,
onServiceSelect: (Int) -> Unit,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState())
.padding(horizontal = 12.dp, vertical = 4.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
SEARCH_SERVICES.forEach { candidate ->
val label = stringResource(candidate.labelRes)
FilterChip(
selected = service == candidate.id,
onClick = { onServiceSelect(candidate.id) },
leadingIcon = {
Icon(
painter = painterResource(candidate.iconRes),
contentDescription = null,
tint = candidate.brandColor,
modifier = Modifier.size(18.dp),
)
},
label = { Text(label) },
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class SearchViewModel @Inject constructor(
loadMoreError = false,
)
}
is SearchAction.OnServiceSelect -> selectService(action.service)
is SearchAction.OnContentFilterSelect -> selectContentFilter(action.value)
is SearchAction.OnFilterToggle -> toggleFilter(action.groupKey, action.optionValue)
SearchAction.OnResetFilters -> resetFilters()
Expand All @@ -98,6 +99,12 @@ class SearchViewModel @Inject constructor(
}
}

private fun selectService(service: Int) {
viewModelScope.launch {
userSettingsRepository.update { it.copy(defaultService = service) }
}
}

private fun observeSuggestions() {
viewModelScope.launch {
_state
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dev.typetype.android.feature.subscriptions

import dev.typetype.android.domain.feed.Video

internal fun mergeSubscriptionFirstPage(
loadedVideos: List<Video>,
firstPage: List<Video>,
): List<Video> {
val refreshedUrls = firstPage.mapTo(mutableSetOf()) { video -> video.url }
return (firstPage + loadedVideos.filterNot { video -> video.url in refreshedUrls })
.distinctBy { video -> video.url }
}
Loading