From e4dd783a6a684b714233184f24e0b95571a8d01e Mon Sep 17 00:00:00 2001
From: Ivan <ivan.zderadicka@gmail.com>
Date: Thu, 26 Apr 2018 07:54:39 +0200
Subject: [PATCH] Fixed lost connection - must close input stream of the
 connection and proper handling of play when not connected

---
 README.md                                     | 16 +++++++----
 .../eu/zderadicka/audioserve/AudioService.kt  |  7 +++--
 .../audioserve/fragments/FolderFragment.kt    |  9 +++++-
 .../zderadicka/audioserve/net/CacheManager.kt |  7 ++++-
 .../eu/zderadicka/audioserve/net/FileCache.kt | 28 +++++++++++++++----
 app/src/main/res/values/strings.xml           |  1 +
 6 files changed, 52 insertions(+), 16 deletions(-)

diff --git a/README.md b/README.md
index a9cd224..2d9c20a 100644
--- a/README.md
+++ b/README.md
@@ -58,14 +58,18 @@ but it's not priority now.
 How to install
 --------------
 
-For now you have build yourself with Android SDK or Android Studio
+I provide built .apk file signed with dummy certificate on 
+[github releases page](https://github.com/izderadicka/audioserve-android/releases).
+
+You can download to Android device and install it there 
+(provided you have allowed Unknown Sources in Security settings).
+
+Supported platforms are from 5.0 Lollipop on ( but I'm testing mostly on 7.0 Nougat).
+
+You can also build yourself with Android Studio - just checkout the project from github and
+open in Android Studio.
 
-This is how to build debug apk from commandline (when you have studio or SDK+tools installed):
-```bash
-./gradlew assembleDebug
-ls app/build/outputs/apk/debug/
 
-```
 
 License
 -------
diff --git a/app/src/main/java/eu/zderadicka/audioserve/AudioService.kt b/app/src/main/java/eu/zderadicka/audioserve/AudioService.kt
index 57c34e9..7b869a0 100644
--- a/app/src/main/java/eu/zderadicka/audioserve/AudioService.kt
+++ b/app/src/main/java/eu/zderadicka/audioserve/AudioService.kt
@@ -50,6 +50,7 @@ private const val FF_MS = 30 * 1000L
 private const val REWIND_MS = 15 * 1000L
 const val MEDIA_FULLY_CACHED = "eu.zderadicka.audioserve.FULLY_CACHED"
 const val MEDIA_CACHE_DELETED = "eu.zderadicka.audioserve.CACHE_DELETED"
+const val PLAYER_NOT_READY = "eu.zderadicka.audioserve.PLAYER_NOT_READY"
 
 
 private class ResultWrapper(val result: MediaBrowserServiceCompat.Result<List<MediaBrowserCompat.MediaItem>>) {
@@ -208,7 +209,7 @@ class AudioService : MediaBrowserServiceCompat() {
 
 
             deletePreviousQueueItem = -1
-            if (folderPosition >= 0) {
+            if (folderPosition >= 0 && sourceFactory!=null) {
                 val factory = sourceFactory?: throw IllegalStateException("Session not ready")
                 var source: MediaSource
                 playQueue = currentFolder.slice(folderPosition until currentFolder.size).toMutableList()
@@ -235,9 +236,11 @@ class AudioService : MediaBrowserServiceCompat() {
 
             } else {
                 //source = factory.createMediaSource(apiClient.uriFromMediaId(mediaId))
-                Log.e(LOG_TAG, "Folder is not synched - cannot played")
+                Log.e(LOG_TAG, "Folder is not synched or offline- cannot played")
                 playQueue.clear()
                 currentSourcesList = null
+
+                session.sendSessionEvent(PLAYER_NOT_READY, null)
             }
 
         }
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 5be7d83..f1f226f 100644
--- a/app/src/main/java/eu/zderadicka/audioserve/fragments/FolderFragment.kt
+++ b/app/src/main/java/eu/zderadicka/audioserve/fragments/FolderFragment.kt
@@ -20,6 +20,7 @@ import android.support.v4.content.ContextCompat
 import android.widget.*
 import eu.zderadicka.audioserve.MEDIA_CACHE_DELETED
 import eu.zderadicka.audioserve.MEDIA_FULLY_CACHED
+import eu.zderadicka.audioserve.PLAYER_NOT_READY
 import eu.zderadicka.audioserve.R
 import eu.zderadicka.audioserve.data.*
 import eu.zderadicka.audioserve.ui.SwipeRevealLayout
@@ -285,13 +286,19 @@ class FolderFragment : MediaFragment() {
 
         override fun onSessionEvent(event: String?, extras: Bundle?) {
             super.onSessionEvent(event, extras)
-            if (event == MEDIA_FULLY_CACHED || event == MEDIA_CACHE_DELETED) {
+            when (event) {
+            MEDIA_FULLY_CACHED, MEDIA_CACHE_DELETED -> {
                 val cached = event == MEDIA_FULLY_CACHED
                 val mediaId = extras?.getString(METADATA_KEY_MEDIA_ID)
                 if (mediaId != null) {
                     adapter.updatedCached(mediaId, cached)
                 }
             }
+                PLAYER_NOT_READY -> {
+                    Toast.makeText(context,getString(R.string.player_not_ready),
+                            Toast.LENGTH_LONG).show()
+                }
+            }
         }
 
     }
diff --git a/app/src/main/java/eu/zderadicka/audioserve/net/CacheManager.kt b/app/src/main/java/eu/zderadicka/audioserve/net/CacheManager.kt
index af4808b..ae98443 100644
--- a/app/src/main/java/eu/zderadicka/audioserve/net/CacheManager.kt
+++ b/app/src/main/java/eu/zderadicka/audioserve/net/CacheManager.kt
@@ -78,7 +78,7 @@ class CachedFileDataSource(val cache: FileCache) : DataSource, CacheItem.Listene
             if (item.state == CacheItem.State.Empty) {
                 cacheReadyCondition.block(TIME_TO_WAIT_FOR_CACHE)
                 if (item.state == CacheItem.State.Empty) {
-                    throw IOException("Empty cache")
+                    throw IOException("Cache not filling - status of FileLoader - online = ${cache.loaderOnline}")
                 }
             }
 
@@ -292,6 +292,11 @@ class CacheManager private constructor(val context: Context) {
         return false
     }
 
+    internal val loaderOnline: Boolean
+    get(){
+        return cache.loaderOnline
+    }
+
     companion object {
         @Volatile
         private var instance: CacheManager? = null
diff --git a/app/src/main/java/eu/zderadicka/audioserve/net/FileCache.kt b/app/src/main/java/eu/zderadicka/audioserve/net/FileCache.kt
index 2ec3b1a..e62e7bb 100644
--- a/app/src/main/java/eu/zderadicka/audioserve/net/FileCache.kt
+++ b/app/src/main/java/eu/zderadicka/audioserve/net/FileCache.kt
@@ -14,7 +14,9 @@ import eu.zderadicka.audioserve.utils.defPrefs
 import eu.zderadicka.audioserve.utils.isNetworkConnected
 import java.io.File
 import java.io.IOException
+import java.io.InputStream
 import java.net.HttpURLConnection
+import java.net.SocketException
 import java.net.URL
 import java.util.concurrent.BlockingDeque
 import java.util.concurrent.LinkedBlockingDeque
@@ -324,6 +326,9 @@ class FileCache(val cacheDir: File,
         item
     }
 
+    internal val loaderOnline: Boolean
+    get() = loader?.isConnected?:false
+
     fun stopAllLoading(vararg keepLoading: String) = synchronized(this) {
         queue.clear()
         val shouldInterrupt: Boolean = if (keepLoading.size == 0) {
@@ -391,7 +396,7 @@ class FileLoader(private val queue: BlockingDeque<CacheItem>,
     private var putBack = false
     private var myThread: Thread? = null
 
-    private var isConnected = true
+    internal var isConnected = true
         set(v) {
             field = v
             if (field) {
@@ -403,6 +408,7 @@ class FileLoader(private val queue: BlockingDeque<CacheItem>,
                 myThread?.let {
                     putBack = true
                     Log.d(LOG_TAG, "Trying to interrupt thread ${it.name}")
+                    Thread{ stream?.close()}.start()
                     it.interrupt()
                 }
             }
@@ -419,6 +425,8 @@ class FileLoader(private val queue: BlockingDeque<CacheItem>,
 
     }
 
+    private var stream: InputStream? = null
+
     init {
 
         context.registerReceiver(connectivityStateReceiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
@@ -442,8 +450,8 @@ class FileLoader(private val queue: BlockingDeque<CacheItem>,
         }
     }
 
-    private fun retry(item: CacheItem) {
-        item.retries++
+    private fun retry(item: CacheItem, dontCount: Boolean = false) {
+        if (! dontCount) item.retries++
         if (item.retries <= MAX_DOWNLOAD_RETRIES) {
             returnToQueue(item)
         } else {
@@ -512,7 +520,8 @@ class FileLoader(private val queue: BlockingDeque<CacheItem>,
                             break
                         }
                         val buf = ByteArray(LOADER_BUFFER_SIZE)
-                        conn.inputStream.use {
+                        stream = conn.inputStream
+                        stream?.use {
                             while (true) {
                                 val read = it.read(buf)
                                 if (read < 0) {
@@ -533,6 +542,7 @@ class FileLoader(private val queue: BlockingDeque<CacheItem>,
 
                     } finally {
                         Log.d(LOG_TAG, "Finished download of $url complete $complete")
+                        stream = null
                         item.closeForAppend(complete)
                     }
 
@@ -546,8 +556,14 @@ class FileLoader(private val queue: BlockingDeque<CacheItem>,
                 putItemBack(item)
                 Log.d(LOG_TAG, "Load interrupted in wait")
             } catch (e: IOException) {
-                Log.e(LOG_TAG, "Network error for url $url", e)
-                item?.let { retry(it) }
+                val dontCount: Boolean = if (e is SocketException && e.message == "Socket closed") {
+                    Log.w(LOG_TAG, "Socket closed probaly due to drop in connection")
+                    true
+                } else {
+                    Log.e(LOG_TAG, "Network error for url $url", e)
+                    false
+                }
+                item?.let { retry(it, dontCount) }
             } catch (e: Exception) {
                 Log.e(LOG_TAG, "Other error for url $url", e)
                 item?.hasError = true
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 58c4ad3..6c47c83 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -66,4 +66,5 @@
     <string name="note_image">Note image</string>
     <string name="folder_image">Folder Image</string>
     <string name="image_about_current_folder">Image about current folder</string>
+    <string name="player_not_ready">Player not ready - probably not connected to server</string>
 </resources>