mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-01-06 01:20:29 +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)
|
[![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)
|
[![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/)
|
[![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/)
|
</div><div align="center" style="width:100%; display:flex; justify-content:space-between;">
|
||||||
[<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/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/tgload.png" alt="Get it on GitHub" height="80">](https://t.me/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>
|
</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)
|
<div style="width:100%; display:flex; justify-content:space-between;">
|
||||||
[<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)
|
|
||||||
|
|
||||||
## 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 ||
|
</div>
|
||||||
| - | - |
|
|
||||||
| User Accounts | ✅ |
|
|
||||||
| Subscriptions | ✅ |
|
|
||||||
| User Playlists | ✅ |
|
|
||||||
| Channel Playlists | ✅ |
|
|
||||||
| Search Filters | ✅ |
|
|
||||||
| SponsorBlock | ✅ |
|
|
||||||
| Subtitles | ✅ |
|
|
||||||
| Comments | ✅ |
|
|
||||||
|
|
||||||
|
## ⭐ 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!
|
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 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.
|
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">
|
<a href="https://hosted.weblate.org/projects/libretube/#languages">
|
||||||
<img src="https://hosted.weblate.org/widgets/libretube/-/287x66-grey.png" alt="Translation status" />
|
<img src="https://hosted.weblate.org/widgets/libretube/-/287x66-grey.png" alt="Translation status" />
|
||||||
</a>
|
</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)
|
[![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`
|
**BTC:** `bc1q0hk2smc74ej8fxupfrp05wk867e54e2zztnxfc`
|
||||||
|
|
||||||
**XMR:** `44txdmy4E5bDzMYQJh1ZSoHbrp1sWfpGa2FYg26L2ya8EaRejPsh42yVrYhepW9P4YWvrqmTZvms35z5FDgqy1xcVewk18d`
|
**XMR:** `44txdmy4E5bDzMYQJh1ZSoHbrp1sWfpGa2FYg26L2ya8EaRejPsh42yVrYhepW9P4YWvrqmTZvms35z5FDgqy1xcVewk18d`
|
||||||
|
|
||||||
## Mirrors (read-only)
|
## 🪞 Mirrors (read-only)
|
||||||
|
|
||||||
<a href="https://gitlab.com/libretube/LibreTube">GitLab</a></p>
|
<a href="https://gitlab.com/libretube/LibreTube">GitLab</a></p>
|
||||||
<a href="https://notabug.org/LibreTube/LibreTube">NotABug</a></p>
|
<a href="https://notabug.org/LibreTube/LibreTube">NotABug</a></p>
|
||||||
|
@ -10,13 +10,17 @@ android {
|
|||||||
applicationId 'com.github.libretube'
|
applicationId 'com.github.libretube'
|
||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 31
|
targetSdk 31
|
||||||
versionCode 12
|
versionCode 13
|
||||||
versionName '0.3.2'
|
versionName '0.3.3'
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||||
resValue "string", "app_name", "LibreTube"
|
resValue "string", "app_name", "LibreTube"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
viewBinding true
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
|
@ -22,15 +22,17 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.Purple">
|
android:theme="@style/Theme.Purple">
|
||||||
<activity
|
<activity
|
||||||
android:name=".util.Player"
|
android:name=".activities.Player"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:exported="false"
|
android:exported="false" />
|
||||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".SettingsActivity"
|
android:name=".activities.NoInternetActivity"
|
||||||
|
android:label="@string/noInternet" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.SettingsActivity"
|
||||||
android:label="@string/settings" />
|
android:label="@string/settings" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".activities.MainActivity"
|
||||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
|
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
@ -53,7 +55,7 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_gradient_round"
|
android:roundIcon="@mipmap/ic_gradient_round"
|
||||||
android:supportsPictureInPicture="true"
|
android:supportsPictureInPicture="true"
|
||||||
android:targetActivity=".MainActivity">
|
android:targetActivity=".activities.MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@ -70,7 +72,7 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_fire_round"
|
android:roundIcon="@mipmap/ic_fire_round"
|
||||||
android:supportsPictureInPicture="true"
|
android:supportsPictureInPicture="true"
|
||||||
android:targetActivity=".MainActivity">
|
android:targetActivity=".activities.MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@ -87,7 +89,7 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_flame_round"
|
android:roundIcon="@mipmap/ic_flame_round"
|
||||||
android:supportsPictureInPicture="true"
|
android:supportsPictureInPicture="true"
|
||||||
android:targetActivity=".MainActivity">
|
android:targetActivity=".activities.MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@ -104,7 +106,7 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_shaped_round"
|
android:roundIcon="@mipmap/ic_shaped_round"
|
||||||
android:supportsPictureInPicture="true"
|
android:supportsPictureInPicture="true"
|
||||||
android:targetActivity=".MainActivity">
|
android:targetActivity=".activities.MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@ -121,7 +123,7 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_torch_round"
|
android:roundIcon="@mipmap/ic_torch_round"
|
||||||
android:supportsPictureInPicture="true"
|
android:supportsPictureInPicture="true"
|
||||||
android:targetActivity=".MainActivity">
|
android:targetActivity=".activities.MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@ -138,7 +140,7 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_legacy_round"
|
android:roundIcon="@mipmap/ic_legacy_round"
|
||||||
android:supportsPictureInPicture="true"
|
android:supportsPictureInPicture="true"
|
||||||
android:targetActivity=".MainActivity">
|
android:targetActivity=".activities.MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@ -155,7 +157,7 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_bird_round"
|
android:roundIcon="@mipmap/ic_bird_round"
|
||||||
android:supportsPictureInPicture="true"
|
android:supportsPictureInPicture="true"
|
||||||
android:targetActivity=".MainActivity">
|
android:targetActivity=".activities.MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@ -163,7 +165,7 @@
|
|||||||
</activity-alias>
|
</activity-alias>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".RouterActivity"
|
android:name=".activities.RouterActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:launchMode="singleInstance">
|
android:launchMode="singleInstance">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@ -1,8 +1,23 @@
|
|||||||
package com.github.libretube
|
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"
|
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 WEBSITE_URL = "https://libre-tube.github.io/"
|
||||||
const val AUTHORS_URL = "https://github.com/libre-tube/LibreTube/graphs/contributors"
|
const val DONATE_URL = "https://github.com/libre-tube/LibreTube#donate"
|
||||||
const val DONATE_URL = "https://libre-tube.github.io/#donate"
|
const val GITHUB_URL = "https://github.com/libre-tube/LibreTube"
|
||||||
const val CONTRIBUTING_URL = "https://github.com/libre-tube/LibreTube#donate"
|
|
||||||
const val PIPED_GITHUB_URL = "https://github.com/TeamPiped/Piped"
|
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.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.net.ConnectivityManager
|
|
||||||
import android.net.NetworkCapabilities
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowInsets
|
import android.view.WindowInsets
|
||||||
import android.view.WindowInsetsController
|
import android.view.WindowInsetsController
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.Button
|
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.widget.Toolbar
|
|
||||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.text.HtmlCompat
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.ui.setupWithNavController
|
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.PlayerFragment
|
||||||
import com.github.libretube.fragments.isFullScreen
|
import com.github.libretube.fragments.isFullScreen
|
||||||
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
import com.github.libretube.services.ClosingService
|
import com.github.libretube.services.ClosingService
|
||||||
|
import com.github.libretube.util.ConnectionHelper
|
||||||
import com.github.libretube.util.CronetHelper
|
import com.github.libretube.util.CronetHelper
|
||||||
import com.github.libretube.util.LocaleHelper
|
import com.github.libretube.util.LocaleHelper
|
||||||
import com.github.libretube.util.PreferenceHelper
|
|
||||||
import com.github.libretube.util.RetrofitInstance
|
import com.github.libretube.util.RetrofitInstance
|
||||||
import com.github.libretube.util.ThemeHelper
|
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.color.DynamicColors
|
||||||
|
import com.google.android.material.elevation.SurfaceColors
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
val TAG = "MainActivity"
|
val TAG = "MainActivity"
|
||||||
|
|
||||||
lateinit var bottomNavigationView: BottomNavigationView
|
lateinit var binding: ActivityMainBinding
|
||||||
private lateinit var toolbar: Toolbar
|
|
||||||
lateinit var navController: NavController
|
lateinit var navController: NavController
|
||||||
|
private var startFragmentId = R.id.homeFragment
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
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)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
// start service that gets called on closure
|
// start service that gets called on closure
|
||||||
@ -61,74 +74,89 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
RetrofitInstance.url =
|
RetrofitInstance.url =
|
||||||
PreferenceHelper.getString(this, "selectInstance", "https://pipedapi.kavin.rocks/")!!
|
PreferenceHelper.getString(this, "selectInstance", "https://pipedapi.kavin.rocks/")!!
|
||||||
|
// set auth instance
|
||||||
ThemeHelper.updateTheme(this)
|
RetrofitInstance.authUrl =
|
||||||
LocaleHelper.updateLanguage(this)
|
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
|
// show noInternet Activity if no internet available on app startup
|
||||||
if (!isNetworkAvailable(this)) {
|
if (!ConnectionHelper.isNetworkAvailable(this)) {
|
||||||
setContentView(R.layout.activity_nointernet)
|
val noInternetIntent = Intent(this, NoInternetActivity::class.java)
|
||||||
findViewById<Button>(R.id.retry_button).setOnClickListener {
|
startActivity(noInternetIntent)
|
||||||
recreate()
|
|
||||||
}
|
|
||||||
findViewById<ImageView>(R.id.noInternet_settingsImageView).setOnClickListener {
|
|
||||||
val intent = Intent(this, SettingsActivity::class.java)
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
setContentView(R.layout.activity_main)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||||
|
|
||||||
bottomNavigationView = findViewById(R.id.bottomNav)
|
|
||||||
navController = findNavController(R.id.fragment)
|
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
|
// hide the trending page if enabled
|
||||||
val hideTrendingPage = PreferenceHelper.getBoolean(this, "hide_trending_page", false)
|
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
|
// save start tab fragment id
|
||||||
when (PreferenceHelper.getString(this, "default_tab", "home")) {
|
startFragmentId = when (PreferenceHelper.getString(this, "default_tab", "home")) {
|
||||||
"home" -> navController.navigate(R.id.home2)
|
"home" -> R.id.homeFragment
|
||||||
"subscriptions" -> navController.navigate(R.id.subscriptions)
|
"subscriptions" -> R.id.subscriptionsFragment
|
||||||
"library" -> navController.navigate(R.id.library)
|
"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) {
|
when (it.itemId) {
|
||||||
R.id.home2 -> {
|
R.id.homeFragment -> {
|
||||||
navController.backQueue.clear()
|
navController.navigate(R.id.homeFragment)
|
||||||
navController.navigate(R.id.home2)
|
|
||||||
}
|
}
|
||||||
R.id.subscriptions -> {
|
R.id.subscriptionsFragment -> {
|
||||||
// navController.backQueue.clear()
|
navController.navigate(R.id.subscriptionsFragment)
|
||||||
navController.navigate(R.id.subscriptions)
|
|
||||||
}
|
}
|
||||||
R.id.library -> {
|
R.id.libraryFragment -> {
|
||||||
// navController.backQueue.clear()
|
navController.navigate(R.id.libraryFragment)
|
||||||
navController.navigate(R.id.library)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
toolbar = findViewById(R.id.toolbar)
|
/**
|
||||||
val typedValue = TypedValue()
|
* don't remove this line
|
||||||
this.theme.resolveAttribute(R.attr.colorPrimary, typedValue, true)
|
* this prevents reselected items at the bottomNav to be duplicated in the backstack
|
||||||
val hexColor = String.format("#%06X", (0xFFFFFF and typedValue.data))
|
*/
|
||||||
val appName = HtmlCompat.fromHtml(
|
binding.bottomNav.setOnItemReselectedListener {}
|
||||||
"Libre<span style='color:$hexColor';>Tube</span>",
|
|
||||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
|
||||||
)
|
|
||||||
toolbar.title = appName
|
|
||||||
|
|
||||||
toolbar.setNavigationOnClickListener {
|
binding.toolbar.title = ThemeHelper.getStyledAppName(this)
|
||||||
|
|
||||||
|
binding.toolbar.setNavigationOnClickListener {
|
||||||
// settings activity stuff
|
// settings activity stuff
|
||||||
val intent = Intent(this, SettingsActivity::class.java)
|
val intent = Intent(this, SettingsActivity::class.java)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
toolbar.setOnMenuItemClickListener {
|
binding.toolbar.setOnMenuItemClickListener {
|
||||||
when (it.itemId) {
|
when (it.itemId) {
|
||||||
R.id.action_search -> {
|
R.id.action_search -> {
|
||||||
navController.navigate(R.id.searchFragment)
|
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() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
val intentData: Uri? = intent?.data
|
val intentData: Uri? = intent?.data
|
||||||
@ -183,7 +189,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
channel = channel!!.replace("/c/", "")
|
channel = channel!!.replace("/c/", "")
|
||||||
channel = channel.replace("/user/", "")
|
channel = channel.replace("/user/", "")
|
||||||
val bundle = bundleOf("channel_id" to channel)
|
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")) {
|
} else if (data.path!!.contains("/playlist")) {
|
||||||
Log.i(TAG, "URI Type: Playlist")
|
Log.i(TAG, "URI Type: Playlist")
|
||||||
var playlist = data.query!!
|
var playlist = data.query!!
|
||||||
@ -266,34 +272,37 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
try {
|
if (binding.mainMotionLayout.progress == 0F) {
|
||||||
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
|
|
||||||
try {
|
try {
|
||||||
navController.popBackStack()
|
minimizePlayer()
|
||||||
moveTaskToBack(true)
|
|
||||||
} catch (e: Exception) {
|
} 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) {
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
super.onConfigurationChanged(newConfig)
|
super.onConfigurationChanged(newConfig)
|
||||||
val orientation = newConfig.orientation
|
val orientation = newConfig.orientation
|
||||||
@ -362,10 +371,6 @@ fun Fragment.hideKeyboard() {
|
|||||||
view?.let { activity?.hideKeyboard(it) }
|
view?.let { activity?.hideKeyboard(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Activity.hideKeyboard() {
|
|
||||||
hideKeyboard(currentFocus ?: View(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.hideKeyboard(view: View) {
|
fun Context.hideKeyboard(view: View) {
|
||||||
val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
|
val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
|
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.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
@ -6,6 +6,7 @@ import android.net.Uri
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.github.libretube.R
|
||||||
import com.github.libretube.util.ThemeHelper
|
import com.github.libretube.util.ThemeHelper
|
||||||
|
|
||||||
class RouterActivity : AppCompatActivity() {
|
class RouterActivity : AppCompatActivity() {
|
@ -1,13 +1,12 @@
|
|||||||
package com.github.libretube
|
package com.github.libretube.activities
|
||||||
|
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
|
||||||
import android.widget.ImageButton
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.app.ActivityCompat
|
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.preferences.MainSettings
|
||||||
import com.github.libretube.util.ThemeHelper
|
import com.github.libretube.util.ThemeHelper
|
||||||
import com.google.android.material.color.DynamicColors
|
import com.google.android.material.color.DynamicColors
|
||||||
@ -17,24 +16,27 @@ var requireMainActivityRestart = false
|
|||||||
|
|
||||||
class SettingsActivity : AppCompatActivity() {
|
class SettingsActivity : AppCompatActivity() {
|
||||||
val TAG = "SettingsActivity"
|
val TAG = "SettingsActivity"
|
||||||
|
lateinit var binding: ActivitySettingsBinding
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
DynamicColors.applyToActivityIfAvailable(this)
|
DynamicColors.applyToActivityIfAvailable(this)
|
||||||
ThemeHelper.updateTheme(this)
|
ThemeHelper.updateTheme(this)
|
||||||
|
|
||||||
// makes the preference dialogs use material dialogs
|
// makes the preference dialogs use material dialogs
|
||||||
setTheme(R.style.MaterialAlertDialog)
|
setTheme(R.style.MaterialAlertDialog)
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
overridePendingTransition(50, 50)
|
overridePendingTransition(50, 50)
|
||||||
}
|
}
|
||||||
val view = this.findViewById<View>(android.R.id.content)
|
binding.root.alpha = 0F
|
||||||
view.alpha = 0F
|
binding.root.animate().alpha(1F).duration = 300
|
||||||
view.animate().alpha(1F).duration = 300
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_settings)
|
setContentView(binding.root)
|
||||||
|
|
||||||
val backButton = view.findViewById<ImageButton>(R.id.back_imageButton)
|
binding.backImageButton.setOnClickListener {
|
||||||
backButton.setOnClickListener {
|
|
||||||
onBackPressed()
|
onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,8 +68,11 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
.beginTransaction()
|
.beginTransaction()
|
||||||
.replace(R.id.settings, MainSettings())
|
.replace(R.id.settings, MainSettings())
|
||||||
.commit()
|
.commit()
|
||||||
val topBarTextView = findViewById<TextView>(R.id.topBar_textView)
|
changeTopBarText(getString(R.string.settings))
|
||||||
topBarTextView?.text = 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.os.Bundle
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.databinding.VideoChannelRowBinding
|
||||||
import com.github.libretube.dialogs.VideoOptionsDialog
|
import com.github.libretube.dialogs.VideoOptionsDialog
|
||||||
import com.github.libretube.fragments.PlayerFragment
|
import com.github.libretube.fragments.PlayerFragment
|
||||||
import com.github.libretube.obj.StreamItem
|
import com.github.libretube.obj.StreamItem
|
||||||
@ -22,6 +20,7 @@ class ChannelAdapter(
|
|||||||
private val childFragmentManager: FragmentManager
|
private val childFragmentManager: FragmentManager
|
||||||
) :
|
) :
|
||||||
RecyclerView.Adapter<ChannelViewHolder>() {
|
RecyclerView.Adapter<ChannelViewHolder>() {
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return videoFeed.size
|
return videoFeed.size
|
||||||
}
|
}
|
||||||
@ -33,43 +32,41 @@ class ChannelAdapter(
|
|||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelViewHolder {
|
||||||
val layoutInflater = LayoutInflater.from(parent.context)
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
val cell = layoutInflater.inflate(R.layout.video_channel_row, parent, false)
|
val binding = VideoChannelRowBinding.inflate(layoutInflater, parent, false)
|
||||||
return ChannelViewHolder(cell)
|
return ChannelViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ChannelViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ChannelViewHolder, position: Int) {
|
||||||
val trending = videoFeed[position]
|
val trending = videoFeed[position]
|
||||||
holder.v.findViewById<TextView>(R.id.channel_description).text = trending.title
|
holder.binding.apply {
|
||||||
holder.v.findViewById<TextView>(R.id.channel_views).text =
|
channelDescription.text = trending.title
|
||||||
trending.views.formatShort() + " • " +
|
channelViews.text =
|
||||||
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
|
trending.views.formatShort() + " • " +
|
||||||
holder.v.findViewById<TextView>(R.id.channel_duration).text =
|
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
|
||||||
DateUtils.formatElapsedTime(trending.duration!!)
|
channelDuration.text =
|
||||||
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.channel_thumbnail)
|
DateUtils.formatElapsedTime(trending.duration!!)
|
||||||
Picasso.get().load(trending.thumbnail).into(thumbnailImage)
|
Picasso.get().load(trending.thumbnail).into(channelThumbnail)
|
||||||
holder.v.setOnClickListener {
|
root.setOnClickListener {
|
||||||
var bundle = Bundle()
|
var bundle = Bundle()
|
||||||
bundle.putString("videoId", trending.url!!.replace("/watch?v=", ""))
|
bundle.putString("videoId", trending.url!!.replace("/watch?v=", ""))
|
||||||
var frag = PlayerFragment()
|
var frag = PlayerFragment()
|
||||||
frag.arguments = bundle
|
frag.arguments = bundle
|
||||||
val activity = holder.v.context as AppCompatActivity
|
val activity = root.context as AppCompatActivity
|
||||||
activity.supportFragmentManager.beginTransaction()
|
activity.supportFragmentManager.beginTransaction()
|
||||||
.remove(PlayerFragment())
|
.remove(PlayerFragment())
|
||||||
.commit()
|
.commit()
|
||||||
activity.supportFragmentManager.beginTransaction()
|
activity.supportFragmentManager.beginTransaction()
|
||||||
.replace(R.id.container, frag)
|
.replace(R.id.container, frag)
|
||||||
.commitNow()
|
.commitNow()
|
||||||
}
|
}
|
||||||
holder.v.setOnLongClickListener {
|
root.setOnLongClickListener {
|
||||||
val videoId = trending.url!!.replace("/watch?v=", "")
|
val videoId = trending.url!!.replace("/watch?v=", "")
|
||||||
VideoOptionsDialog(videoId, holder.v.context)
|
VideoOptionsDialog(videoId, root.context)
|
||||||
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
||||||
true
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChannelViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
class ChannelViewHolder(val binding: VideoChannelRowBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
init {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package com.github.libretube.adapters
|
package com.github.libretube.adapters
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.libretube.R
|
import com.github.libretube.databinding.ChapterColumnBinding
|
||||||
import com.github.libretube.obj.ChapterSegment
|
import com.github.libretube.obj.ChapterSegment
|
||||||
import com.google.android.exoplayer2.ExoPlayer
|
import com.google.android.exoplayer2.ExoPlayer
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
@ -19,21 +16,20 @@ class ChaptersAdapter(
|
|||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChaptersViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChaptersViewHolder {
|
||||||
val layoutInflater = LayoutInflater.from(parent.context)
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
val cell = layoutInflater.inflate(R.layout.chapter_column, parent, false)
|
val binding = ChapterColumnBinding.inflate(layoutInflater, parent, false)
|
||||||
return ChaptersViewHolder(cell)
|
return ChaptersViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ChaptersViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ChaptersViewHolder, position: Int) {
|
||||||
val chapter = chapters[position]
|
val chapter = chapters[position]
|
||||||
val chapterImage = holder.v.findViewById<ImageView>(R.id.chapter_image)
|
holder.binding.apply {
|
||||||
Picasso.get().load(chapter.image).fit().centerCrop().into(chapterImage)
|
Picasso.get().load(chapter.image).fit().centerCrop().into(chapterImage)
|
||||||
|
chapterTitle.text = chapter.title
|
||||||
|
|
||||||
val chapterTitle = holder.v.findViewById<TextView>(R.id.chapter_title)
|
root.setOnClickListener {
|
||||||
chapterTitle.text = chapter.title
|
val chapterStart = chapter.start!!.toLong() * 1000 // s -> ms
|
||||||
|
exoPlayer.seekTo(chapterStart)
|
||||||
holder.v.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) {
|
class ChaptersViewHolder(val binding: ChapterColumnBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
init {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
package com.github.libretube.adapters
|
package com.github.libretube.adapters
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.libretube.MainActivity
|
|
||||||
import com.github.libretube.R
|
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.Comment
|
||||||
import com.github.libretube.obj.CommentsPage
|
import com.github.libretube.obj.CommentsPage
|
||||||
import com.github.libretube.util.RetrofitInstance
|
import com.github.libretube.util.RetrofitInstance
|
||||||
@ -29,8 +27,8 @@ class CommentsAdapter(
|
|||||||
private val videoId: String,
|
private val videoId: String,
|
||||||
private val comments: MutableList<Comment>
|
private val comments: MutableList<Comment>
|
||||||
) : RecyclerView.Adapter<CommentsViewHolder>() {
|
) : RecyclerView.Adapter<CommentsViewHolder>() {
|
||||||
|
|
||||||
private val TAG = "CommentsAdapter"
|
private val TAG = "CommentsAdapter"
|
||||||
|
|
||||||
private var isLoading = false
|
private var isLoading = false
|
||||||
private var nextpage = ""
|
private var nextpage = ""
|
||||||
private var repliesPage = CommentsPage()
|
private var repliesPage = CommentsPage()
|
||||||
@ -42,59 +40,61 @@ class CommentsAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommentsViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommentsViewHolder {
|
||||||
val commentsView =
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
LayoutInflater.from(parent.context).inflate(R.layout.comments_row, parent, false)
|
val binding = CommentsRowBinding.inflate(layoutInflater, parent, false)
|
||||||
return CommentsViewHolder(commentsView)
|
return CommentsViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
|
||||||
override fun onBindViewHolder(holder: CommentsViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: CommentsViewHolder, position: Int) {
|
||||||
holder.v.findViewById<TextView>(R.id.comment_infos).text =
|
val comment = comments[position]
|
||||||
comments[position].author.toString() +
|
holder.binding.apply {
|
||||||
" • " + comments[position].commentedTime.toString()
|
commentInfos.text =
|
||||||
holder.v.findViewById<TextView>(R.id.comment_text).text =
|
comment.author.toString() +
|
||||||
comments[position].commentText.toString()
|
" • " + comment.commentedTime.toString()
|
||||||
val channelImage = holder.v.findViewById<ImageView>(R.id.commentor_image)
|
commentText.text =
|
||||||
Picasso.get().load(comments[position].thumbnail).fit().centerCrop().into(channelImage)
|
comment.commentText.toString()
|
||||||
holder.v.findViewById<TextView>(R.id.likes_textView).text =
|
Picasso.get().load(comment.thumbnail).fit().centerCrop().into(commentorImage)
|
||||||
comments[position].likeCount?.toLong().formatShort()
|
likesTextView.text =
|
||||||
if (comments[position].verified == true) {
|
comment.likeCount?.toLong().formatShort()
|
||||||
holder.v.findViewById<ImageView>(R.id.verified_imageView).visibility = View.VISIBLE
|
if (comment.verified == true) {
|
||||||
}
|
verifiedImageView.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) {
|
|
||||||
}
|
}
|
||||||
}
|
if (comment.pinned == true) {
|
||||||
val repliesRecView = holder.v.findViewById<RecyclerView>(R.id.replies_recView)
|
pinnedImageView.visibility = View.VISIBLE
|
||||||
repliesRecView.layoutManager = LinearLayoutManager(holder.v.context)
|
}
|
||||||
val repliesAdapter = RepliesAdapter(CommentsPage().comments)
|
if (comment.hearted == true) {
|
||||||
repliesRecView.adapter = repliesAdapter
|
heartedImageView.visibility = View.VISIBLE
|
||||||
holder.v.setOnClickListener {
|
}
|
||||||
if (repliesAdapter.itemCount == 0) {
|
commentorImage.setOnClickListener {
|
||||||
if (comments[position].repliesPage != null) {
|
val activity = root.context as MainActivity
|
||||||
nextpage = comments[position].repliesPage!!
|
val bundle = bundleOf("channel_id" to comment.commentorUrl)
|
||||||
fetchReplies(nextpage, repliesAdapter)
|
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||||
} else {
|
try {
|
||||||
Toast.makeText(holder.v.context, R.string.no_replies, Toast.LENGTH_SHORT).show()
|
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
|
return comments.size
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchReplies(nextpage: String, repliesAdapter: RepliesAdapter) {
|
private fun fetchReplies(nextPage: String, repliesAdapter: RepliesAdapter) {
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
if (!isLoading) {
|
if (!isLoading) {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
try {
|
try {
|
||||||
repliesPage = RetrofitInstance.api.getCommentsNextPage(videoId, nextpage)
|
repliesPage = RetrofitInstance.api.getCommentsNextPage(videoId, nextPage)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
println(e)
|
println(e)
|
||||||
Log.e(TAG, "IOException, you might not have internet connection")
|
Log.e(TAG, "IOException, you might not have internet connection")
|
||||||
} catch (e: HttpException) {
|
} catch (e: HttpException) {
|
||||||
Log.e(TAG, "HttpException, unexpected response," + e.response())
|
Log.e(TAG, "HttpException, unexpected response," + e.response())
|
||||||
}
|
}
|
||||||
// nextpage = if (repliesPage.nextpage!! != null) repliesPage.nextpage!! else ""
|
|
||||||
repliesAdapter.updateItems(repliesPage.comments)
|
repliesAdapter.updateItems(repliesPage.comments)
|
||||||
isLoading = false
|
isLoading = false
|
||||||
}
|
}
|
||||||
@ -123,7 +122,4 @@ class CommentsAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CommentsViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
class CommentsViewHolder(val binding: CommentsRowBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
init {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -7,17 +7,16 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.databinding.PlaylistRowBinding
|
||||||
import com.github.libretube.dialogs.VideoOptionsDialog
|
import com.github.libretube.dialogs.VideoOptionsDialog
|
||||||
import com.github.libretube.fragments.PlayerFragment
|
import com.github.libretube.fragments.PlayerFragment
|
||||||
import com.github.libretube.obj.PlaylistId
|
import com.github.libretube.obj.PlaylistId
|
||||||
import com.github.libretube.obj.StreamItem
|
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.github.libretube.util.RetrofitInstance
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -34,6 +33,7 @@ class PlaylistAdapter(
|
|||||||
private val childFragmentManager: FragmentManager
|
private val childFragmentManager: FragmentManager
|
||||||
) : RecyclerView.Adapter<PlaylistViewHolder>() {
|
) : RecyclerView.Adapter<PlaylistViewHolder>() {
|
||||||
private val TAG = "PlaylistAdapter"
|
private val TAG = "PlaylistAdapter"
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return videoFeed.size
|
return videoFeed.size
|
||||||
}
|
}
|
||||||
@ -45,45 +45,44 @@ class PlaylistAdapter(
|
|||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlaylistViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlaylistViewHolder {
|
||||||
val layoutInflater = LayoutInflater.from(parent.context)
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
val cell = layoutInflater.inflate(R.layout.playlist_row, parent, false)
|
val binding = PlaylistRowBinding.inflate(layoutInflater, parent, false)
|
||||||
return PlaylistViewHolder(cell)
|
return PlaylistViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: PlaylistViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: PlaylistViewHolder, position: Int) {
|
||||||
val streamItem = videoFeed[position]
|
val streamItem = videoFeed[position]
|
||||||
holder.v.findViewById<TextView>(R.id.playlist_title).text = streamItem.title
|
holder.binding.apply {
|
||||||
holder.v.findViewById<TextView>(R.id.playlist_description).text = streamItem.uploaderName
|
playlistTitle.text = streamItem.title
|
||||||
holder.v.findViewById<TextView>(R.id.playlist_duration).text =
|
playlistDescription.text = streamItem.uploaderName
|
||||||
DateUtils.formatElapsedTime(streamItem.duration!!)
|
playlistDuration.text = DateUtils.formatElapsedTime(streamItem.duration!!)
|
||||||
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.playlist_thumbnail)
|
Picasso.get().load(streamItem.thumbnail).into(playlistThumbnail)
|
||||||
Picasso.get().load(streamItem.thumbnail).into(thumbnailImage)
|
root.setOnClickListener {
|
||||||
holder.v.setOnClickListener {
|
var bundle = Bundle()
|
||||||
var bundle = Bundle()
|
bundle.putString("videoId", streamItem.url!!.replace("/watch?v=", ""))
|
||||||
bundle.putString("videoId", streamItem.url!!.replace("/watch?v=", ""))
|
bundle.putString("playlistId", playlistId)
|
||||||
bundle.putString("playlistId", playlistId)
|
var frag = PlayerFragment()
|
||||||
var frag = PlayerFragment()
|
frag.arguments = bundle
|
||||||
frag.arguments = bundle
|
val activity = root.context as AppCompatActivity
|
||||||
val activity = holder.v.context as AppCompatActivity
|
activity.supportFragmentManager.beginTransaction()
|
||||||
activity.supportFragmentManager.beginTransaction()
|
.remove(PlayerFragment())
|
||||||
.remove(PlayerFragment())
|
.commit()
|
||||||
.commit()
|
activity.supportFragmentManager.beginTransaction()
|
||||||
activity.supportFragmentManager.beginTransaction()
|
.replace(R.id.container, frag)
|
||||||
.replace(R.id.container, frag)
|
.commitNow()
|
||||||
.commitNow()
|
}
|
||||||
}
|
root.setOnLongClickListener {
|
||||||
holder.v.setOnLongClickListener {
|
val videoId = streamItem.url!!.replace("/watch?v=", "")
|
||||||
val videoId = streamItem.url!!.replace("/watch?v=", "")
|
VideoOptionsDialog(videoId, root.context)
|
||||||
VideoOptionsDialog(videoId, holder.v.context)
|
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
||||||
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
true
|
||||||
true
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (isOwner) {
|
if (isOwner) {
|
||||||
val delete = holder.v.findViewById<ImageView>(R.id.delete_playlist)
|
deletePlaylist.visibility = View.VISIBLE
|
||||||
delete.visibility = View.VISIBLE
|
deletePlaylist.setOnClickListener {
|
||||||
delete.setOnClickListener {
|
val token = PreferenceHelper.getToken(root.context)
|
||||||
val token = PreferenceHelper.getToken(holder.v.context)
|
removeFromPlaylist(token, position)
|
||||||
removeFromPlaylist(token, position)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,7 +91,7 @@ class PlaylistAdapter(
|
|||||||
fun run() {
|
fun run() {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.removeFromPlaylist(
|
RetrofitInstance.authApi.removeFromPlaylist(
|
||||||
token,
|
token,
|
||||||
PlaylistId(playlistId = playlistId, index = position)
|
PlaylistId(playlistId = playlistId, index = position)
|
||||||
)
|
)
|
||||||
@ -111,10 +110,6 @@ class PlaylistAdapter(
|
|||||||
videoFeed.removeAt(position)
|
videoFeed.removeAt(position)
|
||||||
// FIXME: This needs to run on UI thread?
|
// FIXME: This needs to run on UI thread?
|
||||||
activity.runOnUiThread { notifyDataSetChanged() }
|
activity.runOnUiThread { notifyDataSetChanged() }
|
||||||
|
|
||||||
/*if(playlists.isEmpty()){
|
|
||||||
view.findViewById<ImageView>(R.id.boogh2).visibility=View.VISIBLE
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, e.toString())
|
Log.e(TAG, e.toString())
|
||||||
@ -125,7 +120,4 @@ class PlaylistAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PlaylistViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
class PlaylistViewHolder(val binding: PlaylistRowBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
init {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,21 +3,20 @@ package com.github.libretube.adapters
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.libretube.MainActivity
|
|
||||||
import com.github.libretube.R
|
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.PlaylistId
|
||||||
import com.github.libretube.obj.Playlists
|
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.RetrofitInstance
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -27,6 +26,7 @@ class PlaylistsAdapter(
|
|||||||
private val activity: Activity
|
private val activity: Activity
|
||||||
) : RecyclerView.Adapter<PlaylistsViewHolder>() {
|
) : RecyclerView.Adapter<PlaylistsViewHolder>() {
|
||||||
val TAG = "PlaylistsAdapter"
|
val TAG = "PlaylistsAdapter"
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return playlists.size
|
return playlists.size
|
||||||
}
|
}
|
||||||
@ -38,45 +38,46 @@ class PlaylistsAdapter(
|
|||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlaylistsViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlaylistsViewHolder {
|
||||||
val layoutInflater = LayoutInflater.from(parent.context)
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
val cell = layoutInflater.inflate(R.layout.playlists_row, parent, false)
|
val binding = PlaylistsRowBinding.inflate(layoutInflater, parent, false)
|
||||||
return PlaylistsViewHolder(cell)
|
return PlaylistsViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: PlaylistsViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: PlaylistsViewHolder, position: Int) {
|
||||||
val playlist = playlists[position]
|
val playlist = playlists[position]
|
||||||
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.playlist_thumbnail)
|
holder.binding.apply {
|
||||||
Picasso.get().load(playlist.thumbnail).into(thumbnailImage)
|
Picasso.get().load(playlist.thumbnail).into(playlistThumbnail)
|
||||||
// set imageview drawable as empty playlist if imageview empty
|
// set imageview drawable as empty playlist if imageview empty
|
||||||
if (thumbnailImage.drawable == null) {
|
if (playlistThumbnail.drawable == null) {
|
||||||
thumbnailImage.setImageResource(R.drawable.ic_empty_playlist)
|
playlistThumbnail.setImageResource(R.drawable.ic_empty_playlist)
|
||||||
thumbnailImage.setBackgroundColor(R.attr.colorSurface)
|
playlistThumbnail.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)
|
|
||||||
}
|
}
|
||||||
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) {
|
private fun deletePlaylist(id: String, token: String, position: Int) {
|
||||||
fun run() {
|
fun run() {
|
||||||
GlobalScope.launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.deletePlaylist(token, PlaylistId(id))
|
RetrofitInstance.authApi.deletePlaylist(token, PlaylistId(id))
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
println(e)
|
println(e)
|
||||||
Log.e(TAG, "IOException, you might not have internet connection")
|
Log.e(TAG, "IOException, you might not have internet connection")
|
||||||
@ -84,7 +85,6 @@ class PlaylistsAdapter(
|
|||||||
} catch (e: HttpException) {
|
} catch (e: HttpException) {
|
||||||
Log.e(TAG, "HttpException, unexpected response")
|
Log.e(TAG, "HttpException, unexpected response")
|
||||||
return@launch
|
return@launch
|
||||||
} finally {
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (response.message == "ok") {
|
if (response.message == "ok") {
|
||||||
@ -102,7 +102,4 @@ class PlaylistsAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PlaylistsViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
class PlaylistsViewHolder(val binding: PlaylistsRowBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
init {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,13 +3,12 @@ package com.github.libretube.adapters
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.libretube.MainActivity
|
|
||||||
import com.github.libretube.R
|
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.obj.Comment
|
||||||
import com.github.libretube.util.formatShort
|
import com.github.libretube.util.formatShort
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
@ -17,10 +16,7 @@ import com.squareup.picasso.Picasso
|
|||||||
class RepliesAdapter(
|
class RepliesAdapter(
|
||||||
private val replies: MutableList<Comment>
|
private val replies: MutableList<Comment>
|
||||||
) : RecyclerView.Adapter<RepliesViewHolder>() {
|
) : RecyclerView.Adapter<RepliesViewHolder>() {
|
||||||
|
|
||||||
private val TAG = "RepliesAdapter"
|
private val TAG = "RepliesAdapter"
|
||||||
private var isLoading = false
|
|
||||||
private var nextPage = ""
|
|
||||||
|
|
||||||
fun clear() {
|
fun clear() {
|
||||||
val size: Int = replies.size
|
val size: Int = replies.size
|
||||||
@ -35,41 +31,45 @@ class RepliesAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RepliesViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RepliesViewHolder {
|
||||||
var repliesView =
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
LayoutInflater.from(parent.context).inflate(R.layout.replies_row, parent, false)
|
val binding = RepliesRowBinding.inflate(layoutInflater, parent, false)
|
||||||
return RepliesViewHolder(repliesView)
|
return RepliesViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RepliesViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RepliesViewHolder, position: Int) {
|
||||||
holder.v.findViewById<TextView>(R.id.comment_infos).text =
|
holder.binding.apply {
|
||||||
replies[position].author.toString() +
|
val reply = replies[position]
|
||||||
" • " + replies[position].commentedTime.toString()
|
commentInfos.text =
|
||||||
holder.v.findViewById<TextView>(R.id.comment_text).text =
|
reply.author.toString() +
|
||||||
replies[position].commentText.toString()
|
" • " + reply.commentedTime.toString()
|
||||||
val channelImage = holder.v.findViewById<ImageView>(R.id.commentor_image)
|
commentText.text =
|
||||||
Picasso.get().load(replies[position].thumbnail).fit().centerCrop().into(channelImage)
|
reply.commentText.toString()
|
||||||
holder.v.findViewById<TextView>(R.id.likes_textView).text =
|
Picasso.get().load(reply.thumbnail).fit().centerCrop().into(commentorImage)
|
||||||
replies[position].likeCount?.toLong().formatShort()
|
likesTextView.text =
|
||||||
if (replies[position].verified == true) {
|
reply.likeCount?.toLong().formatShort()
|
||||||
holder.v.findViewById<ImageView>(R.id.verified_imageView).visibility = View.VISIBLE
|
if (reply.verified == true) {
|
||||||
}
|
verifiedImageView.visibility = View.VISIBLE
|
||||||
if (replies[position].pinned == true) {
|
}
|
||||||
holder.v.findViewById<ImageView>(R.id.pinned_imageView).visibility = View.VISIBLE
|
if (reply.pinned == true) {
|
||||||
}
|
pinnedImageView.visibility = View.VISIBLE
|
||||||
if (replies[position].hearted == true) {
|
}
|
||||||
holder.v.findViewById<ImageView>(R.id.hearted_imageView).visibility = View.VISIBLE
|
if (reply.hearted == true) {
|
||||||
}
|
heartedImageView.visibility = View.VISIBLE
|
||||||
channelImage.setOnClickListener {
|
}
|
||||||
val activity = holder.v.context as MainActivity
|
commentorImage.setOnClickListener {
|
||||||
val bundle = bundleOf("channel_id" to replies[position].commentorUrl)
|
val activity = root.context as MainActivity
|
||||||
activity.navController.navigate(R.id.channel, bundle)
|
val bundle = bundleOf("channel_id" to reply.commentorUrl)
|
||||||
try {
|
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||||
val mainMotionLayout = activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
try {
|
||||||
if (mainMotionLayout.progress == 0.toFloat()) {
|
val mainMotionLayout =
|
||||||
mainMotionLayout.transitionToEnd()
|
activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||||
activity.findViewById<MotionLayout>(R.id.playerMotionLayout).transitionToEnd()
|
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) {
|
class RepliesViewHolder(val binding: RepliesRowBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
init {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,16 +3,16 @@ package com.github.libretube.adapters
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.libretube.MainActivity
|
|
||||||
import com.github.libretube.R
|
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.PlaylistOptionsDialog
|
||||||
import com.github.libretube.dialogs.VideoOptionsDialog
|
import com.github.libretube.dialogs.VideoOptionsDialog
|
||||||
import com.github.libretube.fragments.PlayerFragment
|
import com.github.libretube.fragments.PlayerFragment
|
||||||
@ -27,7 +27,7 @@ class SearchAdapter(
|
|||||||
RecyclerView.Adapter<SearchViewHolder>() {
|
RecyclerView.Adapter<SearchViewHolder>() {
|
||||||
|
|
||||||
fun updateItems(newItems: List<SearchItem>) {
|
fun updateItems(newItems: List<SearchItem>) {
|
||||||
var searchItemsSize = searchItems.size
|
val searchItemsSize = searchItems.size
|
||||||
searchItems.addAll(newItems)
|
searchItems.addAll(newItems)
|
||||||
notifyItemRangeInserted(searchItemsSize, newItems.size)
|
notifyItemRangeInserted(searchItemsSize, newItems.size)
|
||||||
}
|
}
|
||||||
@ -37,19 +37,32 @@ class SearchAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
|
||||||
val layout = when (viewType) {
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
0 -> R.layout.video_search_row
|
|
||||||
1 -> R.layout.channel_search_row
|
return when (viewType) {
|
||||||
2 -> R.layout.playlist_search_row
|
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")
|
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) {
|
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 {
|
override fun getItemViewType(position: Int): Int {
|
||||||
@ -60,117 +73,110 @@ class SearchAdapter(
|
|||||||
else -> 3
|
else -> 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class SearchViewHolder(
|
private fun bindWatch(item: SearchItem, binding: VideoSearchRowBinding) {
|
||||||
private val v: View,
|
binding.apply {
|
||||||
private val childFragmentManager: FragmentManager
|
Picasso.get().load(item.thumbnail).fit().centerCrop().into(searchThumbnail)
|
||||||
) : RecyclerView.ViewHolder(v) {
|
if (item.duration != -1L) {
|
||||||
|
searchThumbnailDuration.text = DateUtils.formatElapsedTime(item.duration!!)
|
||||||
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"
|
|
||||||
} else {
|
} 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) {
|
private fun bindChannel(item: SearchItem, binding: ChannelSearchRowBinding) {
|
||||||
val channelImage = v.findViewById<ImageView>(R.id.search_channel_image)
|
binding.apply {
|
||||||
Picasso.get().load(item.thumbnail).fit().centerCrop().into(channelImage)
|
Picasso.get().load(item.thumbnail).fit().centerCrop().into(searchChannelImage)
|
||||||
val channelName = v.findViewById<TextView>(R.id.search_channel_name)
|
searchChannelName.text = item.name
|
||||||
channelName.text = item.name
|
searchViews.text = root.context.getString(
|
||||||
val channelViews = v.findViewById<TextView>(R.id.search_views)
|
R.string.subscribers,
|
||||||
channelViews.text = v.context.getString(
|
item.subscribers.formatShort()
|
||||||
R.string.subscribers,
|
) + " • " + root.context.getString(R.string.videoCount, item.videos.toString())
|
||||||
item.subscribers.formatShort()
|
root.setOnClickListener {
|
||||||
) + " • " + v.context.getString(R.string.videoCount, item.videos.toString())
|
val activity = root.context as MainActivity
|
||||||
v.setOnClickListener {
|
val bundle = bundleOf("channel_id" to item.url)
|
||||||
val activity = v.context as MainActivity
|
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bind(searchItem: SearchItem) {
|
private fun bindPlaylist(item: SearchItem, binding: PlaylistSearchRowBinding) {
|
||||||
when {
|
binding.apply {
|
||||||
searchItem.url!!.startsWith("/watch", false) -> bindWatch(searchItem)
|
Picasso.get().load(item.thumbnail).fit().centerCrop().into(searchThumbnail)
|
||||||
searchItem.url!!.startsWith("/channel", false) -> bindChannel(searchItem)
|
if (item.videos?.toInt() != -1) searchPlaylistNumber.text = item.videos.toString()
|
||||||
searchItem.url!!.startsWith("/playlist", false) -> bindPlaylist(searchItem)
|
searchDescription.text = item.name
|
||||||
else -> {
|
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.content.Context
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
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.fragments.SearchFragment
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
|
|
||||||
class SearchHistoryAdapter(
|
class SearchHistoryAdapter(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
@ -26,28 +23,28 @@ class SearchHistoryAdapter(
|
|||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchHistoryViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchHistoryViewHolder {
|
||||||
val layoutInflater = LayoutInflater.from(parent.context)
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
val cell = layoutInflater.inflate(R.layout.searchhistory_row, parent, false)
|
val binding = SearchhistoryRowBinding.inflate(layoutInflater, parent, false)
|
||||||
return SearchHistoryViewHolder(cell)
|
return SearchHistoryViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: SearchHistoryViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: SearchHistoryViewHolder, position: Int) {
|
||||||
val history = historyList[position]
|
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 {
|
deleteHistory.setOnClickListener {
|
||||||
historyList = historyList - history
|
historyList = historyList - history
|
||||||
PreferenceHelper.saveHistory(context, historyList)
|
PreferenceHelper.saveHistory(context, historyList)
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.v.setOnClickListener {
|
root.setOnClickListener {
|
||||||
editText.setText(history)
|
editText.setText(history)
|
||||||
searchFragment.fetchSearch(history)
|
searchFragment.fetchSearch(history)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SearchHistoryViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
class SearchHistoryViewHolder(val binding: SearchhistoryRowBinding) :
|
||||||
init {
|
RecyclerView.ViewHolder(binding.root)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
package com.github.libretube.adapters
|
package com.github.libretube.adapters
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.libretube.R
|
import com.github.libretube.databinding.SearchsuggestionRowBinding
|
||||||
import com.github.libretube.fragments.SearchFragment
|
import com.github.libretube.fragments.SearchFragment
|
||||||
|
|
||||||
class SearchSuggestionsAdapter(
|
class SearchSuggestionsAdapter(
|
||||||
@ -16,28 +14,29 @@ class SearchSuggestionsAdapter(
|
|||||||
) :
|
) :
|
||||||
RecyclerView.Adapter<SearchSuggestionsViewHolder>() {
|
RecyclerView.Adapter<SearchSuggestionsViewHolder>() {
|
||||||
|
|
||||||
|
private val TAG = "SearchSuggestionsAdapter"
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return suggestionsList.size
|
return suggestionsList.size
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchSuggestionsViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchSuggestionsViewHolder {
|
||||||
val layoutInflater = LayoutInflater.from(parent.context)
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
val cell = layoutInflater.inflate(R.layout.searchsuggestion_row, parent, false)
|
val binding = SearchsuggestionRowBinding.inflate(layoutInflater, parent, false)
|
||||||
return SearchSuggestionsViewHolder(cell)
|
return SearchSuggestionsViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: SearchSuggestionsViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: SearchSuggestionsViewHolder, position: Int) {
|
||||||
val suggestion = suggestionsList[position]
|
val suggestion = suggestionsList[position]
|
||||||
val suggestionTextView = holder.v.findViewById<TextView>(R.id.suggestion_text)
|
holder.binding.apply {
|
||||||
suggestionTextView.text = suggestion
|
suggestionText.text = suggestion
|
||||||
holder.v.setOnClickListener {
|
root.setOnClickListener {
|
||||||
editText.setText(suggestion)
|
editText.setText(suggestion)
|
||||||
searchFragment.fetchSearch(editText.text.toString())
|
searchFragment.fetchSearch(editText.text.toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SearchSuggestionsViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
class SearchSuggestionsViewHolder(val binding: SearchsuggestionRowBinding) :
|
||||||
init {
|
RecyclerView.ViewHolder(binding.root)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,17 +3,15 @@ package com.github.libretube.adapters
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.libretube.MainActivity
|
|
||||||
import com.github.libretube.R
|
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.dialogs.VideoOptionsDialog
|
||||||
import com.github.libretube.fragments.PlayerFragment
|
import com.github.libretube.fragments.PlayerFragment
|
||||||
import com.github.libretube.obj.StreamItem
|
import com.github.libretube.obj.StreamItem
|
||||||
@ -23,16 +21,15 @@ import com.squareup.picasso.Picasso
|
|||||||
class SubscriptionAdapter(
|
class SubscriptionAdapter(
|
||||||
private val videoFeed: List<StreamItem>,
|
private val videoFeed: List<StreamItem>,
|
||||||
private val childFragmentManager: FragmentManager
|
private val childFragmentManager: FragmentManager
|
||||||
) :
|
) : RecyclerView.Adapter<SubscriptionViewHolder>() {
|
||||||
RecyclerView.Adapter<SubscriptionViewHolder>() {
|
private val TAG = "SubscriptionAdapter"
|
||||||
// private var limitedVideoFeed: MutableList<String> = [""].toMutableList()
|
|
||||||
var i = 0
|
var i = 0
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateItems() {
|
fun updateItems() {
|
||||||
// limitedVideoFeed.add("")
|
|
||||||
i += 10
|
i += 10
|
||||||
if (i > videoFeed.size) {
|
if (i > videoFeed.size) {
|
||||||
i = videoFeed.size
|
i = videoFeed.size
|
||||||
@ -42,64 +39,63 @@ class SubscriptionAdapter(
|
|||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubscriptionViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubscriptionViewHolder {
|
||||||
val layoutInflater = LayoutInflater.from(parent.context)
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
val cell = layoutInflater.inflate(R.layout.trending_row, parent, false)
|
val binding = TrendingRowBinding.inflate(layoutInflater, parent, false)
|
||||||
return SubscriptionViewHolder(cell)
|
return SubscriptionViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: SubscriptionViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: SubscriptionViewHolder, position: Int) {
|
||||||
val trending = videoFeed[position]
|
val trending = videoFeed[position]
|
||||||
holder.v.findViewById<TextView>(R.id.textView_title).text = trending.title
|
holder.binding.apply {
|
||||||
holder.v.findViewById<TextView>(R.id.textView_channel).text =
|
textViewTitle.text = trending.title
|
||||||
trending.uploaderName + " • " +
|
textViewChannel.text =
|
||||||
trending.views.formatShort() + " • " +
|
trending.uploaderName + " • " +
|
||||||
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
|
trending.views.formatShort() + " • " +
|
||||||
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.thumbnail)
|
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
|
||||||
val thumbnailDuration = holder.v.findViewById<TextView>(R.id.thumbnail_duration)
|
if (trending.duration != -1L) {
|
||||||
if (trending.duration != -1L) {
|
thumbnailDuration.text = DateUtils.formatElapsedTime(trending.duration!!)
|
||||||
thumbnailDuration.text = DateUtils.formatElapsedTime(trending.duration!!)
|
} else {
|
||||||
} else {
|
thumbnailDuration.text = root.context.getString(R.string.live)
|
||||||
thumbnailDuration.text = holder.v.context.getString(R.string.live)
|
thumbnailDuration.setBackgroundColor(R.attr.colorPrimaryDark)
|
||||||
thumbnailDuration.setBackgroundColor(R.attr.colorPrimaryDark)
|
}
|
||||||
}
|
channelImage.setOnClickListener {
|
||||||
val channelImage = holder.v.findViewById<ImageView>(R.id.channel_image)
|
val activity = root.context as MainActivity
|
||||||
channelImage.setOnClickListener {
|
val bundle = bundleOf("channel_id" to trending.uploaderUrl)
|
||||||
val activity = holder.v.context as MainActivity
|
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||||
val bundle = bundleOf("channel_id" to trending.uploaderUrl)
|
try {
|
||||||
activity.navController.navigate(R.id.channel, bundle)
|
val mainMotionLayout =
|
||||||
try {
|
activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||||
val mainMotionLayout = activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
if (mainMotionLayout.progress == 0.toFloat()) {
|
||||||
if (mainMotionLayout.progress == 0.toFloat()) {
|
mainMotionLayout.transitionToEnd()
|
||||||
mainMotionLayout.transitionToEnd()
|
activity.findViewById<MotionLayout>(R.id.playerMotionLayout)
|
||||||
activity.findViewById<MotionLayout>(R.id.playerMotionLayout).transitionToEnd()
|
.transitionToEnd()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} 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) {
|
class SubscriptionViewHolder(val binding: TrendingRowBinding) :
|
||||||
init {
|
RecyclerView.ViewHolder(binding.root)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,17 +3,15 @@ package com.github.libretube.adapters
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.libretube.MainActivity
|
|
||||||
import com.github.libretube.R
|
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.Subscribe
|
||||||
import com.github.libretube.obj.Subscription
|
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.github.libretube.util.RetrofitInstance
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -25,8 +23,10 @@ import java.io.IOException
|
|||||||
class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscription>) :
|
class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscription>) :
|
||||||
RecyclerView.Adapter<SubscriptionChannelViewHolder>() {
|
RecyclerView.Adapter<SubscriptionChannelViewHolder>() {
|
||||||
val TAG = "SubChannelAdapter"
|
val TAG = "SubChannelAdapter"
|
||||||
|
|
||||||
private var subscribed = true
|
private var subscribed = true
|
||||||
private var isLoading = false
|
private var isLoading = false
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return subscriptions.size
|
return subscriptions.size
|
||||||
}
|
}
|
||||||
@ -34,34 +34,32 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
|
|||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
|
||||||
SubscriptionChannelViewHolder {
|
SubscriptionChannelViewHolder {
|
||||||
val layoutInflater = LayoutInflater.from(parent.context)
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
val cell = layoutInflater.inflate(R.layout.channel_subscription_row, parent, false)
|
val binding = ChannelSubscriptionRowBinding.inflate(layoutInflater, parent, false)
|
||||||
return SubscriptionChannelViewHolder(cell)
|
return SubscriptionChannelViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: SubscriptionChannelViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: SubscriptionChannelViewHolder, position: Int) {
|
||||||
val subscription = subscriptions[position]
|
val subscription = subscriptions[position]
|
||||||
holder.v.findViewById<TextView>(R.id.subscription_channel_name).text = subscription.name
|
holder.binding.apply {
|
||||||
val avatar = holder.v.findViewById<ImageView>(R.id.subscription_channel_image)
|
subscriptionChannelName.text = subscription.name
|
||||||
Picasso.get().load(subscription.avatar).into(avatar)
|
Picasso.get().load(subscription.avatar).into(subscriptionChannelImage)
|
||||||
holder.v.setOnClickListener {
|
root.setOnClickListener {
|
||||||
val activity = holder.v.context as MainActivity
|
val activity = root.context as MainActivity
|
||||||
val bundle = bundleOf("channel_id" to subscription.url)
|
val bundle = bundleOf("channel_id" to subscription.url)
|
||||||
activity.navController.navigate(R.id.channel, bundle)
|
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||||
}
|
}
|
||||||
val subscribeBtn = holder.v
|
subscriptionSubscribe.setOnClickListener {
|
||||||
.findViewById<com.google.android.material.button.MaterialButton>(
|
if (!isLoading) {
|
||||||
R.id.subscription_subscribe
|
isLoading = true
|
||||||
)
|
val channelId = subscription.url?.replace("/channel/", "")!!
|
||||||
subscribeBtn.setOnClickListener {
|
if (subscribed) {
|
||||||
if (!isLoading) {
|
unsubscribe(root.context, channelId)
|
||||||
isLoading = true
|
subscriptionSubscribe.text = root.context.getString(R.string.subscribe)
|
||||||
val channelId = subscription.url?.replace("/channel/", "")!!
|
} else {
|
||||||
if (subscribed) {
|
subscribe(root.context, channelId)
|
||||||
unsubscribe(holder.v.context, channelId)
|
subscriptionSubscribe.text =
|
||||||
subscribeBtn.text = holder.v.context.getString(R.string.subscribe)
|
root.context.getString(R.string.unsubscribe)
|
||||||
} else {
|
}
|
||||||
subscribe(holder.v.context, channelId)
|
|
||||||
subscribeBtn.text = holder.v.context.getString(R.string.unsubscribe)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,7 +70,7 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
|
|||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val response = try {
|
val response = try {
|
||||||
val token = PreferenceHelper.getToken(context)
|
val token = PreferenceHelper.getToken(context)
|
||||||
RetrofitInstance.api.subscribe(
|
RetrofitInstance.authApi.subscribe(
|
||||||
token,
|
token,
|
||||||
Subscribe(channelId)
|
Subscribe(channelId)
|
||||||
)
|
)
|
||||||
@ -94,7 +92,7 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
|
|||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val response = try {
|
val response = try {
|
||||||
val token = PreferenceHelper.getToken(context)
|
val token = PreferenceHelper.getToken(context)
|
||||||
RetrofitInstance.api.unsubscribe(
|
RetrofitInstance.authApi.unsubscribe(
|
||||||
token,
|
token,
|
||||||
Subscribe(channelId)
|
Subscribe(channelId)
|
||||||
)
|
)
|
||||||
@ -112,7 +110,5 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SubscriptionChannelViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
class SubscriptionChannelViewHolder(val binding: ChannelSubscriptionRowBinding) :
|
||||||
init {
|
RecyclerView.ViewHolder(binding.root)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,17 +3,15 @@ package com.github.libretube.adapters
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.libretube.MainActivity
|
|
||||||
import com.github.libretube.R
|
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.dialogs.VideoOptionsDialog
|
||||||
import com.github.libretube.fragments.PlayerFragment
|
import com.github.libretube.fragments.PlayerFragment
|
||||||
import com.github.libretube.obj.StreamItem
|
import com.github.libretube.obj.StreamItem
|
||||||
@ -24,77 +22,75 @@ class TrendingAdapter(
|
|||||||
private val videoFeed: List<StreamItem>,
|
private val videoFeed: List<StreamItem>,
|
||||||
private val childFragmentManager: FragmentManager
|
private val childFragmentManager: FragmentManager
|
||||||
) : RecyclerView.Adapter<TrendingViewHolder>() {
|
) : RecyclerView.Adapter<TrendingViewHolder>() {
|
||||||
|
private val TAG = "TrendingAdapter"
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return videoFeed.size
|
return videoFeed.size
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrendingViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrendingViewHolder {
|
||||||
val layoutInflater = LayoutInflater.from(parent.context)
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
val cell = layoutInflater.inflate(R.layout.trending_row, parent, false)
|
val binding = TrendingRowBinding.inflate(layoutInflater, parent, false)
|
||||||
return TrendingViewHolder(cell)
|
return TrendingViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: TrendingViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: TrendingViewHolder, position: Int) {
|
||||||
val trending = videoFeed[position]
|
val trending = videoFeed[position]
|
||||||
holder.v.findViewById<TextView>(R.id.textView_title).text = trending.title
|
holder.binding.apply {
|
||||||
holder.v.findViewById<TextView>(R.id.textView_channel).text =
|
textViewTitle.text = trending.title
|
||||||
trending.uploaderName + " • " +
|
textViewChannel.text =
|
||||||
trending.views.formatShort() + " • " +
|
trending.uploaderName + " • " +
|
||||||
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
|
trending.views.formatShort() + " • " +
|
||||||
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.thumbnail)
|
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
|
||||||
val thumbnailDuration = holder.v.findViewById<TextView>(R.id.thumbnail_duration)
|
if (trending.duration != -1L) {
|
||||||
if (trending.duration != -1L) {
|
thumbnailDuration.text = DateUtils.formatElapsedTime(trending.duration!!)
|
||||||
thumbnailDuration.text = DateUtils.formatElapsedTime(trending.duration!!)
|
} else {
|
||||||
} else {
|
thumbnailDuration.text = root.context.getString(R.string.live)
|
||||||
thumbnailDuration.text = holder.v.context.getString(R.string.live)
|
thumbnailDuration.setBackgroundColor(R.attr.colorPrimaryDark)
|
||||||
thumbnailDuration.setBackgroundColor(R.attr.colorPrimaryDark)
|
}
|
||||||
}
|
channelImage.setOnClickListener {
|
||||||
val channelImage = holder.v.findViewById<ImageView>(R.id.channel_image)
|
val activity = root.context as MainActivity
|
||||||
channelImage.setOnClickListener {
|
val bundle = bundleOf("channel_id" to trending.uploaderUrl)
|
||||||
val activity = holder.v.context as MainActivity
|
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||||
val bundle = bundleOf("channel_id" to trending.uploaderUrl)
|
try {
|
||||||
activity.navController.navigate(R.id.channel, bundle)
|
val mainMotionLayout =
|
||||||
try {
|
activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||||
val mainMotionLayout = activity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
if (mainMotionLayout.progress == 0.toFloat()) {
|
||||||
if (mainMotionLayout.progress == 0.toFloat()) {
|
mainMotionLayout.transitionToEnd()
|
||||||
mainMotionLayout.transitionToEnd()
|
activity.findViewById<MotionLayout>(R.id.playerMotionLayout)
|
||||||
activity.findViewById<MotionLayout>(R.id.playerMotionLayout).transitionToEnd()
|
.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) {
|
class TrendingViewHolder(val binding: TrendingRowBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
init {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Button
|
|
||||||
import android.widget.Spinner
|
|
||||||
import android.widget.TextView
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.text.HtmlCompat
|
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.databinding.DialogAddtoplaylistBinding
|
||||||
import com.github.libretube.obj.PlaylistId
|
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.RetrofitInstance
|
||||||
|
import com.github.libretube.util.ThemeHelper
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class AddtoPlaylistDialog : DialogFragment() {
|
class AddtoPlaylistDialog : DialogFragment() {
|
||||||
private val TAG = "AddToPlaylistDialog"
|
private val TAG = "AddToPlaylistDialog"
|
||||||
|
private lateinit var binding: DialogAddtoplaylistBinding
|
||||||
|
|
||||||
private lateinit var videoId: String
|
private lateinit var videoId: String
|
||||||
private lateinit var token: String
|
private lateinit var token: String
|
||||||
private lateinit var spinner: Spinner
|
|
||||||
private lateinit var button: Button
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
return activity?.let {
|
return activity?.let {
|
||||||
videoId = arguments?.getString("videoId")!!
|
videoId = arguments?.getString("videoId")!!
|
||||||
val builder = MaterialAlertDialogBuilder(it)
|
val builder = MaterialAlertDialogBuilder(it)
|
||||||
// Get the layout inflater
|
// Get the layout inflater
|
||||||
val inflater = requireActivity().layoutInflater
|
binding = DialogAddtoplaylistBinding.inflate(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
|
|
||||||
|
|
||||||
builder.setView(view)
|
token = PreferenceHelper.getToken(requireContext())
|
||||||
|
|
||||||
|
if (token != "") fetchPlaylists()
|
||||||
|
|
||||||
|
binding.title.text = ThemeHelper.getStyledAppName(requireContext())
|
||||||
|
|
||||||
|
builder.setView(binding.root)
|
||||||
builder.create()
|
builder.create()
|
||||||
} ?: throw IllegalStateException("Activity cannot be null")
|
} ?: throw IllegalStateException("Activity cannot be null")
|
||||||
}
|
}
|
||||||
@ -59,7 +47,7 @@ class AddtoPlaylistDialog : DialogFragment() {
|
|||||||
fun run() {
|
fun run() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.playlists(token)
|
RetrofitInstance.authApi.playlists(token)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
println(e)
|
println(e)
|
||||||
Log.e(TAG, "IOException, you might not have internet connection")
|
Log.e(TAG, "IOException, you might not have internet connection")
|
||||||
@ -80,10 +68,12 @@ class AddtoPlaylistDialog : DialogFragment() {
|
|||||||
arrayAdapter.setDropDownViewResource(
|
arrayAdapter.setDropDownViewResource(
|
||||||
android.R.layout.simple_spinner_dropdown_item
|
android.R.layout.simple_spinner_dropdown_item
|
||||||
)
|
)
|
||||||
spinner.adapter = arrayAdapter
|
binding.playlistsSpinner.adapter = arrayAdapter
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
button.setOnClickListener {
|
binding.addToPlaylist.setOnClickListener {
|
||||||
addToPlaylist(response[spinner.selectedItemPosition].id!!)
|
addToPlaylist(
|
||||||
|
response[binding.playlistsSpinner.selectedItemPosition].id!!
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -97,7 +87,7 @@ class AddtoPlaylistDialog : DialogFragment() {
|
|||||||
fun run() {
|
fun run() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.addToPlaylist(token, PlaylistId(playlistId, videoId))
|
RetrofitInstance.authApi.addToPlaylist(token, PlaylistId(playlistId, videoId))
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
println(e)
|
println(e)
|
||||||
Log.e(TAG, "IOException, you might not have internet connection")
|
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.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
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 android.widget.Toast
|
||||||
import androidx.core.text.HtmlCompat
|
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.github.libretube.R
|
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.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.RetrofitInstance
|
||||||
|
import com.github.libretube.util.ThemeHelper
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.textfield.TextInputEditText
|
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class CreatePlaylistDialog : DialogFragment() {
|
class CreatePlaylistDialog : DialogFragment() {
|
||||||
val TAG = "CreatePlaylistDialog"
|
val TAG = "CreatePlaylistDialog"
|
||||||
private var token: String = ""
|
private var token: String = ""
|
||||||
|
private lateinit var binding: DialogCreatePlaylistBinding
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
return activity?.let {
|
return activity?.let {
|
||||||
val builder = MaterialAlertDialogBuilder(it)
|
val builder = MaterialAlertDialogBuilder(it)
|
||||||
val inflater = requireActivity().layoutInflater
|
binding = DialogCreatePlaylistBinding.inflate(layoutInflater)
|
||||||
val view: View = inflater.inflate(R.layout.dialog_create_playlist, null)
|
|
||||||
|
|
||||||
val typedValue = TypedValue()
|
binding.title.text = ThemeHelper.getStyledAppName(requireContext())
|
||||||
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
|
|
||||||
|
|
||||||
val cancelBtn = view.findViewById<Button>(R.id.cancel_button)
|
binding.cancelButton.setOnClickListener {
|
||||||
cancelBtn.setOnClickListener {
|
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
token = PreferenceHelper.getToken(requireContext())
|
token = PreferenceHelper.getToken(requireContext())
|
||||||
|
|
||||||
val playlistName = view.findViewById<TextInputEditText>(R.id.playlist_name)
|
binding.createNewPlaylist.setOnClickListener {
|
||||||
val createPlaylistBtn = view.findViewById<Button>(R.id.create_new_playlist)
|
|
||||||
createPlaylistBtn.setOnClickListener {
|
|
||||||
// avoid creating the same playlist multiple times by spamming the button
|
// avoid creating the same playlist multiple times by spamming the button
|
||||||
createPlaylistBtn.setOnClickListener(null)
|
binding.createNewPlaylist.setOnClickListener(null)
|
||||||
val listName = playlistName.text.toString()
|
val listName = binding.playlistName.text.toString()
|
||||||
if (listName != "") {
|
if (listName != "") {
|
||||||
createPlaylist(listName)
|
createPlaylist(listName)
|
||||||
} else {
|
} else {
|
||||||
@ -60,7 +46,7 @@ class CreatePlaylistDialog : DialogFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.setView(view)
|
builder.setView(binding.root)
|
||||||
builder.create()
|
builder.create()
|
||||||
} ?: throw IllegalStateException("Activity cannot be null")
|
} ?: throw IllegalStateException("Activity cannot be null")
|
||||||
}
|
}
|
||||||
@ -69,7 +55,7 @@ class CreatePlaylistDialog : DialogFragment() {
|
|||||||
fun run() {
|
fun run() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.createPlaylist(token, Playlists(name = name))
|
RetrofitInstance.authApi.createPlaylist(token, Playlists(name = name))
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
println(e)
|
println(e)
|
||||||
Log.e(TAG, "IOException, you might not have internet connection")
|
Log.e(TAG, "IOException, you might not have internet connection")
|
||||||
@ -88,7 +74,7 @@ class CreatePlaylistDialog : DialogFragment() {
|
|||||||
}
|
}
|
||||||
// refresh the playlists in the library
|
// refresh the playlists in the library
|
||||||
try {
|
try {
|
||||||
val parent = parentFragment as Library
|
val parent = parentFragment as LibraryFragment
|
||||||
parent.fetchPlaylists()
|
parent.fetchPlaylists()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, e.toString())
|
Log.e(TAG, e.toString())
|
||||||
|
@ -2,45 +2,34 @@ package com.github.libretube.dialogs
|
|||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
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 android.widget.Toast
|
||||||
import androidx.core.text.HtmlCompat
|
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.databinding.DialogCustomInstanceBinding
|
||||||
import com.github.libretube.obj.CustomInstance
|
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.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.textfield.TextInputEditText
|
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
class CustomInstanceDialog : DialogFragment() {
|
class CustomInstanceDialog : DialogFragment() {
|
||||||
val TAG = "CustomInstanceDialog"
|
val TAG = "CustomInstanceDialog"
|
||||||
|
private lateinit var binding: DialogCustomInstanceBinding
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
return activity?.let {
|
return activity?.let {
|
||||||
val builder = MaterialAlertDialogBuilder(it)
|
val builder = MaterialAlertDialogBuilder(it)
|
||||||
val inflater = requireActivity().layoutInflater
|
binding = DialogCustomInstanceBinding.inflate(layoutInflater)
|
||||||
val view: View = inflater.inflate(R.layout.dialog_custom_instance, null)
|
|
||||||
|
|
||||||
val instanceNameEditText = view.findViewById<TextInputEditText>(R.id.instanceName)
|
binding.cancel.setOnClickListener {
|
||||||
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 {
|
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
addInstanceButton.setOnClickListener {
|
binding.addInstance.setOnClickListener {
|
||||||
val customInstance = CustomInstance()
|
val customInstance = CustomInstance()
|
||||||
customInstance.name = instanceNameEditText.text.toString()
|
customInstance.name = binding.instanceName.text.toString()
|
||||||
customInstance.apiUrl = instanceApiUrlEditText.text.toString()
|
customInstance.apiUrl = binding.instanceApiUrl.text.toString()
|
||||||
customInstance.frontendUrl = instanceFrontendUrlEditText.text.toString()
|
customInstance.frontendUrl = binding.instanceFrontendUrl.text.toString()
|
||||||
|
|
||||||
if (
|
if (
|
||||||
customInstance.name != "" &&
|
customInstance.name != "" &&
|
||||||
@ -73,16 +62,9 @@ class CustomInstanceDialog : DialogFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val typedValue = TypedValue()
|
binding.title.text = ThemeHelper.getStyledAppName(requireContext())
|
||||||
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()
|
builder.create()
|
||||||
} ?: throw IllegalStateException("Activity cannot be null")
|
} ?: throw IllegalStateException("Activity cannot be null")
|
||||||
}
|
}
|
||||||
|
@ -3,54 +3,42 @@ package com.github.libretube.dialogs
|
|||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
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 android.widget.Toast
|
||||||
import androidx.core.text.HtmlCompat
|
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.github.libretube.R
|
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.obj.DeleteUserRequest
|
||||||
import com.github.libretube.requireMainActivityRestart
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
import com.github.libretube.util.PreferenceHelper
|
|
||||||
import com.github.libretube.util.RetrofitInstance
|
import com.github.libretube.util.RetrofitInstance
|
||||||
|
import com.github.libretube.util.ThemeHelper
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
|
||||||
class DeleteAccountDialog : DialogFragment() {
|
class DeleteAccountDialog : DialogFragment() {
|
||||||
private val TAG = "DeleteAccountDialog"
|
private val TAG = "DeleteAccountDialog"
|
||||||
lateinit var username: EditText
|
private lateinit var binding: DialogDeleteAccountBinding
|
||||||
lateinit var password: EditText
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
return activity?.let {
|
return activity?.let {
|
||||||
val builder = MaterialAlertDialogBuilder(it)
|
val builder = MaterialAlertDialogBuilder(it)
|
||||||
val inflater = requireActivity().layoutInflater
|
binding = DialogDeleteAccountBinding.inflate(layoutInflater)
|
||||||
val view = inflater.inflate(R.layout.dialog_delete_account, null)
|
|
||||||
|
|
||||||
view.findViewById<Button>(R.id.cancel_button).setOnClickListener {
|
binding.cancelButton.setOnClickListener {
|
||||||
dialog?.dismiss()
|
dialog?.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
password = view.findViewById(R.id.delete_password)
|
binding.deleteAccountConfirm.setOnClickListener {
|
||||||
view.findViewById<Button>(R.id.delete_account_confirm).setOnClickListener {
|
if (binding.deletePassword.text.toString() != "") {
|
||||||
if (password.text.toString() != "") {
|
deleteAccount(binding.deletePassword.text.toString())
|
||||||
deleteAccount(password.text.toString())
|
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(context, R.string.empty, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, R.string.empty, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val typedValue = TypedValue()
|
binding.title.text = ThemeHelper.getStyledAppName(requireContext())
|
||||||
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()
|
builder.create()
|
||||||
} ?: throw IllegalStateException("Activity cannot be null")
|
} ?: throw IllegalStateException("Activity cannot be null")
|
||||||
}
|
}
|
||||||
@ -61,7 +49,7 @@ class DeleteAccountDialog : DialogFragment() {
|
|||||||
val token = PreferenceHelper.getToken(requireContext())
|
val token = PreferenceHelper.getToken(requireContext())
|
||||||
|
|
||||||
try {
|
try {
|
||||||
RetrofitInstance.api.deleteAccount(token, DeleteUserRequest(password))
|
RetrofitInstance.authApi.deleteAccount(token, DeleteUserRequest(password))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, e.toString())
|
Log.e(TAG, e.toString())
|
||||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
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.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Button
|
import android.widget.Toast
|
||||||
import android.widget.Spinner
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.text.HtmlCompat
|
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.github.libretube.MainActivity
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.github.libretube.R
|
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.obj.Streams
|
||||||
import com.github.libretube.services.DownloadService
|
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 com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import retrofit2.HttpException
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
class DownloadDialog : DialogFragment() {
|
class DownloadDialog : DialogFragment() {
|
||||||
private val TAG = "DownloadDialog"
|
private val TAG = "DownloadDialog"
|
||||||
|
private lateinit var binding: DialogDownloadBinding
|
||||||
|
|
||||||
private lateinit var streams: Streams
|
private lateinit var streams: Streams
|
||||||
private lateinit var videoId: String
|
private lateinit var videoId: String
|
||||||
@ -32,14 +34,13 @@ class DownloadDialog : DialogFragment() {
|
|||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
return activity?.let {
|
return activity?.let {
|
||||||
streams = arguments?.getParcelable("streams")!!
|
|
||||||
videoId = arguments?.getString("video_id")!!
|
videoId = arguments?.getString("video_id")!!
|
||||||
|
|
||||||
val mainActivity = activity as MainActivity
|
val mainActivity = activity as MainActivity
|
||||||
val builder = MaterialAlertDialogBuilder(it)
|
val builder = MaterialAlertDialogBuilder(it)
|
||||||
// Get the layout inflater
|
binding = DialogDownloadBinding.inflate(layoutInflater)
|
||||||
val inflater = requireActivity().layoutInflater
|
|
||||||
var view: View = inflater.inflate(R.layout.dialog_download, null)
|
fetchStreams()
|
||||||
|
|
||||||
// request storage permissions if not granted yet
|
// request storage permissions if not granted yet
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
@ -75,78 +76,89 @@ class DownloadDialog : DialogFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var vidName = arrayListOf<String>()
|
binding.title.text = ThemeHelper.getStyledAppName(requireContext())
|
||||||
var vidUrl = arrayListOf<String>()
|
|
||||||
|
|
||||||
// add empty selection
|
builder.setView(binding.root)
|
||||||
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.create()
|
builder.create()
|
||||||
} ?: throw IllegalStateException("Activity cannot be null")
|
} ?: 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.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
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 android.widget.Toast
|
||||||
import androidx.core.text.HtmlCompat
|
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.github.libretube.R
|
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.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.RetrofitInstance
|
||||||
|
import com.github.libretube.util.ThemeHelper
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class LoginDialog : DialogFragment() {
|
class LoginDialog : DialogFragment() {
|
||||||
private val TAG = "LoginDialog"
|
private val TAG = "LoginDialog"
|
||||||
lateinit var username: EditText
|
private lateinit var binding: DialogLoginBinding
|
||||||
lateinit var password: EditText
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
return activity?.let {
|
return activity?.let {
|
||||||
val builder = MaterialAlertDialogBuilder(it)
|
val builder = MaterialAlertDialogBuilder(it)
|
||||||
// Get the layout inflater
|
// Get the layout inflater
|
||||||
val inflater = requireActivity().layoutInflater
|
binding = DialogLoginBinding.inflate(layoutInflater)
|
||||||
val token = PreferenceHelper.getToken(requireContext())
|
|
||||||
var view: View
|
binding.login.setOnClickListener {
|
||||||
Log.e("dafaq", token!!)
|
if (binding.username.text.toString() != "" && binding.password.text.toString() != "") {
|
||||||
if (token != "") {
|
val login =
|
||||||
val user = PreferenceHelper.getUsername(requireContext())
|
Login(binding.username.text.toString(), binding.password.text.toString())
|
||||||
view = inflater.inflate(R.layout.dialog_logout, null)
|
login(login)
|
||||||
view.findViewById<TextView>(R.id.user).text =
|
} else {
|
||||||
view.findViewById<TextView>(R.id.user).text.toString() + " (" + user + ")"
|
Toast.makeText(context, R.string.empty, Toast.LENGTH_SHORT).show()
|
||||||
view.findViewById<Button>(R.id.logout).setOnClickListener {
|
|
||||||
Toast.makeText(context, R.string.loggedout, Toast.LENGTH_SHORT).show()
|
|
||||||
PreferenceHelper.setToken(requireContext(), "")
|
|
||||||
dialog?.dismiss()
|
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
view = inflater.inflate(R.layout.dialog_login, null)
|
binding.register.setOnClickListener {
|
||||||
username = view.findViewById(R.id.username)
|
if (
|
||||||
password = view.findViewById(R.id.password)
|
binding.username.text.toString() != "" &&
|
||||||
view.findViewById<Button>(R.id.login).setOnClickListener {
|
binding.password.text.toString() != ""
|
||||||
if (username.text.toString() != "" && password.text.toString() != "") {
|
) {
|
||||||
val login = Login(username.text.toString(), password.text.toString())
|
val login = Login(
|
||||||
login(login)
|
binding.username.text.toString(),
|
||||||
} else {
|
binding.password.text.toString()
|
||||||
Toast.makeText(context, R.string.empty, Toast.LENGTH_SHORT).show()
|
)
|
||||||
}
|
register(login)
|
||||||
}
|
} else {
|
||||||
view.findViewById<Button>(R.id.register).setOnClickListener {
|
Toast.makeText(context, R.string.empty, Toast.LENGTH_SHORT).show()
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val typedValue = TypedValue()
|
binding.title.text = ThemeHelper.getStyledAppName(requireContext())
|
||||||
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()
|
builder.create()
|
||||||
} ?: throw IllegalStateException("Activity cannot be null")
|
} ?: throw IllegalStateException("Activity cannot be null")
|
||||||
}
|
}
|
||||||
@ -82,7 +62,7 @@ class LoginDialog : DialogFragment() {
|
|||||||
fun run() {
|
fun run() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.login(login)
|
RetrofitInstance.authApi.login(login)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
println(e)
|
println(e)
|
||||||
Log.e(TAG, "IOException, you might not have internet connection")
|
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()
|
Toast.makeText(context, R.string.loggedIn, Toast.LENGTH_SHORT).show()
|
||||||
PreferenceHelper.setToken(requireContext(), response.token!!)
|
PreferenceHelper.setToken(requireContext(), response.token!!)
|
||||||
PreferenceHelper.setUsername(requireContext(), login.username!!)
|
PreferenceHelper.setUsername(requireContext(), login.username!!)
|
||||||
|
requireMainActivityRestart = true
|
||||||
dialog?.dismiss()
|
dialog?.dismiss()
|
||||||
|
activity?.recreate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,7 +95,7 @@ class LoginDialog : DialogFragment() {
|
|||||||
fun run() {
|
fun run() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.register(login)
|
RetrofitInstance.authApi.register(login)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
println(e)
|
println(e)
|
||||||
Log.e(TAG, "IOException, you might not have internet connection")
|
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 androidx.fragment.app.DialogFragment
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.obj.PlaylistId
|
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.RetrofitInstance
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -20,16 +20,23 @@ import java.io.IOException
|
|||||||
|
|
||||||
class PlaylistOptionsDialog(
|
class PlaylistOptionsDialog(
|
||||||
private val playlistId: String,
|
private val playlistId: String,
|
||||||
|
private val isOwner: Boolean,
|
||||||
context: Context
|
context: Context
|
||||||
) : DialogFragment() {
|
) : DialogFragment() {
|
||||||
val TAG = "PlaylistOptionsDialog"
|
val TAG = "PlaylistOptionsDialog"
|
||||||
|
|
||||||
private val optionsList = listOf(
|
private var optionsList = listOf(
|
||||||
context.getString(R.string.clonePlaylist),
|
context.getString(R.string.clonePlaylist),
|
||||||
context.getString(R.string.share)
|
context.getString(R.string.share)
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
if (isOwner) {
|
||||||
|
optionsList = optionsList +
|
||||||
|
context?.getString(R.string.deletePlaylist)!! -
|
||||||
|
context?.getString(R.string.clonePlaylist)!!
|
||||||
|
}
|
||||||
|
|
||||||
val dialog = MaterialAlertDialogBuilder(requireContext())
|
val dialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
.setNegativeButton(R.string.cancel) { dialog, _ ->
|
.setNegativeButton(R.string.cancel) { dialog, _ ->
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
@ -41,12 +48,12 @@ class PlaylistOptionsDialog(
|
|||||||
optionsList
|
optionsList
|
||||||
)
|
)
|
||||||
) { _, which ->
|
) { _, which ->
|
||||||
when (which) {
|
when (optionsList[which]) {
|
||||||
// Clone the playlist to the users Piped account
|
// Clone the playlist to the users Piped account
|
||||||
0 -> {
|
context?.getString(R.string.clonePlaylist) -> {
|
||||||
val token = PreferenceHelper.getToken(requireContext())
|
val token = PreferenceHelper.getToken(requireContext())
|
||||||
if (token != "") {
|
if (token != "") {
|
||||||
importPlaylist(token!!, playlistId)
|
importPlaylist(token, playlistId)
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
context,
|
context,
|
||||||
@ -56,11 +63,15 @@ class PlaylistOptionsDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// share the playlist
|
// share the playlist
|
||||||
1 -> {
|
context?.getString(R.string.share) -> {
|
||||||
val shareDialog = ShareDialog(playlistId, true)
|
val shareDialog = ShareDialog(playlistId, true)
|
||||||
// using parentFragmentManager is important here
|
// using parentFragmentManager, childFragmentManager doesn't work here
|
||||||
shareDialog.show(parentFragmentManager, "ShareDialog")
|
shareDialog.show(parentFragmentManager, "ShareDialog")
|
||||||
}
|
}
|
||||||
|
context?.getString(R.string.deletePlaylist) -> {
|
||||||
|
val token = PreferenceHelper.getToken(requireContext())
|
||||||
|
deletePlaylist(playlistId, token)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dialog.show()
|
return dialog.show()
|
||||||
@ -70,7 +81,7 @@ class PlaylistOptionsDialog(
|
|||||||
fun run() {
|
fun run() {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.importPlaylist(token, PlaylistId(playlistId))
|
RetrofitInstance.authApi.importPlaylist(token, PlaylistId(playlistId))
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
println(e)
|
println(e)
|
||||||
return@launch
|
return@launch
|
||||||
@ -82,4 +93,22 @@ class PlaylistOptionsDialog(
|
|||||||
}
|
}
|
||||||
run()
|
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 android.os.Bundle
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
|
||||||
class ShareDialog(
|
class ShareDialog(
|
||||||
|
@ -6,9 +6,9 @@ import android.os.Bundle
|
|||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.github.libretube.BackgroundMode
|
|
||||||
import com.github.libretube.R
|
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
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,14 +43,14 @@ class VideoOptionsDialog(private val videoId: String, context: Context) : Dialog
|
|||||||
) { _, which ->
|
) { _, which ->
|
||||||
// For now, this checks the position of the option with the position that is in the
|
// 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.
|
// 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
|
// This for example will be the "Background mode" option
|
||||||
0 -> {
|
context?.getString(R.string.playOnBackground) -> {
|
||||||
BackgroundMode.getInstance()
|
BackgroundMode.getInstance()
|
||||||
.playOnBackgroundMode(requireContext(), videoId, 0)
|
.playOnBackgroundMode(requireContext(), videoId)
|
||||||
}
|
}
|
||||||
// Add Video to Playlist Dialog
|
// Add Video to Playlist Dialog
|
||||||
1 -> {
|
context?.getString(R.string.addToPlaylist) -> {
|
||||||
val token = PreferenceHelper.getToken(requireContext())
|
val token = PreferenceHelper.getToken(requireContext())
|
||||||
if (token != "") {
|
if (token != "") {
|
||||||
val newFragment = AddtoPlaylistDialog()
|
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()
|
Toast.makeText(context, R.string.login_first, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2 -> {
|
context?.getString(R.string.share) -> {
|
||||||
val shareDialog = ShareDialog(videoId, false)
|
val shareDialog = ShareDialog(videoId, false)
|
||||||
// using parentFragmentManager is important here
|
// using parentFragmentManager is important here
|
||||||
shareDialog.show(parentFragmentManager, "ShareDialog")
|
shareDialog.show(parentFragmentManager, "ShareDialog")
|
||||||
|
@ -6,18 +6,14 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.ScrollView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.adapters.ChannelAdapter
|
import com.github.libretube.adapters.ChannelAdapter
|
||||||
|
import com.github.libretube.databinding.FragmentChannelBinding
|
||||||
import com.github.libretube.obj.Subscribe
|
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.RetrofitInstance
|
||||||
import com.github.libretube.util.formatShort
|
import com.github.libretube.util.formatShort
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
@ -26,19 +22,19 @@ import retrofit2.HttpException
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class ChannelFragment : Fragment() {
|
class ChannelFragment : Fragment() {
|
||||||
|
|
||||||
private var channel_id: String? = null
|
|
||||||
private val TAG = "ChannelFragment"
|
private val TAG = "ChannelFragment"
|
||||||
|
private lateinit var binding: FragmentChannelBinding
|
||||||
|
|
||||||
|
private var channelId: String? = null
|
||||||
var nextPage: String? = null
|
var nextPage: String? = null
|
||||||
var channelAdapter: ChannelAdapter? = null
|
private var channelAdapter: ChannelAdapter? = null
|
||||||
var isLoading = true
|
private var isLoading = true
|
||||||
var isSubscribed: Boolean = false
|
private var isSubscribed: Boolean = false
|
||||||
private var refreshLayout: SwipeRefreshLayout? = null
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
arguments?.let {
|
arguments?.let {
|
||||||
channel_id = it.getString("channel_id")
|
channelId = it.getString("channel_id")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,43 +42,39 @@ class ChannelFragment : Fragment() {
|
|||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View {
|
||||||
// Inflate the layout for this fragment
|
binding = FragmentChannelBinding.inflate(layoutInflater, container, false)
|
||||||
return inflater.inflate(R.layout.fragment_channel, container, false)
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
channel_id = channel_id!!.replace("/channel/", "")
|
channelId = channelId!!.replace("/channel/", "")
|
||||||
view.findViewById<TextView>(R.id.channel_name).text = channel_id
|
binding.channelName.text = channelId
|
||||||
val recyclerView = view.findViewById<RecyclerView>(R.id.channel_recView)
|
binding.channelRecView.layoutManager = LinearLayoutManager(context)
|
||||||
recyclerView.layoutManager = LinearLayoutManager(context)
|
|
||||||
refreshLayout = view.findViewById(R.id.channel_refresh)
|
|
||||||
|
|
||||||
val refreshChannel = {
|
val refreshChannel = {
|
||||||
refreshLayout?.isRefreshing = true
|
binding.channelRefresh.isRefreshing = true
|
||||||
fetchChannel(view)
|
fetchChannel()
|
||||||
val subButton = view.findViewById<MaterialButton>(R.id.channel_subscribe)
|
|
||||||
if (PreferenceHelper.getToken(requireContext()) != "") {
|
if (PreferenceHelper.getToken(requireContext()) != "") {
|
||||||
isSubscribed(subButton)
|
isSubscribed(binding.channelSubscribe)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refreshChannel()
|
refreshChannel()
|
||||||
refreshLayout?.setOnRefreshListener {
|
binding.channelRefresh.setOnRefreshListener {
|
||||||
refreshChannel()
|
refreshChannel()
|
||||||
}
|
}
|
||||||
|
|
||||||
val scrollView = view.findViewById<ScrollView>(R.id.channel_scrollView)
|
binding.channelScrollView.viewTreeObserver
|
||||||
scrollView.viewTreeObserver
|
|
||||||
.addOnScrollChangedListener {
|
.addOnScrollChangedListener {
|
||||||
if (scrollView.getChildAt(0).bottom
|
if (binding.channelScrollView.getChildAt(0).bottom
|
||||||
== (scrollView.height + scrollView.scrollY)
|
== (binding.channelScrollView.height + binding.channelScrollView.scrollY)
|
||||||
) {
|
) {
|
||||||
// scroll view is at bottom
|
// scroll view is at bottom
|
||||||
if (nextPage != null && !isLoading) {
|
if (nextPage != null && !isLoading) {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
refreshLayout?.isRefreshing = true
|
binding.channelRefresh.isRefreshing = true
|
||||||
fetchNextPage()
|
fetchNextPage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,8 +87,8 @@ class ChannelFragment : Fragment() {
|
|||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
val token = PreferenceHelper.getToken(requireContext())
|
val token = PreferenceHelper.getToken(requireContext())
|
||||||
RetrofitInstance.api.isSubscribed(
|
RetrofitInstance.authApi.isSubscribed(
|
||||||
channel_id!!,
|
channelId!!,
|
||||||
token
|
token
|
||||||
)
|
)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
@ -135,9 +127,9 @@ class ChannelFragment : Fragment() {
|
|||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
val token = PreferenceHelper.getToken(requireContext())
|
val token = PreferenceHelper.getToken(requireContext())
|
||||||
RetrofitInstance.api.subscribe(
|
RetrofitInstance.authApi.subscribe(
|
||||||
token,
|
token,
|
||||||
Subscribe(channel_id)
|
Subscribe(channelId)
|
||||||
)
|
)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
println(e)
|
println(e)
|
||||||
@ -158,9 +150,9 @@ class ChannelFragment : Fragment() {
|
|||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
val token = PreferenceHelper.getToken(requireContext())
|
val token = PreferenceHelper.getToken(requireContext())
|
||||||
RetrofitInstance.api.unsubscribe(
|
RetrofitInstance.authApi.unsubscribe(
|
||||||
token,
|
token,
|
||||||
Subscribe(channel_id)
|
Subscribe(channelId)
|
||||||
)
|
)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
println(e)
|
println(e)
|
||||||
@ -176,55 +168,52 @@ class ChannelFragment : Fragment() {
|
|||||||
run()
|
run()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchChannel(view: View) {
|
private fun fetchChannel() {
|
||||||
fun run() {
|
fun run() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.getChannel(channel_id!!)
|
RetrofitInstance.api.getChannel(channelId!!)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
refreshLayout?.isRefreshing = false
|
binding.channelRefresh.isRefreshing = false
|
||||||
println(e)
|
println(e)
|
||||||
Log.e(TAG, "IOException, you might not have internet connection")
|
Log.e(TAG, "IOException, you might not have internet connection")
|
||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
} catch (e: HttpException) {
|
} catch (e: HttpException) {
|
||||||
refreshLayout?.isRefreshing = false
|
binding.channelRefresh.isRefreshing = false
|
||||||
Log.e(TAG, "HttpException, unexpected response")
|
Log.e(TAG, "HttpException, unexpected response")
|
||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
}
|
}
|
||||||
nextPage = response.nextpage
|
nextPage = response.nextpage
|
||||||
isLoading = false
|
isLoading = false
|
||||||
refreshLayout?.isRefreshing = false
|
binding.channelRefresh.isRefreshing = false
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
view.findViewById<ScrollView>(R.id.channel_scrollView).visibility = View.VISIBLE
|
binding.channelScrollView.visibility = View.VISIBLE
|
||||||
val channelName = view.findViewById<TextView>(R.id.channel_name)
|
binding.channelName.text = response.name
|
||||||
channelName.text = response.name
|
|
||||||
if (response.verified) {
|
if (response.verified) {
|
||||||
channelName.setCompoundDrawablesWithIntrinsicBounds(
|
binding.channelName.setCompoundDrawablesWithIntrinsicBounds(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
R.drawable.ic_verified,
|
R.drawable.ic_verified,
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
view.findViewById<TextView>(R.id.channel_subs).text = resources.getString(
|
binding.channelSubs.text = resources.getString(
|
||||||
R.string.subscribers,
|
R.string.subscribers,
|
||||||
response.subscriberCount.formatShort()
|
response.subscriberCount.formatShort()
|
||||||
)
|
)
|
||||||
val channelDescription = view.findViewById<TextView>(R.id.channel_description)
|
|
||||||
if (response.description?.trim() == "") {
|
if (response.description?.trim() == "") {
|
||||||
channelDescription.visibility = View.GONE
|
binding.channelDescription.visibility = View.GONE
|
||||||
} else {
|
} 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(binding.channelBanner)
|
||||||
Picasso.get().load(response.bannerUrl).into(bannerImage)
|
Picasso.get().load(response.avatarUrl).into(binding.channelImage)
|
||||||
Picasso.get().load(response.avatarUrl).into(channelImage)
|
|
||||||
channelAdapter = ChannelAdapter(
|
channelAdapter = ChannelAdapter(
|
||||||
response.relatedStreams!!.toMutableList(),
|
response.relatedStreams!!.toMutableList(),
|
||||||
childFragmentManager
|
childFragmentManager
|
||||||
)
|
)
|
||||||
view.findViewById<RecyclerView>(R.id.channel_recView).adapter = channelAdapter
|
binding.channelRecView.adapter = channelAdapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,21 +224,21 @@ class ChannelFragment : Fragment() {
|
|||||||
fun run() {
|
fun run() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.getChannelNextPage(channel_id!!, nextPage!!)
|
RetrofitInstance.api.getChannelNextPage(channelId!!, nextPage!!)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
refreshLayout?.isRefreshing = false
|
binding.channelRefresh.isRefreshing = false
|
||||||
println(e)
|
println(e)
|
||||||
Log.e(TAG, "IOException, you might not have internet connection")
|
Log.e(TAG, "IOException, you might not have internet connection")
|
||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
} catch (e: HttpException) {
|
} catch (e: HttpException) {
|
||||||
refreshLayout?.isRefreshing = false
|
binding.channelRefresh.isRefreshing = false
|
||||||
Log.e(TAG, "HttpException, unexpected response," + e.response())
|
Log.e(TAG, "HttpException, unexpected response," + e.response())
|
||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
}
|
}
|
||||||
nextPage = response.nextpage
|
nextPage = response.nextpage
|
||||||
channelAdapter?.updateItems(response.relatedStreams!!)
|
channelAdapter?.updateItems(response.relatedStreams!!)
|
||||||
isLoading = false
|
isLoading = false
|
||||||
refreshLayout?.isRefreshing = false
|
binding.channelRefresh.isRefreshing = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
run()
|
run()
|
||||||
@ -260,13 +249,4 @@ class ChannelFragment : Fragment() {
|
|||||||
if (!isAdded) return // Fragment not attached to an Activity
|
if (!isAdded) return // Fragment not attached to an Activity
|
||||||
activity?.runOnUiThread(action)
|
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.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ProgressBar
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.adapters.TrendingAdapter
|
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 com.github.libretube.util.RetrofitInstance
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class Home : Fragment() {
|
class HomeFragment : Fragment() {
|
||||||
|
|
||||||
private val TAG = "HomeFragment"
|
private val TAG = "HomeFragment"
|
||||||
private var refreshLayout: SwipeRefreshLayout? = null
|
private lateinit var binding: FragmentHomeBinding
|
||||||
|
private lateinit var region: String
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
arguments?.let {
|
arguments?.let {
|
||||||
@ -33,36 +33,41 @@ class Home : Fragment() {
|
|||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View {
|
||||||
// Inflate the layout for this fragment
|
binding = FragmentHomeBinding.inflate(layoutInflater, container, false)
|
||||||
return inflater.inflate(R.layout.fragment_home, container, false)
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
val recyclerView = view.findViewById<RecyclerView>(R.id.recview)
|
|
||||||
val grid = PreferenceHelper.getString(
|
val grid = PreferenceHelper.getString(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
"grid",
|
"grid",
|
||||||
resources.getInteger(R.integer.grid_items).toString()
|
resources.getInteger(R.integer.grid_items).toString()
|
||||||
)!!
|
)!!
|
||||||
recyclerView.layoutManager = GridLayoutManager(view.context, grid.toInt())
|
|
||||||
val progressbar = view.findViewById<ProgressBar>(R.id.progressBar)
|
val regionPref = PreferenceHelper.getString(requireContext(), "region", "sys")!!
|
||||||
fetchJson(progressbar, recyclerView)
|
|
||||||
refreshLayout = view.findViewById(R.id.home_refresh)
|
// get the system default country if auto region selected
|
||||||
refreshLayout?.isEnabled = true
|
region = if (regionPref == "sys") {
|
||||||
refreshLayout?.setOnRefreshListener {
|
LocaleHelper
|
||||||
Log.d(TAG, "hmm")
|
.getDetectedCountry(requireContext(), "UK")
|
||||||
fetchJson(progressbar, recyclerView)
|
.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() {
|
fun run() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
val region = PreferenceHelper.getString(requireContext(), "region", "US")
|
RetrofitInstance.api.getTrending(region)
|
||||||
RetrofitInstance.api.getTrending(region!!)
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
println(e)
|
println(e)
|
||||||
Log.e(TAG, "IOException, you might not have internet connection")
|
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()
|
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
|
||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
} finally {
|
} finally {
|
||||||
refreshLayout?.isRefreshing = false
|
binding.homeRefresh.isRefreshing = false
|
||||||
}
|
}
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
progressBar.visibility = View.GONE
|
binding.progressBar.visibility = View.GONE
|
||||||
recyclerView.adapter = TrendingAdapter(response, childFragmentManager)
|
binding.recview.adapter = TrendingAdapter(response, childFragmentManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,11 +94,4 @@ class Home : Fragment() {
|
|||||||
if (!isAdded) return // Fragment not attached to an Activity
|
if (!isAdded) return // Fragment not attached to an Activity
|
||||||
activity?.runOnUiThread(action)
|
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.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.adapters.PlaylistsAdapter
|
import com.github.libretube.adapters.PlaylistsAdapter
|
||||||
|
import com.github.libretube.databinding.FragmentLibraryBinding
|
||||||
import com.github.libretube.dialogs.CreatePlaylistDialog
|
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.github.libretube.util.RetrofitInstance
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class Library : Fragment() {
|
class LibraryFragment : Fragment() {
|
||||||
|
|
||||||
private val TAG = "LibraryFragment"
|
private val TAG = "LibraryFragment"
|
||||||
lateinit var token: String
|
lateinit var token: String
|
||||||
private lateinit var playlistRecyclerView: RecyclerView
|
private lateinit var binding: FragmentLibraryBinding
|
||||||
private lateinit var refreshLayout: SwipeRefreshLayout
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -39,51 +35,59 @@ class Library : Fragment() {
|
|||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View {
|
||||||
// Inflate the layout for this fragment
|
binding = FragmentLibraryBinding.inflate(layoutInflater, container, false)
|
||||||
return inflater.inflate(R.layout.fragment_library, container, false)
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
playlistRecyclerView = view.findViewById(R.id.playlist_recView)
|
binding.playlistRecView.layoutManager = LinearLayoutManager(view.context)
|
||||||
playlistRecyclerView.layoutManager = LinearLayoutManager(view.context)
|
|
||||||
token = PreferenceHelper.getToken(requireContext())
|
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 != "") {
|
if (token != "") {
|
||||||
view.findViewById<ImageView>(R.id.boogh2).visibility = View.GONE
|
binding.boogh.visibility = View.GONE
|
||||||
view.findViewById<TextView>(R.id.textLike2).visibility = View.GONE
|
binding.textLike.visibility = View.GONE
|
||||||
fetchPlaylists()
|
fetchPlaylists()
|
||||||
refreshLayout.isEnabled = true
|
binding.playlistRefresh.isEnabled = true
|
||||||
refreshLayout.setOnRefreshListener {
|
binding.playlistRefresh.setOnRefreshListener {
|
||||||
fetchPlaylists()
|
fetchPlaylists()
|
||||||
}
|
}
|
||||||
val createPlaylistButton = view.findViewById<FloatingActionButton>(R.id.create_playlist)
|
binding.createPlaylist.setOnClickListener {
|
||||||
createPlaylistButton.setOnClickListener {
|
|
||||||
val newFragment = CreatePlaylistDialog()
|
val newFragment = CreatePlaylistDialog()
|
||||||
newFragment.show(childFragmentManager, "Create Playlist")
|
newFragment.show(childFragmentManager, "Create Playlist")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
refreshLayout.isEnabled = false
|
binding.playlistRefresh.isEnabled = false
|
||||||
view.findViewById<FloatingActionButton>(R.id.create_playlist).visibility = View.GONE
|
binding.createPlaylist.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
// optimize CreatePlaylistFab bottom margin if miniPlayer active
|
// optimize CreatePlaylistFab bottom margin if miniPlayer active
|
||||||
val createPlaylistButton = view?.findViewById<FloatingActionButton>(R.id.create_playlist)
|
val layoutParams = binding.createPlaylist.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
val layoutParams = createPlaylistButton?.layoutParams as ViewGroup.MarginLayoutParams
|
|
||||||
layoutParams.bottomMargin = if (isMiniPlayerVisible) 180 else 64
|
layoutParams.bottomMargin = if (isMiniPlayerVisible) 180 else 64
|
||||||
createPlaylistButton?.layoutParams = layoutParams
|
binding.createPlaylist.layoutParams = layoutParams
|
||||||
super.onResume()
|
super.onResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fetchPlaylists() {
|
fun fetchPlaylists() {
|
||||||
fun run() {
|
fun run() {
|
||||||
refreshLayout.isRefreshing = true
|
binding.playlistRefresh.isRefreshing = true
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.playlists(token)
|
RetrofitInstance.authApi.playlists(token)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
println(e)
|
println(e)
|
||||||
Log.e(TAG, "IOException, you might not have internet connection")
|
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()
|
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
|
||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
} finally {
|
} finally {
|
||||||
refreshLayout.isRefreshing = false
|
binding.playlistRefresh.isRefreshing = false
|
||||||
}
|
}
|
||||||
if (response.isNotEmpty()) {
|
if (response.isNotEmpty()) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
view?.findViewById<ImageView>(R.id.boogh2)?.visibility = View.GONE
|
binding.boogh.visibility = View.GONE
|
||||||
view?.findViewById<TextView>(R.id.textLike2)?.visibility = View.GONE
|
binding.textLike.visibility = View.GONE
|
||||||
}
|
}
|
||||||
val playlistsAdapter = PlaylistsAdapter(
|
val playlistsAdapter = PlaylistsAdapter(
|
||||||
response.toMutableList(),
|
response.toMutableList(),
|
||||||
requireActivity()
|
requireActivity()
|
||||||
)
|
)
|
||||||
playlistRecyclerView.adapter = playlistsAdapter
|
binding.playlistRecView.adapter = playlistsAdapter
|
||||||
} else {
|
} else {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
view?.findViewById<ImageView>(R.id.boogh2).apply {
|
binding.boogh.apply {
|
||||||
this?.visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
this?.setImageResource(R.drawable.ic_list)
|
setImageResource(R.drawable.ic_list)
|
||||||
}
|
}
|
||||||
view?.findViewById<TextView>(R.id.textLike2).apply {
|
binding.textLike.apply {
|
||||||
this?.visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
this?.text = getString(R.string.emptyList)
|
text = getString(R.string.emptyList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,6 +11,8 @@ import android.net.Uri
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Build.VERSION.SDK_INT
|
import android.os.Build.VERSION.SDK_INT
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.support.v4.media.session.MediaSessionCompat
|
import android.support.v4.media.session.MediaSessionCompat
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
@ -19,14 +21,6 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
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 android.widget.Toast
|
||||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
@ -37,16 +31,17 @@ import androidx.fragment.app.Fragment
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.github.libretube.MainActivity
|
|
||||||
import com.github.libretube.R
|
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.ChaptersAdapter
|
||||||
import com.github.libretube.adapters.CommentsAdapter
|
import com.github.libretube.adapters.CommentsAdapter
|
||||||
import com.github.libretube.adapters.TrendingAdapter
|
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.AddtoPlaylistDialog
|
||||||
import com.github.libretube.dialogs.DownloadDialog
|
import com.github.libretube.dialogs.DownloadDialog
|
||||||
import com.github.libretube.dialogs.ShareDialog
|
import com.github.libretube.dialogs.ShareDialog
|
||||||
import com.github.libretube.hideKeyboard
|
|
||||||
import com.github.libretube.obj.ChapterSegment
|
import com.github.libretube.obj.ChapterSegment
|
||||||
import com.github.libretube.obj.PipedStream
|
import com.github.libretube.obj.PipedStream
|
||||||
import com.github.libretube.obj.Playlist
|
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.StreamItem
|
||||||
import com.github.libretube.obj.Streams
|
import com.github.libretube.obj.Streams
|
||||||
import com.github.libretube.obj.Subscribe
|
import com.github.libretube.obj.Subscribe
|
||||||
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
import com.github.libretube.services.IS_DOWNLOAD_RUNNING
|
import com.github.libretube.services.IS_DOWNLOAD_RUNNING
|
||||||
import com.github.libretube.util.CronetHelper
|
import com.github.libretube.util.CronetHelper
|
||||||
import com.github.libretube.util.DescriptionAdapter
|
import com.github.libretube.util.DescriptionAdapter
|
||||||
import com.github.libretube.util.PreferenceHelper
|
|
||||||
import com.github.libretube.util.RetrofitInstance
|
import com.github.libretube.util.RetrofitInstance
|
||||||
import com.github.libretube.util.formatShort
|
import com.github.libretube.util.formatShort
|
||||||
|
import com.github.libretube.views.DoubleClickListener
|
||||||
import com.google.android.exoplayer2.C
|
import com.google.android.exoplayer2.C
|
||||||
import com.google.android.exoplayer2.DefaultLoadControl
|
import com.google.android.exoplayer2.DefaultLoadControl
|
||||||
import com.google.android.exoplayer2.ExoPlayer
|
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.AspectRatioFrameLayout
|
||||||
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
||||||
import com.google.android.exoplayer2.ui.StyledPlayerView
|
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.DataSource
|
||||||
import com.google.android.exoplayer2.upstream.DefaultDataSource
|
import com.google.android.exoplayer2.upstream.DefaultDataSource
|
||||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
|
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
|
||||||
import com.google.android.exoplayer2.util.RepeatModeUtil
|
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.button.MaterialButton
|
||||||
import com.google.android.material.card.MaterialCardView
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -102,6 +99,9 @@ var isMiniPlayerVisible = false
|
|||||||
class PlayerFragment : Fragment() {
|
class PlayerFragment : Fragment() {
|
||||||
|
|
||||||
private val TAG = "PlayerFragment"
|
private val TAG = "PlayerFragment"
|
||||||
|
private lateinit var binding: FragmentPlayerBinding
|
||||||
|
private lateinit var playerBinding: ExoStyledPlayerControlViewBinding
|
||||||
|
|
||||||
private var videoId: String? = null
|
private var videoId: String? = null
|
||||||
private var playlistId: String? = null
|
private var playlistId: String? = null
|
||||||
private var sId: Int = 0
|
private var sId: Int = 0
|
||||||
@ -114,14 +114,11 @@ class PlayerFragment : Fragment() {
|
|||||||
|
|
||||||
private var isSubscribed: Boolean = false
|
private var isSubscribed: Boolean = false
|
||||||
|
|
||||||
private lateinit var relatedRecView: RecyclerView
|
|
||||||
private lateinit var commentsRecView: RecyclerView
|
|
||||||
private var commentsAdapter: CommentsAdapter? = null
|
private var commentsAdapter: CommentsAdapter? = null
|
||||||
private var commentsLoaded: Boolean? = false
|
private var commentsLoaded: Boolean? = false
|
||||||
private var nextPage: String? = null
|
private var nextPage: String? = null
|
||||||
private var isLoading = true
|
private var isLoading = true
|
||||||
private lateinit var exoPlayerView: StyledPlayerView
|
private lateinit var exoPlayerView: StyledPlayerView
|
||||||
private lateinit var motionLayout: MotionLayout
|
|
||||||
private lateinit var exoPlayer: ExoPlayer
|
private lateinit var exoPlayer: ExoPlayer
|
||||||
private lateinit var segmentData: Segments
|
private lateinit var segmentData: Segments
|
||||||
private var relatedStreamsEnabled = true
|
private var relatedStreamsEnabled = true
|
||||||
@ -133,8 +130,6 @@ class PlayerFragment : Fragment() {
|
|||||||
|
|
||||||
private var isPlayerLocked: Boolean = false
|
private var isPlayerLocked: Boolean = false
|
||||||
|
|
||||||
private lateinit var relDownloadVideo: LinearLayout
|
|
||||||
|
|
||||||
private lateinit var mediaSession: MediaSessionCompat
|
private lateinit var mediaSession: MediaSessionCompat
|
||||||
private lateinit var mediaSessionConnector: MediaSessionConnector
|
private lateinit var mediaSessionConnector: MediaSessionConnector
|
||||||
private lateinit var playerNotification: PlayerNotificationManager
|
private lateinit var playerNotification: PlayerNotificationManager
|
||||||
@ -156,9 +151,11 @@ class PlayerFragment : Fragment() {
|
|||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View {
|
||||||
|
binding = FragmentPlayerBinding.inflate(layoutInflater, container, false)
|
||||||
|
playerBinding = binding.player.binding
|
||||||
// Inflate the layout for this fragment
|
// 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?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
@ -172,17 +169,14 @@ class PlayerFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initializeTransitionLayout(view: View) {
|
private fun initializeTransitionLayout(view: View) {
|
||||||
val playerDescription = view.findViewById<TextView>(R.id.player_description)
|
|
||||||
videoId = videoId!!.replace("/watch?v=", "")
|
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
|
val mainActivity = activity as MainActivity
|
||||||
playerMotionLayout.addTransitionListener(object : MotionLayout.TransitionListener {
|
mainActivity.binding.container.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
exoPlayerView = binding.player
|
||||||
|
|
||||||
|
binding.playerMotionLayout.addTransitionListener(object : MotionLayout.TransitionListener {
|
||||||
override fun onTransitionStarted(
|
override fun onTransitionStarted(
|
||||||
motionLayout: MotionLayout?,
|
motionLayout: MotionLayout?,
|
||||||
startId: Int,
|
startId: Int,
|
||||||
@ -198,7 +192,7 @@ class PlayerFragment : Fragment() {
|
|||||||
) {
|
) {
|
||||||
val mainActivity = activity as MainActivity
|
val mainActivity = activity as MainActivity
|
||||||
val mainMotionLayout =
|
val mainMotionLayout =
|
||||||
mainActivity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
mainActivity.binding.mainMotionLayout
|
||||||
mainMotionLayout.progress = abs(progress)
|
mainMotionLayout.progress = abs(progress)
|
||||||
exoPlayerView.hideController()
|
exoPlayerView.hideController()
|
||||||
eId = endId
|
eId = endId
|
||||||
@ -209,7 +203,7 @@ class PlayerFragment : Fragment() {
|
|||||||
println(currentId)
|
println(currentId)
|
||||||
val mainActivity = activity as MainActivity
|
val mainActivity = activity as MainActivity
|
||||||
val mainMotionLayout =
|
val mainMotionLayout =
|
||||||
mainActivity.findViewById<MotionLayout>(R.id.mainMotionLayout)
|
mainActivity.binding.mainMotionLayout
|
||||||
if (currentId == eId) {
|
if (currentId == eId) {
|
||||||
isMiniPlayerVisible = true
|
isMiniPlayerVisible = true
|
||||||
exoPlayerView.useController = false
|
exoPlayerView.useController = false
|
||||||
@ -222,7 +216,7 @@ class PlayerFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onTransitionTrigger(
|
override fun onTransitionTrigger(
|
||||||
motionLayout: MotionLayout?,
|
MotionLayout: MotionLayout?,
|
||||||
triggerId: Int,
|
triggerId: Int,
|
||||||
positive: Boolean,
|
positive: Boolean,
|
||||||
progress: Float
|
progress: Float
|
||||||
@ -230,93 +224,65 @@ class PlayerFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
playerMotionLayout.progress = 1.toFloat()
|
binding.playerMotionLayout.progress = 1.toFloat()
|
||||||
playerMotionLayout.transitionToStart()
|
binding.playerMotionLayout.transitionToStart()
|
||||||
|
|
||||||
view.findViewById<ImageView>(R.id.close_imageView).setOnClickListener {
|
binding.closeImageView.setOnClickListener {
|
||||||
isMiniPlayerVisible = false
|
isMiniPlayerVisible = false
|
||||||
motionLayout.transitionToEnd()
|
binding.playerMotionLayout.transitionToEnd()
|
||||||
val mainActivity = activity as MainActivity
|
val mainActivity = activity as MainActivity
|
||||||
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||||
mainActivity.supportFragmentManager.beginTransaction()
|
mainActivity.supportFragmentManager.beginTransaction()
|
||||||
.remove(this)
|
.remove(this)
|
||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
view.findViewById<ImageButton>(R.id.close_imageButton).setOnClickListener {
|
playerBinding.closeImageButton.setOnClickListener {
|
||||||
isMiniPlayerVisible = false
|
isMiniPlayerVisible = false
|
||||||
motionLayout.transitionToEnd()
|
binding.playerMotionLayout.transitionToEnd()
|
||||||
val mainActivity = activity as MainActivity
|
val mainActivity = activity as MainActivity
|
||||||
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||||
mainActivity.supportFragmentManager.beginTransaction()
|
mainActivity.supportFragmentManager.beginTransaction()
|
||||||
.remove(this)
|
.remove(this)
|
||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
val playImageView = view.findViewById<ImageView>(R.id.play_imageView)
|
binding.playImageView.setOnClickListener {
|
||||||
playImageView.setOnClickListener {
|
|
||||||
paused = if (paused) {
|
paused = if (paused) {
|
||||||
playImageView.setImageResource(R.drawable.ic_pause)
|
binding.playImageView.setImageResource(R.drawable.ic_pause)
|
||||||
exoPlayer.play()
|
exoPlayer.play()
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
playImageView.setImageResource(R.drawable.ic_play)
|
binding.playImageView.setImageResource(R.drawable.ic_play)
|
||||||
exoPlayer.pause()
|
exoPlayer.pause()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// video description and chapters toggle
|
// video description and chapters toggle
|
||||||
val descLinLayout = view.findViewById<LinearLayout>(R.id.desc_linLayout)
|
binding.playerTitleLayout.setOnClickListener {
|
||||||
view.findViewById<RelativeLayout>(R.id.player_title_layout).setOnClickListener {
|
binding.playerDescriptionArrow.animate().rotationBy(180F).setDuration(250).start()
|
||||||
val arrowImageView = view.findViewById<ImageView>(R.id.player_description_arrow)
|
binding.descLinLayout.visibility =
|
||||||
arrowImageView.animate().rotationBy(180F).setDuration(250).start()
|
if (binding.descLinLayout.isVisible) View.GONE else View.VISIBLE
|
||||||
descLinLayout.visibility = if (descLinLayout.isVisible) View.GONE else View.VISIBLE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
view.findViewById<MaterialCardView>(R.id.comments_toggle)
|
binding.commentsToggle.setOnClickListener {
|
||||||
.setOnClickListener {
|
toggleComments()
|
||||||
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)
|
|
||||||
|
|
||||||
// FullScreen button trigger
|
// FullScreen button trigger
|
||||||
fullScreenButton.setOnClickListener {
|
playerBinding.fullscreen.setOnClickListener {
|
||||||
|
// hide player controller
|
||||||
exoPlayerView.hideController()
|
exoPlayerView.hideController()
|
||||||
if (!isFullScreen) {
|
if (!isFullScreen) {
|
||||||
with(motionLayout) {
|
// go to fullscreen mode
|
||||||
getConstraintSet(R.id.start).constrainHeight(R.id.player, -1)
|
setFullscreen()
|
||||||
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
|
|
||||||
} else {
|
} else {
|
||||||
with(motionLayout) {
|
// exit fullscreen mode
|
||||||
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
|
unsetFullscreen()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
isFullScreen = !isFullScreen
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// switching between original aspect ratio (black bars) and zoomed to fill device screen
|
// 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) {
|
if (isZoomed) {
|
||||||
exoPlayerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
|
exoPlayerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||||
isZoomed = false
|
isZoomed = false
|
||||||
@ -327,13 +293,12 @@ class PlayerFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// lock and unlock the player
|
// lock and unlock the player
|
||||||
val lockPlayerButton = view.findViewById<ImageButton>(R.id.lock_player)
|
playerBinding.lockPlayer.setOnClickListener {
|
||||||
lockPlayerButton.setOnClickListener {
|
|
||||||
// change the locked/unlocked icon
|
// change the locked/unlocked icon
|
||||||
if (!isPlayerLocked) {
|
if (!isPlayerLocked) {
|
||||||
lockPlayerButton.setImageResource(R.drawable.ic_locked)
|
playerBinding.lockPlayer.setImageResource(R.drawable.ic_locked)
|
||||||
} else {
|
} else {
|
||||||
lockPlayerButton.setImageResource(R.drawable.ic_unlocked)
|
playerBinding.lockPlayer.setImageResource(R.drawable.ic_unlocked)
|
||||||
}
|
}
|
||||||
|
|
||||||
// show/hide all the controls
|
// show/hide all the controls
|
||||||
@ -343,32 +308,88 @@ class PlayerFragment : Fragment() {
|
|||||||
isPlayerLocked = !isPlayerLocked
|
isPlayerLocked = !isPlayerLocked
|
||||||
}
|
}
|
||||||
|
|
||||||
val scrollView = view.findViewById<ScrollView>(R.id.player_scrollView)
|
binding.playerScrollView.viewTreeObserver
|
||||||
scrollView.viewTreeObserver
|
|
||||||
.addOnScrollChangedListener {
|
.addOnScrollChangedListener {
|
||||||
if (scrollView.getChildAt(0).bottom
|
if (binding.playerScrollView.getChildAt(0).bottom
|
||||||
== (scrollView.height + scrollView.scrollY) &&
|
== (binding.playerScrollView.height + binding.playerScrollView.scrollY) &&
|
||||||
nextPage != null
|
nextPage != null
|
||||||
) {
|
) {
|
||||||
fetchNextComments()
|
fetchNextComments()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
commentsRecView = view.findViewById(R.id.comments_recView)
|
binding.commentsRecView.layoutManager = LinearLayoutManager(view.context)
|
||||||
commentsRecView.layoutManager = LinearLayoutManager(view.context)
|
binding.commentsRecView.setItemViewCacheSize(20)
|
||||||
|
|
||||||
commentsRecView.setItemViewCacheSize(20)
|
binding.relatedRecView.layoutManager =
|
||||||
|
|
||||||
relatedRecView = view.findViewById(R.id.player_recView)
|
|
||||||
relatedRecView.layoutManager =
|
|
||||||
GridLayoutManager(view.context, resources.getInteger(R.integer.grid_items))
|
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() {
|
private fun toggleComments() {
|
||||||
commentsRecView.visibility =
|
binding.commentsRecView.visibility =
|
||||||
if (commentsRecView.isVisible) View.GONE else View.VISIBLE
|
if (binding.commentsRecView.isVisible) View.GONE else View.VISIBLE
|
||||||
relatedRecView.visibility =
|
binding.relatedRecView.visibility =
|
||||||
if (relatedRecView.isVisible) View.GONE else View.VISIBLE
|
if (binding.relatedRecView.isVisible) View.GONE else View.VISIBLE
|
||||||
if (!commentsLoaded!!) fetchComments()
|
if (!commentsLoaded!!) fetchComments()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,10 +407,7 @@ class PlayerFragment : Fragment() {
|
|||||||
|
|
||||||
// pause player if screen off and setting enabled
|
// pause player if screen off and setting enabled
|
||||||
if (
|
if (
|
||||||
this::exoPlayer.isInitialized &&
|
this::exoPlayer.isInitialized && !isScreenOn && pausePlayerOnScreenOffEnabled
|
||||||
exoPlayer != null &&
|
|
||||||
!isScreenOn &&
|
|
||||||
pausePlayerOnScreenOffEnabled
|
|
||||||
) {
|
) {
|
||||||
exoPlayer.pause()
|
exoPlayer.pause()
|
||||||
}
|
}
|
||||||
@ -399,6 +417,7 @@ class PlayerFragment : Fragment() {
|
|||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
try {
|
try {
|
||||||
|
saveWatchPosition()
|
||||||
mediaSession.isActive = false
|
mediaSession.isActive = false
|
||||||
mediaSession.release()
|
mediaSession.release()
|
||||||
mediaSessionConnector.setPlayer(null)
|
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() {
|
private fun checkForSegments() {
|
||||||
if (!exoPlayer.isPlaying || !sponsorBlockPrefs.sponsorBlockEnabled) return
|
if (!exoPlayer.isPlaying || !sponsorBlockPrefs.sponsorBlockEnabled) return
|
||||||
|
|
||||||
@ -462,17 +500,12 @@ class PlayerFragment : Fragment() {
|
|||||||
relatedStreams = response.relatedStreams
|
relatedStreams = response.relatedStreams
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
if (response.chapters != null) initializeChapters(response.chapters)
|
|
||||||
// set media sources for the player
|
// set media sources for the player
|
||||||
setResolutionAndSubtitles(view, response)
|
setResolutionAndSubtitles(view, response)
|
||||||
exoPlayer.prepare()
|
|
||||||
prepareExoPlayerView()
|
prepareExoPlayerView()
|
||||||
initializePlayerView(view, response)
|
initializePlayerView(view, response)
|
||||||
// support for time stamped links
|
seekToWatchPosition()
|
||||||
if (arguments?.getLong("timeStamp") != null) {
|
exoPlayer.prepare()
|
||||||
val position = arguments?.getLong("timeStamp")!! * 1000
|
|
||||||
exoPlayer.seekTo(position)
|
|
||||||
}
|
|
||||||
exoPlayer.play()
|
exoPlayer.play()
|
||||||
exoPlayerView.useController = true
|
exoPlayerView.useController = true
|
||||||
initializePlayerNotification(requireContext())
|
initializePlayerNotification(requireContext())
|
||||||
@ -481,12 +514,32 @@ class PlayerFragment : Fragment() {
|
|||||||
if (!relatedStreamsEnabled) toggleComments()
|
if (!relatedStreamsEnabled) toggleComments()
|
||||||
// prepare for autoplay
|
// prepare for autoplay
|
||||||
initAutoPlay()
|
initAutoPlay()
|
||||||
|
val watchHistoryEnabled =
|
||||||
|
PreferenceHelper.getBoolean(requireContext(), "Watch_history_toggle", true)
|
||||||
|
if (watchHistoryEnabled) {
|
||||||
|
PreferenceHelper.addToWatchHistory(requireContext(), videoId!!, response)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
run()
|
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
|
// the function is working recursively
|
||||||
private fun initAutoPlay() {
|
private fun initAutoPlay() {
|
||||||
// save related streams for autoplay
|
// save related streams for autoplay
|
||||||
@ -535,7 +588,7 @@ class PlayerFragment : Fragment() {
|
|||||||
// if it's not a playlist then use the next related video
|
// if it's not a playlist then use the next related video
|
||||||
} else if (relatedStreams != null && relatedStreams!!.isNotEmpty()) {
|
} else if (relatedStreams != null && relatedStreams!!.isNotEmpty()) {
|
||||||
// save next video from related streams for autoplay
|
// 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) {
|
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()) +
|
context?.getString(R.string.views, response.views.formatShort()) +
|
||||||
" • " + response.uploadDate
|
" • " + response.uploadDate
|
||||||
view.findViewById<TextView>(R.id.textLike).text = response.likes.formatShort()
|
binding.textLike.text = response.likes.formatShort()
|
||||||
view.findViewById<TextView>(R.id.textDislike).text = response.dislikes.formatShort()
|
binding.textDislike.text = response.dislikes.formatShort()
|
||||||
val channelImage = view.findViewById<ImageView>(R.id.player_channelImage)
|
Picasso.get().load(response.uploaderAvatar).into(binding.playerChannelImage)
|
||||||
Picasso.get().load(response.uploaderAvatar).into(channelImage)
|
binding.playerChannelName.text = response.uploader
|
||||||
view.findViewById<TextView>(R.id.player_channelName).text = response.uploader
|
|
||||||
|
|
||||||
view.findViewById<TextView>(R.id.title_textView).text = response.title
|
binding.titleTextView.text = response.title
|
||||||
view.findViewById<TextView>(R.id.player_title).text = response.title
|
binding.playerTitle.text = response.title
|
||||||
view.findViewById<TextView>(R.id.player_description).text = response.description
|
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
|
// Listener for play and pause icon change
|
||||||
exoPlayer.addListener(object : Player.Listener {
|
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)
|
@Deprecated(message = "Deprecated", level = DeprecationLevel.HIDDEN)
|
||||||
override fun onPlayerStateChanged(
|
override fun onPlayerStateChanged(
|
||||||
playWhenReady: Boolean,
|
playWhenReady: Boolean,
|
||||||
@ -690,38 +764,34 @@ class PlayerFragment : Fragment() {
|
|||||||
if (playWhenReady && playbackState == Player.STATE_READY) {
|
if (playWhenReady && playbackState == Player.STATE_READY) {
|
||||||
// media actually playing
|
// media actually playing
|
||||||
transitioning = false
|
transitioning = false
|
||||||
view.findViewById<ImageView>(R.id.play_imageView)
|
binding.playImageView.setImageResource(R.drawable.ic_pause)
|
||||||
.setImageResource(R.drawable.ic_pause)
|
|
||||||
} else if (playWhenReady) {
|
} else if (playWhenReady) {
|
||||||
// might be idle (plays after prepare()),
|
// might be idle (plays after prepare()),
|
||||||
// buffering (plays when data available)
|
// buffering (plays when data available)
|
||||||
// or ended (plays when seek away from end)
|
// or ended (plays when seek away from end)
|
||||||
view.findViewById<ImageView>(R.id.play_imageView)
|
binding.playImageView.setImageResource(R.drawable.ic_play)
|
||||||
.setImageResource(R.drawable.ic_play)
|
|
||||||
} else {
|
} else {
|
||||||
// player paused in any state
|
// player paused in any state
|
||||||
view.findViewById<ImageView>(R.id.play_imageView)
|
binding.playImageView.setImageResource(R.drawable.ic_play)
|
||||||
.setImageResource(R.drawable.ic_play)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// share button
|
// share button
|
||||||
view.findViewById<LinearLayout>(R.id.relPlayer_share).setOnClickListener {
|
binding.relPlayerShare.setOnClickListener {
|
||||||
val shareDialog = ShareDialog(videoId!!, false)
|
val shareDialog = ShareDialog(videoId!!, false)
|
||||||
shareDialog.show(childFragmentManager, "ShareDialog")
|
shareDialog.show(childFragmentManager, "ShareDialog")
|
||||||
}
|
}
|
||||||
// check if livestream
|
// check if livestream
|
||||||
if (response.duration!! > 0) {
|
if (response.duration!! > 0) {
|
||||||
// download clicked
|
// download clicked
|
||||||
relDownloadVideo.setOnClickListener {
|
binding.relPlayerDownload.setOnClickListener {
|
||||||
if (!IS_DOWNLOAD_RUNNING) {
|
if (!IS_DOWNLOAD_RUNNING) {
|
||||||
val newFragment = DownloadDialog()
|
val newFragment = DownloadDialog()
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putString("video_id", videoId)
|
bundle.putString("video_id", videoId)
|
||||||
bundle.putParcelable("streams", response)
|
|
||||||
newFragment.arguments = bundle
|
newFragment.arguments = bundle
|
||||||
newFragment.show(childFragmentManager, "Download")
|
newFragment.show(childFragmentManager, "DownloadDialog")
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(context, R.string.dlisinprogress, Toast.LENGTH_SHORT)
|
Toast.makeText(context, R.string.dlisinprogress, Toast.LENGTH_SHORT)
|
||||||
.show()
|
.show()
|
||||||
@ -732,7 +802,7 @@ class PlayerFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (response.hls != null) {
|
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
|
// start an intent with video as mimetype using the hls stream
|
||||||
val uri: Uri = Uri.parse(response.hls)
|
val uri: Uri = Uri.parse(response.hls)
|
||||||
val intent = Intent()
|
val intent = Intent()
|
||||||
@ -749,14 +819,14 @@ class PlayerFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
if (relatedStreamsEnabled) {
|
if (relatedStreamsEnabled) {
|
||||||
// only show related streams if enabled
|
// only show related streams if enabled
|
||||||
relatedRecView.adapter = TrendingAdapter(
|
binding.relatedRecView.adapter = TrendingAdapter(
|
||||||
response.relatedStreams!!,
|
response.relatedStreams!!,
|
||||||
childFragmentManager
|
childFragmentManager
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// set video description
|
// set video description
|
||||||
val description = response.description!!
|
val description = response.description!!
|
||||||
view.findViewById<TextView>(R.id.player_description).text =
|
binding.playerDescription.text =
|
||||||
// detect whether the description is html formatted
|
// detect whether the description is html formatted
|
||||||
if (description.contains("<") && description.contains(">")) {
|
if (description.contains("<") && description.contains(">")) {
|
||||||
if (SDK_INT >= Build.VERSION_CODES.N) {
|
if (SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
@ -769,19 +839,18 @@ class PlayerFragment : Fragment() {
|
|||||||
description
|
description
|
||||||
}
|
}
|
||||||
|
|
||||||
view.findViewById<RelativeLayout>(R.id.player_channel).setOnClickListener {
|
binding.playerChannel.setOnClickListener {
|
||||||
val activity = view.context as MainActivity
|
val activity = view.context as MainActivity
|
||||||
val bundle = bundleOf("channel_id" to response.uploaderUrl)
|
val bundle = bundleOf("channel_id" to response.uploaderUrl)
|
||||||
activity.navController.navigate(R.id.channel, bundle)
|
activity.navController.navigate(R.id.channelFragment, bundle)
|
||||||
activity.findViewById<MotionLayout>(R.id.mainMotionLayout).transitionToEnd()
|
activity.binding.mainMotionLayout.transitionToEnd()
|
||||||
view.findViewById<MotionLayout>(R.id.playerMotionLayout).transitionToEnd()
|
binding.playerMotionLayout.transitionToEnd()
|
||||||
}
|
}
|
||||||
val token = PreferenceHelper.getToken(requireContext())
|
val token = PreferenceHelper.getToken(requireContext())
|
||||||
if (token != "") {
|
if (token != "") {
|
||||||
val channelId = response.uploaderUrl?.replace("/channel/", "")
|
val channelId = response.uploaderUrl?.replace("/channel/", "")
|
||||||
val subButton = view.findViewById<MaterialButton>(R.id.player_subscribe)
|
isSubscribed(binding.playerSubscribe, channelId!!)
|
||||||
isSubscribed(subButton, channelId!!)
|
binding.save.setOnClickListener {
|
||||||
view.findViewById<LinearLayout>(R.id.save).setOnClickListener {
|
|
||||||
val newFragment = AddtoPlaylistDialog()
|
val newFragment = AddtoPlaylistDialog()
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putString("videoId", videoId)
|
bundle.putString("videoId", videoId)
|
||||||
@ -791,14 +860,81 @@ class PlayerFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initializeChapters(chapters: List<ChapterSegment>) {
|
private fun enableDoubleTapToSeek() {
|
||||||
val chaptersRecView = view?.findViewById<RecyclerView>(R.id.chapters_recView)
|
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()) {
|
if (chapters.isNotEmpty()) {
|
||||||
chaptersRecView?.layoutManager =
|
binding.chaptersRecView.layoutManager =
|
||||||
LinearLayoutManager(this.context, LinearLayoutManager.HORIZONTAL, false)
|
LinearLayoutManager(this.context, LinearLayoutManager.HORIZONTAL, false)
|
||||||
chaptersRecView?.adapter = ChaptersAdapter(chapters, exoPlayer)
|
binding.chaptersRecView.adapter = ChaptersAdapter(chapters, exoPlayer)
|
||||||
chaptersRecView?.visibility = View.VISIBLE
|
binding.chaptersRecView.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -816,7 +952,7 @@ class PlayerFragment : Fragment() {
|
|||||||
val videoSource: MediaSource =
|
val videoSource: MediaSource =
|
||||||
DefaultMediaSourceFactory(dataSourceFactory)
|
DefaultMediaSourceFactory(dataSourceFactory)
|
||||||
.createMediaSource(videoItem)
|
.createMediaSource(videoItem)
|
||||||
var audioSource: MediaSource =
|
val audioSource: MediaSource =
|
||||||
ProgressiveMediaSource.Factory(dataSourceFactory)
|
ProgressiveMediaSource.Factory(dataSourceFactory)
|
||||||
.createMediaSource(fromUri(audioUrl))
|
.createMediaSource(fromUri(audioUrl))
|
||||||
val mergeSource: MediaSource =
|
val mergeSource: MediaSource =
|
||||||
@ -829,15 +965,12 @@ class PlayerFragment : Fragment() {
|
|||||||
PreferenceHelper.getString(requireContext(), "player_video_format", "WEBM")
|
PreferenceHelper.getString(requireContext(), "player_video_format", "WEBM")
|
||||||
val defres = PreferenceHelper.getString(requireContext(), "default_res", "")!!
|
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 videosNameArray: Array<CharSequence> = arrayOf()
|
||||||
var videosUrlArray: Array<Uri> = arrayOf()
|
var videosUrlArray: Array<Uri> = arrayOf()
|
||||||
|
|
||||||
// append hls to list if available
|
// append hls to list if available
|
||||||
if (response.hls != null) {
|
if (response.hls != null) {
|
||||||
videosNameArray += "HLS"
|
videosNameArray += getString(R.string.hls)
|
||||||
videosUrlArray += response.hls.toUri()
|
videosUrlArray += response.hls.toUri()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -871,7 +1004,7 @@ class PlayerFragment : Fragment() {
|
|||||||
val videoUri = videosUrlArray[index]
|
val videoUri = videosUrlArray[index]
|
||||||
val audioUrl = getMostBitRate(response.audioStreams!!)
|
val audioUrl = getMostBitRate(response.audioStreams!!)
|
||||||
setMediaSource(subtitle, videoUri, audioUrl)
|
setMediaSource(subtitle, videoUri, audioUrl)
|
||||||
qualityText.text = videosNameArray[index]
|
playerBinding.qualityText.text = videosNameArray[index]
|
||||||
return@lit
|
return@lit
|
||||||
} else if (response.hls != null) {
|
} else if (response.hls != null) {
|
||||||
val mediaItem: MediaItem = MediaItem.Builder()
|
val mediaItem: MediaItem = MediaItem.Builder()
|
||||||
@ -902,11 +1035,11 @@ class PlayerFragment : Fragment() {
|
|||||||
val videoUri = videosUrlArray[0]
|
val videoUri = videosUrlArray[0]
|
||||||
val audioUrl = getMostBitRate(response.audioStreams!!)
|
val audioUrl = getMostBitRate(response.audioStreams!!)
|
||||||
setMediaSource(subtitle, videoUri, audioUrl)
|
setMediaSource(subtitle, videoUri, audioUrl)
|
||||||
qualityText.text = videosNameArray[0]
|
playerBinding.qualityText.text = videosNameArray[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qualitySelect.setOnClickListener {
|
playerBinding.qualityLinLayout.setOnClickListener {
|
||||||
// Dialog for quality selection
|
// Dialog for quality selection
|
||||||
val builder: MaterialAlertDialogBuilder? = activity?.let {
|
val builder: MaterialAlertDialogBuilder? = activity?.let {
|
||||||
MaterialAlertDialogBuilder(it)
|
MaterialAlertDialogBuilder(it)
|
||||||
@ -918,7 +1051,7 @@ class PlayerFragment : Fragment() {
|
|||||||
) { _, which ->
|
) { _, which ->
|
||||||
whichQuality = which
|
whichQuality = which
|
||||||
if (
|
if (
|
||||||
videosNameArray[which] == "HLS" ||
|
videosNameArray[which] == getString(R.string.hls) ||
|
||||||
videosNameArray[which] == "LBRY HLS"
|
videosNameArray[which] == "LBRY HLS"
|
||||||
) {
|
) {
|
||||||
// no need to merge sources if using hls
|
// no need to merge sources if using hls
|
||||||
@ -933,7 +1066,7 @@ class PlayerFragment : Fragment() {
|
|||||||
setMediaSource(subtitle, videoUri, audioUrl)
|
setMediaSource(subtitle, videoUri, audioUrl)
|
||||||
}
|
}
|
||||||
exoPlayer.seekTo(lastPosition)
|
exoPlayer.seekTo(lastPosition)
|
||||||
qualityText.text = videosNameArray[which]
|
playerBinding.qualityText.text = videosNameArray[which]
|
||||||
}
|
}
|
||||||
val dialog = builder.create()
|
val dialog = builder.create()
|
||||||
dialog.show()
|
dialog.show()
|
||||||
@ -969,7 +1102,7 @@ class PlayerFragment : Fragment() {
|
|||||||
// cache the last three minutes
|
// cache the last three minutes
|
||||||
.setBackBuffer(1000 * 60 * 3, true)
|
.setBackBuffer(1000 * 60 * 3, true)
|
||||||
.setBufferDurationsMs(
|
.setBufferDurationsMs(
|
||||||
DefaultLoadControl.DEFAULT_MIN_BUFFER_MS,
|
1000 * 10, // exo default is 50s
|
||||||
bufferingGoal,
|
bufferingGoal,
|
||||||
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
|
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
|
||||||
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS
|
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS
|
||||||
@ -1006,21 +1139,19 @@ class PlayerFragment : Fragment() {
|
|||||||
|
|
||||||
playerNotification.apply {
|
playerNotification.apply {
|
||||||
setPlayer(exoPlayer)
|
setPlayer(exoPlayer)
|
||||||
setUseNextAction(false)
|
|
||||||
setUsePreviousAction(false)
|
setUsePreviousAction(false)
|
||||||
|
setUseStopAction(true)
|
||||||
setMediaSessionToken(mediaSession.sessionToken)
|
setMediaSessionToken(mediaSession.sessionToken)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun lockPlayer(isLocked: Boolean) {
|
private fun lockPlayer(isLocked: Boolean) {
|
||||||
val visibility = if (isLocked) View.VISIBLE else View.INVISIBLE
|
val visibility = if (isLocked) View.VISIBLE else View.GONE
|
||||||
exoPlayerView.findViewById<LinearLayout>(R.id.exo_top_bar_right).visibility = visibility
|
playerBinding.exoTopBarRight.visibility = visibility
|
||||||
exoPlayerView.findViewById<ImageButton>(R.id.exo_play_pause).visibility = visibility
|
playerBinding.exoPlayPause.visibility = visibility
|
||||||
exoPlayerView.findViewById<Button>(R.id.exo_ffwd_with_amount).visibility = visibility
|
playerBinding.exoBottomBar.visibility = visibility
|
||||||
exoPlayerView.findViewById<Button>(R.id.exo_rew_with_amount).visibility = visibility
|
playerBinding.closeImageButton.visibility = visibility
|
||||||
exoPlayerView.findViewById<FrameLayout>(R.id.exo_bottom_bar).visibility = visibility
|
playerBinding.exoTitle.visibility = visibility
|
||||||
exoPlayerView.findViewById<TextView>(R.id.exo_title).visibility =
|
|
||||||
if (isLocked && isFullScreen) View.VISIBLE else View.INVISIBLE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSubscribed(button: MaterialButton, channel_id: String) {
|
private fun isSubscribed(button: MaterialButton, channel_id: String) {
|
||||||
@ -1029,7 +1160,7 @@ class PlayerFragment : Fragment() {
|
|||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
val token = PreferenceHelper.getToken(requireContext())
|
val token = PreferenceHelper.getToken(requireContext())
|
||||||
RetrofitInstance.api.isSubscribed(
|
RetrofitInstance.authApi.isSubscribed(
|
||||||
channel_id,
|
channel_id,
|
||||||
token
|
token
|
||||||
)
|
)
|
||||||
@ -1069,7 +1200,7 @@ class PlayerFragment : Fragment() {
|
|||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
val token = PreferenceHelper.getToken(requireContext())
|
val token = PreferenceHelper.getToken(requireContext())
|
||||||
RetrofitInstance.api.subscribe(
|
RetrofitInstance.authApi.subscribe(
|
||||||
token,
|
token,
|
||||||
Subscribe(channel_id)
|
Subscribe(channel_id)
|
||||||
)
|
)
|
||||||
@ -1092,7 +1223,7 @@ class PlayerFragment : Fragment() {
|
|||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
val token = PreferenceHelper.getToken(requireContext())
|
val token = PreferenceHelper.getToken(requireContext())
|
||||||
RetrofitInstance.api.unsubscribe(
|
RetrofitInstance.authApi.unsubscribe(
|
||||||
token,
|
token,
|
||||||
Subscribe(channel_id)
|
Subscribe(channel_id)
|
||||||
)
|
)
|
||||||
@ -1143,7 +1274,7 @@ class PlayerFragment : Fragment() {
|
|||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
}
|
}
|
||||||
commentsAdapter = CommentsAdapter(videoId!!, commentsResponse.comments)
|
commentsAdapter = CommentsAdapter(videoId!!, commentsResponse.comments)
|
||||||
commentsRecView.adapter = commentsAdapter
|
binding.commentsRecView.adapter = commentsAdapter
|
||||||
nextPage = commentsResponse.nextpage
|
nextPage = commentsResponse.nextpage
|
||||||
commentsLoaded = true
|
commentsLoaded = true
|
||||||
isLoading = false
|
isLoading = false
|
||||||
@ -1176,37 +1307,35 @@ class PlayerFragment : Fragment() {
|
|||||||
if (isInPictureInPictureMode) {
|
if (isInPictureInPictureMode) {
|
||||||
exoPlayerView.hideController()
|
exoPlayerView.hideController()
|
||||||
exoPlayerView.useController = false
|
exoPlayerView.useController = false
|
||||||
with(motionLayout) {
|
binding.linLayout.visibility = View.GONE
|
||||||
|
|
||||||
|
with(binding.playerMotionLayout) {
|
||||||
getConstraintSet(R.id.start).constrainHeight(R.id.player, -1)
|
getConstraintSet(R.id.start).constrainHeight(R.id.player, -1)
|
||||||
enableTransition(R.id.yt_transition, false)
|
enableTransition(R.id.yt_transition, false)
|
||||||
}
|
}
|
||||||
view?.findViewById<ConstraintLayout>(R.id.main_container)?.isClickable = true
|
binding.mainContainer.isClickable = true
|
||||||
view?.findViewById<LinearLayout>(R.id.top_bar)?.visibility = View.GONE
|
|
||||||
val mainActivity = activity as MainActivity
|
val mainActivity = activity as MainActivity
|
||||||
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||||
isFullScreen = false
|
isFullScreen = false
|
||||||
} else {
|
} else {
|
||||||
with(motionLayout) {
|
with(binding.playerMotionLayout) {
|
||||||
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
|
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
|
||||||
enableTransition(R.id.yt_transition, true)
|
enableTransition(R.id.yt_transition, true)
|
||||||
}
|
}
|
||||||
exoPlayerView.showController()
|
|
||||||
exoPlayerView.useController = true
|
exoPlayerView.useController = true
|
||||||
view?.findViewById<ConstraintLayout>(R.id.main_container)?.isClickable = false
|
binding.linLayout.visibility = View.VISIBLE
|
||||||
view?.findViewById<LinearLayout>(R.id.top_bar)?.visibility = View.VISIBLE
|
binding.mainContainer.isClickable = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onUserLeaveHint() {
|
fun onUserLeaveHint() {
|
||||||
val bounds = Rect()
|
val bounds = Rect()
|
||||||
val scrollView = view?.findViewById<ScrollView>(R.id.player_scrollView)
|
binding.playerScrollView.getHitRect(bounds)
|
||||||
scrollView?.getHitRect(bounds)
|
|
||||||
|
|
||||||
if (SDK_INT >= Build.VERSION_CODES.O &&
|
if (SDK_INT >= Build.VERSION_CODES.O &&
|
||||||
exoPlayer.isPlaying && (
|
exoPlayer.isPlaying && (binding.playerScrollView.getLocalVisibleRect(bounds) || isFullScreen)
|
||||||
scrollView?.getLocalVisibleRect(bounds) == true ||
|
|
||||||
isFullScreen
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
activity?.enterPictureInPictureMode(updatePipParams())
|
activity?.enterPictureInPictureMode(updatePipParams())
|
||||||
}
|
}
|
||||||
|
@ -5,27 +5,27 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ProgressBar
|
|
||||||
import android.widget.ScrollView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.adapters.PlaylistAdapter
|
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 com.github.libretube.util.RetrofitInstance
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class PlaylistFragment : Fragment() {
|
class PlaylistFragment : Fragment() {
|
||||||
private val TAG = "PlaylistFragment"
|
private val TAG = "PlaylistFragment"
|
||||||
|
private lateinit var binding: FragmentPlaylistBinding
|
||||||
|
|
||||||
private var playlistId: String? = null
|
private var playlistId: String? = null
|
||||||
var nextPage: String? = null
|
var nextPage: String? = null
|
||||||
var playlistAdapter: PlaylistAdapter? = null
|
private var playlistAdapter: PlaylistAdapter? = null
|
||||||
var isLoading = true
|
private var isLoading = true
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
arguments?.let {
|
arguments?.let {
|
||||||
@ -37,24 +37,22 @@ class PlaylistFragment : Fragment() {
|
|||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View {
|
||||||
// Inflate the layout for this fragment
|
binding = FragmentPlaylistBinding.inflate(layoutInflater, container, false)
|
||||||
return inflater.inflate(R.layout.fragment_playlist, container, false)
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
playlistId = playlistId!!.replace("/playlist?list=", "")
|
playlistId = playlistId!!.replace("/playlist?list=", "")
|
||||||
val recyclerView = view.findViewById<RecyclerView>(R.id.playlist_recView)
|
binding.playlistRecView.layoutManager = LinearLayoutManager(context)
|
||||||
recyclerView.layoutManager = LinearLayoutManager(context)
|
|
||||||
|
|
||||||
val progressBar = view.findViewById<ProgressBar>(R.id.playlist_progress)
|
binding.playlistProgress.visibility = View.VISIBLE
|
||||||
progressBar.visibility = View.VISIBLE
|
fetchPlaylist()
|
||||||
fetchPlaylist(view)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchPlaylist(view: View) {
|
private fun fetchPlaylist() {
|
||||||
fun run() {
|
fun run() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
@ -70,16 +68,24 @@ class PlaylistFragment : Fragment() {
|
|||||||
nextPage = response.nextpage
|
nextPage = response.nextpage
|
||||||
isLoading = false
|
isLoading = false
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
view.findViewById<ProgressBar>(R.id.playlist_progress).visibility = View.GONE
|
binding.playlistProgress.visibility = View.GONE
|
||||||
view.findViewById<TextView>(R.id.playlist_name).text = response.name
|
binding.playlistName.text = response.name
|
||||||
view.findViewById<TextView>(R.id.playlist_uploader).text = response.uploader
|
binding.playlistUploader.text = response.uploader
|
||||||
view.findViewById<TextView>(R.id.playlist_totVideos).text =
|
binding.playlistTotVideos.text =
|
||||||
getString(R.string.videoCount, response.videos.toString())
|
getString(R.string.videoCount, response.videos.toString())
|
||||||
|
|
||||||
val user = PreferenceHelper.getUsername(requireContext())
|
val user = PreferenceHelper.getUsername(requireContext())
|
||||||
var isOwner = false
|
// check whether the user owns the playlist
|
||||||
if (response.uploaderUrl == null && response.uploader.equals(user, true)) {
|
val isOwner = response.uploaderUrl == null &&
|
||||||
isOwner = true
|
response.uploader.equals(user, true)
|
||||||
|
|
||||||
|
// show playlist options
|
||||||
|
binding.optionsMenu.setOnClickListener {
|
||||||
|
val optionsDialog =
|
||||||
|
PlaylistOptionsDialog(playlistId!!, isOwner, requireContext())
|
||||||
|
optionsDialog.show(childFragmentManager, "PlaylistOptionsDialog")
|
||||||
}
|
}
|
||||||
|
|
||||||
playlistAdapter = PlaylistAdapter(
|
playlistAdapter = PlaylistAdapter(
|
||||||
response.relatedStreams!!.toMutableList(),
|
response.relatedStreams!!.toMutableList(),
|
||||||
playlistId!!,
|
playlistId!!,
|
||||||
@ -87,12 +93,11 @@ class PlaylistFragment : Fragment() {
|
|||||||
requireActivity(),
|
requireActivity(),
|
||||||
childFragmentManager
|
childFragmentManager
|
||||||
)
|
)
|
||||||
view.findViewById<RecyclerView>(R.id.playlist_recView).adapter = playlistAdapter
|
binding.playlistRecView.adapter = playlistAdapter
|
||||||
val scrollView = view.findViewById<ScrollView>(R.id.playlist_scrollview)
|
binding.playlistScrollview.viewTreeObserver
|
||||||
scrollView.viewTreeObserver
|
|
||||||
.addOnScrollChangedListener {
|
.addOnScrollChangedListener {
|
||||||
if (scrollView.getChildAt(0).bottom
|
if (binding.playlistScrollview.getChildAt(0).bottom
|
||||||
== (scrollView.height + scrollView.scrollY)
|
== (binding.playlistScrollview.height + binding.playlistScrollview.scrollY)
|
||||||
) {
|
) {
|
||||||
// scroll view is at bottom
|
// scroll view is at bottom
|
||||||
if (nextPage != null && !isLoading) {
|
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.EditorInfo
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView.GONE
|
import android.widget.TextView.GONE
|
||||||
import android.widget.TextView.OnEditorActionListener
|
import android.widget.TextView.OnEditorActionListener
|
||||||
import android.widget.TextView.VISIBLE
|
import android.widget.TextView.VISIBLE
|
||||||
@ -20,28 +19,26 @@ import androidx.fragment.app.Fragment
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.activities.hideKeyboard
|
||||||
import com.github.libretube.adapters.SearchAdapter
|
import com.github.libretube.adapters.SearchAdapter
|
||||||
import com.github.libretube.adapters.SearchHistoryAdapter
|
import com.github.libretube.adapters.SearchHistoryAdapter
|
||||||
import com.github.libretube.adapters.SearchSuggestionsAdapter
|
import com.github.libretube.adapters.SearchSuggestionsAdapter
|
||||||
import com.github.libretube.hideKeyboard
|
import com.github.libretube.databinding.FragmentSearchBinding
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
import com.github.libretube.util.RetrofitInstance
|
import com.github.libretube.util.RetrofitInstance
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class SearchFragment : Fragment() {
|
class SearchFragment : Fragment() {
|
||||||
private val TAG = "SearchFragment"
|
private val TAG = "SearchFragment"
|
||||||
|
private lateinit var binding: FragmentSearchBinding
|
||||||
|
|
||||||
private var selectedFilter = 0
|
private var selectedFilter = 0
|
||||||
private var apiSearchFilter = "all"
|
private var apiSearchFilter = "all"
|
||||||
private var nextPage: String? = null
|
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 searchAdapter: SearchAdapter? = null
|
||||||
private var isLoading: Boolean = true
|
private var isLoading: Boolean = true
|
||||||
private var isFetchingSearch: Boolean = false
|
private var isFetchingSearch: Boolean = false
|
||||||
@ -56,27 +53,21 @@ class SearchFragment : Fragment() {
|
|||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View {
|
||||||
// Inflate the layout for this fragment
|
binding = FragmentSearchBinding.inflate(layoutInflater, container, false)
|
||||||
return inflater.inflate(R.layout.fragment_search, container, false)
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
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
|
var tempSelectedItem = 0
|
||||||
|
|
||||||
clearSearchButton.setOnClickListener {
|
binding.clearSearchImageView.setOnClickListener {
|
||||||
autoTextView.text.clear()
|
binding.autoCompleteTextView.text.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
filterImageView.setOnClickListener {
|
binding.filterMenuImageView.setOnClickListener {
|
||||||
val filterOptions = arrayOf(
|
val filterOptions = arrayOf(
|
||||||
getString(R.string.all),
|
getString(R.string.all),
|
||||||
getString(R.string.videos),
|
getString(R.string.videos),
|
||||||
@ -108,7 +99,7 @@ class SearchFragment : Fragment() {
|
|||||||
7 -> "music_playlists"
|
7 -> "music_playlists"
|
||||||
else -> "all"
|
else -> "all"
|
||||||
}
|
}
|
||||||
fetchSearch(autoTextView.text.toString())
|
fetchSearch(binding.autoCompleteTextView.text.toString())
|
||||||
}
|
}
|
||||||
.setNegativeButton(getString(R.string.cancel), null)
|
.setNegativeButton(getString(R.string.cancel), null)
|
||||||
.create()
|
.create()
|
||||||
@ -116,16 +107,16 @@ class SearchFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// show search history
|
// show search history
|
||||||
historyRecView.layoutManager = LinearLayoutManager(view.context)
|
binding.historyRecycler.layoutManager = LinearLayoutManager(view.context)
|
||||||
showHistory()
|
showHistory()
|
||||||
|
|
||||||
searchRecView.layoutManager = GridLayoutManager(view.context, 1)
|
binding.searchRecycler.layoutManager = GridLayoutManager(view.context, 1)
|
||||||
autoTextView.requestFocus()
|
binding.autoCompleteTextView.requestFocus()
|
||||||
val imm =
|
val imm =
|
||||||
requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
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(
|
override fun beforeTextChanged(
|
||||||
s: CharSequence?,
|
s: CharSequence?,
|
||||||
start: Int,
|
start: Int,
|
||||||
@ -136,18 +127,15 @@ class SearchFragment : Fragment() {
|
|||||||
|
|
||||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||||
if (s!! != "") {
|
if (s!! != "") {
|
||||||
searchRecView.adapter = null
|
binding.searchRecycler.adapter = null
|
||||||
|
|
||||||
searchRecView.viewTreeObserver
|
binding.searchRecycler.viewTreeObserver
|
||||||
.addOnScrollChangedListener {
|
.addOnScrollChangedListener {
|
||||||
if (!searchRecView.canScrollVertically(1)) {
|
if (!binding.searchRecycler.canScrollVertically(1)) {
|
||||||
fetchNextSearchItems(autoTextView.text.toString())
|
fetchNextSearchItems(binding.autoCompleteTextView.text.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fetchSuggestions(s.toString(), binding.autoCompleteTextView)
|
||||||
GlobalScope.launch {
|
|
||||||
fetchSuggestions(s.toString(), autoTextView)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,13 +145,13 @@ class SearchFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
autoTextView.setOnEditorActionListener(
|
binding.autoCompleteTextView.setOnEditorActionListener(
|
||||||
OnEditorActionListener { _, actionId, _ ->
|
OnEditorActionListener { _, actionId, _ ->
|
||||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
searchRecView.visibility = VISIBLE
|
binding.searchRecycler.visibility = VISIBLE
|
||||||
historyRecView.visibility = GONE
|
binding.historyRecycler.visibility = GONE
|
||||||
fetchSearch(autoTextView.text.toString())
|
fetchSearch(binding.autoCompleteTextView.text.toString())
|
||||||
return@OnEditorActionListener true
|
return@OnEditorActionListener true
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
@ -174,8 +162,8 @@ class SearchFragment : Fragment() {
|
|||||||
private fun fetchSuggestions(query: String, autoTextView: EditText) {
|
private fun fetchSuggestions(query: String, autoTextView: EditText) {
|
||||||
fun run() {
|
fun run() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
searchRecView.visibility = GONE
|
binding.searchRecycler.visibility = GONE
|
||||||
historyRecView.visibility = VISIBLE
|
binding.historyRecycler.visibility = VISIBLE
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.getSuggestions(query)
|
RetrofitInstance.api.getSuggestions(query)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
@ -188,13 +176,16 @@ class SearchFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
val suggestionsAdapter =
|
val suggestionsAdapter =
|
||||||
SearchSuggestionsAdapter(response, autoTextView, this@SearchFragment)
|
SearchSuggestionsAdapter(response, autoTextView, this@SearchFragment)
|
||||||
historyRecView.adapter = suggestionsAdapter
|
binding.historyRecycler.adapter = suggestionsAdapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isFetchingSearch) run()
|
if (!isFetchingSearch) run()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fetchSearch(query: String) {
|
fun fetchSearch(query: String) {
|
||||||
|
runOnUiThread {
|
||||||
|
binding.historyRecycler.visibility = GONE
|
||||||
|
}
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
isFetchingSearch = true
|
isFetchingSearch = true
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
@ -211,10 +202,9 @@ class SearchFragment : Fragment() {
|
|||||||
nextPage = response.nextpage
|
nextPage = response.nextpage
|
||||||
if (response.items!!.isNotEmpty()) {
|
if (response.items!!.isNotEmpty()) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
historyRecView.visibility = GONE
|
binding.searchRecycler.visibility = VISIBLE
|
||||||
searchRecView.visibility = VISIBLE
|
|
||||||
searchAdapter = SearchAdapter(response.items, childFragmentManager)
|
searchAdapter = SearchAdapter(response.items, childFragmentManager)
|
||||||
searchRecView.adapter = searchAdapter
|
binding.searchRecycler.adapter = searchAdapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addToHistory(query)
|
addToHistory(query)
|
||||||
@ -265,12 +255,17 @@ class SearchFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun showHistory() {
|
private fun showHistory() {
|
||||||
searchRecView.visibility = GONE
|
binding.searchRecycler.visibility = GONE
|
||||||
val historyList = PreferenceHelper.getHistory(requireContext())
|
val historyList = PreferenceHelper.getHistory(requireContext())
|
||||||
if (historyList.isNotEmpty()) {
|
if (historyList.isNotEmpty()) {
|
||||||
historyRecView.adapter =
|
binding.historyRecycler.adapter =
|
||||||
SearchHistoryAdapter(requireContext(), historyList, autoTextView, this)
|
SearchHistoryAdapter(
|
||||||
historyRecView.visibility = VISIBLE
|
requireContext(),
|
||||||
|
historyList,
|
||||||
|
binding.autoCompleteTextView,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
binding.historyRecycler.visibility = VISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,11 +5,7 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.ProgressBar
|
import android.widget.ProgressBar
|
||||||
import android.widget.RelativeLayout
|
|
||||||
import android.widget.ScrollView
|
|
||||||
import android.widget.TextView
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
@ -17,21 +13,23 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.adapters.SubscriptionAdapter
|
import com.github.libretube.adapters.SubscriptionAdapter
|
||||||
import com.github.libretube.adapters.SubscriptionChannelAdapter
|
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 com.github.libretube.util.RetrofitInstance
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class Subscriptions : Fragment() {
|
class SubscriptionsFragment : Fragment() {
|
||||||
val TAG = "SubFragment"
|
val TAG = "SubFragment"
|
||||||
|
private lateinit var binding: FragmentSubscriptionsBinding
|
||||||
|
|
||||||
lateinit var token: String
|
lateinit var token: String
|
||||||
var isLoaded = false
|
private var isLoaded = false
|
||||||
private var subscriptionAdapter: SubscriptionAdapter? = null
|
private var subscriptionAdapter: SubscriptionAdapter? = null
|
||||||
private var refreshLayout: SwipeRefreshLayout? = null
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
arguments?.let {
|
arguments?.let {
|
||||||
@ -42,88 +40,76 @@ class Subscriptions : Fragment() {
|
|||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View {
|
||||||
// Inflate the layout for this fragment
|
binding = FragmentSubscriptionsBinding.inflate(layoutInflater, container, false)
|
||||||
return inflater.inflate(R.layout.fragment_subscriptions, container, false)
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
token = PreferenceHelper.getToken(requireContext())
|
token = PreferenceHelper.getToken(requireContext())
|
||||||
refreshLayout = view.findViewById(R.id.sub_refresh)
|
|
||||||
if (token != "") {
|
if (token != "") {
|
||||||
view.findViewById<RelativeLayout>(R.id.loginOrRegister).visibility = View.GONE
|
binding.loginOrRegister.visibility = View.GONE
|
||||||
refreshLayout?.isEnabled = true
|
binding.subRefresh.isEnabled = true
|
||||||
|
|
||||||
var progressBar = view.findViewById<ProgressBar>(R.id.sub_progress)
|
binding.subProgress.visibility = View.VISIBLE
|
||||||
progressBar.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(
|
val grid = PreferenceHelper.getString(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
"grid",
|
"grid",
|
||||||
resources.getInteger(R.integer.grid_items).toString()
|
resources.getInteger(R.integer.grid_items).toString()
|
||||||
)!!
|
)!!
|
||||||
feedRecView.layoutManager = GridLayoutManager(view.context, grid.toInt())
|
binding.subFeed.layoutManager = GridLayoutManager(view.context, grid.toInt())
|
||||||
fetchFeed(feedRecView, progressBar, view)
|
fetchFeed(binding.subFeed, binding.subProgress)
|
||||||
|
|
||||||
refreshLayout?.setOnRefreshListener {
|
binding.subRefresh.setOnRefreshListener {
|
||||||
fetchChannels(channelRecView)
|
fetchChannels(binding.subChannels)
|
||||||
fetchFeed(feedRecView, progressBar, view)
|
fetchFeed(binding.subFeed, binding.subProgress)
|
||||||
}
|
}
|
||||||
|
|
||||||
var toggleSubs = view.findViewById<RelativeLayout>(R.id.toggle_subs)
|
binding.toggleSubs.visibility = View.VISIBLE
|
||||||
val arrowImageView = view.findViewById<ImageView>(R.id.toggle)
|
|
||||||
|
|
||||||
toggleSubs.visibility = View.VISIBLE
|
|
||||||
var loadedSubbedChannels = false
|
var loadedSubbedChannels = false
|
||||||
|
|
||||||
toggleSubs.setOnClickListener {
|
binding.toggleSubs.setOnClickListener {
|
||||||
arrowImageView.animate().rotationBy(180F).setDuration(100).start()
|
binding.toggle.animate().rotationBy(180F).setDuration(100).start()
|
||||||
if (!channelRecView.isVisible) {
|
if (!binding.subChannels.isVisible) {
|
||||||
if (!loadedSubbedChannels) {
|
if (!loadedSubbedChannels) {
|
||||||
channelRecView?.layoutManager = LinearLayoutManager(context)
|
binding.subChannels.layoutManager = LinearLayoutManager(context)
|
||||||
fetchChannels(channelRecView)
|
fetchChannels(binding.subChannels)
|
||||||
loadedSubbedChannels = true
|
loadedSubbedChannels = true
|
||||||
}
|
}
|
||||||
channelRecView.visibility = View.VISIBLE
|
binding.subChannels.visibility = View.VISIBLE
|
||||||
feedRecView.visibility = View.GONE
|
binding.subFeed.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
channelRecView.visibility = View.GONE
|
binding.subChannels.visibility = View.GONE
|
||||||
feedRecView.visibility = View.VISIBLE
|
binding.subFeed.visibility = View.VISIBLE
|
||||||
|
|
||||||
// toggle button
|
|
||||||
val image = view.findViewById<ImageView>(R.id.toggle)
|
|
||||||
image.clearAnimation()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val scrollView = view.findViewById<ScrollView>(R.id.scrollview_sub)
|
binding.scrollviewSub.viewTreeObserver
|
||||||
scrollView.viewTreeObserver
|
|
||||||
.addOnScrollChangedListener {
|
.addOnScrollChangedListener {
|
||||||
if (scrollView.getChildAt(0).bottom
|
if (binding.scrollviewSub.getChildAt(0).bottom
|
||||||
== (scrollView.height + scrollView.scrollY)
|
== (binding.scrollviewSub.height + binding.scrollviewSub.scrollY)
|
||||||
) {
|
) {
|
||||||
// scroll view is at bottom
|
// scroll view is at bottom
|
||||||
if (isLoaded) {
|
if (isLoaded) {
|
||||||
refreshLayout?.isRefreshing = true
|
binding.subRefresh.isRefreshing = true
|
||||||
subscriptionAdapter?.updateItems()
|
subscriptionAdapter?.updateItems()
|
||||||
refreshLayout?.isRefreshing = false
|
binding.subRefresh.isRefreshing = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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() {
|
fun run() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.getFeed(token)
|
RetrofitInstance.authApi.getFeed(token)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.e(TAG, e.toString())
|
Log.e(TAG, e.toString())
|
||||||
Log.e(TAG, "IOException, you might not have internet connection")
|
Log.e(TAG, "IOException, you might not have internet connection")
|
||||||
@ -132,7 +118,7 @@ class Subscriptions : Fragment() {
|
|||||||
Log.e(TAG, "HttpException, unexpected response")
|
Log.e(TAG, "HttpException, unexpected response")
|
||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
} finally {
|
} finally {
|
||||||
refreshLayout?.isRefreshing = false
|
binding.subRefresh.isRefreshing = false
|
||||||
}
|
}
|
||||||
if (response.isNotEmpty()) {
|
if (response.isNotEmpty()) {
|
||||||
subscriptionAdapter = SubscriptionAdapter(response, childFragmentManager)
|
subscriptionAdapter = SubscriptionAdapter(response, childFragmentManager)
|
||||||
@ -140,16 +126,15 @@ class Subscriptions : Fragment() {
|
|||||||
subscriptionAdapter?.updateItems()
|
subscriptionAdapter?.updateItems()
|
||||||
} else {
|
} else {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
with(view.findViewById<ImageView>(R.id.boogh)) {
|
with(binding.boogh) {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
setImageResource(R.drawable.ic_list)
|
setImageResource(R.drawable.ic_list)
|
||||||
}
|
}
|
||||||
with(view.findViewById<TextView>(R.id.textLike)) {
|
with(binding.textLike) {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
text = getString(R.string.emptyList)
|
text = getString(R.string.emptyList)
|
||||||
}
|
}
|
||||||
view.findViewById<RelativeLayout>(R.id.loginOrRegister)
|
binding.loginOrRegister.visibility = View.VISIBLE
|
||||||
.visibility = View.VISIBLE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
progressBar.visibility = View.GONE
|
progressBar.visibility = View.GONE
|
||||||
@ -163,7 +148,7 @@ class Subscriptions : Fragment() {
|
|||||||
fun run() {
|
fun run() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.subscriptions(token)
|
RetrofitInstance.authApi.subscriptions(token)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.e(TAG, e.toString())
|
Log.e(TAG, e.toString())
|
||||||
Log.e(TAG, "IOException, you might not have internet connection")
|
Log.e(TAG, "IOException, you might not have internet connection")
|
||||||
@ -172,7 +157,7 @@ class Subscriptions : Fragment() {
|
|||||||
Log.e(TAG, "HttpException, unexpected response")
|
Log.e(TAG, "HttpException, unexpected response")
|
||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
} finally {
|
} finally {
|
||||||
refreshLayout?.isRefreshing = false
|
binding.subRefresh.isRefreshing = false
|
||||||
}
|
}
|
||||||
if (response.isNotEmpty()) {
|
if (response.isNotEmpty()) {
|
||||||
channelRecView.adapter = SubscriptionChannelAdapter(response.toMutableList())
|
channelRecView.adapter = SubscriptionChannelAdapter(response.toMutableList())
|
||||||
@ -184,13 +169,6 @@ class Subscriptions : Fragment() {
|
|||||||
run()
|
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) {
|
private fun Fragment?.runOnUiThread(action: () -> Unit) {
|
||||||
this ?: return
|
this ?: return
|
||||||
if (!isAdded) return // Fragment not attached to an Activity
|
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
|
package com.github.libretube.obj
|
||||||
|
|
||||||
import android.os.Parcel
|
|
||||||
import android.os.Parcelable
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
@ -28,68 +26,9 @@ data class Streams(
|
|||||||
val livestream: Boolean?,
|
val livestream: Boolean?,
|
||||||
val proxyUrl: String?,
|
val proxyUrl: String?,
|
||||||
val chapters: List<ChapterSegment>?
|
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(
|
constructor() : this(
|
||||||
"", "", "", "", "", "", "", "", "", "", null, -1, -1, -1, -1, emptyList(), emptyList(),
|
"", "", "", "", "", "", "", "", "", "", null, -1, -1, -1, -1, emptyList(), emptyList(),
|
||||||
emptyList(), emptyList(), null, "", 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.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.fragment.app.Fragment
|
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.DONATE_URL
|
||||||
|
import com.github.libretube.GITHUB_URL
|
||||||
import com.github.libretube.PIPED_GITHUB_URL
|
import com.github.libretube.PIPED_GITHUB_URL
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.WEBSITE_URL
|
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.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
|
||||||
class AboutFragment : Fragment() {
|
class AboutFragment : Fragment() {
|
||||||
|
private lateinit var binding: FragmentAboutBinding
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View {
|
||||||
return inflater.inflate(R.layout.fragment_about, container, false)
|
binding = FragmentAboutBinding.inflate(layoutInflater)
|
||||||
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
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)
|
val settingsActivity = activity as SettingsActivity
|
||||||
website.setOnClickListener {
|
settingsActivity.changeTopBarText(getString(R.string.about))
|
||||||
|
|
||||||
|
binding.website.setOnClickListener {
|
||||||
openLinkFromHref(WEBSITE_URL)
|
openLinkFromHref(WEBSITE_URL)
|
||||||
}
|
}
|
||||||
val authors = view.findViewById<LinearLayout>(R.id.authors)
|
binding.website.setOnLongClickListener {
|
||||||
authors.setOnClickListener {
|
val text = context?.getString(R.string.website_summary)!!
|
||||||
openLinkFromHref(AUTHORS_URL)
|
showSnackBar(text)
|
||||||
|
true
|
||||||
}
|
}
|
||||||
val piped = view.findViewById<LinearLayout>(R.id.piped)
|
|
||||||
piped.setOnClickListener {
|
binding.piped.setOnClickListener {
|
||||||
openLinkFromHref(PIPED_GITHUB_URL)
|
openLinkFromHref(PIPED_GITHUB_URL)
|
||||||
}
|
}
|
||||||
val donate = view.findViewById<LinearLayout>(R.id.donate)
|
binding.piped.setOnLongClickListener {
|
||||||
donate.setOnClickListener {
|
val text = context?.getString(R.string.piped_summary)!!
|
||||||
|
showSnackBar(text)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.donate.setOnClickListener {
|
||||||
openLinkFromHref(DONATE_URL)
|
openLinkFromHref(DONATE_URL)
|
||||||
}
|
}
|
||||||
val contributing = view.findViewById<LinearLayout>(R.id.contributing)
|
binding.donate.setOnLongClickListener {
|
||||||
contributing.setOnClickListener {
|
val text = context?.getString(R.string.donate_summary)!!
|
||||||
openLinkFromHref(CONTRIBUTING_URL)
|
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!!)
|
binding.github.setOnClickListener {
|
||||||
.setPositiveButton(getString(R.string.okay)) { _, _ -> }
|
openLinkFromHref(GITHUB_URL)
|
||||||
.setMessage(licenseHtml)
|
}
|
||||||
.create()
|
binding.github.setOnLongClickListener {
|
||||||
.show()
|
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)
|
val intent = Intent(Intent.ACTION_VIEW).setData(uri)
|
||||||
startActivity(intent)
|
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
|
package com.github.libretube.preferences
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.requireMainActivityRestart
|
import com.github.libretube.activities.SettingsActivity
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.activities.requireMainActivityRestart
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
|
||||||
class AdvancedSettings : PreferenceFragmentCompat() {
|
class AdvancedSettings : PreferenceFragmentCompat() {
|
||||||
@ -15,15 +14,24 @@ class AdvancedSettings : PreferenceFragmentCompat() {
|
|||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
setPreferencesFromResource(R.xml.advanced_settings, rootKey)
|
setPreferencesFromResource(R.xml.advanced_settings, rootKey)
|
||||||
|
|
||||||
val topBarTextView = activity?.findViewById<TextView>(R.id.topBar_textView)
|
val settingsActivity = activity as SettingsActivity
|
||||||
topBarTextView?.text = getString(R.string.advanced)
|
settingsActivity.changeTopBarText(getString(R.string.advanced))
|
||||||
|
|
||||||
|
// clear search history
|
||||||
val clearHistory = findPreference<Preference>("clear_history")
|
val clearHistory = findPreference<Preference>("clear_history")
|
||||||
clearHistory?.setOnPreferenceClickListener {
|
clearHistory?.setOnPreferenceClickListener {
|
||||||
PreferenceHelper.removePreference(requireContext(), "search_history")
|
PreferenceHelper.removePreference(requireContext(), "search_history")
|
||||||
true
|
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")
|
val resetSettings = findPreference<Preference>("reset_settings")
|
||||||
resetSettings?.setOnPreferenceClickListener {
|
resetSettings?.setOnPreferenceClickListener {
|
||||||
showResetDialog()
|
showResetDialog()
|
||||||
|
@ -1,32 +1,33 @@
|
|||||||
package com.github.libretube.preferences
|
package com.github.libretube.preferences
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.Preference
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.SwitchPreference
|
import androidx.preference.SwitchPreference
|
||||||
import com.github.libretube.R
|
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.github.libretube.util.ThemeHelper
|
||||||
|
import com.google.android.material.color.DynamicColors
|
||||||
|
|
||||||
class AppearanceSettings : PreferenceFragmentCompat() {
|
class AppearanceSettings : PreferenceFragmentCompat() {
|
||||||
private val TAG = "AppearanceSettings"
|
private val TAG = "AppearanceSettings"
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
setPreferencesFromResource(R.xml.appearance_settings, rootKey)
|
setPreferencesFromResource(R.xml.appearance_settings, rootKey)
|
||||||
|
|
||||||
val topBarTextView = activity?.findViewById<TextView>(R.id.topBar_textView)
|
val settingsActivity = activity as SettingsActivity
|
||||||
topBarTextView?.text = getString(R.string.appearance)
|
settingsActivity.changeTopBarText(getString(R.string.appearance))
|
||||||
|
|
||||||
val themeToggle = findPreference<ListPreference>("theme_togglee")
|
val themeToggle = findPreference<ListPreference>("theme_togglee")
|
||||||
themeToggle?.setOnPreferenceChangeListener { _, _ ->
|
themeToggle?.setOnPreferenceChangeListener { _, _ ->
|
||||||
requireMainActivityRestart = true
|
requireMainActivityRestart = true
|
||||||
ThemeHelper.restartMainActivity(requireContext())
|
activity?.recreate()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
val accentColor = findPreference<Preference>("accent_color")
|
val accentColor = findPreference<ListPreference>("accent_color")
|
||||||
accentColor?.setOnPreferenceChangeListener { _, _ ->
|
updateAccentColorValues(accentColor!!)
|
||||||
|
accentColor.setOnPreferenceChangeListener { _, _ ->
|
||||||
requireMainActivityRestart = true
|
requireMainActivityRestart = true
|
||||||
activity?.recreate()
|
activity?.recreate()
|
||||||
true
|
true
|
||||||
@ -50,4 +51,17 @@ class AppearanceSettings : PreferenceFragmentCompat() {
|
|||||||
true
|
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.Manifest
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.TextView
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
@ -18,12 +18,14 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
import androidx.preference.SwitchPreference
|
||||||
import com.github.libretube.R
|
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.CustomInstanceDialog
|
||||||
import com.github.libretube.dialogs.DeleteAccountDialog
|
import com.github.libretube.dialogs.DeleteAccountDialog
|
||||||
import com.github.libretube.dialogs.LoginDialog
|
import com.github.libretube.dialogs.LoginDialog
|
||||||
import com.github.libretube.requireMainActivityRestart
|
import com.github.libretube.dialogs.LogoutDialog
|
||||||
import com.github.libretube.util.PreferenceHelper
|
|
||||||
import com.github.libretube.util.RetrofitInstance
|
import com.github.libretube.util.RetrofitInstance
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import org.json.JSONTokener
|
import org.json.JSONTokener
|
||||||
@ -110,20 +112,49 @@ class InstanceSettings : PreferenceFragmentCompat() {
|
|||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
setPreferencesFromResource(R.xml.instance_settings, rootKey)
|
setPreferencesFromResource(R.xml.instance_settings, rootKey)
|
||||||
|
|
||||||
val topBarTextView = activity?.findViewById<TextView>(R.id.topBar_textView)
|
val settingsActivity = activity as SettingsActivity
|
||||||
topBarTextView?.text = getString(R.string.instance)
|
settingsActivity.changeTopBarText(getString(R.string.instance))
|
||||||
|
|
||||||
val instance = findPreference<ListPreference>("selectInstance")
|
val instance = findPreference<ListPreference>("selectInstance")
|
||||||
// fetchInstance()
|
// fetchInstance()
|
||||||
initCustomInstances()
|
initCustomInstances(instance!!)
|
||||||
instance?.setOnPreferenceChangeListener { _, newValue ->
|
instance.setOnPreferenceChangeListener { _, newValue ->
|
||||||
requireMainActivityRestart = true
|
requireMainActivityRestart = true
|
||||||
RetrofitInstance.url = newValue.toString()
|
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()
|
RetrofitInstance.lazyMgr.reset()
|
||||||
logout()
|
logout()
|
||||||
true
|
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")
|
val customInstance = findPreference<Preference>("customInstance")
|
||||||
customInstance?.setOnPreferenceClickListener {
|
customInstance?.setOnPreferenceClickListener {
|
||||||
val newFragment = CustomInstanceDialog()
|
val newFragment = CustomInstanceDialog()
|
||||||
@ -134,15 +165,23 @@ class InstanceSettings : PreferenceFragmentCompat() {
|
|||||||
val clearCustomInstances = findPreference<Preference>("clearCustomInstances")
|
val clearCustomInstances = findPreference<Preference>("clearCustomInstances")
|
||||||
clearCustomInstances?.setOnPreferenceClickListener {
|
clearCustomInstances?.setOnPreferenceClickListener {
|
||||||
PreferenceHelper.removePreference(requireContext(), "customInstances")
|
PreferenceHelper.removePreference(requireContext(), "customInstances")
|
||||||
activity?.recreate()
|
val intent = Intent(context, SettingsActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
val login = findPreference<Preference>("login_register")
|
val login = findPreference<Preference>("login_register")
|
||||||
|
val token = PreferenceHelper.getToken(requireContext())
|
||||||
|
if (token != "") login?.setTitle(R.string.logout)
|
||||||
login?.setOnPreferenceClickListener {
|
login?.setOnPreferenceClickListener {
|
||||||
requireMainActivityRestart = true
|
if (token == "") {
|
||||||
val newFragment = LoginDialog()
|
val newFragment = LoginDialog()
|
||||||
newFragment.show(childFragmentManager, "Login")
|
newFragment.show(childFragmentManager, "Login")
|
||||||
|
} else {
|
||||||
|
val newFragment = LogoutDialog()
|
||||||
|
newFragment.show(childFragmentManager, "Logout")
|
||||||
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,58 +199,12 @@ class InstanceSettings : PreferenceFragmentCompat() {
|
|||||||
|
|
||||||
val importFromYt = findPreference<Preference>("import_from_yt")
|
val importFromYt = findPreference<Preference>("import_from_yt")
|
||||||
importFromYt?.setOnPreferenceClickListener {
|
importFromYt?.setOnPreferenceClickListener {
|
||||||
val token = PreferenceHelper.getToken(requireContext())
|
importSubscriptions()
|
||||||
// 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initCustomInstances() {
|
private fun initCustomInstances(instancePref: ListPreference) {
|
||||||
val customInstances = PreferenceHelper.getCustomInstances(requireContext())
|
val customInstances = PreferenceHelper.getCustomInstances(requireContext())
|
||||||
|
|
||||||
var instanceNames = resources.getStringArray(R.array.instances)
|
var instanceNames = resources.getStringArray(R.array.instances)
|
||||||
@ -222,10 +215,9 @@ class InstanceSettings : PreferenceFragmentCompat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add custom instances to the list preference
|
// add custom instances to the list preference
|
||||||
val instance = findPreference<ListPreference>("selectInstance")
|
instancePref.entries = instanceNames
|
||||||
instance?.entries = instanceNames
|
instancePref.entryValues = instanceValues
|
||||||
instance?.entryValues = instanceValues
|
instancePref.summaryProvider =
|
||||||
instance?.summaryProvider =
|
|
||||||
Preference.SummaryProvider<ListPreference> { preference ->
|
Preference.SummaryProvider<ListPreference> { preference ->
|
||||||
val text = preference.entry
|
val text = preference.entry
|
||||||
if (TextUtils.isEmpty(text)) {
|
if (TextUtils.isEmpty(text)) {
|
||||||
@ -238,6 +230,7 @@ class InstanceSettings : PreferenceFragmentCompat() {
|
|||||||
|
|
||||||
private fun logout() {
|
private fun logout() {
|
||||||
PreferenceHelper.setToken(requireContext(), "")
|
PreferenceHelper.setToken(requireContext(), "")
|
||||||
|
Toast.makeText(context, getString(R.string.loggedout), Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchInstance() {
|
private fun fetchInstance() {
|
||||||
@ -289,12 +282,62 @@ class InstanceSettings : PreferenceFragmentCompat() {
|
|||||||
activity?.runOnUiThread(action)
|
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>) {
|
private fun subscribe(channels: List<String>) {
|
||||||
fun run() {
|
fun run() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
val token = PreferenceHelper.getToken(requireContext())
|
val token = PreferenceHelper.getToken(requireContext())
|
||||||
RetrofitInstance.api.importSubscriptions(
|
RetrofitInstance.authApi.importSubscriptions(
|
||||||
false,
|
false,
|
||||||
token,
|
token,
|
||||||
channels
|
channels
|
||||||
|
@ -8,8 +8,8 @@ import androidx.preference.Preference
|
|||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import com.github.libretube.BuildConfig
|
import com.github.libretube.BuildConfig
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.isCurrentViewMainSettings
|
import com.github.libretube.activities.isCurrentViewMainSettings
|
||||||
import com.github.libretube.requireMainActivityRestart
|
import com.github.libretube.activities.requireMainActivityRestart
|
||||||
import com.github.libretube.util.ThemeHelper
|
import com.github.libretube.util.ThemeHelper
|
||||||
import com.github.libretube.util.checkUpdate
|
import com.github.libretube.util.checkUpdate
|
||||||
|
|
||||||
@ -38,35 +38,35 @@ class MainSettings : PreferenceFragmentCompat() {
|
|||||||
val instance = findPreference<Preference>("instance")
|
val instance = findPreference<Preference>("instance")
|
||||||
instance?.setOnPreferenceClickListener {
|
instance?.setOnPreferenceClickListener {
|
||||||
val newFragment = InstanceSettings()
|
val newFragment = InstanceSettings()
|
||||||
navigateSettings(newFragment)
|
navigateToSettingsFragment(newFragment)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
val appearance = findPreference<Preference>("appearance")
|
val appearance = findPreference<Preference>("appearance")
|
||||||
appearance?.setOnPreferenceClickListener {
|
appearance?.setOnPreferenceClickListener {
|
||||||
val newFragment = AppearanceSettings()
|
val newFragment = AppearanceSettings()
|
||||||
navigateSettings(newFragment)
|
navigateToSettingsFragment(newFragment)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
val sponsorBlock = findPreference<Preference>("sponsorblock")
|
val sponsorBlock = findPreference<Preference>("sponsorblock")
|
||||||
sponsorBlock?.setOnPreferenceClickListener {
|
sponsorBlock?.setOnPreferenceClickListener {
|
||||||
val newFragment = SponsorBlockSettings()
|
val newFragment = SponsorBlockSettings()
|
||||||
navigateSettings(newFragment)
|
navigateToSettingsFragment(newFragment)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
val player = findPreference<Preference>("player")
|
val player = findPreference<Preference>("player")
|
||||||
player?.setOnPreferenceClickListener {
|
player?.setOnPreferenceClickListener {
|
||||||
val newFragment = PlayerSettings()
|
val newFragment = PlayerSettings()
|
||||||
navigateSettings(newFragment)
|
navigateToSettingsFragment(newFragment)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
val advanced = findPreference<Preference>("advanced")
|
val advanced = findPreference<Preference>("advanced")
|
||||||
advanced?.setOnPreferenceClickListener {
|
advanced?.setOnPreferenceClickListener {
|
||||||
val newFragment = AdvancedSettings()
|
val newFragment = AdvancedSettings()
|
||||||
navigateSettings(newFragment)
|
navigateToSettingsFragment(newFragment)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,12 +80,19 @@ class MainSettings : PreferenceFragmentCompat() {
|
|||||||
val about = findPreference<Preference>("about")
|
val about = findPreference<Preference>("about")
|
||||||
about?.setOnPreferenceClickListener {
|
about?.setOnPreferenceClickListener {
|
||||||
val newFragment = AboutFragment()
|
val newFragment = AboutFragment()
|
||||||
navigateSettings(newFragment)
|
navigateToSettingsFragment(newFragment)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
val community = findPreference<Preference>("community")
|
||||||
|
community?.setOnPreferenceClickListener {
|
||||||
|
val newFragment = CommunityFragment()
|
||||||
|
navigateToSettingsFragment(newFragment)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateSettings(newFragment: Fragment) {
|
private fun navigateToSettingsFragment(newFragment: Fragment) {
|
||||||
isCurrentViewMainSettings = false
|
isCurrentViewMainSettings = false
|
||||||
parentFragmentManager.beginTransaction()
|
parentFragmentManager.beginTransaction()
|
||||||
.replace(R.id.settings, newFragment)
|
.replace(R.id.settings, newFragment)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package com.github.libretube.preferences
|
package com.github.libretube.preferences
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.activities.SettingsActivity
|
||||||
|
|
||||||
class PlayerSettings : PreferenceFragmentCompat() {
|
class PlayerSettings : PreferenceFragmentCompat() {
|
||||||
val TAG = "PlayerSettings"
|
val TAG = "PlayerSettings"
|
||||||
@ -11,7 +11,7 @@ class PlayerSettings : PreferenceFragmentCompat() {
|
|||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
setPreferencesFromResource(R.xml.player_settings, rootKey)
|
setPreferencesFromResource(R.xml.player_settings, rootKey)
|
||||||
|
|
||||||
val topBarTextView = activity?.findViewById<TextView>(R.id.topBar_textView)
|
val settingsActivity = activity as SettingsActivity
|
||||||
topBarTextView?.text = getString(R.string.player)
|
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.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.github.libretube.obj.CustomInstance
|
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.common.reflect.TypeToken
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
object PreferenceHelper {
|
object PreferenceHelper {
|
||||||
|
private val TAG = "PreferenceHelper"
|
||||||
|
|
||||||
fun setString(context: Context, key: String?, value: String?) {
|
fun setString(context: Context, key: String?, value: String?) {
|
||||||
val editor = getDefaultSharedPreferencesEditor(context)
|
val editor = getDefaultSharedPreferencesEditor(context)
|
||||||
editor.putString(key, value)
|
editor.putString(key, value)
|
||||||
@ -122,6 +127,97 @@ object PreferenceHelper {
|
|||||||
editor.putStringSet("search_history", set).apply()
|
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 {
|
private fun getDefaultSharedPreferences(context: Context): SharedPreferences {
|
||||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
package com.github.libretube.preferences
|
package com.github.libretube.preferences
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.activities.SettingsActivity
|
||||||
|
|
||||||
class SponsorBlockSettings : PreferenceFragmentCompat() {
|
class SponsorBlockSettings : PreferenceFragmentCompat() {
|
||||||
private val TAG = "SponsorBlockSettings"
|
private val TAG = "SponsorBlockSettings"
|
||||||
@ -11,7 +11,7 @@ class SponsorBlockSettings : PreferenceFragmentCompat() {
|
|||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
setPreferencesFromResource(R.xml.sponsorblock_settings, rootKey)
|
setPreferencesFromResource(R.xml.sponsorblock_settings, rootKey)
|
||||||
|
|
||||||
val topBarTextView = activity?.findViewById<TextView>(R.id.topBar_textView)
|
val settingsActivity = activity as SettingsActivity
|
||||||
topBarTextView?.text = getString(R.string.sponsorblock)
|
settingsActivity.changeTopBarText(getString(R.string.sponsorblock))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ import androidx.core.app.NotificationCompat
|
|||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import com.arthenica.ffmpegkit.FFmpegKit
|
import com.arthenica.ffmpegkit.FFmpegKit
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.util.PreferenceHelper
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
var IS_DOWNLOAD_RUNNING = false
|
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.content.Context
|
||||||
import android.support.v4.media.session.MediaSessionCompat
|
import android.support.v4.media.session.MediaSessionCompat
|
||||||
import com.github.libretube.obj.Streams
|
import com.github.libretube.obj.Streams
|
||||||
import com.github.libretube.util.DescriptionAdapter
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
import com.github.libretube.util.RetrofitInstance
|
|
||||||
import com.google.android.exoplayer2.C
|
import com.google.android.exoplayer2.C
|
||||||
import com.google.android.exoplayer2.ExoPlayer
|
import com.google.android.exoplayer2.ExoPlayer
|
||||||
import com.google.android.exoplayer2.MediaItem
|
import com.google.android.exoplayer2.MediaItem
|
||||||
|
import com.google.android.exoplayer2.Player
|
||||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||||
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
|
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
|
||||||
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
||||||
@ -42,7 +43,7 @@ class BackgroundMode {
|
|||||||
/**
|
/**
|
||||||
* The [PlayerNotificationManager] to load the [mediaSession] content on it.
|
* 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]
|
* The [AudioAttributes] handle the audio focus of the [player]
|
||||||
@ -63,9 +64,45 @@ class BackgroundMode {
|
|||||||
.setAudioAttributes(audioAttributes, true)
|
.setAudioAttributes(audioAttributes, true)
|
||||||
.build()
|
.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)
|
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.
|
* Initializes the [playerNotification] attached to the [player] and shows it.
|
||||||
*/
|
*/
|
||||||
@ -82,10 +119,12 @@ class BackgroundMode {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
playerNotification.apply {
|
playerNotification?.apply {
|
||||||
setPlayer(player)
|
setPlayer(player)
|
||||||
setUsePreviousAction(false)
|
|
||||||
setUseNextAction(false)
|
setUseNextAction(false)
|
||||||
|
setUsePreviousAction(false)
|
||||||
|
setUseStopAction(true)
|
||||||
|
setColorized(true)
|
||||||
setMediaSessionToken(mediaSession.sessionToken)
|
setMediaSessionToken(mediaSession.sessionToken)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,7 +149,11 @@ class BackgroundMode {
|
|||||||
/**
|
/**
|
||||||
* Gets the video data and prepares the [player].
|
* 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 {
|
runBlocking {
|
||||||
val job = launch {
|
val job = launch {
|
||||||
response = RetrofitInstance.api.getStreams(videoId)
|
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.content.Intent
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
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.Player
|
||||||
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
@ -83,8 +83,8 @@ class DescriptionAdapter(
|
|||||||
return try {
|
return try {
|
||||||
val resizedBitmap = Bitmap.createScaledBitmap(
|
val resizedBitmap = Bitmap.createScaledBitmap(
|
||||||
bitmap,
|
bitmap,
|
||||||
1080,
|
bitmap.width,
|
||||||
1080,
|
bitmap.width,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
resizedBitmap
|
resizedBitmap
|
||||||
|
@ -2,25 +2,28 @@ package com.github.libretube.util
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.telephony.TelephonyManager
|
||||||
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
object LocaleHelper {
|
object LocaleHelper {
|
||||||
|
|
||||||
fun updateLanguage(context: Context) {
|
fun updateLanguage(context: Context) {
|
||||||
val languageName = PreferenceHelper.getString(context, "language", "en")
|
val languageName = PreferenceHelper.getString(context, "language", "sys")
|
||||||
if (languageName != "") {
|
if (languageName == "sys") updateLocaleConf(context, Locale.getDefault())
|
||||||
setLanguage(context, languageName!!)
|
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) {
|
private fun updateLocaleConf(context: Context, locale: Locale) {
|
||||||
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()
|
|
||||||
}
|
|
||||||
// Change API Language
|
// Change API Language
|
||||||
Locale.setDefault(locale)
|
Locale.setDefault(locale)
|
||||||
|
|
||||||
@ -35,4 +38,51 @@ object LocaleHelper {
|
|||||||
}
|
}
|
||||||
res.updateConfiguration(conf, dm)
|
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 java.util.*
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
class ResettableLazyManager {
|
class ResettableLazyManager {
|
||||||
// we synchronize to make sure the timing of a reset() call and new inits do not collide
|
// 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) {
|
fun register(managed: Resettable) {
|
||||||
synchronized(managedDelegates) {
|
synchronized(managedDelegates) {
|
||||||
@ -38,7 +38,7 @@ class ResettableLazy<PROPTYPE>(val manager: ResettableLazyManager, val init: ()
|
|||||||
lazyHolder = makeInitBlock()
|
lazyHolder = makeInitBlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun makeInitBlock(): Lazy<PROPTYPE> {
|
private fun makeInitBlock(): Lazy<PROPTYPE> {
|
||||||
return lazy {
|
return lazy {
|
||||||
manager.register(this)
|
manager.register(this)
|
||||||
init()
|
init()
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package com.github.libretube.util
|
package com.github.libretube.util
|
||||||
|
|
||||||
import com.github.libretube.resettableLazy
|
|
||||||
import com.github.libretube.resettableManager
|
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.jackson.JacksonConverterFactory
|
import retrofit2.converter.jackson.JacksonConverterFactory
|
||||||
|
|
||||||
object RetrofitInstance {
|
object RetrofitInstance {
|
||||||
lateinit var url: String
|
lateinit var url: String
|
||||||
|
lateinit var authUrl: String
|
||||||
val lazyMgr = resettableManager()
|
val lazyMgr = resettableManager()
|
||||||
val api: PipedApi by resettableLazy(lazyMgr) {
|
val api: PipedApi by resettableLazy(lazyMgr) {
|
||||||
Retrofit.Builder()
|
Retrofit.Builder()
|
||||||
@ -15,4 +14,11 @@ object RetrofitInstance {
|
|||||||
.build()
|
.build()
|
||||||
.create(PipedApi::class.java)
|
.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.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.text.Spanned
|
||||||
|
import android.util.TypedValue
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.text.HtmlCompat
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
|
|
||||||
object ThemeHelper {
|
object ThemeHelper {
|
||||||
|
|
||||||
@ -18,7 +22,7 @@ object ThemeHelper {
|
|||||||
|
|
||||||
private fun updateAccentColor(context: Context) {
|
private fun updateAccentColor(context: Context) {
|
||||||
when (PreferenceHelper.getString(context, "accent_color", "purple")) {
|
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)
|
"red" -> context.setTheme(R.style.Theme_Red)
|
||||||
"blue" -> context.setTheme(R.style.Theme_Blue)
|
"blue" -> context.setTheme(R.style.Theme_Blue)
|
||||||
"yellow" -> context.setTheme(R.style.Theme_Yellow)
|
"yellow" -> context.setTheme(R.style.Theme_Yellow)
|
||||||
@ -69,4 +73,19 @@ object ThemeHelper {
|
|||||||
intent?.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK
|
intent?.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
context.startActivity(intent)
|
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.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
|
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
||||||
import com.google.android.exoplayer2.ui.StyledPlayerView
|
import com.google.android.exoplayer2.ui.StyledPlayerView
|
||||||
|
|
||||||
internal class CustomExoPlayerView(
|
internal class CustomExoPlayerView(
|
||||||
context: Context,
|
context: Context,
|
||||||
attributeSet: AttributeSet? = null
|
attributeSet: AttributeSet? = null
|
||||||
) : StyledPlayerView(context, attributeSet) {
|
) : StyledPlayerView(context, attributeSet) {
|
||||||
|
val binding: ExoStyledPlayerControlViewBinding = ExoStyledPlayerControlViewBinding.bind(this)
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
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.content.Context
|
||||||
import android.util.AttributeSet
|
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.content.Context
|
||||||
import android.graphics.Rect
|
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"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:tint="@android:color/white"
|
android:tint="?android:attr/colorControlNormal"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<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
|
<ImageView
|
||||||
android:id="@+id/noInternet_settingsImageView"
|
android:id="@+id/noInternet_settingsImageView"
|
||||||
android:layout_width="30dp"
|
android:layout_width="33dp"
|
||||||
android:layout_height="30dp"
|
android:layout_height="33dp"
|
||||||
android:layout_margin="16dp"
|
android:layout_margin="16dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:padding="3dp"
|
||||||
android:src="@drawable/ic_settings"
|
android:src="@drawable/ic_settings"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="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_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:layout_marginLeft="10dp"
|
android:layout_marginStart="10dp"
|
||||||
android:layout_toEndOf="@id/subscription_channel_image"
|
android:layout_toEndOf="@id/subscription_channel_image"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:text="Channel Name"
|
android:text="Channel Name"
|
||||||
android:textSize="16dp" />
|
android:textSize="16sp" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/subscription_subscribe"
|
android:id="@+id/subscription_subscribe"
|
||||||
@ -35,7 +35,7 @@
|
|||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:backgroundTint="?attr/colorOnPrimary"
|
android:backgroundTint="?attr/colorOnPrimary"
|
||||||
android:text="@string/unsubscribe"
|
android:text="@string/unsubscribe"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="@android:color/white"
|
||||||
android:textSize="11dp"
|
android:textSize="11sp"
|
||||||
app:cornerRadius="20dp" />
|
app:cornerRadius="20dp" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
@ -3,8 +3,9 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="100dp"
|
android:layout_width="100dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="5dp"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="5dp">
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
android:id="@+id/chapter_image"
|
android:id="@+id/chapter_image"
|
||||||
|
@ -8,12 +8,16 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
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
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
@ -106,8 +110,7 @@
|
|||||||
android:id="@+id/replies_recView"
|
android:id="@+id/replies_recView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="16dp"
|
android:background="@null"
|
||||||
android:layout_marginBottom="10dp"
|
|
||||||
android:nestedScrollingEnabled="false" />
|
android:nestedScrollingEnabled="false" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
<Button
|
<Button
|
||||||
android:id="@+id/logout"
|
android:id="@+id/logout"
|
||||||
style="@style/CustomDialogButton"
|
style="@style/CustomDialogButton"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
android:text="@string/logout" />
|
android:text="@string/logout" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -1,26 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright 2020 The Android Open Source Project
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
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.
|
|
||||||
-->
|
|
||||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
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
|
<View
|
||||||
android:id="@id/exo_controls_background"
|
android:id="@id/exo_controls_background"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@ -28,7 +9,7 @@
|
|||||||
android:background="@color/exo_black_opacity_60" />
|
android:background="@color/exo_black_opacity_60" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/top_bar"
|
android:id="@+id/exo_top_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/exo_styled_bottom_bar_height"
|
android:layout_height="@dimen/exo_styled_bottom_bar_height"
|
||||||
android:layout_gravity="top"
|
android:layout_gravity="top"
|
||||||
@ -49,11 +30,10 @@
|
|||||||
android:id="@+id/close_imageButton"
|
android:id="@+id/close_imageButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginRight="10dp"
|
android:layout_marginRight="5dp"
|
||||||
android:background="#00FFFFFF"
|
android:background="#00FFFFFF"
|
||||||
android:padding="@dimen/exo_icon_padding"
|
android:padding="@dimen/exo_icon_padding"
|
||||||
android:src="@drawable/ic_close"
|
android:src="@drawable/ic_close"
|
||||||
android:visibility="gone"
|
|
||||||
app:tint="@android:color/white" />
|
app:tint="@android:color/white" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
@ -110,7 +90,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:padding="@dimen/exo_icon_padding"
|
android:padding="@dimen/exo_icon_padding"
|
||||||
android:text="HLS"
|
android:text="@string/hls"
|
||||||
android:textColor="#FFFFFF" />
|
android:textColor="#FFFFFF" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
@ -132,7 +112,6 @@
|
|||||||
android:layout_height="@dimen/exo_styled_bottom_bar_height"
|
android:layout_height="@dimen/exo_styled_bottom_bar_height"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:layout_marginTop="@dimen/exo_styled_bottom_bar_margin_top"
|
android:layout_marginTop="@dimen/exo_styled_bottom_bar_margin_top"
|
||||||
android:background="@color/exo_bottom_bar_background"
|
|
||||||
android:layoutDirection="ltr">
|
android:layoutDirection="ltr">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@ -182,7 +161,8 @@
|
|||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/fullscreen"
|
android:id="@+id/fullscreen"
|
||||||
style="@style/ExoStyledControls.Button.Bottom.FullScreen"
|
style="@style/ExoStyledControls.Button.Bottom.FullScreen"
|
||||||
android:src="@drawable/ic_fullscreen" />
|
android:src="@drawable/ic_fullscreen"
|
||||||
|
app:tint="@android:color/white" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@ -190,7 +170,6 @@
|
|||||||
android:id="@+id/progress_bar"
|
android:id="@+id/progress_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="15dp"
|
android:layout_height="15dp"
|
||||||
android:layout_above="@id/exo_basic_controls"
|
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:layout_marginLeft="10dp"
|
android:layout_marginLeft="10dp"
|
||||||
android:layout_marginRight="10dp"
|
android:layout_marginRight="10dp"
|
||||||
@ -200,7 +179,6 @@
|
|||||||
android:id="@id/exo_progress"
|
android:id="@id/exo_progress"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="15dp"
|
android:layout_height="15dp"
|
||||||
android:background="@color/exo_bottom_bar_background"
|
|
||||||
app:bar_height="2dp"
|
app:bar_height="2dp"
|
||||||
app:played_color="?attr/colorOnSecondary"
|
app:played_color="?attr/colorOnSecondary"
|
||||||
app:scrubber_color="?attr/colorOnPrimary"
|
app:scrubber_color="?attr/colorOnPrimary"
|
||||||
@ -233,25 +211,11 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:padding="@dimen/exo_styled_controls_padding">
|
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
|
<ImageButton
|
||||||
android:id="@id/exo_play_pause"
|
android:id="@id/exo_play_pause"
|
||||||
style="@style/ExoStyledControls.Button.Center.PlayPause"
|
style="@style/ExoStyledControls.Button.Center.PlayPause"
|
||||||
android:scaleX="1.1"
|
android:background="?android:selectableItemBackgroundBorderless"
|
||||||
android:scaleY="1.1" />
|
app:tint="@android:color/white" />
|
||||||
|
|
||||||
<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" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<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_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
@ -8,125 +9,113 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<ImageView
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
style="@style/roundedImageViewRounded"
|
android:layout_width="90dp"
|
||||||
android:layout_width="80dp"
|
android:layout_height="90dp"
|
||||||
android:layout_height="80dp"
|
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_marginTop="30dp"
|
android:layout_marginTop="50dp"
|
||||||
android:src="@mipmap/ic_launcher" />
|
android:src="@mipmap/ic_launcher_round"
|
||||||
|
app:shapeAppearanceOverlay="@style/CircleImageView" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_marginTop="20dp"
|
android:layout_marginTop="20dp"
|
||||||
android:layout_marginBottom="20dp"
|
android:layout_marginBottom="40dp"
|
||||||
android:text="LibreTube"
|
android:text="LibreTube"
|
||||||
android:textSize="27sp"
|
android:textSize="24sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<LinearLayout
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/website"
|
android:id="@+id/website"
|
||||||
style="@style/AboutItem">
|
style="@style/AboutCard">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout style="@style/AboutItem">
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text="@string/website"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:layout_width="match_parent"
|
style="@style/AboutImageView"
|
||||||
android:layout_height="match_parent"
|
android:src="@drawable/ic_region" />
|
||||||
android:text="@string/website_summary" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
<TextView
|
||||||
|
style="@style/AboutTextView"
|
||||||
|
android:text="@string/website" />
|
||||||
|
|
||||||
<LinearLayout
|
</LinearLayout>
|
||||||
android:id="@+id/contributing"
|
|
||||||
style="@style/AboutItem">
|
|
||||||
|
|
||||||
<TextView
|
</com.google.android.material.card.MaterialCardView>
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text="@string/contributing"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/github"
|
||||||
android:layout_height="match_parent"
|
style="@style/AboutCard">
|
||||||
android:text="@string/contributing_summary" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
<LinearLayout style="@style/AboutItem">
|
||||||
|
|
||||||
<LinearLayout
|
<ImageView
|
||||||
android:id="@+id/authors"
|
style="@style/AboutImageView"
|
||||||
style="@style/AboutItem">
|
android:src="@drawable/ic_github" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
style="@style/AboutTextView"
|
||||||
android:layout_height="match_parent"
|
android:text="@string/github" />
|
||||||
android:text="@string/authors"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
</LinearLayout>
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text="@string/authors_summary" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<LinearLayout
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/piped"
|
android:id="@+id/piped"
|
||||||
style="@style/AboutItem">
|
style="@style/AboutCard">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout style="@style/AboutItem">
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text="@string/piped"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:layout_width="match_parent"
|
style="@style/AboutImageView"
|
||||||
android:layout_height="match_parent"
|
android:src="@drawable/ic_piped" />
|
||||||
android:text="@string/piped_summary" />
|
|
||||||
|
|
||||||
</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"
|
android:id="@+id/donate"
|
||||||
style="@style/AboutItem">
|
style="@style/AboutCard">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout style="@style/AboutItem">
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text="@string/donate"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:layout_width="match_parent"
|
style="@style/AboutImageView"
|
||||||
android:layout_height="match_parent"
|
android:src="@drawable/ic_donate" />
|
||||||
android:text="@string/donate_summary" />
|
|
||||||
|
|
||||||
</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"
|
android:id="@+id/license"
|
||||||
style="@style/AboutItem">
|
style="@style/AboutCard">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout style="@style/AboutItem">
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text="@string/license"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:layout_width="match_parent"
|
style="@style/AboutImageView"
|
||||||
android:layout_height="match_parent"
|
android:src="@drawable/ic_license" />
|
||||||
android:text="@string/license_summary" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
<TextView
|
||||||
|
style="@style/AboutTextView"
|
||||||
|
android:text="@string/license" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/channel_refresh"
|
android:id="@+id/channel_refresh"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -66,7 +66,7 @@
|
|||||||
android:id="@+id/channel_subs"
|
android:id="@+id/channel_subs"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="left"
|
android:layout_gravity="start"
|
||||||
android:text="@string/app_name"
|
android:text="@string/app_name"
|
||||||
android:textSize="12sp" />
|
android:textSize="12sp" />
|
||||||
|
|
||||||
@ -76,11 +76,10 @@
|
|||||||
android:id="@+id/channel_subscribe"
|
android:id="@+id/channel_subscribe"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="center_horizontal"
|
||||||
android:backgroundTint="?attr/colorOnPrimary"
|
android:backgroundTint="?attr/colorOnPrimary"
|
||||||
android:drawableLeft="@drawable/ic_bell_small"
|
android:drawableStart="@drawable/ic_bell_small"
|
||||||
android:drawableTint="?android:attr/textColorPrimary"
|
android:drawableTint="@android:color/white"
|
||||||
android:text="@string/subscribe"
|
android:text="@string/subscribe"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
android:textSize="11sp" />
|
android:textSize="11sp" />
|
||||||
@ -113,4 +112,4 @@
|
|||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".fragments.Home">
|
tools:context=".fragments.HomeFragment">
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progressBar"
|
android:id="@+id/progressBar"
|
||||||
|
@ -4,17 +4,15 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".fragments.Library">
|
tools:context=".fragments.LibraryFragment">
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/loginOrRegister2"
|
android:id="@+id/loginOrRegister2"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:layout_centerHorizontal="true"
|
|
||||||
android:layout_centerVertical="true">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/boogh2"
|
android:id="@+id/boogh"
|
||||||
android:layout_width="100dp"
|
android:layout_width="100dp"
|
||||||
android:layout_height="100dp"
|
android:layout_height="100dp"
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
@ -22,18 +20,19 @@
|
|||||||
android:src="@drawable/ic_login" />
|
android:src="@drawable/ic_login" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/textLike2"
|
android:id="@+id/text_like"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/boogh2"
|
android:layout_below="@id/boogh"
|
||||||
android:layout_centerHorizontal="true"
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_marginHorizontal="10dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="@string/please_login"
|
android:text="@string/please_login"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<com.github.libretube.util.CustomSwipeToRefresh
|
<com.github.libretube.views.CustomSwipeToRefresh
|
||||||
android:id="@+id/playlist_refresh"
|
android:id="@+id/playlist_refresh"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
@ -45,9 +44,35 @@
|
|||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
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
|
<RelativeLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@ -62,7 +87,7 @@
|
|||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</com.github.libretube.util.CustomSwipeToRefresh>
|
</com.github.libretube.views.CustomSwipeToRefresh>
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/create_playlist"
|
android:id="@+id/create_playlist"
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/playerMotionLayout"
|
android:id="@+id/playerMotionLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layoutDescription="@xml/player_scene"
|
app:layoutDescription="@xml/player_scene"
|
||||||
tools:context=".PlayerFragment">
|
tools:context=".fragments.PlayerFragment">
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
android:id="@+id/player_scrollView"
|
android:id="@+id/player_scrollView"
|
||||||
@ -193,13 +193,13 @@
|
|||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="25dp"
|
android:layout_height="25dp"
|
||||||
android:padding="2dp"
|
android:padding="2dp"
|
||||||
android:src="@drawable/ic_vlc" />
|
android:src="@drawable/ic_player" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="VLC" />
|
android:text="@string/open" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@ -281,7 +281,10 @@
|
|||||||
style="@style/Widget.Material3.CardView.Elevated"
|
style="@style/Widget.Material3.CardView.Elevated"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
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">
|
app:cardCornerRadius="18dp">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
@ -319,14 +322,13 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/comments_toggle"
|
android:layout_below="@id/comments_toggle"
|
||||||
android:layout_marginLeft="20dp"
|
android:layout_marginTop="10dp"
|
||||||
android:layout_marginTop="20dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:layout_marginRight="20dp"
|
|
||||||
android:nestedScrollingEnabled="false"
|
android:nestedScrollingEnabled="false"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/player_recView"
|
android:id="@+id/related_rec_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/comments_recView"
|
android:layout_below="@id/comments_recView"
|
||||||
@ -347,21 +349,59 @@
|
|||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:background="?attr/colorSurface"
|
android:background="?attr/colorSurface"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHeight_percent="0.35"
|
app:layout_constraintHeight_percent="0.3"
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<com.github.libretube.util.CustomExoPlayerView
|
<com.github.libretube.views.CustomExoPlayerView
|
||||||
android:id="@+id/player"
|
android:id="@+id/player"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
android:background="#000000"
|
android:background="?attr/colorSurface"
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/main_container"
|
app:layout_constraintBottom_toBottomOf="@id/main_container"
|
||||||
app:layout_constraintStart_toStartOf="@id/main_container"
|
app:layout_constraintStart_toStartOf="@id/main_container"
|
||||||
app:layout_constraintTop_toTopOf="@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
|
<ImageView
|
||||||
android:id="@+id/close_imageView"
|
android:id="@+id/close_imageView"
|
||||||
@ -402,4 +442,4 @@
|
|||||||
app:layout_constraintStart_toEndOf="@+id/player"
|
app:layout_constraintStart_toEndOf="@+id/player"
|
||||||
app:layout_constraintTop_toTopOf="@+id/play_imageView" />
|
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:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/playlist_name"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="8dp"
|
android:orientation="horizontal"
|
||||||
android:text=""
|
android:padding="8dp">
|
||||||
android:textSize="24sp"
|
|
||||||
android:textStyle="bold" />
|
<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
|
<TextView
|
||||||
android:id="@+id/playlist_uploader"
|
android:id="@+id/playlist_uploader"
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".fragments.Subscriptions">
|
tools:context=".fragments.SubscriptionsFragment">
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/sub_progress"
|
android:id="@+id/sub_progress"
|
||||||
@ -33,13 +33,14 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/boogh"
|
android:layout_below="@id/boogh"
|
||||||
android:layout_centerHorizontal="true"
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_marginHorizontal="10dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="@string/please_login"
|
android:text="@string/please_login"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<com.github.libretube.util.CustomSwipeToRefresh
|
<com.github.libretube.views.CustomSwipeToRefresh
|
||||||
android:id="@+id/sub_refresh"
|
android:id="@+id/sub_refresh"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
@ -101,5 +102,5 @@
|
|||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</com.github.libretube.util.CustomSwipeToRefresh>
|
</com.github.libretube.views.CustomSwipeToRefresh>
|
||||||
</RelativeLayout>
|
</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"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:background="?android:attr/selectableItemBackground">
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
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