diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0ab3b397..43955c8e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: uses: actions/setup-java@v2 with: distribution: 'temurin' - java-version: 11 + java-version: 17 cache: 'gradle' - name: Build and test with Gradle diff --git a/app/build.gradle b/app/build.gradle index cb859280..da0337c5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,26 +6,26 @@ apply plugin: 'koin' apply plugin: 'androidx.navigation.safeargs.kotlin' android { - compileSdkVersion 33 - buildToolsVersion '33.0.2' + compileSdkVersion 34 + buildToolsVersion '34.0.0' defaultConfig { applicationId "de.christinecoenen.code.zapp" minSdkVersion 26 - targetSdkVersion 33 - versionCode 65 - versionName "8.2.0" + targetSdkVersion 34 + versionCode 67 + versionName "8.3.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - resConfigs 'de', 'en' + resourceConfigurations += ['de', 'en'] } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8.toString() + jvmTarget = JavaVersion.VERSION_17.toString() freeCompilerArgs += [ '-opt-in=kotlin.RequiresOptIn', ] @@ -38,14 +38,13 @@ android { } buildFeatures { - viewBinding { - enabled = true - } + viewBinding = true } buildTypes { debug { shrinkResources false + applicationIdSuffix '.debug' } release { shrinkResources true @@ -78,17 +77,17 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - def fragment_version = '1.5.5' + def fragment_version = '1.6.0' // kotlin - implementation 'androidx.core:core-ktx:1.9.0' + implementation 'androidx.core:core-ktx:1.10.1' // tests def androix_test_version = '1.5.0' // device tests def espresso_version = '3.5.1' - debugImplementation "androidx.fragment:fragment-testing:1.6.0-alpha08" + debugImplementation "androidx.fragment:fragment-testing:$fragment_version" androidTestImplementation "androidx.test:core-ktx:$androix_test_version" androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.5' androidTestImplementation 'androidx.test:runner:1.5.2' @@ -103,15 +102,15 @@ dependencies { // unit tests testImplementation 'junit:junit:4.13.2' testImplementation "androidx.test:core-ktx:$androix_test_version" - testImplementation 'org.robolectric:robolectric:4.6.1' - testImplementation 'org.mockito.kotlin:mockito-kotlin:3.2.0' - testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4' + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.mockito.kotlin:mockito-kotlin:5.0.0' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.2' // support implementation "androidx.fragment:fragment-ktx:$fragment_version" - implementation 'androidx.activity:activity-ktx:1.7.0' + implementation 'androidx.activity:activity-ktx:1.7.2' implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.8.0' + implementation 'com.google.android.material:material:1.9.0' implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.annotation:annotation:1.6.0' @@ -127,7 +126,7 @@ dependencies { implementation "androidx.navigation:navigation-ui-ktx:$nav_version" // room database - def room_version = "2.5.1" + def room_version = "2.5.2" implementation "androidx.room:room-runtime:$room_version" implementation "androidx.room:room-paging:$room_version" kapt "androidx.room:room-compiler:$room_version" @@ -137,7 +136,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.10.1' // sortable list - implementation 'com.github.woxthebox:draglistview:1.7.2' + implementation 'com.github.woxthebox:draglistview:1.7.3' // markdown implementation 'io.noties.markwon:core:4.6.2' @@ -147,7 +146,7 @@ dependencies { implementation "androidx.work:work-runtime-ktx:$work_version" // exo player - def media3_version = "1.0.0" + def media3_version = "1.1.0" implementation "androidx.media3:media3-exoplayer:$media3_version" implementation "androidx.media3:media3-exoplayer-hls:$media3_version" implementation "androidx.media3:media3-ui:$media3_version" @@ -160,7 +159,7 @@ dependencies { implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // ACRA crash reporting - def acra_version = "5.9.7" + def acra_version = "5.11.0" implementation "ch.acra:acra-mail:$acra_version" implementation "ch.acra:acra-dialog:$acra_version" @@ -171,7 +170,7 @@ dependencies { implementation 'com.jakewharton:process-phoenix:2.1.2' // joda time - implementation 'joda-time:joda-time:2.12.2' + implementation 'joda-time:joda-time:2.12.5' // about libraries view implementation "com.mikepenz:aboutlibraries:$about_libraries_version" diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 8b137891..bd1010dc 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1 +1,21 @@ +# Please add these rules to your existing keep rules in order to suppress warnings. +# This is generated automatically by the Android Gradle plugin. +-dontwarn org.bouncycastle.jsse.BCSSLParameters +-dontwarn org.bouncycastle.jsse.BCSSLSocket +-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider +-dontwarn org.joda.convert.FromString +-dontwarn org.joda.convert.ToString +-dontwarn org.openjsse.javax.net.ssl.SSLParameters +-dontwarn org.openjsse.javax.net.ssl.SSLSocket +-dontwarn org.openjsse.net.ssl.OpenJSSE +# https://r8.googlesource.com/r8/+/refs/heads/main/compatibility-faq.md#troubleshooting-gson-gson +-keep class com.google.gson.reflect.TypeToken { *; } +-keep class * extends com.google.gson.reflect.TypeToken + +# https://github.com/square/retrofit/issues/3751#issuecomment-1564410089 +-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation +-keep,allowobfuscation,allowshrinking interface retrofit2.Call +-keep,allowobfuscation,allowshrinking class retrofit2.Response +-if interface * { @retrofit2.http.* public *** *(...); } +-keep,allowoptimization,allowshrinking,allowobfuscation class <3> diff --git a/app/src/debug/res/values/ic_launcher_background.xml b/app/src/debug/res/values/ic_launcher_background.xml new file mode 100644 index 00000000..e8e14748 --- /dev/null +++ b/app/src/debug/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #850048 + diff --git a/app/src/debug/res/values/strings.xml b/app/src/debug/res/values/strings.xml new file mode 100644 index 00000000..2ec0a56b --- /dev/null +++ b/app/src/debug/res/values/strings.xml @@ -0,0 +1,3 @@ + + Zapp (Debug) + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3da1499c..21281e75 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,8 @@ + + + + diff --git a/app/src/main/java/de/christinecoenen/code/zapp/app/main/MainActivity.kt b/app/src/main/java/de/christinecoenen/code/zapp/app/main/MainActivity.kt index 9436a572..b07ae605 100644 --- a/app/src/main/java/de/christinecoenen/code/zapp/app/main/MainActivity.kt +++ b/app/src/main/java/de/christinecoenen/code/zapp/app/main/MainActivity.kt @@ -14,6 +14,7 @@ import androidx.core.view.MenuProvider import androidx.core.view.isVisible import androidx.navigation.NavController import androidx.navigation.NavDestination +import androidx.navigation.NavGraph import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.* import androidx.preference.PreferenceManager @@ -43,7 +44,10 @@ class MainActivity : AppCompatActivity(), MenuProvider { setContentView(binding.root) + val startFragmentId = settingsRepository.startFragment + navController = binding.navHostFragment.getFragment().navController + navController.graph.setStartDestination(startFragmentId) appBarConfiguration = AppBarConfiguration( setOf( @@ -65,6 +69,8 @@ class MainActivity : AppCompatActivity(), MenuProvider { PreferenceManager.setDefaultValues(application, R.xml.preferences, false) + navController.navigate(startFragmentId) + requestPermissions() if (!settingsRepository.dynamicColors) { diff --git a/app/src/main/java/de/christinecoenen/code/zapp/app/mediathek/controller/downloads/DownloadWorker.kt b/app/src/main/java/de/christinecoenen/code/zapp/app/mediathek/controller/downloads/DownloadWorker.kt index 9d87b6aa..a24f7edf 100644 --- a/app/src/main/java/de/christinecoenen/code/zapp/app/mediathek/controller/downloads/DownloadWorker.kt +++ b/app/src/main/java/de/christinecoenen/code/zapp/app/mediathek/controller/downloads/DownloadWorker.kt @@ -3,6 +3,8 @@ package de.christinecoenen.code.zapp.app.mediathek.controller.downloads import android.annotation.SuppressLint import android.app.PendingIntent import android.content.Context +import android.content.pm.ServiceInfo +import android.os.Build import androidx.core.app.NotificationManagerCompat import androidx.work.* import de.christinecoenen.code.zapp.app.mediathek.controller.downloads.notifications.DownloadCompletedEventNotification @@ -237,10 +239,18 @@ class DownloadWorker(appContext: Context, workerParams: WorkerParameters) : } override suspend fun getForegroundInfo() = - ForegroundInfo( - id.hashCode(), - downloadProgressNotification.build(progress, downloadedBytes, totalBytes) - ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + ForegroundInfo( + id.hashCode(), + downloadProgressNotification.build(progress, downloadedBytes, totalBytes), + ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC + ) + } else { + ForegroundInfo( + id.hashCode(), + downloadProgressNotification.build(progress, downloadedBytes, totalBytes) + ) + } private suspend fun reportProgress() { val update = workDataOf(ProgressKey to progress) @@ -256,16 +266,21 @@ class DownloadWorker(appContext: Context, workerParams: WorkerParameters) : HttpURLConnection.HTTP_NOT_FOUND, HttpURLConnection.HTTP_GONE -> ErrorType.FileNotFound + HttpURLConnection.HTTP_UNAUTHORIZED, HttpURLConnection.HTTP_FORBIDDEN, 451 -> ErrorType.FileForbidden + 429 -> ErrorType.TooManyRequests + in 400..499 -> ErrorType.ClientError + in 500..600 -> ErrorType.ServerError + else -> ErrorType.Unknown } diff --git a/app/src/main/java/de/christinecoenen/code/zapp/app/mediathek/ui/list/MediathekListFragment.kt b/app/src/main/java/de/christinecoenen/code/zapp/app/mediathek/ui/list/MediathekListFragment.kt index d86c840d..a2960218 100644 --- a/app/src/main/java/de/christinecoenen/code/zapp/app/mediathek/ui/list/MediathekListFragment.kt +++ b/app/src/main/java/de/christinecoenen/code/zapp/app/mediathek/ui/list/MediathekListFragment.kt @@ -26,6 +26,7 @@ import de.christinecoenen.code.zapp.app.mediathek.ui.list.adapter.PagedMediathek import de.christinecoenen.code.zapp.databinding.MediathekListFragmentBinding import de.christinecoenen.code.zapp.databinding.ViewNoShowsBinding import de.christinecoenen.code.zapp.models.shows.MediathekShow +import de.christinecoenen.code.zapp.utils.system.LifecycleOwnerHelper.launchOnCreated import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.drop @@ -120,13 +121,13 @@ class MediathekListFragment : Fragment(), binding.list.adapter = adapter.withLoadStateFooter(FooterLoadStateAdapter(adapter::retry)) - viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.launchOnCreated { viewmodel.pageFlow.collectLatest { pagingData -> adapter.submitData(pagingData) } } - viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.launchOnCreated { viewmodel .pageFlow .drop(1) @@ -138,7 +139,7 @@ class MediathekListFragment : Fragment(), } } - viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.launchOnCreated { adapter.loadStateFlow .map { it.refresh } .distinctUntilChanged() diff --git a/app/src/main/java/de/christinecoenen/code/zapp/app/mediathek/ui/list/adapter/MediathekItemViewHolder.kt b/app/src/main/java/de/christinecoenen/code/zapp/app/mediathek/ui/list/adapter/MediathekItemViewHolder.kt index 70971fc2..ce7b6b50 100644 --- a/app/src/main/java/de/christinecoenen/code/zapp/app/mediathek/ui/list/adapter/MediathekItemViewHolder.kt +++ b/app/src/main/java/de/christinecoenen/code/zapp/app/mediathek/ui/list/adapter/MediathekItemViewHolder.kt @@ -28,8 +28,8 @@ class MediathekItemViewHolder( private val mediathekRepository: MediathekRepository by inject() - private val bgColorDefault = binding.root.context.themeColor(R.attr.backgroundColor) - private val bgColorHighlight by lazy { binding.root.context.themeColor(R.attr.colorSurface) } + private val bgColorDefault = binding.root.context.themeColor(com.google.android.material.R.attr.backgroundColor) + private val bgColorHighlight by lazy { binding.root.context.themeColor(com.google.android.material.R.attr.colorSurface) } private var isRelevantForUserJob: Job? = null private var isBookmarkedJob: Job? = null diff --git a/app/src/main/java/de/christinecoenen/code/zapp/app/player/SleepTimerBottomSheet.kt b/app/src/main/java/de/christinecoenen/code/zapp/app/player/SleepTimerBottomSheet.kt index ae1c149e..b9a82706 100644 --- a/app/src/main/java/de/christinecoenen/code/zapp/app/player/SleepTimerBottomSheet.kt +++ b/app/src/main/java/de/christinecoenen/code/zapp/app/player/SleepTimerBottomSheet.kt @@ -82,6 +82,7 @@ class SleepTimerBottomSheet : BottomSheetDialogFragment(), SleepTimer.Listener { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) BackgroundPlayerService.bind(requireContext(), backgroundPlayerServiceConnection) + expand() } override fun onDestroyView() { diff --git a/app/src/main/java/de/christinecoenen/code/zapp/app/settings/repository/SettingsRepository.kt b/app/src/main/java/de/christinecoenen/code/zapp/app/settings/repository/SettingsRepository.kt index 226cb7c6..ae4880aa 100644 --- a/app/src/main/java/de/christinecoenen/code/zapp/app/settings/repository/SettingsRepository.kt +++ b/app/src/main/java/de/christinecoenen/code/zapp/app/settings/repository/SettingsRepository.kt @@ -78,6 +78,13 @@ class SettingsRepository(context: Context) { return prefValueToUiMode(uiMode) } + val startFragment: Int + get() = when (preferences.getString(context.getString(R.string.pref_key_start_tab), "live")) { + "mediathek" -> R.id.mediathekListFragment + "personal" -> R.id.personalFragment + else -> R.id.channelListFragment + } + fun prefValueToUiMode(prefSetting: String?): Int { val defaultMode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM else AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY diff --git a/app/src/main/java/de/christinecoenen/code/zapp/utils/video/SwipeablePlayerView.kt b/app/src/main/java/de/christinecoenen/code/zapp/utils/video/SwipeablePlayerView.kt index a7e20517..84e1e0c8 100644 --- a/app/src/main/java/de/christinecoenen/code/zapp/utils/video/SwipeablePlayerView.kt +++ b/app/src/main/java/de/christinecoenen/code/zapp/utils/video/SwipeablePlayerView.kt @@ -5,10 +5,16 @@ import android.app.Activity import android.content.Context import android.media.AudioManager import android.util.AttributeSet -import android.view.* +import android.view.GestureDetector import android.view.GestureDetector.SimpleOnGestureListener +import android.view.Gravity +import android.view.MotionEvent +import android.view.ScaleGestureDetector import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener +import android.view.View import android.view.View.OnTouchListener +import android.view.ViewGroup +import android.view.Window import android.widget.Toast import androidx.media3.ui.AspectRatioFrameLayout import androidx.media3.ui.AspectRatioFrameLayout.AspectRatioListener @@ -81,6 +87,9 @@ class SwipeablePlayerView @JvmOverloads constructor( } else { setZoomStateBoxed() } + + subtitleView?.setUserDefaultStyle() + subtitleView?.setUserDefaultTextSize() } fun toggleControls() { @@ -183,12 +192,18 @@ class SwipeablePlayerView @JvmOverloads constructor( return super.onDown(e) } + // removing this leads to a compilation error with sdk 33 + @Suppress("NOTHING_TO_OVERRIDE") override fun onScroll( - e1: MotionEvent, + e1: MotionEvent?, e2: MotionEvent, distanceX: Float, distanceY: Float ): Boolean { + if (e1 == null) { + return false + } + if (!canUseWipeControls || e1.y <= forbiddenAreaSizeTop) { return super.onScroll(e1, e2, distanceX, distanceY) } @@ -213,10 +228,12 @@ class SwipeablePlayerView @JvmOverloads constructor( adjustBrightness(yPercent) true } + e2.x > width - INDICATOR_WIDTH -> { adjustVolume(yPercent) true } + else -> { endScroll() super.onScroll(e1, e2, distanceX, distanceY) diff --git a/app/src/main/res/raw/changelog.md b/app/src/main/res/raw/changelog.md index d5fefbaf..11cb7237 100644 --- a/app/src/main/res/raw/changelog.md +++ b/app/src/main/res/raw/changelog.md @@ -1,7 +1,12 @@ -# v-next +# 8.3.1 +* Absturz bei geänderter Sender-Anordnung gefixt + +# 8.3.0 * Einstellungen im Material-Design (danke an Bnyro) * Schlummerfunktion (danke an Bnyro) * Exo-Player-Update - sollte Playback-Probleme auf einigen Geräten beheben +* Möglichkeit, Start-Tab in den Einstellungen zu setzen (danke an Bnyro) +* Videos nutzen Untertitel-Stil aus den Android-Systemeinstellungen # 8.2.0 * Unterstützung für dynamische Gerätefarben diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index f7f839cb..1c0e6ace 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -151,6 +151,8 @@ Always light Always dark + Start page + Don\'t play back Lowest quality Medium quality diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 5561e47e..7da61ca9 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -29,6 +29,18 @@ @string/stream_quality_cellular_highest + + @string/activity_main_tab_live + @string/activity_main_tab_mediathek + @string/activity_main_tab_personal + + + + live + mediathek + personal + + @fraction/mediathek_filter_min_duration @fraction/mediathek_filter_max_duration diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 0e758570..525c05fb 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -13,5 +13,6 @@ pref_key_sleep_timer_delay_millis pref_key_player_zoomed pref_key_download_to_sd_card + pref_key_start_tab diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4203336b..e1762453 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -151,6 +151,8 @@ Immer hell Immer dunkel + Startseite + Videos nicht abspielen Niedrigste Qualität Mittlere Qualität diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 3587a448..2f66eb73 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -16,9 +16,17 @@ + android:title="@string/pref_dynamic_colors" /> + +