diff --git a/app/build.gradle b/app/build.gradle index faeb4ef..8b91641 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { applicationId "eu.zderadicka.audioserve" minSdkVersion 21 targetSdkVersion 27 - versionCode 8 - versionName "0.6.1" + versionCode 9 + versionName "0.6.2" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/app/src/main/java/eu/zderadicka/audioserve/AudioService.kt b/app/src/main/java/eu/zderadicka/audioserve/AudioService.kt index 1a17359..d549eed 100644 --- a/app/src/main/java/eu/zderadicka/audioserve/AudioService.kt +++ b/app/src/main/java/eu/zderadicka/audioserve/AudioService.kt @@ -19,8 +19,7 @@ import android.support.v4.media.MediaMetadataCompat import android.support.v4.media.session.MediaControllerCompat import android.support.v4.media.session.MediaSessionCompat import android.support.v4.media.session.PlaybackStateCompat -import android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID -import android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID +import android.support.v4.media.session.PlaybackStateCompat.* import android.util.Log import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.ExoPlayerFactory @@ -602,7 +601,9 @@ mediaSessionConnector.setErrorMessageProvider(messageProvider); private val SEARCH_RE = Regex("""^(\d+)_(.*)""") override fun onLoadChildren(parentId: String, result:Result>) { if (parentId == RECENTLY_LISTENED_TAG) { + result.detach() Log.d(LOG_TAG, "Requesting list of recently listened items") + val list = ArrayList() if (currentMediaItem != null) { val mi = currentMediaItem!! @@ -610,12 +611,21 @@ mediaSessionConnector.setErrorMessageProvider(messageProvider); updateCurrentMediaItemTime() list.add(mi) } - var path: String? = null - if (currentMediaItem != null) { - path = File(currentMediaItem!!.mediaId).parent - } - list.addAll(getRecents(applicationContext,path )) - result.sendResult(list) + + Thread({ + + var path: String? = null + if (currentMediaItem != null) { + path = File(currentMediaItem!!.mediaId).parent + } + list.addAll(getRecents(applicationContext,path )) + result.sendResult(list) + }).start() + + + + + } else { if (isOffline) { @@ -722,6 +732,23 @@ mediaSessionConnector.setErrorMessageProvider(messageProvider); { result.sendResult(it.getMediaItems(cacheManager)) currentFolder = it.getPlayableItems(cacheManager) + + if (session.controller.playbackState.state == PlaybackStateCompat.STATE_NONE || + session.controller.playbackState.state == PlaybackStateCompat.STATE_STOPPED) { + // If player is stopped we can prepare latest bookmark + + val lastItems = getRecents(this, onlyLatest = true) + if (lastItems.size>0) { + val lastItem = lastItems[0] + val lastFolderId = folderIdFromFileId(lastItem.mediaId!!) + if (lastFolderId == parentId) { + val idx = findIndexInFolder(lastItem.mediaId!!) + if (idx >= 0) { + Log.d(LOG_TAG, "This folder can resume last played item ${lastItem.mediaId}") + } + } + } + } } } diff --git a/app/src/main/java/eu/zderadicka/audioserve/SettingsActivity.kt b/app/src/main/java/eu/zderadicka/audioserve/SettingsActivity.kt index 65206d6..955b6b5 100644 --- a/app/src/main/java/eu/zderadicka/audioserve/SettingsActivity.kt +++ b/app/src/main/java/eu/zderadicka/audioserve/SettingsActivity.kt @@ -12,7 +12,6 @@ import eu.zderadicka.audioserve.net.ApiClient import eu.zderadicka.audioserve.net.CacheManager import eu.zderadicka.audioserve.net.MEDIA_CACHE_DIR import java.io.File -import java.net.URL import java.util.* private const val LOG_TAG = "Settings" @@ -127,22 +126,23 @@ class SettingsFragment: PreferenceFragment(), SharedPreferences.OnSharedPreferen private val storagesList: List> get (){ - fun fileSize(s:Long): String { - return android.text.format.Formatter.formatShortFileSize(activity, s) + fun freeSize(s:Long): String { + val size = android.text.format.Formatter.formatShortFileSize(activity, s) + return getString(R.string.free_capacity, size) } val l = ArrayList>() val cname = getString(R.string.storage_internal_cache) val cfile = activity.cacheDir - l.add(Pair(cname + " (${fileSize(cfile.freeSpace)})",cfile.absolutePath)) + l.add(Pair(cname + " (${freeSize(cfile.freeSpace)})",cfile.absolutePath)) val sname = R.string.storage_internal val sfile = activity.filesDir - l.add(Pair(getString(sname) + " (${fileSize(sfile.freeSpace)})",sfile.absolutePath)) + l.add(Pair(getString(sname) + " (${freeSize(sfile.freeSpace)})",sfile.absolutePath)) activity.externalMediaDirs.forEachIndexed { index, file -> if (Environment.getExternalStorageState(file) == Environment.MEDIA_MOUNTED) { val name = getString(R.string.storage_external, index.toString()) - l.add(Pair(name+ " (${fileSize(file.freeSpace)})",file.absolutePath)) + l.add(Pair(name+ " (${freeSize(file.freeSpace)})",file.absolutePath)) } } return l diff --git a/app/src/main/java/eu/zderadicka/audioserve/data/Recent.kt b/app/src/main/java/eu/zderadicka/audioserve/data/Recent.kt index cb5a367..7bba959 100644 --- a/app/src/main/java/eu/zderadicka/audioserve/data/Recent.kt +++ b/app/src/main/java/eu/zderadicka/audioserve/data/Recent.kt @@ -19,6 +19,7 @@ object RecentContract { const val CONTENT_AUTHORITY = "eu.zderadicka.audioserve" object RecentEntry { val CONTENT_URI = Uri.parse("content://$CONTENT_AUTHORITY/$RECENT_PATH") + val CONTENT_URI_LATEST = CONTENT_URI.buildUpon().appendPath("latest").build() const val TABLE_NAME = "recent" const val _ID = BaseColumns._ID const val COLUMN_TIMESTAMP = "timestamp" @@ -89,13 +90,20 @@ class RecentProvider: ContentProvider() { } override fun query(uri: Uri?, projection: Array?, selection: String?, selectionArgs: Array?, sortOrder: String?): Cursor { - if (uri != RecentEntry.CONTENT_URI) throw UnsupportedOperationException("Invalid content URI") + if (uri == RecentEntry.CONTENT_URI || uri == RecentEntry.CONTENT_URI_LATEST) { + + val limit = if (uri == RecentEntry.CONTENT_URI_LATEST) "1" else null + + val db = dbHelper.readableDatabase + val c = db.query(RecentEntry.TABLE_NAME, projection + ?: RecentEntry.DEFAULT_PROJECTION, selection, selectionArgs, + null, null, RecentEntry.COLUMN_TIMESTAMP + " DESC", limit) + c.setNotificationUri(context.contentResolver, uri) + return c + } + + throw UnsupportedOperationException("Invalid content URI") - val db = dbHelper.readableDatabase - val c =db.query(RecentEntry.TABLE_NAME, projection?: RecentEntry.DEFAULT_PROJECTION, selection, selectionArgs, - null,null, RecentEntry.COLUMN_TIMESTAMP + " DESC") - c.setNotificationUri(context.contentResolver, uri) - return c } override fun onCreate(): Boolean { @@ -131,7 +139,7 @@ fun saveRecent(item:MediaBrowserCompat.MediaItem, context: Context) { context.contentResolver.insert(RecentEntry.CONTENT_URI, v) } -fun getRecents(context: Context, exceptPath: String? = null): List { +fun getRecents(context: Context, exceptPath: String? = null, onlyLatest: Boolean = false): List { var selection:String? = null var selectionArgs:Array? = null if (exceptPath != null) { @@ -139,7 +147,11 @@ fun getRecents(context: Context, exceptPath: String? = null): List() - val c = context.contentResolver.query(RecentEntry.CONTENT_URI, null, selection,selectionArgs,null) + val c = context.contentResolver.query( + if (onlyLatest) RecentEntry.CONTENT_URI_LATEST else RecentEntry.CONTENT_URI, + null, selection,selectionArgs, + null + ) c.use { while (c.moveToNext()) { val extras = Bundle() diff --git a/app/src/main/java/eu/zderadicka/audioserve/fragments/FolderFragment.kt b/app/src/main/java/eu/zderadicka/audioserve/fragments/FolderFragment.kt index f1f226f..21dab48 100644 --- a/app/src/main/java/eu/zderadicka/audioserve/fragments/FolderFragment.kt +++ b/app/src/main/java/eu/zderadicka/audioserve/fragments/FolderFragment.kt @@ -2,6 +2,7 @@ package eu.zderadicka.audioserve.fragments import android.content.Context import android.os.Bundle +import android.os.Handler import android.support.v4.media.MediaBrowserCompat import android.support.v4.media.MediaBrowserCompat.MediaItem import android.support.v4.media.MediaMetadataCompat @@ -261,6 +262,8 @@ class FolderFragment : MediaFragment() { private lateinit var folderView: RecyclerView private lateinit var loadingProgress: ProgressBar + private val handler = Handler() + override val mCallback = object: MediaControllerCompat.Callback() { override fun onMetadataChanged(metadata: MediaMetadataCompat?) { @@ -373,14 +376,23 @@ class FolderFragment : MediaFragment() { folderViewState = getFolderViewState() } - //TODO - consider additional caching here to minimize network need + private val showProgress = object: Runnable { + override fun run() { + loadingProgress.visibility = View.VISIBLE + } + + } + private fun startLoading() { mediaActivity?.mediaBrowser?.subscribe(folderId, subscribeCallback) - loadingProgress.visibility = View.VISIBLE - folderView.visibility = View.INVISIBLE + loadingProgress.visibility = View.INVISIBLE + // do not show loading immediatelly for cached and quick responces + handler.postDelayed(showProgress, 500) + folderView.visibility = View.VISIBLE } private fun doneLoading(folderDetails: Bundle?, error: Boolean = false, empty: Boolean = false) { + handler.removeCallbacks(showProgress) loadingProgress.visibility = View.INVISIBLE folderView.visibility = View.VISIBLE diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 424ad11..8673ce1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -80,4 +80,5 @@ You can cancel timer or extend it by actions below Cancel Sleep Timer Sound Notifications + %1$s free