mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-15 14:50:30 +05:30
Merge branch 'libre-tube:master' into master
This commit is contained in:
commit
ef6cbeb591
72
README.md
72
README.md
@ -6,60 +6,70 @@
|
||||
[![Telegram](https://libre-tube.github.io/assets/tg-widget.svg)](https://t.me/libretube)
|
||||
[![Twitter](https://libre-tube.github.io/assets/tw-widget.svg)](https://twitter.com/libretube)
|
||||
[![Reddit](https://libre-tube.github.io/assets/rd-widget.svg)](https://www.reddit.com/r/Libretube/)
|
||||
|
||||
[<img src="https://libre-tube.github.io/assets/fdrload.png" alt="Get it on F-Droid" height="80">](https://f-droid.org/en/packages/com.github.libretube/)
|
||||
[<img src="https://libre-tube.github.io/assets/izzyload.png" alt="Get it on IzzyOnDroid" height="80">](https://apt.izzysoft.de/fdroid/index/apk/com.github.libretube)
|
||||
[<img src="https://libre-tube.github.io/assets/ghload.png" alt="Get it on GitHub" height="80">](https://github.com/libre-tube/LibreTube/releases/latest)
|
||||
[<img src="https://libre-tube.github.io/assets/tgload.png" alt="Get it on GitHub" height="80">](https://t.me/LibreTube)
|
||||
|
||||
</div><div align="center" style="width:100%; display:flex; justify-content:space-between;">
|
||||
|
||||
[<img src="https://libre-tube.github.io/assets/fdrload.png" alt="Get it on F-Droid" width="30%">](https://f-droid.org/en/packages/com.github.libretube/)
|
||||
[<img src="https://libre-tube.github.io/assets/izzyload.png" alt="Get it on IzzyOnDroid" width="30%">](https://apt.izzysoft.de/fdroid/index/apk/com.github.libretube)<br/>
|
||||
[<img src="https://libre-tube.github.io/assets/ghload.png" alt="Get it on GitHub" width="30%">](https://github.com/libre-tube/LibreTube/releases/latest)
|
||||
[<img src="https://libre-tube.github.io/assets/tgload.png" alt="Get it on GitHub" width="30%">](https://t.me/LibreTube)
|
||||
|
||||
</div>
|
||||
|
||||
## Screenshots
|
||||
## 📱 Screenshots
|
||||
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_1.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_1.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_2.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_2.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_3.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_3.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_4.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_4.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_5.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_5.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_6.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_6.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_7.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_7.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_8.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_8.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.png)
|
||||
<div style="width:100%; display:flex; justify-content:space-between;">
|
||||
|
||||
## Features
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_1.png" width=30% alt="Home">](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_1.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_2.png" width=30% alt="Search">](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_2.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_3.png" width=30% alt="Player">](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_3.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_4.png" width=30% alt="Channel">](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_4.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_5.png" width=30% alt="Settings">](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_5.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_6.png" width=30% alt="Subscriptions">](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_6.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_7.png" width=30% alt="Subscriptions List">](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_7.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_8.png" width=30% alt="Library">](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_8.png)
|
||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.png" width=30% alt="Playlist">](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.png)
|
||||
|
||||
| Feature ||
|
||||
| - | - |
|
||||
| User Accounts | ✅ |
|
||||
| Subscriptions | ✅ |
|
||||
| User Playlists | ✅ |
|
||||
| Channel Playlists | ✅ |
|
||||
| Search Filters | ✅ |
|
||||
| SponsorBlock | ✅ |
|
||||
| Subtitles | ✅ |
|
||||
| Comments | ✅ |
|
||||
</div>
|
||||
|
||||
## ⭐ Features
|
||||
|
||||
| Feature | |
|
||||
| ----------------- | --- |
|
||||
| User Accounts | ✅ |
|
||||
| Subscriptions | ✅ |
|
||||
| User Playlists | ✅ |
|
||||
| Channel Playlists | ✅ |
|
||||
| Search Filters | ✅ |
|
||||
| SponsorBlock | ✅ |
|
||||
| Subtitles | ✅ |
|
||||
| Comments | ✅ |
|
||||
|
||||
## 😇 Contributing
|
||||
|
||||
## Contributing
|
||||
Whether you have ideas, translations, design changes, code cleaning, or real heavy code changes, help is always welcome.The more is done the better it gets!
|
||||
|
||||
If creating a pull request, please make sure to format your code (preferred ktlint) before.
|
||||
|
||||
If opening an issue without following the issue template, we will ignore the issue and force close it.
|
||||
|
||||
WARNING: THIS IS A BETA VERSION, THEREFORE YOU MAY ENCOUNTER BUGS. IF YOU DO, OPEN AN ISSUE VIA OUR GITHUB REPOSITORY.
|
||||
> **⚠️ WARNING: This is a beta version, therefore you may encounter bugs. If you do, open an issue via our github repository.**
|
||||
|
||||
### 📝 Translation
|
||||
|
||||
### Translation
|
||||
<a href="https://hosted.weblate.org/projects/libretube/#languages">
|
||||
<img src="https://hosted.weblate.org/widgets/libretube/-/287x66-grey.png" alt="Translation status" />
|
||||
</a>
|
||||
|
||||
### Donate
|
||||
### 💰 Donate
|
||||
|
||||
[![Support us on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dlibretubeteam%26type%3Dpatrons&style=for-the-badge)](https://patreon.com/libretubeteam)
|
||||
|
||||
**BTC:** `bc1q0hk2smc74ej8fxupfrp05wk867e54e2zztnxfc`
|
||||
|
||||
**XMR:** `44txdmy4E5bDzMYQJh1ZSoHbrp1sWfpGa2FYg26L2ya8EaRejPsh42yVrYhepW9P4YWvrqmTZvms35z5FDgqy1xcVewk18d`
|
||||
|
||||
## Mirrors (read-only)
|
||||
## 🪞 Mirrors (read-only)
|
||||
|
||||
<a href="https://gitlab.com/libretube/LibreTube">GitLab</a></p>
|
||||
<a href="https://notabug.org/LibreTube/LibreTube">NotABug</a></p>
|
||||
|
@ -10,13 +10,17 @@ android {
|
||||
applicationId 'com.github.libretube'
|
||||
minSdk 21
|
||||
targetSdk 31
|
||||
versionCode 12
|
||||
versionName '0.3.2'
|
||||
versionCode 13
|
||||
versionName '0.3.3'
|
||||
multiDexEnabled true
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
resValue "string", "app_name", "LibreTube"
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
|
@ -22,15 +22,17 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Purple">
|
||||
<activity
|
||||
android:name=".util.Player"
|
||||
android:name=".activities.Player"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
android:name=".activities.NoInternetActivity"
|
||||
android:label="@string/noInternet" />
|
||||
<activity
|
||||
android:name=".activities.SettingsActivity"
|
||||
android:label="@string/settings" />
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:name=".activities.MainActivity"
|
||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
|
||||
android:exported="true"
|
||||
android:hardwareAccelerated="true"
|
||||
@ -53,7 +55,7 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_gradient_round"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:targetActivity=".MainActivity">
|
||||
android:targetActivity=".activities.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@ -70,7 +72,7 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_fire_round"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:targetActivity=".MainActivity">
|
||||
android:targetActivity=".activities.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@ -87,7 +89,7 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_flame_round"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:targetActivity=".MainActivity">
|
||||
android:targetActivity=".activities.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@ -104,7 +106,7 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_shaped_round"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:targetActivity=".MainActivity">
|
||||
android:targetActivity=".activities.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@ -121,7 +123,7 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_torch_round"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:targetActivity=".MainActivity">
|
||||
android:targetActivity=".activities.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@ -138,7 +140,7 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_legacy_round"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:targetActivity=".MainActivity">
|
||||
android:targetActivity=".activities.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@ -155,7 +157,7 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_bird_round"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:targetActivity=".MainActivity">
|
||||
android:targetActivity=".activities.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@ -163,7 +165,7 @@
|
||||
</activity-alias>
|
||||
|
||||
<activity
|
||||
android:name=".RouterActivity"
|
||||
android:name=".activities.RouterActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleInstance">
|
||||
<intent-filter>
|
||||
|
@ -1,8 +1,23 @@
|
||||
package com.github.libretube
|
||||
|
||||
/**
|
||||
* API link for the update checker
|
||||
*/
|
||||
const val GITHUB_API_URL = "https://api.github.com/repos/libre-tube/LibreTube/releases/latest"
|
||||
|
||||
/**
|
||||
* Links for the about fragment
|
||||
*/
|
||||
const val WEBSITE_URL = "https://libre-tube.github.io/"
|
||||
const val AUTHORS_URL = "https://github.com/libre-tube/LibreTube/graphs/contributors"
|
||||
const val DONATE_URL = "https://libre-tube.github.io/#donate"
|
||||
const val CONTRIBUTING_URL = "https://github.com/libre-tube/LibreTube#donate"
|
||||
const val DONATE_URL = "https://github.com/libre-tube/LibreTube#donate"
|
||||
const val GITHUB_URL = "https://github.com/libre-tube/LibreTube"
|
||||
const val PIPED_GITHUB_URL = "https://github.com/TeamPiped/Piped"
|
||||
|
||||
/**
|
||||
* Social media links for the community fragment
|
||||
*/
|
||||
const val TELEGRAM_URL = "https://t.me/libretube"
|
||||
const val MATRIX_URL = "https://matrix.to/#/#LibreTube:matrix.org"
|
||||
const val DISCORD_URL = "https://discord.com/invite/Qc34xCj2GV"
|
||||
const val REDDIT_URL = "https://www.reddit.com/r/Libretube/"
|
||||
const val TWITTER_URL = "https://twitter.com/libretube"
|
||||
|
@ -1,57 +1,70 @@
|
||||
package com.github.libretube
|
||||
package com.github.libretube.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.res.Configuration
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.WindowInsets
|
||||
import android.view.WindowInsetsController
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.ActivityMainBinding
|
||||
import com.github.libretube.fragments.PlayerFragment
|
||||
import com.github.libretube.fragments.isFullScreen
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.services.ClosingService
|
||||
import com.github.libretube.util.ConnectionHelper
|
||||
import com.github.libretube.util.CronetHelper
|
||||
import com.github.libretube.util.LocaleHelper
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import com.google.android.material.elevation.SurfaceColors
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
val TAG = "MainActivity"
|
||||
|
||||
lateinit var bottomNavigationView: BottomNavigationView
|
||||
private lateinit var toolbar: Toolbar
|
||||
lateinit var binding: ActivityMainBinding
|
||||
|
||||
lateinit var navController: NavController
|
||||
private var startFragmentId = R.id.homeFragment
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
DynamicColors.applyToActivityIfAvailable(this)
|
||||
/**
|
||||
* apply dynamic colors if enabled
|
||||
*/
|
||||
val materialColorsEnabled = PreferenceHelper
|
||||
.getString(this, "accent_color", "purple") == "my"
|
||||
if (materialColorsEnabled) {
|
||||
// apply dynamic colors to the current activity
|
||||
DynamicColors.applyToActivityIfAvailable(this)
|
||||
// apply dynamic colors to the all other activities
|
||||
DynamicColors.applyToActivitiesIfAvailable(application)
|
||||
}
|
||||
|
||||
// set the theme
|
||||
ThemeHelper.updateTheme(this)
|
||||
// set the language
|
||||
LocaleHelper.updateLanguage(this)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// start service that gets called on closure
|
||||
@ -61,74 +74,89 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
RetrofitInstance.url =
|
||||
PreferenceHelper.getString(this, "selectInstance", "https://pipedapi.kavin.rocks/")!!
|
||||
|
||||
ThemeHelper.updateTheme(this)
|
||||
LocaleHelper.updateLanguage(this)
|
||||
// set auth instance
|
||||
RetrofitInstance.authUrl =
|
||||
if (PreferenceHelper.getBoolean(this, "auth_instance_toggle", false)) {
|
||||
PreferenceHelper.getString(
|
||||
this,
|
||||
"selectAuthInstance",
|
||||
"https://pipedapi.kavin.rocks/"
|
||||
)!!
|
||||
} else {
|
||||
RetrofitInstance.url
|
||||
}
|
||||
|
||||
// show noInternet Activity if no internet available on app startup
|
||||
if (!isNetworkAvailable(this)) {
|
||||
setContentView(R.layout.activity_nointernet)
|
||||
findViewById<Button>(R.id.retry_button).setOnClickListener {
|
||||
recreate()
|
||||
}
|
||||
findViewById<ImageView>(R.id.noInternet_settingsImageView).setOnClickListener {
|
||||
val intent = Intent(this, SettingsActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
if (!ConnectionHelper.isNetworkAvailable(this)) {
|
||||
val noInternetIntent = Intent(this, NoInternetActivity::class.java)
|
||||
startActivity(noInternetIntent)
|
||||
} else {
|
||||
setContentView(R.layout.activity_main)
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
|
||||
bottomNavigationView = findViewById(R.id.bottomNav)
|
||||
navController = findNavController(R.id.fragment)
|
||||
bottomNavigationView.setupWithNavController(navController)
|
||||
binding.bottomNav.setupWithNavController(navController)
|
||||
|
||||
// gets the surface color of the bottom navigation view
|
||||
val color = SurfaceColors.getColorForElevation(this, 10F)
|
||||
|
||||
// sets the navigation bar color to the previously calculated color
|
||||
window.navigationBarColor = color
|
||||
|
||||
// hide the trending page if enabled
|
||||
val hideTrendingPage = PreferenceHelper.getBoolean(this, "hide_trending_page", false)
|
||||
if (hideTrendingPage) bottomNavigationView.menu.findItem(R.id.home2).isVisible = false
|
||||
if (hideTrendingPage) binding.bottomNav.menu.findItem(R.id.homeFragment).isVisible =
|
||||
false
|
||||
|
||||
// navigate to the default start tab
|
||||
when (PreferenceHelper.getString(this, "default_tab", "home")) {
|
||||
"home" -> navController.navigate(R.id.home2)
|
||||
"subscriptions" -> navController.navigate(R.id.subscriptions)
|
||||
"library" -> navController.navigate(R.id.library)
|
||||
// save start tab fragment id
|
||||
startFragmentId = when (PreferenceHelper.getString(this, "default_tab", "home")) {
|
||||
"home" -> R.id.homeFragment
|
||||
"subscriptions" -> R.id.subscriptionsFragment
|
||||
"library" -> R.id.libraryFragment
|
||||
else -> R.id.homeFragment
|
||||
}
|
||||
|
||||
bottomNavigationView.setOnItemSelectedListener {
|
||||
// set default tab as start fragment
|
||||
navController.graph.setStartDestination(startFragmentId)
|
||||
|
||||
// navigate to the default fragment
|
||||
navController.navigate(startFragmentId)
|
||||
|
||||
binding.bottomNav.setOnItemSelectedListener {
|
||||
// clear backstack if it's the start fragment
|
||||
if (startFragmentId == it.itemId) navController.backQueue.clear()
|
||||
// set menu item on click listeners
|
||||
when (it.itemId) {
|
||||
R.id.home2 -> {
|
||||
navController.backQueue.clear()
|
||||
navController.navigate(R.id.home2)
|
||||
R.id.homeFragment -> {
|
||||
navController.navigate(R.id.homeFragment)
|
||||
}
|
||||
R.id.subscriptions -> {
|
||||
// navController.backQueue.clear()
|
||||
navController.navigate(R.id.subscriptions)
|
||||
R.id.subscriptionsFragment -> {
|
||||
navController.navigate(R.id.subscriptionsFragment)
|
||||
}
|
||||
R.id.library -> {
|
||||
// navController.backQueue.clear()
|
||||
navController.navigate(R.id.library)
|
||||
R.id.libraryFragment -> {
|
||||
navController.navigate(R.id.libraryFragment)
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
toolbar = findViewById(R.id.toolbar)
|
||||
val typedValue = TypedValue()
|
||||
this.theme.resolveAttribute(R.attr.colorPrimary, typedValue, true)
|
||||
val hexColor = String.format("#%06X", (0xFFFFFF and typedValue.data))
|
||||
val appName = HtmlCompat.fromHtml(
|
||||
"Libre<span style='color:$hexColor';>Tube</span>",
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
)
|
||||
toolbar.title = appName
|
||||
/**
|
||||
* don't remove this line
|
||||
* this prevents reselected items at the bottomNav to be duplicated in the backstack
|
||||
*/
|
||||
binding.bottomNav.setOnItemReselectedListener {}
|
||||
|
||||
toolbar.setNavigationOnClickListener {
|
||||
binding.toolbar.title = ThemeHelper.getStyledAppName(this)
|
||||
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
// settings activity stuff
|
||||
val intent = Intent(this, SettingsActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
toolbar.setOnMenuItemClickListener {
|
||||
binding.toolbar.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.action_search -> {
|
||||
navController.navigate(R.id.searchFragment)
|
||||
@ -139,28 +167,6 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun isNetworkAvailable(context: Context): Boolean {
|
||||
val connectivityManager =
|
||||
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
val nw = connectivityManager.activeNetwork ?: return false
|
||||
val actNw = connectivityManager.getNetworkCapabilities(nw) ?: return false
|
||||
return when {
|
||||
// WiFi
|
||||
actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
||||
// Mobile
|
||||
actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
||||
// Ethernet
|
||||
actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
|
||||
// Bluetooth
|
||||
actNw.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true
|
||||
else -> false
|
||||
}
|
||||
} else {
|
||||
return connectivityManager.activeNetworkInfo?.isConnected ?: false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val intentData: Uri? = intent?.data
|
||||
@ -183,7 +189,7 @@ class MainActivity : AppCompatActivity() {
|
||||
channel = channel!!.replace("/c/", "")
|
||||
channel = channel.replace("/user/", "")
|
||||
val bundle = bundleOf("channel_id" to channel)
|
||||
navController.navigate(R.id.channel, bundle)
|
||||
navController.navigate(R.id.channelFragment, bundle)
|
||||
} else if (data.path!!.contains("/playlist")) {
|
||||
Log.i(TAG, "URI Type: Playlist")
|
||||
var playlist = data.query!!
|
||||
@ -266,34 +272,37 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
try {
|
||||
val mainMotionLayout = findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||
if (mainMotionLayout.progress == 0.toFloat()) {
|
||||
mainMotionLayout.transitionToEnd()
|
||||
findViewById<ConstraintLayout>(R.id.main_container).isClickable = false
|
||||
val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
|
||||
motionLayout.transitionToEnd()
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
with(motionLayout) {
|
||||
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
|
||||
enableTransition(R.id.yt_transition, true)
|
||||
}
|
||||
findViewById<LinearLayout>(R.id.linLayout).visibility = View.VISIBLE
|
||||
isFullScreen = false
|
||||
} else {
|
||||
navController.popBackStack()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// try catch to prevent nointernet activity to crash
|
||||
if (binding.mainMotionLayout.progress == 0F) {
|
||||
try {
|
||||
navController.popBackStack()
|
||||
moveTaskToBack(true)
|
||||
minimizePlayer()
|
||||
} catch (e: Exception) {
|
||||
super.onBackPressed()
|
||||
if (navController.currentDestination?.id == startFragmentId) {
|
||||
super.onBackPressed()
|
||||
} else {
|
||||
navController.popBackStack()
|
||||
}
|
||||
}
|
||||
} else if (navController.currentDestination?.id == startFragmentId) {
|
||||
super.onBackPressed()
|
||||
} else {
|
||||
navController.popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
private fun minimizePlayer() {
|
||||
binding.mainMotionLayout.transitionToEnd()
|
||||
findViewById<ConstraintLayout>(R.id.main_container).isClickable = false
|
||||
val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
|
||||
motionLayout.transitionToEnd()
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
with(motionLayout) {
|
||||
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
|
||||
enableTransition(R.id.yt_transition, true)
|
||||
}
|
||||
findViewById<LinearLayout>(R.id.linLayout).visibility = View.VISIBLE
|
||||
isFullScreen = false
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
val orientation = newConfig.orientation
|
||||
@ -362,10 +371,6 @@ fun Fragment.hideKeyboard() {
|
||||
view?.let { activity?.hideKeyboard(it) }
|
||||
}
|
||||
|
||||
fun Activity.hideKeyboard() {
|
||||
hideKeyboard(currentFocus ?: View(this))
|
||||
}
|
||||
|
||||
fun Context.hideKeyboard(view: View) {
|
||||
val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
|
@ -0,0 +1,51 @@
|
||||
package com.github.libretube.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.ActivityNointernetBinding
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.ConnectionHelper
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
class NoInternetActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityNointernetBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
/**
|
||||
* apply dynamic colors if enabled
|
||||
*/
|
||||
val materialColorsEnabled = PreferenceHelper
|
||||
.getString(this, "accent_color", "purple") == "my"
|
||||
if (materialColorsEnabled) {
|
||||
DynamicColors.applyToActivityIfAvailable(this)
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityNointernetBinding.inflate(layoutInflater)
|
||||
// retry button
|
||||
binding.retryButton.setOnClickListener {
|
||||
if (ConnectionHelper.isNetworkAvailable(this)) {
|
||||
ThemeHelper.restartMainActivity(this)
|
||||
} else {
|
||||
val snackBar = Snackbar
|
||||
.make(binding.root, R.string.turnInternetOn, Snackbar.LENGTH_LONG)
|
||||
snackBar.show()
|
||||
}
|
||||
}
|
||||
binding.noInternetSettingsImageView.setOnClickListener {
|
||||
val intent = Intent(this, SettingsActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
setContentView(binding.root)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
finishAffinity()
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.github.libretube
|
||||
package com.github.libretube.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
@ -6,6 +6,7 @@ import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
|
||||
class RouterActivity : AppCompatActivity() {
|
@ -1,13 +1,12 @@
|
||||
package com.github.libretube
|
||||
package com.github.libretube.activities
|
||||
|
||||
import android.app.NotificationManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.ActivitySettingsBinding
|
||||
import com.github.libretube.preferences.MainSettings
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
import com.google.android.material.color.DynamicColors
|
||||
@ -17,24 +16,27 @@ var requireMainActivityRestart = false
|
||||
|
||||
class SettingsActivity : AppCompatActivity() {
|
||||
val TAG = "SettingsActivity"
|
||||
lateinit var binding: ActivitySettingsBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
DynamicColors.applyToActivityIfAvailable(this)
|
||||
ThemeHelper.updateTheme(this)
|
||||
|
||||
// makes the preference dialogs use material dialogs
|
||||
setTheme(R.style.MaterialAlertDialog)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
overridePendingTransition(50, 50)
|
||||
}
|
||||
val view = this.findViewById<View>(android.R.id.content)
|
||||
view.alpha = 0F
|
||||
view.animate().alpha(1F).duration = 300
|
||||
binding.root.alpha = 0F
|
||||
binding.root.animate().alpha(1F).duration = 300
|
||||
|
||||
setContentView(R.layout.activity_settings)
|
||||
setContentView(binding.root)
|
||||
|
||||
val backButton = view.findViewById<ImageButton>(R.id.back_imageButton)
|
||||
backButton.setOnClickListener {
|
||||
binding.backImageButton.setOnClickListener {
|
||||
onBackPressed()
|
||||
}
|
||||
|
||||
@ -66,8 +68,11 @@ class SettingsActivity : AppCompatActivity() {
|
||||
.beginTransaction()
|
||||
.replace(R.id.settings, MainSettings())
|
||||
.commit()
|
||||
val topBarTextView = findViewById<TextView>(R.id.topBar_textView)
|
||||
topBarTextView?.text = getString(R.string.settings)
|
||||
changeTopBarText(getString(R.string.settings))
|
||||
}
|
||||
}
|
||||
|
||||
fun changeTopBarText(text: String) {
|
||||
if (this::binding.isInitialized) binding.topBarTextView.text = text
|
||||
}
|
||||
}
|
@ -3,14 +3,12 @@ package com.github.libretube.adapters
|
||||
import android.os.Bundle
|
||||
import android.text.format.DateUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.VideoChannelRowBinding
|
||||
import com.github.libretube.dialogs.VideoOptionsDialog
|
||||
import com.github.libretube.fragments.PlayerFragment
|
||||
import com.github.libretube.obj.StreamItem
|
||||
@ -22,6 +20,7 @@ class ChannelAdapter(
|
||||
private val childFragmentManager: FragmentManager
|
||||
) :
|
||||
RecyclerView.Adapter<ChannelViewHolder>() {
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return videoFeed.size
|
||||
}
|
||||
@ -33,43 +32,41 @@ class ChannelAdapter(
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val cell = layoutInflater.inflate(R.layout.video_channel_row, parent, false)
|
||||
return ChannelViewHolder(cell)
|
||||
val binding = VideoChannelRowBinding.inflate(layoutInflater, parent, false)
|
||||
return ChannelViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ChannelViewHolder, position: Int) {
|
||||
val trending = videoFeed[position]
|
||||
holder.v.findViewById<TextView>(R.id.channel_description).text = trending.title
|
||||
holder.v.findViewById<TextView>(R.id.channel_views).text =
|
||||
trending.views.formatShort() + " • " +
|
||||
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
|
||||
holder.v.findViewById<TextView>(R.id.channel_duration).text =
|
||||
DateUtils.formatElapsedTime(trending.duration!!)
|
||||
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.channel_thumbnail)
|
||||
Picasso.get().load(trending.thumbnail).into(thumbnailImage)
|
||||
holder.v.setOnClickListener {
|
||||
var bundle = Bundle()
|
||||
bundle.putString("videoId", trending.url!!.replace("/watch?v=", ""))
|
||||
var frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
val activity = holder.v.context as AppCompatActivity
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
}
|
||||
holder.v.setOnLongClickListener {
|
||||
val videoId = trending.url!!.replace("/watch?v=", "")
|
||||
VideoOptionsDialog(videoId, holder.v.context)
|
||||
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
||||
true
|
||||
holder.binding.apply {
|
||||
channelDescription.text = trending.title
|
||||
channelViews.text =
|
||||
trending.views.formatShort() + " • " +
|
||||
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
|
||||
channelDuration.text =
|
||||
DateUtils.formatElapsedTime(trending.duration!!)
|
||||
Picasso.get().load(trending.thumbnail).into(channelThumbnail)
|
||||
root.setOnClickListener {
|
||||
var bundle = Bundle()
|
||||
bundle.putString("videoId", trending.url!!.replace("/watch?v=", ""))
|
||||
var frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
val activity = root.context as AppCompatActivity
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
}
|
||||
root.setOnLongClickListener {
|
||||
val videoId = trending.url!!.replace("/watch?v=", "")
|
||||
VideoOptionsDialog(videoId, root.context)
|
||||
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
||||
init {
|
||||
}
|
||||
}
|
||||
class ChannelViewHolder(val binding: VideoChannelRowBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
|
@ -1,12 +1,9 @@
|
||||
package com.github.libretube.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.ChapterColumnBinding
|
||||
import com.github.libretube.obj.ChapterSegment
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
import com.squareup.picasso.Picasso
|
||||
@ -19,21 +16,20 @@ class ChaptersAdapter(
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChaptersViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val cell = layoutInflater.inflate(R.layout.chapter_column, parent, false)
|
||||
return ChaptersViewHolder(cell)
|
||||
val binding = ChapterColumnBinding.inflate(layoutInflater, parent, false)
|
||||
return ChaptersViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ChaptersViewHolder, position: Int) {
|
||||
val chapter = chapters[position]
|
||||
val chapterImage = holder.v.findViewById<ImageView>(R.id.chapter_image)
|
||||
Picasso.get().load(chapter.image).fit().centerCrop().into(chapterImage)
|
||||
holder.binding.apply {
|
||||
Picasso.get().load(chapter.image).fit().centerCrop().into(chapterImage)
|
||||
chapterTitle.text = chapter.title
|
||||
|
||||
val chapterTitle = holder.v.findViewById<TextView>(R.id.chapter_title)
|
||||
chapterTitle.text = chapter.title
|
||||
|
||||
holder.v.setOnClickListener {
|
||||
val chapterStart = chapter.start!!.toLong() * 1000 // s -> ms
|
||||
exoPlayer.seekTo(chapterStart)
|
||||
root.setOnClickListener {
|
||||
val chapterStart = chapter.start!!.toLong() * 1000 // s -> ms
|
||||
exoPlayer.seekTo(chapterStart)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,7 +38,4 @@ class ChaptersAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
class ChaptersViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
||||
init {
|
||||
}
|
||||
}
|
||||
class ChaptersViewHolder(val binding: ChapterColumnBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
|
@ -1,19 +1,17 @@
|
||||
package com.github.libretube.adapters
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.MainActivity
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.MainActivity
|
||||
import com.github.libretube.databinding.CommentsRowBinding
|
||||
import com.github.libretube.obj.Comment
|
||||
import com.github.libretube.obj.CommentsPage
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
@ -29,8 +27,8 @@ class CommentsAdapter(
|
||||
private val videoId: String,
|
||||
private val comments: MutableList<Comment>
|
||||
) : RecyclerView.Adapter<CommentsViewHolder>() {
|
||||
|
||||
private val TAG = "CommentsAdapter"
|
||||
|
||||
private var isLoading = false
|
||||
private var nextpage = ""
|
||||
private var repliesPage = CommentsPage()
|
||||
@ -42,59 +40,61 @@ class CommentsAdapter(
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommentsViewHolder {
|
||||
val commentsView =
|
||||
LayoutInflater.from(parent.context).inflate(R.layout.comments_row, parent, false)
|
||||
return CommentsViewHolder(commentsView)
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val binding = CommentsRowBinding.inflate(layoutInflater, parent, false)
|
||||
return CommentsViewHolder(binding)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: CommentsViewHolder, position: Int) {
|
||||
holder.v.findViewById<TextView>(R.id.comment_infos).text =
|
||||
comments[position].author.toString() +
|
||||
" • " + comments[position].commentedTime.toString()
|
||||
holder.v.findViewById<TextView>(R.id.comment_text).text =
|
||||
comments[position].commentText.toString()
|
||||
val channelImage = holder.v.findViewById<ImageView>(R.id.commentor_image)
|
||||
Picasso.get().load(comments[position].thumbnail).fit().centerCrop().into(channelImage)
|
||||
holder.v.findViewById<TextView>(R.id.likes_textView).text =
|
||||
comments[position].likeCount?.toLong().formatShort()
|
||||
if (comments[position].verified == true) {
|
||||
holder.v.findViewById<ImageView>(R.id.verified_imageView).visibility = View.VISIBLE
|
||||
}
|
||||
if (comments[position].pinned == true) {
|
||||
holder.v.findViewById<ImageView>(R.id.pinned_imageView).visibility = View.VISIBLE
|
||||
}
|
||||
if (comments[position].hearted == true) {
|
||||
holder.v.findViewById<ImageView>(R.id.hearted_imageView).visibility = View.VISIBLE
|
||||
}
|
||||
channelImage.setOnClickListener {
|
||||
val activity = holder.v.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to comments[position].commentorUrl)
|
||||
activity.navController.navigate(R.id.channel, bundle)
|
||||
try {
|
||||
val mainMotionLayout = activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||
if (mainMotionLayout.progress == 0.toFloat()) {
|
||||
mainMotionLayout.transitionToEnd()
|
||||
activity.findViewById<MotionLayout>(R.id.playerMotionLayout).transitionToEnd()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
val comment = comments[position]
|
||||
holder.binding.apply {
|
||||
commentInfos.text =
|
||||
comment.author.toString() +
|
||||
" • " + comment.commentedTime.toString()
|
||||
commentText.text =
|
||||
comment.commentText.toString()
|
||||
Picasso.get().load(comment.thumbnail).fit().centerCrop().into(commentorImage)
|
||||
likesTextView.text =
|
||||
comment.likeCount?.toLong().formatShort()
|
||||
if (comment.verified == true) {
|
||||
verifiedImageView.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
val repliesRecView = holder.v.findViewById<RecyclerView>(R.id.replies_recView)
|
||||
repliesRecView.layoutManager = LinearLayoutManager(holder.v.context)
|
||||
val repliesAdapter = RepliesAdapter(CommentsPage().comments)
|
||||
repliesRecView.adapter = repliesAdapter
|
||||
holder.v.setOnClickListener {
|
||||
if (repliesAdapter.itemCount == 0) {
|
||||
if (comments[position].repliesPage != null) {
|
||||
nextpage = comments[position].repliesPage!!
|
||||
fetchReplies(nextpage, repliesAdapter)
|
||||
} else {
|
||||
Toast.makeText(holder.v.context, R.string.no_replies, Toast.LENGTH_SHORT).show()
|
||||
if (comment.pinned == true) {
|
||||
pinnedImageView.visibility = View.VISIBLE
|
||||
}
|
||||
if (comment.hearted == true) {
|
||||
heartedImageView.visibility = View.VISIBLE
|
||||
}
|
||||
commentorImage.setOnClickListener {
|
||||
val activity = root.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to comment.commentorUrl)
|
||||
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||
try {
|
||||
val mainMotionLayout =
|
||||
activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||
if (mainMotionLayout.progress == 0.toFloat()) {
|
||||
mainMotionLayout.transitionToEnd()
|
||||
activity.findViewById<MotionLayout>(R.id.playerMotionLayout)
|
||||
.transitionToEnd()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
repliesRecView.layoutManager = LinearLayoutManager(root.context)
|
||||
val repliesAdapter = RepliesAdapter(CommentsPage().comments)
|
||||
repliesRecView.adapter = repliesAdapter
|
||||
root.setOnClickListener {
|
||||
if (repliesAdapter.itemCount == 0) {
|
||||
if (comment.repliesPage != null) {
|
||||
nextpage = comment.repliesPage
|
||||
fetchReplies(nextpage, repliesAdapter)
|
||||
} else {
|
||||
Toast.makeText(root.context, R.string.no_replies, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
} else {
|
||||
repliesAdapter.clear()
|
||||
}
|
||||
// repliesAdapter.updateItems(repliesPage.comments)
|
||||
} else {
|
||||
repliesAdapter.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -103,19 +103,18 @@ class CommentsAdapter(
|
||||
return comments.size
|
||||
}
|
||||
|
||||
private fun fetchReplies(nextpage: String, repliesAdapter: RepliesAdapter) {
|
||||
private fun fetchReplies(nextPage: String, repliesAdapter: RepliesAdapter) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
if (!isLoading) {
|
||||
isLoading = true
|
||||
try {
|
||||
repliesPage = RetrofitInstance.api.getCommentsNextPage(videoId, nextpage)
|
||||
repliesPage = RetrofitInstance.api.getCommentsNextPage(videoId, nextPage)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG, "HttpException, unexpected response," + e.response())
|
||||
}
|
||||
// nextpage = if (repliesPage.nextpage!! != null) repliesPage.nextpage!! else ""
|
||||
repliesAdapter.updateItems(repliesPage.comments)
|
||||
isLoading = false
|
||||
}
|
||||
@ -123,7 +122,4 @@ class CommentsAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
class CommentsViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
||||
init {
|
||||
}
|
||||
}
|
||||
class CommentsViewHolder(val binding: CommentsRowBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
|
@ -7,17 +7,16 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.PlaylistRowBinding
|
||||
import com.github.libretube.dialogs.VideoOptionsDialog
|
||||
import com.github.libretube.fragments.PlayerFragment
|
||||
import com.github.libretube.obj.PlaylistId
|
||||
import com.github.libretube.obj.StreamItem
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.squareup.picasso.Picasso
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@ -34,6 +33,7 @@ class PlaylistAdapter(
|
||||
private val childFragmentManager: FragmentManager
|
||||
) : RecyclerView.Adapter<PlaylistViewHolder>() {
|
||||
private val TAG = "PlaylistAdapter"
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return videoFeed.size
|
||||
}
|
||||
@ -45,45 +45,44 @@ class PlaylistAdapter(
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlaylistViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val cell = layoutInflater.inflate(R.layout.playlist_row, parent, false)
|
||||
return PlaylistViewHolder(cell)
|
||||
val binding = PlaylistRowBinding.inflate(layoutInflater, parent, false)
|
||||
return PlaylistViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PlaylistViewHolder, position: Int) {
|
||||
val streamItem = videoFeed[position]
|
||||
holder.v.findViewById<TextView>(R.id.playlist_title).text = streamItem.title
|
||||
holder.v.findViewById<TextView>(R.id.playlist_description).text = streamItem.uploaderName
|
||||
holder.v.findViewById<TextView>(R.id.playlist_duration).text =
|
||||
DateUtils.formatElapsedTime(streamItem.duration!!)
|
||||
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.playlist_thumbnail)
|
||||
Picasso.get().load(streamItem.thumbnail).into(thumbnailImage)
|
||||
holder.v.setOnClickListener {
|
||||
var bundle = Bundle()
|
||||
bundle.putString("videoId", streamItem.url!!.replace("/watch?v=", ""))
|
||||
bundle.putString("playlistId", playlistId)
|
||||
var frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
val activity = holder.v.context as AppCompatActivity
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
}
|
||||
holder.v.setOnLongClickListener {
|
||||
val videoId = streamItem.url!!.replace("/watch?v=", "")
|
||||
VideoOptionsDialog(videoId, holder.v.context)
|
||||
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
||||
true
|
||||
}
|
||||
holder.binding.apply {
|
||||
playlistTitle.text = streamItem.title
|
||||
playlistDescription.text = streamItem.uploaderName
|
||||
playlistDuration.text = DateUtils.formatElapsedTime(streamItem.duration!!)
|
||||
Picasso.get().load(streamItem.thumbnail).into(playlistThumbnail)
|
||||
root.setOnClickListener {
|
||||
var bundle = Bundle()
|
||||
bundle.putString("videoId", streamItem.url!!.replace("/watch?v=", ""))
|
||||
bundle.putString("playlistId", playlistId)
|
||||
var frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
val activity = root.context as AppCompatActivity
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
}
|
||||
root.setOnLongClickListener {
|
||||
val videoId = streamItem.url!!.replace("/watch?v=", "")
|
||||
VideoOptionsDialog(videoId, root.context)
|
||||
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
||||
true
|
||||
}
|
||||
|
||||
if (isOwner) {
|
||||
val delete = holder.v.findViewById<ImageView>(R.id.delete_playlist)
|
||||
delete.visibility = View.VISIBLE
|
||||
delete.setOnClickListener {
|
||||
val token = PreferenceHelper.getToken(holder.v.context)
|
||||
removeFromPlaylist(token, position)
|
||||
if (isOwner) {
|
||||
deletePlaylist.visibility = View.VISIBLE
|
||||
deletePlaylist.setOnClickListener {
|
||||
val token = PreferenceHelper.getToken(root.context)
|
||||
removeFromPlaylist(token, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,7 +91,7 @@ class PlaylistAdapter(
|
||||
fun run() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val response = try {
|
||||
RetrofitInstance.api.removeFromPlaylist(
|
||||
RetrofitInstance.authApi.removeFromPlaylist(
|
||||
token,
|
||||
PlaylistId(playlistId = playlistId, index = position)
|
||||
)
|
||||
@ -111,10 +110,6 @@ class PlaylistAdapter(
|
||||
videoFeed.removeAt(position)
|
||||
// FIXME: This needs to run on UI thread?
|
||||
activity.runOnUiThread { notifyDataSetChanged() }
|
||||
|
||||
/*if(playlists.isEmpty()){
|
||||
view.findViewById<ImageView>(R.id.boogh2).visibility=View.VISIBLE
|
||||
}*/
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.toString())
|
||||
@ -125,7 +120,4 @@ class PlaylistAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
class PlaylistViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
||||
init {
|
||||
}
|
||||
}
|
||||
class PlaylistViewHolder(val binding: PlaylistRowBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
|
@ -3,21 +3,20 @@ package com.github.libretube.adapters
|
||||
import android.app.Activity
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.MainActivity
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.MainActivity
|
||||
import com.github.libretube.databinding.PlaylistsRowBinding
|
||||
import com.github.libretube.obj.PlaylistId
|
||||
import com.github.libretube.obj.Playlists
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.squareup.picasso.Picasso
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
@ -27,6 +26,7 @@ class PlaylistsAdapter(
|
||||
private val activity: Activity
|
||||
) : RecyclerView.Adapter<PlaylistsViewHolder>() {
|
||||
val TAG = "PlaylistsAdapter"
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return playlists.size
|
||||
}
|
||||
@ -38,45 +38,46 @@ class PlaylistsAdapter(
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlaylistsViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val cell = layoutInflater.inflate(R.layout.playlists_row, parent, false)
|
||||
return PlaylistsViewHolder(cell)
|
||||
val binding = PlaylistsRowBinding.inflate(layoutInflater, parent, false)
|
||||
return PlaylistsViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PlaylistsViewHolder, position: Int) {
|
||||
val playlist = playlists[position]
|
||||
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.playlist_thumbnail)
|
||||
Picasso.get().load(playlist.thumbnail).into(thumbnailImage)
|
||||
// set imageview drawable as empty playlist if imageview empty
|
||||
if (thumbnailImage.drawable == null) {
|
||||
thumbnailImage.setImageResource(R.drawable.ic_empty_playlist)
|
||||
thumbnailImage.setBackgroundColor(R.attr.colorSurface)
|
||||
}
|
||||
holder.v.findViewById<TextView>(R.id.playlist_title).text = playlist.name
|
||||
holder.v.findViewById<ImageView>(R.id.delete_playlist).setOnClickListener {
|
||||
val builder = MaterialAlertDialogBuilder(holder.v.context)
|
||||
builder.setTitle(R.string.deletePlaylist)
|
||||
builder.setMessage(R.string.areYouSure)
|
||||
builder.setPositiveButton(R.string.yes) { _, _ ->
|
||||
val token = PreferenceHelper.getToken(holder.v.context)
|
||||
deletePlaylist(playlist.id!!, token, position)
|
||||
holder.binding.apply {
|
||||
Picasso.get().load(playlist.thumbnail).into(playlistThumbnail)
|
||||
// set imageview drawable as empty playlist if imageview empty
|
||||
if (playlistThumbnail.drawable == null) {
|
||||
playlistThumbnail.setImageResource(R.drawable.ic_empty_playlist)
|
||||
playlistThumbnail.setBackgroundColor(R.attr.colorSurface)
|
||||
}
|
||||
builder.setNegativeButton(R.string.cancel) { _, _ ->
|
||||
playlistTitle.text = playlist.name
|
||||
deletePlaylist.setOnClickListener {
|
||||
val builder = MaterialAlertDialogBuilder(root.context)
|
||||
builder.setTitle(R.string.deletePlaylist)
|
||||
builder.setMessage(R.string.areYouSure)
|
||||
builder.setPositiveButton(R.string.yes) { _, _ ->
|
||||
val token = PreferenceHelper.getToken(root.context)
|
||||
deletePlaylist(playlist.id!!, token, position)
|
||||
}
|
||||
builder.setNegativeButton(R.string.cancel) { _, _ ->
|
||||
}
|
||||
builder.show()
|
||||
}
|
||||
root.setOnClickListener {
|
||||
// playlists clicked
|
||||
val activity = root.context as MainActivity
|
||||
val bundle = bundleOf("playlist_id" to playlist.id)
|
||||
activity.navController.navigate(R.id.playlistFragment, bundle)
|
||||
}
|
||||
builder.show()
|
||||
}
|
||||
holder.v.setOnClickListener {
|
||||
// playlists clicked
|
||||
val activity = holder.v.context as MainActivity
|
||||
val bundle = bundleOf("playlist_id" to playlist.id)
|
||||
activity.navController.navigate(R.id.playlistFragment, bundle)
|
||||
}
|
||||
}
|
||||
|
||||
private fun deletePlaylist(id: String, token: String, position: Int) {
|
||||
fun run() {
|
||||
GlobalScope.launch {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val response = try {
|
||||
RetrofitInstance.api.deletePlaylist(token, PlaylistId(id))
|
||||
RetrofitInstance.authApi.deletePlaylist(token, PlaylistId(id))
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
@ -84,7 +85,6 @@ class PlaylistsAdapter(
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG, "HttpException, unexpected response")
|
||||
return@launch
|
||||
} finally {
|
||||
}
|
||||
try {
|
||||
if (response.message == "ok") {
|
||||
@ -102,7 +102,4 @@ class PlaylistsAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
class PlaylistsViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
||||
init {
|
||||
}
|
||||
}
|
||||
class PlaylistsViewHolder(val binding: PlaylistsRowBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
|
@ -3,13 +3,12 @@ package com.github.libretube.adapters
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.MainActivity
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.MainActivity
|
||||
import com.github.libretube.databinding.RepliesRowBinding
|
||||
import com.github.libretube.obj.Comment
|
||||
import com.github.libretube.util.formatShort
|
||||
import com.squareup.picasso.Picasso
|
||||
@ -17,10 +16,7 @@ import com.squareup.picasso.Picasso
|
||||
class RepliesAdapter(
|
||||
private val replies: MutableList<Comment>
|
||||
) : RecyclerView.Adapter<RepliesViewHolder>() {
|
||||
|
||||
private val TAG = "RepliesAdapter"
|
||||
private var isLoading = false
|
||||
private var nextPage = ""
|
||||
|
||||
fun clear() {
|
||||
val size: Int = replies.size
|
||||
@ -35,41 +31,45 @@ class RepliesAdapter(
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RepliesViewHolder {
|
||||
var repliesView =
|
||||
LayoutInflater.from(parent.context).inflate(R.layout.replies_row, parent, false)
|
||||
return RepliesViewHolder(repliesView)
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val binding = RepliesRowBinding.inflate(layoutInflater, parent, false)
|
||||
return RepliesViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RepliesViewHolder, position: Int) {
|
||||
holder.v.findViewById<TextView>(R.id.comment_infos).text =
|
||||
replies[position].author.toString() +
|
||||
" • " + replies[position].commentedTime.toString()
|
||||
holder.v.findViewById<TextView>(R.id.comment_text).text =
|
||||
replies[position].commentText.toString()
|
||||
val channelImage = holder.v.findViewById<ImageView>(R.id.commentor_image)
|
||||
Picasso.get().load(replies[position].thumbnail).fit().centerCrop().into(channelImage)
|
||||
holder.v.findViewById<TextView>(R.id.likes_textView).text =
|
||||
replies[position].likeCount?.toLong().formatShort()
|
||||
if (replies[position].verified == true) {
|
||||
holder.v.findViewById<ImageView>(R.id.verified_imageView).visibility = View.VISIBLE
|
||||
}
|
||||
if (replies[position].pinned == true) {
|
||||
holder.v.findViewById<ImageView>(R.id.pinned_imageView).visibility = View.VISIBLE
|
||||
}
|
||||
if (replies[position].hearted == true) {
|
||||
holder.v.findViewById<ImageView>(R.id.hearted_imageView).visibility = View.VISIBLE
|
||||
}
|
||||
channelImage.setOnClickListener {
|
||||
val activity = holder.v.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to replies[position].commentorUrl)
|
||||
activity.navController.navigate(R.id.channel, bundle)
|
||||
try {
|
||||
val mainMotionLayout = activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||
if (mainMotionLayout.progress == 0.toFloat()) {
|
||||
mainMotionLayout.transitionToEnd()
|
||||
activity.findViewById<MotionLayout>(R.id.playerMotionLayout).transitionToEnd()
|
||||
holder.binding.apply {
|
||||
val reply = replies[position]
|
||||
commentInfos.text =
|
||||
reply.author.toString() +
|
||||
" • " + reply.commentedTime.toString()
|
||||
commentText.text =
|
||||
reply.commentText.toString()
|
||||
Picasso.get().load(reply.thumbnail).fit().centerCrop().into(commentorImage)
|
||||
likesTextView.text =
|
||||
reply.likeCount?.toLong().formatShort()
|
||||
if (reply.verified == true) {
|
||||
verifiedImageView.visibility = View.VISIBLE
|
||||
}
|
||||
if (reply.pinned == true) {
|
||||
pinnedImageView.visibility = View.VISIBLE
|
||||
}
|
||||
if (reply.hearted == true) {
|
||||
heartedImageView.visibility = View.VISIBLE
|
||||
}
|
||||
commentorImage.setOnClickListener {
|
||||
val activity = root.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to reply.commentorUrl)
|
||||
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||
try {
|
||||
val mainMotionLayout =
|
||||
activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||
if (mainMotionLayout.progress == 0.toFloat()) {
|
||||
mainMotionLayout.transitionToEnd()
|
||||
activity.findViewById<MotionLayout>(R.id.playerMotionLayout)
|
||||
.transitionToEnd()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -79,7 +79,4 @@ class RepliesAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
class RepliesViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
||||
init {
|
||||
}
|
||||
}
|
||||
class RepliesViewHolder(val binding: RepliesRowBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
|
@ -3,16 +3,16 @@ package com.github.libretube.adapters
|
||||
import android.os.Bundle
|
||||
import android.text.format.DateUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.MainActivity
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.MainActivity
|
||||
import com.github.libretube.databinding.ChannelSearchRowBinding
|
||||
import com.github.libretube.databinding.PlaylistSearchRowBinding
|
||||
import com.github.libretube.databinding.VideoSearchRowBinding
|
||||
import com.github.libretube.dialogs.PlaylistOptionsDialog
|
||||
import com.github.libretube.dialogs.VideoOptionsDialog
|
||||
import com.github.libretube.fragments.PlayerFragment
|
||||
@ -27,7 +27,7 @@ class SearchAdapter(
|
||||
RecyclerView.Adapter<SearchViewHolder>() {
|
||||
|
||||
fun updateItems(newItems: List<SearchItem>) {
|
||||
var searchItemsSize = searchItems.size
|
||||
val searchItemsSize = searchItems.size
|
||||
searchItems.addAll(newItems)
|
||||
notifyItemRangeInserted(searchItemsSize, newItems.size)
|
||||
}
|
||||
@ -37,19 +37,32 @@ class SearchAdapter(
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
|
||||
val layout = when (viewType) {
|
||||
0 -> R.layout.video_search_row
|
||||
1 -> R.layout.channel_search_row
|
||||
2 -> R.layout.playlist_search_row
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
|
||||
return when (viewType) {
|
||||
0 -> SearchViewHolder(
|
||||
VideoSearchRowBinding.inflate(layoutInflater, parent, false)
|
||||
)
|
||||
1 -> SearchViewHolder(
|
||||
ChannelSearchRowBinding.inflate(layoutInflater, parent, false)
|
||||
)
|
||||
2 -> SearchViewHolder(
|
||||
PlaylistSearchRowBinding.inflate(layoutInflater, parent, false)
|
||||
)
|
||||
else -> throw IllegalArgumentException("Invalid type")
|
||||
}
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val cell = layoutInflater.inflate(layout, parent, false)
|
||||
return SearchViewHolder(cell, childFragmentManager)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SearchViewHolder, position: Int) {
|
||||
holder.bind(searchItems[position])
|
||||
val searchItem = searchItems[position]
|
||||
|
||||
val videoRowBinding = holder.videoRowBinding
|
||||
val channelRowBinding = holder.channelRowBinding
|
||||
val playlistRowBinding = holder.playlistRowBinding
|
||||
|
||||
if (videoRowBinding != null) bindWatch(searchItem, videoRowBinding)
|
||||
else if (channelRowBinding != null) bindChannel(searchItem, channelRowBinding)
|
||||
else if (playlistRowBinding != null) bindPlaylist(searchItem, playlistRowBinding)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
@ -60,117 +73,110 @@ class SearchAdapter(
|
||||
else -> 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SearchViewHolder(
|
||||
private val v: View,
|
||||
private val childFragmentManager: FragmentManager
|
||||
) : RecyclerView.ViewHolder(v) {
|
||||
|
||||
private fun bindWatch(item: SearchItem) {
|
||||
val thumbnailImage = v.findViewById<ImageView>(R.id.search_thumbnail)
|
||||
Picasso.get().load(item.thumbnail).fit().centerCrop().into(thumbnailImage)
|
||||
val thumbnailDuration = v.findViewById<TextView>(R.id.search_thumbnail_duration)
|
||||
if (item.duration != -1L) {
|
||||
thumbnailDuration.text = DateUtils.formatElapsedTime(item.duration!!)
|
||||
} else {
|
||||
thumbnailDuration.text = v.context.getString(R.string.live)
|
||||
thumbnailDuration.setBackgroundColor(R.attr.colorPrimaryDark)
|
||||
}
|
||||
val channelImage = v.findViewById<ImageView>(R.id.search_channel_image)
|
||||
Picasso.get().load(item.uploaderAvatar).fit().centerCrop().into(channelImage)
|
||||
val title = v.findViewById<TextView>(R.id.search_description)
|
||||
title.text = item.title
|
||||
val views = v.findViewById<TextView>(R.id.search_views)
|
||||
val viewsString = if (item.views?.toInt() != -1) item.views.formatShort() else ""
|
||||
val uploadDate = if (item.uploadedDate != null) item.uploadedDate else ""
|
||||
views.text =
|
||||
if (viewsString != "" && uploadDate != "") {
|
||||
"$viewsString • $uploadDate"
|
||||
private fun bindWatch(item: SearchItem, binding: VideoSearchRowBinding) {
|
||||
binding.apply {
|
||||
Picasso.get().load(item.thumbnail).fit().centerCrop().into(searchThumbnail)
|
||||
if (item.duration != -1L) {
|
||||
searchThumbnailDuration.text = DateUtils.formatElapsedTime(item.duration!!)
|
||||
} else {
|
||||
viewsString + uploadDate
|
||||
searchThumbnailDuration.text = root.context.getString(R.string.live)
|
||||
searchThumbnailDuration.setBackgroundColor(R.attr.colorPrimaryDark)
|
||||
}
|
||||
Picasso.get().load(item.uploaderAvatar).fit().centerCrop().into(searchChannelImage)
|
||||
searchDescription.text = item.title
|
||||
val viewsString = if (item.views?.toInt() != -1) item.views.formatShort() else ""
|
||||
val uploadDate = if (item.uploadedDate != null) item.uploadedDate else ""
|
||||
searchViews.text =
|
||||
if (viewsString != "" && uploadDate != "") {
|
||||
"$viewsString • $uploadDate"
|
||||
} else {
|
||||
viewsString + uploadDate
|
||||
}
|
||||
searchChannelName.text = item.uploaderName
|
||||
root.setOnClickListener {
|
||||
val bundle = Bundle()
|
||||
bundle.putString("videoId", item.url!!.replace("/watch?v=", ""))
|
||||
val frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
val activity = root.context as AppCompatActivity
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
}
|
||||
root.setOnLongClickListener {
|
||||
val videoId = item.url!!.replace("/watch?v=", "")
|
||||
VideoOptionsDialog(videoId, root.context)
|
||||
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
||||
true
|
||||
}
|
||||
searchChannelImage.setOnClickListener {
|
||||
val activity = root.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to item.uploaderUrl)
|
||||
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||
}
|
||||
val channelName = v.findViewById<TextView>(R.id.search_channel_name)
|
||||
channelName.text = item.uploaderName
|
||||
v.setOnClickListener {
|
||||
var bundle = Bundle()
|
||||
bundle.putString("videoId", item.url!!.replace("/watch?v=", ""))
|
||||
var frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
val activity = v.context as AppCompatActivity
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
}
|
||||
v.setOnLongClickListener {
|
||||
val videoId = item.url!!.replace("/watch?v=", "")
|
||||
VideoOptionsDialog(videoId, v.context)
|
||||
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
||||
true
|
||||
}
|
||||
channelImage.setOnClickListener {
|
||||
val activity = v.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to item.uploaderUrl)
|
||||
activity.navController.navigate(R.id.channel, bundle)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindChannel(item: SearchItem) {
|
||||
val channelImage = v.findViewById<ImageView>(R.id.search_channel_image)
|
||||
Picasso.get().load(item.thumbnail).fit().centerCrop().into(channelImage)
|
||||
val channelName = v.findViewById<TextView>(R.id.search_channel_name)
|
||||
channelName.text = item.name
|
||||
val channelViews = v.findViewById<TextView>(R.id.search_views)
|
||||
channelViews.text = v.context.getString(
|
||||
R.string.subscribers,
|
||||
item.subscribers.formatShort()
|
||||
) + " • " + v.context.getString(R.string.videoCount, item.videos.toString())
|
||||
v.setOnClickListener {
|
||||
val activity = v.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to item.url)
|
||||
activity.navController.navigate(R.id.channel, bundle)
|
||||
}
|
||||
// todo sub button
|
||||
}
|
||||
|
||||
private fun bindPlaylist(item: SearchItem) {
|
||||
val playlistImage = v.findViewById<ImageView>(R.id.search_thumbnail)
|
||||
Picasso.get().load(item.thumbnail).fit().centerCrop().into(playlistImage)
|
||||
val playlistNumber = v.findViewById<TextView>(R.id.search_playlist_number)
|
||||
if (item.videos?.toInt() != -1) playlistNumber.text = item.videos.toString()
|
||||
val playlistName = v.findViewById<TextView>(R.id.search_description)
|
||||
playlistName.text = item.name
|
||||
val playlistChannelName = v.findViewById<TextView>(R.id.search_name)
|
||||
playlistChannelName.text = item.uploaderName
|
||||
val playlistVideosNumber = v.findViewById<TextView>(R.id.search_playlist_videos)
|
||||
if (item.videos?.toInt() != -1) {
|
||||
playlistVideosNumber.text =
|
||||
v.context.getString(R.string.videoCount, item.videos.toString())
|
||||
}
|
||||
v.setOnClickListener {
|
||||
// playlist clicked
|
||||
val activity = v.context as MainActivity
|
||||
val bundle = bundleOf("playlist_id" to item.url)
|
||||
activity.navController.navigate(R.id.playlistFragment, bundle)
|
||||
}
|
||||
v.setOnLongClickListener {
|
||||
val playlistId = item.url!!.replace("/playlist?list=", "")
|
||||
PlaylistOptionsDialog(playlistId, v.context)
|
||||
.show(childFragmentManager, "PlaylistOptionsDialog")
|
||||
true
|
||||
private fun bindChannel(item: SearchItem, binding: ChannelSearchRowBinding) {
|
||||
binding.apply {
|
||||
Picasso.get().load(item.thumbnail).fit().centerCrop().into(searchChannelImage)
|
||||
searchChannelName.text = item.name
|
||||
searchViews.text = root.context.getString(
|
||||
R.string.subscribers,
|
||||
item.subscribers.formatShort()
|
||||
) + " • " + root.context.getString(R.string.videoCount, item.videos.toString())
|
||||
root.setOnClickListener {
|
||||
val activity = root.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to item.url)
|
||||
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(searchItem: SearchItem) {
|
||||
when {
|
||||
searchItem.url!!.startsWith("/watch", false) -> bindWatch(searchItem)
|
||||
searchItem.url!!.startsWith("/channel", false) -> bindChannel(searchItem)
|
||||
searchItem.url!!.startsWith("/playlist", false) -> bindPlaylist(searchItem)
|
||||
else -> {
|
||||
private fun bindPlaylist(item: SearchItem, binding: PlaylistSearchRowBinding) {
|
||||
binding.apply {
|
||||
Picasso.get().load(item.thumbnail).fit().centerCrop().into(searchThumbnail)
|
||||
if (item.videos?.toInt() != -1) searchPlaylistNumber.text = item.videos.toString()
|
||||
searchDescription.text = item.name
|
||||
searchName.text = item.uploaderName
|
||||
if (item.videos?.toInt() != -1) {
|
||||
searchPlaylistNumber.text =
|
||||
root.context.getString(R.string.videoCount, item.videos.toString())
|
||||
}
|
||||
root.setOnClickListener {
|
||||
// playlist clicked
|
||||
val activity = root.context as MainActivity
|
||||
val bundle = bundleOf("playlist_id" to item.url)
|
||||
activity.navController.navigate(R.id.playlistFragment, bundle)
|
||||
}
|
||||
root.setOnLongClickListener {
|
||||
val playlistId = item.url!!.replace("/playlist?list=", "")
|
||||
PlaylistOptionsDialog(playlistId, false, root.context)
|
||||
.show(childFragmentManager, "PlaylistOptionsDialog")
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SearchViewHolder : RecyclerView.ViewHolder {
|
||||
var videoRowBinding: VideoSearchRowBinding? = null
|
||||
var channelRowBinding: ChannelSearchRowBinding? = null
|
||||
var playlistRowBinding: PlaylistSearchRowBinding? = null
|
||||
|
||||
constructor(binding: VideoSearchRowBinding) : super(binding.root) {
|
||||
videoRowBinding = binding
|
||||
}
|
||||
|
||||
constructor(binding: ChannelSearchRowBinding) : super(binding.root) {
|
||||
channelRowBinding = binding
|
||||
}
|
||||
|
||||
constructor(binding: PlaylistSearchRowBinding) : super(binding.root) {
|
||||
playlistRowBinding = binding
|
||||
}
|
||||
}
|
||||
|
@ -2,15 +2,12 @@ package com.github.libretube.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.SearchhistoryRowBinding
|
||||
import com.github.libretube.fragments.SearchFragment
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
|
||||
class SearchHistoryAdapter(
|
||||
private val context: Context,
|
||||
@ -26,28 +23,28 @@ class SearchHistoryAdapter(
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchHistoryViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val cell = layoutInflater.inflate(R.layout.searchhistory_row, parent, false)
|
||||
return SearchHistoryViewHolder(cell)
|
||||
val binding = SearchhistoryRowBinding.inflate(layoutInflater, parent, false)
|
||||
return SearchHistoryViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SearchHistoryViewHolder, position: Int) {
|
||||
val history = historyList[position]
|
||||
holder.v.findViewById<TextView>(R.id.history_text).text = history
|
||||
holder.binding.apply {
|
||||
historyText.text = history
|
||||
|
||||
holder.v.findViewById<ImageView>(R.id.delete_history).setOnClickListener {
|
||||
historyList = historyList - history
|
||||
PreferenceHelper.saveHistory(context, historyList)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
deleteHistory.setOnClickListener {
|
||||
historyList = historyList - history
|
||||
PreferenceHelper.saveHistory(context, historyList)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
holder.v.setOnClickListener {
|
||||
editText.setText(history)
|
||||
searchFragment.fetchSearch(history)
|
||||
root.setOnClickListener {
|
||||
editText.setText(history)
|
||||
searchFragment.fetchSearch(history)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SearchHistoryViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
||||
init {
|
||||
}
|
||||
}
|
||||
class SearchHistoryViewHolder(val binding: SearchhistoryRowBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
@ -1,12 +1,10 @@
|
||||
package com.github.libretube.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.SearchsuggestionRowBinding
|
||||
import com.github.libretube.fragments.SearchFragment
|
||||
|
||||
class SearchSuggestionsAdapter(
|
||||
@ -16,28 +14,29 @@ class SearchSuggestionsAdapter(
|
||||
) :
|
||||
RecyclerView.Adapter<SearchSuggestionsViewHolder>() {
|
||||
|
||||
private val TAG = "SearchSuggestionsAdapter"
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return suggestionsList.size
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchSuggestionsViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val cell = layoutInflater.inflate(R.layout.searchsuggestion_row, parent, false)
|
||||
return SearchSuggestionsViewHolder(cell)
|
||||
val binding = SearchsuggestionRowBinding.inflate(layoutInflater, parent, false)
|
||||
return SearchSuggestionsViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SearchSuggestionsViewHolder, position: Int) {
|
||||
val suggestion = suggestionsList[position]
|
||||
val suggestionTextView = holder.v.findViewById<TextView>(R.id.suggestion_text)
|
||||
suggestionTextView.text = suggestion
|
||||
holder.v.setOnClickListener {
|
||||
editText.setText(suggestion)
|
||||
searchFragment.fetchSearch(editText.text.toString())
|
||||
holder.binding.apply {
|
||||
suggestionText.text = suggestion
|
||||
root.setOnClickListener {
|
||||
editText.setText(suggestion)
|
||||
searchFragment.fetchSearch(editText.text.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SearchSuggestionsViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
||||
init {
|
||||
}
|
||||
}
|
||||
class SearchSuggestionsViewHolder(val binding: SearchsuggestionRowBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
@ -3,17 +3,15 @@ package com.github.libretube.adapters
|
||||
import android.os.Bundle
|
||||
import android.text.format.DateUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.MainActivity
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.MainActivity
|
||||
import com.github.libretube.databinding.TrendingRowBinding
|
||||
import com.github.libretube.dialogs.VideoOptionsDialog
|
||||
import com.github.libretube.fragments.PlayerFragment
|
||||
import com.github.libretube.obj.StreamItem
|
||||
@ -23,16 +21,15 @@ import com.squareup.picasso.Picasso
|
||||
class SubscriptionAdapter(
|
||||
private val videoFeed: List<StreamItem>,
|
||||
private val childFragmentManager: FragmentManager
|
||||
) :
|
||||
RecyclerView.Adapter<SubscriptionViewHolder>() {
|
||||
// private var limitedVideoFeed: MutableList<String> = [""].toMutableList()
|
||||
) : RecyclerView.Adapter<SubscriptionViewHolder>() {
|
||||
private val TAG = "SubscriptionAdapter"
|
||||
|
||||
var i = 0
|
||||
override fun getItemCount(): Int {
|
||||
return i
|
||||
}
|
||||
|
||||
fun updateItems() {
|
||||
// limitedVideoFeed.add("")
|
||||
i += 10
|
||||
if (i > videoFeed.size) {
|
||||
i = videoFeed.size
|
||||
@ -42,64 +39,63 @@ class SubscriptionAdapter(
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubscriptionViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val cell = layoutInflater.inflate(R.layout.trending_row, parent, false)
|
||||
return SubscriptionViewHolder(cell)
|
||||
val binding = TrendingRowBinding.inflate(layoutInflater, parent, false)
|
||||
return SubscriptionViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SubscriptionViewHolder, position: Int) {
|
||||
val trending = videoFeed[position]
|
||||
holder.v.findViewById<TextView>(R.id.textView_title).text = trending.title
|
||||
holder.v.findViewById<TextView>(R.id.textView_channel).text =
|
||||
trending.uploaderName + " • " +
|
||||
trending.views.formatShort() + " • " +
|
||||
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
|
||||
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.thumbnail)
|
||||
val thumbnailDuration = holder.v.findViewById<TextView>(R.id.thumbnail_duration)
|
||||
if (trending.duration != -1L) {
|
||||
thumbnailDuration.text = DateUtils.formatElapsedTime(trending.duration!!)
|
||||
} else {
|
||||
thumbnailDuration.text = holder.v.context.getString(R.string.live)
|
||||
thumbnailDuration.setBackgroundColor(R.attr.colorPrimaryDark)
|
||||
}
|
||||
val channelImage = holder.v.findViewById<ImageView>(R.id.channel_image)
|
||||
channelImage.setOnClickListener {
|
||||
val activity = holder.v.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to trending.uploaderUrl)
|
||||
activity.navController.navigate(R.id.channel, bundle)
|
||||
try {
|
||||
val mainMotionLayout = activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||
if (mainMotionLayout.progress == 0.toFloat()) {
|
||||
mainMotionLayout.transitionToEnd()
|
||||
activity.findViewById<MotionLayout>(R.id.playerMotionLayout).transitionToEnd()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
holder.binding.apply {
|
||||
textViewTitle.text = trending.title
|
||||
textViewChannel.text =
|
||||
trending.uploaderName + " • " +
|
||||
trending.views.formatShort() + " • " +
|
||||
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
|
||||
if (trending.duration != -1L) {
|
||||
thumbnailDuration.text = DateUtils.formatElapsedTime(trending.duration!!)
|
||||
} else {
|
||||
thumbnailDuration.text = root.context.getString(R.string.live)
|
||||
thumbnailDuration.setBackgroundColor(R.attr.colorPrimaryDark)
|
||||
}
|
||||
channelImage.setOnClickListener {
|
||||
val activity = root.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to trending.uploaderUrl)
|
||||
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||
try {
|
||||
val mainMotionLayout =
|
||||
activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||
if (mainMotionLayout.progress == 0.toFloat()) {
|
||||
mainMotionLayout.transitionToEnd()
|
||||
activity.findViewById<MotionLayout>(R.id.playerMotionLayout)
|
||||
.transitionToEnd()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
Picasso.get().load(trending.thumbnail).into(thumbnail)
|
||||
Picasso.get().load(trending.uploaderAvatar).into(channelImage)
|
||||
root.setOnClickListener {
|
||||
val bundle = Bundle()
|
||||
bundle.putString("videoId", trending.url!!.replace("/watch?v=", ""))
|
||||
val frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
val activity = root.context as AppCompatActivity
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
}
|
||||
root.setOnLongClickListener {
|
||||
val videoId = trending.url!!.replace("/watch?v=", "")
|
||||
VideoOptionsDialog(videoId, root.context)
|
||||
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
||||
true
|
||||
}
|
||||
}
|
||||
Picasso.get().load(trending.thumbnail).into(thumbnailImage)
|
||||
Picasso.get().load(trending.uploaderAvatar).into(channelImage)
|
||||
holder.v.setOnClickListener {
|
||||
var bundle = Bundle()
|
||||
bundle.putString("videoId", trending.url!!.replace("/watch?v=", ""))
|
||||
var frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
val activity = holder.v.context as AppCompatActivity
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
}
|
||||
holder.v.setOnLongClickListener {
|
||||
val videoId = trending.url!!.replace("/watch?v=", "")
|
||||
VideoOptionsDialog(videoId, holder.v.context)
|
||||
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SubscriptionViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
||||
init {
|
||||
}
|
||||
}
|
||||
class SubscriptionViewHolder(val binding: TrendingRowBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
@ -3,17 +3,15 @@ package com.github.libretube.adapters
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.MainActivity
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.MainActivity
|
||||
import com.github.libretube.databinding.ChannelSubscriptionRowBinding
|
||||
import com.github.libretube.obj.Subscribe
|
||||
import com.github.libretube.obj.Subscription
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.squareup.picasso.Picasso
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@ -25,8 +23,10 @@ import java.io.IOException
|
||||
class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscription>) :
|
||||
RecyclerView.Adapter<SubscriptionChannelViewHolder>() {
|
||||
val TAG = "SubChannelAdapter"
|
||||
|
||||
private var subscribed = true
|
||||
private var isLoading = false
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return subscriptions.size
|
||||
}
|
||||
@ -34,34 +34,32 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
|
||||
SubscriptionChannelViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val cell = layoutInflater.inflate(R.layout.channel_subscription_row, parent, false)
|
||||
return SubscriptionChannelViewHolder(cell)
|
||||
val binding = ChannelSubscriptionRowBinding.inflate(layoutInflater, parent, false)
|
||||
return SubscriptionChannelViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SubscriptionChannelViewHolder, position: Int) {
|
||||
val subscription = subscriptions[position]
|
||||
holder.v.findViewById<TextView>(R.id.subscription_channel_name).text = subscription.name
|
||||
val avatar = holder.v.findViewById<ImageView>(R.id.subscription_channel_image)
|
||||
Picasso.get().load(subscription.avatar).into(avatar)
|
||||
holder.v.setOnClickListener {
|
||||
val activity = holder.v.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to subscription.url)
|
||||
activity.navController.navigate(R.id.channel, bundle)
|
||||
}
|
||||
val subscribeBtn = holder.v
|
||||
.findViewById<com.google.android.material.button.MaterialButton>(
|
||||
R.id.subscription_subscribe
|
||||
)
|
||||
subscribeBtn.setOnClickListener {
|
||||
if (!isLoading) {
|
||||
isLoading = true
|
||||
val channelId = subscription.url?.replace("/channel/", "")!!
|
||||
if (subscribed) {
|
||||
unsubscribe(holder.v.context, channelId)
|
||||
subscribeBtn.text = holder.v.context.getString(R.string.subscribe)
|
||||
} else {
|
||||
subscribe(holder.v.context, channelId)
|
||||
subscribeBtn.text = holder.v.context.getString(R.string.unsubscribe)
|
||||
holder.binding.apply {
|
||||
subscriptionChannelName.text = subscription.name
|
||||
Picasso.get().load(subscription.avatar).into(subscriptionChannelImage)
|
||||
root.setOnClickListener {
|
||||
val activity = root.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to subscription.url)
|
||||
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||
}
|
||||
subscriptionSubscribe.setOnClickListener {
|
||||
if (!isLoading) {
|
||||
isLoading = true
|
||||
val channelId = subscription.url?.replace("/channel/", "")!!
|
||||
if (subscribed) {
|
||||
unsubscribe(root.context, channelId)
|
||||
subscriptionSubscribe.text = root.context.getString(R.string.subscribe)
|
||||
} else {
|
||||
subscribe(root.context, channelId)
|
||||
subscriptionSubscribe.text =
|
||||
root.context.getString(R.string.unsubscribe)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -72,7 +70,7 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val response = try {
|
||||
val token = PreferenceHelper.getToken(context)
|
||||
RetrofitInstance.api.subscribe(
|
||||
RetrofitInstance.authApi.subscribe(
|
||||
token,
|
||||
Subscribe(channelId)
|
||||
)
|
||||
@ -94,7 +92,7 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val response = try {
|
||||
val token = PreferenceHelper.getToken(context)
|
||||
RetrofitInstance.api.unsubscribe(
|
||||
RetrofitInstance.authApi.unsubscribe(
|
||||
token,
|
||||
Subscribe(channelId)
|
||||
)
|
||||
@ -112,7 +110,5 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
|
||||
}
|
||||
}
|
||||
|
||||
class SubscriptionChannelViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
||||
init {
|
||||
}
|
||||
}
|
||||
class SubscriptionChannelViewHolder(val binding: ChannelSubscriptionRowBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
@ -3,17 +3,15 @@ package com.github.libretube.adapters
|
||||
import android.os.Bundle
|
||||
import android.text.format.DateUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.MainActivity
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.MainActivity
|
||||
import com.github.libretube.databinding.TrendingRowBinding
|
||||
import com.github.libretube.dialogs.VideoOptionsDialog
|
||||
import com.github.libretube.fragments.PlayerFragment
|
||||
import com.github.libretube.obj.StreamItem
|
||||
@ -24,77 +22,75 @@ class TrendingAdapter(
|
||||
private val videoFeed: List<StreamItem>,
|
||||
private val childFragmentManager: FragmentManager
|
||||
) : RecyclerView.Adapter<TrendingViewHolder>() {
|
||||
private val TAG = "TrendingAdapter"
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return videoFeed.size
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrendingViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val cell = layoutInflater.inflate(R.layout.trending_row, parent, false)
|
||||
return TrendingViewHolder(cell)
|
||||
val binding = TrendingRowBinding.inflate(layoutInflater, parent, false)
|
||||
return TrendingViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: TrendingViewHolder, position: Int) {
|
||||
val trending = videoFeed[position]
|
||||
holder.v.findViewById<TextView>(R.id.textView_title).text = trending.title
|
||||
holder.v.findViewById<TextView>(R.id.textView_channel).text =
|
||||
trending.uploaderName + " • " +
|
||||
trending.views.formatShort() + " • " +
|
||||
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
|
||||
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.thumbnail)
|
||||
val thumbnailDuration = holder.v.findViewById<TextView>(R.id.thumbnail_duration)
|
||||
if (trending.duration != -1L) {
|
||||
thumbnailDuration.text = DateUtils.formatElapsedTime(trending.duration!!)
|
||||
} else {
|
||||
thumbnailDuration.text = holder.v.context.getString(R.string.live)
|
||||
thumbnailDuration.setBackgroundColor(R.attr.colorPrimaryDark)
|
||||
}
|
||||
val channelImage = holder.v.findViewById<ImageView>(R.id.channel_image)
|
||||
channelImage.setOnClickListener {
|
||||
val activity = holder.v.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to trending.uploaderUrl)
|
||||
activity.navController.navigate(R.id.channel, bundle)
|
||||
try {
|
||||
val mainMotionLayout = activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||
if (mainMotionLayout.progress == 0.toFloat()) {
|
||||
mainMotionLayout.transitionToEnd()
|
||||
activity.findViewById<MotionLayout>(R.id.playerMotionLayout).transitionToEnd()
|
||||
holder.binding.apply {
|
||||
textViewTitle.text = trending.title
|
||||
textViewChannel.text =
|
||||
trending.uploaderName + " • " +
|
||||
trending.views.formatShort() + " • " +
|
||||
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
|
||||
if (trending.duration != -1L) {
|
||||
thumbnailDuration.text = DateUtils.formatElapsedTime(trending.duration!!)
|
||||
} else {
|
||||
thumbnailDuration.text = root.context.getString(R.string.live)
|
||||
thumbnailDuration.setBackgroundColor(R.attr.colorPrimaryDark)
|
||||
}
|
||||
channelImage.setOnClickListener {
|
||||
val activity = root.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to trending.uploaderUrl)
|
||||
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||
try {
|
||||
val mainMotionLayout =
|
||||
activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||
if (mainMotionLayout.progress == 0.toFloat()) {
|
||||
mainMotionLayout.transitionToEnd()
|
||||
activity.findViewById<MotionLayout>(R.id.playerMotionLayout)
|
||||
.transitionToEnd()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
if (trending.thumbnail!!.isNotEmpty()) {
|
||||
Picasso.get().load(trending.thumbnail).into(thumbnail)
|
||||
}
|
||||
if (trending.uploaderAvatar!!.isNotEmpty()) {
|
||||
Picasso.get().load(trending.uploaderAvatar).into(channelImage)
|
||||
}
|
||||
|
||||
root.setOnClickListener {
|
||||
var bundle = Bundle()
|
||||
bundle.putString("videoId", trending.url!!.replace("/watch?v=", ""))
|
||||
var frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
val activity = root.context as AppCompatActivity
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
}
|
||||
root.setOnLongClickListener {
|
||||
val videoId = trending.url!!.replace("/watch?v=", "")
|
||||
VideoOptionsDialog(videoId, root.context)
|
||||
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
||||
true
|
||||
}
|
||||
}
|
||||
if (trending.thumbnail!!.isEmpty()) {
|
||||
} else {
|
||||
Picasso.get().load(trending.thumbnail).into(thumbnailImage)
|
||||
}
|
||||
if (trending.uploaderAvatar!!.isEmpty()) {
|
||||
} else {
|
||||
Picasso.get().load(trending.uploaderAvatar).into(channelImage)
|
||||
}
|
||||
|
||||
holder.v.setOnClickListener {
|
||||
var bundle = Bundle()
|
||||
bundle.putString("videoId", trending.url!!.replace("/watch?v=", ""))
|
||||
var frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
val activity = holder.v.context as AppCompatActivity
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
}
|
||||
holder.v.setOnLongClickListener {
|
||||
val videoId = trending.url!!.replace("/watch?v=", "")
|
||||
VideoOptionsDialog(videoId, holder.v.context)
|
||||
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TrendingViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
||||
init {
|
||||
}
|
||||
}
|
||||
class TrendingViewHolder(val binding: TrendingRowBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
|
@ -0,0 +1,92 @@
|
||||
package com.github.libretube.adapters
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.format.DateUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.MainActivity
|
||||
import com.github.libretube.databinding.WatchHistoryRowBinding
|
||||
import com.github.libretube.dialogs.VideoOptionsDialog
|
||||
import com.github.libretube.fragments.PlayerFragment
|
||||
import com.github.libretube.obj.WatchHistoryItem
|
||||
import com.squareup.picasso.Picasso
|
||||
|
||||
class WatchHistoryAdapter(
|
||||
private val watchHistory: MutableList<WatchHistoryItem>,
|
||||
private val childFragmentManager: FragmentManager
|
||||
) :
|
||||
RecyclerView.Adapter<WatchHistoryViewHolder>() {
|
||||
private val TAG = "WatchHistoryAdapter"
|
||||
|
||||
fun clear() {
|
||||
val size = watchHistory.size
|
||||
watchHistory.clear()
|
||||
notifyItemRangeRemoved(0, size)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WatchHistoryViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val binding = WatchHistoryRowBinding.inflate(layoutInflater, parent, false)
|
||||
return WatchHistoryViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: WatchHistoryViewHolder, position: Int) {
|
||||
val video = watchHistory[position]
|
||||
holder.binding.apply {
|
||||
videoTitle.text = video.title
|
||||
channelName.text = video.uploader
|
||||
uploadDate.text = video.uploadDate
|
||||
thumbnailDuration.text = DateUtils.formatElapsedTime(video.duration?.toLong()!!)
|
||||
Picasso.get().load(video.thumbnailUrl).into(thumbnail)
|
||||
Picasso.get().load(video.uploaderAvatar).into(channelImage)
|
||||
|
||||
channelImage.setOnClickListener {
|
||||
val activity = root.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to video.uploaderUrl)
|
||||
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||
try {
|
||||
val mainMotionLayout =
|
||||
activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||
if (mainMotionLayout.progress == 0.toFloat()) {
|
||||
mainMotionLayout.transitionToEnd()
|
||||
activity.findViewById<MotionLayout>(R.id.playerMotionLayout)
|
||||
.transitionToEnd()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
root.setOnClickListener {
|
||||
var bundle = Bundle()
|
||||
bundle.putString("videoId", video.videoId)
|
||||
var frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
val activity = root.context as AppCompatActivity
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
}
|
||||
root.setOnLongClickListener {
|
||||
VideoOptionsDialog(video.videoId!!, root.context)
|
||||
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return watchHistory.size
|
||||
}
|
||||
}
|
||||
|
||||
class WatchHistoryViewHolder(val binding: WatchHistoryRowBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
@ -3,54 +3,42 @@ package com.github.libretube.dialogs
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Button
|
||||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.DialogAddtoplaylistBinding
|
||||
import com.github.libretube.obj.PlaylistId
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class AddtoPlaylistDialog : DialogFragment() {
|
||||
private val TAG = "AddToPlaylistDialog"
|
||||
private lateinit var binding: DialogAddtoplaylistBinding
|
||||
|
||||
private lateinit var videoId: String
|
||||
private lateinit var token: String
|
||||
private lateinit var spinner: Spinner
|
||||
private lateinit var button: Button
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return activity?.let {
|
||||
videoId = arguments?.getString("videoId")!!
|
||||
val builder = MaterialAlertDialogBuilder(it)
|
||||
// Get the layout inflater
|
||||
val inflater = requireActivity().layoutInflater
|
||||
token = PreferenceHelper.getToken(requireContext())
|
||||
var view: View = inflater.inflate(R.layout.dialog_addtoplaylist, null)
|
||||
spinner = view.findViewById(R.id.playlists_spinner)
|
||||
button = view.findViewById(R.id.addToPlaylist)
|
||||
if (token != "") {
|
||||
fetchPlaylists()
|
||||
}
|
||||
val typedValue = TypedValue()
|
||||
this.requireActivity().theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true)
|
||||
val hexColor = String.format("#%06X", (0xFFFFFF and typedValue.data))
|
||||
val appName = HtmlCompat.fromHtml(
|
||||
"Libre<span style='color:$hexColor';>Tube</span>",
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
)
|
||||
view.findViewById<TextView>(R.id.title).text = appName
|
||||
binding = DialogAddtoplaylistBinding.inflate(layoutInflater)
|
||||
|
||||
builder.setView(view)
|
||||
token = PreferenceHelper.getToken(requireContext())
|
||||
|
||||
if (token != "") fetchPlaylists()
|
||||
|
||||
binding.title.text = ThemeHelper.getStyledAppName(requireContext())
|
||||
|
||||
builder.setView(binding.root)
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
@ -59,7 +47,7 @@ class AddtoPlaylistDialog : DialogFragment() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.api.playlists(token)
|
||||
RetrofitInstance.authApi.playlists(token)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
@ -80,10 +68,12 @@ class AddtoPlaylistDialog : DialogFragment() {
|
||||
arrayAdapter.setDropDownViewResource(
|
||||
android.R.layout.simple_spinner_dropdown_item
|
||||
)
|
||||
spinner.adapter = arrayAdapter
|
||||
binding.playlistsSpinner.adapter = arrayAdapter
|
||||
runOnUiThread {
|
||||
button.setOnClickListener {
|
||||
addToPlaylist(response[spinner.selectedItemPosition].id!!)
|
||||
binding.addToPlaylist.setOnClickListener {
|
||||
addToPlaylist(
|
||||
response[binding.playlistsSpinner.selectedItemPosition].id!!
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -97,7 +87,7 @@ class AddtoPlaylistDialog : DialogFragment() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.api.addToPlaylist(token, PlaylistId(playlistId, videoId))
|
||||
RetrofitInstance.authApi.addToPlaylist(token, PlaylistId(playlistId, videoId))
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
|
@ -3,56 +3,42 @@ package com.github.libretube.dialogs
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.fragments.Library
|
||||
import com.github.libretube.databinding.DialogCreatePlaylistBinding
|
||||
import com.github.libretube.fragments.LibraryFragment
|
||||
import com.github.libretube.obj.Playlists
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class CreatePlaylistDialog : DialogFragment() {
|
||||
val TAG = "CreatePlaylistDialog"
|
||||
private var token: String = ""
|
||||
private lateinit var binding: DialogCreatePlaylistBinding
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return activity?.let {
|
||||
val builder = MaterialAlertDialogBuilder(it)
|
||||
val inflater = requireActivity().layoutInflater
|
||||
val view: View = inflater.inflate(R.layout.dialog_create_playlist, null)
|
||||
binding = DialogCreatePlaylistBinding.inflate(layoutInflater)
|
||||
|
||||
val typedValue = TypedValue()
|
||||
this.requireActivity().theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true)
|
||||
val hexColor = String.format("#%06X", (0xFFFFFF and typedValue.data))
|
||||
val appName = HtmlCompat.fromHtml(
|
||||
"Libre<span style='color:$hexColor';>Tube</span>",
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
)
|
||||
view.findViewById<TextView>(R.id.title).text = appName
|
||||
binding.title.text = ThemeHelper.getStyledAppName(requireContext())
|
||||
|
||||
val cancelBtn = view.findViewById<Button>(R.id.cancel_button)
|
||||
cancelBtn.setOnClickListener {
|
||||
binding.cancelButton.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
token = PreferenceHelper.getToken(requireContext())
|
||||
|
||||
val playlistName = view.findViewById<TextInputEditText>(R.id.playlist_name)
|
||||
val createPlaylistBtn = view.findViewById<Button>(R.id.create_new_playlist)
|
||||
createPlaylistBtn.setOnClickListener {
|
||||
binding.createNewPlaylist.setOnClickListener {
|
||||
// avoid creating the same playlist multiple times by spamming the button
|
||||
createPlaylistBtn.setOnClickListener(null)
|
||||
val listName = playlistName.text.toString()
|
||||
binding.createNewPlaylist.setOnClickListener(null)
|
||||
val listName = binding.playlistName.text.toString()
|
||||
if (listName != "") {
|
||||
createPlaylist(listName)
|
||||
} else {
|
||||
@ -60,7 +46,7 @@ class CreatePlaylistDialog : DialogFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
builder.setView(view)
|
||||
builder.setView(binding.root)
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
@ -69,7 +55,7 @@ class CreatePlaylistDialog : DialogFragment() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.api.createPlaylist(token, Playlists(name = name))
|
||||
RetrofitInstance.authApi.createPlaylist(token, Playlists(name = name))
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
@ -88,7 +74,7 @@ class CreatePlaylistDialog : DialogFragment() {
|
||||
}
|
||||
// refresh the playlists in the library
|
||||
try {
|
||||
val parent = parentFragment as Library
|
||||
val parent = parentFragment as LibraryFragment
|
||||
parent.fetchPlaylists()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.toString())
|
||||
|
@ -2,45 +2,34 @@ package com.github.libretube.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.DialogCustomInstanceBinding
|
||||
import com.github.libretube.obj.CustomInstance
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import java.net.URL
|
||||
|
||||
class CustomInstanceDialog : DialogFragment() {
|
||||
val TAG = "CustomInstanceDialog"
|
||||
private lateinit var binding: DialogCustomInstanceBinding
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return activity?.let {
|
||||
val builder = MaterialAlertDialogBuilder(it)
|
||||
val inflater = requireActivity().layoutInflater
|
||||
val view: View = inflater.inflate(R.layout.dialog_custom_instance, null)
|
||||
binding = DialogCustomInstanceBinding.inflate(layoutInflater)
|
||||
|
||||
val instanceNameEditText = view.findViewById<TextInputEditText>(R.id.instanceName)
|
||||
val instanceApiUrlEditText = view.findViewById<TextInputEditText>(R.id.instanceApiUrl)
|
||||
val instanceFrontendUrlEditText = view
|
||||
.findViewById<TextInputEditText>(R.id.instanceFrontendUrl)
|
||||
|
||||
val addInstanceButton = view.findViewById<Button>(R.id.addInstance)
|
||||
val cancelButton = view.findViewById<Button>(R.id.cancel)
|
||||
cancelButton.setOnClickListener {
|
||||
binding.cancel.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
addInstanceButton.setOnClickListener {
|
||||
binding.addInstance.setOnClickListener {
|
||||
val customInstance = CustomInstance()
|
||||
customInstance.name = instanceNameEditText.text.toString()
|
||||
customInstance.apiUrl = instanceApiUrlEditText.text.toString()
|
||||
customInstance.frontendUrl = instanceFrontendUrlEditText.text.toString()
|
||||
customInstance.name = binding.instanceName.text.toString()
|
||||
customInstance.apiUrl = binding.instanceApiUrl.text.toString()
|
||||
customInstance.frontendUrl = binding.instanceFrontendUrl.text.toString()
|
||||
|
||||
if (
|
||||
customInstance.name != "" &&
|
||||
@ -73,16 +62,9 @@ class CustomInstanceDialog : DialogFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
val typedValue = TypedValue()
|
||||
this.requireActivity().theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true)
|
||||
val hexColor = String.format("#%06X", (0xFFFFFF and typedValue.data))
|
||||
val appName = HtmlCompat.fromHtml(
|
||||
"Libre<span style='color:$hexColor';>Tube</span>",
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
)
|
||||
view.findViewById<TextView>(R.id.title).text = appName
|
||||
binding.title.text = ThemeHelper.getStyledAppName(requireContext())
|
||||
|
||||
builder.setView(view)
|
||||
builder.setView(binding.root)
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
|
@ -3,54 +3,42 @@ package com.github.libretube.dialogs
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.requireMainActivityRestart
|
||||
import com.github.libretube.databinding.DialogDeleteAccountBinding
|
||||
import com.github.libretube.obj.DeleteUserRequest
|
||||
import com.github.libretube.requireMainActivityRestart
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
class DeleteAccountDialog : DialogFragment() {
|
||||
private val TAG = "DeleteAccountDialog"
|
||||
lateinit var username: EditText
|
||||
lateinit var password: EditText
|
||||
private lateinit var binding: DialogDeleteAccountBinding
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return activity?.let {
|
||||
val builder = MaterialAlertDialogBuilder(it)
|
||||
val inflater = requireActivity().layoutInflater
|
||||
val view = inflater.inflate(R.layout.dialog_delete_account, null)
|
||||
binding = DialogDeleteAccountBinding.inflate(layoutInflater)
|
||||
|
||||
view.findViewById<Button>(R.id.cancel_button).setOnClickListener {
|
||||
binding.cancelButton.setOnClickListener {
|
||||
dialog?.dismiss()
|
||||
}
|
||||
|
||||
password = view.findViewById(R.id.delete_password)
|
||||
view.findViewById<Button>(R.id.delete_account_confirm).setOnClickListener {
|
||||
if (password.text.toString() != "") {
|
||||
deleteAccount(password.text.toString())
|
||||
binding.deleteAccountConfirm.setOnClickListener {
|
||||
if (binding.deletePassword.text.toString() != "") {
|
||||
deleteAccount(binding.deletePassword.text.toString())
|
||||
} else {
|
||||
Toast.makeText(context, R.string.empty, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
val typedValue = TypedValue()
|
||||
this.requireActivity().theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true)
|
||||
val hexColor = String.format("#%06X", (0xFFFFFF and typedValue.data))
|
||||
val appName = HtmlCompat.fromHtml(
|
||||
"Libre<span style='color:$hexColor';>Tube</span>",
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
)
|
||||
view.findViewById<TextView>(R.id.title).text = appName
|
||||
binding.title.text = ThemeHelper.getStyledAppName(requireContext())
|
||||
|
||||
builder.setView(view)
|
||||
builder.setView(binding.root)
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
@ -61,7 +49,7 @@ class DeleteAccountDialog : DialogFragment() {
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
|
||||
try {
|
||||
RetrofitInstance.api.deleteAccount(token, DeleteUserRequest(password))
|
||||
RetrofitInstance.authApi.deleteAccount(token, DeleteUserRequest(password))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.toString())
|
||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
|
@ -8,23 +8,25 @@ import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Button
|
||||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.github.libretube.MainActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.MainActivity
|
||||
import com.github.libretube.databinding.DialogDownloadBinding
|
||||
import com.github.libretube.obj.Streams
|
||||
import com.github.libretube.services.DownloadService
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class DownloadDialog : DialogFragment() {
|
||||
private val TAG = "DownloadDialog"
|
||||
private lateinit var binding: DialogDownloadBinding
|
||||
|
||||
private lateinit var streams: Streams
|
||||
private lateinit var videoId: String
|
||||
@ -32,14 +34,13 @@ class DownloadDialog : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return activity?.let {
|
||||
streams = arguments?.getParcelable("streams")!!
|
||||
videoId = arguments?.getString("video_id")!!
|
||||
|
||||
val mainActivity = activity as MainActivity
|
||||
val builder = MaterialAlertDialogBuilder(it)
|
||||
// Get the layout inflater
|
||||
val inflater = requireActivity().layoutInflater
|
||||
var view: View = inflater.inflate(R.layout.dialog_download, null)
|
||||
binding = DialogDownloadBinding.inflate(layoutInflater)
|
||||
|
||||
fetchStreams()
|
||||
|
||||
// request storage permissions if not granted yet
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
@ -75,78 +76,89 @@ class DownloadDialog : DialogFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
var vidName = arrayListOf<String>()
|
||||
var vidUrl = arrayListOf<String>()
|
||||
binding.title.text = ThemeHelper.getStyledAppName(requireContext())
|
||||
|
||||
// add empty selection
|
||||
vidName.add(getString(R.string.no_video))
|
||||
vidUrl.add("")
|
||||
|
||||
// add all available video streams
|
||||
for (vid in streams.videoStreams!!) {
|
||||
val name = vid.quality + " " + vid.format
|
||||
vidName.add(name)
|
||||
vidUrl.add(vid.url!!)
|
||||
}
|
||||
|
||||
var audioName = arrayListOf<String>()
|
||||
var audioUrl = arrayListOf<String>()
|
||||
|
||||
// add empty selection
|
||||
audioName.add(getString(R.string.no_audio))
|
||||
audioUrl.add("")
|
||||
|
||||
// add all available audio streams
|
||||
for (audio in streams.audioStreams!!) {
|
||||
val name = audio.quality + " " + audio.format
|
||||
audioName.add(name)
|
||||
audioUrl.add(audio.url!!)
|
||||
}
|
||||
|
||||
val videoSpinner = view.findViewById<Spinner>(R.id.video_spinner)
|
||||
val videoArrayAdapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_spinner_item,
|
||||
vidName
|
||||
)
|
||||
videoArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
videoSpinner.adapter = videoArrayAdapter
|
||||
videoSpinner.setSelection(1)
|
||||
|
||||
val audioSpinner = view.findViewById<Spinner>(R.id.audio_spinner)
|
||||
val audioArrayAdapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_spinner_item,
|
||||
audioName
|
||||
)
|
||||
audioArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
audioSpinner.adapter = audioArrayAdapter
|
||||
audioSpinner.setSelection(1)
|
||||
|
||||
view.findViewById<Button>(R.id.download).setOnClickListener {
|
||||
val selectedAudioUrl = audioUrl[audioSpinner.selectedItemPosition]
|
||||
val selectedVideoUrl = vidUrl[videoSpinner.selectedItemPosition]
|
||||
|
||||
val intent = Intent(context, DownloadService::class.java)
|
||||
intent.putExtra("videoId", videoId)
|
||||
intent.putExtra("videoUrl", selectedVideoUrl)
|
||||
intent.putExtra("audioUrl", selectedAudioUrl)
|
||||
intent.putExtra("duration", duration)
|
||||
context?.startService(intent)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
val typedValue = TypedValue()
|
||||
this.requireActivity().theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true)
|
||||
val hexColor = String.format("#%06X", (0xFFFFFF and typedValue.data))
|
||||
val appName = HtmlCompat.fromHtml(
|
||||
"Libre<span style='color:$hexColor';>Tube</span>",
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
)
|
||||
view.findViewById<TextView>(R.id.title).text = appName
|
||||
|
||||
builder.setView(view)
|
||||
builder.setView(binding.root)
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
|
||||
private fun fetchStreams() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.api.getStreams(videoId!!)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
return@launchWhenCreated
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG, "HttpException, unexpected response")
|
||||
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
|
||||
return@launchWhenCreated
|
||||
}
|
||||
initDownloadOptions(response)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initDownloadOptions(streams: Streams) {
|
||||
var vidName = arrayListOf<String>()
|
||||
var vidUrl = arrayListOf<String>()
|
||||
|
||||
// add empty selection
|
||||
vidName.add(getString(R.string.no_video))
|
||||
vidUrl.add("")
|
||||
|
||||
// add all available video streams
|
||||
for (vid in streams.videoStreams!!) {
|
||||
val name = vid.quality + " " + vid.format
|
||||
vidName.add(name)
|
||||
vidUrl.add(vid.url!!)
|
||||
}
|
||||
|
||||
var audioName = arrayListOf<String>()
|
||||
var audioUrl = arrayListOf<String>()
|
||||
|
||||
// add empty selection
|
||||
audioName.add(getString(R.string.no_audio))
|
||||
audioUrl.add("")
|
||||
|
||||
// add all available audio streams
|
||||
for (audio in streams.audioStreams!!) {
|
||||
val name = audio.quality + " " + audio.format
|
||||
audioName.add(name)
|
||||
audioUrl.add(audio.url!!)
|
||||
}
|
||||
|
||||
val videoArrayAdapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_spinner_item,
|
||||
vidName
|
||||
)
|
||||
videoArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
binding.videoSpinner.adapter = videoArrayAdapter
|
||||
binding.videoSpinner.setSelection(1)
|
||||
|
||||
val audioArrayAdapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_spinner_item,
|
||||
audioName
|
||||
)
|
||||
audioArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
binding.audioSpinner.adapter = audioArrayAdapter
|
||||
binding.audioSpinner.setSelection(1)
|
||||
|
||||
binding.download.setOnClickListener {
|
||||
val selectedAudioUrl = audioUrl[binding.audioSpinner.selectedItemPosition]
|
||||
val selectedVideoUrl = vidUrl[binding.videoSpinner.selectedItemPosition]
|
||||
|
||||
val intent = Intent(context, DownloadService::class.java)
|
||||
intent.putExtra("videoId", videoId)
|
||||
intent.putExtra("videoUrl", selectedVideoUrl)
|
||||
intent.putExtra("audioUrl", selectedAudioUrl)
|
||||
intent.putExtra("duration", duration)
|
||||
context?.startService(intent)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,77 +3,57 @@ package com.github.libretube.dialogs
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.requireMainActivityRestart
|
||||
import com.github.libretube.databinding.DialogLoginBinding
|
||||
import com.github.libretube.obj.Login
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class LoginDialog : DialogFragment() {
|
||||
private val TAG = "LoginDialog"
|
||||
lateinit var username: EditText
|
||||
lateinit var password: EditText
|
||||
private lateinit var binding: DialogLoginBinding
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return activity?.let {
|
||||
val builder = MaterialAlertDialogBuilder(it)
|
||||
// Get the layout inflater
|
||||
val inflater = requireActivity().layoutInflater
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
var view: View
|
||||
Log.e("dafaq", token!!)
|
||||
if (token != "") {
|
||||
val user = PreferenceHelper.getUsername(requireContext())
|
||||
view = inflater.inflate(R.layout.dialog_logout, null)
|
||||
view.findViewById<TextView>(R.id.user).text =
|
||||
view.findViewById<TextView>(R.id.user).text.toString() + " (" + user + ")"
|
||||
view.findViewById<Button>(R.id.logout).setOnClickListener {
|
||||
Toast.makeText(context, R.string.loggedout, Toast.LENGTH_SHORT).show()
|
||||
PreferenceHelper.setToken(requireContext(), "")
|
||||
dialog?.dismiss()
|
||||
binding = DialogLoginBinding.inflate(layoutInflater)
|
||||
|
||||
binding.login.setOnClickListener {
|
||||
if (binding.username.text.toString() != "" && binding.password.text.toString() != "") {
|
||||
val login =
|
||||
Login(binding.username.text.toString(), binding.password.text.toString())
|
||||
login(login)
|
||||
} else {
|
||||
Toast.makeText(context, R.string.empty, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} else {
|
||||
view = inflater.inflate(R.layout.dialog_login, null)
|
||||
username = view.findViewById(R.id.username)
|
||||
password = view.findViewById(R.id.password)
|
||||
view.findViewById<Button>(R.id.login).setOnClickListener {
|
||||
if (username.text.toString() != "" && password.text.toString() != "") {
|
||||
val login = Login(username.text.toString(), password.text.toString())
|
||||
login(login)
|
||||
} else {
|
||||
Toast.makeText(context, R.string.empty, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
view.findViewById<Button>(R.id.register).setOnClickListener {
|
||||
if (username.text.toString() != "" && password.text.toString() != "") {
|
||||
val login = Login(username.text.toString(), password.text.toString())
|
||||
register(login)
|
||||
} else {
|
||||
Toast.makeText(context, R.string.empty, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
binding.register.setOnClickListener {
|
||||
if (
|
||||
binding.username.text.toString() != "" &&
|
||||
binding.password.text.toString() != ""
|
||||
) {
|
||||
val login = Login(
|
||||
binding.username.text.toString(),
|
||||
binding.password.text.toString()
|
||||
)
|
||||
register(login)
|
||||
} else {
|
||||
Toast.makeText(context, R.string.empty, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
val typedValue = TypedValue()
|
||||
this.requireActivity().theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true)
|
||||
val hexColor = String.format("#%06X", (0xFFFFFF and typedValue.data))
|
||||
val appName = HtmlCompat.fromHtml(
|
||||
"Libre<span style='color:$hexColor';>Tube</span>",
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
)
|
||||
view.findViewById<TextView>(R.id.title).text = appName
|
||||
binding.title.text = ThemeHelper.getStyledAppName(requireContext())
|
||||
|
||||
builder.setView(view)
|
||||
builder.setView(binding.root)
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
@ -82,7 +62,7 @@ class LoginDialog : DialogFragment() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.api.login(login)
|
||||
RetrofitInstance.authApi.login(login)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
@ -102,7 +82,9 @@ class LoginDialog : DialogFragment() {
|
||||
Toast.makeText(context, R.string.loggedIn, Toast.LENGTH_SHORT).show()
|
||||
PreferenceHelper.setToken(requireContext(), response.token!!)
|
||||
PreferenceHelper.setUsername(requireContext(), login.username!!)
|
||||
requireMainActivityRestart = true
|
||||
dialog?.dismiss()
|
||||
activity?.recreate()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,7 +95,7 @@ class LoginDialog : DialogFragment() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.api.register(login)
|
||||
RetrofitInstance.authApi.register(login)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
|
@ -0,0 +1,41 @@
|
||||
package com.github.libretube.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.requireMainActivityRestart
|
||||
import com.github.libretube.databinding.DialogLogoutBinding
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
class LogoutDialog : DialogFragment() {
|
||||
private val TAG = "LogoutDialog"
|
||||
private lateinit var binding: DialogLogoutBinding
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return activity?.let {
|
||||
val builder = MaterialAlertDialogBuilder(it)
|
||||
binding = DialogLogoutBinding.inflate(layoutInflater)
|
||||
|
||||
val user = PreferenceHelper.getUsername(requireContext())
|
||||
|
||||
binding.user.text =
|
||||
binding.user.text.toString() + " (" + user + ")"
|
||||
binding.logout.setOnClickListener {
|
||||
requireMainActivityRestart = true
|
||||
Toast.makeText(context, R.string.loggedout, Toast.LENGTH_SHORT).show()
|
||||
PreferenceHelper.setToken(requireContext(), "")
|
||||
dialog?.dismiss()
|
||||
activity?.recreate()
|
||||
}
|
||||
|
||||
binding.title.text = ThemeHelper.getStyledAppName(requireContext())
|
||||
|
||||
builder.setView(binding.root)
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.obj.PlaylistId
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@ -20,16 +20,23 @@ import java.io.IOException
|
||||
|
||||
class PlaylistOptionsDialog(
|
||||
private val playlistId: String,
|
||||
private val isOwner: Boolean,
|
||||
context: Context
|
||||
) : DialogFragment() {
|
||||
val TAG = "PlaylistOptionsDialog"
|
||||
|
||||
private val optionsList = listOf(
|
||||
private var optionsList = listOf(
|
||||
context.getString(R.string.clonePlaylist),
|
||||
context.getString(R.string.share)
|
||||
)
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
if (isOwner) {
|
||||
optionsList = optionsList +
|
||||
context?.getString(R.string.deletePlaylist)!! -
|
||||
context?.getString(R.string.clonePlaylist)!!
|
||||
}
|
||||
|
||||
val dialog = MaterialAlertDialogBuilder(requireContext())
|
||||
.setNegativeButton(R.string.cancel) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
@ -41,12 +48,12 @@ class PlaylistOptionsDialog(
|
||||
optionsList
|
||||
)
|
||||
) { _, which ->
|
||||
when (which) {
|
||||
when (optionsList[which]) {
|
||||
// Clone the playlist to the users Piped account
|
||||
0 -> {
|
||||
context?.getString(R.string.clonePlaylist) -> {
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
if (token != "") {
|
||||
importPlaylist(token!!, playlistId)
|
||||
importPlaylist(token, playlistId)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
context,
|
||||
@ -56,11 +63,15 @@ class PlaylistOptionsDialog(
|
||||
}
|
||||
}
|
||||
// share the playlist
|
||||
1 -> {
|
||||
context?.getString(R.string.share) -> {
|
||||
val shareDialog = ShareDialog(playlistId, true)
|
||||
// using parentFragmentManager is important here
|
||||
// using parentFragmentManager, childFragmentManager doesn't work here
|
||||
shareDialog.show(parentFragmentManager, "ShareDialog")
|
||||
}
|
||||
context?.getString(R.string.deletePlaylist) -> {
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
deletePlaylist(playlistId, token)
|
||||
}
|
||||
}
|
||||
}
|
||||
return dialog.show()
|
||||
@ -70,7 +81,7 @@ class PlaylistOptionsDialog(
|
||||
fun run() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val response = try {
|
||||
RetrofitInstance.api.importPlaylist(token, PlaylistId(playlistId))
|
||||
RetrofitInstance.authApi.importPlaylist(token, PlaylistId(playlistId))
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
return@launch
|
||||
@ -82,4 +93,22 @@ class PlaylistOptionsDialog(
|
||||
}
|
||||
run()
|
||||
}
|
||||
|
||||
private fun deletePlaylist(id: String, token: String) {
|
||||
fun run() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val response = try {
|
||||
RetrofitInstance.authApi.deletePlaylist(token, PlaylistId(id))
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
return@launch
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG, "HttpException, unexpected response")
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
}
|
||||
run()
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
class ShareDialog(
|
||||
|
@ -6,9 +6,9 @@ import android.os.Bundle
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.github.libretube.BackgroundMode
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.BackgroundMode
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
/**
|
||||
@ -43,14 +43,14 @@ class VideoOptionsDialog(private val videoId: String, context: Context) : Dialog
|
||||
) { _, which ->
|
||||
// For now, this checks the position of the option with the position that is in the
|
||||
// list. I don't like it, but we will do like this for now.
|
||||
when (which) {
|
||||
when (optionsList[which]) {
|
||||
// This for example will be the "Background mode" option
|
||||
0 -> {
|
||||
context?.getString(R.string.playOnBackground) -> {
|
||||
BackgroundMode.getInstance()
|
||||
.playOnBackgroundMode(requireContext(), videoId, 0)
|
||||
.playOnBackgroundMode(requireContext(), videoId)
|
||||
}
|
||||
// Add Video to Playlist Dialog
|
||||
1 -> {
|
||||
context?.getString(R.string.addToPlaylist) -> {
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
if (token != "") {
|
||||
val newFragment = AddtoPlaylistDialog()
|
||||
@ -62,7 +62,7 @@ class VideoOptionsDialog(private val videoId: String, context: Context) : Dialog
|
||||
Toast.makeText(context, R.string.login_first, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
context?.getString(R.string.share) -> {
|
||||
val shareDialog = ShareDialog(videoId, false)
|
||||
// using parentFragmentManager is important here
|
||||
shareDialog.show(parentFragmentManager, "ShareDialog")
|
||||
|
@ -6,18 +6,14 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.ScrollView
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.adapters.ChannelAdapter
|
||||
import com.github.libretube.databinding.FragmentChannelBinding
|
||||
import com.github.libretube.obj.Subscribe
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.util.formatShort
|
||||
import com.google.android.material.button.MaterialButton
|
||||
@ -26,19 +22,19 @@ import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class ChannelFragment : Fragment() {
|
||||
|
||||
private var channel_id: String? = null
|
||||
private val TAG = "ChannelFragment"
|
||||
private lateinit var binding: FragmentChannelBinding
|
||||
|
||||
private var channelId: String? = null
|
||||
var nextPage: String? = null
|
||||
var channelAdapter: ChannelAdapter? = null
|
||||
var isLoading = true
|
||||
var isSubscribed: Boolean = false
|
||||
private var refreshLayout: SwipeRefreshLayout? = null
|
||||
private var channelAdapter: ChannelAdapter? = null
|
||||
private var isLoading = true
|
||||
private var isSubscribed: Boolean = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
arguments?.let {
|
||||
channel_id = it.getString("channel_id")
|
||||
channelId = it.getString("channel_id")
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,43 +42,39 @@ class ChannelFragment : Fragment() {
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_channel, container, false)
|
||||
): View {
|
||||
binding = FragmentChannelBinding.inflate(layoutInflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
channel_id = channel_id!!.replace("/channel/", "")
|
||||
view.findViewById<TextView>(R.id.channel_name).text = channel_id
|
||||
val recyclerView = view.findViewById<RecyclerView>(R.id.channel_recView)
|
||||
recyclerView.layoutManager = LinearLayoutManager(context)
|
||||
refreshLayout = view.findViewById(R.id.channel_refresh)
|
||||
channelId = channelId!!.replace("/channel/", "")
|
||||
binding.channelName.text = channelId
|
||||
binding.channelRecView.layoutManager = LinearLayoutManager(context)
|
||||
|
||||
val refreshChannel = {
|
||||
refreshLayout?.isRefreshing = true
|
||||
fetchChannel(view)
|
||||
val subButton = view.findViewById<MaterialButton>(R.id.channel_subscribe)
|
||||
binding.channelRefresh.isRefreshing = true
|
||||
fetchChannel()
|
||||
if (PreferenceHelper.getToken(requireContext()) != "") {
|
||||
isSubscribed(subButton)
|
||||
isSubscribed(binding.channelSubscribe)
|
||||
}
|
||||
}
|
||||
refreshChannel()
|
||||
refreshLayout?.setOnRefreshListener {
|
||||
binding.channelRefresh.setOnRefreshListener {
|
||||
refreshChannel()
|
||||
}
|
||||
|
||||
val scrollView = view.findViewById<ScrollView>(R.id.channel_scrollView)
|
||||
scrollView.viewTreeObserver
|
||||
binding.channelScrollView.viewTreeObserver
|
||||
.addOnScrollChangedListener {
|
||||
if (scrollView.getChildAt(0).bottom
|
||||
== (scrollView.height + scrollView.scrollY)
|
||||
if (binding.channelScrollView.getChildAt(0).bottom
|
||||
== (binding.channelScrollView.height + binding.channelScrollView.scrollY)
|
||||
) {
|
||||
// scroll view is at bottom
|
||||
if (nextPage != null && !isLoading) {
|
||||
isLoading = true
|
||||
refreshLayout?.isRefreshing = true
|
||||
binding.channelRefresh.isRefreshing = true
|
||||
fetchNextPage()
|
||||
}
|
||||
}
|
||||
@ -95,8 +87,8 @@ class ChannelFragment : Fragment() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
RetrofitInstance.api.isSubscribed(
|
||||
channel_id!!,
|
||||
RetrofitInstance.authApi.isSubscribed(
|
||||
channelId!!,
|
||||
token
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
@ -135,9 +127,9 @@ class ChannelFragment : Fragment() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
RetrofitInstance.api.subscribe(
|
||||
RetrofitInstance.authApi.subscribe(
|
||||
token,
|
||||
Subscribe(channel_id)
|
||||
Subscribe(channelId)
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
@ -158,9 +150,9 @@ class ChannelFragment : Fragment() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
RetrofitInstance.api.unsubscribe(
|
||||
RetrofitInstance.authApi.unsubscribe(
|
||||
token,
|
||||
Subscribe(channel_id)
|
||||
Subscribe(channelId)
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
@ -176,55 +168,52 @@ class ChannelFragment : Fragment() {
|
||||
run()
|
||||
}
|
||||
|
||||
private fun fetchChannel(view: View) {
|
||||
private fun fetchChannel() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.api.getChannel(channel_id!!)
|
||||
RetrofitInstance.api.getChannel(channelId!!)
|
||||
} catch (e: IOException) {
|
||||
refreshLayout?.isRefreshing = false
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
return@launchWhenCreated
|
||||
} catch (e: HttpException) {
|
||||
refreshLayout?.isRefreshing = false
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
Log.e(TAG, "HttpException, unexpected response")
|
||||
return@launchWhenCreated
|
||||
}
|
||||
nextPage = response.nextpage
|
||||
isLoading = false
|
||||
refreshLayout?.isRefreshing = false
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
runOnUiThread {
|
||||
view.findViewById<ScrollView>(R.id.channel_scrollView).visibility = View.VISIBLE
|
||||
val channelName = view.findViewById<TextView>(R.id.channel_name)
|
||||
channelName.text = response.name
|
||||
binding.channelScrollView.visibility = View.VISIBLE
|
||||
binding.channelName.text = response.name
|
||||
if (response.verified) {
|
||||
channelName.setCompoundDrawablesWithIntrinsicBounds(
|
||||
binding.channelName.setCompoundDrawablesWithIntrinsicBounds(
|
||||
0,
|
||||
0,
|
||||
R.drawable.ic_verified,
|
||||
0
|
||||
)
|
||||
}
|
||||
view.findViewById<TextView>(R.id.channel_subs).text = resources.getString(
|
||||
binding.channelSubs.text = resources.getString(
|
||||
R.string.subscribers,
|
||||
response.subscriberCount.formatShort()
|
||||
)
|
||||
val channelDescription = view.findViewById<TextView>(R.id.channel_description)
|
||||
if (response.description?.trim() == "") {
|
||||
channelDescription.visibility = View.GONE
|
||||
binding.channelDescription.visibility = View.GONE
|
||||
} else {
|
||||
channelDescription.text = response.description?.trim()
|
||||
binding.channelDescription.text = response.description?.trim()
|
||||
}
|
||||
val bannerImage = view.findViewById<ImageView>(R.id.channel_banner)
|
||||
val channelImage = view.findViewById<ImageView>(R.id.channel_image)
|
||||
Picasso.get().load(response.bannerUrl).into(bannerImage)
|
||||
Picasso.get().load(response.avatarUrl).into(channelImage)
|
||||
|
||||
Picasso.get().load(response.bannerUrl).into(binding.channelBanner)
|
||||
Picasso.get().load(response.avatarUrl).into(binding.channelImage)
|
||||
channelAdapter = ChannelAdapter(
|
||||
response.relatedStreams!!.toMutableList(),
|
||||
childFragmentManager
|
||||
)
|
||||
view.findViewById<RecyclerView>(R.id.channel_recView).adapter = channelAdapter
|
||||
binding.channelRecView.adapter = channelAdapter
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -235,21 +224,21 @@ class ChannelFragment : Fragment() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.api.getChannelNextPage(channel_id!!, nextPage!!)
|
||||
RetrofitInstance.api.getChannelNextPage(channelId!!, nextPage!!)
|
||||
} catch (e: IOException) {
|
||||
refreshLayout?.isRefreshing = false
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
return@launchWhenCreated
|
||||
} catch (e: HttpException) {
|
||||
refreshLayout?.isRefreshing = false
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
Log.e(TAG, "HttpException, unexpected response," + e.response())
|
||||
return@launchWhenCreated
|
||||
}
|
||||
nextPage = response.nextpage
|
||||
channelAdapter?.updateItems(response.relatedStreams!!)
|
||||
isLoading = false
|
||||
refreshLayout?.isRefreshing = false
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
}
|
||||
}
|
||||
run()
|
||||
@ -260,13 +249,4 @@ class ChannelFragment : Fragment() {
|
||||
if (!isAdded) return // Fragment not attached to an Activity
|
||||
activity?.runOnUiThread(action)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
val scrollView = view?.findViewById<ScrollView>(R.id.channel_scrollView)
|
||||
scrollView?.viewTreeObserver?.removeOnScrollChangedListener {
|
||||
}
|
||||
channelAdapter = null
|
||||
view?.findViewById<RecyclerView>(R.id.channel_recView)?.adapter = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
||||
|
@ -5,24 +5,24 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.adapters.TrendingAdapter
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.databinding.FragmentHomeBinding
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.LocaleHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class Home : Fragment() {
|
||||
|
||||
class HomeFragment : Fragment() {
|
||||
private val TAG = "HomeFragment"
|
||||
private var refreshLayout: SwipeRefreshLayout? = null
|
||||
private lateinit var binding: FragmentHomeBinding
|
||||
private lateinit var region: String
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
arguments?.let {
|
||||
@ -33,36 +33,41 @@ class Home : Fragment() {
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_home, container, false)
|
||||
): View {
|
||||
binding = FragmentHomeBinding.inflate(layoutInflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val recyclerView = view.findViewById<RecyclerView>(R.id.recview)
|
||||
val grid = PreferenceHelper.getString(
|
||||
requireContext(),
|
||||
"grid",
|
||||
resources.getInteger(R.integer.grid_items).toString()
|
||||
)!!
|
||||
recyclerView.layoutManager = GridLayoutManager(view.context, grid.toInt())
|
||||
val progressbar = view.findViewById<ProgressBar>(R.id.progressBar)
|
||||
fetchJson(progressbar, recyclerView)
|
||||
refreshLayout = view.findViewById(R.id.home_refresh)
|
||||
refreshLayout?.isEnabled = true
|
||||
refreshLayout?.setOnRefreshListener {
|
||||
Log.d(TAG, "hmm")
|
||||
fetchJson(progressbar, recyclerView)
|
||||
|
||||
val regionPref = PreferenceHelper.getString(requireContext(), "region", "sys")!!
|
||||
|
||||
// get the system default country if auto region selected
|
||||
region = if (regionPref == "sys") {
|
||||
LocaleHelper
|
||||
.getDetectedCountry(requireContext(), "UK")
|
||||
.uppercase()
|
||||
} else regionPref
|
||||
|
||||
binding.recview.layoutManager = GridLayoutManager(view.context, grid.toInt())
|
||||
fetchJson()
|
||||
binding.homeRefresh.isEnabled = true
|
||||
binding.homeRefresh.setOnRefreshListener {
|
||||
fetchJson()
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchJson(progressBar: ProgressBar, recyclerView: RecyclerView) {
|
||||
private fun fetchJson() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
val region = PreferenceHelper.getString(requireContext(), "region", "US")
|
||||
RetrofitInstance.api.getTrending(region!!)
|
||||
RetrofitInstance.api.getTrending(region)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
@ -73,11 +78,11 @@ class Home : Fragment() {
|
||||
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
|
||||
return@launchWhenCreated
|
||||
} finally {
|
||||
refreshLayout?.isRefreshing = false
|
||||
binding.homeRefresh.isRefreshing = false
|
||||
}
|
||||
runOnUiThread {
|
||||
progressBar.visibility = View.GONE
|
||||
recyclerView.adapter = TrendingAdapter(response, childFragmentManager)
|
||||
binding.progressBar.visibility = View.GONE
|
||||
binding.recview.adapter = TrendingAdapter(response, childFragmentManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,11 +94,4 @@ class Home : Fragment() {
|
||||
if (!isAdded) return // Fragment not attached to an Activity
|
||||
activity?.runOnUiThread(action)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
view?.findViewById<RecyclerView>(R.id.recview)?.adapter = null
|
||||
refreshLayout = null
|
||||
Log.e(TAG, "destroyview")
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
@ -5,29 +5,25 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.adapters.PlaylistsAdapter
|
||||
import com.github.libretube.databinding.FragmentLibraryBinding
|
||||
import com.github.libretube.dialogs.CreatePlaylistDialog
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class Library : Fragment() {
|
||||
class LibraryFragment : Fragment() {
|
||||
|
||||
private val TAG = "LibraryFragment"
|
||||
lateinit var token: String
|
||||
private lateinit var playlistRecyclerView: RecyclerView
|
||||
private lateinit var refreshLayout: SwipeRefreshLayout
|
||||
private lateinit var binding: FragmentLibraryBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -39,51 +35,59 @@ class Library : Fragment() {
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_library, container, false)
|
||||
): View {
|
||||
binding = FragmentLibraryBinding.inflate(layoutInflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
playlistRecyclerView = view.findViewById(R.id.playlist_recView)
|
||||
playlistRecyclerView.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.playlistRecView.layoutManager = LinearLayoutManager(view.context)
|
||||
token = PreferenceHelper.getToken(requireContext())
|
||||
refreshLayout = view.findViewById(R.id.playlist_refresh)
|
||||
|
||||
// hide watch history button of history disabled
|
||||
val watchHistoryEnabled =
|
||||
PreferenceHelper.getBoolean(requireContext(), "watch_history_toggle", true)
|
||||
if (!watchHistoryEnabled) {
|
||||
binding.showWatchHistory.visibility = View.GONE
|
||||
} else {
|
||||
binding.showWatchHistory.setOnClickListener {
|
||||
findNavController().navigate(R.id.watchHistoryFragment)
|
||||
}
|
||||
}
|
||||
|
||||
if (token != "") {
|
||||
view.findViewById<ImageView>(R.id.boogh2).visibility = View.GONE
|
||||
view.findViewById<TextView>(R.id.textLike2).visibility = View.GONE
|
||||
binding.boogh.visibility = View.GONE
|
||||
binding.textLike.visibility = View.GONE
|
||||
fetchPlaylists()
|
||||
refreshLayout.isEnabled = true
|
||||
refreshLayout.setOnRefreshListener {
|
||||
binding.playlistRefresh.isEnabled = true
|
||||
binding.playlistRefresh.setOnRefreshListener {
|
||||
fetchPlaylists()
|
||||
}
|
||||
val createPlaylistButton = view.findViewById<FloatingActionButton>(R.id.create_playlist)
|
||||
createPlaylistButton.setOnClickListener {
|
||||
binding.createPlaylist.setOnClickListener {
|
||||
val newFragment = CreatePlaylistDialog()
|
||||
newFragment.show(childFragmentManager, "Create Playlist")
|
||||
}
|
||||
} else {
|
||||
refreshLayout.isEnabled = false
|
||||
view.findViewById<FloatingActionButton>(R.id.create_playlist).visibility = View.GONE
|
||||
binding.playlistRefresh.isEnabled = false
|
||||
binding.createPlaylist.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
// optimize CreatePlaylistFab bottom margin if miniPlayer active
|
||||
val createPlaylistButton = view?.findViewById<FloatingActionButton>(R.id.create_playlist)
|
||||
val layoutParams = createPlaylistButton?.layoutParams as ViewGroup.MarginLayoutParams
|
||||
val layoutParams = binding.createPlaylist.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParams.bottomMargin = if (isMiniPlayerVisible) 180 else 64
|
||||
createPlaylistButton?.layoutParams = layoutParams
|
||||
binding.createPlaylist.layoutParams = layoutParams
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
fun fetchPlaylists() {
|
||||
fun run() {
|
||||
refreshLayout.isRefreshing = true
|
||||
binding.playlistRefresh.isRefreshing = true
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.api.playlists(token)
|
||||
RetrofitInstance.authApi.playlists(token)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
@ -94,27 +98,27 @@ class Library : Fragment() {
|
||||
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
|
||||
return@launchWhenCreated
|
||||
} finally {
|
||||
refreshLayout.isRefreshing = false
|
||||
binding.playlistRefresh.isRefreshing = false
|
||||
}
|
||||
if (response.isNotEmpty()) {
|
||||
runOnUiThread {
|
||||
view?.findViewById<ImageView>(R.id.boogh2)?.visibility = View.GONE
|
||||
view?.findViewById<TextView>(R.id.textLike2)?.visibility = View.GONE
|
||||
binding.boogh.visibility = View.GONE
|
||||
binding.textLike.visibility = View.GONE
|
||||
}
|
||||
val playlistsAdapter = PlaylistsAdapter(
|
||||
response.toMutableList(),
|
||||
requireActivity()
|
||||
)
|
||||
playlistRecyclerView.adapter = playlistsAdapter
|
||||
binding.playlistRecView.adapter = playlistsAdapter
|
||||
} else {
|
||||
runOnUiThread {
|
||||
view?.findViewById<ImageView>(R.id.boogh2).apply {
|
||||
this?.visibility = View.VISIBLE
|
||||
this?.setImageResource(R.drawable.ic_list)
|
||||
binding.boogh.apply {
|
||||
visibility = View.VISIBLE
|
||||
setImageResource(R.drawable.ic_list)
|
||||
}
|
||||
view?.findViewById<TextView>(R.id.textLike2).apply {
|
||||
this?.visibility = View.VISIBLE
|
||||
this?.text = getString(R.string.emptyList)
|
||||
binding.textLike.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = getString(R.string.emptyList)
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@ import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.PowerManager
|
||||
import android.support.v4.media.session.MediaSessionCompat
|
||||
import android.text.Html
|
||||
@ -19,14 +21,6 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.ScrollView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
@ -37,16 +31,17 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.MainActivity
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.MainActivity
|
||||
import com.github.libretube.activities.hideKeyboard
|
||||
import com.github.libretube.adapters.ChaptersAdapter
|
||||
import com.github.libretube.adapters.CommentsAdapter
|
||||
import com.github.libretube.adapters.TrendingAdapter
|
||||
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
||||
import com.github.libretube.databinding.FragmentPlayerBinding
|
||||
import com.github.libretube.dialogs.AddtoPlaylistDialog
|
||||
import com.github.libretube.dialogs.DownloadDialog
|
||||
import com.github.libretube.dialogs.ShareDialog
|
||||
import com.github.libretube.hideKeyboard
|
||||
import com.github.libretube.obj.ChapterSegment
|
||||
import com.github.libretube.obj.PipedStream
|
||||
import com.github.libretube.obj.Playlist
|
||||
@ -56,12 +51,13 @@ import com.github.libretube.obj.SponsorBlockPrefs
|
||||
import com.github.libretube.obj.StreamItem
|
||||
import com.github.libretube.obj.Streams
|
||||
import com.github.libretube.obj.Subscribe
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.services.IS_DOWNLOAD_RUNNING
|
||||
import com.github.libretube.util.CronetHelper
|
||||
import com.github.libretube.util.DescriptionAdapter
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.util.formatShort
|
||||
import com.github.libretube.views.DoubleClickListener
|
||||
import com.google.android.exoplayer2.C
|
||||
import com.google.android.exoplayer2.DefaultLoadControl
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
@ -79,12 +75,13 @@ import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
|
||||
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
||||
import com.google.android.exoplayer2.ui.StyledPlayerView
|
||||
import com.google.android.exoplayer2.ui.TimeBar
|
||||
import com.google.android.exoplayer2.upstream.DataSource
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSource
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
|
||||
import com.google.android.exoplayer2.util.RepeatModeUtil
|
||||
import com.google.android.exoplayer2.video.VideoSize
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.squareup.picasso.Picasso
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@ -102,6 +99,9 @@ var isMiniPlayerVisible = false
|
||||
class PlayerFragment : Fragment() {
|
||||
|
||||
private val TAG = "PlayerFragment"
|
||||
private lateinit var binding: FragmentPlayerBinding
|
||||
private lateinit var playerBinding: ExoStyledPlayerControlViewBinding
|
||||
|
||||
private var videoId: String? = null
|
||||
private var playlistId: String? = null
|
||||
private var sId: Int = 0
|
||||
@ -114,14 +114,11 @@ class PlayerFragment : Fragment() {
|
||||
|
||||
private var isSubscribed: Boolean = false
|
||||
|
||||
private lateinit var relatedRecView: RecyclerView
|
||||
private lateinit var commentsRecView: RecyclerView
|
||||
private var commentsAdapter: CommentsAdapter? = null
|
||||
private var commentsLoaded: Boolean? = false
|
||||
private var nextPage: String? = null
|
||||
private var isLoading = true
|
||||
private lateinit var exoPlayerView: StyledPlayerView
|
||||
private lateinit var motionLayout: MotionLayout
|
||||
private lateinit var exoPlayer: ExoPlayer
|
||||
private lateinit var segmentData: Segments
|
||||
private var relatedStreamsEnabled = true
|
||||
@ -133,8 +130,6 @@ class PlayerFragment : Fragment() {
|
||||
|
||||
private var isPlayerLocked: Boolean = false
|
||||
|
||||
private lateinit var relDownloadVideo: LinearLayout
|
||||
|
||||
private lateinit var mediaSession: MediaSessionCompat
|
||||
private lateinit var mediaSessionConnector: MediaSessionConnector
|
||||
private lateinit var playerNotification: PlayerNotificationManager
|
||||
@ -156,9 +151,11 @@ class PlayerFragment : Fragment() {
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
): View {
|
||||
binding = FragmentPlayerBinding.inflate(layoutInflater, container, false)
|
||||
playerBinding = binding.player.binding
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_player, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
@ -172,17 +169,14 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun initializeTransitionLayout(view: View) {
|
||||
val playerDescription = view.findViewById<TextView>(R.id.player_description)
|
||||
videoId = videoId!!.replace("/watch?v=", "")
|
||||
relDownloadVideo = view.findViewById(R.id.relPlayer_download)
|
||||
val mainActivity = activity as MainActivity
|
||||
mainActivity.findViewById<FrameLayout>(R.id.container).visibility = View.VISIBLE
|
||||
val playerMotionLayout = view.findViewById<MotionLayout>(R.id.playerMotionLayout)
|
||||
motionLayout = playerMotionLayout
|
||||
exoPlayerView = view.findViewById(R.id.player)
|
||||
|
||||
view.findViewById<TextView>(R.id.player_description).text = videoId
|
||||
playerMotionLayout.addTransitionListener(object : MotionLayout.TransitionListener {
|
||||
val mainActivity = activity as MainActivity
|
||||
mainActivity.binding.container.visibility = View.VISIBLE
|
||||
|
||||
exoPlayerView = binding.player
|
||||
|
||||
binding.playerMotionLayout.addTransitionListener(object : MotionLayout.TransitionListener {
|
||||
override fun onTransitionStarted(
|
||||
motionLayout: MotionLayout?,
|
||||
startId: Int,
|
||||
@ -198,7 +192,7 @@ class PlayerFragment : Fragment() {
|
||||
) {
|
||||
val mainActivity = activity as MainActivity
|
||||
val mainMotionLayout =
|
||||
mainActivity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||
mainActivity.binding.mainMotionLayout
|
||||
mainMotionLayout.progress = abs(progress)
|
||||
exoPlayerView.hideController()
|
||||
eId = endId
|
||||
@ -209,7 +203,7 @@ class PlayerFragment : Fragment() {
|
||||
println(currentId)
|
||||
val mainActivity = activity as MainActivity
|
||||
val mainMotionLayout =
|
||||
mainActivity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||
mainActivity.binding.mainMotionLayout
|
||||
if (currentId == eId) {
|
||||
isMiniPlayerVisible = true
|
||||
exoPlayerView.useController = false
|
||||
@ -222,7 +216,7 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
|
||||
override fun onTransitionTrigger(
|
||||
motionLayout: MotionLayout?,
|
||||
MotionLayout: MotionLayout?,
|
||||
triggerId: Int,
|
||||
positive: Boolean,
|
||||
progress: Float
|
||||
@ -230,93 +224,65 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
})
|
||||
|
||||
playerMotionLayout.progress = 1.toFloat()
|
||||
playerMotionLayout.transitionToStart()
|
||||
binding.playerMotionLayout.progress = 1.toFloat()
|
||||
binding.playerMotionLayout.transitionToStart()
|
||||
|
||||
view.findViewById<ImageView>(R.id.close_imageView).setOnClickListener {
|
||||
binding.closeImageView.setOnClickListener {
|
||||
isMiniPlayerVisible = false
|
||||
motionLayout.transitionToEnd()
|
||||
binding.playerMotionLayout.transitionToEnd()
|
||||
val mainActivity = activity as MainActivity
|
||||
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
mainActivity.supportFragmentManager.beginTransaction()
|
||||
.remove(this)
|
||||
.commit()
|
||||
}
|
||||
view.findViewById<ImageButton>(R.id.close_imageButton).setOnClickListener {
|
||||
playerBinding.closeImageButton.setOnClickListener {
|
||||
isMiniPlayerVisible = false
|
||||
motionLayout.transitionToEnd()
|
||||
binding.playerMotionLayout.transitionToEnd()
|
||||
val mainActivity = activity as MainActivity
|
||||
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
mainActivity.supportFragmentManager.beginTransaction()
|
||||
.remove(this)
|
||||
.commit()
|
||||
}
|
||||
val playImageView = view.findViewById<ImageView>(R.id.play_imageView)
|
||||
playImageView.setOnClickListener {
|
||||
binding.playImageView.setOnClickListener {
|
||||
paused = if (paused) {
|
||||
playImageView.setImageResource(R.drawable.ic_pause)
|
||||
binding.playImageView.setImageResource(R.drawable.ic_pause)
|
||||
exoPlayer.play()
|
||||
false
|
||||
} else {
|
||||
playImageView.setImageResource(R.drawable.ic_play)
|
||||
binding.playImageView.setImageResource(R.drawable.ic_play)
|
||||
exoPlayer.pause()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// video description and chapters toggle
|
||||
val descLinLayout = view.findViewById<LinearLayout>(R.id.desc_linLayout)
|
||||
view.findViewById<RelativeLayout>(R.id.player_title_layout).setOnClickListener {
|
||||
val arrowImageView = view.findViewById<ImageView>(R.id.player_description_arrow)
|
||||
arrowImageView.animate().rotationBy(180F).setDuration(250).start()
|
||||
descLinLayout.visibility = if (descLinLayout.isVisible) View.GONE else View.VISIBLE
|
||||
binding.playerTitleLayout.setOnClickListener {
|
||||
binding.playerDescriptionArrow.animate().rotationBy(180F).setDuration(250).start()
|
||||
binding.descLinLayout.visibility =
|
||||
if (binding.descLinLayout.isVisible) View.GONE else View.VISIBLE
|
||||
}
|
||||
|
||||
view.findViewById<MaterialCardView>(R.id.comments_toggle)
|
||||
.setOnClickListener {
|
||||
toggleComments()
|
||||
}
|
||||
|
||||
val fullScreenButton = view.findViewById<ImageButton>(R.id.fullscreen)
|
||||
val exoTitle = view.findViewById<TextView>(R.id.exo_title)
|
||||
val mainContainer = view.findViewById<ConstraintLayout>(R.id.main_container)
|
||||
val linLayout = view.findViewById<LinearLayout>(R.id.linLayout)
|
||||
binding.commentsToggle.setOnClickListener {
|
||||
toggleComments()
|
||||
}
|
||||
|
||||
// FullScreen button trigger
|
||||
fullScreenButton.setOnClickListener {
|
||||
playerBinding.fullscreen.setOnClickListener {
|
||||
// hide player controller
|
||||
exoPlayerView.hideController()
|
||||
if (!isFullScreen) {
|
||||
with(motionLayout) {
|
||||
getConstraintSet(R.id.start).constrainHeight(R.id.player, -1)
|
||||
enableTransition(R.id.yt_transition, false)
|
||||
}
|
||||
|
||||
mainContainer.isClickable = true
|
||||
linLayout.visibility = View.GONE
|
||||
fullScreenButton.setImageResource(R.drawable.ic_fullscreen_exit)
|
||||
exoTitle.visibility = View.VISIBLE
|
||||
|
||||
val mainActivity = activity as MainActivity
|
||||
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|
||||
// go to fullscreen mode
|
||||
setFullscreen()
|
||||
} else {
|
||||
with(motionLayout) {
|
||||
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
|
||||
enableTransition(R.id.yt_transition, true)
|
||||
}
|
||||
|
||||
mainContainer.isClickable = false
|
||||
linLayout.visibility = View.VISIBLE
|
||||
fullScreenButton.setImageResource(R.drawable.ic_fullscreen)
|
||||
exoTitle.visibility = View.INVISIBLE
|
||||
|
||||
val mainActivity = activity as MainActivity
|
||||
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
// exit fullscreen mode
|
||||
unsetFullscreen()
|
||||
}
|
||||
isFullScreen = !isFullScreen
|
||||
}
|
||||
|
||||
// switching between original aspect ratio (black bars) and zoomed to fill device screen
|
||||
view.findViewById<ImageButton>(R.id.aspect_ratio_button).setOnClickListener {
|
||||
playerBinding.aspectRatioButton.setOnClickListener {
|
||||
if (isZoomed) {
|
||||
exoPlayerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||
isZoomed = false
|
||||
@ -327,13 +293,12 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
|
||||
// lock and unlock the player
|
||||
val lockPlayerButton = view.findViewById<ImageButton>(R.id.lock_player)
|
||||
lockPlayerButton.setOnClickListener {
|
||||
playerBinding.lockPlayer.setOnClickListener {
|
||||
// change the locked/unlocked icon
|
||||
if (!isPlayerLocked) {
|
||||
lockPlayerButton.setImageResource(R.drawable.ic_locked)
|
||||
playerBinding.lockPlayer.setImageResource(R.drawable.ic_locked)
|
||||
} else {
|
||||
lockPlayerButton.setImageResource(R.drawable.ic_unlocked)
|
||||
playerBinding.lockPlayer.setImageResource(R.drawable.ic_unlocked)
|
||||
}
|
||||
|
||||
// show/hide all the controls
|
||||
@ -343,32 +308,88 @@ class PlayerFragment : Fragment() {
|
||||
isPlayerLocked = !isPlayerLocked
|
||||
}
|
||||
|
||||
val scrollView = view.findViewById<ScrollView>(R.id.player_scrollView)
|
||||
scrollView.viewTreeObserver
|
||||
binding.playerScrollView.viewTreeObserver
|
||||
.addOnScrollChangedListener {
|
||||
if (scrollView.getChildAt(0).bottom
|
||||
== (scrollView.height + scrollView.scrollY) &&
|
||||
if (binding.playerScrollView.getChildAt(0).bottom
|
||||
== (binding.playerScrollView.height + binding.playerScrollView.scrollY) &&
|
||||
nextPage != null
|
||||
) {
|
||||
fetchNextComments()
|
||||
}
|
||||
}
|
||||
|
||||
commentsRecView = view.findViewById(R.id.comments_recView)
|
||||
commentsRecView.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.commentsRecView.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.commentsRecView.setItemViewCacheSize(20)
|
||||
|
||||
commentsRecView.setItemViewCacheSize(20)
|
||||
|
||||
relatedRecView = view.findViewById(R.id.player_recView)
|
||||
relatedRecView.layoutManager =
|
||||
binding.relatedRecView.layoutManager =
|
||||
GridLayoutManager(view.context, resources.getInteger(R.integer.grid_items))
|
||||
}
|
||||
|
||||
private fun setFullscreen() {
|
||||
with(binding.playerMotionLayout) {
|
||||
getConstraintSet(R.id.start).constrainHeight(R.id.player, -1)
|
||||
enableTransition(R.id.yt_transition, false)
|
||||
}
|
||||
|
||||
binding.mainContainer.isClickable = true
|
||||
binding.linLayout.visibility = View.GONE
|
||||
playerBinding.fullscreen.setImageResource(R.drawable.ic_fullscreen_exit)
|
||||
playerBinding.exoTitle.visibility = View.VISIBLE
|
||||
|
||||
val mainActivity = activity as MainActivity
|
||||
val fullscreenOrientationPref = PreferenceHelper
|
||||
.getString(requireContext(), "fullscreen_orientation", "ratio")
|
||||
|
||||
val scaleFactor = 1.3F
|
||||
playerBinding.exoPlayPause.scaleX = scaleFactor
|
||||
playerBinding.exoPlayPause.scaleY = scaleFactor
|
||||
|
||||
val orientation = when (fullscreenOrientationPref) {
|
||||
"ratio" -> {
|
||||
val videoSize = exoPlayer.videoSize
|
||||
// probably a youtube shorts video
|
||||
Log.e(TAG, videoSize.height.toString() + " " + videoSize.width.toString())
|
||||
if (videoSize.height > videoSize.width) ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
// a video with normal aspect ratio
|
||||
else ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|
||||
}
|
||||
"auto" -> ActivityInfo.SCREEN_ORIENTATION_USER
|
||||
"landscape" -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|
||||
"portrait" -> ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
else -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|
||||
}
|
||||
mainActivity.requestedOrientation = orientation
|
||||
|
||||
isFullScreen = true
|
||||
}
|
||||
|
||||
private fun unsetFullscreen() {
|
||||
// leave fullscreen mode
|
||||
with(binding.playerMotionLayout) {
|
||||
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
|
||||
enableTransition(R.id.yt_transition, true)
|
||||
}
|
||||
|
||||
binding.mainContainer.isClickable = false
|
||||
binding.linLayout.visibility = View.VISIBLE
|
||||
playerBinding.fullscreen.setImageResource(R.drawable.ic_fullscreen)
|
||||
playerBinding.exoTitle.visibility = View.INVISIBLE
|
||||
|
||||
val scaleFactor = 1F
|
||||
playerBinding.exoPlayPause.scaleX = scaleFactor
|
||||
playerBinding.exoPlayPause.scaleY = scaleFactor
|
||||
|
||||
val mainActivity = activity as MainActivity
|
||||
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
|
||||
isFullScreen = false
|
||||
}
|
||||
|
||||
private fun toggleComments() {
|
||||
commentsRecView.visibility =
|
||||
if (commentsRecView.isVisible) View.GONE else View.VISIBLE
|
||||
relatedRecView.visibility =
|
||||
if (relatedRecView.isVisible) View.GONE else View.VISIBLE
|
||||
binding.commentsRecView.visibility =
|
||||
if (binding.commentsRecView.isVisible) View.GONE else View.VISIBLE
|
||||
binding.relatedRecView.visibility =
|
||||
if (binding.relatedRecView.isVisible) View.GONE else View.VISIBLE
|
||||
if (!commentsLoaded!!) fetchComments()
|
||||
}
|
||||
|
||||
@ -386,10 +407,7 @@ class PlayerFragment : Fragment() {
|
||||
|
||||
// pause player if screen off and setting enabled
|
||||
if (
|
||||
this::exoPlayer.isInitialized &&
|
||||
exoPlayer != null &&
|
||||
!isScreenOn &&
|
||||
pausePlayerOnScreenOffEnabled
|
||||
this::exoPlayer.isInitialized && !isScreenOn && pausePlayerOnScreenOffEnabled
|
||||
) {
|
||||
exoPlayer.pause()
|
||||
}
|
||||
@ -399,6 +417,7 @@ class PlayerFragment : Fragment() {
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
try {
|
||||
saveWatchPosition()
|
||||
mediaSession.isActive = false
|
||||
mediaSession.release()
|
||||
mediaSessionConnector.setPlayer(null)
|
||||
@ -412,6 +431,25 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
// save the watch position if video isn't finished and option enabled
|
||||
private fun saveWatchPosition() {
|
||||
val watchPositionsEnabled = PreferenceHelper.getBoolean(
|
||||
requireContext(),
|
||||
"watch_positions_toggle",
|
||||
true
|
||||
)
|
||||
if (watchPositionsEnabled && exoPlayer.currentPosition != exoPlayer.duration) {
|
||||
PreferenceHelper.saveWatchPosition(
|
||||
requireContext(),
|
||||
videoId!!,
|
||||
exoPlayer.currentPosition
|
||||
)
|
||||
} else if (watchPositionsEnabled) {
|
||||
// delete watch position if video has ended
|
||||
PreferenceHelper.removeWatchPosition(requireContext(), videoId!!)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkForSegments() {
|
||||
if (!exoPlayer.isPlaying || !sponsorBlockPrefs.sponsorBlockEnabled) return
|
||||
|
||||
@ -462,17 +500,12 @@ class PlayerFragment : Fragment() {
|
||||
relatedStreams = response.relatedStreams
|
||||
|
||||
runOnUiThread {
|
||||
if (response.chapters != null) initializeChapters(response.chapters)
|
||||
// set media sources for the player
|
||||
setResolutionAndSubtitles(view, response)
|
||||
exoPlayer.prepare()
|
||||
prepareExoPlayerView()
|
||||
initializePlayerView(view, response)
|
||||
// support for time stamped links
|
||||
if (arguments?.getLong("timeStamp") != null) {
|
||||
val position = arguments?.getLong("timeStamp")!! * 1000
|
||||
exoPlayer.seekTo(position)
|
||||
}
|
||||
seekToWatchPosition()
|
||||
exoPlayer.prepare()
|
||||
exoPlayer.play()
|
||||
exoPlayerView.useController = true
|
||||
initializePlayerNotification(requireContext())
|
||||
@ -481,12 +514,32 @@ class PlayerFragment : Fragment() {
|
||||
if (!relatedStreamsEnabled) toggleComments()
|
||||
// prepare for autoplay
|
||||
initAutoPlay()
|
||||
val watchHistoryEnabled =
|
||||
PreferenceHelper.getBoolean(requireContext(), "Watch_history_toggle", true)
|
||||
if (watchHistoryEnabled) {
|
||||
PreferenceHelper.addToWatchHistory(requireContext(), videoId!!, response)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
run()
|
||||
}
|
||||
|
||||
private fun seekToWatchPosition() {
|
||||
// seek to saved watch position if available
|
||||
val watchPositions = PreferenceHelper.getWatchPositions(requireContext())
|
||||
var position: Long? = null
|
||||
watchPositions.forEach {
|
||||
if (it.videoId == videoId) position = it.position
|
||||
}
|
||||
// support for time stamped links
|
||||
val timeStamp: Long? = arguments?.getLong("timeStamp")
|
||||
if (timeStamp != null && timeStamp != 0L) {
|
||||
position = timeStamp * 1000
|
||||
}
|
||||
if (position != null) exoPlayer.seekTo(position!!)
|
||||
}
|
||||
|
||||
// the function is working recursively
|
||||
private fun initAutoPlay() {
|
||||
// save related streams for autoplay
|
||||
@ -535,7 +588,7 @@ class PlayerFragment : Fragment() {
|
||||
// if it's not a playlist then use the next related video
|
||||
} else if (relatedStreams != null && relatedStreams!!.isNotEmpty()) {
|
||||
// save next video from related streams for autoplay
|
||||
nextStreamId = relatedStreams!![0].url!!.replace("/watch?v=", "")!!
|
||||
nextStreamId = relatedStreams!![0].url!!.replace("/watch?v=", "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -638,20 +691,25 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun initializePlayerView(view: View, response: Streams) {
|
||||
view.findViewById<TextView>(R.id.player_views_info).text =
|
||||
binding.playerViewsInfo.text =
|
||||
context?.getString(R.string.views, response.views.formatShort()) +
|
||||
" • " + response.uploadDate
|
||||
view.findViewById<TextView>(R.id.textLike).text = response.likes.formatShort()
|
||||
view.findViewById<TextView>(R.id.textDislike).text = response.dislikes.formatShort()
|
||||
val channelImage = view.findViewById<ImageView>(R.id.player_channelImage)
|
||||
Picasso.get().load(response.uploaderAvatar).into(channelImage)
|
||||
view.findViewById<TextView>(R.id.player_channelName).text = response.uploader
|
||||
binding.textLike.text = response.likes.formatShort()
|
||||
binding.textDislike.text = response.dislikes.formatShort()
|
||||
Picasso.get().load(response.uploaderAvatar).into(binding.playerChannelImage)
|
||||
binding.playerChannelName.text = response.uploader
|
||||
|
||||
view.findViewById<TextView>(R.id.title_textView).text = response.title
|
||||
view.findViewById<TextView>(R.id.player_title).text = response.title
|
||||
view.findViewById<TextView>(R.id.player_description).text = response.description
|
||||
binding.titleTextView.text = response.title
|
||||
binding.playerTitle.text = response.title
|
||||
binding.playerDescription.text = response.description
|
||||
|
||||
view.findViewById<TextView>(R.id.exo_title).text = response.title
|
||||
playerBinding.exoTitle.text = response.title
|
||||
|
||||
enableSeekbarPreview()
|
||||
enableDoubleTapToSeek()
|
||||
|
||||
// init the chapters recyclerview
|
||||
if (response.chapters != null) initializeChapters(response.chapters)
|
||||
|
||||
// Listener for play and pause icon change
|
||||
exoPlayer.addListener(object : Player.Listener {
|
||||
@ -664,6 +722,22 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onVideoSizeChanged(
|
||||
videoSize: VideoSize
|
||||
) {
|
||||
// Set new width/height of view
|
||||
// height or width must be cast to float as int/int will give 0
|
||||
|
||||
// Redraw the player container with the new layout height
|
||||
val params = binding.player.layoutParams
|
||||
params.height = videoSize.height / videoSize.width * params.width
|
||||
binding.player.layoutParams = params
|
||||
binding.player.requestLayout()
|
||||
(binding.mainContainer.layoutParams as ConstraintLayout.LayoutParams).apply {
|
||||
matchConstraintPercentHeight = (videoSize.height / videoSize.width).toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated(message = "Deprecated", level = DeprecationLevel.HIDDEN)
|
||||
override fun onPlayerStateChanged(
|
||||
playWhenReady: Boolean,
|
||||
@ -690,38 +764,34 @@ class PlayerFragment : Fragment() {
|
||||
if (playWhenReady && playbackState == Player.STATE_READY) {
|
||||
// media actually playing
|
||||
transitioning = false
|
||||
view.findViewById<ImageView>(R.id.play_imageView)
|
||||
.setImageResource(R.drawable.ic_pause)
|
||||
binding.playImageView.setImageResource(R.drawable.ic_pause)
|
||||
} else if (playWhenReady) {
|
||||
// might be idle (plays after prepare()),
|
||||
// buffering (plays when data available)
|
||||
// or ended (plays when seek away from end)
|
||||
view.findViewById<ImageView>(R.id.play_imageView)
|
||||
.setImageResource(R.drawable.ic_play)
|
||||
binding.playImageView.setImageResource(R.drawable.ic_play)
|
||||
} else {
|
||||
// player paused in any state
|
||||
view.findViewById<ImageView>(R.id.play_imageView)
|
||||
.setImageResource(R.drawable.ic_play)
|
||||
binding.playImageView.setImageResource(R.drawable.ic_play)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// share button
|
||||
view.findViewById<LinearLayout>(R.id.relPlayer_share).setOnClickListener {
|
||||
binding.relPlayerShare.setOnClickListener {
|
||||
val shareDialog = ShareDialog(videoId!!, false)
|
||||
shareDialog.show(childFragmentManager, "ShareDialog")
|
||||
}
|
||||
// check if livestream
|
||||
if (response.duration!! > 0) {
|
||||
// download clicked
|
||||
relDownloadVideo.setOnClickListener {
|
||||
binding.relPlayerDownload.setOnClickListener {
|
||||
if (!IS_DOWNLOAD_RUNNING) {
|
||||
val newFragment = DownloadDialog()
|
||||
val bundle = Bundle()
|
||||
bundle.putString("video_id", videoId)
|
||||
bundle.putParcelable("streams", response)
|
||||
newFragment.arguments = bundle
|
||||
newFragment.show(childFragmentManager, "Download")
|
||||
newFragment.show(childFragmentManager, "DownloadDialog")
|
||||
} else {
|
||||
Toast.makeText(context, R.string.dlisinprogress, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
@ -732,7 +802,7 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
|
||||
if (response.hls != null) {
|
||||
view.findViewById<LinearLayout>(R.id.relPlayer_vlc).setOnClickListener {
|
||||
binding.relPlayerVlc.setOnClickListener {
|
||||
// start an intent with video as mimetype using the hls stream
|
||||
val uri: Uri = Uri.parse(response.hls)
|
||||
val intent = Intent()
|
||||
@ -749,14 +819,14 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
if (relatedStreamsEnabled) {
|
||||
// only show related streams if enabled
|
||||
relatedRecView.adapter = TrendingAdapter(
|
||||
binding.relatedRecView.adapter = TrendingAdapter(
|
||||
response.relatedStreams!!,
|
||||
childFragmentManager
|
||||
)
|
||||
}
|
||||
// set video description
|
||||
val description = response.description!!
|
||||
view.findViewById<TextView>(R.id.player_description).text =
|
||||
binding.playerDescription.text =
|
||||
// detect whether the description is html formatted
|
||||
if (description.contains("<") && description.contains(">")) {
|
||||
if (SDK_INT >= Build.VERSION_CODES.N) {
|
||||
@ -769,19 +839,18 @@ class PlayerFragment : Fragment() {
|
||||
description
|
||||
}
|
||||
|
||||
view.findViewById<RelativeLayout>(R.id.player_channel).setOnClickListener {
|
||||
binding.playerChannel.setOnClickListener {
|
||||
val activity = view.context as MainActivity
|
||||
val bundle = bundleOf("channel_id" to response.uploaderUrl)
|
||||
activity.navController.navigate(R.id.channel, bundle)
|
||||
activity.findViewById<MotionLayout>(R.id.mainMotionLayout).transitionToEnd()
|
||||
view.findViewById<MotionLayout>(R.id.playerMotionLayout).transitionToEnd()
|
||||
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||
activity.binding.mainMotionLayout.transitionToEnd()
|
||||
binding.playerMotionLayout.transitionToEnd()
|
||||
}
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
if (token != "") {
|
||||
val channelId = response.uploaderUrl?.replace("/channel/", "")
|
||||
val subButton = view.findViewById<MaterialButton>(R.id.player_subscribe)
|
||||
isSubscribed(subButton, channelId!!)
|
||||
view.findViewById<LinearLayout>(R.id.save).setOnClickListener {
|
||||
isSubscribed(binding.playerSubscribe, channelId!!)
|
||||
binding.save.setOnClickListener {
|
||||
val newFragment = AddtoPlaylistDialog()
|
||||
val bundle = Bundle()
|
||||
bundle.putString("videoId", videoId)
|
||||
@ -791,14 +860,81 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeChapters(chapters: List<ChapterSegment>) {
|
||||
val chaptersRecView = view?.findViewById<RecyclerView>(R.id.chapters_recView)
|
||||
private fun enableDoubleTapToSeek() {
|
||||
val seekIncrement =
|
||||
PreferenceHelper.getString(requireContext(), "seek_increment", "5")?.toLong()!! * 1000
|
||||
|
||||
// enable rewind button
|
||||
binding.rewindFL.setOnClickListener(
|
||||
DoubleClickListener(
|
||||
callback = object : DoubleClickListener.Callback {
|
||||
override fun doubleClicked() {
|
||||
binding.rewindBTN.visibility = View.VISIBLE
|
||||
exoPlayer.seekTo(exoPlayer.currentPosition - seekIncrement)
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
binding.rewindBTN.visibility = View.INVISIBLE
|
||||
}, 700)
|
||||
}
|
||||
|
||||
override fun singleClicked() {
|
||||
toggleController()
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
// enable fast forward button
|
||||
binding.forwardFL.setOnClickListener(
|
||||
DoubleClickListener(
|
||||
callback = object : DoubleClickListener.Callback {
|
||||
override fun doubleClicked() {
|
||||
binding.forwardBTN.visibility = View.VISIBLE
|
||||
exoPlayer.seekTo(exoPlayer.currentPosition + seekIncrement)
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
binding.forwardBTN.visibility = View.INVISIBLE
|
||||
}, 700)
|
||||
}
|
||||
|
||||
override fun singleClicked() {
|
||||
toggleController()
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// toggle the visibility of the player controller
|
||||
private fun toggleController() {
|
||||
if (exoPlayerView.isControllerFullyVisible) exoPlayerView.hideController()
|
||||
else exoPlayerView.showController()
|
||||
}
|
||||
|
||||
// enable seek bar preview
|
||||
private fun enableSeekbarPreview() {
|
||||
playerBinding.exoProgress.addListener(object : TimeBar.OnScrubListener {
|
||||
override fun onScrubStart(timeBar: TimeBar, position: Long) {
|
||||
exoPlayer.pause()
|
||||
}
|
||||
|
||||
override fun onScrubMove(timeBar: TimeBar, position: Long) {
|
||||
exoPlayer.seekTo(position)
|
||||
}
|
||||
|
||||
override fun onScrubStop(timeBar: TimeBar, position: Long, canceled: Boolean) {
|
||||
exoPlayer.play()
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
exoPlayerView.hideController()
|
||||
}, 200)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun initializeChapters(chapters: List<ChapterSegment>) {
|
||||
if (chapters.isNotEmpty()) {
|
||||
chaptersRecView?.layoutManager =
|
||||
binding.chaptersRecView.layoutManager =
|
||||
LinearLayoutManager(this.context, LinearLayoutManager.HORIZONTAL, false)
|
||||
chaptersRecView?.adapter = ChaptersAdapter(chapters, exoPlayer)
|
||||
chaptersRecView?.visibility = View.VISIBLE
|
||||
binding.chaptersRecView.adapter = ChaptersAdapter(chapters, exoPlayer)
|
||||
binding.chaptersRecView.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
@ -816,7 +952,7 @@ class PlayerFragment : Fragment() {
|
||||
val videoSource: MediaSource =
|
||||
DefaultMediaSourceFactory(dataSourceFactory)
|
||||
.createMediaSource(videoItem)
|
||||
var audioSource: MediaSource =
|
||||
val audioSource: MediaSource =
|
||||
ProgressiveMediaSource.Factory(dataSourceFactory)
|
||||
.createMediaSource(fromUri(audioUrl))
|
||||
val mergeSource: MediaSource =
|
||||
@ -829,15 +965,12 @@ class PlayerFragment : Fragment() {
|
||||
PreferenceHelper.getString(requireContext(), "player_video_format", "WEBM")
|
||||
val defres = PreferenceHelper.getString(requireContext(), "default_res", "")!!
|
||||
|
||||
val qualityText = view.findViewById<TextView>(R.id.quality_text)
|
||||
val qualitySelect = view.findViewById<LinearLayout>(R.id.quality_linLayout)
|
||||
|
||||
var videosNameArray: Array<CharSequence> = arrayOf()
|
||||
var videosUrlArray: Array<Uri> = arrayOf()
|
||||
|
||||
// append hls to list if available
|
||||
if (response.hls != null) {
|
||||
videosNameArray += "HLS"
|
||||
videosNameArray += getString(R.string.hls)
|
||||
videosUrlArray += response.hls.toUri()
|
||||
}
|
||||
|
||||
@ -871,7 +1004,7 @@ class PlayerFragment : Fragment() {
|
||||
val videoUri = videosUrlArray[index]
|
||||
val audioUrl = getMostBitRate(response.audioStreams!!)
|
||||
setMediaSource(subtitle, videoUri, audioUrl)
|
||||
qualityText.text = videosNameArray[index]
|
||||
playerBinding.qualityText.text = videosNameArray[index]
|
||||
return@lit
|
||||
} else if (response.hls != null) {
|
||||
val mediaItem: MediaItem = MediaItem.Builder()
|
||||
@ -902,11 +1035,11 @@ class PlayerFragment : Fragment() {
|
||||
val videoUri = videosUrlArray[0]
|
||||
val audioUrl = getMostBitRate(response.audioStreams!!)
|
||||
setMediaSource(subtitle, videoUri, audioUrl)
|
||||
qualityText.text = videosNameArray[0]
|
||||
playerBinding.qualityText.text = videosNameArray[0]
|
||||
}
|
||||
}
|
||||
|
||||
qualitySelect.setOnClickListener {
|
||||
playerBinding.qualityLinLayout.setOnClickListener {
|
||||
// Dialog for quality selection
|
||||
val builder: MaterialAlertDialogBuilder? = activity?.let {
|
||||
MaterialAlertDialogBuilder(it)
|
||||
@ -918,7 +1051,7 @@ class PlayerFragment : Fragment() {
|
||||
) { _, which ->
|
||||
whichQuality = which
|
||||
if (
|
||||
videosNameArray[which] == "HLS" ||
|
||||
videosNameArray[which] == getString(R.string.hls) ||
|
||||
videosNameArray[which] == "LBRY HLS"
|
||||
) {
|
||||
// no need to merge sources if using hls
|
||||
@ -933,7 +1066,7 @@ class PlayerFragment : Fragment() {
|
||||
setMediaSource(subtitle, videoUri, audioUrl)
|
||||
}
|
||||
exoPlayer.seekTo(lastPosition)
|
||||
qualityText.text = videosNameArray[which]
|
||||
playerBinding.qualityText.text = videosNameArray[which]
|
||||
}
|
||||
val dialog = builder.create()
|
||||
dialog.show()
|
||||
@ -969,7 +1102,7 @@ class PlayerFragment : Fragment() {
|
||||
// cache the last three minutes
|
||||
.setBackBuffer(1000 * 60 * 3, true)
|
||||
.setBufferDurationsMs(
|
||||
DefaultLoadControl.DEFAULT_MIN_BUFFER_MS,
|
||||
1000 * 10, // exo default is 50s
|
||||
bufferingGoal,
|
||||
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
|
||||
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS
|
||||
@ -1006,21 +1139,19 @@ class PlayerFragment : Fragment() {
|
||||
|
||||
playerNotification.apply {
|
||||
setPlayer(exoPlayer)
|
||||
setUseNextAction(false)
|
||||
setUsePreviousAction(false)
|
||||
setUseStopAction(true)
|
||||
setMediaSessionToken(mediaSession.sessionToken)
|
||||
}
|
||||
}
|
||||
|
||||
private fun lockPlayer(isLocked: Boolean) {
|
||||
val visibility = if (isLocked) View.VISIBLE else View.INVISIBLE
|
||||
exoPlayerView.findViewById<LinearLayout>(R.id.exo_top_bar_right).visibility = visibility
|
||||
exoPlayerView.findViewById<ImageButton>(R.id.exo_play_pause).visibility = visibility
|
||||
exoPlayerView.findViewById<Button>(R.id.exo_ffwd_with_amount).visibility = visibility
|
||||
exoPlayerView.findViewById<Button>(R.id.exo_rew_with_amount).visibility = visibility
|
||||
exoPlayerView.findViewById<FrameLayout>(R.id.exo_bottom_bar).visibility = visibility
|
||||
exoPlayerView.findViewById<TextView>(R.id.exo_title).visibility =
|
||||
if (isLocked && isFullScreen) View.VISIBLE else View.INVISIBLE
|
||||
val visibility = if (isLocked) View.VISIBLE else View.GONE
|
||||
playerBinding.exoTopBarRight.visibility = visibility
|
||||
playerBinding.exoPlayPause.visibility = visibility
|
||||
playerBinding.exoBottomBar.visibility = visibility
|
||||
playerBinding.closeImageButton.visibility = visibility
|
||||
playerBinding.exoTitle.visibility = visibility
|
||||
}
|
||||
|
||||
private fun isSubscribed(button: MaterialButton, channel_id: String) {
|
||||
@ -1029,7 +1160,7 @@ class PlayerFragment : Fragment() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
RetrofitInstance.api.isSubscribed(
|
||||
RetrofitInstance.authApi.isSubscribed(
|
||||
channel_id,
|
||||
token
|
||||
)
|
||||
@ -1069,7 +1200,7 @@ class PlayerFragment : Fragment() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
RetrofitInstance.api.subscribe(
|
||||
RetrofitInstance.authApi.subscribe(
|
||||
token,
|
||||
Subscribe(channel_id)
|
||||
)
|
||||
@ -1092,7 +1223,7 @@ class PlayerFragment : Fragment() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
RetrofitInstance.api.unsubscribe(
|
||||
RetrofitInstance.authApi.unsubscribe(
|
||||
token,
|
||||
Subscribe(channel_id)
|
||||
)
|
||||
@ -1143,7 +1274,7 @@ class PlayerFragment : Fragment() {
|
||||
return@launchWhenCreated
|
||||
}
|
||||
commentsAdapter = CommentsAdapter(videoId!!, commentsResponse.comments)
|
||||
commentsRecView.adapter = commentsAdapter
|
||||
binding.commentsRecView.adapter = commentsAdapter
|
||||
nextPage = commentsResponse.nextpage
|
||||
commentsLoaded = true
|
||||
isLoading = false
|
||||
@ -1176,37 +1307,35 @@ class PlayerFragment : Fragment() {
|
||||
if (isInPictureInPictureMode) {
|
||||
exoPlayerView.hideController()
|
||||
exoPlayerView.useController = false
|
||||
with(motionLayout) {
|
||||
binding.linLayout.visibility = View.GONE
|
||||
|
||||
with(binding.playerMotionLayout) {
|
||||
getConstraintSet(R.id.start).constrainHeight(R.id.player, -1)
|
||||
enableTransition(R.id.yt_transition, false)
|
||||
}
|
||||
view?.findViewById<ConstraintLayout>(R.id.main_container)?.isClickable = true
|
||||
view?.findViewById<LinearLayout>(R.id.top_bar)?.visibility = View.GONE
|
||||
binding.mainContainer.isClickable = true
|
||||
|
||||
val mainActivity = activity as MainActivity
|
||||
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
isFullScreen = false
|
||||
} else {
|
||||
with(motionLayout) {
|
||||
with(binding.playerMotionLayout) {
|
||||
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
|
||||
enableTransition(R.id.yt_transition, true)
|
||||
}
|
||||
exoPlayerView.showController()
|
||||
|
||||
exoPlayerView.useController = true
|
||||
view?.findViewById<ConstraintLayout>(R.id.main_container)?.isClickable = false
|
||||
view?.findViewById<LinearLayout>(R.id.top_bar)?.visibility = View.VISIBLE
|
||||
binding.linLayout.visibility = View.VISIBLE
|
||||
binding.mainContainer.isClickable = false
|
||||
}
|
||||
}
|
||||
|
||||
fun onUserLeaveHint() {
|
||||
val bounds = Rect()
|
||||
val scrollView = view?.findViewById<ScrollView>(R.id.player_scrollView)
|
||||
scrollView?.getHitRect(bounds)
|
||||
binding.playerScrollView.getHitRect(bounds)
|
||||
|
||||
if (SDK_INT >= Build.VERSION_CODES.O &&
|
||||
exoPlayer.isPlaying && (
|
||||
scrollView?.getLocalVisibleRect(bounds) == true ||
|
||||
isFullScreen
|
||||
)
|
||||
exoPlayer.isPlaying && (binding.playerScrollView.getLocalVisibleRect(bounds) || isFullScreen)
|
||||
) {
|
||||
activity?.enterPictureInPictureMode(updatePipParams())
|
||||
}
|
||||
|
@ -5,27 +5,27 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.ScrollView
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.adapters.PlaylistAdapter
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.databinding.FragmentPlaylistBinding
|
||||
import com.github.libretube.dialogs.PlaylistOptionsDialog
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class PlaylistFragment : Fragment() {
|
||||
private val TAG = "PlaylistFragment"
|
||||
private lateinit var binding: FragmentPlaylistBinding
|
||||
|
||||
private var playlistId: String? = null
|
||||
var nextPage: String? = null
|
||||
var playlistAdapter: PlaylistAdapter? = null
|
||||
var isLoading = true
|
||||
private var playlistAdapter: PlaylistAdapter? = null
|
||||
private var isLoading = true
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
arguments?.let {
|
||||
@ -37,24 +37,22 @@ class PlaylistFragment : Fragment() {
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_playlist, container, false)
|
||||
): View {
|
||||
binding = FragmentPlaylistBinding.inflate(layoutInflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
playlistId = playlistId!!.replace("/playlist?list=", "")
|
||||
val recyclerView = view.findViewById<RecyclerView>(R.id.playlist_recView)
|
||||
recyclerView.layoutManager = LinearLayoutManager(context)
|
||||
binding.playlistRecView.layoutManager = LinearLayoutManager(context)
|
||||
|
||||
val progressBar = view.findViewById<ProgressBar>(R.id.playlist_progress)
|
||||
progressBar.visibility = View.VISIBLE
|
||||
fetchPlaylist(view)
|
||||
binding.playlistProgress.visibility = View.VISIBLE
|
||||
fetchPlaylist()
|
||||
}
|
||||
|
||||
private fun fetchPlaylist(view: View) {
|
||||
private fun fetchPlaylist() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
@ -70,16 +68,24 @@ class PlaylistFragment : Fragment() {
|
||||
nextPage = response.nextpage
|
||||
isLoading = false
|
||||
runOnUiThread {
|
||||
view.findViewById<ProgressBar>(R.id.playlist_progress).visibility = View.GONE
|
||||
view.findViewById<TextView>(R.id.playlist_name).text = response.name
|
||||
view.findViewById<TextView>(R.id.playlist_uploader).text = response.uploader
|
||||
view.findViewById<TextView>(R.id.playlist_totVideos).text =
|
||||
binding.playlistProgress.visibility = View.GONE
|
||||
binding.playlistName.text = response.name
|
||||
binding.playlistUploader.text = response.uploader
|
||||
binding.playlistTotVideos.text =
|
||||
getString(R.string.videoCount, response.videos.toString())
|
||||
|
||||
val user = PreferenceHelper.getUsername(requireContext())
|
||||
var isOwner = false
|
||||
if (response.uploaderUrl == null && response.uploader.equals(user, true)) {
|
||||
isOwner = true
|
||||
// check whether the user owns the playlist
|
||||
val isOwner = response.uploaderUrl == null &&
|
||||
response.uploader.equals(user, true)
|
||||
|
||||
// show playlist options
|
||||
binding.optionsMenu.setOnClickListener {
|
||||
val optionsDialog =
|
||||
PlaylistOptionsDialog(playlistId!!, isOwner, requireContext())
|
||||
optionsDialog.show(childFragmentManager, "PlaylistOptionsDialog")
|
||||
}
|
||||
|
||||
playlistAdapter = PlaylistAdapter(
|
||||
response.relatedStreams!!.toMutableList(),
|
||||
playlistId!!,
|
||||
@ -87,12 +93,11 @@ class PlaylistFragment : Fragment() {
|
||||
requireActivity(),
|
||||
childFragmentManager
|
||||
)
|
||||
view.findViewById<RecyclerView>(R.id.playlist_recView).adapter = playlistAdapter
|
||||
val scrollView = view.findViewById<ScrollView>(R.id.playlist_scrollview)
|
||||
scrollView.viewTreeObserver
|
||||
binding.playlistRecView.adapter = playlistAdapter
|
||||
binding.playlistScrollview.viewTreeObserver
|
||||
.addOnScrollChangedListener {
|
||||
if (scrollView.getChildAt(0).bottom
|
||||
== (scrollView.height + scrollView.scrollY)
|
||||
if (binding.playlistScrollview.getChildAt(0).bottom
|
||||
== (binding.playlistScrollview.height + binding.playlistScrollview.scrollY)
|
||||
) {
|
||||
// scroll view is at bottom
|
||||
if (nextPage != null && !isLoading) {
|
||||
|
@ -12,7 +12,6 @@ import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView.GONE
|
||||
import android.widget.TextView.OnEditorActionListener
|
||||
import android.widget.TextView.VISIBLE
|
||||
@ -20,28 +19,26 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.hideKeyboard
|
||||
import com.github.libretube.adapters.SearchAdapter
|
||||
import com.github.libretube.adapters.SearchHistoryAdapter
|
||||
import com.github.libretube.adapters.SearchSuggestionsAdapter
|
||||
import com.github.libretube.hideKeyboard
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.databinding.FragmentSearchBinding
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class SearchFragment : Fragment() {
|
||||
private val TAG = "SearchFragment"
|
||||
private lateinit var binding: FragmentSearchBinding
|
||||
|
||||
private var selectedFilter = 0
|
||||
private var apiSearchFilter = "all"
|
||||
private var nextPage: String? = null
|
||||
private lateinit var searchRecView: RecyclerView
|
||||
private lateinit var historyRecView: RecyclerView
|
||||
private lateinit var autoTextView: EditText
|
||||
|
||||
private var searchAdapter: SearchAdapter? = null
|
||||
private var isLoading: Boolean = true
|
||||
private var isFetchingSearch: Boolean = false
|
||||
@ -56,27 +53,21 @@ class SearchFragment : Fragment() {
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_search, container, false)
|
||||
): View {
|
||||
binding = FragmentSearchBinding.inflate(layoutInflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
searchRecView = view.findViewById(R.id.search_recycler)
|
||||
historyRecView = view.findViewById(R.id.history_recycler)
|
||||
autoTextView = view.findViewById(R.id.autoCompleteTextView)
|
||||
|
||||
val clearSearchButton = view.findViewById<ImageView>(R.id.clearSearch_imageView)
|
||||
val filterImageView = view.findViewById<ImageView>(R.id.filterMenu_imageView)
|
||||
|
||||
var tempSelectedItem = 0
|
||||
|
||||
clearSearchButton.setOnClickListener {
|
||||
autoTextView.text.clear()
|
||||
binding.clearSearchImageView.setOnClickListener {
|
||||
binding.autoCompleteTextView.text.clear()
|
||||
}
|
||||
|
||||
filterImageView.setOnClickListener {
|
||||
binding.filterMenuImageView.setOnClickListener {
|
||||
val filterOptions = arrayOf(
|
||||
getString(R.string.all),
|
||||
getString(R.string.videos),
|
||||
@ -108,7 +99,7 @@ class SearchFragment : Fragment() {
|
||||
7 -> "music_playlists"
|
||||
else -> "all"
|
||||
}
|
||||
fetchSearch(autoTextView.text.toString())
|
||||
fetchSearch(binding.autoCompleteTextView.text.toString())
|
||||
}
|
||||
.setNegativeButton(getString(R.string.cancel), null)
|
||||
.create()
|
||||
@ -116,16 +107,16 @@ class SearchFragment : Fragment() {
|
||||
}
|
||||
|
||||
// show search history
|
||||
historyRecView.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.historyRecycler.layoutManager = LinearLayoutManager(view.context)
|
||||
showHistory()
|
||||
|
||||
searchRecView.layoutManager = GridLayoutManager(view.context, 1)
|
||||
autoTextView.requestFocus()
|
||||
binding.searchRecycler.layoutManager = GridLayoutManager(view.context, 1)
|
||||
binding.autoCompleteTextView.requestFocus()
|
||||
val imm =
|
||||
requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.showSoftInput(autoTextView, InputMethodManager.SHOW_IMPLICIT)
|
||||
imm.showSoftInput(binding.autoCompleteTextView, InputMethodManager.SHOW_IMPLICIT)
|
||||
|
||||
autoTextView.addTextChangedListener(object : TextWatcher {
|
||||
binding.autoCompleteTextView.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(
|
||||
s: CharSequence?,
|
||||
start: Int,
|
||||
@ -136,18 +127,15 @@ class SearchFragment : Fragment() {
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
if (s!! != "") {
|
||||
searchRecView.adapter = null
|
||||
binding.searchRecycler.adapter = null
|
||||
|
||||
searchRecView.viewTreeObserver
|
||||
binding.searchRecycler.viewTreeObserver
|
||||
.addOnScrollChangedListener {
|
||||
if (!searchRecView.canScrollVertically(1)) {
|
||||
fetchNextSearchItems(autoTextView.text.toString())
|
||||
if (!binding.searchRecycler.canScrollVertically(1)) {
|
||||
fetchNextSearchItems(binding.autoCompleteTextView.text.toString())
|
||||
}
|
||||
}
|
||||
|
||||
GlobalScope.launch {
|
||||
fetchSuggestions(s.toString(), autoTextView)
|
||||
}
|
||||
fetchSuggestions(s.toString(), binding.autoCompleteTextView)
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,13 +145,13 @@ class SearchFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
})
|
||||
autoTextView.setOnEditorActionListener(
|
||||
binding.autoCompleteTextView.setOnEditorActionListener(
|
||||
OnEditorActionListener { _, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||
hideKeyboard()
|
||||
searchRecView.visibility = VISIBLE
|
||||
historyRecView.visibility = GONE
|
||||
fetchSearch(autoTextView.text.toString())
|
||||
binding.searchRecycler.visibility = VISIBLE
|
||||
binding.historyRecycler.visibility = GONE
|
||||
fetchSearch(binding.autoCompleteTextView.text.toString())
|
||||
return@OnEditorActionListener true
|
||||
}
|
||||
false
|
||||
@ -174,8 +162,8 @@ class SearchFragment : Fragment() {
|
||||
private fun fetchSuggestions(query: String, autoTextView: EditText) {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
searchRecView.visibility = GONE
|
||||
historyRecView.visibility = VISIBLE
|
||||
binding.searchRecycler.visibility = GONE
|
||||
binding.historyRecycler.visibility = VISIBLE
|
||||
val response = try {
|
||||
RetrofitInstance.api.getSuggestions(query)
|
||||
} catch (e: IOException) {
|
||||
@ -188,13 +176,16 @@ class SearchFragment : Fragment() {
|
||||
}
|
||||
val suggestionsAdapter =
|
||||
SearchSuggestionsAdapter(response, autoTextView, this@SearchFragment)
|
||||
historyRecView.adapter = suggestionsAdapter
|
||||
binding.historyRecycler.adapter = suggestionsAdapter
|
||||
}
|
||||
}
|
||||
if (!isFetchingSearch) run()
|
||||
}
|
||||
|
||||
fun fetchSearch(query: String) {
|
||||
runOnUiThread {
|
||||
binding.historyRecycler.visibility = GONE
|
||||
}
|
||||
lifecycleScope.launchWhenCreated {
|
||||
isFetchingSearch = true
|
||||
hideKeyboard()
|
||||
@ -211,10 +202,9 @@ class SearchFragment : Fragment() {
|
||||
nextPage = response.nextpage
|
||||
if (response.items!!.isNotEmpty()) {
|
||||
runOnUiThread {
|
||||
historyRecView.visibility = GONE
|
||||
searchRecView.visibility = VISIBLE
|
||||
binding.searchRecycler.visibility = VISIBLE
|
||||
searchAdapter = SearchAdapter(response.items, childFragmentManager)
|
||||
searchRecView.adapter = searchAdapter
|
||||
binding.searchRecycler.adapter = searchAdapter
|
||||
}
|
||||
}
|
||||
addToHistory(query)
|
||||
@ -265,12 +255,17 @@ class SearchFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun showHistory() {
|
||||
searchRecView.visibility = GONE
|
||||
binding.searchRecycler.visibility = GONE
|
||||
val historyList = PreferenceHelper.getHistory(requireContext())
|
||||
if (historyList.isNotEmpty()) {
|
||||
historyRecView.adapter =
|
||||
SearchHistoryAdapter(requireContext(), historyList, autoTextView, this)
|
||||
historyRecView.visibility = VISIBLE
|
||||
binding.historyRecycler.adapter =
|
||||
SearchHistoryAdapter(
|
||||
requireContext(),
|
||||
historyList,
|
||||
binding.autoCompleteTextView,
|
||||
this
|
||||
)
|
||||
binding.historyRecycler.visibility = VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,11 +5,7 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.ScrollView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
@ -17,21 +13,23 @@ import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.adapters.SubscriptionAdapter
|
||||
import com.github.libretube.adapters.SubscriptionChannelAdapter
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.databinding.FragmentSubscriptionsBinding
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class Subscriptions : Fragment() {
|
||||
class SubscriptionsFragment : Fragment() {
|
||||
val TAG = "SubFragment"
|
||||
private lateinit var binding: FragmentSubscriptionsBinding
|
||||
|
||||
lateinit var token: String
|
||||
var isLoaded = false
|
||||
private var isLoaded = false
|
||||
private var subscriptionAdapter: SubscriptionAdapter? = null
|
||||
private var refreshLayout: SwipeRefreshLayout? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
arguments?.let {
|
||||
@ -42,88 +40,76 @@ class Subscriptions : Fragment() {
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_subscriptions, container, false)
|
||||
): View {
|
||||
binding = FragmentSubscriptionsBinding.inflate(layoutInflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
token = PreferenceHelper.getToken(requireContext())
|
||||
refreshLayout = view.findViewById(R.id.sub_refresh)
|
||||
|
||||
if (token != "") {
|
||||
view.findViewById<RelativeLayout>(R.id.loginOrRegister).visibility = View.GONE
|
||||
refreshLayout?.isEnabled = true
|
||||
binding.loginOrRegister.visibility = View.GONE
|
||||
binding.subRefresh.isEnabled = true
|
||||
|
||||
var progressBar = view.findViewById<ProgressBar>(R.id.sub_progress)
|
||||
progressBar.visibility = View.VISIBLE
|
||||
binding.subProgress.visibility = View.VISIBLE
|
||||
|
||||
var channelRecView = view.findViewById<RecyclerView>(R.id.sub_channels)
|
||||
|
||||
var feedRecView = view.findViewById<RecyclerView>(R.id.sub_feed)
|
||||
val grid = PreferenceHelper.getString(
|
||||
requireContext(),
|
||||
"grid",
|
||||
resources.getInteger(R.integer.grid_items).toString()
|
||||
)!!
|
||||
feedRecView.layoutManager = GridLayoutManager(view.context, grid.toInt())
|
||||
fetchFeed(feedRecView, progressBar, view)
|
||||
binding.subFeed.layoutManager = GridLayoutManager(view.context, grid.toInt())
|
||||
fetchFeed(binding.subFeed, binding.subProgress)
|
||||
|
||||
refreshLayout?.setOnRefreshListener {
|
||||
fetchChannels(channelRecView)
|
||||
fetchFeed(feedRecView, progressBar, view)
|
||||
binding.subRefresh.setOnRefreshListener {
|
||||
fetchChannels(binding.subChannels)
|
||||
fetchFeed(binding.subFeed, binding.subProgress)
|
||||
}
|
||||
|
||||
var toggleSubs = view.findViewById<RelativeLayout>(R.id.toggle_subs)
|
||||
val arrowImageView = view.findViewById<ImageView>(R.id.toggle)
|
||||
|
||||
toggleSubs.visibility = View.VISIBLE
|
||||
binding.toggleSubs.visibility = View.VISIBLE
|
||||
var loadedSubbedChannels = false
|
||||
|
||||
toggleSubs.setOnClickListener {
|
||||
arrowImageView.animate().rotationBy(180F).setDuration(100).start()
|
||||
if (!channelRecView.isVisible) {
|
||||
binding.toggleSubs.setOnClickListener {
|
||||
binding.toggle.animate().rotationBy(180F).setDuration(100).start()
|
||||
if (!binding.subChannels.isVisible) {
|
||||
if (!loadedSubbedChannels) {
|
||||
channelRecView?.layoutManager = LinearLayoutManager(context)
|
||||
fetchChannels(channelRecView)
|
||||
binding.subChannels.layoutManager = LinearLayoutManager(context)
|
||||
fetchChannels(binding.subChannels)
|
||||
loadedSubbedChannels = true
|
||||
}
|
||||
channelRecView.visibility = View.VISIBLE
|
||||
feedRecView.visibility = View.GONE
|
||||
binding.subChannels.visibility = View.VISIBLE
|
||||
binding.subFeed.visibility = View.GONE
|
||||
} else {
|
||||
channelRecView.visibility = View.GONE
|
||||
feedRecView.visibility = View.VISIBLE
|
||||
|
||||
// toggle button
|
||||
val image = view.findViewById<ImageView>(R.id.toggle)
|
||||
image.clearAnimation()
|
||||
binding.subChannels.visibility = View.GONE
|
||||
binding.subFeed.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
val scrollView = view.findViewById<ScrollView>(R.id.scrollview_sub)
|
||||
scrollView.viewTreeObserver
|
||||
binding.scrollviewSub.viewTreeObserver
|
||||
.addOnScrollChangedListener {
|
||||
if (scrollView.getChildAt(0).bottom
|
||||
== (scrollView.height + scrollView.scrollY)
|
||||
if (binding.scrollviewSub.getChildAt(0).bottom
|
||||
== (binding.scrollviewSub.height + binding.scrollviewSub.scrollY)
|
||||
) {
|
||||
// scroll view is at bottom
|
||||
if (isLoaded) {
|
||||
refreshLayout?.isRefreshing = true
|
||||
binding.subRefresh.isRefreshing = true
|
||||
subscriptionAdapter?.updateItems()
|
||||
refreshLayout?.isRefreshing = false
|
||||
binding.subRefresh.isRefreshing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
refreshLayout?.isEnabled = false
|
||||
binding.subRefresh.isEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchFeed(feedRecView: RecyclerView, progressBar: ProgressBar, view: View) {
|
||||
private fun fetchFeed(feedRecView: RecyclerView, progressBar: ProgressBar) {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.api.getFeed(token)
|
||||
RetrofitInstance.authApi.getFeed(token)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, e.toString())
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
@ -132,7 +118,7 @@ class Subscriptions : Fragment() {
|
||||
Log.e(TAG, "HttpException, unexpected response")
|
||||
return@launchWhenCreated
|
||||
} finally {
|
||||
refreshLayout?.isRefreshing = false
|
||||
binding.subRefresh.isRefreshing = false
|
||||
}
|
||||
if (response.isNotEmpty()) {
|
||||
subscriptionAdapter = SubscriptionAdapter(response, childFragmentManager)
|
||||
@ -140,16 +126,15 @@ class Subscriptions : Fragment() {
|
||||
subscriptionAdapter?.updateItems()
|
||||
} else {
|
||||
runOnUiThread {
|
||||
with(view.findViewById<ImageView>(R.id.boogh)) {
|
||||
with(binding.boogh) {
|
||||
visibility = View.VISIBLE
|
||||
setImageResource(R.drawable.ic_list)
|
||||
}
|
||||
with(view.findViewById<TextView>(R.id.textLike)) {
|
||||
with(binding.textLike) {
|
||||
visibility = View.VISIBLE
|
||||
text = getString(R.string.emptyList)
|
||||
}
|
||||
view.findViewById<RelativeLayout>(R.id.loginOrRegister)
|
||||
.visibility = View.VISIBLE
|
||||
binding.loginOrRegister.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
progressBar.visibility = View.GONE
|
||||
@ -163,7 +148,7 @@ class Subscriptions : Fragment() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.api.subscriptions(token)
|
||||
RetrofitInstance.authApi.subscriptions(token)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, e.toString())
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
@ -172,7 +157,7 @@ class Subscriptions : Fragment() {
|
||||
Log.e(TAG, "HttpException, unexpected response")
|
||||
return@launchWhenCreated
|
||||
} finally {
|
||||
refreshLayout?.isRefreshing = false
|
||||
binding.subRefresh.isRefreshing = false
|
||||
}
|
||||
if (response.isNotEmpty()) {
|
||||
channelRecView.adapter = SubscriptionChannelAdapter(response.toMutableList())
|
||||
@ -184,13 +169,6 @@ class Subscriptions : Fragment() {
|
||||
run()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.e(TAG, "Destroyed")
|
||||
super.onDestroy()
|
||||
subscriptionAdapter = null
|
||||
view?.findViewById<RecyclerView>(R.id.sub_feed)?.adapter = null
|
||||
}
|
||||
|
||||
private fun Fragment?.runOnUiThread(action: () -> Unit) {
|
||||
this ?: return
|
||||
if (!isAdded) return // Fragment not attached to an Activity
|
@ -0,0 +1,45 @@
|
||||
package com.github.libretube.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.adapters.WatchHistoryAdapter
|
||||
import com.github.libretube.databinding.FragmentWatchHistoryBinding
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
|
||||
class WatchHistoryFragment : Fragment() {
|
||||
private val TAG = "WatchHistoryFragment"
|
||||
private lateinit var binding: FragmentWatchHistoryBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentWatchHistoryBinding.inflate(layoutInflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val watchHistory = PreferenceHelper.getWatchHistory(requireContext())
|
||||
val watchHistoryAdapter = WatchHistoryAdapter(watchHistory, childFragmentManager)
|
||||
binding.watchHistoryRecView.adapter = watchHistoryAdapter
|
||||
|
||||
binding.clearHistory.setOnClickListener {
|
||||
PreferenceHelper.removePreference(requireContext(), "watch_history")
|
||||
watchHistoryAdapter.clear()
|
||||
}
|
||||
|
||||
// reverse order
|
||||
val linearLayoutManager = LinearLayoutManager(view.context)
|
||||
linearLayoutManager.reverseLayout = true
|
||||
linearLayoutManager.stackFromEnd = true
|
||||
|
||||
binding.watchHistoryRecView.layoutManager = linearLayoutManager
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package com.github.libretube.obj
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@ -28,68 +26,9 @@ data class Streams(
|
||||
val livestream: Boolean?,
|
||||
val proxyUrl: String?,
|
||||
val chapters: List<ChapterSegment>?
|
||||
) : Parcelable {
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readValue(Long::class.java.classLoader) as? Long,
|
||||
parcel.readValue(Long::class.java.classLoader) as? Long,
|
||||
parcel.readValue(Int::class.java.classLoader) as? Long,
|
||||
TODO("audioStreams"),
|
||||
TODO("videoStreams"),
|
||||
TODO("relatedStreams"),
|
||||
TODO("subtitles"),
|
||||
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
|
||||
parcel.readString(),
|
||||
TODO("chapters")
|
||||
)
|
||||
|
||||
) {
|
||||
constructor() : this(
|
||||
"", "", "", "", "", "", "", "", "", "", null, -1, -1, -1, -1, emptyList(), emptyList(),
|
||||
emptyList(), emptyList(), null, "", emptyList()
|
||||
)
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(title)
|
||||
parcel.writeString(description)
|
||||
parcel.writeString(uploadDate)
|
||||
parcel.writeString(uploader)
|
||||
parcel.writeString(uploaderUrl)
|
||||
parcel.writeString(uploaderAvatar)
|
||||
parcel.writeString(thumbnailUrl)
|
||||
parcel.writeString(hls)
|
||||
parcel.writeString(dash)
|
||||
parcel.writeString(lbryId)
|
||||
parcel.writeValue(uploaderVerified)
|
||||
parcel.writeValue(duration)
|
||||
parcel.writeValue(views)
|
||||
parcel.writeValue(likes)
|
||||
parcel.writeValue(dislikes)
|
||||
parcel.writeValue(livestream)
|
||||
parcel.writeString(proxyUrl)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<Streams> {
|
||||
override fun createFromParcel(parcel: Parcel): Streams {
|
||||
return Streams(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<Streams?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package com.github.libretube.obj
|
||||
|
||||
data class WatchHistoryItem(
|
||||
val videoId: String?,
|
||||
val title: String?,
|
||||
val uploadDate: String?,
|
||||
val uploader: String?,
|
||||
val uploaderUrl: String?,
|
||||
val uploaderAvatar: String?,
|
||||
val thumbnailUrl: String?,
|
||||
val duration: Int?
|
||||
)
|
@ -0,0 +1,6 @@
|
||||
package com.github.libretube.obj
|
||||
|
||||
data class WatchPosition(
|
||||
val videoId: String,
|
||||
val position: Long
|
||||
)
|
@ -8,68 +8,79 @@ import android.text.Html
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.github.libretube.AUTHORS_URL
|
||||
import com.github.libretube.CONTRIBUTING_URL
|
||||
import com.github.libretube.DONATE_URL
|
||||
import com.github.libretube.GITHUB_URL
|
||||
import com.github.libretube.PIPED_GITHUB_URL
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.WEBSITE_URL
|
||||
import com.github.libretube.activities.SettingsActivity
|
||||
import com.github.libretube.databinding.FragmentAboutBinding
|
||||
import com.github.libretube.util.ThemeHelper.getThemeColor
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
class AboutFragment : Fragment() {
|
||||
private lateinit var binding: FragmentAboutBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_about, container, false)
|
||||
): View {
|
||||
binding = FragmentAboutBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val topBarText = activity?.findViewById<TextView>(R.id.topBar_textView)
|
||||
topBarText?.text = getString(R.string.about)
|
||||
|
||||
val website = view.findViewById<LinearLayout>(R.id.website)
|
||||
website.setOnClickListener {
|
||||
val settingsActivity = activity as SettingsActivity
|
||||
settingsActivity.changeTopBarText(getString(R.string.about))
|
||||
|
||||
binding.website.setOnClickListener {
|
||||
openLinkFromHref(WEBSITE_URL)
|
||||
}
|
||||
val authors = view.findViewById<LinearLayout>(R.id.authors)
|
||||
authors.setOnClickListener {
|
||||
openLinkFromHref(AUTHORS_URL)
|
||||
binding.website.setOnLongClickListener {
|
||||
val text = context?.getString(R.string.website_summary)!!
|
||||
showSnackBar(text)
|
||||
true
|
||||
}
|
||||
val piped = view.findViewById<LinearLayout>(R.id.piped)
|
||||
piped.setOnClickListener {
|
||||
|
||||
binding.piped.setOnClickListener {
|
||||
openLinkFromHref(PIPED_GITHUB_URL)
|
||||
}
|
||||
val donate = view.findViewById<LinearLayout>(R.id.donate)
|
||||
donate.setOnClickListener {
|
||||
binding.piped.setOnLongClickListener {
|
||||
val text = context?.getString(R.string.piped_summary)!!
|
||||
showSnackBar(text)
|
||||
true
|
||||
}
|
||||
|
||||
binding.donate.setOnClickListener {
|
||||
openLinkFromHref(DONATE_URL)
|
||||
}
|
||||
val contributing = view.findViewById<LinearLayout>(R.id.contributing)
|
||||
contributing.setOnClickListener {
|
||||
openLinkFromHref(CONTRIBUTING_URL)
|
||||
binding.donate.setOnLongClickListener {
|
||||
val text = context?.getString(R.string.donate_summary)!!
|
||||
showSnackBar(text)
|
||||
true
|
||||
}
|
||||
val license = view.findViewById<LinearLayout>(R.id.license)
|
||||
license.setOnClickListener {
|
||||
val licenseString = view.context.assets
|
||||
.open("gpl3.html").bufferedReader().use {
|
||||
it.readText()
|
||||
}
|
||||
val licenseHtml = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
Html.fromHtml(licenseString, 1)
|
||||
} else {
|
||||
Html.fromHtml(licenseString)
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder(view.context!!)
|
||||
.setPositiveButton(getString(R.string.okay)) { _, _ -> }
|
||||
.setMessage(licenseHtml)
|
||||
.create()
|
||||
.show()
|
||||
binding.github.setOnClickListener {
|
||||
openLinkFromHref(GITHUB_URL)
|
||||
}
|
||||
binding.github.setOnLongClickListener {
|
||||
val text = context?.getString(R.string.contributing_summary)!!
|
||||
showSnackBar(text)
|
||||
true
|
||||
}
|
||||
|
||||
binding.license.setOnClickListener {
|
||||
showLicense()
|
||||
}
|
||||
binding.license.setOnLongClickListener {
|
||||
val text = context?.getString(R.string.license_summary)!!
|
||||
showSnackBar(text)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,4 +89,40 @@ class AboutFragment : Fragment() {
|
||||
val intent = Intent(Intent.ACTION_VIEW).setData(uri)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
private fun showSnackBar(text: String) {
|
||||
val snackBar = Snackbar
|
||||
.make(binding.root, text, Snackbar.LENGTH_LONG)
|
||||
|
||||
// set snackBar color
|
||||
snackBar.setBackgroundTint(getThemeColor(requireContext(), R.attr.colorSurface))
|
||||
snackBar.setTextColor(getThemeColor(requireContext(), R.attr.colorPrimary))
|
||||
|
||||
// prevent the text from being partially hidden
|
||||
snackBar.setTextMaxLines(3)
|
||||
|
||||
snackBar.show()
|
||||
}
|
||||
|
||||
private fun showLicense() {
|
||||
val assets = view?.context?.assets
|
||||
val licenseString = assets
|
||||
?.open("gpl3.html")
|
||||
?.bufferedReader()
|
||||
.use {
|
||||
it?.readText()
|
||||
}
|
||||
|
||||
val licenseHtml = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
Html.fromHtml(licenseString.toString(), 1)
|
||||
} else {
|
||||
Html.fromHtml(licenseString.toString())
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setPositiveButton(getString(R.string.okay)) { _, _ -> }
|
||||
.setMessage(licenseHtml)
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
package com.github.libretube.preferences
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.TextView
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.requireMainActivityRestart
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.activities.SettingsActivity
|
||||
import com.github.libretube.activities.requireMainActivityRestart
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
class AdvancedSettings : PreferenceFragmentCompat() {
|
||||
@ -15,15 +14,24 @@ class AdvancedSettings : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.advanced_settings, rootKey)
|
||||
|
||||
val topBarTextView = activity?.findViewById<TextView>(R.id.topBar_textView)
|
||||
topBarTextView?.text = getString(R.string.advanced)
|
||||
val settingsActivity = activity as SettingsActivity
|
||||
settingsActivity.changeTopBarText(getString(R.string.advanced))
|
||||
|
||||
// clear search history
|
||||
val clearHistory = findPreference<Preference>("clear_history")
|
||||
clearHistory?.setOnPreferenceClickListener {
|
||||
PreferenceHelper.removePreference(requireContext(), "search_history")
|
||||
true
|
||||
}
|
||||
|
||||
// clear watch history and positions
|
||||
val clearWatchHistory = findPreference<Preference>("clear_watch_history")
|
||||
clearWatchHistory?.setOnPreferenceClickListener {
|
||||
PreferenceHelper.removePreference(requireContext(), "watch_history")
|
||||
PreferenceHelper.removePreference(requireContext(), "watch_positions")
|
||||
true
|
||||
}
|
||||
|
||||
val resetSettings = findPreference<Preference>("reset_settings")
|
||||
resetSettings?.setOnPreferenceClickListener {
|
||||
showResetDialog()
|
||||
|
@ -1,32 +1,33 @@
|
||||
package com.github.libretube.preferences
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.TextView
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.SwitchPreference
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.requireMainActivityRestart
|
||||
import com.github.libretube.activities.SettingsActivity
|
||||
import com.github.libretube.activities.requireMainActivityRestart
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
import com.google.android.material.color.DynamicColors
|
||||
|
||||
class AppearanceSettings : PreferenceFragmentCompat() {
|
||||
private val TAG = "AppearanceSettings"
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.appearance_settings, rootKey)
|
||||
|
||||
val topBarTextView = activity?.findViewById<TextView>(R.id.topBar_textView)
|
||||
topBarTextView?.text = getString(R.string.appearance)
|
||||
val settingsActivity = activity as SettingsActivity
|
||||
settingsActivity.changeTopBarText(getString(R.string.appearance))
|
||||
|
||||
val themeToggle = findPreference<ListPreference>("theme_togglee")
|
||||
themeToggle?.setOnPreferenceChangeListener { _, _ ->
|
||||
requireMainActivityRestart = true
|
||||
ThemeHelper.restartMainActivity(requireContext())
|
||||
activity?.recreate()
|
||||
true
|
||||
}
|
||||
|
||||
val accentColor = findPreference<Preference>("accent_color")
|
||||
accentColor?.setOnPreferenceChangeListener { _, _ ->
|
||||
val accentColor = findPreference<ListPreference>("accent_color")
|
||||
updateAccentColorValues(accentColor!!)
|
||||
accentColor.setOnPreferenceChangeListener { _, _ ->
|
||||
requireMainActivityRestart = true
|
||||
activity?.recreate()
|
||||
true
|
||||
@ -50,4 +51,17 @@ class AppearanceSettings : PreferenceFragmentCompat() {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// remove material you from accent color option if not available
|
||||
private fun updateAccentColorValues(pref: ListPreference) {
|
||||
val dynamicColorsAvailable = DynamicColors.isDynamicColorAvailable()
|
||||
if (!dynamicColorsAvailable) {
|
||||
val entries = pref.entries.toMutableList()
|
||||
entries -= entries[0]
|
||||
pref.entries = entries.toTypedArray()
|
||||
val values = pref.entryValues.toMutableList()
|
||||
values -= values[0]
|
||||
pref.entryValues = values.toTypedArray()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
package com.github.libretube.preferences
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.github.libretube.DISCORD_URL
|
||||
import com.github.libretube.MATRIX_URL
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.REDDIT_URL
|
||||
import com.github.libretube.TELEGRAM_URL
|
||||
import com.github.libretube.TWITTER_URL
|
||||
import com.github.libretube.activities.SettingsActivity
|
||||
import com.github.libretube.databinding.FragmentCommunityBinding
|
||||
|
||||
class CommunityFragment : Fragment() {
|
||||
private lateinit var binding: FragmentCommunityBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentCommunityBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val settingsActivity = activity as SettingsActivity
|
||||
settingsActivity.changeTopBarText(getString(R.string.community))
|
||||
|
||||
binding.telegram.setOnClickListener {
|
||||
openLinkFromHref(TELEGRAM_URL)
|
||||
}
|
||||
|
||||
binding.matrix.setOnClickListener {
|
||||
openLinkFromHref(MATRIX_URL)
|
||||
}
|
||||
|
||||
binding.discord.setOnClickListener {
|
||||
openLinkFromHref(DISCORD_URL)
|
||||
}
|
||||
|
||||
binding.reddit.setOnClickListener {
|
||||
openLinkFromHref(REDDIT_URL)
|
||||
}
|
||||
|
||||
binding.twitter.setOnClickListener {
|
||||
openLinkFromHref(TWITTER_URL)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openLinkFromHref(link: String) {
|
||||
val uri = Uri.parse(link)
|
||||
val intent = Intent(Intent.ACTION_VIEW).setData(uri)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
@ -2,13 +2,13 @@ package com.github.libretube.preferences
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ContentResolver
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.app.ActivityCompat
|
||||
@ -18,12 +18,14 @@ import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.SwitchPreference
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.SettingsActivity
|
||||
import com.github.libretube.activities.requireMainActivityRestart
|
||||
import com.github.libretube.dialogs.CustomInstanceDialog
|
||||
import com.github.libretube.dialogs.DeleteAccountDialog
|
||||
import com.github.libretube.dialogs.LoginDialog
|
||||
import com.github.libretube.requireMainActivityRestart
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.dialogs.LogoutDialog
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import org.json.JSONObject
|
||||
import org.json.JSONTokener
|
||||
@ -110,20 +112,49 @@ class InstanceSettings : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.instance_settings, rootKey)
|
||||
|
||||
val topBarTextView = activity?.findViewById<TextView>(R.id.topBar_textView)
|
||||
topBarTextView?.text = getString(R.string.instance)
|
||||
val settingsActivity = activity as SettingsActivity
|
||||
settingsActivity.changeTopBarText(getString(R.string.instance))
|
||||
|
||||
val instance = findPreference<ListPreference>("selectInstance")
|
||||
// fetchInstance()
|
||||
initCustomInstances()
|
||||
instance?.setOnPreferenceChangeListener { _, newValue ->
|
||||
initCustomInstances(instance!!)
|
||||
instance.setOnPreferenceChangeListener { _, newValue ->
|
||||
requireMainActivityRestart = true
|
||||
RetrofitInstance.url = newValue.toString()
|
||||
if (!PreferenceHelper.getBoolean(requireContext(), "auth_instance_toggle", false)) {
|
||||
RetrofitInstance.authUrl = newValue.toString()
|
||||
logout()
|
||||
}
|
||||
RetrofitInstance.lazyMgr.reset()
|
||||
true
|
||||
}
|
||||
|
||||
val authInstance = findPreference<ListPreference>("selectAuthInstance")
|
||||
initCustomInstances(authInstance!!)
|
||||
// hide auth instance if option deselected
|
||||
if (!PreferenceHelper.getBoolean(requireContext(), "auth_instance_toggle", false)) {
|
||||
authInstance.isVisible = false
|
||||
}
|
||||
authInstance.setOnPreferenceChangeListener { _, newValue ->
|
||||
requireMainActivityRestart = true
|
||||
// save new auth url
|
||||
RetrofitInstance.authUrl = newValue.toString()
|
||||
RetrofitInstance.lazyMgr.reset()
|
||||
logout()
|
||||
true
|
||||
}
|
||||
|
||||
val authInstanceToggle = findPreference<SwitchPreference>("auth_instance_toggle")
|
||||
authInstanceToggle?.setOnPreferenceChangeListener { _, newValue ->
|
||||
requireMainActivityRestart = true
|
||||
authInstance.isVisible = newValue == true
|
||||
logout()
|
||||
// either use new auth url or the normal api url if auth instance disabled
|
||||
RetrofitInstance.authUrl = if (newValue == false) RetrofitInstance.url
|
||||
else authInstance.value
|
||||
true
|
||||
}
|
||||
|
||||
val customInstance = findPreference<Preference>("customInstance")
|
||||
customInstance?.setOnPreferenceClickListener {
|
||||
val newFragment = CustomInstanceDialog()
|
||||
@ -134,15 +165,23 @@ class InstanceSettings : PreferenceFragmentCompat() {
|
||||
val clearCustomInstances = findPreference<Preference>("clearCustomInstances")
|
||||
clearCustomInstances?.setOnPreferenceClickListener {
|
||||
PreferenceHelper.removePreference(requireContext(), "customInstances")
|
||||
activity?.recreate()
|
||||
val intent = Intent(context, SettingsActivity::class.java)
|
||||
startActivity(intent)
|
||||
true
|
||||
}
|
||||
|
||||
val login = findPreference<Preference>("login_register")
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
if (token != "") login?.setTitle(R.string.logout)
|
||||
login?.setOnPreferenceClickListener {
|
||||
requireMainActivityRestart = true
|
||||
val newFragment = LoginDialog()
|
||||
newFragment.show(childFragmentManager, "Login")
|
||||
if (token == "") {
|
||||
val newFragment = LoginDialog()
|
||||
newFragment.show(childFragmentManager, "Login")
|
||||
} else {
|
||||
val newFragment = LogoutDialog()
|
||||
newFragment.show(childFragmentManager, "Logout")
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
@ -160,58 +199,12 @@ class InstanceSettings : PreferenceFragmentCompat() {
|
||||
|
||||
val importFromYt = findPreference<Preference>("import_from_yt")
|
||||
importFromYt?.setOnPreferenceClickListener {
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
// check StorageAccess
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
Log.d("myz", "" + Build.VERSION.SDK_INT)
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
this.requireContext(),
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
)
|
||||
!= PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this.requireActivity(),
|
||||
arrayOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.MANAGE_EXTERNAL_STORAGE
|
||||
),
|
||||
1
|
||||
) // permission request code is just an int
|
||||
} else if (token != "") {
|
||||
MainSettings.getContent.launch("*/*")
|
||||
} else {
|
||||
Toast.makeText(context, R.string.login_first, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} else {
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
) != PackageManager.PERMISSION_GRANTED ||
|
||||
ActivityCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this.requireActivity(),
|
||||
arrayOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
),
|
||||
1
|
||||
)
|
||||
} else if (token != "") {
|
||||
MainSettings.getContent.launch("*/*")
|
||||
} else {
|
||||
Toast.makeText(context, R.string.login_first, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
importSubscriptions()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun initCustomInstances() {
|
||||
private fun initCustomInstances(instancePref: ListPreference) {
|
||||
val customInstances = PreferenceHelper.getCustomInstances(requireContext())
|
||||
|
||||
var instanceNames = resources.getStringArray(R.array.instances)
|
||||
@ -222,10 +215,9 @@ class InstanceSettings : PreferenceFragmentCompat() {
|
||||
}
|
||||
|
||||
// add custom instances to the list preference
|
||||
val instance = findPreference<ListPreference>("selectInstance")
|
||||
instance?.entries = instanceNames
|
||||
instance?.entryValues = instanceValues
|
||||
instance?.summaryProvider =
|
||||
instancePref.entries = instanceNames
|
||||
instancePref.entryValues = instanceValues
|
||||
instancePref.summaryProvider =
|
||||
Preference.SummaryProvider<ListPreference> { preference ->
|
||||
val text = preference.entry
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
@ -238,6 +230,7 @@ class InstanceSettings : PreferenceFragmentCompat() {
|
||||
|
||||
private fun logout() {
|
||||
PreferenceHelper.setToken(requireContext(), "")
|
||||
Toast.makeText(context, getString(R.string.loggedout), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun fetchInstance() {
|
||||
@ -289,12 +282,62 @@ class InstanceSettings : PreferenceFragmentCompat() {
|
||||
activity?.runOnUiThread(action)
|
||||
}
|
||||
|
||||
private fun importSubscriptions() {
|
||||
val token = PreferenceHelper.getToken(requireContext())
|
||||
// check StorageAccess
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
Log.d("myz", "" + Build.VERSION.SDK_INT)
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
this.requireContext(),
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
)
|
||||
!= PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this.requireActivity(),
|
||||
arrayOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.MANAGE_EXTERNAL_STORAGE
|
||||
),
|
||||
1
|
||||
) // permission request code is just an int
|
||||
} else if (token != "") {
|
||||
MainSettings.getContent.launch("*/*")
|
||||
} else {
|
||||
Toast.makeText(context, R.string.login_first, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} else {
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
) != PackageManager.PERMISSION_GRANTED ||
|
||||
ActivityCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this.requireActivity(),
|
||||
arrayOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
),
|
||||
1
|
||||
)
|
||||
} else if (token != "") {
|
||||
MainSettings.getContent.launch("*/*")
|
||||
} 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(requireContext())
|
||||
RetrofitInstance.api.importSubscriptions(
|
||||
RetrofitInstance.authApi.importSubscriptions(
|
||||
false,
|
||||
token,
|
||||
channels
|
||||
|
@ -8,8 +8,8 @@ import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.github.libretube.BuildConfig
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.isCurrentViewMainSettings
|
||||
import com.github.libretube.requireMainActivityRestart
|
||||
import com.github.libretube.activities.isCurrentViewMainSettings
|
||||
import com.github.libretube.activities.requireMainActivityRestart
|
||||
import com.github.libretube.util.ThemeHelper
|
||||
import com.github.libretube.util.checkUpdate
|
||||
|
||||
@ -38,35 +38,35 @@ class MainSettings : PreferenceFragmentCompat() {
|
||||
val instance = findPreference<Preference>("instance")
|
||||
instance?.setOnPreferenceClickListener {
|
||||
val newFragment = InstanceSettings()
|
||||
navigateSettings(newFragment)
|
||||
navigateToSettingsFragment(newFragment)
|
||||
true
|
||||
}
|
||||
|
||||
val appearance = findPreference<Preference>("appearance")
|
||||
appearance?.setOnPreferenceClickListener {
|
||||
val newFragment = AppearanceSettings()
|
||||
navigateSettings(newFragment)
|
||||
navigateToSettingsFragment(newFragment)
|
||||
true
|
||||
}
|
||||
|
||||
val sponsorBlock = findPreference<Preference>("sponsorblock")
|
||||
sponsorBlock?.setOnPreferenceClickListener {
|
||||
val newFragment = SponsorBlockSettings()
|
||||
navigateSettings(newFragment)
|
||||
navigateToSettingsFragment(newFragment)
|
||||
true
|
||||
}
|
||||
|
||||
val player = findPreference<Preference>("player")
|
||||
player?.setOnPreferenceClickListener {
|
||||
val newFragment = PlayerSettings()
|
||||
navigateSettings(newFragment)
|
||||
navigateToSettingsFragment(newFragment)
|
||||
true
|
||||
}
|
||||
|
||||
val advanced = findPreference<Preference>("advanced")
|
||||
advanced?.setOnPreferenceClickListener {
|
||||
val newFragment = AdvancedSettings()
|
||||
navigateSettings(newFragment)
|
||||
navigateToSettingsFragment(newFragment)
|
||||
true
|
||||
}
|
||||
|
||||
@ -80,12 +80,19 @@ class MainSettings : PreferenceFragmentCompat() {
|
||||
val about = findPreference<Preference>("about")
|
||||
about?.setOnPreferenceClickListener {
|
||||
val newFragment = AboutFragment()
|
||||
navigateSettings(newFragment)
|
||||
navigateToSettingsFragment(newFragment)
|
||||
true
|
||||
}
|
||||
|
||||
val community = findPreference<Preference>("community")
|
||||
community?.setOnPreferenceClickListener {
|
||||
val newFragment = CommunityFragment()
|
||||
navigateToSettingsFragment(newFragment)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateSettings(newFragment: Fragment) {
|
||||
private fun navigateToSettingsFragment(newFragment: Fragment) {
|
||||
isCurrentViewMainSettings = false
|
||||
parentFragmentManager.beginTransaction()
|
||||
.replace(R.id.settings, newFragment)
|
||||
|
@ -1,9 +1,9 @@
|
||||
package com.github.libretube.preferences
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.TextView
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.SettingsActivity
|
||||
|
||||
class PlayerSettings : PreferenceFragmentCompat() {
|
||||
val TAG = "PlayerSettings"
|
||||
@ -11,7 +11,7 @@ class PlayerSettings : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.player_settings, rootKey)
|
||||
|
||||
val topBarTextView = activity?.findViewById<TextView>(R.id.topBar_textView)
|
||||
topBarTextView?.text = getString(R.string.player)
|
||||
val settingsActivity = activity as SettingsActivity
|
||||
settingsActivity.changeTopBarText(getString(R.string.audio_video))
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,19 @@
|
||||
package com.github.libretube.util
|
||||
package com.github.libretube.preferences
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.github.libretube.obj.CustomInstance
|
||||
import com.github.libretube.obj.Streams
|
||||
import com.github.libretube.obj.WatchHistoryItem
|
||||
import com.github.libretube.obj.WatchPosition
|
||||
import com.google.common.reflect.TypeToken
|
||||
import com.google.gson.Gson
|
||||
import java.lang.reflect.Type
|
||||
|
||||
object PreferenceHelper {
|
||||
private val TAG = "PreferenceHelper"
|
||||
|
||||
fun setString(context: Context, key: String?, value: String?) {
|
||||
val editor = getDefaultSharedPreferencesEditor(context)
|
||||
editor.putString(key, value)
|
||||
@ -122,6 +127,97 @@ object PreferenceHelper {
|
||||
editor.putStringSet("search_history", set).apply()
|
||||
}
|
||||
|
||||
fun addToWatchHistory(context: Context, videoId: String, streams: Streams) {
|
||||
val editor = getDefaultSharedPreferencesEditor(context)
|
||||
val gson = Gson()
|
||||
|
||||
val watchHistoryItem = WatchHistoryItem(
|
||||
videoId,
|
||||
streams.title,
|
||||
streams.uploadDate,
|
||||
streams.uploader,
|
||||
streams.uploaderUrl?.replace("/channel/", ""),
|
||||
streams.uploaderAvatar,
|
||||
streams.thumbnailUrl,
|
||||
streams.duration
|
||||
)
|
||||
|
||||
val watchHistory = getWatchHistory(context)
|
||||
|
||||
// delete entries that have the same videoId
|
||||
var indexToRemove: Int? = null
|
||||
watchHistory.forEachIndexed { index, item ->
|
||||
if (item.videoId == videoId) indexToRemove = index
|
||||
}
|
||||
if (indexToRemove != null) watchHistory.removeAt(indexToRemove!!)
|
||||
|
||||
watchHistory += watchHistoryItem
|
||||
|
||||
val json = gson.toJson(watchHistory)
|
||||
editor.putString("watch_history", json).apply()
|
||||
}
|
||||
|
||||
fun getWatchHistory(context: Context): ArrayList<WatchHistoryItem> {
|
||||
val settings = getDefaultSharedPreferences(context)
|
||||
val gson = Gson()
|
||||
val json: String = settings.getString("watch_history", "")!!
|
||||
val type: Type = object : TypeToken<List<WatchHistoryItem?>?>() {}.type
|
||||
return try {
|
||||
gson.fromJson(json, type)
|
||||
} catch (e: Exception) {
|
||||
arrayListOf()
|
||||
}
|
||||
}
|
||||
|
||||
fun saveWatchPosition(context: Context, videoId: String, position: Long) {
|
||||
val editor = getDefaultSharedPreferencesEditor(context)
|
||||
|
||||
val watchPositions = getWatchPositions(context)
|
||||
val watchPositionItem = WatchPosition(videoId, position)
|
||||
|
||||
var indexToRemove: Int? = null
|
||||
watchPositions.forEachIndexed { index, item ->
|
||||
if (item.videoId == videoId) indexToRemove = index
|
||||
}
|
||||
|
||||
if (indexToRemove != null) watchPositions.removeAt(indexToRemove!!)
|
||||
|
||||
watchPositions += watchPositionItem
|
||||
|
||||
val gson = Gson()
|
||||
val json = gson.toJson(watchPositions)
|
||||
editor.putString("watch_positions", json).commit()
|
||||
}
|
||||
|
||||
fun removeWatchPosition(context: Context, videoId: String) {
|
||||
val editor = getDefaultSharedPreferencesEditor(context)
|
||||
|
||||
val watchPositions = getWatchPositions(context)
|
||||
|
||||
var indexToRemove: Int? = null
|
||||
watchPositions.forEachIndexed { index, item ->
|
||||
if (item.videoId == videoId) indexToRemove = index
|
||||
}
|
||||
|
||||
if (indexToRemove != null) watchPositions.removeAt(indexToRemove!!)
|
||||
|
||||
val gson = Gson()
|
||||
val json = gson.toJson(watchPositions)
|
||||
editor.putString("watch_positions", json).commit()
|
||||
}
|
||||
|
||||
fun getWatchPositions(context: Context): ArrayList<WatchPosition> {
|
||||
val settings = getDefaultSharedPreferences(context)
|
||||
val gson = Gson()
|
||||
val json: String = settings.getString("watch_positions", "")!!
|
||||
val type: Type = object : TypeToken<List<WatchPosition?>?>() {}.type
|
||||
return try {
|
||||
gson.fromJson(json, type)
|
||||
} catch (e: Exception) {
|
||||
arrayListOf()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDefaultSharedPreferences(context: Context): SharedPreferences {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
package com.github.libretube.preferences
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.TextView
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.activities.SettingsActivity
|
||||
|
||||
class SponsorBlockSettings : PreferenceFragmentCompat() {
|
||||
private val TAG = "SponsorBlockSettings"
|
||||
@ -11,7 +11,7 @@ class SponsorBlockSettings : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.sponsorblock_settings, rootKey)
|
||||
|
||||
val topBarTextView = activity?.findViewById<TextView>(R.id.topBar_textView)
|
||||
topBarTextView?.text = getString(R.string.sponsorblock)
|
||||
val settingsActivity = activity as SettingsActivity
|
||||
settingsActivity.changeTopBarText(getString(R.string.sponsorblock))
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import com.arthenica.ffmpegkit.FFmpegKit
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import java.io.File
|
||||
|
||||
var IS_DOWNLOAD_RUNNING = false
|
||||
|
@ -1,13 +1,14 @@
|
||||
package com.github.libretube
|
||||
package com.github.libretube.util
|
||||
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.support.v4.media.session.MediaSessionCompat
|
||||
import com.github.libretube.obj.Streams
|
||||
import com.github.libretube.util.DescriptionAdapter
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.google.android.exoplayer2.C
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
import com.google.android.exoplayer2.MediaItem
|
||||
import com.google.android.exoplayer2.Player
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
|
||||
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
||||
@ -42,7 +43,7 @@ class BackgroundMode {
|
||||
/**
|
||||
* The [PlayerNotificationManager] to load the [mediaSession] content on it.
|
||||
*/
|
||||
private lateinit var playerNotification: PlayerNotificationManager
|
||||
private var playerNotification: PlayerNotificationManager? = null
|
||||
|
||||
/**
|
||||
* The [AudioAttributes] handle the audio focus of the [player]
|
||||
@ -63,9 +64,45 @@ class BackgroundMode {
|
||||
.setAudioAttributes(audioAttributes, true)
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for changed playbackStates (e.g. pause, end)
|
||||
* Plays the next video when the current one ended
|
||||
*/
|
||||
player!!.addListener(object : Player.Listener {
|
||||
override fun onPlaybackStateChanged(@Player.State state: Int) {
|
||||
val autoplay = PreferenceHelper.getBoolean(c, "autoplay", false)
|
||||
if (state == Player.STATE_ENDED) {
|
||||
if (autoplay) playNextVideo(c)
|
||||
}
|
||||
}
|
||||
})
|
||||
setMediaItem(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the first related video to the current (used when the playback of the current video ended)
|
||||
*/
|
||||
private fun playNextVideo(c: Context) {
|
||||
if (response!!.relatedStreams!!.isNotEmpty()) {
|
||||
val videoId = response!!
|
||||
.relatedStreams!![0].url!!
|
||||
.replace("/watch?v=", "")
|
||||
|
||||
// destroy old player and its notification
|
||||
playerNotification = null
|
||||
player = null
|
||||
|
||||
// kill old notification
|
||||
val notificationManager = c.getSystemService(Context.NOTIFICATION_SERVICE)
|
||||
as NotificationManager
|
||||
notificationManager.cancel(1)
|
||||
|
||||
// play new video on background
|
||||
playOnBackgroundMode(c, videoId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the [playerNotification] attached to the [player] and shows it.
|
||||
*/
|
||||
@ -82,10 +119,12 @@ class BackgroundMode {
|
||||
)
|
||||
)
|
||||
.build()
|
||||
playerNotification.apply {
|
||||
playerNotification?.apply {
|
||||
setPlayer(player)
|
||||
setUsePreviousAction(false)
|
||||
setUseNextAction(false)
|
||||
setUsePreviousAction(false)
|
||||
setUseStopAction(true)
|
||||
setColorized(true)
|
||||
setMediaSessionToken(mediaSession.sessionToken)
|
||||
}
|
||||
}
|
||||
@ -110,7 +149,11 @@ class BackgroundMode {
|
||||
/**
|
||||
* Gets the video data and prepares the [player].
|
||||
*/
|
||||
fun playOnBackgroundMode(c: Context, videoId: String, seekToPosition: Long) {
|
||||
fun playOnBackgroundMode(
|
||||
c: Context,
|
||||
videoId: String,
|
||||
seekToPosition: Long = 0
|
||||
) {
|
||||
runBlocking {
|
||||
val job = launch {
|
||||
response = RetrofitInstance.api.getStreams(videoId)
|
@ -0,0 +1,32 @@
|
||||
package com.github.libretube.util
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.os.Build
|
||||
|
||||
object ConnectionHelper {
|
||||
fun isNetworkAvailable(context: Context): Boolean {
|
||||
val connectivityManager =
|
||||
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
val nw = connectivityManager.activeNetwork ?: return false
|
||||
val actNw = connectivityManager.getNetworkCapabilities(nw) ?: return false
|
||||
return when {
|
||||
// WiFi
|
||||
actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
||||
// Mobile
|
||||
actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
||||
// Ethernet
|
||||
actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
|
||||
// Bluetooth
|
||||
actNw.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true
|
||||
// VPN
|
||||
actNw.hasCapability(NetworkCapabilities.TRANSPORT_VPN) -> true
|
||||
else -> false
|
||||
}
|
||||
} else {
|
||||
return connectivityManager.activeNetworkInfo?.isConnected ?: false
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import com.github.libretube.MainActivity
|
||||
import com.github.libretube.activities.MainActivity
|
||||
import com.google.android.exoplayer2.Player
|
||||
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
||||
import java.net.URL
|
||||
@ -83,8 +83,8 @@ class DescriptionAdapter(
|
||||
return try {
|
||||
val resizedBitmap = Bitmap.createScaledBitmap(
|
||||
bitmap,
|
||||
1080,
|
||||
1080,
|
||||
bitmap.width,
|
||||
bitmap.width,
|
||||
false
|
||||
)
|
||||
resizedBitmap
|
||||
|
@ -2,25 +2,28 @@ package com.github.libretube.util
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.telephony.TelephonyManager
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import java.util.*
|
||||
|
||||
object LocaleHelper {
|
||||
|
||||
fun updateLanguage(context: Context) {
|
||||
val languageName = PreferenceHelper.getString(context, "language", "en")
|
||||
if (languageName != "") {
|
||||
setLanguage(context, languageName!!)
|
||||
val languageName = PreferenceHelper.getString(context, "language", "sys")
|
||||
if (languageName == "sys") updateLocaleConf(context, Locale.getDefault())
|
||||
else if ("$languageName".length < 3) {
|
||||
val locale = Locale(languageName.toString())
|
||||
updateLocaleConf(context, locale)
|
||||
} else if ("$languageName".length > 3) {
|
||||
val locale = Locale(
|
||||
languageName?.substring(0, 2).toString(),
|
||||
languageName?.substring(4, 6).toString()
|
||||
)
|
||||
updateLocaleConf(context, locale)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLanguage(context: Context, languageName: String) {
|
||||
val locale = if (languageName != "sys" && "$languageName".length < 3) {
|
||||
Locale(languageName)
|
||||
} else if ("$languageName".length > 3) {
|
||||
Locale(languageName?.substring(0, 2), languageName?.substring(4, 6))
|
||||
} else {
|
||||
Locale.getDefault()
|
||||
}
|
||||
private fun updateLocaleConf(context: Context, locale: Locale) {
|
||||
// Change API Language
|
||||
Locale.setDefault(locale)
|
||||
|
||||
@ -35,4 +38,51 @@ object LocaleHelper {
|
||||
}
|
||||
res.updateConfiguration(conf, dm)
|
||||
}
|
||||
|
||||
fun getDetectedCountry(context: Context, defaultCountryIsoCode: String): String {
|
||||
detectSIMCountry(context)?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
detectNetworkCountry(context)?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
detectLocaleCountry(context)?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
return defaultCountryIsoCode
|
||||
}
|
||||
|
||||
private fun detectSIMCountry(context: Context): String? {
|
||||
try {
|
||||
val telephonyManager =
|
||||
context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
|
||||
return telephonyManager.simCountryIso
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun detectNetworkCountry(context: Context): String? {
|
||||
try {
|
||||
val telephonyManager =
|
||||
context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
|
||||
return telephonyManager.networkCountryIso
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun detectLocaleCountry(context: Context): String? {
|
||||
try {
|
||||
return context.resources.configuration.locales[0].country
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
package com.github.libretube.util
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import com.github.libretube.R
|
||||
import com.google.android.material.color.DynamicColors
|
||||
|
||||
class Player : Activity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
DynamicColors.applyToActivityIfAvailable(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_player)
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
package com.github.libretube
|
||||
package com.github.libretube.util
|
||||
|
||||
import java.util.*
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class ResettableLazyManager {
|
||||
// we synchronize to make sure the timing of a reset() call and new inits do not collide
|
||||
val managedDelegates = LinkedList<Resettable>()
|
||||
private val managedDelegates = LinkedList<Resettable>()
|
||||
|
||||
fun register(managed: Resettable) {
|
||||
synchronized(managedDelegates) {
|
||||
@ -38,7 +38,7 @@ class ResettableLazy<PROPTYPE>(val manager: ResettableLazyManager, val init: ()
|
||||
lazyHolder = makeInitBlock()
|
||||
}
|
||||
|
||||
fun makeInitBlock(): Lazy<PROPTYPE> {
|
||||
private fun makeInitBlock(): Lazy<PROPTYPE> {
|
||||
return lazy {
|
||||
manager.register(this)
|
||||
init()
|
||||
|
@ -1,12 +1,11 @@
|
||||
package com.github.libretube.util
|
||||
|
||||
import com.github.libretube.resettableLazy
|
||||
import com.github.libretube.resettableManager
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.jackson.JacksonConverterFactory
|
||||
|
||||
object RetrofitInstance {
|
||||
lateinit var url: String
|
||||
lateinit var authUrl: String
|
||||
val lazyMgr = resettableManager()
|
||||
val api: PipedApi by resettableLazy(lazyMgr) {
|
||||
Retrofit.Builder()
|
||||
@ -15,4 +14,11 @@ object RetrofitInstance {
|
||||
.build()
|
||||
.create(PipedApi::class.java)
|
||||
}
|
||||
val authApi: PipedApi by resettableLazy(lazyMgr) {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(authUrl)
|
||||
.addConverterFactory(JacksonConverterFactory.create())
|
||||
.build()
|
||||
.create(PipedApi::class.java)
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,13 @@ import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.text.Spanned
|
||||
import android.util.TypedValue
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.text.HtmlCompat
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
|
||||
object ThemeHelper {
|
||||
|
||||
@ -18,7 +22,7 @@ object ThemeHelper {
|
||||
|
||||
private fun updateAccentColor(context: Context) {
|
||||
when (PreferenceHelper.getString(context, "accent_color", "purple")) {
|
||||
"my" -> context.setTheme(R.style.Theme_MY)
|
||||
"my" -> context.setTheme(R.style.MaterialYou)
|
||||
"red" -> context.setTheme(R.style.Theme_Red)
|
||||
"blue" -> context.setTheme(R.style.Theme_Blue)
|
||||
"yellow" -> context.setTheme(R.style.Theme_Yellow)
|
||||
@ -69,4 +73,19 @@ object ThemeHelper {
|
||||
intent?.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
fun getThemeColor(context: Context, colorCode: Int): Int {
|
||||
val value = TypedValue()
|
||||
context.theme.resolveAttribute(colorCode, value, true)
|
||||
return value.data
|
||||
}
|
||||
|
||||
fun getStyledAppName(context: Context): Spanned {
|
||||
val colorPrimary = getThemeColor(context, R.attr.colorPrimaryDark)
|
||||
val hexColor = String.format("#%06X", (0xFFFFFF and colorPrimary))
|
||||
return HtmlCompat.fromHtml(
|
||||
"Libre<span style='color:$hexColor';>Tube</span>",
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,17 @@
|
||||
package com.github.libretube.util
|
||||
package com.github.libretube.views
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
||||
import com.google.android.exoplayer2.ui.StyledPlayerView
|
||||
|
||||
internal class CustomExoPlayerView(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null
|
||||
) : StyledPlayerView(context, attributeSet) {
|
||||
val binding: ExoStyledPlayerControlViewBinding = ExoStyledPlayerControlViewBinding.bind(this)
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
@ -1,4 +1,4 @@
|
||||
package com.github.libretube.util
|
||||
package com.github.libretube.views
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
@ -0,0 +1,49 @@
|
||||
package com.github.libretube.views
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.View
|
||||
|
||||
class DoubleClickListener(
|
||||
private val doubleClickTimeLimitMills: Long = 300,
|
||||
private val callback: Callback
|
||||
) : View.OnClickListener {
|
||||
private var lastClicked: Long = -1L
|
||||
private var doubleClicked: Boolean = false
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
lastClicked = when {
|
||||
lastClicked == -1L -> {
|
||||
doubleClicked = false
|
||||
System.currentTimeMillis()
|
||||
}
|
||||
isDoubleClicked() -> {
|
||||
doubleClicked = true
|
||||
callback.doubleClicked()
|
||||
-1L
|
||||
}
|
||||
else -> {
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
if (!doubleClicked) callback.singleClicked()
|
||||
}, doubleClickTimeLimitMills)
|
||||
System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTimeDiff(from: Long, to: Long): Long {
|
||||
return to - from
|
||||
}
|
||||
|
||||
private fun isDoubleClicked(): Boolean {
|
||||
return getTimeDiff(
|
||||
lastClicked,
|
||||
System.currentTimeMillis()
|
||||
) <= doubleClickTimeLimitMills
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun doubleClicked()
|
||||
fun singleClicked()
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.github.libretube.util
|
||||
package com.github.libretube.views
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
14
app/src/main/res/drawable/ic_auth.xml
Normal file
14
app/src/main/res/drawable/ic_auth.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="36"
|
||||
android:viewportHeight="36">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M23.38,16.77l0.6,-0.6A5,5 0,0 0,24 9.1L18.71,3.84a5,5 0,0 0,-7.07 0L3.09,12.39a5,5 0,0 0,0 7.07l5.26,5.26a5,5 0,0 0,7.07 0l0.45,-0.45 2.1,2.2h3.44v3h3.69v1.63L28,34h6L34,27.45ZM14.82,10.18L9.37,15.64a1,1 0,0 1,-1.41 0l-0.4,-0.4a1,1 0,0 1,0 -1.41L13,8.36a1,1 0,0 1,1.41 0l0.4,0.4A1,1 0,0 1,14.82 10.18ZM32,32L28.86,32l-1.77,-1.76v-2.8L23.41,27.44v-3L18.8,24.44l-1.52,-1.61L22,18.18 32,28.28Z" />
|
||||
<path
|
||||
android:fillAlpha="0"
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M0,0h36v36h-36z" />
|
||||
</vector>
|
15
app/src/main/res/drawable/ic_community.xml
Normal file
15
app/src/main/res/drawable/ic_community.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M20,32C28.837,32 36,26.627 36,20C36,13.373 28.837,8 20,8C11.163,8 4,13.373 4,20C4,22.684 5.175,25.163 7.16,27.162C6.356,29.454 5.313,31.172 4.65,32.132C4.407,32.483 4.657,32.98 5.083,32.945C6.785,32.806 10.122,32.311 12.374,30.552C14.641,31.475 17.239,32 20,32Z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M22.784,33.834C31.403,32.793 38,26.996 38,20C38,19.463 37.961,18.933 37.886,18.412C41.553,20.1 44,23.136 44,26.6C44,28.748 43.06,30.73 41.472,32.329C42.068,34.028 42.828,35.333 43.358,36.126C43.595,36.481 43.342,36.978 42.917,36.937C41.504,36.802 39.011,36.377 37.301,35.042C35.487,35.781 33.409,36.2 31.2,36.2C27.978,36.2 25.034,35.307 22.784,33.834Z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_discord.xml
Normal file
10
app/src/main/res/drawable/ic_discord.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m15.43,11.4c-0.054,0.633 -0.582,1.127 -1.224,1.127 -0.678,0 -1.229,-0.55 -1.229,-1.229s0.55,-1.229 1.228,-1.229c0.683,0.029 1.225,0.59 1.225,1.277 0,0.019 0,0.037 -0.001,0.056v-0.003zM9.826,10.07c-0.688,0.061 -1.223,0.634 -1.223,1.332s0.535,1.271 1.218,1.332h0.005c0.683,-0.029 1.225,-0.59 1.225,-1.277 0,-0.019 0,-0.037 -0.001,-0.056v0.003c0.001,-0.02 0.002,-0.043 0.002,-0.067 0,-0.685 -0.541,-1.243 -1.219,-1.269h-0.002zM22.5,2.472v21.528c-3.023,-2.672 -2.057,-1.787 -5.568,-5.052l0.636,2.22h-13.609c-1.359,-0.004 -2.46,-1.106 -2.46,-2.466 0,-0.002 0,-0.004 0,-0.006v-16.224c0,-0.002 0,-0.004 0,-0.006 0,-1.36 1.101,-2.462 2.459,-2.466h16.081c1.359,0.004 2.46,1.106 2.46,2.466v0.006zM19.08,13.848c-0.042,-2.559 -0.676,-4.96 -1.77,-7.086l0.042,0.09c-0.924,-0.731 -2.088,-1.195 -3.358,-1.259l-0.014,-0.001 -0.168,0.192c1.15,0.312 2.15,0.837 3.002,1.535l-0.014,-0.011c-1.399,-0.769 -3.066,-1.222 -4.839,-1.222 -1.493,0 -2.911,0.321 -4.189,0.898l0.064,-0.026c-0.444,0.204 -0.708,0.35 -0.708,0.35 0.884,-0.722 1.942,-1.266 3.1,-1.56l0.056,-0.012 -0.12,-0.144c-1.284,0.065 -2.448,0.529 -3.384,1.269l0.012,-0.009c-1.052,2.036 -1.686,4.437 -1.728,6.982v0.014c0.799,1.111 2.088,1.826 3.543,1.826 0.041,0 0.082,-0.001 0.123,-0.002h-0.006s0.444,-0.54 0.804,-0.996c-0.866,-0.223 -1.592,-0.727 -2.093,-1.406l-0.007,-0.01c0.176,0.124 0.468,0.284 0.49,0.3 1.209,0.672 2.652,1.067 4.188,1.067 1.191,0 2.326,-0.238 3.36,-0.668l-0.058,0.021c0.528,-0.202 0.982,-0.44 1.404,-0.723l-0.025,0.016c-0.526,0.703 -1.277,1.212 -2.144,1.423l-0.026,0.005c0.36,0.456 0.792,0.972 0.792,0.972 0.033,0.001 0.072,0.001 0.111,0.001 1.461,0 2.755,-0.714 3.552,-1.813l0.009,-0.013z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_donate.xml
Normal file
10
app/src/main/res/drawable/ic_donate.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M256,416c114.9,0 208,-93.1 208,-208S370.9,0 256,0 48,93.1 48,208s93.1,208 208,208zM233.8,97.4V80.6c0,-9.2 7.4,-16.6 16.6,-16.6h11.1c9.2,0 16.6,7.4 16.6,16.6v17c15.5,0.8 30.5,6.1 43,15.4 5.6,4.1 6.2,12.3 1.2,17.1L306,145.6c-3.8,3.7 -9.5,3.8 -14,1 -5.4,-3.4 -11.4,-5.1 -17.8,-5.1h-38.9c-9,0 -16.3,8.2 -16.3,18.3 0,8.2 5,15.5 12.1,17.6l62.3,18.7c25.7,7.7 43.7,32.4 43.7,60.1 0,34 -26.4,61.5 -59.1,62.4v16.8c0,9.2 -7.4,16.6 -16.6,16.6h-11.1c-9.2,0 -16.6,-7.4 -16.6,-16.6v-17c-15.5,-0.8 -30.5,-6.1 -43,-15.4 -5.6,-4.1 -6.2,-12.3 -1.2,-17.1l16.3,-15.5c3.8,-3.7 9.5,-3.8 14,-1 5.4,3.4 11.4,5.1 17.8,5.1h38.9c9,0 16.3,-8.2 16.3,-18.3 0,-8.2 -5,-15.5 -12.1,-17.6l-62.3,-18.7c-25.7,-7.7 -43.7,-32.4 -43.7,-60.1 0.1,-34 26.4,-61.5 59.1,-62.4zM480,352h-32.5c-19.6,26 -44.6,47.7 -73,64h63.8c5.3,0 9.6,3.6 9.6,8v16c0,4.4 -4.3,8 -9.6,8H73.6c-5.3,0 -9.6,-3.6 -9.6,-8v-16c0,-4.4 4.3,-8 9.6,-8h63.8c-28.4,-16.3 -53.3,-38 -73,-64H32c-17.7,0 -32,14.3 -32,32v96c0,17.7 14.3,32 32,32h448c17.7,0 32,-14.3 32,-32v-96c0,-17.7 -14.3,-32 -32,-32z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_forward.xml
Normal file
10
app/src/main/res/drawable/ic_forward.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="@android:color/white"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z" />
|
||||
</vector>
|
@ -1,7 +1,7 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="@android:color/white"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
|
10
app/src/main/res/drawable/ic_github.xml
Normal file
10
app/src/main/res/drawable/ic_github.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="32"
|
||||
android:viewportHeight="32">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M16,0.396c-8.839,0 -16,7.167 -16,16 0,7.073 4.584,13.068 10.937,15.183 0.803,0.151 1.093,-0.344 1.093,-0.772 0,-0.38 -0.009,-1.385 -0.015,-2.719 -4.453,0.964 -5.391,-2.151 -5.391,-2.151 -0.729,-1.844 -1.781,-2.339 -1.781,-2.339 -1.448,-0.989 0.115,-0.968 0.115,-0.968 1.604,0.109 2.448,1.645 2.448,1.645 1.427,2.448 3.744,1.74 4.661,1.328 0.14,-1.031 0.557,-1.74 1.011,-2.135 -3.552,-0.401 -7.287,-1.776 -7.287,-7.907 0,-1.751 0.62,-3.177 1.645,-4.297 -0.177,-0.401 -0.719,-2.031 0.141,-4.235 0,0 1.339,-0.427 4.4,1.641 1.281,-0.355 2.641,-0.532 4,-0.541 1.36,0.009 2.719,0.187 4,0.541 3.043,-2.068 4.381,-1.641 4.381,-1.641 0.859,2.204 0.317,3.833 0.161,4.235 1.015,1.12 1.635,2.547 1.635,4.297 0,6.145 -3.74,7.5 -7.296,7.891 0.556,0.479 1.077,1.464 1.077,2.959 0,2.14 -0.02,3.864 -0.02,4.385 0,0.416 0.28,0.916 1.104,0.755 6.4,-2.093 10.979,-8.093 10.979,-15.156 0,-8.833 -7.161,-16 -16,-16z" />
|
||||
</vector>
|
16
app/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
16
app/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="1280dp"
|
||||
android:height="1280dp"
|
||||
android:viewportWidth="1280"
|
||||
android:viewportHeight="1280">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="m863.3,639.7c0,-2.3 -1.2,-4.4 -3.1,-5.5L527.9,437.1c-5.8,-3.4 -13.1,0.8 -13.1,7.6v85c0,3.2 1.7,6.1 4.4,7.7l165.6,94.8c5.9,3.4 5.9,12 0,15.4l-165.6,94.8c-2.7,1.6 -4.4,4.5 -4.4,7.7v84.9c0,6.8 7.3,11.1 13.1,7.6L860.1,645.2c1.9,-1.2 3.1,-3.3 3.1,-5.5z"
|
||||
android:strokeWidth="30"
|
||||
android:strokeColor="#000000" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="m582.8,634.5c4.2,2.4 4.2,8.5 0,10.9l-73.8,42.5c-4.1,2.4 -9.2,-0.6 -9.2,-5.4v-85.1c0,-4.8 5.1,-7.8 9.2,-5.4z"
|
||||
android:strokeWidth="0"
|
||||
android:strokeColor="#000000" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_matrix.xml
Normal file
10
app/src/main/res/drawable/ic_matrix.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M0.632,0.55v22.9L2.28,23.45L2.28,24L0,24L0,0h2.28v0.55zM7.675,7.81v1.157h0.033c0.309,-0.443 0.683,-0.784 1.117,-1.024 0.433,-0.245 0.936,-0.365 1.5,-0.365 0.54,0 1.033,0.107 1.481,0.314 0.448,0.208 0.785,0.582 1.02,1.108 0.254,-0.374 0.6,-0.706 1.034,-0.992 0.434,-0.287 0.95,-0.43 1.546,-0.43 0.453,0 0.872,0.056 1.26,0.167 0.388,0.11 0.716,0.286 0.993,0.53 0.276,0.245 0.489,0.559 0.646,0.951 0.152,0.392 0.23,0.863 0.23,1.417v5.728h-2.349L16.186,11.52c0,-0.286 -0.01,-0.559 -0.032,-0.812a1.755,1.755 0,0 0,-0.18 -0.66,1.106 1.106,0 0,0 -0.438,-0.448c-0.194,-0.11 -0.457,-0.166 -0.785,-0.166 -0.332,0 -0.6,0.064 -0.803,0.189a1.38,1.38 0,0 0,-0.48 0.499,1.946 1.946,0 0,0 -0.231,0.696 5.56,5.56 0,0 0,-0.06 0.785v4.768h-2.35v-4.8c0,-0.254 -0.004,-0.503 -0.018,-0.752a2.074,2.074 0,0 0,-0.143 -0.688,1.052 1.052,0 0,0 -0.415,-0.503c-0.194,-0.125 -0.476,-0.19 -0.854,-0.19 -0.111,0 -0.259,0.024 -0.439,0.074 -0.18,0.051 -0.36,0.143 -0.53,0.282 -0.171,0.138 -0.319,0.337 -0.439,0.595 -0.12,0.259 -0.18,0.6 -0.18,1.02v4.966L5.46,16.375L5.46,7.81zM23.368,23.45L23.368,0.55L21.72,0.55L21.72,0L24,0v24h-2.28v-0.55z" />
|
||||
</vector>
|
93
app/src/main/res/drawable/ic_piped.xml
Normal file
93
app/src/main/res/drawable/ic_piped.xml
Normal file
@ -0,0 +1,93 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="1080"
|
||||
android:viewportHeight="1080">
|
||||
<path android:pathData="M308.4,156.8C308.4,156.8 315.7,151.4 328.7,144.1C336,141 345.1,137.7 355.5,134.8C366.4,132.6 378.6,130.8 391.1,129.9C403.9,129.9 416.9,130.7 429.1,132.3C441.2,134.7 452.4,137.9 461.8,141.2C480,149.7 491.4,156.8 491.4,156.8C491.4,156.8 491.4,168.5 491.4,189.1C491.4,199.3 491.4,211.8 491.4,226C491.4,240.3 491.4,256.4 491.4,274C491.4,291.6 491.4,310.6 491.4,330.8C491.4,340.8 491.4,351.2 491.4,361.7C491.4,372.3 491.4,383.1 491.4,394.1C491.4,405.1 491.4,416.3 491.4,427.6C491.4,438.9 491.4,450.3 491.4,461.8C491.4,473.4 491.4,485 491.4,496.7C491.4,508.3 491.4,520.1 491.4,531.8C491.4,543.5 491.4,555.2 491.4,566.9C491.4,578.5 491.4,590.2 491.4,601.7C491.4,613.3 491.4,624.7 491.4,636C491.4,647.3 491.4,658.5 491.4,669.5C491.4,680.4 491.4,691.2 491.4,701.8C491.4,712.4 491.4,722.7 491.4,732.8C491.4,752.9 491.4,772 491.4,789.5C491.4,807.1 491.4,823.2 491.4,837.5C491.4,851.8 491.4,864.3 491.4,874.5C491.4,895 491.4,906.7 491.4,906.7C491.4,906.7 480,906.7 462.8,906.7C454.2,906.7 444.2,906.7 433.5,906.7C422.8,906.7 411.3,906.7 399.9,906.7C388.5,906.7 377,906.7 366.3,906.7C355.6,906.7 345.6,906.7 337,906.7C319.9,906.7 308.4,906.7 308.4,906.7C308.4,906.7 308.4,895 308.4,874.5C308.4,864.3 308.4,851.8 308.4,837.5C308.4,823.2 308.4,807.1 308.4,789.5C308.4,772 308.4,752.9 308.4,732.8C308.4,722.7 308.4,712.4 308.4,701.8C308.4,691.2 308.4,680.4 308.4,669.5C308.4,658.5 308.4,647.3 308.4,636C308.4,624.7 308.4,613.3 308.4,601.7C308.4,590.2 308.4,578.5 308.4,566.9C308.4,555.2 308.4,543.5 308.4,531.8C308.4,520.1 308.4,508.3 308.4,496.7C308.4,485 308.4,473.4 308.4,461.8C308.4,450.3 308.4,438.9 308.4,427.6C308.4,416.3 308.4,405.1 308.4,394.1C308.4,383.1 308.4,372.3 308.4,361.7C308.4,351.2 308.4,340.8 308.4,330.8C308.4,310.6 308.4,291.6 308.4,274C308.4,256.4 308.4,240.3 308.4,226C308.4,211.8 308.4,199.3 308.4,189.1C308.4,168.5 308.4,156.8 308.4,156.8Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="522.5"
|
||||
android:endY="481"
|
||||
android:startX="138.3"
|
||||
android:startY="555.7"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FF8B1010"
|
||||
android:offset="0.2" />
|
||||
<item
|
||||
android:color="#FFF84330"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path android:pathData="M400,948C349.1,948 308,931.5 308,911C308,890.5 349.1,874 400,874C450.9,874 492,890.5 492,911C492,931.5 450.9,948 400,948Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="321.5"
|
||||
android:endY="986.1"
|
||||
android:startX="353.4"
|
||||
android:startY="835.9"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FF493B32"
|
||||
android:offset="0.2" />
|
||||
<item
|
||||
android:color="#FFDBCCC4"
|
||||
android:offset="0.6" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path android:pathData="M550.1,292.5C550.1,292.5 550.1,282.8 550,270.2C549.8,264.6 549.6,258.3 549.4,252C549.2,245.7 549,239 548.8,232.1C548.7,224.7 548.7,216.6 548.8,208.3C549.1,199 549.5,189.3 550,180.1C551.3,158.9 552.6,142.4 552.6,142.4C552.6,142.4 567.2,141.2 591.9,141C597.8,141.4 604.3,142 611.3,142.8C618.2,143.7 625.6,144.9 633.4,146.3C640.9,148.2 648.7,150.6 656.8,153.4C664.8,156.2 673.1,159.4 681.5,163.1C689.5,167.4 697.5,172.2 705.6,177.6C713.6,182.9 721.7,188.8 729.7,195.2C737.2,202 744.5,209.4 751.7,217.3C758.5,225.5 765.1,234.1 771.4,243.2C777.3,252.6 783,262.4 788.2,272.5C793,282.9 797.4,293.5 801.4,304.5C804.9,315.6 808,326.9 810.6,338.4C812.7,350 814.3,361.7 815.5,373.4C816,385.1 816.1,396.8 815.7,408.4C814.8,420 813.3,431.4 811.3,442.6C808.8,453.7 805.9,464.5 802.4,475C798.4,485.3 794,495.3 789.1,504.9C783.8,514.2 778,523.1 771.9,531.5C765.3,539.7 758.5,547.3 751.3,554.5C743.5,561.1 735.5,567.3 727.4,573C719.3,578.6 711.1,583.8 702.8,588.4C693.9,592.4 685,596 676.3,599.1C667.5,602.2 659,604.9 650.7,607.2C641.9,608.9 633.4,610.4 625.5,611.5C617.5,612.7 610,613.6 603.1,614.2C588.6,614.6 576.9,614.7 568.9,614.6C560.9,614.6 556.5,614.4 556.5,614.4C556.5,614.4 556.8,604.8 557.3,591.3C557.4,584.9 557.6,577.5 557.8,569.7C557.9,561.9 557.9,553.4 557.9,544.6C557.8,535.4 557.7,525.6 557.5,515.8C557.1,505.3 556.7,494.7 556.3,485C555.1,463.5 554.1,447.5 554.1,447.5C554.1,447.5 561,447.9 572.7,448C578.3,447.8 584.8,447.3 592,446.4C599,445.1 606.4,443.4 614,441.1C621.1,438.4 628.3,435 635.2,431C638.5,428.8 641.6,426.5 644.6,424C647.5,421.5 650.2,418.7 652.8,415.8C655.1,412.9 657.3,409.8 659.4,406.6C661.2,403.3 662.8,399.9 664.2,396.4C665.4,392.9 666.4,389.3 667.2,385.6C667.7,382 668,378.2 668.1,374.5C668,370.8 667.6,367 667,363.3C666.2,359.6 665.1,355.9 663.8,352.3C662.3,348.7 660.7,345.2 658.8,341.8C656.7,338.4 654.4,335.1 651.9,332C649.3,328.9 646.5,326 643.5,323.2C640.4,320.5 637.2,317.9 633.9,315.5C626.7,311 619.4,307.1 612,303.8C604.2,300.9 596.5,298.6 589.4,296.8C581.9,295.3 575.1,294.3 569.4,293.6C557.3,292.7 550.1,292.5 550.1,292.5Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="861.4"
|
||||
android:endY="323.3"
|
||||
android:startX="300.2"
|
||||
android:startY="432.4"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FF8B1010"
|
||||
android:offset="0.2" />
|
||||
<item
|
||||
android:color="#FFF84330"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path android:pathData="M556.4,294C533.9,294 515.7,259.9 515.7,217.8C515.7,175.6 533.9,141.5 556.4,141.5C578.8,141.5 597,175.6 597,217.8C597,259.9 578.8,294 556.4,294Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="504.2"
|
||||
android:endY="372.5"
|
||||
android:startX="553.3"
|
||||
android:startY="63"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FF493B32"
|
||||
android:offset="0.2" />
|
||||
<item
|
||||
android:color="#FFDBCCC4"
|
||||
android:offset="0.6" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path android:pathData="M557.9,615C535.4,615 517.3,577.5 517.3,531C517.3,484.5 535.4,447 557.9,447C580.4,447 598.5,484.5 598.5,531C598.5,577.5 580.4,615 557.9,615Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="503.3"
|
||||
android:endY="701.5"
|
||||
android:startX="557.3"
|
||||
android:startY="360.5"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FF493B32"
|
||||
android:offset="0.2" />
|
||||
<item
|
||||
android:color="#FFDBCCC4"
|
||||
android:offset="0.6" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
12
app/src/main/res/drawable/ic_player.xml
Normal file
12
app/src/main/res/drawable/ic_player.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="244.86"
|
||||
android:viewportHeight="244.86">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M240.9,38.89c-2.43,-1.31 -5.39,-1.17 -7.69,0.35l-64.63,42.81V64.38c0,-14.48 -11.78,-26.25 -26.25,-26.25H26.25C11.78,38.13 0,49.91 0,64.38v116.09c0,14.47 11.78,26.25 26.25,26.25h116.09c14.47,0 26.25,-11.78 26.25,-26.25v-17.67l64.63,42.81c1.25,0.83 2.69,1.25 4.14,1.25c1.22,0 2.44,-0.3 3.55,-0.89c2.43,-1.31 3.95,-3.85 3.95,-6.61V45.5C244.86,42.74 243.34,40.2 240.9,38.89zM153.59,180.47c0,6.2 -5.05,11.25 -11.25,11.25H26.25c-6.2,0 -11.25,-5.05 -11.25,-11.25V64.38c0,-6.2 5.05,-11.25 11.25,-11.25h116.09c6.2,0 11.25,5.05 11.25,11.25v31.64v52.82V180.47zM229.86,185.39l-61.27,-40.58v-44.76l61.27,-40.58V185.39z"
|
||||
android:strokeWidth="10"
|
||||
android:strokeColor="#000000" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_reddit.xml
Normal file
10
app/src/main/res/drawable/ic_reddit.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,0A12,12 0,0 0,0 12a12,12 0,0 0,12 12,12 12,0 0,0 12,-12A12,12 0,0 0,12 0zM17.01,4.744c0.688,0 1.25,0.561 1.25,1.249a1.25,1.25 0,0 1,-2.498 0.056l-2.597,-0.547 -0.8,3.747c1.824,0.07 3.48,0.632 4.674,1.488 0.308,-0.309 0.73,-0.491 1.207,-0.491 0.968,0 1.754,0.786 1.754,1.754 0,0.716 -0.435,1.333 -1.01,1.614a3.111,3.111 0,0 1,0.042 0.52c0,2.694 -3.13,4.87 -7.004,4.87 -3.874,0 -7.004,-2.176 -7.004,-4.87 0,-0.183 0.015,-0.366 0.043,-0.534A1.748,1.748 0,0 1,4.028 12c0,-0.968 0.786,-1.754 1.754,-1.754 0.463,0 0.898,0.196 1.207,0.49 1.207,-0.883 2.878,-1.43 4.744,-1.487l0.885,-4.182a0.342,0.342 0,0 1,0.14 -0.197,0.35 0.35,0 0,1 0.238,-0.042l2.906,0.617a1.214,1.214 0,0 1,1.108 -0.701zM9.25,12C8.561,12 8,12.562 8,13.25c0,0.687 0.561,1.248 1.25,1.248 0.687,0 1.248,-0.561 1.248,-1.249 0,-0.688 -0.561,-1.249 -1.249,-1.249zM14.75,12c-0.687,0 -1.248,0.561 -1.248,1.25 0,0.687 0.561,1.248 1.249,1.248 0.688,0 1.249,-0.561 1.249,-1.249 0,-0.687 -0.562,-1.249 -1.25,-1.249zM9.284,15.99a0.327,0.327 0,0 0,-0.231 0.094,0.33 0.33,0 0,0 0,0.463c0.842,0.842 2.484,0.913 2.961,0.913 0.477,0 2.105,-0.056 2.961,-0.913a0.361,0.361 0,0 0,0.029 -0.463,0.33 0.33,0 0,0 -0.464,0c-0.547,0.533 -1.684,0.73 -2.512,0.73 -0.828,0 -1.979,-0.196 -2.512,-0.73a0.326,0.326 0,0 0,-0.232 -0.095z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_rewind.xml
Normal file
10
app/src/main/res/drawable/ic_rewind.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="@android:color/white"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_telegram.xml
Normal file
10
app/src/main/res/drawable/ic_telegram.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="15"
|
||||
android:viewportHeight="15">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M14.993,1.582C15.022,1.407 14.957,1.23 14.821,1.116C14.685,1.003 14.499,0.97 14.332,1.029L0.332,6.029C0.143,6.096 0.013,6.27 0.001,6.47C-0.011,6.67 0.097,6.858 0.276,6.947L4.276,8.947C4.437,9.027 4.628,9.016 4.777,8.916L8.098,6.702L6.11,9.188C6.022,9.297 5.984,9.438 6.006,9.577C6.027,9.715 6.106,9.838 6.223,9.916L12.223,13.916C12.364,14.01 12.543,14.026 12.699,13.959C12.854,13.891 12.965,13.75 12.993,13.582L14.993,1.582Z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_three_dots.xml
Normal file
10
app/src/main/res/drawable/ic_three_dots.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M9.5,13a1.5,1.5 0,1 1,-3 0,1.5 1.5,0 0,1 3,0zM9.5,8a1.5,1.5 0,1 1,-3 0,1.5 1.5,0 0,1 3,0zM9.5,3a1.5,1.5 0,1 1,-3 0,1.5 1.5,0 0,1 3,0z" />
|
||||
</vector>
|
22
app/src/main/res/drawable/ic_time_outlined.xml
Normal file
22
app/src/main/res/drawable/ic_time_outlined.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M21.25,12C21.25,17.108 17.109,21.25 12,21.25C6.892,21.25 2.75,17.108 2.75,12C2.75,6.891 6.892,2.75 12,2.75C17.109,2.75 21.25,6.891 21.25,12Z"
|
||||
android:strokeWidth="2.5"
|
||||
android:strokeColor="#130F26"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M16.191,12.767L11.661,12.693V7.846"
|
||||
android:strokeWidth="2.5"
|
||||
android:strokeColor="#130F26"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_twitter.xml
Normal file
10
app/src/main/res/drawable/ic_twitter.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="310"
|
||||
android:viewportHeight="310">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M302.97,57.39c-4.87,2.16 -9.88,3.98 -14.99,5.46c6.06,-6.85 10.68,-14.91 13.49,-23.73c0.63,-1.98 -0.02,-4.14 -1.65,-5.43c-1.62,-1.29 -3.88,-1.45 -5.66,-0.39c-10.86,6.44 -22.59,11.07 -34.88,13.78c-12.38,-12.1 -29.2,-18.98 -46.58,-18.98c-36.69,0 -66.55,29.85 -66.55,66.55c0,2.89 0.18,5.76 0.55,8.6C101.16,99.24 58.83,76.86 29.76,41.2c-1.04,-1.27 -2.63,-1.96 -4.27,-1.83c-1.63,0.13 -3.1,1.05 -3.93,2.47c-5.9,10.12 -9.01,21.69 -9.01,33.46c0,16.03 5.72,31.25 15.84,43.14c-3.08,-1.07 -6.06,-2.4 -8.91,-3.98c-1.53,-0.85 -3.39,-0.84 -4.91,0.03c-1.52,0.87 -2.47,2.47 -2.51,4.22c-0.01,0.29 -0.01,0.59 -0.01,0.89c0,23.93 12.88,45.48 32.58,57.23c-1.69,-0.17 -3.38,-0.41 -5.06,-0.74c-1.73,-0.33 -3.51,0.28 -4.68,1.6c-1.17,1.32 -1.56,3.16 -1.02,4.84c7.29,22.76 26.06,39.5 48.75,44.6c-18.82,11.79 -40.34,17.96 -62.93,17.96c-4.71,0 -9.45,-0.28 -14.1,-0.83c-2.31,-0.27 -4.51,1.09 -5.29,3.28c-0.79,2.19 0.05,4.64 2.01,5.89c29.02,18.61 62.58,28.44 97.05,28.44c67.75,0 110.14,-31.95 133.76,-58.75c29.46,-33.42 46.36,-77.66 46.36,-121.37c0,-1.83 -0.03,-3.67 -0.08,-5.51c11.62,-8.76 21.63,-19.35 29.77,-31.54c1.24,-1.85 1.1,-4.3 -0.33,-6C307.39,57.04 305.01,56.49 302.97,57.39z" />
|
||||
</vector>
|
@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/colorControlNormal"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M395.6,69.8L325.8,0h-58.2l69.8,69.8H395.6zM23.3,0H0v69.8h93.1L23.3,0zM244.4,69.8L174.5,0h-58.2l69.8,69.8H244.4zM418.9,162.9h-93.1l69.8,-69.8h-58.2l-69.8,69.8h-93.1l69.8,-69.8h-58.2l-69.8,69.8H23.3l69.8,-69.8H0v372.4C0,491.1 20.9,512 46.5,512h418.9c25.7,0 46.5,-20.9 46.5,-46.5V93.1h-23.3L418.9,162.9zM186.2,442.2V232.7l186.2,104.7L186.2,442.2zM418.9,0l69.8,69.8H512V0H418.9z" />
|
||||
</vector>
|
@ -1,16 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="1000"
|
||||
android:viewportHeight="1000">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M469.81,12.11c-5.14,0.79 -12.65,6.33 -17,11.86c-6.33,8.3 -23.33,56.15 -37.96,109.13c-5.14,17 77.1,28.47 134.84,18.98c42.31,-7.12 41.12,-4.75 25.7,-56.54c-19.38,-66.03 -25.7,-77.5 -43.1,-82.24C518.05,9.34 486.81,8.95 469.81,12.11z" />
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M356.72,328.83c-1.19,1.58 -13.05,40.73 -26.89,87.39l-24.91,84.62l16.61,14.63c40.33,35.99 154.6,52.99 253.85,37.56c60.89,-9.09 110.71,-30.45 116.64,-49.82c1.98,-5.93 -42.7,-170.42 -47.85,-177.14c-0.79,-0.79 -3.56,0.4 -6.32,2.77c-3.16,2.37 -18.58,7.91 -34.79,12.65c-62.87,18.19 -178.33,13.84 -228.15,-7.91C365.42,329.62 357.51,327.25 356.72,328.83z" />
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M185.51,628.94c-14.24,2.77 -19.77,12.65 -34.8,60.5C85.87,897.82 72.43,942.89 70.45,958.71c-3.95,34 -39.93,31.23 432.58,31.23c459.46,-0 428.23,1.98 428.23,-24.91c0,-6.72 -4.75,-27.29 -10.28,-45.87c-32.42,-107.95 -85.01,-274.81 -88.57,-280.74c-4.35,-8.31 -22.93,-12.26 -61.29,-12.26l-27.68,-0l1.19,24.12l1.58,24.12l-19.77,18.98c-35.19,33.61 -105.18,54.17 -200.47,58.12c-114.67,4.75 -219.06,-20.56 -257.41,-62.87c-13.44,-15.02 -13.84,-17 -12.65,-39.14l1.19,-23.33l-31.63,0.4C208.05,626.96 189.86,628.15 185.51,628.94z" />
|
||||
</vector>
|
@ -6,9 +6,11 @@
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/noInternet_settingsImageView"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_width="33dp"
|
||||
android:layout_height="33dp"
|
||||
android:layout_margin="16dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:padding="3dp"
|
||||
android:src="@drawable/ic_settings"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black"
|
||||
tools:context=".util.Player">
|
||||
|
||||
<com.github.libretube.util.CustomExoPlayerView
|
||||
android:id="@+id/fullscreen_player"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black" />
|
||||
|
||||
|
||||
</RelativeLayout>
|
@ -20,12 +20,12 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_toEndOf="@id/subscription_channel_image"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="Channel Name"
|
||||
android:textSize="16dp" />
|
||||
android:textSize="16sp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/subscription_subscribe"
|
||||
@ -35,7 +35,7 @@
|
||||
android:layout_alignParentEnd="true"
|
||||
android:backgroundTint="?attr/colorOnPrimary"
|
||||
android:text="@string/unsubscribe"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="11dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="11sp"
|
||||
app:cornerRadius="20dp" />
|
||||
</RelativeLayout>
|
@ -3,8 +3,9 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="5dp"
|
||||
android:orientation="vertical">
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="5dp">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/chapter_image"
|
||||
|
@ -8,12 +8,16 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_marginTop="10dp"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
@ -106,8 +110,7 @@
|
||||
android:id="@+id/replies_recView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@null"
|
||||
android:nestedScrollingEnabled="false" />
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -24,6 +24,7 @@
|
||||
<Button
|
||||
android:id="@+id/logout"
|
||||
style="@style/CustomDialogButton"
|
||||
android:layout_marginRight="16dp"
|
||||
android:text="@string/logout" />
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -1,26 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright 2020 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<!-- 0dp dimensions are used to prevent this view from influencing the size of
|
||||
the parent view if it uses "wrap_content". It is expanded to occupy the
|
||||
entirety of the parent in code, after the parent's size has been
|
||||
determined. See: https://github.com/google/ExoPlayer/issues/8726.
|
||||
-->
|
||||
|
||||
<View
|
||||
android:id="@id/exo_controls_background"
|
||||
android:layout_width="0dp"
|
||||
@ -28,7 +9,7 @@
|
||||
android:background="@color/exo_black_opacity_60" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/top_bar"
|
||||
android:id="@+id/exo_top_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/exo_styled_bottom_bar_height"
|
||||
android:layout_gravity="top"
|
||||
@ -49,11 +30,10 @@
|
||||
android:id="@+id/close_imageButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:background="#00FFFFFF"
|
||||
android:padding="@dimen/exo_icon_padding"
|
||||
android:src="@drawable/ic_close"
|
||||
android:visibility="gone"
|
||||
app:tint="@android:color/white" />
|
||||
|
||||
<ImageButton
|
||||
@ -110,7 +90,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:padding="@dimen/exo_icon_padding"
|
||||
android:text="HLS"
|
||||
android:text="@string/hls"
|
||||
android:textColor="#FFFFFF" />
|
||||
|
||||
<ImageView
|
||||
@ -132,7 +112,6 @@
|
||||
android:layout_height="@dimen/exo_styled_bottom_bar_height"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginTop="@dimen/exo_styled_bottom_bar_margin_top"
|
||||
android:background="@color/exo_bottom_bar_background"
|
||||
android:layoutDirection="ltr">
|
||||
|
||||
<LinearLayout
|
||||
@ -182,7 +161,8 @@
|
||||
<ImageButton
|
||||
android:id="@+id/fullscreen"
|
||||
style="@style/ExoStyledControls.Button.Bottom.FullScreen"
|
||||
android:src="@drawable/ic_fullscreen" />
|
||||
android:src="@drawable/ic_fullscreen"
|
||||
app:tint="@android:color/white" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -190,7 +170,6 @@
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="15dp"
|
||||
android:layout_above="@id/exo_basic_controls"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
@ -200,7 +179,6 @@
|
||||
android:id="@id/exo_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="15dp"
|
||||
android:background="@color/exo_bottom_bar_background"
|
||||
app:bar_height="2dp"
|
||||
app:played_color="?attr/colorOnSecondary"
|
||||
app:scrubber_color="?attr/colorOnPrimary"
|
||||
@ -233,25 +211,11 @@
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/exo_styled_controls_padding">
|
||||
|
||||
<Button
|
||||
android:id="@id/exo_rew_with_amount"
|
||||
style="@style/ExoStyledControls.Button.Center.RewWithAmount"
|
||||
android:layout_marginRight="25dp"
|
||||
android:scaleX="1.1"
|
||||
android:scaleY="1.1" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@id/exo_play_pause"
|
||||
style="@style/ExoStyledControls.Button.Center.PlayPause"
|
||||
android:scaleX="1.1"
|
||||
android:scaleY="1.1" />
|
||||
|
||||
<Button
|
||||
android:id="@id/exo_ffwd_with_amount"
|
||||
style="@style/ExoStyledControls.Button.Center.FfwdWithAmount"
|
||||
android:layout_marginLeft="25dp"
|
||||
android:scaleX="1.1"
|
||||
android:scaleY="1.1" />
|
||||
android:background="?android:selectableItemBackgroundBorderless"
|
||||
app:tint="@android:color/white" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
@ -8,125 +9,113 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
style="@style/roundedImageViewRounded"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="90dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="30dp"
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
android:layout_marginTop="50dp"
|
||||
android:src="@mipmap/ic_launcher_round"
|
||||
app:shapeAppearanceOverlay="@style/CircleImageView" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginBottom="40dp"
|
||||
android:text="LibreTube"
|
||||
android:textSize="27sp"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/website"
|
||||
style="@style/AboutItem">
|
||||
style="@style/AboutCard">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/website"
|
||||
android:textStyle="bold" />
|
||||
<LinearLayout style="@style/AboutItem">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/website_summary" />
|
||||
<ImageView
|
||||
style="@style/AboutImageView"
|
||||
android:src="@drawable/ic_region" />
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
style="@style/AboutTextView"
|
||||
android:text="@string/website" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/contributing"
|
||||
style="@style/AboutItem">
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/contributing"
|
||||
android:textStyle="bold" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/contributing_summary" />
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/github"
|
||||
style="@style/AboutCard">
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout style="@style/AboutItem">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/authors"
|
||||
style="@style/AboutItem">
|
||||
<ImageView
|
||||
style="@style/AboutImageView"
|
||||
android:src="@drawable/ic_github" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/authors"
|
||||
android:textStyle="bold" />
|
||||
<TextView
|
||||
style="@style/AboutTextView"
|
||||
android:text="@string/github" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/authors_summary" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<LinearLayout
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/piped"
|
||||
style="@style/AboutItem">
|
||||
style="@style/AboutCard">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/piped"
|
||||
android:textStyle="bold" />
|
||||
<LinearLayout style="@style/AboutItem">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/piped_summary" />
|
||||
<ImageView
|
||||
style="@style/AboutImageView"
|
||||
android:src="@drawable/ic_piped" />
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
style="@style/AboutTextView"
|
||||
android:text="@string/piped" />
|
||||
|
||||
<LinearLayout
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/donate"
|
||||
style="@style/AboutItem">
|
||||
style="@style/AboutCard">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/donate"
|
||||
android:textStyle="bold" />
|
||||
<LinearLayout style="@style/AboutItem">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/donate_summary" />
|
||||
<ImageView
|
||||
style="@style/AboutImageView"
|
||||
android:src="@drawable/ic_donate" />
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
style="@style/AboutTextView"
|
||||
android:text="@string/donate" />
|
||||
|
||||
<LinearLayout
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/license"
|
||||
style="@style/AboutItem">
|
||||
style="@style/AboutCard">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/license"
|
||||
android:textStyle="bold" />
|
||||
<LinearLayout style="@style/AboutItem">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/license_summary" />
|
||||
<ImageView
|
||||
style="@style/AboutImageView"
|
||||
android:src="@drawable/ic_license" />
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
style="@style/AboutTextView"
|
||||
android:text="@string/license" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.github.libretube.util.CustomSwipeToRefresh xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<com.github.libretube.views.CustomSwipeToRefresh xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/channel_refresh"
|
||||
android:layout_width="match_parent"
|
||||
@ -66,7 +66,7 @@
|
||||
android:id="@+id/channel_subs"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left"
|
||||
android:layout_gravity="start"
|
||||
android:text="@string/app_name"
|
||||
android:textSize="12sp" />
|
||||
|
||||
@ -76,11 +76,10 @@
|
||||
android:id="@+id/channel_subscribe"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:backgroundTint="?attr/colorOnPrimary"
|
||||
android:drawableLeft="@drawable/ic_bell_small"
|
||||
android:drawableTint="?android:attr/textColorPrimary"
|
||||
android:drawableStart="@drawable/ic_bell_small"
|
||||
android:drawableTint="@android:color/white"
|
||||
android:text="@string/subscribe"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="11sp" />
|
||||
@ -113,4 +112,4 @@
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</com.github.libretube.util.CustomSwipeToRefresh>
|
||||
</com.github.libretube.views.CustomSwipeToRefresh>
|
||||
|
122
app/src/main/res/layout/fragment_community.xml
Normal file
122
app/src/main/res/layout/fragment_community.xml
Normal file
@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="90dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="50dp"
|
||||
android:src="@mipmap/ic_launcher_round"
|
||||
app:shapeAppearanceOverlay="@style/CircleImageView" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="40dp"
|
||||
android:text="@string/community"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/telegram"
|
||||
style="@style/AboutCard">
|
||||
|
||||
<LinearLayout style="@style/AboutItem">
|
||||
|
||||
<ImageView
|
||||
style="@style/AboutImageView"
|
||||
android:src="@drawable/ic_telegram" />
|
||||
|
||||
<TextView
|
||||
style="@style/AboutTextView"
|
||||
android:text="@string/telegram" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/matrix"
|
||||
style="@style/AboutCard">
|
||||
|
||||
<LinearLayout style="@style/AboutItem">
|
||||
|
||||
<ImageView
|
||||
style="@style/AboutImageView"
|
||||
android:src="@drawable/ic_matrix" />
|
||||
|
||||
<TextView
|
||||
style="@style/AboutTextView"
|
||||
android:text="@string/matrix" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/discord"
|
||||
style="@style/AboutCard">
|
||||
|
||||
<LinearLayout style="@style/AboutItem">
|
||||
|
||||
<ImageView
|
||||
style="@style/AboutImageView"
|
||||
android:src="@drawable/ic_discord" />
|
||||
|
||||
<TextView
|
||||
style="@style/AboutTextView"
|
||||
android:text="@string/discord" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/reddit"
|
||||
style="@style/AboutCard">
|
||||
|
||||
<LinearLayout style="@style/AboutItem">
|
||||
|
||||
<ImageView
|
||||
style="@style/AboutImageView"
|
||||
android:src="@drawable/ic_reddit" />
|
||||
|
||||
<TextView
|
||||
style="@style/AboutTextView"
|
||||
android:text="@string/reddit" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/twitter"
|
||||
style="@style/AboutCard">
|
||||
|
||||
<LinearLayout style="@style/AboutItem">
|
||||
|
||||
<ImageView
|
||||
style="@style/AboutImageView"
|
||||
android:src="@drawable/ic_twitter" />
|
||||
|
||||
<TextView
|
||||
style="@style/AboutTextView"
|
||||
android:text="@string/twitter" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
@ -4,7 +4,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".fragments.Home">
|
||||
tools:context=".fragments.HomeFragment">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
|
@ -4,17 +4,15 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".fragments.Library">
|
||||
tools:context=".fragments.LibraryFragment">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/loginOrRegister2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/boogh2"
|
||||
android:id="@+id/boogh"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_centerInParent="true"
|
||||
@ -22,18 +20,19 @@
|
||||
android:src="@drawable/ic_login" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textLike2"
|
||||
android:id="@+id/text_like"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/boogh2"
|
||||
android:layout_below="@id/boogh"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/please_login"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
</RelativeLayout>
|
||||
|
||||
<com.github.libretube.util.CustomSwipeToRefresh
|
||||
<com.github.libretube.views.CustomSwipeToRefresh
|
||||
android:id="@+id/playlist_refresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
@ -45,9 +44,35 @@
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/showWatchHistory"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="10dp"
|
||||
android:src="@drawable/ic_time_outlined" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:text="@string/watch_history"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -62,7 +87,7 @@
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</com.github.libretube.util.CustomSwipeToRefresh>
|
||||
</com.github.libretube.views.CustomSwipeToRefresh>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/create_playlist"
|
||||
|
@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.github.libretube.util.SingleViewTouchableMotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<com.github.libretube.views.SingleViewTouchableMotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/playerMotionLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutDescription="@xml/player_scene"
|
||||
tools:context=".PlayerFragment">
|
||||
tools:context=".fragments.PlayerFragment">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/player_scrollView"
|
||||
@ -193,13 +193,13 @@
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="25dp"
|
||||
android:padding="2dp"
|
||||
android:src="@drawable/ic_vlc" />
|
||||
android:src="@drawable/ic_player" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="VLC" />
|
||||
android:text="@string/open" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
@ -281,7 +281,10 @@
|
||||
style="@style/Widget.Material3.CardView.Elevated"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:cardCornerRadius="18dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
@ -319,14 +322,13 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/comments_toggle"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:visibility="gone" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/player_recView"
|
||||
android:id="@+id/related_rec_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/comments_recView"
|
||||
@ -347,21 +349,59 @@
|
||||
android:layout_height="0dp"
|
||||
android:background="?attr/colorSurface"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_percent="0.35"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHeight_percent="0.3"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.github.libretube.util.CustomExoPlayerView
|
||||
<com.github.libretube.views.CustomExoPlayerView
|
||||
android:id="@+id/player"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:background="#000000"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorSurface"
|
||||
app:layout_constraintBottom_toBottomOf="@id/main_container"
|
||||
app:layout_constraintStart_toStartOf="@id/main_container"
|
||||
app:layout_constraintTop_toTopOf="@id/main_container"
|
||||
app:show_buffering="always" />
|
||||
app:show_buffering="when_playing">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/forwardFL"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_marginVertical="50dp">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/forwardBTN"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:selectableItemBackgroundBorderless"
|
||||
android:clickable="false"
|
||||
android:src="@drawable/ic_forward"
|
||||
android:visibility="invisible"
|
||||
app:tint="@android:color/white" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/rewindFL"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_vertical|start"
|
||||
android:layout_marginVertical="50dp">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/rewindBTN"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:selectableItemBackgroundBorderless"
|
||||
android:clickable="false"
|
||||
android:src="@drawable/ic_rewind"
|
||||
android:visibility="invisible"
|
||||
app:tint="@android:color/white" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</com.github.libretube.views.CustomExoPlayerView>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/close_imageView"
|
||||
@ -402,4 +442,4 @@
|
||||
app:layout_constraintStart_toEndOf="@+id/player"
|
||||
app:layout_constraintTop_toTopOf="@+id/play_imageView" />
|
||||
|
||||
</com.github.libretube.util.SingleViewTouchableMotionLayout>
|
||||
</com.github.libretube.views.SingleViewTouchableMotionLayout>
|
||||
|
@ -23,14 +23,30 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playlist_name"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:text=""
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
android:orientation="horizontal"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playlist_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text=""
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/optionsMenu"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginRight="10dp"
|
||||
android:src="@drawable/ic_three_dots" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playlist_uploader"
|
||||
|
@ -3,7 +3,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".fragments.Subscriptions">
|
||||
tools:context=".fragments.SubscriptionsFragment">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/sub_progress"
|
||||
@ -33,13 +33,14 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/boogh"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/please_login"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
</RelativeLayout>
|
||||
|
||||
<com.github.libretube.util.CustomSwipeToRefresh
|
||||
<com.github.libretube.views.CustomSwipeToRefresh
|
||||
android:id="@+id/sub_refresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
@ -101,5 +102,5 @@
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</com.github.libretube.util.CustomSwipeToRefresh>
|
||||
</com.github.libretube.views.CustomSwipeToRefresh>
|
||||
</RelativeLayout>
|
||||
|
43
app/src/main/res/layout/fragment_watch_history.xml
Normal file
43
app/src/main/res/layout/fragment_watch_history.xml
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:paddingHorizontal="8dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/watch_history"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/clearHistory"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginRight="5dp"
|
||||
android:src="@drawable/ic_reset" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/watchHistoryRecView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:nestedScrollingEnabled="false" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
@ -2,8 +2,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user