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" />
+
+