Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
54 changes: 42 additions & 12 deletions .github/workflows/android-beta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,29 +46,34 @@ env:

jobs:
release:
runs-on: ubuntu-latest
runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && fromJSON('["self-hosted","stack-android"]') || 'ubuntu-latest' }}
timeout-minutes: 50
permissions:
contents: write
outputs:
version: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.tag }}
steps:
- name: Checkout
uses: actions/checkout@v7.0.1
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up Java
uses: actions/setup-java@v6.0.1
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: temurin
java-version: 21

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v6.4.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-disabled: ${{ runner.environment == 'self-hosted' }}

- name: Install Android SDK platform
if: ${{ runner.environment == 'github-hosted' }}
run: |
"$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --install \
"platforms;android-37.0" "build-tools;36.0.0" > /dev/null

- name: Validate beta release version
id: version
env:
GH_TOKEN: ${{ github.token }}
run: |
Expand Down Expand Up @@ -103,8 +108,13 @@ jobs:
echo "RELEASE_CODE=${version_code}"
echo "RELEASE_TAG=${release_tag}"
} >> "$GITHUB_ENV"
{
echo "version=${version_name}"
echo "tag=${release_tag}"
} >> "$GITHUB_OUTPUT"

- name: Prepare signing
if: ${{ github.event_name != 'pull_request' }}
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
Expand Down Expand Up @@ -135,7 +145,7 @@ jobs:

- name: Upload lint report
if: ${{ always() }}
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: typetype-beta-lint-report
path: |
Expand All @@ -153,7 +163,9 @@ jobs:
test -n "$build_tools"

"$build_tools/zipalign" -c -P 16 -v 4 "$apk"
"$build_tools/apksigner" verify --verbose --print-certs "$apk"
if [ "$GITHUB_EVENT_NAME" != pull_request ]; then
"$build_tools/apksigner" verify --verbose --print-certs "$apk"
fi

apk_application_id="$("$ANDROID_HOME/cmdline-tools/latest/bin/apkanalyzer" manifest application-id "$apk")"
apk_version="$("$ANDROID_HOME/cmdline-tools/latest/bin/apkanalyzer" manifest version-name "$apk")"
Expand Down Expand Up @@ -185,7 +197,7 @@ jobs:
> artifacts/typetype-update.json

- name: Upload beta APK
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: typetype-beta-apk
path: |
Expand All @@ -196,8 +208,27 @@ jobs:
compression-level: 0
retention-days: 30

publish:
needs: release
if: ${{ github.event_name != 'pull_request' }}
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
env:
RELEASE_VERSION: ${{ needs.release.outputs.version }}
RELEASE_TAG: ${{ needs.release.outputs.tag }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Download beta APK
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: typetype-beta-apk
path: artifacts

- name: Prepare beta release notes
if: ${{ github.event_name != 'pull_request' }}
env:
HEAD_MESSAGE: ${{ github.event.head_commit.message }}
run: |
Expand All @@ -208,8 +239,7 @@ jobs:
fi

- name: Publish GitHub prerelease
if: ${{ github.event_name != 'pull_request' }}
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
with:
tag_name: ${{ env.RELEASE_TAG }}
name: TypeType Android ${{ env.RELEASE_VERSION }}
Expand Down
27 changes: 15 additions & 12 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ env:
jobs:
verify:
if: ${{ github.event_name != 'workflow_dispatch' || !inputs.publish_release || github.ref != 'refs/heads/main' }}
runs-on: ubuntu-latest
runs-on: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON('["self-hosted","stack-android"]') || 'ubuntu-latest' }}
timeout-minutes: 45
steps:
- name: Checkout
uses: actions/checkout@v7.0.1
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Check file length
run: |
Expand All @@ -63,15 +63,18 @@ jobs:
exit "$status"

- name: Set up Java
uses: actions/setup-java@v6.0.1
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: temurin
java-version: 21

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v6.4.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-disabled: ${{ runner.environment == 'self-hosted' }}

- name: Install Android SDK platform
if: ${{ runner.environment == 'github-hosted' }}
run: |
"$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --install \
"platforms;android-37.0" "build-tools;36.0.0" > /dev/null
Expand All @@ -90,7 +93,7 @@ jobs:
./gradlew --stacktrace "${tasks[@]}"

- name: Upload debug APK
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: typetype-debug-apk
path: |
Expand All @@ -101,7 +104,7 @@ jobs:

- name: Upload lint report
if: ${{ always() }}
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: lint-report
path: |
Expand All @@ -117,16 +120,16 @@ jobs:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v7.0.1
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up Java
uses: actions/setup-java@v6.0.1
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: temurin
java-version: 21

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v6.4.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0

- name: Install Android SDK platform
run: |
Expand Down Expand Up @@ -182,7 +185,7 @@ jobs:

- name: Upload lint report
if: ${{ always() }}
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: lint-report
path: |
Expand Down Expand Up @@ -224,7 +227,7 @@ jobs:
)

- name: Upload release APK
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: typetype-release-apk
path: |
Expand All @@ -236,7 +239,7 @@ jobs:
retention-days: 30

- name: Publish GitHub release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
with:
tag_name: ${{ env.RELEASE_TAG }}
name: ${{ env.RELEASE_TAG }}
Expand Down
5 changes: 2 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ android {

defaultConfig {
applicationId = "dev.typetype.android"
versionCode = 11104
versionName = "1.11.0-beta.4"
versionCode = 11105
versionName = "1.11.0-beta.5"
}

signingConfigs {
Expand Down Expand Up @@ -79,7 +79,6 @@ android {
}

androidComponents.onVariants { variant ->
// Benchmarks and profile generation install their own package, so they never replace the user's app.
if (variant.name == "benchmarkRelease" || variant.name == "nonMinifiedRelease") {
variant.applicationId.set("dev.typetype.android.benchmark")
}
Expand Down
6 changes: 0 additions & 6 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.kts.

# Keep line numbers for stack traces in production crash reports.
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile

# kotlinx.serialization
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
static <1>$Companion Companion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ class ShortsPlaybackContinuityAndroidTest {
instrumentation.runOnMainSync {
controller.stop()
controller.clearMediaItems()
// The surface dies with this activity; a released controller could no longer detach it from the
// shared service player, and the next test would decode into it.
controller.clearVideoSurface()
controller.release()
}
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/java/dev/typetype/android/SettingsNavigation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import dev.typetype.android.feature.settings.SettingsRoute as SettingsRouteScree
import dev.typetype.android.feature.settings.about.LicensesScreen
import dev.typetype.android.feature.settings.appearance.AppearanceRoute as AppearanceScreen
import dev.typetype.android.feature.settings.imports.PortabilityRoute as ImportDataRouteScreen
import dev.typetype.android.feature.settings.notifications.ChannelNotificationsRoute as ChannelNotificationsRouteScreen
import dev.typetype.android.feature.settings.notifications.PushNotificationsRoute as PushNotificationsRouteScreen
import dev.typetype.android.feature.settings.rss.RssFeedsRoute as RssFeedsRouteScreen

Expand Down Expand Up @@ -64,6 +65,12 @@ internal fun NavGraphBuilder.settingsDestinations(
composable<dev.typetype.android.core.ui.navigation.PushNotificationsRoute> {
PushNotificationsRouteScreen(
onNavigateBack = { navController.popBackStack() },
onOpenChannels = { navController.navigate(dev.typetype.android.core.ui.navigation.ChannelNotificationsRoute) },
)
}
composable<dev.typetype.android.core.ui.navigation.ChannelNotificationsRoute> {
ChannelNotificationsRouteScreen(
onNavigateBack = { navController.popBackStack() },
)
}
composable<RssFeedsRoute> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

// Runs on a device where the benchmark build is already signed in to a test instance: the journeys need a home
// feed, Shorts and a playable video, which a fresh install does not have.
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {
@get:Rule
Expand Down
3 changes: 0 additions & 3 deletions build-logic/convention/src/main/kotlin/AndroidDefaults.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import org.gradle.kotlin.dsl.getByType
internal const val COMPILE_SDK = 37
internal const val MIN_SDK = 23

// java.time and other newer APIs are desugared for API 23, in every module that uses them.
internal fun CommonExtension.configureAndroidDefaults(project: Project) {
compileSdk = COMPILE_SDK
defaultConfig.minSdk = MIN_SDK
Expand All @@ -19,9 +18,7 @@ internal fun CommonExtension.configureAndroidDefaults(project: Project) {
lint.abortOnError = true
lint.checkReleaseBuilds = false
lint.warningsAsErrors = true
// Newer versions appear on their own schedule; a build should not start failing because a day passed.
lint.disable += setOf("GradleDependency", "NewerVersionAvailable", "AndroidGradlePluginVersion")
// Verifying the TypeType links needs an assetlinks file on the domain, which is a release decision.
lint.informational += "AppLinkWarning"
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
project.dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.project

// A screen family: Compose and Hilt, the shared core modules, and the libraries every screen uses.
class AndroidFeatureConventionPlugin : Plugin<Project> {
override fun apply(target: Project) = with(target) {
pluginManager.apply("typetype.android.compose")
Expand Down
1 change: 0 additions & 1 deletion core/data/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ android {
namespace = "dev.typetype.android.core.data"
}

// Migration tests read the exported schemas from the test APK assets.
androidComponents.onVariants { variant ->
variant.androidTest?.sources?.assets?.addStaticSourceDirectory("$projectDir/schemas")
}
Expand Down
1 change: 0 additions & 1 deletion core/data/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Tests serve media and downloads from a local MockWebServer over plain HTTP. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:usesCleartextTraffic="true" />
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dev.typetype.android.data.network.dto

import dev.typetype.android.domain.notifications.NotificationItem
import dev.typetype.android.domain.notifications.NotificationsPage
import dev.typetype.android.domain.notifications.UNKNOWN_SERVICE_ID
import kotlinx.serialization.Serializable

@Serializable
Expand All @@ -14,6 +15,7 @@ data class NotificationItemDto(
val channelName: String,
val channelAvatarUrl: String,
val video: VideoItem,
val serviceId: Int = UNKNOWN_SERVICE_ID,
)

@Serializable
Expand Down Expand Up @@ -53,4 +55,5 @@ private fun NotificationItemDto.toDomain(): NotificationItem = NotificationItem(
channelName = channelName,
channelAvatarUrl = channelAvatarUrl,
video = video.toDomainVideo(),
serviceId = serviceId,
)
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,5 @@ internal fun ChannelNotificationPreferenceDto.toDomain(): ChannelNotificationsPr
ChannelNotificationsPreference(
channelUrl = channelUrl,
enabled = enabled,
updatedAtMillis = updatedAt,
)
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,6 @@ internal class SabrPlaybackSessionPreparer(
}
}

// The server can leave a session waiting on a segment that never comes, while a new session at the same position
// plays at once. Replacing it here keeps the player's buffer and media item; a second stall is reported.
private suspend fun replacingStuckSession(
api: TypeTypeMediaApi,
baseUrl: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ internal suspend fun <T> transientPlaybackRequest(
}
}

// Covers a gateway blip of about ten seconds. Longer outages during playback are retried by SabrWindowRecovery, so
// a video that cannot open fails within seconds instead of a minute of skeleton.
private const val MAX_SERVER_RESPONSE_RETRIES = 6
private const val MAX_CONNECTED_TRANSPORT_RETRIES = 8
private const val MAX_OFFLINE_WAIT_MS = 10_000L
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import okhttp3.OkHttpClient
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer

// Queues the control responses of a TypeType server for one SABR video, in the order the client asks for them.
internal class SabrServerScript {
val server = MockWebServer().apply { start() }
val baseUrl: String = server.url("/api/").toString()
Expand Down Expand Up @@ -61,7 +60,6 @@ internal class SabrServerScript {
)
}

// A pending prefetch keeps the same buffered edge, so repeating it is what the client reads as a stuck session.
fun enqueuePrefetch(ready: Boolean, sessionId: String = "session", generation: Long = 0) {
val retry = if (ready) "null" else "250"
val status = if (ready) "ready" else "preparing"
Expand Down
Loading