Skip to content

Commit 23ac327

Browse files
authored
Merge pull request #1 from DevinDuricka/media3-update
Media3 (and other dependencies) update
2 parents 44406ad + 17e8c5d commit 23ac327

28 files changed

Lines changed: 946 additions & 1384 deletions

app/build.gradle

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
apply plugin: 'com.android.application'
22
apply plugin: 'kotlin-android'
3-
apply plugin: 'kotlin-android-extensions'
3+
apply plugin: 'kotlin-kapt'
44

55
android {
66

@@ -13,20 +13,17 @@ android {
1313
jvmTarget = JavaVersion.VERSION_1_8.toString()
1414
}
1515

16-
compileSdkVersion 29
17-
buildToolsVersion "29.0.3"
16+
compileSdkVersion 33
1817

1918
defaultConfig {
2019
applicationId "one.fable.fable"
2120
minSdkVersion 21
22-
targetSdkVersion 29
21+
targetSdkVersion 33
2322
versionCode 6
2423
versionName "1.0"
2524
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
2625
}
2726

28-
apply plugin: 'kotlin-kapt'
29-
3027

3128
buildTypes {
3229
release {
@@ -35,6 +32,7 @@ android {
3532
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
3633
}
3734
}
35+
3836
dataBinding {
3937
enabled = true
4038
}
@@ -45,12 +43,12 @@ dependencies {
4543
//Default Dependencies
4644
implementation fileTree(dir: 'libs', include: ['*.jar'])
4745
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
48-
implementation 'androidx.appcompat:appcompat:1.1.0'
49-
implementation 'androidx.core:core-ktx:1.3.0'
50-
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
46+
implementation 'androidx.appcompat:appcompat:1.5.1'
47+
implementation 'androidx.core:core-ktx:1.9.0'
48+
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
5149
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
5250
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
53-
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
51+
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
5452
testImplementation 'junit:junit:4.12'
5553
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
5654
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
@@ -63,18 +61,20 @@ dependencies {
6361
implementation 'androidx.navigation:navigation-ui-ktx:2.3.0-beta01'
6462

6563
//RecyclerView
66-
implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha03'
64+
implementation 'androidx.recyclerview:recyclerview:1.3.0-rc01'
6765

6866
//ExoPlayer
69-
implementation 'com.google.android.exoplayer:exoplayer:2.11.4'
70-
implementation 'com.google.android.exoplayer:extension-mediasession:2.10.0'
67+
implementation "androidx.media3:media3-exoplayer:1.0.0-beta02"
68+
implementation "androidx.media3:media3-ui:1.0.0-beta02"
69+
implementation "androidx.media3:media3-exoplayer-dash:1.0.0-beta02"
70+
implementation 'androidx.media3:media3-session:1.0.0-beta02'
7171

7272
//Coroutines
7373
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5"
74-
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5"
74+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"
7575

7676
//Material Design
77-
implementation 'com.google.android.material:material:1.1.0'
77+
implementation 'com.google.android.material:material:1.7.0'
7878

7979
//Room
8080
implementation "androidx.room:room-runtime:2.2.5"
@@ -85,9 +85,9 @@ dependencies {
8585
implementation "androidx.preference:preference-ktx:1.1.1"
8686

8787
//Tutorial item highlight library
88-
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.0'
88+
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.3'
8989

9090
//Palette API for images
91-
implementation 'androidx.palette:palette:1.0.0'
91+
implementation 'androidx.palette:palette-ktx:1.0.0'
9292

9393
}

app/src/main/AndroidManifest.xml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
android:supportsRtl="true"
1616
android:theme="@style/AppTheme"
1717
android:name=".Fable">
18-
<activity android:name=".MainActivity">
18+
<activity android:name=".MainActivity"
19+
android:exported="true">
1920
<intent-filter>
2021
<action android:name="android.intent.action.MAIN" />
2122
<action android:name="android.intent.action.SEARCH" />
@@ -24,7 +25,14 @@
2425
<meta-data android:name="android.app.searchable"
2526
android:resource="@xml/searchable"/>
2627
</activity>
27-
<service android:name=".exoplayer.AudioPlayerService"/>
28+
<service
29+
android:name=".exoplayer.PlaybackService"
30+
android:foregroundServiceType="mediaPlayback"
31+
android:exported="true">
32+
<intent-filter>
33+
<action android:name="androidx.media3.session.MediaSessionService"/>
34+
</intent-filter>
35+
</service>
2836
</application>
2937

3038
</manifest>

app/src/main/java/one/fable/fable/Fable.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
package one.fable.fable
22

33
import android.app.Application
4-
import android.content.Intent
5-
import android.media.AudioManager
64
import androidx.appcompat.app.AppCompatDelegate
75
import androidx.preference.PreferenceManager
86
import one.fable.fable.database.FableDatabase
97
import one.fable.fable.database.daos.AudiobookDao
108
import one.fable.fable.database.daos.DirectoryDao
11-
import one.fable.fable.exoplayer.AudioPlayerService
129
import one.fable.fable.exoplayer.ExoPlayerMasterObject
1310
import timber.log.Timber
1411

@@ -19,8 +16,13 @@ class Fable : Application() {
1916
lateinit var directoryDao: DirectoryDao
2017
fun isdirectoriesDaoInitialized() :Boolean = this::directoryDao.isInitialized
2118

19+
companion object {
20+
lateinit var instance: Fable private set
21+
}
22+
2223
override fun onCreate() {
2324
super.onCreate()
25+
instance = this
2426
Timber.plant(Timber.DebugTree())
2527
initalizeDependecies()
2628

@@ -34,9 +36,7 @@ class Fable : Application() {
3436
audiobookDao = FableDatabase.getInstance(applicationContext).audiobookDao
3537
directoryDao = FableDatabase.getInstance(applicationContext).directoryDao
3638

37-
ExoPlayerMasterObject.applicationContext = applicationContext
38-
ExoPlayerMasterObject.buildExoPlayer(applicationContext)
39-
ExoPlayerMasterObject.setSharedPreferencesAndListener(PreferenceManager.getDefaultSharedPreferences(applicationContext))
39+
//ExoPlayerMasterObject.setSharedPreferencesAndListener()
4040
ExoPlayerMasterObject.audiobookDao = audiobookDao
4141
}
4242

app/src/main/java/one/fable/fable/GlobalSettingsFragment.kt

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,73 @@
11
package one.fable.fable
22

3-
import android.content.SharedPreferences
43
import android.os.Bundle
54
import android.text.InputType
6-
import android.text.TextUtils
75
import android.view.View
6+
import android.widget.Toast
87
import androidx.appcompat.app.AppCompatDelegate
98
import androidx.preference.*
10-
import java.util.prefs.PreferenceChangeEvent
11-
import java.util.prefs.PreferenceChangeListener
9+
import timber.log.Timber
1210

1311

1412
class GlobalSettingsFragment : PreferenceFragmentCompat() {
15-
lateinit var sharedPreferences: SharedPreferences
16-
lateinit var seekbarListener: SharedPreferences.OnSharedPreferenceChangeListener
1713
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
1814
setPreferencesFromResource(R.xml.global_preferences, rootKey)
1915
}
2016

2117
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
22-
2318
super.onViewCreated(view, savedInstanceState)
24-
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
2519

2620
val rewindPreference : EditTextPreference? = findPreference("rewind_seconds")
2721
val fastForwardPreference : EditTextPreference? = findPreference("fastforward_seconds")
2822
val speedSeekBar : SeekBarPreference? = findPreference("playback_speed_seekbar")
2923
val theme : ListPreference? = findPreference("theme")
3024

31-
val settingChangeEventListener: SharedPreferences.OnSharedPreferenceChangeListener
32-
//val seekbar : SeekBarPreference? = findPreference("playback_speed")
33-
34-
25+
//--Rewind Preference--
3526
rewindPreference?.setOnBindEditTextListener { editText ->
36-
editText.inputType = InputType.TYPE_CLASS_NUMBER
27+
editText.inputType = InputType.TYPE_CLASS_NUMBER //Show only numbers for the rewind preference editor
3728
}
29+
//Add a listener to show a toast when the rewind speed changes
30+
//Don't add to the editTextListener above as it will show the toast when the keyboard appears
31+
rewindPreference?.setOnPreferenceChangeListener { _, _ ->
32+
Toast.makeText(context, "Preference will be reflected after app restart", Toast.LENGTH_LONG).show()
33+
true
34+
}
35+
3836

37+
//--Fast Forward Preference
3938
fastForwardPreference?.setOnBindEditTextListener { editText ->
40-
editText.inputType = InputType.TYPE_CLASS_NUMBER
39+
editText.inputType = InputType.TYPE_CLASS_NUMBER //Show only numbers for the fast forward preference editor
40+
}
41+
//Add a listener to show a toast when the fast forward speed changes
42+
//Don't add to the editTextListener above as it will show the toast when the keyboard appears
43+
fastForwardPreference?.setOnPreferenceChangeListener { _, _ ->
44+
Toast.makeText(context, "Preference will be reflected after app restart", Toast.LENGTH_LONG).show()
45+
true
4146
}
4247

43-
theme?.setOnPreferenceChangeListener(object : Preference.OnPreferenceChangeListener{
44-
override fun onPreferenceChange(preference: Preference?, newValue: Any?): Boolean {
45-
when (newValue){
46-
"1" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
47-
"2" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
48-
"-1" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
49-
}
50-
return true
48+
//Theme preference listener
49+
theme?.setOnPreferenceChangeListener { _, newValue ->
50+
when (newValue){
51+
"1" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
52+
"2" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
53+
"-1" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
5154
}
52-
})
53-
//AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
54-
55-
speedSeekBar?.title = speedSeekBar?.value?.let { setSpeedSeekBarText(it) }
55+
true
56+
}
5657

57-
seekbarListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
58-
if (key == "playback_speed_seekbar"){
59-
speedSeekBar?.title = setSpeedSeekBarText(sharedPreferences.getInt(key, 10))
58+
//Speed Seekbar Preference
59+
speedSeekBar?.title = speedSeekBar?.value?.let { formatSpeedSeekBarText(it) } //This will initialize the speedseekbar with the applicable text
60+
speedSeekBar?.setOnPreferenceChangeListener { _, newValue ->
61+
try {
62+
speedSeekBar.title = formatSpeedSeekBarText(newValue as Int) //The new value should always be an Int, but just in case, catch it
63+
} catch (e : Exception) {
64+
Timber.e(e)
6065
}
66+
true
6167
}
62-
63-
sharedPreferences.registerOnSharedPreferenceChangeListener(seekbarListener)
64-
6568
}
6669

67-
fun setSpeedSeekBarText(speed: Int) : String{
70+
private fun formatSpeedSeekBarText(speed: Int) : String{
6871
val playbackSpeedFloat = speed/10.0
6972
return "Default playback speed: " + playbackSpeedFloat + "x"
7073
}

app/src/main/java/one/fable/fable/MainActivity.kt

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package one.fable.fable
22

3+
import android.content.ComponentName
34
import android.content.Intent
45
import android.media.AudioManager
56
import android.media.MediaMetadataRetriever
@@ -8,16 +9,19 @@ import androidx.appcompat.app.AppCompatActivity
89
import android.os.Bundle
910
import android.provider.DocumentsContract
1011
import android.provider.MediaStore
12+
import android.widget.MediaController
1113
import android.widget.Toast
1214
import androidx.documentfile.provider.DocumentFile
15+
import androidx.media3.session.SessionToken
1316
import androidx.navigation.Navigation
1417
import androidx.navigation.fragment.NavHostFragment
1518
import androidx.palette.graphics.Palette
1619
import com.getkeepsafe.taptargetview.TapTarget
1720
import com.getkeepsafe.taptargetview.TapTargetView
21+
import com.google.common.util.concurrent.MoreExecutors
1822
import kotlinx.coroutines.*
1923
import one.fable.fable.database.entities.*
20-
import one.fable.fable.exoplayer.AudioPlayerService
24+
import one.fable.fable.exoplayer.PlaybackService
2125
import one.fable.fable.library.flags
2226
import timber.log.Timber
2327
import java.util.concurrent.TimeUnit
@@ -72,14 +76,18 @@ class MainActivity : AppCompatActivity() {
7276
// val navOptions = NavOptions.Builder().setPopUpTo(R.id.libraryFragment, true).build()
7377
// navController.navigate(R.id.libraryFragment, null, navOptions)
7478
// }
75-
startAudioPlayerService()
79+
//startAudioPlayerService()
80+
val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
81+
val mediaControllerFuture = androidx.media3.session.MediaController.Builder(this, sessionToken).buildAsync()
82+
mediaControllerFuture.addListener({}, MoreExecutors.directExecutor())
83+
7684
super.onStart()
7785
}
7886

79-
fun startAudioPlayerService(){
80-
val intent = Intent(applicationContext, AudioPlayerService::class.java)
81-
startService(intent)
82-
}
87+
// fun startAudioPlayerService(){
88+
// val intent = Intent(applicationContext, AudioPlayerService::class.java)
89+
// startService(intent)
90+
// }
8391

8492

8593
fun takePersistablePermissionsToDatabaseEntries(directoryUri : Uri){
@@ -206,7 +214,7 @@ class MainActivity : AppCompatActivity() {
206214
if(isLocal(authority)){
207215
val mediaMetadataRetriever = MediaMetadataRetriever()
208216
mediaMetadataRetriever.setDataSource(applicationContext, childUri)
209-
duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).toLong()
217+
duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong()
210218
(mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM))?.let { title = it }
211219
author = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST)
212220
(mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE))?.let { trackTitle = it }
@@ -396,5 +404,20 @@ fun Long.millisToMinutesSecondsString() : String{ //MM:SS
396404
fun Long.millisToHoursMinutesString() : String{ //HH:MM
397405
return String.format("%02d:%02d",
398406
TimeUnit.MILLISECONDS.toHours(this),
399-
TimeUnit.MILLISECONDS.toMinutes(this) % TimeUnit.HOURS.toSeconds(1))
407+
TimeUnit.MILLISECONDS.toMinutes(this) % TimeUnit.HOURS.toMinutes(1))
408+
}
409+
410+
fun Long.millisToHoursMinutesRemainingString() : String{ //HH:MM
411+
return String.format("%02d hrs %02d min remaining",
412+
TimeUnit.MILLISECONDS.toHours(this),
413+
TimeUnit.MILLISECONDS.toMinutes(this) % TimeUnit.HOURS.toMinutes(1))
414+
}
415+
416+
fun durationRemaining(elapsed : Long, duration: Long) : String {
417+
val remaining = duration - elapsed
418+
return if (remaining <= 60000L) {
419+
"Audiobook Complete"
420+
} else {
421+
remaining.millisToHoursMinutesRemainingString()
422+
}
400423
}

0 commit comments

Comments
 (0)