Skip to content
This repository has been archived by the owner on Nov 27, 2024. It is now read-only.

Commit

Permalink
Merge pull request #34 from kuylar/feat/better_setup_screen
Browse files Browse the repository at this point in the history
Redo the whole setup screen
  • Loading branch information
kuylar authored Jun 4, 2024
2 parents d3b8fca + 64bf6b4 commit 306e58e
Show file tree
Hide file tree
Showing 23 changed files with 915 additions and 228 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Make sure that your pull request is based off of the latest `master` branch.

## PR titles / content

Please make sure to follow the default PR template (available in [.github/pull_request_template.md](https://github.com/kuylar/lighttube-android/blob/master/.github/pull_request_template.md)).
Please make sure to follow the default PR template (available in [.github/pull_request_template.md](https://github.com/lighttube-org/lighttube-android/blob/master/.github/pull_request_template.md)).

Also, make sure that your PR titles describe what it does in a brief but understandable way.

Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# LightTube Android

> ⚠️ This app is in early development. Use at your own risk
> [!WARNING]
> This app is in early development. Use at your own risk
A Material You design YouTube client, using [LightTube][1] to sync subscriptions & playlists between
devices
Expand All @@ -13,9 +14,9 @@ devices
| Subscription Feeds | Complete |
| Dislike Counts (using [ReturnYouTubeDislike][2]) | Complete |
| [SponsorBlock][3] | Complete |
| Playlists | Complete |
| Video Downloads & Offline Playback | Planned |
| Background playback | Planned |
| Playlists | Planned |
| More to possibly come soon | |

## Installing
Expand All @@ -26,10 +27,10 @@ devices
3. Scroll down, and download the artifact (named LightTube, you might need to log into GitHub)
4. Unzip the file & install it on your device

[1]: https://github.com/kuylar/lighttube
[1]: https://github.com/lighttube-org/lighttube

[2]: https://returnyoutubedislike.com/

[3]: https://sponsor.ajay.app/

[4]: https://github.com/kuylar/lighttube-android/actions
[4]: https://github.com/lighttube-org/lighttube-android/actions
4 changes: 2 additions & 2 deletions app/src/main/java/dev/kuylar/lighttube/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class Utils {
}

private fun getUserAgent(): String =
"LightTube-Android/${BuildConfig.VERSION_NAME} (https://github.com/kuylar/lighttube-android)"
"LightTube-Android/${BuildConfig.VERSION_NAME} (https://github.com/lighttube-org/lighttube-android)"

fun getDislikeCount(videoId: String): Long {
try {
Expand Down Expand Up @@ -136,7 +136,7 @@ class Utils {
var updateInfo: UpdateInfo? = null
try {
val req = Request.Builder().apply {
url("https://api.github.com/repos/kuylar/lighttube-android/releases/latest")
url("https://api.github.com/repos/lighttube-org/lighttube-android/releases/latest")
header("User-Agent", getUserAgent())
}.build()

Expand Down
11 changes: 8 additions & 3 deletions app/src/main/java/dev/kuylar/lighttube/api/LightTubeApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import java.io.IOException
import java.net.URLEncoder


class LightTubeApi(context: Context) {
class LightTubeApi {
private val tag = "LightTubeApi"
private val client = OkHttpClient()
private val gson = GsonBuilder().apply {
Expand All @@ -44,16 +44,21 @@ class LightTubeApi(context: Context) {
val host: String
private val refreshToken: String?

init {
constructor(context: Context, refreshToken: String? = null) {
val sp = context.getSharedPreferences("main", Context.MODE_PRIVATE)
host = sp.getString("instanceHost", "")!!
refreshToken = sp.getString("refreshToken", null)
this.refreshToken = refreshToken ?: sp.getString("refreshToken", null)
Log.i(
tag,
"Initialized the API for $host ${if (refreshToken != null) "with" else "without"} a refresh token"
)
}

constructor(instance: String) {
host = instance
refreshToken = null
}

@Throws(LightTubeException::class, IOException::class)
private fun <T> get(
token: TypeToken<ApiResponse<T>>,
Expand Down
71 changes: 63 additions & 8 deletions app/src/main/java/dev/kuylar/lighttube/api/UtilityApi.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
package dev.kuylar.lighttube.api

import android.app.Activity
import android.util.Log
import android.view.View
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.reflect.TypeToken
import dev.kuylar.lighttube.R
import dev.kuylar.lighttube.api.models.InstanceInfo
import dev.kuylar.lighttube.databinding.ItemInstanceBinding
import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.Request
import java.util.Date

class UtilityApi {
companion object {
private val http = OkHttpClient()
private var refreshToken: String = ""
private var accessToken: String = ""
private var tokenExpiryTimestamp: Long = 0

fun getInstances(): ArrayList<LightTubeInstance> {
val request: Request = Request.Builder()
.url("https://raw.githubusercontent.com/kuylar/lighttube/master/public_instances.json")
.url("https://lighttube.kuylar.dev/instances")
.build()

OkHttpClient().newCall(request).execute().use { response ->
http.newCall(request).execute().use { response ->
return Gson().fromJson(
response.body!!.string(),
object : TypeToken<ArrayList<LightTubeInstance>>() {}.type
Expand Down Expand Up @@ -58,7 +64,7 @@ class UtilityApi {
.build()


OkHttpClient().newCall(request).execute().use { response ->
http.newCall(request).execute().use { response ->
val res = Gson().fromJson(
response.body!!.string(),
JsonObject::class.java
Expand Down Expand Up @@ -91,7 +97,7 @@ class UtilityApi {
.post(body)
.build()

OkHttpClient().newCall(request).execute().use { response ->
http.newCall(request).execute().use { response ->
val res = Gson().fromJson(
response.body!!.string(),
JsonObject::class.java
Expand All @@ -112,8 +118,57 @@ class UtilityApi {
}
}

class LightTubeInstance(
data class LightTubeInstance(
val host: String,
val api: Boolean,
val accounts: Boolean
)
val country: String,
val scheme: String,
val isCloudflare: Boolean,
val apiEnabled: Boolean,
val proxyEnabled: String,
val accountsEnabled: Boolean,
) {
fun fillBinding(binding: ItemInstanceBinding, activity: Activity) {
val context = binding.root.context
val instanceInfo: InstanceInfo
try {
instanceInfo = LightTubeApi("$scheme://$host").getInstanceInfo()
} catch (e: Exception) {
activity.runOnUiThread {
binding.loading.visibility = View.GONE
Log.e("UtilityApi.fillBinding", "Failed to get information about instance $host")
binding.instanceTitle.text = arrayOf(getFlag(), host).joinToString(" ")
binding.instanceDescription.text = context.getString(
R.string.setup_instance_load_fail,
context.getString(R.string.setup_instance_load_fail_this)
)
}
return
}

activity.runOnUiThread {
binding.loading.visibility = View.GONE
binding.instanceTitle.text = arrayOf(getFlag(), host).joinToString(" ")
binding.instanceDescription.text = if (instanceInfo.type != "lighttube")
context.getString(R.string.setup_instance_invalid, instanceInfo.type)
else if (apiEnabled)
context.getString(
R.string.template_instance_info,
instanceInfo.version,
if (accountsEnabled) context.getString(R.string.enabled) else context.getString(
R.string.disabled
),
if (proxyEnabled == "all") context.getString(R.string.enabled) else context.getString(
R.string.disabled
)
)
else context.getString(R.string.setup_instance_api_disabled)
binding.instanceCloudflare.visibility = if (isCloudflare) View.VISIBLE else View.GONE
}
}

private fun getFlag(): String {
val firstLetter = Character.codePointAt(country, 0) - 0x41 + 0x1F1E6
val secondLetter = Character.codePointAt(country, 1) - 0x41 + 0x1F1E6
return String(Character.toChars(firstLetter)) + String(Character.toChars(secondLetter))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ class InstanceInfo (
val type: String,
val version: String,
val motd: String,
val allowsAPI: Boolean,
val allowsApi: Boolean,
val allowsNewUsers: Boolean,
val allowsOauthAPI: Boolean,
val allowsOauthApi: Boolean,
val allowsThirdPartyProxyUsage: Boolean
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import android.webkit.WebView
import android.webkit.WebViewClient


class LoginWebViewClient(val onTokenReceived: (String) -> Unit, val showLoading: (Boolean) -> Unit) : WebViewClient() {
class LoginWebViewClient(
private val onTokenReceived: (String) -> Unit,
private val showLoading: (Boolean) -> Unit
) : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
if (request == null) return false;
return if (request.url != null && request.url.scheme == "lighttube") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package dev.kuylar.lighttube.ui.activity

import android.app.ProgressDialog
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import com.google.android.material.snackbar.Snackbar
import dev.kuylar.lighttube.R
import dev.kuylar.lighttube.api.LightTubeApi
import dev.kuylar.lighttube.api.UtilityApi
import dev.kuylar.lighttube.databinding.ActivityLoginBinding
import dev.kuylar.lighttube.ui.LoginWebViewClient
import java.net.URLEncoder
import java.nio.charset.Charset
import kotlin.concurrent.thread


Expand All @@ -31,12 +33,14 @@ class LoginActivity : AppCompatActivity() {
binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)

val isRegister = intent.extras?.getBoolean("register", false) ?: false

sp = getSharedPreferences("main", MODE_PRIVATE)

val client = LoginWebViewClient(this::onTokenReceived, this::showProgressBarLoading)
binding.loginWebView.webViewClient = client
binding.loginWebView.settings.databaseEnabled = true
binding.loginWebView.loadUrl(getOauthUrl(scopes))
binding.loginWebView.loadUrl(if (isRegister) getRegisterUrl(scopes) else getOauthUrl(scopes))
Snackbar.make(binding.root, R.string.login_hint, Snackbar.LENGTH_LONG).show()
}

Expand All @@ -60,6 +64,23 @@ class LoginActivity : AppCompatActivity() {
}"
}

private fun getRegisterUrl(scopes: List<String>): String {
val sp = getSharedPreferences("main", MODE_PRIVATE)
val host = sp.getString("instanceHost", "")
val redirectUrl =
"/oauth2/authorize?response_type=code&client_id=LightTube Android&redirect_uri=lighttube://login&scope=${
scopes.joinToString(
" "
)
}"
return "$host/account/register?redirectUrl=${
URLEncoder.encode(
redirectUrl,
Charset.defaultCharset().name()
)
}"
}

private fun onTokenReceived(token: String) {
showProgressBarLoading(true)
val dialog = ProgressDialog.show(
Expand All @@ -74,13 +95,13 @@ class LoginActivity : AppCompatActivity() {
token,
"lighttube://login"
)
sp.edit().apply {
putString("refreshToken", token)
apply()
}
val api = LightTubeApi(this)
val api = LightTubeApi(this, token)
val user = api.getCurrentUser()
val username = user.data!!.userID
sp.edit {
putString("refreshToken", token)
putString("username", username)
}
runOnUiThread {
dialog.dismiss()
Toast.makeText(
Expand All @@ -89,7 +110,7 @@ class LoginActivity : AppCompatActivity() {
Toast.LENGTH_LONG
).show()
showProgressBarLoading(false)
startActivity(Intent(this, MainActivity::class.java))
setResult(RESULT_OK)
finish()
}
}
Expand Down
Loading

0 comments on commit 306e58e

Please sign in to comment.