Skip to content

Commit

Permalink
merge main branch
Browse files Browse the repository at this point in the history
  • Loading branch information
anilbeesetti committed Jan 15, 2025
2 parents 10ce8b1 + abec77c commit 1edd48d
Show file tree
Hide file tree
Showing 16 changed files with 200 additions and 87 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,8 @@ nextlib/ffmpeg/sources
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/test_output

vendor/
.bundle/
.kotlin
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ android {
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
applicationId = "dev.anilbeesetti.nextplayer"
versionCode = 20
versionName = "0.12.3"
versionCode = 29
versionName = "0.13.0"
}

buildFeatures {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.database.Cursor
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
Expand Down Expand Up @@ -93,6 +92,9 @@ fun Context.getPath(uri: Uri): String? {
}
}
} else if (ContentResolver.SCHEME_CONTENT.equals(uri.scheme, ignoreCase = true)) {
if (uri.isLocalPhotoPickerUri) return null
if (uri.isCloudPhotoPickerUri) return null

return if (uri.isGooglePhotosUri) {
uri.lastPathSegment
} else {
Expand All @@ -116,19 +118,17 @@ private fun Context.getDataColumn(
selection: String? = null,
selectionArgs: Array<String>? = null,
): String? {
var cursor: Cursor? = null
val column = MediaStore.Images.Media.DATA
val projection = arrayOf(column)
try {
cursor = contentResolver.query(uri, projection, selection, selectionArgs, null)
if (cursor != null && cursor.moveToFirst()) {
val index = cursor.getColumnIndexOrThrow(column)
return cursor.getString(index)
contentResolver.query(uri, projection, selection, selectionArgs, null)?.use { cursor ->
if (cursor.moveToFirst()) {
val index = cursor.getColumnIndexOrThrow(column)
return cursor.getString(index)
}
}
} catch (e: Exception) {
return null
} finally {
cursor?.close()
}
return null
}
Expand Down Expand Up @@ -172,26 +172,24 @@ fun Context.getFilenameFromContentUri(uri: Uri): String? {
fun Context.getMediaContentUri(uri: Uri): Uri? {
val path = getPath(uri) ?: return null

var cursor: Cursor? = null
val column = MediaStore.Video.Media._ID
val projection = arrayOf(column)
try {
cursor = contentResolver.query(
contentResolver.query(
VIDEO_COLLECTION_URI,
projection,
"${MediaStore.Images.Media.DATA} = ?",
arrayOf(path),
null,
)
if (cursor != null && cursor.moveToFirst()) {
val index = cursor.getColumnIndexOrThrow(column)
val id = cursor.getLong(index)
return ContentUris.withAppendedId(VIDEO_COLLECTION_URI, id)
)?.use { cursor ->
if (cursor.moveToFirst()) {
val index = cursor.getColumnIndexOrThrow(column)
val id = cursor.getLong(index)
return ContentUris.withAppendedId(VIDEO_COLLECTION_URI, id)
}
}
} catch (e: Exception) {
return null
} finally {
cursor?.close()
}
return null
}
Expand Down Expand Up @@ -275,7 +273,14 @@ private fun detectCharset(url: URL): Charset {

private fun detectCharsetFromStream(inputStream: InputStream): Charset {
return BufferedInputStream(inputStream).use { bufferedStream ->
val data = bufferedStream.readBytes()
val maxBytes = 1024 * 100 // 100 KB
val data = ByteArray(maxBytes)
val bytesRead = bufferedStream.read(data, 0, maxBytes)

if (bytesRead <= 0) {
return@use Charset.forName(StandardCharsets.UTF_8.name())
}

UniversalDetector(null).run {
handleData(data, 0, data.size)
dataEnd()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ suspend fun File.getSubtitles(): List<File> = withContext(Dispatchers.IO) {

subtitleExtensions.mapNotNull { extension ->
val file = File(parentDir, "$mediaName.$extension")
file.takeIf { it.exists() }
file.takeIf { it.exists() && it.isFile }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,15 @@ val Uri.isMediaDocument: Boolean
*/
val Uri.isGooglePhotosUri: Boolean
get() = "com.google.android.apps.photos.content" == authority

/**
* Whether the Uri authority is PhotoPicker.
*/
val Uri.isLocalPhotoPickerUri: Boolean
get() = toString().contains("com.android.providers.media.photopicker")

/**
* Whether the Uri authority is PhotoPicker.
*/
val Uri.isCloudPhotoPickerUri: Boolean
get() = toString().contains("com.google.android.apps.photos.cloudpicker")
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ class LocalMediaRepository @Inject constructor(
return mediumDao.get(uri)?.toVideoState()
}

override fun updateMediumLastPlayedTime(uri: String, lastPlayedTime: Long) {
applicationScope.launch {
mediumDao.updateMediumLastPlayedTime(uri, lastPlayedTime)
}
}

override fun updateMediumPosition(uri: String, position: Long) {
applicationScope.launch {
val duration = mediumDao.get(uri)?.duration ?: position.plus(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface MediaRepository {

suspend fun getVideoState(uri: String): VideoState?

fun updateMediumLastPlayedTime(uri: String, lastPlayedTime: Long)
fun updateMediumPosition(uri: String, position: Long)
fun updateMediumPlaybackSpeed(uri: String, playbackSpeed: Float)
fun updateMediumAudioTrack(uri: String, audioTrackIndex: Int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ class FakeMediaRepository : MediaRepository {
return null
}

override fun updateMediumLastPlayedTime(uri: String, lastPlayedTime: Long) {
}

override fun updateMediumPosition(uri: String, position: Long) {
}

Expand Down
10 changes: 9 additions & 1 deletion fastlane/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,21 @@ For _fastlane_ installation instructions, see [Installing _fastlane_](https://do

## Android

### android build

```sh
[bundle exec] fastlane android build
```

Build the project

### android publish

```sh
[bundle exec] fastlane android publish
```

Build and publish
Publish the build to Google Play

----

Expand Down
6 changes: 6 additions & 0 deletions fastlane/metadata/android/en-US/changelogs/29.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- Added preference to change control buttons position to either left or right
- Added Background playback with notification
- Improved Pip functionality
- Updated Androidx Media3 version 1.5.1
- Fixed an issue where video locations weren't properly updating after moving files to new folders
- Resolved bugs related to Picture-in-Picture mode and background playback
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ import dev.anilbeesetti.nextplayer.feature.player.dialogs.PlaybackSpeedControlsD
import dev.anilbeesetti.nextplayer.feature.player.dialogs.TrackSelectionDialogFragment
import dev.anilbeesetti.nextplayer.feature.player.dialogs.VideoZoomOptionsDialogFragment
import dev.anilbeesetti.nextplayer.feature.player.dialogs.nameRes
import dev.anilbeesetti.nextplayer.feature.player.extensions.audioSessionId
import dev.anilbeesetti.nextplayer.feature.player.extensions.isPortrait
import dev.anilbeesetti.nextplayer.feature.player.extensions.next
import dev.anilbeesetti.nextplayer.feature.player.extensions.seekBack
Expand All @@ -90,6 +89,7 @@ import dev.anilbeesetti.nextplayer.feature.player.extensions.toggleSystemBars
import dev.anilbeesetti.nextplayer.feature.player.extensions.uriToSubtitleConfiguration
import dev.anilbeesetti.nextplayer.feature.player.service.PlayerService
import dev.anilbeesetti.nextplayer.feature.player.service.addSubtitleTrack
import dev.anilbeesetti.nextplayer.feature.player.service.getAudioSessionId
import dev.anilbeesetti.nextplayer.feature.player.service.getSkipSilenceEnabled
import dev.anilbeesetti.nextplayer.feature.player.service.switchAudioTrack
import dev.anilbeesetti.nextplayer.feature.player.service.switchSubtitleTrack
Expand Down Expand Up @@ -130,6 +130,7 @@ class PlayerActivity : AppCompatActivity() {
private var hideInfoLayoutJob: Job? = null

private var playInBackground: Boolean = false
private var isIntentNew: Boolean = true

private val shouldFastSeek: Boolean
get() = playerPreferences.shouldFastSeek(mediaController?.duration ?: C.TIME_UNSET)
Expand All @@ -143,7 +144,6 @@ class PlayerActivity : AppCompatActivity() {
private lateinit var playerApi: PlayerApi
private lateinit var volumeManager: VolumeManager
private lateinit var brightnessManager: BrightnessManager
var loudnessEnhancer: LoudnessEnhancer? = null
private var pipBroadcastReceiver: BroadcastReceiver? = null

/**
Expand Down Expand Up @@ -327,25 +327,23 @@ class PlayerActivity : AppCompatActivity() {
setOrientation()
applyVideoZoom(videoZoom = playerPreferences.playerVideoZoom, showInfo = false)
mediaController?.currentMediaItem?.mediaId?.let {
applyVideoScale(videoScale = viewModel.getVideoState(it)?.videoScale ?: 0f)
applyVideoScale(videoScale = viewModel.getVideoState(it)?.videoScale ?: 1f)
}

mediaController?.run {
binding.playerView.player = this
binding.playerView.keepScreenOn = isPlaying
toggleSystemBars(showBars = binding.playerView.isControllerFullyVisible)
videoTitleTextView.text = currentMediaItem?.mediaMetadata?.title
try {
loudnessEnhancer = if (playerPreferences.shouldUseVolumeBoost) LoudnessEnhancer(audioSessionId) else null
} catch (e: Exception) {
e.printStackTrace()
if (playerPreferences.shouldUseVolumeBoost) {
try {
volumeManager.loudnessEnhancer = LoudnessEnhancer(getAudioSessionId())
} catch (e: Exception) {
e.printStackTrace()
}
}
addListener(playbackStateListener)
volumeManager.loudnessEnhancer = loudnessEnhancer

if (intent.data != null && intent.data.toString() != currentMediaItem?.mediaId) {
playVideo(uri = viewModel.currentMediaItemUri ?: intent.data!!)
}
startPlayback()
}
subtitleFileLauncherLaunchedForMediaItem = null
}
Expand Down Expand Up @@ -386,7 +384,7 @@ class PlayerActivity : AppCompatActivity() {
}
}

@SuppressLint("NewApi")
@SuppressLint("NewApi", "MissingSuperCall")
override fun onUserLeaveHint() {
super.onUserLeaveHint()
if (Build.VERSION.SDK_INT in Build.VERSION_CODES.O..<Build.VERSION_CODES.S &&
Expand Down Expand Up @@ -649,10 +647,38 @@ class PlayerActivity : AppCompatActivity() {
}
}

private fun playVideo(uri: Uri) = lifecycleScope.launch(Dispatchers.IO) {
val mediaUri = getMediaContentUri(uri)
val playlist = mediaUri?.let { viewModel.getPlaylistFromUri(it) }?.map { it.uriString } ?: listOf(uri.toString())
val mediaItemIndexToPlay = playlist.indexOfFirst { it == (mediaUri ?: uri).toString() }.takeIf { it >= 0 } ?: 0
private fun startPlayback() {
val uri = intent.data ?: return

// If the intent is not new and the current media item is not null, return
if (!isIntentNew && mediaController?.currentMediaItem != null) return

// If the current media item is not null and the current media item's uri is the same as the intent's data, return
if (mediaController?.currentMediaItem?.localConfiguration?.uri.toString() == uri.toString()) return

isIntentNew = false

lifecycleScope.launch {
playVideo(uri)
}
}

private suspend fun playVideo(uri: Uri) = withContext(Dispatchers.Default) {
val mediaContentUri = getMediaContentUri(uri)
val playlist = mediaContentUri?.let { mediaUri ->
viewModel.getPlaylistFromUri(mediaUri)
.map { it.uriString }
.toMutableList()
.apply {
if (!contains(mediaUri.toString())) {
add(index = 0, element = mediaUri.toString())
}
}
} ?: listOf(uri.toString())

val mediaItemIndexToPlay = playlist.indexOfFirst {
it == (mediaContentUri ?: uri).toString()
}.takeIf { it >= 0 } ?: 0

val mediaItems = playlist.mapIndexed { index, uri ->
MediaItem.Builder().apply {
Expand Down Expand Up @@ -684,7 +710,7 @@ class PlayerActivity : AppCompatActivity() {
private fun playbackStateListener() = object : Player.Listener {
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
super.onMediaItemTransition(mediaItem, reason)
viewModel.currentMediaItemUri = mediaItem?.localConfiguration?.uri
intent.data = mediaItem?.localConfiguration?.uri
isMediaItemReady = false
}

Expand All @@ -703,12 +729,14 @@ class PlayerActivity : AppCompatActivity() {

override fun onAudioSessionIdChanged(audioSessionId: Int) {
super.onAudioSessionIdChanged(audioSessionId)
loudnessEnhancer?.release()
volumeManager.loudnessEnhancer?.release()

try {
loudnessEnhancer = LoudnessEnhancer(audioSessionId)
} catch (e: Exception) {
e.printStackTrace()
if (playerPreferences.shouldUseVolumeBoost) {
try {
volumeManager.loudnessEnhancer = LoudnessEnhancer(audioSessionId)
} catch (e: Exception) {
e.printStackTrace()
}
}
}

Expand Down Expand Up @@ -782,9 +810,10 @@ class PlayerActivity : AppCompatActivity() {
super.onNewIntent(intent)
if (intent.data != null) {
currentOrientation = null
viewModel.currentMediaItemUri = intent.data
setIntent(intent)
isIntentNew = true
if (mediaController != null) {
playVideo(intent.data!!)
startPlayback()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class PlayerViewModel @Inject constructor(
private val getSortedPlaylistUseCase: GetSortedPlaylistUseCase,
) : ViewModel() {

var currentMediaItemUri: Uri? = null
var playWhenReady: Boolean = true
var skipSilenceEnabled: Boolean = false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import dev.anilbeesetti.nextplayer.core.ui.R as coreUiR
import dev.anilbeesetti.nextplayer.feature.player.databinding.PlaybackSpeedBinding
import dev.anilbeesetti.nextplayer.feature.player.service.getSkipSilenceEnabled
import dev.anilbeesetti.nextplayer.feature.player.service.setSkipSilenceEnabled
import dev.anilbeesetti.nextplayer.feature.player.service.setSpeed
import kotlinx.coroutines.launch

class PlaybackSpeedControlsDialogFragment(
Expand All @@ -33,7 +34,7 @@ class PlaybackSpeedControlsDialogFragment(

speed.addOnChangeListener { _, _, _ ->
val newSpeed = speed.value.round(1)
mediaController.setPlaybackSpeed(newSpeed)
mediaController.setSpeed(newSpeed)
speedText.text = newSpeed.toString()
}
incSpeed.setOnClickListener {
Expand Down
Loading

0 comments on commit 1edd48d

Please sign in to comment.