Merge pull request #982 from Bnyro/master

subscription import refactor and crashes fixed
This commit is contained in:
Bnyro 2022-08-06 11:55:49 +02:00 committed by GitHub
commit 870118aff8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 141 additions and 127 deletions

View File

@ -15,8 +15,6 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
RecyclerView.Adapter<SubscriptionChannelViewHolder>() {
val TAG = "SubChannelAdapter"
private var subscribed = true
override fun getItemCount(): Int {
return subscriptions.size
}
@ -30,6 +28,8 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
override fun onBindViewHolder(holder: SubscriptionChannelViewHolder, position: Int) {
val subscription = subscriptions[position]
var subscribed = true
holder.binding.apply {
subscriptionChannelName.text = subscription.name
ConnectionHelper.loadImage(subscription.avatar, subscriptionChannelImage)

View File

@ -26,14 +26,12 @@ class TrendingAdapter(
override fun getItemCount(): Int {
return if (showAllAtOne) streamItems.size
else if (index >= streamItems.size) streamItems.size - 1
else index
}
fun updateItems() {
index += 10
if (index > streamItems.size) {
index = streamItems.size
}
notifyDataSetChanged()
}

View File

@ -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
)

View File

@ -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<NewPipeSubscription>? = null
)

View File

@ -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<String>
}
private lateinit var getContent: ActivityResultLauncher<String>
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<String>()
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<Preference>(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<String>()
var instanceValues = arrayListOf<String>()
val instanceNames = arrayListOf<String>()
val instanceValues = arrayListOf<String>()
// 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<String>) {
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()
}
}

View File

@ -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<String>()
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<String>
} 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()
}
}

View File

@ -65,6 +65,27 @@ object SubscriptionHelper {
}
}
suspend fun importSubscriptions(newChannels: List<String>) {
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(",")
}