diff --git a/app/src/main/java/com/github/libretube/adapters/SubscriptionChannelAdapter.kt b/app/src/main/java/com/github/libretube/adapters/SubscriptionChannelAdapter.kt index 9d4af078b..ef2ff27a1 100644 --- a/app/src/main/java/com/github/libretube/adapters/SubscriptionChannelAdapter.kt +++ b/app/src/main/java/com/github/libretube/adapters/SubscriptionChannelAdapter.kt @@ -15,8 +15,6 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList() { val TAG = "SubChannelAdapter" - private var subscribed = true - override fun getItemCount(): Int { return subscriptions.size } @@ -30,6 +28,8 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList= streamItems.size) streamItems.size - 1 else index } fun updateItems() { index += 10 - if (index > streamItems.size) { - index = streamItems.size - } notifyDataSetChanged() } diff --git a/app/src/main/java/com/github/libretube/obj/NewPipeSubscription.kt b/app/src/main/java/com/github/libretube/obj/NewPipeSubscription.kt new file mode 100644 index 000000000..1db358271 --- /dev/null +++ b/app/src/main/java/com/github/libretube/obj/NewPipeSubscription.kt @@ -0,0 +1,7 @@ +package com.github.libretube.obj + +data class NewPipeSubscription( + val name: String? = null, + val service_id: Int? = null, + val url: String? = null +) diff --git a/app/src/main/java/com/github/libretube/obj/NewPipeSubscriptions.kt b/app/src/main/java/com/github/libretube/obj/NewPipeSubscriptions.kt new file mode 100644 index 000000000..9c19551d2 --- /dev/null +++ b/app/src/main/java/com/github/libretube/obj/NewPipeSubscriptions.kt @@ -0,0 +1,7 @@ +package com.github.libretube.obj + +data class NewPipeSubscriptions( + val app_version: String = "", + val app_version_int: Int = 0, + val subscriptions: List? = null +) diff --git a/app/src/main/java/com/github/libretube/preferences/InstanceSettings.kt b/app/src/main/java/com/github/libretube/preferences/InstanceSettings.kt index d2d82c013..437f8b152 100644 --- a/app/src/main/java/com/github/libretube/preferences/InstanceSettings.kt +++ b/app/src/main/java/com/github/libretube/preferences/InstanceSettings.kt @@ -1,10 +1,8 @@ package com.github.libretube.preferences -import android.content.ContentResolver import android.content.Intent import android.net.Uri import android.os.Bundle -import android.util.Log import android.widget.Toast import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts @@ -21,91 +19,20 @@ import com.github.libretube.dialogs.CustomInstanceDialog import com.github.libretube.dialogs.DeleteAccountDialog import com.github.libretube.dialogs.LoginDialog import com.github.libretube.dialogs.LogoutDialog +import com.github.libretube.util.ImportHelper import com.github.libretube.util.PermissionHelper import com.github.libretube.util.RetrofitInstance -import org.json.JSONObject -import org.json.JSONTokener -import retrofit2.HttpException -import java.io.IOException -import java.io.InputStream -import java.util.zip.ZipEntry -import java.util.zip.ZipInputStream class InstanceSettings : PreferenceFragmentCompat() { val TAG = "InstanceSettings" - - companion object { - lateinit var getContent: ActivityResultLauncher - } + private lateinit var getContent: ActivityResultLauncher override fun onCreate(savedInstanceState: Bundle?) { getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? -> - if (uri != null) { - try { - // Open a specific media item using ParcelFileDescriptor. - val resolver: ContentResolver = - requireActivity() - .contentResolver - - // "rw" for read-and-write; - // "rwt" for truncating or overwriting existing file contents. - // val readOnlyMode = "r" - // uri - I have got from onActivityResult - val type = resolver.getType(uri) - - var inputStream: InputStream? = resolver.openInputStream(uri) - val channels = ArrayList() - if (type == "application/json") { - val json = inputStream?.bufferedReader()?.readLines()?.get(0) - val jsonObject = JSONTokener(json).nextValue() as JSONObject - Log.e(TAG, jsonObject.getJSONArray("subscriptions").toString()) - for ( - i in 0 until jsonObject.getJSONArray("subscriptions") - .length() - ) { - var url = - jsonObject.getJSONArray("subscriptions").getJSONObject(i) - .getString("url") - url = url.replace("https://www.youtube.com/channel/", "") - Log.e(TAG, url) - channels.add(url) - } - } else { - if (type == "application/zip") { - val zis = ZipInputStream(inputStream) - var entry: ZipEntry? = zis.nextEntry - while (entry != null) { - if (entry.name.endsWith(".csv")) { - inputStream = zis - break - } - entry = zis.nextEntry - } - } - - inputStream?.bufferedReader()?.readLines()?.forEach { - if (it.isNotBlank()) { - val channelId = it.substringBefore(",") - if (channelId.length == 24) { - channels.add(channelId) - } - } - } - } - inputStream?.close() - - subscribe(channels) - } catch (e: Exception) { - Log.e(TAG, e.toString()) - Toast.makeText( - context, - R.string.error, - Toast.LENGTH_SHORT - ).show() - } - } + ImportHelper(requireActivity() as AppCompatActivity).importSubscriptions(uri) } + super.onCreate(savedInstanceState) } @@ -201,7 +128,13 @@ class InstanceSettings : PreferenceFragmentCompat() { val importFromYt = findPreference(PreferenceKeys.IMPORT_SUBS) importFromYt?.setOnPreferenceClickListener { - importSubscriptions() + // check StorageAccess + val accessGranted = + PermissionHelper.isStoragePermissionGranted(activity as AppCompatActivity) + // import subscriptions + if (accessGranted) getContent.launch("*/*") + // request permissions if not granted + else PermissionHelper.requestReadWrite(activity as AppCompatActivity) true } } @@ -210,8 +143,8 @@ class InstanceSettings : PreferenceFragmentCompat() { lifecycleScope.launchWhenCreated { val customInstances = PreferenceHelper.getCustomInstances() - var instanceNames = arrayListOf() - var instanceValues = arrayListOf() + val instanceNames = arrayListOf() + val instanceValues = arrayListOf() // fetch official public instances @@ -256,46 +189,4 @@ class InstanceSettings : PreferenceFragmentCompat() { if (!isAdded) return // Fragment not attached to an Activity activity?.runOnUiThread(action) } - - private fun importSubscriptions() { - val token = PreferenceHelper.getToken() - if (token != "") { - // check StorageAccess - val accessGranted = - PermissionHelper.isStoragePermissionGranted(activity as AppCompatActivity) - if (accessGranted) getContent.launch("*/*") - else PermissionHelper.requestReadWrite(activity as AppCompatActivity) - } else { - Toast.makeText(context, R.string.login_first, Toast.LENGTH_SHORT).show() - } - } - - private fun subscribe(channels: List) { - fun run() { - lifecycleScope.launchWhenCreated { - val response = try { - val token = PreferenceHelper.getToken() - RetrofitInstance.authApi.importSubscriptions( - false, - token, - channels - ) - } catch (e: IOException) { - Log.e(TAG, "IOException, you might not have internet connection") - return@launchWhenCreated - } catch (e: HttpException) { - Log.e(TAG, "HttpException, unexpected response$e") - return@launchWhenCreated - } - if (response.message == "ok") { - Toast.makeText( - context, - R.string.importsuccess, - Toast.LENGTH_SHORT - ).show() - } - } - } - run() - } } diff --git a/app/src/main/java/com/github/libretube/util/ImportHelper.kt b/app/src/main/java/com/github/libretube/util/ImportHelper.kt new file mode 100644 index 000000000..92b45aacd --- /dev/null +++ b/app/src/main/java/com/github/libretube/util/ImportHelper.kt @@ -0,0 +1,90 @@ +package com.github.libretube.util + +import android.net.Uri +import android.util.Log +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import com.fasterxml.jackson.databind.ObjectMapper +import com.github.libretube.R +import com.github.libretube.obj.NewPipeSubscriptions +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import java.io.BufferedReader +import java.io.InputStream +import java.io.InputStreamReader +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream + +class ImportHelper( + private val activity: AppCompatActivity +) { + private val TAG = "ImportHelper" + + fun importSubscriptions(uri: Uri?) { + if (uri == null) return + try { + val type = activity.contentResolver.getType(uri) + + var inputStream: InputStream? = activity.contentResolver.openInputStream(uri) + var channels = ArrayList() + if (type == "application/json") { + val mapper = ObjectMapper() + val json = readTextFromUri(uri) + val subscriptions = mapper.readValue(json, NewPipeSubscriptions::class.java) + channels = subscriptions.subscriptions?.map { + it.url?.replace("https://www.youtube.com/channel/", "")!! + } as ArrayList + } else if (type == "application/zip") { + val zis = ZipInputStream(inputStream) + var entry: ZipEntry? = zis.nextEntry + + while (entry != null) { + if (entry.name.endsWith(".csv")) { + inputStream = zis + break + } + entry = zis.nextEntry + inputStream?.bufferedReader()?.readLines()?.forEach { + if (it.isNotBlank()) { + val channelId = it.substringBefore(",") + if (channelId.length == 24) { + channels.add(channelId) + } + } + } + inputStream?.close() + } + } else { + throw IllegalArgumentException("unsupported type") + } + + CoroutineScope(Dispatchers.IO).launch { + SubscriptionHelper.importSubscriptions(channels) + } + + Toast.makeText(activity, R.string.importsuccess, Toast.LENGTH_SHORT).show() + } catch (e: Exception) { + Log.e(TAG, e.toString()) + Toast.makeText( + activity, + R.string.error, + Toast.LENGTH_SHORT + ).show() + } + } + + private fun readTextFromUri(uri: Uri): String { + val stringBuilder = StringBuilder() + activity.contentResolver.openInputStream(uri)?.use { inputStream -> + BufferedReader(InputStreamReader(inputStream)).use { reader -> + var line: String? = reader.readLine() + while (line != null) { + stringBuilder.append(line) + line = reader.readLine() + } + } + } + return stringBuilder.toString() + } +} diff --git a/app/src/main/java/com/github/libretube/util/SubscriptionHelper.kt b/app/src/main/java/com/github/libretube/util/SubscriptionHelper.kt index 19511413f..c917d0900 100644 --- a/app/src/main/java/com/github/libretube/util/SubscriptionHelper.kt +++ b/app/src/main/java/com/github/libretube/util/SubscriptionHelper.kt @@ -65,6 +65,27 @@ object SubscriptionHelper { } } + suspend fun importSubscriptions(newChannels: List) { + if (PreferenceHelper.getToken() != "") { + try { + val token = PreferenceHelper.getToken() + RetrofitInstance.authApi.importSubscriptions( + false, + token, + newChannels + ) + } catch (e: Exception) { + e.printStackTrace() + } + } else { + val channels = PreferenceHelper.getLocalSubscriptions().toMutableList() + newChannels.forEach { + if (!channels.contains(it)) channels += it + } + PreferenceHelper.setLocalSubscriptions(channels) + } + } + fun getFormattedLocalSubscriptions(): String { return PreferenceHelper.getLocalSubscriptions().joinToString(",") }