import subscription

This commit is contained in:
rimthekid 2022-03-31 10:34:19 -07:00
parent 347179b59a
commit 6688e7a5de
9 changed files with 105 additions and 66 deletions

View File

@ -10,8 +10,8 @@ android {
applicationId 'com.github.libretube' applicationId 'com.github.libretube'
minSdk 21 minSdk 21
targetSdk 31 targetSdk 31
versionCode 6 versionCode 7
versionName '0.2.4' versionName '0.2.5'
multiDexEnabled true multiDexEnabled true
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
resValue "string", "app_name", "LibreTube" resValue "string", "app_name", "LibreTube"

View File

@ -16,23 +16,10 @@
} }
], ],
"attributes": [], "attributes": [],
"versionCode": 6, "versionCode": 7,
"versionName": "0.2.4", "versionName": "0.2.5",
"outputFile": "app-x86_64-release.apk" "outputFile": "app-x86_64-release.apk"
}, },
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "arm64-v8a"
}
],
"attributes": [],
"versionCode": 6,
"versionName": "0.2.4",
"outputFile": "app-arm64-v8a-release.apk"
},
{ {
"type": "ONE_OF_MANY", "type": "ONE_OF_MANY",
"filters": [ "filters": [
@ -42,10 +29,23 @@
} }
], ],
"attributes": [], "attributes": [],
"versionCode": 6, "versionCode": 7,
"versionName": "0.2.4", "versionName": "0.2.5",
"outputFile": "app-x86-release.apk" "outputFile": "app-x86-release.apk"
}, },
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "arm64-v8a"
}
],
"attributes": [],
"versionCode": 7,
"versionName": "0.2.5",
"outputFile": "app-arm64-v8a-release.apk"
},
{ {
"type": "ONE_OF_MANY", "type": "ONE_OF_MANY",
"filters": [ "filters": [
@ -55,8 +55,8 @@
} }
], ],
"attributes": [], "attributes": [],
"versionCode": 6, "versionCode": 7,
"versionName": "0.2.4", "versionName": "0.2.5",
"outputFile": "app-armeabi-v7a-release.apk" "outputFile": "app-armeabi-v7a-release.apk"
} }
], ],

View File

@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application <application
android:allowBackup="true" android:allowBackup="true"

View File

@ -15,6 +15,7 @@ import android.view.*
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.constraintlayout.motion.widget.MotionLayout import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
@ -49,6 +50,11 @@ class MainActivity : AppCompatActivity() {
RetrofitInstance.url=sharedPreferences.getString("instance", "https://pipedapi.kavin.rocks/")!! RetrofitInstance.url=sharedPreferences.getString("instance", "https://pipedapi.kavin.rocks/")!!
DynamicColors.applyToActivitiesIfAvailable(application) DynamicColors.applyToActivitiesIfAvailable(application)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
when (sharedPreferences.getString("theme_togglee", "A")!!) {
"A" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
"L" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
"D" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
}
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
bottomNavigationView = findViewById(R.id.bottomNav) bottomNavigationView = findViewById(R.id.bottomNav)

View File

@ -52,8 +52,13 @@ interface PipedApi {
@POST("unsubscribe") @POST("unsubscribe")
suspend fun unsubscribe(@Header("Authorization") token: String, @Body subscribe: Subscribe): Message suspend fun unsubscribe(@Header("Authorization") token: String, @Body subscribe: Subscribe): Message
@POST("import")
suspend fun importSubscriptions(@Header("Authorization") token: String, @Body channels: List<String>): Message
//only for fetching servers list //only for fetching servers list
@GET @GET
suspend fun getInstances(@Url url: String): List<Instances> suspend fun getInstances(@Url url: String): List<Instances>
} }

View File

@ -125,4 +125,8 @@ class SearchFragment : Fragment() {
requireActivity().window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) requireActivity().window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
} }
override fun onStop() {
super.onStop()
hideKeyboard()
}
} }

View File

@ -1,13 +1,12 @@
package com.github.libretube package com.github.libretube
import android.Manifest import android.Manifest
import android.content.ContentValues.TAG import android.content.ContentResolver
import android.content.Context import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Environment
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
@ -15,67 +14,83 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import com.blankj.utilcode.util.UriUtils import com.blankj.utilcode.util.UriUtils
import com.github.libretube.obj.Subscribe
import retrofit2.HttpException import retrofit2.HttpException
import java.io.ByteArrayOutputStream import java.io.*
import java.io.IOException
import java.io.InputStream
import java.util.zip.ZipFile import java.util.zip.ZipFile
class Settings : PreferenceFragmentCompat() { class Settings : PreferenceFragmentCompat() {
val TAG = "Settings" val TAG = "Settings"
companion object { companion object {
lateinit var getContent: ActivityResultLauncher<String> lateinit var getContent: ActivityResultLauncher<String>
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? -> getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
if (uri != null) { if (uri != null) {
try{ try{
Log.d(TAG,UriUtils.uri2File(uri).toString()) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val file = UriUtils.uri2File(uri)
var inputStream: InputStream? = null
if (file.extension == "zip") {
var zipfile = ZipFile(file)
var zipentry = // Open a specific media item using ParcelFileDescriptor.
zipfile.getEntry("Takeout/YouTube and YouTube Music/subscriptions/subscriptions.csv") val resolver: ContentResolver =
requireActivity()
.contentResolver
inputStream = zipfile.getInputStream(zipentry) // "rw" for read-and-write;
}else if(file.extension == "csv"){ // "rwt" for truncating or overwriting existing file contents.
inputStream = file.inputStream() val readOnlyMode = "r"
} // uri - I have got from onActivityResult
val baos = ByteArrayOutputStream() //uri = data.getData();
val parcelFile = resolver.openFileDescriptor(uri, readOnlyMode)
inputStream?.use { it.copyTo(baos) } val fileReader = FileReader(parcelFile!!.fileDescriptor)
val reader = BufferedReader(fileReader)
var subscriptions = baos.toByteArray().decodeToString() var line: String?
while (reader.readLine().also { line = it } != null) {
var subscribedCount = 0 Log.d(TAG,reader.readLine())
for (text in subscriptions.lines()) {
if (text.take(24) != "Channel Id,Channel Url,C" && text.take(24).isNotEmpty()) {
subscribe(text.take(24))
subscribedCount++
Log.d(TAG, "subscribed: " + text + " total: " + subscribedCount)
} }
} reader.close()
fileReader.close()
}else{
Log.d(TAG,UriUtils.uri2File(uri).toString())
val file = UriUtils.uri2File(uri)
var inputStream: InputStream? = null
if (file.extension == "zip") {
var zipfile = ZipFile(file)
Toast.makeText( var zipentry =
context, zipfile.getEntry("Takeout/YouTube and YouTube Music/subscriptions/subscriptions.csv")
"Subscribed to " + subscribedCount + " channels.",
Toast.LENGTH_SHORT inputStream = zipfile.getInputStream(zipentry)
).show() }else if(file.extension == "csv"){
inputStream = file.inputStream()
}
val baos = ByteArrayOutputStream()
inputStream?.use { it.copyTo(baos) }
var subscriptions = baos.toByteArray().decodeToString()
var channels: MutableList<String> = emptyList<String>().toMutableList()
var subscribedCount = 0
for (text in subscriptions.lines().subList(1,subscriptions.lines().size)) {
if (text.replace(" ","") != "") {
val channel = text.split(",")[0]
channels.add(channel)
subscribedCount++
Log.d(TAG, "subscribed: " + text + " total: " + subscribedCount)
}
}
subscribe(channels)
}
}catch (e: Exception){ }catch (e: Exception){
Log.e(TAG,e.toString())
Toast.makeText( Toast.makeText(
context, context,
R.string.error, R.string.error,
@ -121,7 +136,9 @@ class Settings : PreferenceFragmentCompat() {
//check StorageAccess //check StorageAccess
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Log.d("myz", "" + Build.VERSION.SDK_INT) Log.d("myz", "" + Build.VERSION.SDK_INT)
if (!Environment.isExternalStorageManager()) { if (ContextCompat.checkSelfPermission(this.requireContext(), Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions( ActivityCompat.requestPermissions(
this.requireActivity(), arrayOf( this.requireActivity(), arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,
@ -218,15 +235,12 @@ class Settings : PreferenceFragmentCompat() {
} }
private fun subscribe(channel_id: String) { private fun subscribe(channels: List<String>) {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { val response = try {
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE) val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
RetrofitInstance.api.subscribe( RetrofitInstance.api.importSubscriptions(sharedPref?.getString("token", "")!!,channels)
sharedPref?.getString("token", "")!!,
Subscribe(channel_id)
)
} catch (e: IOException) { } catch (e: IOException) {
Log.e(TAG, "IOException, you might not have internet connection") Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated return@launchWhenCreated
@ -234,9 +248,17 @@ class Settings : PreferenceFragmentCompat() {
Log.e(TAG, "HttpException, unexpected response$e") Log.e(TAG, "HttpException, unexpected response$e")
return@launchWhenCreated return@launchWhenCreated
} }
if(response.message == "ok"){
Toast.makeText(
context,
R.string.importsuccess,
Toast.LENGTH_SHORT
).show()
}
} }
} }
run() run()
} }
} }

View File

@ -23,6 +23,7 @@
<string name="region">Choose a region</string> <string name="region">Choose a region</string>
<string name="login_register">Login/Register</string> <string name="login_register">Login/Register</string>
<string name="please_login">Please Login or Register from the settings to show your Subscriptions!</string> <string name="please_login">Please Login or Register from the settings to show your Subscriptions!</string>
<string name="importsuccess">Subscribed successfully!</string>
<string name="subscribeIsEmpty">Subscribe to some channels first!</string> <string name="subscribeIsEmpty">Subscribe to some channels first!</string>
<string name="cannotDownload">Can\'t Download this stream!</string> <string name="cannotDownload">Can\'t Download this stream!</string>
<string name="dlcomplete">Download is completed!</string> <string name="dlcomplete">Download is completed!</string>

View File

@ -59,7 +59,7 @@
app:key="grid" app:key="grid"
app:entries="@array/grid" app:entries="@array/grid"
app:entryValues="@array/grid" app:entryValues="@array/grid"
app:defaultValue="" app:defaultValue="@integer/grid_items"
android:icon="@drawable/ic_column" android:icon="@drawable/ic_column"
app:useSimpleSummaryProvider="true" app:useSimpleSummaryProvider="true"
/> />