47 Commits

Author SHA1 Message Date
0f8c3a7126 Release v0.7.0 2024-04-15 22:18:02 -06:00
d6aeadc489 Fix lint errors 2024-04-15 22:07:14 -06:00
826bb79431 Merge branch 'feature/threads' into 'master'
Feature/threads

See merge request agosto182/p2play!16
2024-04-16 03:55:53 +00:00
2484406cc7 Feature/threads 2024-04-16 03:55:53 +00:00
0705d6dd80 Merge branch 'feature/playbackService' into 'master'
Feature/playback service

See merge request agosto182/p2play!15
2024-04-11 03:15:06 +00:00
dd54d214ff Feature/playback service 2024-04-11 03:15:06 +00:00
48738f100a fix all lint errors 2024-04-06 18:03:23 -06:00
b6e1a979ca Fix lint errors automated 2024-04-06 14:38:04 -06:00
96d8ae19c6 Fix buffer, forward and rewind video 2024-04-05 21:44:56 -06:00
8856984dc3 Fix share and report icons color 2024-04-05 21:30:37 -06:00
2ffb7daede Fix dialogs design 2024-04-05 21:30:22 -06:00
f0b5ba10e8 Fix for wrong pagination 2024-04-05 18:47:08 -06:00
4d2644674e Add 2fa login support 2024-04-05 18:46:57 -06:00
6a8d3baccb Update setting view 2024-03-31 16:59:57 -06:00
ac191c04d3 Updates of descriptions and screenshots 2024-03-28 19:06:38 -06:00
15aaae2fa1 Fix video preview sizes 2024-03-28 18:41:49 -06:00
42be7c4f77 Merge branch 'feature/add-exo-player' into 'master'
Feature/add exo player

See merge request agosto182/p2play!14
2024-03-28 23:58:25 +00:00
e022fe5e96 Add fullscreen to exoplayer 2024-03-28 17:29:32 -06:00
1f90e21f68 Fix: Fullscreen bug 2024-03-27 21:31:41 -06:00
c104bf32da Merge branch 'master' into feature/add-exo-player 2024-03-27 20:57:17 -06:00
a3cd0b92ed Merge branch 'feature/update-video-view' into 'master'
Feature/update video view

See merge request agosto182/p2play!13
2024-03-28 02:54:14 +00:00
2de1bde4cd Feature/update video view 2024-03-28 02:54:14 +00:00
6f0b97dbc4 Exo player first integration 2024-03-27 20:44:52 -06:00
2fcd3b21f8 Add isLive indicator 2024-03-25 19:57:49 -06:00
eeb20f1f9d Update function name to the correct one 2024-03-25 19:57:22 -06:00
0d9bd5410a Update to sdk 32 2024-03-25 18:53:01 -06:00
40b193a816 Update duration data to a floating text 2024-03-22 18:12:14 -06:00
c500ba9aa4 Merge branch 'androidx' into 'master'
Androidx

See merge request agosto182/p2play!12
2024-03-22 23:23:16 +00:00
35aa0fcea1 Androidx 2024-03-22 23:23:16 +00:00
ba0a298ea9 Add new theme to Channel view 2024-03-22 17:20:42 -06:00
815bafaee1 Update edittext to materialTextview 2024-03-22 16:55:52 -06:00
ef5c97dce1 Finish update to androidx 2024-03-22 12:28:39 -06:00
b6ef9b8aca Normalize theme 2024-03-21 17:47:54 -06:00
bf35a75877 Add material theme 2024-03-21 14:23:36 -06:00
65e061fd25 First changes to androidx 2024-03-20 21:04:16 -06:00
ffbc491f4c update metadata 2024-03-19 22:04:26 -06:00
da1d3e301e Fix error getting local videos 2024-03-19 21:42:26 -06:00
6784b5ebbe Update fastlane descriptions 2024-03-18 19:24:57 -06:00
e70108a7ae Update README 2024-03-18 18:53:06 -06:00
2b7d92ac37 Merge branch 'updateAndroidSDK' into 'master'
Update android sdk

See merge request agosto182/p2play!11
2024-03-19 00:45:04 +00:00
4d337074fb Update android sdk 2024-03-19 00:45:04 +00:00
cd27342c74 Merge branch 'master' into 'master'
updated strings

See merge request agosto182/p2play!10
2024-03-18 17:23:49 +00:00
ab1bb48dcc updated strings 2024-03-18 17:23:49 +00:00
0a648ef4bb Merge branch 'master' into 'master'
Add Italian translation

See merge request agosto182/p2play!9
2020-06-26 21:00:36 +00:00
3e41ddd367 Add Italian translation 2020-06-26 21:00:36 +00:00
9c90983ff4 Merge branch 'patch-1' into 'master'
Update README.md

See merge request agosto182/p2play!8
2020-02-17 00:59:25 +00:00
dad366eebf Update README.md 2020-01-11 13:42:03 +00:00
110 changed files with 2658 additions and 1163 deletions

2
.editorconfig Normal file
View File

@ -0,0 +1,2 @@
[*.{kt,kts}]
ktlint_code_style = intellij_idea

View File

@ -1,20 +1,20 @@
# P2Play # P2Play
P2Play is an Android Application for Peertube. P2Play is an unoficial Peertube android application.
[What is Peertube?](https://github.com/Chocobozzz/PeerTube/) [What is Peertube?](https://github.com/Chocobozzz/PeerTube/)
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/org.libre.agosto.p2play/)
## Screenshots ## Screenshots
![screenshot](screenshots/screenshot.png) <img src="metadata/en-US/images/phoneScreenshots/1.jpg" alt="Screenshot" width="14%"/>
![screenshot](screenshots/screenshot2.png) <img src="metadata/en-US/images/phoneScreenshots/2.jpg" alt="Screenshot" width="14%"/>
![screenshot](screenshots/screenshot3.png) <img src="metadata/en-US/images/phoneScreenshots/3.jpg" alt="Screenshot" width="14%"/>
![screenshot](screenshots/screenshot4.png) <img src="metadata/en-US/images/phoneScreenshots/4.jpg" alt="Screenshot" width="14%"/>
![screenshot](screenshots/screenshot5.png) <img src="metadata/en-US/images/phoneScreenshots/5.jpg" alt="Screenshot" width="14%">/
![screenshot](screenshots/screenshot6.png) <img src="metadata/en-US/images/phoneScreenshots/6.jpg" alt="Screenshot" width="14%"/>
## Documentation
Comming soon!
## Realeases (apk's) ## Realeases (apk's)
@ -37,33 +37,40 @@ Comming soon!
- Share videos - Share videos
- Report videos - Report videos
- Peertube profiles - Peertube profiles
- Day/Night theme
## What to do? (in next version) ## What to do? (on incomming updates)
- Playlists
- Manage subscriptions
- Better commentaries
- Account channels view
- Notifications - Notifications
- Upload videos
## Demostrations ## Demostrations
Demostration P2play Beta 0.2: https://peertube.video/videos/watch/730fa68e-32c4-4cdb-a7bb-1a819c9d3a46 P2play beta 0.6.0: https://fediverse.tv/w/suFPkm9zJstSrQU7WmVARv
Demostration P2Play Beta 0.1: https://peertube.video/videos/watch/2eb7b953-0b1b-4019-9300-817539f5f4e8 **Dead links**
[Spanish] Demostracion P2Play Beta 0.1: https://peertube.video/videos/watch/d6a7da26-d3dd-43aa-ad5c-7d032603c848 ~~Demostration P2play Beta 0.2: https://peertube.video/videos/watch/730fa68e-32c4-4cdb-a7bb-1a819c9d3a46~~
~~Demostration P2Play Beta 0.1: https://peertube.video/videos/watch/2eb7b953-0b1b-4019-9300-817539f5f4e8~~
~~[Spanish] Demostracion P2Play Beta 0.1: https://peertube.video/videos/watch/d6a7da26-d3dd-43aa-ad5c-7d032603c848~~
## Contact ## Contact
You can follow our accounts for get news and contact with the developers. You can follow our accounts to get news and contact with the developer(s).
- WriteFreely (ActivityPub): https://personaljournal.ca/p2play/ - WriteFreely (ActivityPub): https://personaljournal.ca/p2play/
- Peertube Channel: https://peertube.video/video-channels/90df4e5f-c834-4720-a5d7-c9faa0af0af5/videos
## About ## About
P2Play is made in Android Studio with Kotlin code. P2Play is made in Android Studio with Kotlin languaje.
![kotlin](https://weblizar.com/blog/wp-content/uploads/2017/11/Kotlin-A-New-Programming-Platform-For-Android-Developers.png) ![kotlin](https://weblizar.com/blog/wp-content/uploads/2017/11/Kotlin-A-New-Programming-Platform-For-Android-Developers.png)
### Developers ### Developers
- Ivan Agosto: [https://nerdica.net/profile/agosto182](https://gnusocial.ml/agosto182) - Ivan Agosto: [https://mast.lat/@agosto182](https://mast.lat/@agosto182)
## License ## License
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

View File

@ -1,19 +1,20 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' plugins {
id "com.android.application"
apply plugin: 'kotlin-android-extensions' id "kotlin-android"
id "kotlin-android-extensions"
}
android { android {
compileSdkVersion 27 compileSdkVersion 34
defaultConfig { defaultConfig {
applicationId "org.libre.agosto.p2play" applicationId "org.libre.agosto.p2play"
minSdkVersion 21 minSdkVersion 26
//noinspection OldTargetApi targetSdkVersion 32
targetSdkVersion 27 versionCode 10
versionCode 7 versionName "0.7.0"
versionName "0.5.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
} }
buildTypes { buildTypes {
release { release {
@ -21,26 +22,33 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
buildToolsVersion '28.0.3'
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_6 targetCompatibility JavaVersion.VERSION_1_8
} }
lintOptions { namespace 'org.libre.agosto.p2play'
checkReleaseBuilds false lint {
abortOnError false abortOnError false
checkReleaseBuilds false
} }
} }
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'com.android.support:appcompat-v7:27.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:support-v4:27.1.0'
implementation 'com.android.support:design:27.1.0'
implementation 'com.squareup.picasso:picasso:2.71828' implementation 'com.squareup.picasso:picasso:2.71828'
testImplementation 'junit:junit:4.12' implementation 'androidx.appcompat:appcompat:1.6.1'
androidTestImplementation 'com.android.support.test:runner:1.0.2' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.preference:preference:1.2.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation 'androidx.media3:media3-exoplayer:1.1.1'
implementation 'androidx.media3:media3-exoplayer-dash:1.1.1'
implementation 'androidx.media3:media3-ui:1.1.1'
implementation 'androidx.media3:media3-exoplayer-hls:1.1.1'
implementation "androidx.media3:media3-session:1.1.1"
} }

View File

@ -1,24 +0,0 @@
package org.libre.agosto.p2play
import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getTargetContext()
assertEquals("org.libre.agosto.p2play", appContext.packageName)
}
}

View File

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android">
package="org.libre.agosto.p2play">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<application <application
android:allowBackup="true" android:allowBackup="true"
@ -11,33 +12,67 @@
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/P2playTheme"> android:theme="@style/Theme.P2play">
<activity android:name=".ChannelActivity" <activity
android:theme="@style/P2playTheme.noBar"></activity> android:name=".SettingsActivity2"
android:exported="false"
android:label="@string/title_activity_settings"
android:theme="@style/Theme.P2play">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity" />
</activity>
<activity
android:name=".ChannelActivity"
android:exported="false"
android:theme="@style/Theme.P2play.NoActionBar" />
<activity <activity
android:name=".SplashActivity" android:name=".SplashActivity"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"> android:exported="true"
android:theme="@style/Theme.P2play.NoActionBar">
<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" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".HostActivity" /> <activity
android:name=".HostActivity"
android:exported="false"
android:theme="@style/Theme.P2play.NoActionBar" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:theme="@style/P2playTheme.NoActionBar" /> android:exported="false"
android:theme="@style/Theme.P2play.NoActionBar" />
<activity <activity
android:name=".ReproductorActivity" android:name=".ReproductorActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:exported="false"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:theme="@style/P2playTheme.noBar" /> android:theme="@style/Theme.P2play.NoActionBar" />
<activity android:name=".LoginActivity" /> <activity
<activity android:name=".RegisterActivity" /> android:name=".LoginActivity"
<activity android:name=".AboutActivity" /> android:exported="false" />
<activity
android:name=".RegisterActivity"
android:exported="false" />
<activity
android:name=".AboutActivity"
android:exported="false" />
<activity <activity
android:name=".SettingsActivity" android:name=".SettingsActivity"
android:label="@string/title_activity_settings" /> android:exported="false"
android:label="@string/title_activity_settings"
android:theme="@style/Theme.P2play" />
<service
android:name=".services.PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
</application> </application>
</manifest> </manifest>

View File

@ -1,8 +1,7 @@
package org.libre.agosto.p2play package org.libre.agosto.p2play
import android.support.v7.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import android.text.method.LinkMovementMethod import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_about.* import kotlinx.android.synthetic.main.activity_about.*
class AboutActivity : AppCompatActivity() { class AboutActivity : AppCompatActivity() {
@ -11,7 +10,7 @@ class AboutActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_about) setContentView(R.layout.activity_about)
aboutUrl.text = "https://"+ManagerSingleton.url+"/about/instance" aboutUrl.text = "https://" + ManagerSingleton.url + "/about/instance"
aboutLabel.text = aboutLabel.text.toString() + " " + this.packageManager.getPackageInfo(this.packageName, 0).versionName aboutLabel.text = aboutLabel.text.toString() + " " + this.packageManager.getPackageInfo(this.packageName, 0).versionName
} }

View File

@ -3,13 +3,13 @@ package org.libre.agosto.p2play
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.preference.PreferenceActivity import android.preference.PreferenceActivity
import android.support.annotation.LayoutRes
import android.support.v7.app.ActionBar
import android.support.v7.app.AppCompatDelegate
import android.support.v7.widget.Toolbar
import android.view.MenuInflater import android.view.MenuInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.appcompat.app.ActionBar
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.widget.Toolbar
/** /**
* A [android.preference.PreferenceActivity] which implements and proxies the necessary calls * A [android.preference.PreferenceActivity] which implements and proxies the necessary calls

View File

@ -1,11 +1,11 @@
package org.libre.agosto.p2play package org.libre.agosto.p2play
import android.os.AsyncTask import android.os.AsyncTask
import android.support.v7.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_channel.* import kotlinx.android.synthetic.main.activity_channel.*
import org.libre.agosto.p2play.adapters.VideosAdapter import org.libre.agosto.p2play.adapters.VideosAdapter
@ -14,14 +14,13 @@ import org.libre.agosto.p2play.ajax.Channels
import org.libre.agosto.p2play.ajax.Videos import org.libre.agosto.p2play.ajax.Videos
import org.libre.agosto.p2play.models.ChannelModel import org.libre.agosto.p2play.models.ChannelModel
import org.libre.agosto.p2play.models.VideoModel import org.libre.agosto.p2play.models.VideoModel
class ChannelActivity : AppCompatActivity() { class ChannelActivity : AppCompatActivity() {
private lateinit var channelId: String private lateinit var channelId: String
private lateinit var channel: ChannelModel private lateinit var channel: ChannelModel
private var isSubcribed: Boolean = false private var isSubcribed: Boolean = false
private val _channel = Channels() private val channelService = Channels()
private val _videos = Videos() private val videosService = Videos()
private val _actions = Actions() private val actionsService = Actions()
private lateinit var recyclerView: RecyclerView private lateinit var recyclerView: RecyclerView
private lateinit var viewAdapter: RecyclerView.Adapter<VideosAdapter.ViewHolder> private lateinit var viewAdapter: RecyclerView.Adapter<VideosAdapter.ViewHolder>
@ -31,7 +30,7 @@ class ChannelActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_channel) setContentView(R.layout.activity_channel)
channelId = this.intent.extras.getString("channel") channelId = this.intent.extras?.getString("channel")!!
viewManager = LinearLayoutManager(this) viewManager = LinearLayoutManager(this)
@ -47,7 +46,7 @@ class ChannelActivity : AppCompatActivity() {
getSubscription() getSubscription()
getVideos() getVideos()
if(ManagerSingleton.user.status == 1) { if (ManagerSingleton.user.status == 1) {
subcriptionBtn.visibility = View.VISIBLE subcriptionBtn.visibility = View.VISIBLE
getSubscription() getSubscription()
} }
@ -55,28 +54,28 @@ class ChannelActivity : AppCompatActivity() {
private fun getChannel() { private fun getChannel() {
AsyncTask.execute { AsyncTask.execute {
channel = _channel.getChannelInfo(channelId) channel = channelService.getChannelInfo(channelId)
runOnUiThread { runOnUiThread {
usernameProfile.text = channel.name usernameProfile.text = channel.name
hostTxt.text = channel.host hostTxt.text = channel.host
subcriptionsTxt.text = channel.followers.toString() subcriptionsTxt.text = channel.followers.toString()
if(channel.channelImg != "") if (channel.channelImg != "") {
Picasso.get().load("https://${ManagerSingleton.url}${channel.channelImg}").into(channelImg) Picasso.get().load("https://${ManagerSingleton.url}${channel.channelImg}").into(channelImg)
}
} }
} }
} }
private fun subscribe() { private fun subscribe() {
AsyncTask.execute { AsyncTask.execute {
val res = _actions.subscribe(ManagerSingleton.token.token, channel.getAccount()) val res = actionsService.subscribe(ManagerSingleton.token.token, channel.getAccount())
runOnUiThread { runOnUiThread {
if(res == 1){ if (res == 1) {
subcriptionBtn.text = getString(R.string.unSubscribeBtn) subcriptionBtn.text = getString(R.string.unSubscribeBtn)
ManagerSingleton.Toast(getString(R.string.subscribeMsg), this) ManagerSingleton.toast(getString(R.string.subscribeMsg), this)
getSubscription() getSubscription()
} } else {
else { ManagerSingleton.toast(getString(R.string.errorMsg), this)
ManagerSingleton.Toast(getString(R.string.errorMsg), this)
} }
} }
} }
@ -84,35 +83,34 @@ class ChannelActivity : AppCompatActivity() {
private fun unSubscribe() { private fun unSubscribe() {
AsyncTask.execute { AsyncTask.execute {
val res = _actions.unSubscribe(ManagerSingleton.token.token, channel.getAccount()) val res = actionsService.unSubscribe(ManagerSingleton.token.token, channel.getAccount())
runOnUiThread { runOnUiThread {
if(res == 1){ if (res == 1) {
subcriptionBtn.text = getString(R.string.subscribeBtn) subcriptionBtn.text = getString(R.string.subscribeBtn)
ManagerSingleton.Toast(getString(R.string.unSubscribeMsg), this) ManagerSingleton.toast(getString(R.string.unSubscribeMsg), this)
getSubscription() getSubscription()
} } else {
else { ManagerSingleton.toast(getString(R.string.errorMsg), this)
ManagerSingleton.Toast(getString(R.string.errorMsg), this)
} }
} }
} }
} }
private fun subscribeAction() { private fun subscribeAction() {
if(isSubcribed) if (isSubcribed) {
unSubscribe() unSubscribe()
else } else {
subscribe() subscribe()
}
} }
private fun getSubscription() { private fun getSubscription() {
AsyncTask.execute { AsyncTask.execute {
isSubcribed = _actions.getSubscription(ManagerSingleton.token.token, channel.getAccount()) isSubcribed = actionsService.getSubscription(ManagerSingleton.token.token, channel.getAccount())
runOnUiThread { runOnUiThread {
if(isSubcribed){ if (isSubcribed) {
subcriptionBtn.text = getText(R.string.unSubscribeBtn) subcriptionBtn.text = getText(R.string.unSubscribeBtn)
} } else {
else {
subcriptionBtn.text = getText(R.string.subscribeBtn) subcriptionBtn.text = getText(R.string.subscribeBtn)
} }
} }
@ -121,7 +119,7 @@ class ChannelActivity : AppCompatActivity() {
private fun getVideos() { private fun getVideos() {
AsyncTask.execute { AsyncTask.execute {
val videos = _videos.channelVideos(channel.getAccount(), 0) val videos = videosService.channelVideos(channel.getAccount(), 0)
runOnUiThread { runOnUiThread {
initRecycler(videos) initRecycler(videos)
} }
@ -129,7 +127,7 @@ class ChannelActivity : AppCompatActivity() {
} }
// Generic function for set data to RecyclerView // Generic function for set data to RecyclerView
private fun initRecycler(data: ArrayList<VideoModel>){ private fun initRecycler(data: ArrayList<VideoModel>) {
// val data = arrayListOf<VideoModel>() // val data = arrayListOf<VideoModel>()
viewAdapter = VideosAdapter(data) viewAdapter = VideosAdapter(data)

View File

@ -8,11 +8,11 @@ import android.database.sqlite.SQLiteOpenHelper
import org.libre.agosto.p2play.models.TokenModel import org.libre.agosto.p2play.models.TokenModel
import org.libre.agosto.p2play.models.UserModel import org.libre.agosto.p2play.models.UserModel
class Database(context:Context): SQLiteOpenHelper(context,"p2play",null,1) { class Database(context: Context) : SQLiteOpenHelper(context, "p2play", null, 1) {
val dbName = "p2play" val dbName = "p2play"
private val dbUsers = "CREATE TABLE users(id INTEGER PRIMARY KEY AUTOINCREMENT, uuid INTEGER, username varchar(30), " + private val dbUsers = "CREATE TABLE users(id INTEGER PRIMARY KEY AUTOINCREMENT, uuid INTEGER, username varchar(30), " +
"nsfw INTEGER, email string, followers INTEGER, avatar string, status integer)" "nsfw INTEGER, email string, followers INTEGER, avatar string, status integer)"
private val dbTokens = "CREATE TABLE tokens(id INTEGER PRIMARY KEY AUTOINCREMENT, token STRING, refresh_token STRING, status INTEGER)" private val dbTokens = "CREATE TABLE tokens(id INTEGER PRIMARY KEY AUTOINCREMENT, token STRING, refresh_token STRING, status INTEGER)"
override fun onCreate(db: SQLiteDatabase?) { override fun onCreate(db: SQLiteDatabase?) {
db?.execSQL(dbUsers) db?.execSQL(dbUsers)
@ -28,12 +28,12 @@ class Database(context:Context): SQLiteOpenHelper(context,"p2play",null,1) {
fun newToken(token: TokenModel): Boolean { fun newToken(token: TokenModel): Boolean {
val db = writableDatabase val db = writableDatabase
this.closeTokens() this.closeTokens()
val newToken=ContentValues() val newToken = ContentValues()
newToken.put("token", token.token) newToken.put("token", token.token)
newToken.put("refresh_token", token.refresh_token) newToken.put("refresh_token", token.refresh_token)
newToken.put("status", token.status) newToken.put("status", token.status)
db.insert("tokens",null,newToken) db.insert("tokens", null, newToken)
return true return true
} }
@ -41,7 +41,7 @@ class Database(context:Context): SQLiteOpenHelper(context,"p2play",null,1) {
fun newUser(user: UserModel): Boolean { fun newUser(user: UserModel): Boolean {
val db = writableDatabase val db = writableDatabase
this.closeUsers() this.closeUsers()
val newUser=ContentValues() val newUser = ContentValues()
newUser.put("uuid", user.uuid) newUser.put("uuid", user.uuid)
newUser.put("username", user.username) newUser.put("username", user.username)
newUser.put("email", user.email) newUser.put("email", user.email)
@ -50,19 +50,19 @@ class Database(context:Context): SQLiteOpenHelper(context,"p2play",null,1) {
newUser.put("avatar", user.avatar) newUser.put("avatar", user.avatar)
newUser.put("status", user.status) newUser.put("status", user.status)
db.insert("users",null, newUser) db.insert("users", null, newUser)
return true return true
} }
fun getToken(): TokenModel{ fun getToken(): TokenModel {
val db = writableDatabase val db = writableDatabase
var token = TokenModel() var token = TokenModel()
try { try {
var cursor= db.rawQuery("SELECT * FROM tokens WHERE status=1 ORDER BY id DESC LIMIT 1",null) var cursor = db.rawQuery("SELECT * FROM tokens WHERE status=1 ORDER BY id DESC LIMIT 1", null)
if(cursor.count != 0){ if (cursor.count != 0) {
cursor.moveToFirst() cursor.moveToFirst()
token.token = cursor.getString(cursor.getColumnIndex("token")).toString() token.token = cursor.getString(cursor.getColumnIndex("token")).toString()
@ -72,23 +72,22 @@ class Database(context:Context): SQLiteOpenHelper(context,"p2play",null,1) {
cursor.close() cursor.close()
return token return token
} catch (e: SQLiteException) {
}catch (e:SQLiteException){
db?.execSQL(dbTokens) db?.execSQL(dbTokens)
}catch (e:Exception){ } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
return token return token
} }
fun getUser(): UserModel{ fun getUser(): UserModel {
val db = writableDatabase val db = writableDatabase
var user = UserModel() var user = UserModel()
try { try {
var cursor= db.rawQuery("SELECT * FROM users WHERE status=1 ORDER BY id DESC LIMIT 1",null) var cursor = db.rawQuery("SELECT * FROM users WHERE status=1 ORDER BY id DESC LIMIT 1", null)
if(cursor.count != 0){ if (cursor.count != 0) {
cursor.moveToFirst() cursor.moveToFirst()
user.uuid = cursor.getString(cursor.getColumnIndex("uuid")).toInt() user.uuid = cursor.getString(cursor.getColumnIndex("uuid")).toInt()
@ -103,28 +102,26 @@ class Database(context:Context): SQLiteOpenHelper(context,"p2play",null,1) {
cursor.close() cursor.close()
return user return user
} catch (e: SQLiteException) {
}catch (e:SQLiteException){
db?.execSQL(dbTokens) db?.execSQL(dbTokens)
}catch (e:Exception){ } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
return user return user
} }
private fun closeTokens(){ private fun closeTokens() {
val db = writableDatabase val db = writableDatabase
db.execSQL("UPDATE tokens SET status=-1 WHERE 1") db.execSQL("UPDATE tokens SET status=-1 WHERE 1")
} }
private fun closeUsers(){ private fun closeUsers() {
val db = writableDatabase val db = writableDatabase
db.execSQL("UPDATE users SET status=-1 WHERE 1") db.execSQL("UPDATE users SET status=-1 WHERE 1")
} }
fun logout(){ fun logout() {
closeUsers() closeUsers()
closeTokens() closeTokens()
} }
} }

View File

@ -3,20 +3,18 @@ package org.libre.agosto.p2play
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.AsyncTask import android.os.AsyncTask
import android.support.v7.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import android.os.Looper import android.os.Looper
import android.preference.PreferenceManager import androidx.appcompat.app.AppCompatActivity
import android.util.Log import androidx.preference.PreferenceManager
import kotlinx.android.synthetic.main.activity_host.* import kotlinx.android.synthetic.main.activity_host.*
import org.libre.agosto.p2play.ajax.Auth import org.libre.agosto.p2play.ajax.Auth
import org.libre.agosto.p2play.ajax.Client
class HostActivity : AppCompatActivity() { class HostActivity : AppCompatActivity() {
lateinit var settings: SharedPreferences lateinit var settings: SharedPreferences
lateinit var editor: SharedPreferences.Editor lateinit var editor: SharedPreferences.Editor
val client:Auth = Auth() val client: Auth = Auth()
val _db = Database(this) private val db = Database(this)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -31,59 +29,57 @@ class HostActivity : AppCompatActivity() {
val host = settings.getString("hostP2play", "") val host = settings.getString("hostP2play", "")
val lastHost = settings.getString("last_host", "") val lastHost = settings.getString("last_host", "")
if(host!=""){ if (host != "") {
if(lastHost!=host){ if (lastHost != host) {
_db.logout() db.logout()
ManagerSingleton.logout() ManagerSingleton.logout()
getKeys(host) getKeys(host!!)
}else{ } else {
ManagerSingleton.url=host ManagerSingleton.url = host
startApp() startApp()
} }
} }
} }
fun saveHost(host: String) {
editor.putString("last_host", host)
fun saveHost(host: String){ editor.putString("hostP2play", host)
editor.putString("last_host",host)
editor.putString("hostP2play",host)
editor.apply() editor.apply()
ManagerSingleton.Toast(getString(R.string.finallyMsg), this) ManagerSingleton.toast(getString(R.string.finallyMsg), this)
ManagerSingleton.url=host ManagerSingleton.url = host
startApp() startApp()
} }
private fun getKeys(hostText: String){ private fun getKeys(hostText: String) {
button.isEnabled = false button.isEnabled = false
var host = hostText.toString() var host = hostText.toString()
host = host.replace("http://","") host = host.replace("http://", "")
host = host.replace("https://","") host = host.replace("https://", "")
host = host.replace("/","") host = host.replace("/", "")
ManagerSingleton.url = host ManagerSingleton.url = host
AsyncTask.execute { AsyncTask.execute {
if (Looper.myLooper()==null) if (Looper.myLooper() == null) {
Looper.prepare() Looper.prepare()
}
val keys = client.getKeys() val keys = client.getKeys()
if(keys.client_id!=""){ if (keys.client_id != "") {
editor.putString("client_id",keys.client_id) editor.putString("client_id", keys.client_id)
editor.putString("client_secret",keys.client_secret) editor.putString("client_secret", keys.client_secret)
editor.apply() editor.apply()
saveHost(host) saveHost(host)
} } else {
else{
runOnUiThread { runOnUiThread {
ManagerSingleton.Toast(getString(R.string.errorMsg), this) ManagerSingleton.toast(getString(R.string.errorMsg), this)
button.isEnabled = true button.isEnabled = true
} }
} }
} }
} }
private fun startApp(){ private fun startApp() {
runOnUiThread { runOnUiThread {
val intent = Intent(this,MainActivity::class.java) val intent = Intent(this, MainActivity::class.java)
startActivity(intent) startActivity(intent)
this.finish() this.finish()
} }

View File

@ -3,85 +3,109 @@ package org.libre.agosto.p2play
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.AsyncTask import android.os.AsyncTask
import android.support.v7.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import android.os.Looper import android.os.Looper
import android.preference.PreferenceManager import android.widget.EditText
import android.util.Log import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
import kotlinx.android.synthetic.main.activity_login.* import kotlinx.android.synthetic.main.activity_login.*
import org.libre.agosto.p2play.ajax.Auth import org.libre.agosto.p2play.ajax.Auth
class LoginActivity : AppCompatActivity() { class LoginActivity : AppCompatActivity() {
private val _auth = Auth() private val auth = Auth()
lateinit var settings: SharedPreferences lateinit var settings: SharedPreferences
lateinit var client_id: String lateinit var clientId: String
lateinit var client_secret: String lateinit var clientSecret: String
private lateinit var _db: Database private lateinit var db: Database
private var optCode: String? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login) setContentView(R.layout.activity_login)
setTitle(R.string.action_login) setTitle(R.string.action_login)
_db = Database(this) db = Database(this)
settings = PreferenceManager.getDefaultSharedPreferences(this) settings = PreferenceManager.getDefaultSharedPreferences(this)
client_id = settings.getString("client_id", "") clientId = settings.getString("client_id", "")!!
client_secret = settings.getString("client_secret", "") clientSecret = settings.getString("client_secret", "")!!
registerActionBtn.setOnClickListener { startActivity(Intent(this, RegisterActivity::class.java)) } registerActionBtn.setOnClickListener { startActivity(Intent(this, RegisterActivity::class.java)) }
loginBtn.setOnClickListener { tryLogin() } loginBtn.setOnClickListener { tryLogin() }
} }
fun tryLogin(){ fun tryLogin() {
loginBtn.isEnabled = false; loginBtn.isEnabled = false
val username = userText.text.toString() val username = userText.text.toString()
val password = passwordText.text.toString() val password = passwordText.text.toString()
AsyncTask.execute { AsyncTask.execute {
if (Looper.myLooper()==null) if (Looper.myLooper() == null) {
Looper.prepare() Looper.prepare()
}
val token = _auth.login(username, password, client_id, client_secret) val token = auth.login(username, password, clientId, clientSecret, optCode)
// Log.d("token", token.token ) // Log.d("token", token.token )
// Log.d("status", token.status.toString() ) // Log.d("status", token.status.toString() )
when (token.status.toString()) {
when(token.status.toString()){
"1" -> { "1" -> {
_db.newToken(token) db.newToken(token)
ManagerSingleton.token = token ManagerSingleton.token = token
getUser() getUser()
} }
"0" -> { "0" -> {
runOnUiThread { runOnUiThread {
ManagerSingleton.Toast(getString(R.string.loginError_msg), this) ManagerSingleton.toast(getString(R.string.loginError_msg), this)
} }
} }
"-1" -> { "-1" -> {
runOnUiThread { runOnUiThread {
loginBtn.isEnabled = true loginBtn.isEnabled = true
ManagerSingleton.Toast(getString(R.string.loginFailed_msg), this) ManagerSingleton.toast(getString(R.string.loginFailed_msg), this)
}
}
"-2" -> {
// TODO: Start 2FA modal
runOnUiThread {
val builder = AlertDialog.Builder(this, R.style.Widget_Material3_MaterialCalendar_Fullscreen)
val dialog = layoutInflater.inflate(R.layout.two_factor_dialog, null)
val inputTwoFactor = dialog.findViewById<EditText>(R.id.twoFactorText)
builder.setView(dialog)
.setTitle(R.string.twoFactorLabel)
// Add action buttons
.setPositiveButton(R.string.loginBtn) { d, _ ->
this.optCode = inputTwoFactor.text.toString()
this.tryLogin()
d.dismiss()
}
.setNegativeButton("Cancel") { d, _ ->
dialog.run { d.cancel() }
loginBtn.isEnabled = true
}
val alertDialog = builder.create()
alertDialog.show()
} }
} }
} }
} }
} }
fun getUser(){ fun getUser() {
val user = _auth.me(ManagerSingleton.token.token) val user = auth.me(ManagerSingleton.token.token)
if(user.status == 1){ if (user.status == 1) {
_db.newUser(user) db.newUser(user)
ManagerSingleton.user = user ManagerSingleton.user = user
runOnUiThread { runOnUiThread {
ManagerSingleton.Toast(getString(R.string.loginSuccess_msg), this) ManagerSingleton.toast(getString(R.string.loginSuccess_msg), this)
finish() finish()
} }
} } else {
else{
runOnUiThread { runOnUiThread {
ManagerSingleton.Toast(getString(R.string.loginError_msg), this) ManagerSingleton.toast(getString(R.string.loginError_msg), this)
} }
} }
} }

View File

@ -4,26 +4,31 @@ import android.content.Intent
import android.os.AsyncTask import android.os.AsyncTask
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.support.design.widget.Snackbar
import android.support.design.widget.NavigationView
import android.support.v4.view.GravityCompat
import android.support.v7.app.ActionBarDrawerToggle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.Menu import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.widget.ImageView import android.view.View
import android.widget.SearchView import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.core.view.GravityCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.navigation.NavigationView
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.drawer_layout
import kotlinx.android.synthetic.main.app_bar_main.* import kotlinx.android.synthetic.main.activity_main.nav_view
import kotlinx.android.synthetic.main.content_main.* import kotlinx.android.synthetic.main.app_bar_main.toolbar
import kotlinx.android.synthetic.main.content_main.mini
import kotlinx.android.synthetic.main.content_main.swipeContainer
import kotlinx.android.synthetic.main.mini_player.mini_play_pause
import kotlinx.android.synthetic.main.mini_player.mini_player_author
import kotlinx.android.synthetic.main.mini_player.mini_player_image
import kotlinx.android.synthetic.main.mini_player.mini_player_title
import kotlinx.android.synthetic.main.nav_header_main.* import kotlinx.android.synthetic.main.nav_header_main.*
import org.libre.agosto.p2play.adapters.VideosAdapter import org.libre.agosto.p2play.adapters.VideosAdapter
import org.libre.agosto.p2play.ajax.Videos import org.libre.agosto.p2play.ajax.Videos
import org.libre.agosto.p2play.models.VideoModel import org.libre.agosto.p2play.models.VideoModel
import org.libre.agosto.p2play.singletons.PlaybackSingleton
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
private lateinit var recyclerView: RecyclerView private lateinit var recyclerView: RecyclerView
@ -31,14 +36,11 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
private lateinit var viewManager: RecyclerView.LayoutManager private lateinit var viewManager: RecyclerView.LayoutManager
private val client: Videos = Videos() private val client: Videos = Videos()
private lateinit var lastItem: MenuItem private lateinit var lastItem: MenuItem
private lateinit var subItem: MenuItem
lateinit var myMenu: Menu lateinit var myMenu: Menu
private val _db = Database(this) private val db = Database(this)
var section: String = "" var section: String = ""
var searchVal: String = "" var searchVal: String = ""
var pagination = 0 var pagination = 0
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
@ -66,6 +68,12 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
this.refresh() this.refresh()
} }
mini_player_image.setOnClickListener { this.resumeVideo() }
mini_player_title.setOnClickListener { this.resumeVideo() }
mini_player_author.setOnClickListener { this.resumeVideo() }
mini.setOnClickListener { this.resumeVideo() }
mini_play_pause.setOnClickListener { this.playPausePlayer() }
Handler().postDelayed({ Handler().postDelayed({
// Title for nav_bar // Title for nav_bar
side_emailTxt?.text = getString(R.string.nav_header_subtitle) + " " + this.packageManager.getPackageInfo(this.packageName, 0).versionName side_emailTxt?.text = getString(R.string.nav_header_subtitle) + " " + this.packageManager.getPackageInfo(this.packageName, 0).versionName
@ -73,7 +81,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
} }
// Generic function for set data to RecyclerView // Generic function for set data to RecyclerView
private fun initRecycler(){ private fun initRecycler() {
val data = arrayListOf<VideoModel>() val data = arrayListOf<VideoModel>()
viewAdapter = VideosAdapter(data) viewAdapter = VideosAdapter(data)
@ -89,42 +97,43 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
adapter = viewAdapter adapter = viewAdapter
this.addOnScrollListener(object : RecyclerView.OnScrollListener() { this.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)
// super.onScrolled(recyclerView!!, dx, dy)
if(!swipeContainer.isRefreshing){ if (!swipeContainer.isRefreshing) {
if(!canScrollVertically(1)){ if (!canScrollVertically(1)) {
loadMore() loadMore()
} }
} }
} }
}) })
} }
// swipeContainer.isRefreshing = false swipeContainer.isRefreshing = false
} }
private fun addVideos(videos: ArrayList<VideoModel>){ private fun addVideos(videos: ArrayList<VideoModel>) {
this.swipeContainer.isRefreshing = true this.swipeContainer.isRefreshing = true
try { try {
if(this.pagination == 0){ if (this.pagination == 0) {
(viewAdapter as VideosAdapter).clearData() (viewAdapter as VideosAdapter).clearData()
recyclerView.scrollToPosition(0) recyclerView.scrollToPosition(0)
} }
(viewAdapter as VideosAdapter).addData(videos) (viewAdapter as VideosAdapter).addData(videos)
}catch (err: Exception){ } catch (err: Exception) {
err.printStackTrace() err.printStackTrace()
ManagerSingleton.Toast(getString(R.string.errorMsg), this) ManagerSingleton.toast(getString(R.string.errorMsg), this)
} }
this.swipeContainer.isRefreshing = false this.swipeContainer.isRefreshing = false
} }
private fun refresh(){ private fun refresh() {
swipeContainer.isRefreshing = true swipeContainer.isRefreshing = true
this.pagination = 0 this.pagination = 0
when(section){ when (section) {
"local" -> this.getLocalVideos() "local" -> this.getLocalVideos()
"popular" -> this.getPopularVideos() "popular" -> this.getPopularVideos()
"trending" -> this.getTrengindVideos() "trending" -> this.getTrengindVideos()
@ -132,17 +141,18 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
"sub" -> this.getSubscriptionVideos() "sub" -> this.getSubscriptionVideos()
"search" -> this.searchVideos() "search" -> this.searchVideos()
"my_videos" -> { "my_videos" -> {
if(ManagerSingleton.token.token != "") if (ManagerSingleton.token.token != "") {
this.getMyVideos() this.getMyVideos()
else } else {
this.getLastVideos() this.getLastVideos()
}
} }
} }
} }
private fun getSubscriptionVideos(){ private fun getSubscriptionVideos() {
if(ManagerSingleton.user.status != 1){ if (ManagerSingleton.user.status != 1) {
ManagerSingleton.Toast("Inicia session primero", this) ManagerSingleton.toast("Inicia session primero", this)
startActivity(Intent(this, LoginActivity::class.java)) startActivity(Intent(this, LoginActivity::class.java))
return return
} }
@ -158,7 +168,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
} }
// Last videos // Last videos
private fun getLastVideos(){ private fun getLastVideos() {
swipeContainer.isRefreshing = true swipeContainer.isRefreshing = true
section = "last" section = "last"
setTitle(R.string.title_recent) setTitle(R.string.title_recent)
@ -171,7 +181,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
} }
// Popular videos // Popular videos
private fun getPopularVideos(){ private fun getPopularVideos() {
swipeContainer.isRefreshing = true swipeContainer.isRefreshing = true
section = "popular" section = "popular"
setTitle(R.string.title_popular) setTitle(R.string.title_popular)
@ -184,7 +194,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
} }
// Trending videos // Trending videos
private fun getTrengindVideos(){ private fun getTrengindVideos() {
swipeContainer.isRefreshing = true swipeContainer.isRefreshing = true
section = "trending" section = "trending"
setTitle(R.string.title_trending) setTitle(R.string.title_trending)
@ -197,7 +207,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
} }
// Local videos // Local videos
private fun getLocalVideos(){ private fun getLocalVideos() {
swipeContainer.isRefreshing = true swipeContainer.isRefreshing = true
section = "local" section = "local"
setTitle(R.string.title_local) setTitle(R.string.title_local)
@ -210,7 +220,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
} }
// Videos of user // Videos of user
private fun getMyVideos(){ private fun getMyVideos() {
swipeContainer.isRefreshing = true swipeContainer.isRefreshing = true
section = "my_videos" section = "my_videos"
setTitle(R.string.title_myVideos) setTitle(R.string.title_myVideos)
@ -223,7 +233,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
} }
// Videos history of user // Videos history of user
private fun getHistory(){ private fun getHistory() {
swipeContainer.isRefreshing = true swipeContainer.isRefreshing = true
section = "my_videos" section = "my_videos"
setTitle(R.string.nav_history) setTitle(R.string.nav_history)
@ -236,7 +246,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
} }
// Most liked // Most liked
private fun getMostLiked(){ private fun getMostLiked() {
swipeContainer.isRefreshing = true swipeContainer.isRefreshing = true
section = "liked" section = "liked"
setTitle(R.string.nav_likes) setTitle(R.string.nav_likes)
@ -251,11 +261,11 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
override fun onBackPressed() { override fun onBackPressed() {
if (drawer_layout.isDrawerOpen(GravityCompat.START)) { if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
drawer_layout.closeDrawer(GravityCompat.START) drawer_layout.closeDrawer(GravityCompat.START)
} } else if (!section.equals("trending")) {
else if(!section.equals("trending")) { // Hot fix
pagination = 0
this.getTrengindVideos() this.getTrengindVideos()
} } else {
else {
super.onBackPressed() super.onBackPressed()
} }
} }
@ -267,13 +277,13 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val searchItem = menu.findItem(R.id.app_bar_search) val searchItem = menu.findItem(R.id.app_bar_search)
val searchView = searchItem.actionView as SearchView val searchView = searchItem.actionView as SearchView
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener{ searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextChange(p0: String?): Boolean { override fun onQueryTextChange(p0: String?): Boolean {
return true return true
} }
override fun onQueryTextSubmit(p0: String?): Boolean { override fun onQueryTextSubmit(p0: String?): Boolean {
if(!p0.isNullOrBlank()){ if (!p0.isNullOrBlank()) {
searchVal = p0 searchVal = p0
pagination = 0 pagination = 0
searchView.onActionViewCollapsed() searchView.onActionViewCollapsed()
@ -281,10 +291,8 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
} }
return true return true
} }
}) })
myMenu = menu myMenu = menu
setSideData() setSideData()
return true return true
@ -302,7 +310,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
// as you specify a parent activity in AndroidManifest.xml. // as you specify a parent activity in AndroidManifest.xml.
when (item.itemId) { when (item.itemId) {
R.id.action_settings -> { R.id.action_settings -> {
val intent = Intent(this, SettingsActivity::class.java) val intent = Intent(this, SettingsActivity2::class.java)
startActivity(intent) startActivity(intent)
return true return true
} }
@ -323,7 +331,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
// Handle navigation view item clicks here. // Handle navigation view item clicks here.
// if(::lastItem.isInitialized){ // if(::lastItem.isInitialized){
// lastItem.isChecked = false // lastItem.isChecked = false
// } // }
lastItem = item lastItem = item
pagination = 0 pagination = 0
@ -351,53 +359,72 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
setSideData() setSideData()
if (PlaybackSingleton.player != null && PlaybackSingleton.player!!.isPlaying) {
PlaybackSingleton.runMediaSession(this)
mini_player_title.text = PlaybackSingleton.video!!.name
mini_player_author.text = PlaybackSingleton.video!!.username
Picasso.get().load("https://${ManagerSingleton.url}${PlaybackSingleton.video!!.thumbUrl}").into(mini_player_image)
mini_play_pause.setImageResource(R.drawable.ic_pause_24)
mini.visibility = View.VISIBLE
} else {
mini.visibility = View.GONE
}
} }
private fun setSideData(){ override fun onDestroy() {
if(ManagerSingleton.user.status == 1){ if (PlaybackSingleton.player != null) {
PlaybackSingleton.release()
}
super.onDestroy()
}
private fun setSideData() {
if (ManagerSingleton.user.status == 1) {
nav_view.menu.findItem(R.id.ml).isVisible = true nav_view.menu.findItem(R.id.ml).isVisible = true
side_usernameTxt?.text = ManagerSingleton.user.username side_usernameTxt?.text = ManagerSingleton.user.username
side_emailTxt?.text = ManagerSingleton.user.email side_emailTxt?.text = ManagerSingleton.user.email
if(ManagerSingleton.user.avatar!="" && side_imageView != null) { if (ManagerSingleton.user.avatar != "" && side_imageView != null) {
Picasso.get().load("https://" + ManagerSingleton.url + ManagerSingleton.user.avatar).into(side_imageView) Picasso.get().load("https://" + ManagerSingleton.url + ManagerSingleton.user.avatar).into(side_imageView)
} }
// side_imageView?.setOnClickListener { side_imageView?.setOnClickListener {
// pagination = 0 pagination = 0
// getMyVideos() getMyVideos()
// drawer_layout.closeDrawer(GravityCompat.START) drawer_layout.closeDrawer(GravityCompat.START)
// } }
if(::myMenu.isInitialized){ if (::myMenu.isInitialized) {
myMenu.findItem(R.id.action_login).isVisible = false myMenu.findItem(R.id.action_login).isVisible = false
myMenu.findItem(R.id.action_logout).isVisible = true myMenu.findItem(R.id.action_logout).isVisible = true
} }
} else {
nav_view.menu.findItem(R.id.ml).isVisible = false
} }
} }
private fun logout(){ private fun logout() {
if(::myMenu.isInitialized){ if (::myMenu.isInitialized) {
myMenu.findItem(R.id.action_login).isVisible = true myMenu.findItem(R.id.action_login).isVisible = true
myMenu.findItem(R.id.action_logout).isVisible = false myMenu.findItem(R.id.action_logout).isVisible = false
} }
nav_view.menu.findItem(R.id.ml).isVisible = false // nav_view.menu.findItem(R.id.ml).isVisible = false
side_usernameTxt?.text = getString(R.string.nav_header_title) side_usernameTxt?.text = getString(R.string.nav_header_title)
side_emailTxt?.text = getString(R.string.nav_header_subtitle) + " " + this.packageManager.getPackageInfo(this.packageName, 0).versionName side_emailTxt?.text = getString(R.string.nav_header_subtitle) + " " + this.packageManager.getPackageInfo(this.packageName, 0).versionName
side_imageView?.setImageResource(R.drawable.default_avatar) side_imageView?.setImageResource(R.drawable.default_avatar)
side_imageView?.setOnClickListener { } side_imageView?.setOnClickListener { }
_db.logout() db.logout()
ManagerSingleton.logout() ManagerSingleton.logout()
this.refresh() this.refresh()
ManagerSingleton.Toast(getString(R.string.logout_msg), this) ManagerSingleton.toast(getString(R.string.logout_msg), this)
setSideData()
} }
private fun loadMore(){ private fun loadMore() {
swipeContainer.isRefreshing = true swipeContainer.isRefreshing = true
this.pagination += ManagerSingleton.videos_count this.pagination += ManagerSingleton.videosCount
when(section){ when (section) {
"local" -> this.getLocalVideos() "local" -> this.getLocalVideos()
"popular" -> this.getPopularVideos() "popular" -> this.getPopularVideos()
"trending" -> this.getTrengindVideos() "trending" -> this.getTrengindVideos()
@ -405,16 +432,17 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
"sub" -> this.getSubscriptionVideos() "sub" -> this.getSubscriptionVideos()
"search" -> this.searchVideos() "search" -> this.searchVideos()
"my_videos" -> { "my_videos" -> {
if(ManagerSingleton.token.token != "") if (ManagerSingleton.token.token != "") {
this.getMyVideos() this.getMyVideos()
else } else {
this.getLastVideos() this.getLastVideos()
}
} }
"liked" -> this.getMostLiked() "liked" -> this.getMostLiked()
} }
} }
private fun searchVideos(){ private fun searchVideos() {
swipeContainer.isRefreshing = true swipeContainer.isRefreshing = true
section = "search" section = "search"
this.title = this.searchVal this.title = this.searchVal
@ -426,4 +454,21 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
} }
} }
private fun resumeVideo() {
val intent = Intent(this, ReproductorActivity::class.java)
intent.putExtra("resume", true)
startActivity(intent)
}
private fun playPausePlayer() {
PlaybackSingleton.player?.let {
if (it.isPlaying) {
it.pause()
mini_play_pause.setImageResource(R.drawable.ic_play_arrow_24)
} else {
it.play()
mini_play_pause.setImageResource(R.drawable.ic_pause_24)
}
}
}
} }

View File

@ -1,22 +1,39 @@
package org.libre.agosto.p2play package org.libre.agosto.p2play
import android.content.Context import android.content.Context
import android.content.SharedPreferences
import org.libre.agosto.p2play.models.TokenModel import org.libre.agosto.p2play.models.TokenModel
import org.libre.agosto.p2play.models.UserModel import org.libre.agosto.p2play.models.UserModel
object ManagerSingleton { object ManagerSingleton {
var url: String?= null var url: String? = null
var user: UserModel = UserModel() var user: UserModel = UserModel()
var token: TokenModel = TokenModel() var token: TokenModel = TokenModel()
var nfsw: Boolean = false var nfsw: Boolean = false
var videos_count: Int = 0 var videosCount: Int = 0
lateinit var settings: SharedPreferences
fun Toast(text: String?, context: Context) { lateinit var db: Database
fun toast(text: String?, context: Context) {
android.widget.Toast.makeText(context, text, android.widget.Toast.LENGTH_SHORT).show() android.widget.Toast.makeText(context, text, android.widget.Toast.LENGTH_SHORT).show()
} }
fun logout(){ fun logout() {
db.logout()
user = UserModel() user = UserModel()
token = TokenModel() token = TokenModel()
} }
fun reloadSettings() {
val host = settings.getString("hostP2play", "")
val lastHost = settings.getString("last_host", "")
if (host != "") {
if (lastHost != host) {
logout()
}
url = host
}
nfsw = settings.getBoolean("show_nsfw", false)
videosCount = settings.getString("videos_count", "15")!!.toInt()
}
} }

View File

@ -2,19 +2,19 @@ package org.libre.agosto.p2play
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.AsyncTask import android.os.AsyncTask
import android.support.v7.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import android.os.Looper import android.os.Looper
import android.preference.PreferenceManager
import android.util.Log import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
import kotlinx.android.synthetic.main.activity_register.* import kotlinx.android.synthetic.main.activity_register.*
import org.libre.agosto.p2play.ajax.Auth import org.libre.agosto.p2play.ajax.Auth
class RegisterActivity : AppCompatActivity() { class RegisterActivity : AppCompatActivity() {
private val _auth = Auth() private val auth = Auth()
lateinit var settings: SharedPreferences lateinit var settings: SharedPreferences
lateinit var client_id: String lateinit var clientId: String
lateinit var client_secret: String lateinit var clientSecret: String
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -22,31 +22,32 @@ class RegisterActivity : AppCompatActivity() {
setTitle(R.string.registerActionBtn) setTitle(R.string.registerActionBtn)
settings = PreferenceManager.getDefaultSharedPreferences(this) settings = PreferenceManager.getDefaultSharedPreferences(this)
client_id = settings.getString("client_id", "") clientId = settings.getString("client_id", "")!!
client_secret = settings.getString("client_secret", "") clientSecret = settings.getString("client_secret", "")!!
registerBtn.setOnClickListener { registerUser() } registerBtn.setOnClickListener { registerUser() }
} }
fun registerUser(){ private fun registerUser() {
registerBtn.isEnabled = false registerBtn.isEnabled = false
val username = userText2.text.toString() val username = userText2.text.toString()
val password = passwordText2.text.toString() val password = passwordText2.text.toString()
val email = emailText.text.toString() val email = emailText.text.toString()
AsyncTask.execute { AsyncTask.execute {
if (Looper.myLooper()==null) if (Looper.myLooper() == null) {
Looper.prepare() Looper.prepare()
}
val res = _auth.register(username, password, email) val res = auth.register(username, password, email)
Log.d("Res register", res.toString()) Log.d("Res register", res.toString())
runOnUiThread { runOnUiThread {
when (res) { when (res) {
1 -> { 1 -> {
ManagerSingleton.Toast(getString(R.string.registerSuccess_msg), this) ManagerSingleton.toast(getString(R.string.registerSuccess_msg), this)
finish() finish()
} }
0 -> ManagerSingleton.Toast(getString(R.string.registerFailed_msg), this) 0 -> ManagerSingleton.toast(getString(R.string.registerFailed_msg), this)
-1 -> ManagerSingleton.Toast(getString(R.string.registerError_msg), this) -1 -> ManagerSingleton.toast(getString(R.string.registerError_msg), this)
} }
registerBtn.isEnabled = true registerBtn.isEnabled = true
} }

View File

@ -1,43 +1,52 @@
package org.libre.agosto.p2play package org.libre.agosto.p2play
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.net.Uri
import android.os.AsyncTask import android.os.AsyncTask
import android.support.v7.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import android.os.Looper import android.os.Looper
import android.support.v4.content.ContextCompat
import android.support.v7.app.AlertDialog
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.util.Log
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.webkit.WebChromeClient import android.webkit.WebChromeClient
import android.widget.EditText import android.widget.EditText
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import androidx.media3.exoplayer.DefaultLoadControl
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.upstream.DefaultAllocator
import androidx.media3.session.MediaController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_reproductor.* import kotlinx.android.synthetic.main.activity_reproductor.*
import kotlinx.android.synthetic.main.comment_component.commentaryBtn
import kotlinx.android.synthetic.main.comment_component.commentaryLayout
import kotlinx.android.synthetic.main.comment_component.commentaryText
import kotlinx.android.synthetic.main.comment_component.userImgCom
import org.libre.agosto.p2play.adapters.CommentariesAdapter import org.libre.agosto.p2play.adapters.CommentariesAdapter
import org.libre.agosto.p2play.ajax.Actions import org.libre.agosto.p2play.ajax.Actions
import org.libre.agosto.p2play.ajax.Comments import org.libre.agosto.p2play.ajax.Comments
import org.libre.agosto.p2play.ajax.Videos import org.libre.agosto.p2play.ajax.Videos
import org.libre.agosto.p2play.helpers.setFullscreen
import org.libre.agosto.p2play.models.CommentaryModel import org.libre.agosto.p2play.models.CommentaryModel
import org.libre.agosto.p2play.models.VideoModel import org.libre.agosto.p2play.models.VideoModel
import org.libre.agosto.p2play.singletons.PlaybackSingleton
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
class ReproductorActivity : AppCompatActivity() { class ReproductorActivity : AppCompatActivity() {
private val clientVideo: Videos = Videos()
lateinit var video: VideoModel lateinit var video: VideoModel
private val _actions: Actions = Actions() lateinit var videoPlayback: VideoModel
private val actions: Actions = Actions()
private val client: Comments = Comments() private val client: Comments = Comments()
private val videos: Videos = Videos() private val videos: Videos = Videos()
@ -46,11 +55,25 @@ class ReproductorActivity : AppCompatActivity() {
private lateinit var viewAdapter: RecyclerView.Adapter<*> private lateinit var viewAdapter: RecyclerView.Adapter<*>
private lateinit var viewManager: RecyclerView.LayoutManager private lateinit var viewManager: RecyclerView.LayoutManager
// Exoplayer
private lateinit var player: ExoPlayer
private lateinit var mediaControl: MediaController
// Fullscreen info
private var isFullscreen = false
// Resume info
private var isResume = false
@SuppressLint("SetJavaScriptEnabled", "SetTextI18n") @SuppressLint("SetJavaScriptEnabled", "SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_reproductor) setContentView(R.layout.activity_reproductor)
val fullscreenButton: ImageView = exoPlayer.findViewById(R.id.exo_fullscreen_custom)
val fullscreenButton2 = fullscreenPlayer.findViewById<ImageView>(R.id.exo_fullscreen_custom)
videoView.webChromeClient = WebClient() videoView.webChromeClient = WebClient()
videoView.settings.javaScriptEnabled = true videoView.settings.javaScriptEnabled = true
videoView.settings.allowContentAccess = true videoView.settings.allowContentAccess = true
@ -61,7 +84,15 @@ class ReproductorActivity : AppCompatActivity() {
videoView.settings.domStorageEnabled = true videoView.settings.domStorageEnabled = true
try { try {
this.video = this.intent.extras.getSerializable("video") as VideoModel val resume = this.intent.extras?.getSerializable("resume")
if (resume == null) {
video = this.intent.extras?.getSerializable("video") as VideoModel
isResume = false
} else {
video = PlaybackSingleton.video!!
isResume = true
}
tittleVideoTxt.text = this.video.name tittleVideoTxt.text = this.video.name
viewsTxt.text = "${this.video.views} ${getString(R.string.view_text)}" viewsTxt.text = "${this.video.views} ${getString(R.string.view_text)}"
userTxt.text = this.video.username userTxt.text = this.video.username
@ -73,11 +104,11 @@ class ReproductorActivity : AppCompatActivity() {
hostTxt.text = this.video.userHost hostTxt.text = this.video.userHost
// Check if user had profile image // Check if user had profile image
if (this.video.userImageUrl != "") if (this.video.userImageUrl != "") {
Picasso.get().load("https://" + ManagerSingleton.url + this.video.userImageUrl).into(userImg) Picasso.get().load("https://" + ManagerSingleton.url + this.video.userImageUrl).into(userImg)
}
// Load the video // Load the video
videoView.loadUrl("https://" + ManagerSingleton.url + this.video.embedUrl) videoView.loadUrl("https://" + ManagerSingleton.url + this.video.embedUrl)
} catch (err: Exception) { } catch (err: Exception) {
err.printStackTrace() err.printStackTrace()
} }
@ -94,22 +125,68 @@ class ReproductorActivity : AppCompatActivity() {
showMoreBtn.setOnClickListener { getDescription() } showMoreBtn.setOnClickListener { getDescription() }
shareLayout.setOnClickListener { shareIntent() } shareLayout.setOnClickListener { shareIntent() }
reportLayout.setOnClickListener { reportIntent() } reportLayout.setOnClickListener { reportIntent() }
fullscreenButton.setOnClickListener { toggleFullscreen() }
fullscreenButton2.setOnClickListener { toggleFullscreen() }
userImg.setOnClickListener { userImg.setOnClickListener {
val intent = Intent(this, ChannelActivity::class.java) val intent = Intent(this, ChannelActivity::class.java)
intent.putExtra("channel", video.getAccount()) intent.putExtra("channel", video.getChannel())
startActivity(intent) startActivity(intent)
} }
AsyncTask.execute {
videoPlayback = this.clientVideo.getVideo(this.video.uuid)
// TODO: Make this configurable
val bufferSize = 1024 * 1024 // 1mb
val allocator = DefaultAllocator(true, bufferSize)
val loadControl = DefaultLoadControl.Builder()
.setAllocator(allocator)
.build()
runOnUiThread {
try {
if (PlaybackSingleton.player == null || !PlaybackSingleton.player!!.playWhenReady) {
PlaybackSingleton.player = ExoPlayer.Builder(this.baseContext)
.setSeekBackIncrementMs(10000)
.setSeekForwardIncrementMs(10000)
.setLoadControl(loadControl).build()
}
player = PlaybackSingleton.player!!
exoPlayer.player = player
println("----- video --------")
println(videoPlayback.streamingData?.playlistUrl)
if (!isResume) {
val mediaItem = MediaItem.Builder()
.setUri(videoPlayback.streamingData?.playlistUrl!!)
.setMediaMetadata(
MediaMetadata.Builder()
.setArtist(videoPlayback.username)
.setTitle(videoPlayback.name)
.setArtworkUri(Uri.parse("https://${ManagerSingleton.url}${videoPlayback.thumbUrl}"))
.build(),
).build()
PlaybackSingleton.setData(mediaItem, video)
}
// Start the playback.
// player.play()
} catch (err: Exception) {
err.printStackTrace()
}
}
}
} }
private fun subscribe() { private fun subscribe() {
AsyncTask.execute { AsyncTask.execute {
if (Looper.myLooper() == null) if (Looper.myLooper() == null) {
Looper.prepare() Looper.prepare()
val res = this._actions.subscribe(ManagerSingleton.token.token, video.getAccount()) }
val res = this.actions.subscribe(ManagerSingleton.token.token, video.getChannel())
if (res == 1) { if (res == 1) {
runOnUiThread { runOnUiThread {
ManagerSingleton.Toast(getString(R.string.subscribeMsg), this) ManagerSingleton.toast(getString(R.string.subscribeMsg), this)
this.changeSubscribeBtn(true) this.changeSubscribeBtn(true)
} }
} }
@ -118,12 +195,13 @@ class ReproductorActivity : AppCompatActivity() {
private fun unSubscribe() { private fun unSubscribe() {
AsyncTask.execute { AsyncTask.execute {
if (Looper.myLooper() == null) if (Looper.myLooper() == null) {
Looper.prepare() Looper.prepare()
val res = this._actions.unSubscribe(ManagerSingleton.token.token, video.getAccount()) }
val res = this.actions.unSubscribe(ManagerSingleton.token.token, video.getChannel())
if (res == 1) { if (res == 1) {
runOnUiThread { runOnUiThread {
ManagerSingleton.Toast(getString(R.string.unSubscribeMsg), this) ManagerSingleton.toast(getString(R.string.unSubscribeMsg), this)
this.changeSubscribeBtn(false) this.changeSubscribeBtn(false)
} }
} }
@ -132,12 +210,13 @@ class ReproductorActivity : AppCompatActivity() {
private fun rate(rate: String) { private fun rate(rate: String) {
AsyncTask.execute { AsyncTask.execute {
if (Looper.myLooper() == null) if (Looper.myLooper() == null) {
Looper.prepare() Looper.prepare()
val res = this._actions.rate(ManagerSingleton.token.token, this.video.id, rate) }
val res = this.actions.rate(ManagerSingleton.token.token, this.video.id, rate)
if (res == 1) { if (res == 1) {
runOnUiThread { runOnUiThread {
ManagerSingleton.Toast(getString(R.string.rateMsg), this) ManagerSingleton.toast(getString(R.string.rateMsg), this)
if (rate == "like") { if (rate == "like") {
textViewLike.setTextColor(ContextCompat.getColor(this, R.color.colorLike)) textViewLike.setTextColor(ContextCompat.getColor(this, R.color.colorLike))
textViewDislike.setTextColor(ContextCompat.getColor(this, R.color.primary_dark_material_light)) textViewDislike.setTextColor(ContextCompat.getColor(this, R.color.primary_dark_material_light))
@ -152,9 +231,10 @@ class ReproductorActivity : AppCompatActivity() {
private fun getRate() { private fun getRate() {
AsyncTask.execute { AsyncTask.execute {
if (Looper.myLooper() == null) if (Looper.myLooper() == null) {
Looper.prepare() Looper.prepare()
val rate = this._actions.getRate(ManagerSingleton.token.token, this.video.id) }
val rate = this.actions.getRate(ManagerSingleton.token.token, this.video.id)
runOnUiThread { runOnUiThread {
when (rate) { when (rate) {
"like" -> { "like" -> {
@ -177,9 +257,10 @@ class ReproductorActivity : AppCompatActivity() {
private fun getSubscription() { private fun getSubscription() {
val account = this.video.nameChannel + "@" + this.video.userHost val account = this.video.nameChannel + "@" + this.video.userHost
AsyncTask.execute { AsyncTask.execute {
if (Looper.myLooper() == null) if (Looper.myLooper() == null) {
Looper.prepare() Looper.prepare()
val isSubscribed = this._actions.getSubscription(ManagerSingleton.token.token, account) }
val isSubscribed = this.actions.getSubscription(ManagerSingleton.token.token, account)
runOnUiThread { runOnUiThread {
this.changeSubscribeBtn(isSubscribed) this.changeSubscribeBtn(isSubscribed)
} }
@ -198,7 +279,7 @@ class ReproductorActivity : AppCompatActivity() {
private fun setDataComments(data: ArrayList<CommentaryModel>) { private fun setDataComments(data: ArrayList<CommentaryModel>) {
// Set data for RecyclerView // Set data for RecyclerView
viewAdapter = CommentariesAdapter(data) viewAdapter = CommentariesAdapter(data).setFragmentManager(supportFragmentManager)
recyclerView = findViewById<RecyclerView>(R.id.listCommentaries).apply { recyclerView = findViewById<RecyclerView>(R.id.listCommentaries).apply {
// use this setting to improve performance if you know that changes // use this setting to improve performance if you know that changes
@ -224,7 +305,7 @@ class ReproductorActivity : AppCompatActivity() {
private fun makeComment() { private fun makeComment() {
if (commentaryText.text.toString() == "") { if (commentaryText.text.toString() == "") {
ManagerSingleton.Toast(getString(R.string.emptyCommentaryMsg), this) ManagerSingleton.toast(getString(R.string.emptyCommentaryMsg), this)
return return
} }
val text = commentaryText.text.toString() val text = commentaryText.text.toString()
@ -232,11 +313,11 @@ class ReproductorActivity : AppCompatActivity() {
val res = this.client.makeCommentary(ManagerSingleton.token.token, this.video.id, text) val res = this.client.makeCommentary(ManagerSingleton.token.token, this.video.id, text)
runOnUiThread { runOnUiThread {
if (res) { if (res) {
ManagerSingleton.Toast(getString(R.string.makedCommentaryMsg), this) ManagerSingleton.toast(getString(R.string.makedCommentaryMsg), this)
commentaryText.text.clear() commentaryText.text?.clear()
this.getComments() this.getComments()
} else { } else {
ManagerSingleton.Toast(getString(R.string.errorCommentaryMsg), this) ManagerSingleton.toast(getString(R.string.errorCommentaryMsg), this)
} }
} }
} }
@ -253,10 +334,12 @@ class ReproductorActivity : AppCompatActivity() {
if (ManagerSingleton.user.avatar != "") { if (ManagerSingleton.user.avatar != "") {
Picasso.get().load("https://" + ManagerSingleton.url + ManagerSingleton.user.avatar).into(userImgCom) Picasso.get().load("https://" + ManagerSingleton.url + ManagerSingleton.user.avatar).into(userImgCom)
} }
} else {
commentaryLayout.visibility = View.GONE
} }
} }
fun getDescription() { private fun getDescription() {
AsyncTask.execute { AsyncTask.execute {
val fullDescription = this.videos.fullDescription(this.video.id) val fullDescription = this.videos.fullDescription(this.video.id)
runOnUiThread { runOnUiThread {
@ -285,6 +368,7 @@ class ReproductorActivity : AppCompatActivity() {
// Inflate and set the layout for the dialog // Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout // Pass null as the parent view because its going in the dialog layout
builder.setView(dialog) builder.setView(dialog)
.setTitle(R.string.reportDialog)
// Add action buttons // Add action buttons
.setPositiveButton(R.string.reportBtn) { _, _ -> .setPositiveButton(R.string.reportBtn) { _, _ ->
val reason = inputReason.text.toString() val reason = inputReason.text.toString()
@ -297,22 +381,59 @@ class ReproductorActivity : AppCompatActivity() {
alertDialog.show() alertDialog.show()
} }
private fun reportVideo(reason: String){ private fun reportVideo(reason: String) {
AsyncTask.execute { AsyncTask.execute {
val res = _actions.reportVideo(video.id, reason, ManagerSingleton.token.token) val res = actions.reportVideo(video.id, reason, ManagerSingleton.token.token)
runOnUiThread { runOnUiThread {
if(res) { if (res) {
ManagerSingleton.Toast(getText(R.string.reportDialogMsg).toString(), this) ManagerSingleton.toast(getText(R.string.reportDialogMsg).toString(), this)
} } else {
else { ManagerSingleton.toast(getText(R.string.errorMsg).toString(), this)
ManagerSingleton.Toast(getText(R.string.errorMsg).toString(), this)
} }
} }
} }
} }
internal inner class WebClient: WebChromeClient() { private fun toggleFullscreen() {
if (isFullscreen) {
nonFullScreen.visibility = View.VISIBLE
fullScreenExo.visibility = View.GONE
exoPlayer.player = player
fullscreenPlayer.player = null
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
val attrs = window.attributes
attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv()
attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON.inv()
window.attributes = attrs
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
isFullscreen = false
} else {
val matchParent = WindowManager.LayoutParams.MATCH_PARENT
nonFullScreen.visibility = View.GONE
fullScreenExo.visibility = View.VISIBLE
exoPlayer.player = null
fullscreenPlayer.player = player
setFullscreen(window)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
isFullscreen = true
}
}
override fun onDestroy() {
if (!player.isPlaying) {
PlaybackSingleton.release()
}
super.onDestroy()
}
internal inner class WebClient : WebChromeClient() {
private var mCustomView: View? = null private var mCustomView: View? = null
private var mCustomViewCallback: WebChromeClient.CustomViewCallback? = null private var mCustomViewCallback: WebChromeClient.CustomViewCallback? = null
private var mOriginalOrientation: Int = 0 private var mOriginalOrientation: Int = 0
@ -320,12 +441,14 @@ class ReproductorActivity : AppCompatActivity() {
override fun getDefaultVideoPoster(): Bitmap? { override fun getDefaultVideoPoster(): Bitmap? {
AsyncTask.execute { AsyncTask.execute {
this@ReproductorActivity._actions.watchVideo(this@ReproductorActivity.video.id, ManagerSingleton.token.token) this@ReproductorActivity.actions.watchVideo(this@ReproductorActivity.video.id, ManagerSingleton.token.token)
} }
return if (mCustomView == null) { return if (mCustomView == null) {
null null
} else BitmapFactory.decodeResource(this@ReproductorActivity.resources, 2130837573) } else {
BitmapFactory.decodeResource(this@ReproductorActivity.resources, 2130837573)
}
} }
override fun onHideCustomView() { override fun onHideCustomView() {
@ -346,8 +469,7 @@ class ReproductorActivity : AppCompatActivity() {
attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON.inv() attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON.inv()
window.attributes = attrs window.attributes = attrs
this@ReproductorActivity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE this@ReproductorActivity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
} } catch (err: Exception) {
catch (err: Exception){
err.printStackTrace() err.printStackTrace()
} }
} }
@ -362,23 +484,17 @@ class ReproductorActivity : AppCompatActivity() {
this.mOriginalSystemUiVisibility = this@ReproductorActivity.window.decorView.systemUiVisibility this.mOriginalSystemUiVisibility = this@ReproductorActivity.window.decorView.systemUiVisibility
this.mOriginalOrientation = this@ReproductorActivity.requestedOrientation this.mOriginalOrientation = this@ReproductorActivity.requestedOrientation
this.mCustomViewCallback = paramCustomViewCallback this.mCustomViewCallback = paramCustomViewCallback
val match_parent = WindowManager.LayoutParams.MATCH_PARENT val matchParent = WindowManager.LayoutParams.MATCH_PARENT
this@ReproductorActivity.nonFullScreen.visibility = View.GONE this@ReproductorActivity.nonFullScreen.visibility = View.GONE
this@ReproductorActivity.fullScreen.visibility = View.VISIBLE this@ReproductorActivity.fullScreen.visibility = View.VISIBLE
this@ReproductorActivity.fullScreen.addView(paramView, FrameLayout.LayoutParams(match_parent, match_parent)) this@ReproductorActivity.fullScreen.addView(paramView, FrameLayout.LayoutParams(matchParent, matchParent))
setFullscreen(this@ReproductorActivity.window)
val attrs = this@ReproductorActivity.window.attributes
attrs.flags = attrs.flags or WindowManager.LayoutParams.FLAG_FULLSCREEN
attrs.flags = attrs.flags or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
window.attributes = attrs
this@ReproductorActivity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE
this@ReproductorActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE this@ReproductorActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
} } catch (err: Exception) {
catch (err: Exception){
err.printStackTrace() err.printStackTrace()
} }
} }

View File

@ -4,8 +4,6 @@ import android.annotation.TargetApi
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.preference.ListPreference import android.preference.ListPreference
@ -13,9 +11,6 @@ import android.preference.Preference
import android.preference.PreferenceActivity import android.preference.PreferenceActivity
import android.preference.PreferenceFragment import android.preference.PreferenceFragment
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.preference.RingtonePreference
import android.text.TextUtils
import android.util.Log
import android.view.MenuItem import android.view.MenuItem
/** /**
@ -37,7 +32,7 @@ class SettingsActivity : AppCompatPreferenceActivity() {
override fun onBackPressed() { override fun onBackPressed() {
super.onBackPressed() super.onBackPressed()
ManagerSingleton.Toast(getString(R.string.pref_message_exit), this) ManagerSingleton.toast(getString(R.string.pref_message_exit), this)
} }
/** /**
@ -67,8 +62,8 @@ class SettingsActivity : AppCompatPreferenceActivity() {
* Make sure to deny any unknown fragments here. * Make sure to deny any unknown fragments here.
*/ */
override fun isValidFragment(fragmentName: String): Boolean { override fun isValidFragment(fragmentName: String): Boolean {
return PreferenceFragment::class.java.name == fragmentName return PreferenceFragment::class.java.name == fragmentName ||
|| GeneralPreferenceFragment::class.java.name == fragmentName GeneralPreferenceFragment::class.java.name == fragmentName
} }
/** /**
@ -116,11 +111,12 @@ class SettingsActivity : AppCompatPreferenceActivity() {
// Set the summary to reflect the new value. // Set the summary to reflect the new value.
preference.setSummary( preference.setSummary(
if (index >= 0) if (index >= 0) {
listPreference.entries[index] listPreference.entries[index]
else } else {
null) null
},
)
} else { } else {
// For all other preferences, set the summary to the value's // For all other preferences, set the summary to the value's
// simple string representation. // simple string representation.
@ -152,10 +148,12 @@ class SettingsActivity : AppCompatPreferenceActivity() {
// Trigger the listener immediately with the preference's // Trigger the listener immediately with the preference's
// current value. // current value.
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, sBindPreferenceSummaryToValueListener.onPreferenceChange(
PreferenceManager preference,
.getDefaultSharedPreferences(preference.context) PreferenceManager
.getString(preference.key, "")) .getDefaultSharedPreferences(preference.context)
.getString(preference.key, ""),
)
} }
} }
} }

View File

@ -0,0 +1,31 @@
package org.libre.agosto.p2play
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceFragmentCompat
class SettingsActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.settings_activity)
if (savedInstanceState == null) {
supportFragmentManager
.beginTransaction()
.replace(R.id.settings, SettingsFragment())
.commit()
}
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
override fun onDestroy() {
super.onDestroy()
ManagerSingleton.reloadSettings()
}
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.root_preferences, rootKey)
}
}
}

View File

@ -3,88 +3,80 @@ package org.libre.agosto.p2play
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.AsyncTask import android.os.AsyncTask
import android.support.v7.app.AppCompatActivity
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.preference.PreferenceManager
import android.util.Log import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
import org.libre.agosto.p2play.ajax.Auth import org.libre.agosto.p2play.ajax.Auth
import java.lang.Exception import java.lang.Exception
class SplashActivity : AppCompatActivity() { class SplashActivity : AppCompatActivity() {
lateinit var settings: SharedPreferences lateinit var settings: SharedPreferences
lateinit var editor: SharedPreferences.Editor
val client: Auth = Auth() val client: Auth = Auth()
val _db = Database(this) val db = Database(this)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash) setContentView(R.layout.activity_splash)
settings = PreferenceManager.getDefaultSharedPreferences(this) settings = PreferenceManager.getDefaultSharedPreferences(this)
ManagerSingleton.settings = settings
ManagerSingleton.db = db
ManagerSingleton.nfsw = settings.getBoolean("show_nfsw", false) ManagerSingleton.reloadSettings()
ManagerSingleton.videos_count = settings.getString("videos_count", "15").toInt()
val host = settings.getString("hostP2play", "")
val host = settings.getString("hostP2play","") val lastHost = settings.getString("last_host", "")
val lastHost = settings.getString("last_host","") if (host != "") {
if(host != ""){ if (lastHost != host) {
if(lastHost != host){
_db.logout()
Handler().postDelayed({ Handler().postDelayed({
startHostActivity() startHostActivity()
}, 2000) }, 2000)
}else{ } else {
ManagerSingleton.url = host ManagerSingleton.url = host
checkUser() checkUser()
} }
} } else {
else{
Handler().postDelayed({ Handler().postDelayed({
startHostActivity() startHostActivity()
}, 2000) }, 2000)
} }
} }
private fun checkUser(){ private fun checkUser() {
Log.d("was", "Chequed") Log.d("was", "Checked")
try { try {
val token = _db.getToken() val token = db.getToken()
val user = _db.getUser() val user = db.getUser()
AsyncTask.execute { AsyncTask.execute {
if (Looper.myLooper() == null) if (Looper.myLooper() == null) {
Looper.prepare() Looper.prepare()
}
if (token.status == 1 && user.status == 1) { if (token.status == 1 && user.status == 1) {
val client_id = settings.getString("client_id", "") val clientId = settings.getString("client_id", "")!!
val client_secret = settings.getString("client_secret", "") val clientSecret = settings.getString("client_secret", "")!!
val newToken = client.refreshToken(token, client_id, client_secret) val newToken = client.refreshToken(token, clientId, clientSecret)
when (token.status.toString()) { when (token.status.toString()) {
"1" -> { "1" -> {
_db.newToken(newToken) db.newToken(newToken)
ManagerSingleton.token = newToken ManagerSingleton.token = newToken
ManagerSingleton.user = user ManagerSingleton.user = user
} }
else -> _db.logout() else -> ManagerSingleton.logout()
} }
} else { } else {
_db.logout() ManagerSingleton.logout()
} }
startApp() startApp()
Log.d("Aqui", "81")
} }
} } catch (err: Exception) {
catch (err: Exception){
err.printStackTrace() err.printStackTrace()
Log.d("Aqui", "89")
Handler().postDelayed({ Handler().postDelayed({
startApp() startApp()
}, 2000) }, 2000)

View File

@ -1,38 +1,42 @@
package org.libre.agosto.p2play.adapters package org.libre.agosto.p2play.adapters
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.os.Bundle
import android.graphics.drawable.Drawable
import android.os.AsyncTask
import android.support.v7.widget.RecyclerView
import android.text.Html 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.ImageView import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import org.libre.agosto.p2play.* import org.libre.agosto.p2play.ManagerSingleton
import org.libre.agosto.p2play.R
import org.libre.agosto.p2play.dialogs.ThreadDialog
import org.libre.agosto.p2play.models.CommentaryModel import org.libre.agosto.p2play.models.CommentaryModel
import org.libre.agosto.p2play.models.VideoModel
import java.io.InputStream
import java.io.Serializable import java.io.Serializable
import java.net.URL
@Suppress("DEPRECATION")
class CommentariesAdapter(private val myDataset: ArrayList<CommentaryModel>) : class CommentariesAdapter(private val myDataset: ArrayList<CommentaryModel>) :
RecyclerView.Adapter<CommentariesAdapter.ViewHolder>() { RecyclerView.Adapter<CommentariesAdapter.ViewHolder>() {
private lateinit var fragmentManager: FragmentManager
fun setFragmentManager(manager: FragmentManager): CommentariesAdapter {
this.fragmentManager = manager
return this
}
// Provide a reference to the views for each data item // Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and // Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder. // you provide access to all the views for a data item in a view holder.
// Each data item is just a string in this case that is shown in a TextView. // Each data item is just a string in this case that is shown in a TextView.
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view){ class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val userImg: ImageView val userImg: ImageView
val username: TextView val username: TextView
val commentary: TextView val commentary: TextView
val replyBtn: TextView
val context: Context val context: Context
init { init {
@ -40,17 +44,20 @@ class CommentariesAdapter(private val myDataset: ArrayList<CommentaryModel>) :
username = view.findViewById(R.id.userTxt) username = view.findViewById(R.id.userTxt)
commentary = view.findViewById(R.id.userCommentary) commentary = view.findViewById(R.id.userCommentary)
userImg = view.findViewById(R.id.userCommentImg) userImg = view.findViewById(R.id.userCommentImg)
replyBtn = view.findViewById(R.id.replyBtn)
context = view.context context = view.context
} }
} }
// Create new views (invoked by the layout manager) // Create new views (invoked by the layout manager)
override fun onCreateViewHolder(parent: ViewGroup, override fun onCreateViewHolder(
viewType: Int): CommentariesAdapter.ViewHolder { parent: ViewGroup,
viewType: Int,
): ViewHolder {
// create a new view // create a new view
val view = LayoutInflater.from(parent.context) val view = LayoutInflater.from(parent.context)
.inflate(R.layout.view_commentary, parent, false) as View .inflate(R.layout.view_commentary, parent, false) as View
// set the view's size, margins, paddings and layout parameters // set the view's size, margins, paddings and layout parameters
return ViewHolder(view) return ViewHolder(view)
} }
@ -62,18 +69,48 @@ class CommentariesAdapter(private val myDataset: ArrayList<CommentaryModel>) :
holder.username.text = myDataset[position].username holder.username.text = myDataset[position].username
// holder.userImg.setOnClickListener { // holder.userImg.setOnClickListener {
// val intent = Intent(holder.context, ChannelActivity::class.java) // val intent = Intent(holder.context, ChannelActivity::class.java)
// intent.putExtra("channel", myDataset[position]) // intent.putExtra("channel", myDataset[position])
// holder.context.startActivity(intent) // holder.context.startActivity(intent)
// } // }
if(myDataset[position].userImageUrl!="") if (myDataset[position].userImageUrl != "") {
Picasso.get().load("https://"+ManagerSingleton.url+myDataset[position].userImageUrl).into(holder.userImg); Picasso.get().load("https://" + ManagerSingleton.url + myDataset[position].userImageUrl).into(holder.userImg)
}
holder.commentary.text = Html.fromHtml(myDataset[position].commentary) holder.commentary.text = Html.fromHtml(myDataset[position].commentary)
holder.replyBtn.setOnClickListener { this.initRepliesDialog(myDataset[position]) }
if (myDataset[position].replies > 0) {
holder.replyBtn.text = holder.itemView.context.getString(R.string.see_replies, myDataset[position].replies)
}
// TODO: Support for view and account (is different than a video channel)
// holder.userImg.setOnClickListener {
// val intent = Intent(holder.context, ChannelActivity::class.java)
// intent.putExtra("channel", myDataset[position].getAccount())
// holder.context.startActivity(intent)
// }
} }
// Return the size of your dataset (invoked by the layout manager) // Return the size of your dataset (invoked by the layout manager)
override fun getItemCount() = myDataset.size override fun getItemCount() = myDataset.size
private fun initRepliesDialog(commentData: CommentaryModel) {
val dialog = ThreadDialog()
val bundle = Bundle()
bundle.putSerializable("comment", commentData as Serializable)
dialog.arguments = bundle
dialog.fragmentManager2 = this.fragmentManager
val transaction = fragmentManager.beginTransaction()
// For a polished look, specify a transition animation.
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
// To make it fullscreen, use the 'content' root view as the container
// for the fragment, which is always the root view for the activity.
transaction
.add(android.R.id.content, dialog)
.addToBackStack("comments")
.commit()
}
} }

View File

@ -1,56 +1,44 @@
package org.libre.agosto.p2play.adapters package org.libre.agosto.p2play.adapters
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.AsyncTask
import android.support.v7.widget.RecyclerView
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.ImageView
import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import org.libre.agosto.p2play.* import org.libre.agosto.p2play.ChannelActivity
import org.libre.agosto.p2play.ManagerSingleton
import org.libre.agosto.p2play.R
import org.libre.agosto.p2play.ReproductorActivity
import org.libre.agosto.p2play.helpers.mapSeconds
import org.libre.agosto.p2play.models.VideoModel import org.libre.agosto.p2play.models.VideoModel
import java.io.InputStream
import java.io.Serializable import java.io.Serializable
import java.net.URL
import java.util.concurrent.TimeUnit
class VideosAdapter(private val myDataset: ArrayList<VideoModel>) : class VideosAdapter(private val myDataset: ArrayList<VideoModel>) :
RecyclerView.Adapter<VideosAdapter.ViewHolder>() { RecyclerView.Adapter<VideosAdapter.ViewHolder>() {
// Provide a reference to the views for each data item // Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and // Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder. // you provide access to all the views for a data item in a view holder.
// Each data item is just a string in this case that is shown in a TextView. // Each data item is just a string in this case that is shown in a TextView.
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view){ class ViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
val thumb: ImageView val thumb: ImageView = view.findViewById(R.id.thumb)
val userImg: ImageView val userImg: ImageView = view.findViewById(R.id.userImg)
val tittle: TextView val title: TextView = view.findViewById(R.id.tittleTxt)
val description: TextView val description: TextView = view.findViewById(R.id.descriptionTxt)
val context: Context val context: Context = view.context
val duration: TextView = view.findViewById(R.id.duration)
init { val isLive: TextView = view.findViewById(R.id.isLive)
// Define click listener for the ViewHolder's View
tittle = view.findViewById(R.id.tittleTxt)
description = view.findViewById(R.id.descriptionTxt)
thumb = view.findViewById(R.id.thumb)
userImg = view.findViewById(R.id.userImg)
context = view.context
}
} }
// Create new views (invoked by the layout manager) // Create new views (invoked by the layout manager)
override fun onCreateViewHolder(parent: ViewGroup, override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
viewType: Int): VideosAdapter.ViewHolder {
// create a new view // create a new view
val view = LayoutInflater.from(parent.context) val view = LayoutInflater.from(parent.context)
.inflate(R.layout.view_video, parent, false) as View .inflate(R.layout.view_video, parent, false) as View
// set the view's size, margins, paddings and layout parameters // set the view's size, margins, paddings and layout parameters
return ViewHolder(view) return ViewHolder(view)
} }
@ -59,55 +47,57 @@ class VideosAdapter(private val myDataset: ArrayList<VideoModel>) :
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// - get element from your dataset at this position // - get element from your dataset at this position
// - replace the contents of the view with that element // - replace the contents of the view with that element
holder.tittle.text = myDataset[position].name holder.title.text = myDataset[position].name
Picasso.get().load("https://"+ManagerSingleton.url+myDataset[position].thumbUrl).into(holder.thumb) holder.title.setOnClickListener {
this.launchChannelActivity(myDataset[position] as Serializable, holder.context)
}
Picasso.get().load("https://" + ManagerSingleton.url + myDataset[position].thumbUrl).into(holder.thumb)
holder.thumb.setOnClickListener { holder.thumb.setOnClickListener {
val intent = Intent(holder.context, ReproductorActivity::class.java) this.launchChannelActivity(myDataset[position] as Serializable, holder.context)
intent.putExtra("video", myDataset[position] as Serializable)
holder.context.startActivity(intent)
} }
holder.userImg.setOnClickListener { holder.userImg.setOnClickListener {
val intent = Intent(holder.context, ChannelActivity::class.java) val intent = Intent(holder.context, ChannelActivity::class.java)
intent.putExtra("channel", myDataset[position].getAccount()) intent.putExtra("channel", myDataset[position].getChannel())
holder.context.startActivity(intent) holder.context.startActivity(intent)
} }
if(myDataset[position].userImageUrl!="") if (myDataset[position].userImageUrl != "") {
Picasso.get().load("https://"+ManagerSingleton.url+myDataset[position].userImageUrl).into(holder.userImg) Picasso.get().load("https://" + ManagerSingleton.url + myDataset[position].userImageUrl).into(holder.userImg)
else } else {
Picasso.get().load(R.drawable.default_avatar).into(holder.userImg) Picasso.get().load(R.drawable.default_avatar).into(holder.userImg)
}
val viewsText = holder.context.getString(R.string.view_text) val viewsText = holder.context.getString(R.string.view_text)
var timeText = holder.context.getString(R.string.timeSec_text) val seconds = myDataset[position].duration.toInt()
var timeString = myDataset[position].duration.toString() val timeString = mapSeconds(seconds)
val seconds = myDataset[position].duration.toInt();
if(seconds > 60 && seconds < (60 * 60)){
timeText = holder.context.getString(R.string.timeMin_text)
timeString = (seconds / 60).toString() + ":" + (seconds % 60).toString()
}
else if(seconds > (60 * 60)){
timeText = holder.context.getString(R.string.timeHrs_text)
timeString = (seconds / 60 / 60).toString() + ":" + (seconds / 60 % 60).toString()
}
holder.description.text = myDataset[position].username+" - "+myDataset[position].views+" "+viewsText+" - "+timeString+" "+timeText holder.duration.text = timeString
holder.description.text = myDataset[position].username + " - " + myDataset[position].views + " " + viewsText
if (myDataset[position].isLive) {
holder.isLive.visibility = View.VISIBLE
}
} }
// Return the size of your dataset (invoked by the layout manager) // Return the size of your dataset (invoked by the layout manager)
override fun getItemCount() = myDataset.size override fun getItemCount() = myDataset.size
fun clearData(){ fun clearData() {
myDataset.clear() myDataset.clear()
notifyDataSetChanged() notifyDataSetChanged()
} }
fun addData(newItems: ArrayList<VideoModel>){ fun addData(newItems: ArrayList<VideoModel>) {
val lastPos = myDataset.size val lastPos = myDataset.size
myDataset.addAll(newItems) myDataset.addAll(newItems)
notifyItemRangeInserted(lastPos, newItems.size) notifyItemRangeInserted(lastPos, newItems.size)
} }
private fun launchChannelActivity(data: Serializable, context: Context) {
val intent = Intent(context, ReproductorActivity::class.java)
intent.putExtra("video", data)
context.startActivity(intent)
}
} }

View File

@ -3,11 +3,11 @@ package org.libre.agosto.p2play.ajax
import android.util.JsonReader import android.util.JsonReader
import java.io.InputStreamReader import java.io.InputStreamReader
class Actions: Client() { class Actions : Client() {
fun subscribe(token: String, account: String):Int{ fun subscribe(token: String, account: String): Int {
val con = this._newCon("users/me/subscriptions","POST", token) val con = this.newCon("users/me/subscriptions", "POST", token)
val params:String= "uri=$account" val params: String = "uri=$account"
con.outputStream.write(params.toByteArray()) con.outputStream.write(params.toByteArray())
var response = 0 var response = 0
@ -15,8 +15,7 @@ class Actions: Client() {
if (con.responseCode == 204) { if (con.responseCode == 204) {
response = 1 response = 1
} }
} } catch (err: Exception) {
catch (err: Exception){
err.printStackTrace() err.printStackTrace()
response = -1 response = -1
} }
@ -25,16 +24,15 @@ class Actions: Client() {
return response return response
} }
fun unSubscribe(token: String, account: String):Int{ fun unSubscribe(token: String, account: String): Int {
val con = this._newCon("users/me/subscriptions/$account","DELETE", token) val con = this.newCon("users/me/subscriptions/$account", "DELETE", token)
var response = 0 var response = 0
try { try {
if (con.responseCode == 204) { if (con.responseCode == 204) {
response = 1 response = 1
} }
} } catch (err: Exception) {
catch (err: Exception){
err.printStackTrace() err.printStackTrace()
response = -1 response = -1
} }
@ -43,8 +41,8 @@ class Actions: Client() {
return response return response
} }
fun getSubscription(token: String, account: String): Boolean{ fun getSubscription(token: String, account: String): Boolean {
val con = this._newCon("users/me/subscriptions/exist?uris=$account","GET", token) val con = this.newCon("users/me/subscriptions/exist?uris=$account", "GET", token)
var isSubscribed = false var isSubscribed = false
try { try {
@ -52,21 +50,20 @@ class Actions: Client() {
val response = InputStreamReader(con.inputStream) val response = InputStreamReader(con.inputStream)
val data = JsonReader(response) val data = JsonReader(response)
data.beginObject() data.beginObject()
while (data.hasNext()){ while (data.hasNext()) {
val key = data.nextName() val key = data.nextName()
when (key.toString()) { when (key.toString()) {
account->{ account -> {
isSubscribed = data.nextBoolean() isSubscribed = data.nextBoolean()
} }
else->{ else -> {
data.skipValue() data.skipValue()
} }
} }
} }
data.close() data.close()
} }
} } catch (err: Exception) {
catch (err: Exception){
err.printStackTrace() err.printStackTrace()
isSubscribed = false isSubscribed = false
} }
@ -75,8 +72,8 @@ class Actions: Client() {
return isSubscribed return isSubscribed
} }
fun rate(token: String, id_video: Int, rate: String):Int{ fun rate(token: String, id_video: Int, rate: String): Int {
val con = this._newCon("videos/$id_video/rate","PUT", token) val con = this.newCon("videos/$id_video/rate", "PUT", token)
val params = "rating=$rate" val params = "rating=$rate"
con.outputStream.write(params.toByteArray()) con.outputStream.write(params.toByteArray())
var response = 0 var response = 0
@ -85,8 +82,7 @@ class Actions: Client() {
if (con.responseCode == 204) { if (con.responseCode == 204) {
response = 1 response = 1
} }
} } catch (err: Exception) {
catch (err: Exception){
err.printStackTrace() err.printStackTrace()
response = -1 response = -1
} }
@ -95,8 +91,8 @@ class Actions: Client() {
return response return response
} }
fun getRate(token: String, id_video: Int):String{ fun getRate(token: String, id_video: Int): String {
val con = this._newCon("users/me/videos/$id_video/rating","GET", token) val con = this.newCon("users/me/videos/$id_video/rating", "GET", token)
var rating = "none" var rating = "none"
try { try {
@ -104,21 +100,20 @@ class Actions: Client() {
val response = InputStreamReader(con.inputStream) val response = InputStreamReader(con.inputStream)
val data = JsonReader(response) val data = JsonReader(response)
data.beginObject() data.beginObject()
while (data.hasNext()){ while (data.hasNext()) {
val key = data.nextName() val key = data.nextName()
when (key.toString()) { when (key.toString()) {
"rating"->{ "rating" -> {
rating = data.nextString() rating = data.nextString()
} }
else->{ else -> {
data.skipValue() data.skipValue()
} }
} }
} }
con.disconnect() con.disconnect()
} }
} } catch (err: Exception) {
catch (err: Exception){
err.printStackTrace() err.printStackTrace()
rating = "none" rating = "none"
} }
@ -128,13 +123,13 @@ class Actions: Client() {
} }
fun reportVideo(videoId: Int, reason: String, token: String): Boolean { fun reportVideo(videoId: Int, reason: String, token: String): Boolean {
val con = this._newCon("videos/$videoId/abuse", "POST", token) val con = this.newCon("videos/$videoId/abuse", "POST", token)
val params = "reason=$reason" val params = "reason=$reason"
con.outputStream.write(params.toByteArray()) con.outputStream.write(params.toByteArray())
var response = false var response = false
try { try {
if(con.responseCode == 200){ if (con.responseCode == 200) {
response = true response = true
} }
} catch (err: Exception) { } catch (err: Exception) {
@ -146,13 +141,13 @@ class Actions: Client() {
} }
fun watchVideo(videoId: Int, token: String): Boolean { fun watchVideo(videoId: Int, token: String): Boolean {
val con = this._newCon("videos/$videoId/watching", "PUT", token) val con = this.newCon("videos/$videoId/watching", "PUT", token)
val params = "currentTime=1" val params = "currentTime=1"
con.outputStream.write(params.toByteArray()) con.outputStream.write(params.toByteArray())
var response = false var response = false
try { try {
if(con.responseCode == 204){ if (con.responseCode == 204) {
response = true response = true
} }
} catch (err: Exception) { } catch (err: Exception) {

View File

@ -4,46 +4,53 @@ package org.libre.agosto.p2play.ajax
import android.util.JsonReader import android.util.JsonReader
import android.util.JsonToken import android.util.JsonToken
import android.util.Log import android.util.Log
import org.libre.agosto.p2play.ManagerSingleton
import org.libre.agosto.p2play.models.TokenModel import org.libre.agosto.p2play.models.TokenModel
import org.libre.agosto.p2play.models.UserModel import org.libre.agosto.p2play.models.UserModel
import java.io.InputStreamReader import java.io.InputStreamReader
class Auth: Client() { class Auth : Client() {
private val stockParams = "grant_type=password" private val stockParams = "grant_type=password"
fun login(username: String, password: String, client_id: String, client_secret: String): TokenModel{ fun login(username: String, password: String, client_id: String, client_secret: String, twoFactorCode: String? = null): TokenModel {
val con = this._newCon("users/token","POST") val con = this.newCon("users/token", "POST")
val params = "$stockParams&username=$username&password=$password&client_id=$client_id&client_secret=$client_secret" val params = "$stockParams&username=$username&password=$password&client_id=$client_id&client_secret=$client_secret"
if (twoFactorCode !== null) {
con.setRequestProperty("x-peertube-otp", twoFactorCode)
}
con.outputStream.write(params.toByteArray()) con.outputStream.write(params.toByteArray())
val token = TokenModel() val token = TokenModel()
try { try {
when (con.responseCode) {
200 -> {
val response = InputStreamReader(con.inputStream)
val data = JsonReader(response)
data.beginObject()
if(con.responseCode==200){ while (data.hasNext()) {
val response = InputStreamReader(con.inputStream) val k = data.nextName()
val data = JsonReader(response) when (k.toString()) {
data.beginObject() "access_token" -> token.token = data.nextString()
"refresh_token" -> token.refresh_token = data.nextString()
while(data.hasNext()){ else -> data.skipValue()
val k = data.nextName() }
when(k.toString()){
"access_token" -> token.token = data.nextString()
"refresh_token" -> token.refresh_token = data.nextString()
else -> data.skipValue()
} }
data.endObject()
data.close()
token.status = 1
}
401 -> {
// User require 2FA code
token.status = -2
}
else -> {
Log.d("Status", con.responseMessage)
} }
data.endObject()
data.close()
token.status = 1
} }
else{ } catch (err: Exception) {
Log.d("Status", con.responseMessage)
}
}
catch (err: Exception){
err.printStackTrace() err.printStackTrace()
token.status = 0 token.status = 0
} }
@ -52,8 +59,8 @@ class Auth: Client() {
return token return token
} }
fun register(username: String, password: String, email: String): Int{ fun register(username: String, password: String, email: String): Int {
val con = this._newCon("users/register","POST") val con = this.newCon("users/register", "POST")
val params = "username=$username&password=$password&email=$email" val params = "username=$username&password=$password&email=$email"
con.outputStream.write(params.toByteArray()) con.outputStream.write(params.toByteArray())
@ -63,32 +70,30 @@ class Auth: Client() {
if (con.responseCode == 204) { if (con.responseCode == 204) {
response = 1 response = 1
} }
} } catch (err: Exception) {
catch (err: Exception){
err.printStackTrace() err.printStackTrace()
response = -1 response = -1
} }
con.disconnect() con.disconnect()
return response return response
} }
fun refreshToken(token: TokenModel, client_id: String, client_secret: String): TokenModel{ fun refreshToken(token: TokenModel, client_id: String, client_secret: String): TokenModel {
val con = this._newCon("users/token", "POST", token.token) val con = this.newCon("users/token", "POST", token.token)
val params = "refresh_token=${token.refresh_token}&response_type=code&grant_type=refresh_token&client_id=$client_id&client_secret=$client_secret" val params = "refresh_token=${token.refresh_token}&response_type=code&grant_type=refresh_token&client_id=$client_id&client_secret=$client_secret"
con.outputStream.write(params.toByteArray()) con.outputStream.write(params.toByteArray())
// val token = TokenModel() // val token = TokenModel()
try { try {
if(con.responseCode==200){ if (con.responseCode == 200) {
val response = InputStreamReader(con.inputStream) val response = InputStreamReader(con.inputStream)
val data = JsonReader(response) val data = JsonReader(response)
data.beginObject() data.beginObject()
while(data.hasNext()){ while (data.hasNext()) {
val k = data.nextName() val k = data.nextName()
when(k.toString()){ when (k.toString()) {
"access_token" -> token.token = data.nextString() "access_token" -> token.token = data.nextString()
"refresh_token" -> token.refresh_token = data.nextString() "refresh_token" -> token.refresh_token = data.nextString()
else -> data.skipValue() else -> data.skipValue()
@ -98,13 +103,10 @@ class Auth: Client() {
data.endObject() data.endObject()
data.close() data.close()
token.status = 1 token.status = 1
} else {
}
else{
Log.d("Status", con.responseMessage) Log.d("Status", con.responseMessage)
} }
} } catch (err: Exception) {
catch (err: Exception){
err.printStackTrace() err.printStackTrace()
token.status = 0 token.status = 0
} }
@ -113,32 +115,31 @@ class Auth: Client() {
return token return token
} }
fun me(token: String): UserModel{ fun me(token: String): UserModel {
val con = this._newCon("users/me","GET", token) val con = this.newCon("users/me", "GET", token)
val user = UserModel() val user = UserModel()
try { try {
if (con.responseCode == 200) {
if(con.responseCode==200){
val response = InputStreamReader(con.inputStream) val response = InputStreamReader(con.inputStream)
val data = JsonReader(response) val data = JsonReader(response)
data.beginObject() data.beginObject()
while(data.hasNext()){ while (data.hasNext()) {
val k = data.nextName() val k = data.nextName()
when(k.toString()){ when (k.toString()) {
"id" -> user.uuid = data.nextInt() "id" -> user.uuid = data.nextInt()
"username" -> user.username = data.nextString() "username" -> user.username = data.nextString()
"email" -> user.email = data.nextString() "email" -> user.email = data.nextString()
"displayNSFW" -> user.nsfw = data.nextBoolean() "displayNSFW" -> user.nsfw = data.nextBoolean()
"account" -> { "account" -> {
data.beginObject() data.beginObject()
while (data.hasNext()){ while (data.hasNext()) {
val l = data.nextName() val l = data.nextName()
when(l.toString()){ when (l.toString()) {
"followersCount" -> user.followers = data.nextInt() "followersCount" -> user.followers = data.nextInt()
"avatar" -> { "avatar" -> {
if(data.peek() == JsonToken.BEGIN_OBJECT) { if (data.peek() == JsonToken.BEGIN_OBJECT) {
data.beginObject() data.beginObject()
while (data.hasNext()) { while (data.hasNext()) {
val m = data.nextName() val m = data.nextName()
@ -148,8 +149,7 @@ class Auth: Client() {
} }
} }
data.endObject() data.endObject()
} } else {
else{
data.skipValue() data.skipValue()
} }
} }
@ -165,13 +165,10 @@ class Auth: Client() {
data.endObject() data.endObject()
data.close() data.close()
user.status = 1 user.status = 1
} else {
}
else{
Log.d("Status", con.responseMessage) Log.d("Status", con.responseMessage)
} }
} } catch (err: Exception) {
catch (err: Exception){
err.printStackTrace() err.printStackTrace()
user.status = 0 user.status = 0
} }
@ -179,5 +176,4 @@ class Auth: Client() {
con.disconnect() con.disconnect()
return user return user
} }
} }

View File

@ -1,14 +1,12 @@
package org.libre.agosto.p2play.ajax package org.libre.agosto.p2play.ajax
import android.util.JsonReader import android.util.JsonReader
import android.util.JsonToken
import org.libre.agosto.p2play.models.ChannelModel import org.libre.agosto.p2play.models.ChannelModel
import org.libre.agosto.p2play.models.CommentaryModel
import java.io.InputStreamReader import java.io.InputStreamReader
class Channels: Client() { class Channels : Client() {
private fun parseChannel(data: JsonReader): ChannelModel{ private fun parseChannel(data: JsonReader): ChannelModel {
val channel = ChannelModel() val channel = ChannelModel()
data.close() data.close()
@ -17,16 +15,16 @@ class Channels: Client() {
} }
fun getChannelInfo(account: String): ChannelModel { fun getChannelInfo(account: String): ChannelModel {
val con = this._newCon("video-channels/$account", "GET") val con = this.newCon("video-channels/$account", "GET")
var channel = ChannelModel() var channel = ChannelModel()
try { try {
if(con.responseCode == 200){ if (con.responseCode == 200) {
val response = InputStreamReader(con.inputStream) val response = InputStreamReader(con.inputStream)
val data = JsonReader(response) val data = JsonReader(response)
channel.parseChannel(data) channel.parseChannel(data)
data.close() data.close()
} }
}catch (err: Exception) { } catch (err: Exception) {
err.printStackTrace() err.printStackTrace()
} }

View File

@ -5,39 +5,37 @@ import android.util.Log
import org.libre.agosto.p2play.ManagerSingleton import org.libre.agosto.p2play.ManagerSingleton
import org.libre.agosto.p2play.models.HostModel import org.libre.agosto.p2play.models.HostModel
import java.io.InputStreamReader import java.io.InputStreamReader
import java.io.Reader
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
open class Client { open class Client {
protected fun newCon(uri: String, method: String, token: String = ""): HttpURLConnection {
protected fun _newCon(uri: String, method: String, token: String = ""): HttpURLConnection {
val url = URL("https://${ManagerSingleton.url}/api/v1/$uri") val url = URL("https://${ManagerSingleton.url}/api/v1/$uri")
val con = url.openConnection() as HttpURLConnection val con = url.openConnection() as HttpURLConnection
con.setRequestProperty("User-Agent", "P2play/0.1") con.setRequestProperty("User-Agent", "P2play/0.5.3")
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded") con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
con.setRequestProperty("Accept", "*/*") con.setRequestProperty("Accept", "*/*")
if(token != ""){ if (token != "") {
con.setRequestProperty("Authorization", "Bearer $token") con.setRequestProperty("Authorization", "Bearer $token")
} }
con.requestMethod=method con.requestMethod = method
con.connectTimeout=60000 con.connectTimeout = 60000
con.readTimeout=60000 con.readTimeout = 60000
if(method == "POST") if (method == "POST") {
con.doOutput=true con.doOutput = true
}
Log.d("Petition", url.toString()) Log.d("Petition", url.toString())
return con return con
} }
fun getKeys():HostModel{ fun getKeys(): HostModel {
val con = this._newCon("oauth-clients/local","GET") val con = this.newCon("oauth-clients/local", "GET")
val keys = HostModel("","") val keys = HostModel("", "")
try { try {
if (con.responseCode == 200) { if (con.responseCode == 200) {
val response = InputStreamReader(con.inputStream) val response = InputStreamReader(con.inputStream)
@ -46,26 +44,24 @@ open class Client {
while (data.hasNext()) { while (data.hasNext()) {
val key = data.nextName() val key = data.nextName()
when (key.toString()) { when (key.toString()) {
"client_id"->{ "client_id" -> {
keys.client_id = data.nextString() keys.client_id = data.nextString()
} }
"client_secret"->{ "client_secret" -> {
keys.client_secret = data.nextString() keys.client_secret = data.nextString()
} }
else->{ else -> {
data.skipValue() data.skipValue()
} }
} }
} }
data.close() data.close()
} }
} catch(err:Exception){ } catch (err: Exception) {
err.printStackTrace() err.printStackTrace()
} }
con.disconnect() con.disconnect()
return keys return keys
} }
} }

View File

@ -1,18 +1,16 @@
package org.libre.agosto.p2play.ajax package org.libre.agosto.p2play.ajax
import android.util.JsonReader import android.util.JsonReader
import android.util.JsonToken
import android.util.Log import android.util.Log
import org.libre.agosto.p2play.models.CommentaryModel import org.libre.agosto.p2play.models.CommentaryModel
import java.io.InputStreamReader import java.io.InputStreamReader
class Comments: Client() { class Comments : Client() {
private fun parseCommentaries(data: JsonReader): ArrayList<CommentaryModel> { private fun parseCommentaries(data: JsonReader): ArrayList<CommentaryModel> {
val commentaries = arrayListOf<CommentaryModel>() val commentaries = arrayListOf<CommentaryModel>()
data.beginObject() data.beginObject()
while (data.hasNext()){ while (data.hasNext()) {
when(data.nextName()) { when (data.nextName()) {
"data" -> { "data" -> {
data.beginArray() data.beginArray()
while (data.hasNext()) { while (data.hasNext()) {
@ -32,7 +30,7 @@ class Comments: Client() {
fun getCommentaries(videoId: Int): ArrayList<CommentaryModel> { fun getCommentaries(videoId: Int): ArrayList<CommentaryModel> {
var commentaries = arrayListOf<CommentaryModel>() var commentaries = arrayListOf<CommentaryModel>()
val con = this._newCon("videos/$videoId/comment-threads", "GET") val con = this.newCon("videos/$videoId/comment-threads", "GET")
try { try {
if (con.responseCode == 200) { if (con.responseCode == 200) {
@ -41,7 +39,7 @@ class Comments: Client() {
commentaries = parseCommentaries(data) commentaries = parseCommentaries(data)
data.close() data.close()
} }
} catch(err:Exception){ } catch (err: Exception) {
err.printStackTrace() err.printStackTrace()
} }
con.disconnect() con.disconnect()
@ -49,7 +47,7 @@ class Comments: Client() {
} }
fun makeCommentary(token: String, videoId: Int, text: String): Boolean { fun makeCommentary(token: String, videoId: Int, text: String): Boolean {
val con = this._newCon("videos/$videoId/comment-threads", "POST", token) val con = this.newCon("videos/$videoId/comment-threads", "POST", token)
val params = "text=$text" val params = "text=$text"
con.outputStream.write(params.toByteArray()) con.outputStream.write(params.toByteArray())
@ -59,13 +57,11 @@ class Comments: Client() {
if (con.responseCode == 200) { if (con.responseCode == 200) {
con.disconnect() con.disconnect()
response = true response = true
} } else {
else{
Log.d("Status", con.responseMessage) Log.d("Status", con.responseMessage)
response = false response = false
} }
} } catch (err: Exception) {
catch (err: Exception){
err.printStackTrace() err.printStackTrace()
response = false response = false
} }
@ -74,4 +70,67 @@ class Comments: Client() {
return response return response
} }
fun getCommentariesThread(videoId: Int, threadId: Int): ArrayList<CommentaryModel> {
var commentaries = arrayListOf<CommentaryModel>()
val con = this.newCon("videos/$videoId/comment-threads/$threadId", "GET")
try {
if (con.responseCode == 200) {
val response = InputStreamReader(con.inputStream)
val data = JsonReader(response)
data.beginObject()
while (data.hasNext()) {
when (data.nextName()) {
"children" -> {
data.beginArray()
while (data.hasNext()) {
data.beginObject()
while (data.hasNext()) {
when (data.nextName()) {
"comment" -> {
val comment = CommentaryModel()
comment.parseCommentary(data)
commentaries.add(comment)
}
else -> data.skipValue()
}
}
data.endObject()
}
data.endArray()
}
else -> data.skipValue()
}
}
data.endObject()
data.close()
}
} catch (err: Exception) {
err.printStackTrace()
}
con.disconnect()
return commentaries
}
fun replyThread(token: String, videoId: Int, threadId: Int, text: String): Boolean {
val con = this.newCon("videos/$videoId/comments/$threadId", "POST", token)
val params = "text=$text"
con.outputStream.write(params.toByteArray())
val response: Boolean = try {
if (con.responseCode == 200) {
con.disconnect()
true
} else {
Log.d("Status", con.responseMessage)
false
}
} catch (err: Exception) {
err.printStackTrace()
false
}
con.disconnect()
return response
}
} }

View File

@ -1,19 +1,18 @@
package org.libre.agosto.p2play.ajax package org.libre.agosto.p2play.ajax
import android.util.JsonReader import android.util.JsonReader
import android.util.JsonToken
import org.libre.agosto.p2play.ManagerSingleton import org.libre.agosto.p2play.ManagerSingleton
import org.libre.agosto.p2play.models.VideoModel import org.libre.agosto.p2play.models.VideoModel
import java.io.InputStreamReader import java.io.InputStreamReader
class Videos: Client() { class Videos : Client() {
private fun parseVideos(data: JsonReader): ArrayList<VideoModel>{ private fun parseVideos(data: JsonReader): ArrayList<VideoModel> {
val videos = arrayListOf<VideoModel>() val videos = arrayListOf<VideoModel>()
data.beginObject() data.beginObject()
while (data.hasNext()){ while (data.hasNext()) {
when(data.nextName()){ when (data.nextName()) {
"data"->{ "data" -> {
data.beginArray() data.beginArray()
while (data.hasNext()) { while (data.hasNext()) {
val video = VideoModel() val video = VideoModel()
@ -22,7 +21,7 @@ class Videos: Client() {
} }
data.endArray() data.endArray()
} }
else-> data.skipValue() else -> data.skipValue()
} }
} }
data.endObject() data.endObject()
@ -30,13 +29,12 @@ class Videos: Client() {
return videos return videos
} }
private fun getVideos(start:Int, sort:String = "-publishedAt", filter:String = ""):ArrayList<VideoModel>{ private fun getVideos(start: Int, sort: String = "-publishedAt", isLocal: Boolean = false): ArrayList<VideoModel> {
val nsfw = ManagerSingleton.nfsw val nsfw = ManagerSingleton.nfsw
val count = ManagerSingleton.videos_count val count = ManagerSingleton.videosCount
var params = "start=$start&count=$count&sort=$sort&nsfw=$nsfw" var params = "start=$start&count=$count&sort=$sort&nsfw=$nsfw&isLocal=$isLocal"
if(filter != "")
params+="&filter=$filter" val con = this.newCon("videos?$params", "GET")
val con = this._newCon("videos?$params","GET")
var videos = arrayListOf<VideoModel>() var videos = arrayListOf<VideoModel>()
try { try {
if (con.responseCode == 200) { if (con.responseCode == 200) {
@ -45,33 +43,33 @@ class Videos: Client() {
videos = parseVideos(data) videos = parseVideos(data)
data.close() data.close()
} }
} catch(err:Exception){ } catch (err: Exception) {
err.printStackTrace() err.printStackTrace()
} }
con.disconnect() con.disconnect()
return videos return videos
} }
fun getLastVideos(start:Int = 0): ArrayList<VideoModel>{ fun getLastVideos(start: Int = 0): ArrayList<VideoModel> {
return this.getVideos(start) return this.getVideos(start)
} }
fun getPopularVideos(start:Int = 0): ArrayList<VideoModel>{ fun getPopularVideos(start: Int = 0): ArrayList<VideoModel> {
return this.getVideos(start,"-views") return this.getVideos(start, "-views")
} }
fun getTrendingVideos(start:Int = 0): ArrayList<VideoModel>{ fun getTrendingVideos(start: Int = 0): ArrayList<VideoModel> {
return this.getVideos(start,"-trending") return this.getVideos(start, "-trending")
} }
fun getLocalVideos(start:Int = 0): ArrayList<VideoModel>{ fun getLocalVideos(start: Int = 0): ArrayList<VideoModel> {
return this.getVideos(start,"-publishedAt", "local") return this.getVideos(start, "-publishedAt", true)
} }
fun myVideos(token: String, start: Int = 0): ArrayList<VideoModel>{ fun myVideos(token: String, start: Int = 0): ArrayList<VideoModel> {
val count = ManagerSingleton.videos_count val count = ManagerSingleton.videosCount
val params = "start=$start&count=$count" val params = "start=$start&count=$count"
val con = this._newCon("users/me/videos?$params","GET", token) val con = this.newCon("users/me/videos?$params", "GET", token)
var videos = arrayListOf<VideoModel>() var videos = arrayListOf<VideoModel>()
try { try {
if (con.responseCode == 200) { if (con.responseCode == 200) {
@ -80,7 +78,7 @@ class Videos: Client() {
videos = parseVideos(data) videos = parseVideos(data)
data.close() data.close()
} }
} catch(err:Exception){ } catch (err: Exception) {
err.printStackTrace() err.printStackTrace()
} }
@ -88,10 +86,34 @@ class Videos: Client() {
return videos return videos
} }
fun videoSubscriptions(token: String, start: Int = 0): ArrayList<VideoModel>{ fun videoSubscriptions(token: String, start: Int = 0): ArrayList<VideoModel> {
val count = ManagerSingleton.videos_count val count = ManagerSingleton.videosCount
val params = "start=$start&count=$count" val params = "start=$start&count=$count"
val con = this._newCon("users/me/subscriptions/videos?$params","GET", token) val con = this.newCon("users/me/subscriptions/videos?$params", "GET", token)
var videos = arrayListOf<VideoModel>()
try {
if (con.responseCode == 200) {
val response = InputStreamReader(con.inputStream)
val data = JsonReader(response)
videos = parseVideos(data)
data.close()
} else {
val response = InputStreamReader(con.inputStream)
val data = JsonReader(response)
print(data)
}
} catch (err: Exception) {
err.printStackTrace()
}
con.disconnect()
return videos
}
fun videoHistory(token: String, start: Int = 0): ArrayList<VideoModel> {
val count = ManagerSingleton.videosCount
val params = "start=$start&count=$count"
val con = this.newCon("users/me/history/videos?$params", "GET", token)
var videos = arrayListOf<VideoModel>() var videos = arrayListOf<VideoModel>()
try { try {
if (con.responseCode == 200) { if (con.responseCode == 200) {
@ -100,7 +122,7 @@ class Videos: Client() {
videos = parseVideos(data) videos = parseVideos(data)
data.close() data.close()
} }
} catch(err:Exception){ } catch (err: Exception) {
err.printStackTrace() err.printStackTrace()
} }
@ -108,31 +130,11 @@ class Videos: Client() {
return videos return videos
} }
fun videoHistory(token: String, start: Int = 0): ArrayList<VideoModel>{ fun search(text: String, start: Int = 0): ArrayList<VideoModel> {
val count = ManagerSingleton.videos_count val count = ManagerSingleton.videosCount
val params = "start=$start&count=$count"
val con = this._newCon("users/me/history/videos?$params","GET", token)
var videos = arrayListOf<VideoModel>()
try {
if (con.responseCode == 200) {
val response = InputStreamReader(con.inputStream)
val data = JsonReader(response)
videos = parseVideos(data)
data.close()
}
} catch(err:Exception){
err.printStackTrace()
}
con.disconnect()
return videos
}
fun search(text: String, start: Int = 0): ArrayList<VideoModel>{
val count = ManagerSingleton.videos_count
val nsfw = ManagerSingleton.nfsw val nsfw = ManagerSingleton.nfsw
val params = "search=$text&start=$start&count=$count&nsfw=$nsfw" val params = "search=$text&start=$start&count=$count&nsfw=$nsfw"
val con = this._newCon("search/videos?$params","GET") val con = this.newCon("search/videos?$params", "GET")
var videos = arrayListOf<VideoModel>() var videos = arrayListOf<VideoModel>()
try { try {
if (con.responseCode == 200) { if (con.responseCode == 200) {
@ -141,15 +143,15 @@ class Videos: Client() {
videos = parseVideos(data) videos = parseVideos(data)
data.close() data.close()
} }
} catch(err:Exception){ } catch (err: Exception) {
err.printStackTrace() err.printStackTrace()
} }
con.disconnect() con.disconnect()
return videos return videos
} }
fun fullDescription(videoId: Int): String{ fun fullDescription(videoId: Int): String {
val con = this._newCon("videos/$videoId/description","GET") val con = this.newCon("videos/$videoId/description", "GET")
var description = "" var description = ""
try { try {
if (con.responseCode == 200) { if (con.responseCode == 200) {
@ -157,9 +159,9 @@ class Videos: Client() {
val data = JsonReader(response) val data = JsonReader(response)
data.beginObject() data.beginObject()
while (data.hasNext()){ while (data.hasNext()) {
val name = data.nextName() val name = data.nextName()
when(name){ when (name) {
"description" -> description = data.nextString() "description" -> description = data.nextString()
else -> data.skipValue() else -> data.skipValue()
} }
@ -167,7 +169,7 @@ class Videos: Client() {
data.endObject() data.endObject()
data.close() data.close()
} }
} catch(err:Exception){ } catch (err: Exception) {
err.printStackTrace() err.printStackTrace()
description = "Error!" description = "Error!"
} }
@ -176,9 +178,9 @@ class Videos: Client() {
} }
fun channelVideos(account: String, start: Int): ArrayList<VideoModel> { fun channelVideos(account: String, start: Int): ArrayList<VideoModel> {
val count = ManagerSingleton.videos_count val count = ManagerSingleton.videosCount
val params = "start=$start&count=$count" val params = "start=$start&count=$count"
val con = this._newCon("video-channels/$account/videos?$params","GET") val con = this.newCon("video-channels/$account/videos?$params", "GET")
var videos = arrayListOf<VideoModel>() var videos = arrayListOf<VideoModel>()
try { try {
if (con.responseCode == 200) { if (con.responseCode == 200) {
@ -187,7 +189,7 @@ class Videos: Client() {
videos = parseVideos(data) videos = parseVideos(data)
data.close() data.close()
} }
} catch(err:Exception){ } catch (err: Exception) {
err.printStackTrace() err.printStackTrace()
} }
@ -195,7 +197,25 @@ class Videos: Client() {
return videos return videos
} }
fun getMostLikedVideos(start:Int = 0): ArrayList<VideoModel>{ fun getMostLikedVideos(start: Int = 0): ArrayList<VideoModel> {
return this.getVideos(start,"-likes") return this.getVideos(start, "-likes")
}
fun getVideo(uuid: String): VideoModel {
val con = this.newCon("videos/$uuid", "GET")
val video = VideoModel()
try {
if (con.responseCode == 200) {
val response = InputStreamReader(con.inputStream)
val data = JsonReader(response)
video.parseVideo(data)
data.close()
}
} catch (err: Exception) {
err.printStackTrace()
}
con.disconnect()
return video
} }
} }

View File

@ -0,0 +1,132 @@
package org.libre.agosto.p2play.dialogs
import android.app.Dialog
import android.os.AsyncTask
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.Window
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.comment_component.commentaryLayout
import kotlinx.android.synthetic.main.comment_component.commentaryText
import kotlinx.android.synthetic.main.comment_component.userImgCom
import kotlinx.android.synthetic.main.comment_component.view.commentaryBtn
import kotlinx.android.synthetic.main.comment_component.view.commentaryLayout
import kotlinx.android.synthetic.main.comment_component.view.commentaryText
import kotlinx.android.synthetic.main.comment_component.view.userImgCom
import kotlinx.android.synthetic.main.dialog_thread.view.materialToolbar
import kotlinx.android.synthetic.main.view_commentary.view.replyBtn
import kotlinx.android.synthetic.main.view_commentary.view.userCommentImg
import kotlinx.android.synthetic.main.view_commentary.view.userCommentary
import kotlinx.android.synthetic.main.view_commentary.view.userTxt
import org.libre.agosto.p2play.ManagerSingleton
import org.libre.agosto.p2play.R
import org.libre.agosto.p2play.adapters.CommentariesAdapter
import org.libre.agosto.p2play.ajax.Comments
import org.libre.agosto.p2play.models.CommentaryModel
class ThreadDialog : DialogFragment() {
private lateinit var comment: CommentaryModel
lateinit var fragmentManager2: FragmentManager
private val client: Comments = Comments()
// The system calls this to get the DialogFragment's layout, regardless of
// whether it's being displayed as a dialog or an embedded fragment.
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
// Inflate the layout to use as a dialog or embedded fragment.
val view = inflater.inflate(R.layout.dialog_thread, container, false)
comment = arguments?.getSerializable("comment") as CommentaryModel
view.userTxt.text = comment.username
view.userCommentary.text = comment.commentary
Picasso.get().load("https://${ManagerSingleton.url}${comment.userImageUrl}").into(view.userCommentImg)
view.replyBtn.visibility = View.GONE
if (ManagerSingleton.user.status == 1) {
view.commentaryText.setText("${comment.username}@${comment.userHost} ")
if (ManagerSingleton.user.avatar != "") {
Picasso.get().load("https://${ManagerSingleton.url}${ManagerSingleton.user.avatar}").into(view.userImgCom)
}
view.commentaryText.requestFocus()
view.commentaryBtn.setOnClickListener { this.replyThread() }
} else {
view.commentaryLayout.visibility = View.GONE
}
view.materialToolbar.setTitle("Thread")
view.materialToolbar.setNavigationIcon(R.drawable.baseline_arrow_back_24)
view.materialToolbar.setNavigationOnClickListener {
dismiss()
this.fragmentManager2.popBackStack()
}
this.getComments()
return view
}
// The system calls this only when creating the layout in a dialog.
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
return dialog
}
private fun getComments() {
AsyncTask.execute {
val data =
this.client.getCommentariesThread(this.comment.videoId, this.comment.id)
activity?.runOnUiThread {
this.setDataComments(data)
}
}
}
private fun setDataComments(data: ArrayList<CommentaryModel>) {
// Set data for RecyclerView
val viewAdapter = CommentariesAdapter(data).setFragmentManager(this.fragmentManager2)
view?.findViewById<RecyclerView>(R.id.listCommentaries)?.let {
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
it.setHasFixedSize(true)
// use a linear layout manager
it.layoutManager = LinearLayoutManager(activity)
// specify an viewAdapter (see also next example)
it.adapter = viewAdapter
}
}
private fun replyThread() {
val commentary = view?.commentaryText?.text.toString()
if (commentary == "") {
ManagerSingleton.toast(getString(R.string.emptyCommentaryMsg), requireActivity())
return
}
AsyncTask.execute {
val res = this.client.replyThread(ManagerSingleton.token.token, this.comment.videoId, this.comment.id, commentary)
activity?.runOnUiThread {
if (res) {
ManagerSingleton.toast(getString(R.string.makedCommentaryMsg), requireActivity())
commentaryText.text?.clear()
this.getComments()
} else {
ManagerSingleton.toast(getString(R.string.errorCommentaryMsg), requireActivity())
}
}
}
}
}

View File

@ -0,0 +1,20 @@
package org.libre.agosto.p2play.helpers
fun mapSeconds(inputSeconds: Int): String {
val seconds = (inputSeconds % 60)
var minutes = inputSeconds / 60
var hours = 0
if (minutes > 60) {
hours = minutes / 60
minutes %= 60
}
var result = minutes.toString().padStart(2, '0') + ":" + seconds.toString().padStart(2, '0')
if (hours > 0) {
result = hours.toString().padStart(2, '0') + ":" + result
}
return result
}

View File

@ -0,0 +1,13 @@
package org.libre.agosto.p2play.helpers
import android.view.View
import android.view.Window
import android.view.WindowManager
fun setFullscreen(window: Window) {
val attrs = window.attributes
attrs.flags = attrs.flags or WindowManager.LayoutParams.FLAG_FULLSCREEN
attrs.flags = attrs.flags or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
window.attributes = attrs
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
}

View File

@ -3,16 +3,16 @@ package org.libre.agosto.p2play.models
import android.util.JsonReader import android.util.JsonReader
import android.util.JsonToken import android.util.JsonToken
class ChannelModel ( class ChannelModel(
var id: Int = 0, var id: Int = 0,
var url: String = "", var url: String = "",
var nameChannel: String = "", var nameChannel: String = "",
var followers: Int = 0, var followers: Int = 0,
var host: String = "", var host: String = "",
var name: String = "", var name: String = "",
var description: String = "", var description: String = "",
var support: String = "", var support: String = "",
var channelImg: String = "" var channelImg: String = "",
) { ) {
fun getAccount(): String { fun getAccount(): String {
return "$nameChannel@$host" return "$nameChannel@$host"
@ -22,8 +22,7 @@ class ChannelModel (
data.beginObject() data.beginObject()
while (data.hasNext()) { while (data.hasNext()) {
when (data.nextName()) {
when(data.nextName()){
"id" -> this.id = data.nextInt() "id" -> this.id = data.nextInt()
"url" -> this.url = data.nextString() "url" -> this.url = data.nextString()
"name" -> this.nameChannel = data.nextString() "name" -> this.nameChannel = data.nextString()
@ -31,30 +30,32 @@ class ChannelModel (
"followersCount" -> this.followers = data.nextInt() "followersCount" -> this.followers = data.nextInt()
"displayName" -> this.name = data.nextString() "displayName" -> this.name = data.nextString()
"description" -> { "description" -> {
if(data.peek() == JsonToken.STRING) if (data.peek() == JsonToken.STRING) {
this.description = data.nextString() this.description = data.nextString()
else } else {
data.skipValue() data.skipValue()
}
} }
"support" -> { "support" -> {
if(data.peek() == JsonToken.STRING) if (data.peek() == JsonToken.STRING) {
this.support = data.nextString() this.support = data.nextString()
else } else {
data.skipValue() data.skipValue()
}
} }
"avatar" -> { "avatar" -> {
if(data.peek() == JsonToken.BEGIN_OBJECT){ if (data.peek() == JsonToken.BEGIN_OBJECT) {
data.beginObject() data.beginObject()
while (data.hasNext()){ while (data.hasNext()) {
when(data.nextName()){ when (data.nextName()) {
"path" -> this.channelImg = data.nextString() "path" -> this.channelImg = data.nextString()
else -> data.skipValue() else -> data.skipValue()
} }
} }
data.endObject() data.endObject()
} } else {
else
data.skipValue() data.skipValue()
}
} }
else -> data.skipValue() else -> data.skipValue()
} }

View File

@ -2,17 +2,20 @@ package org.libre.agosto.p2play.models
import android.util.JsonReader import android.util.JsonReader
import android.util.JsonToken import android.util.JsonToken
import java.io.Serializable
class CommentaryModel ( class CommentaryModel(
var id: Int = 0, var id: Int = 0,
var threadId: Int = 0, var threadId: Int = 0,
var userUuid: String = "", var userUuid: String = "",
var username: String = "", var username: String = "",
var userImageUrl: String = "", var userImageUrl: String = "",
var commentary: String = "", var commentary: String = "",
var userHost: String = "", var userHost: String = "",
var replies: Int = 0 var replies: Int = 0,
) { var nameChannel: String = "",
var videoId: Int = 0,
) : Serializable {
fun parseCommentary(data: JsonReader) { fun parseCommentary(data: JsonReader) {
data.beginObject() data.beginObject()
while (data.hasNext()) { while (data.hasNext()) {
@ -22,30 +25,31 @@ class CommentaryModel (
"threadId" -> this.threadId = data.nextInt() "threadId" -> this.threadId = data.nextInt()
"text" -> this.commentary = data.nextString() "text" -> this.commentary = data.nextString()
"totalReplies" -> this.replies = data.nextInt() "totalReplies" -> this.replies = data.nextInt()
"videoId" -> this.videoId = data.nextInt()
"account" -> { "account" -> {
data.beginObject() data.beginObject()
while (data.hasNext()){ while (data.hasNext()) {
val acKey = data.nextName() val acKey = data.nextName()
when(acKey.toString()){ when (acKey.toString()) {
"displayName"-> this.username=data.nextString() "displayName" -> this.username = data.nextString()
"avatar"-> { "avatar" -> {
if(data.peek() == JsonToken.BEGIN_OBJECT){ if (data.peek() == JsonToken.BEGIN_OBJECT) {
data.beginObject() data.beginObject()
while (data.hasNext()){ while (data.hasNext()) {
val avKey = data.nextName() val avKey = data.nextName()
when(avKey){ when (avKey) {
"path"-> this.userImageUrl = data.nextString() "path" -> this.userImageUrl = data.nextString()
else-> data.skipValue() else -> data.skipValue()
} }
} }
data.endObject() data.endObject()
} } else {
else
data.skipValue() data.skipValue()
}
} }
"uuid" -> this.userUuid = data.nextString() "uuid" -> this.userUuid = data.nextString()
"host" -> this.userHost = data.nextString() "host" -> this.userHost = data.nextString()
"name" -> this.nameChannel = data.nextString()
else -> data.skipValue() else -> data.skipValue()
} }
} }
@ -56,4 +60,8 @@ class CommentaryModel (
} }
data.endObject() data.endObject()
} }
fun getAccount(): String {
return "$nameChannel@$userHost"
}
} }

View File

@ -1,3 +1,3 @@
package org.libre.agosto.p2play.models package org.libre.agosto.p2play.models
class HostModel (var client_id:String, var client_secret:String) class HostModel(var client_id: String, var client_secret: String)

View File

@ -0,0 +1,26 @@
package org.libre.agosto.p2play.models
import android.util.JsonReader
class StreamingModel(
var playlistUrl: String = "",
var segmentsSha256Url: String = "",
// TODO: Download Files
) {
fun parse(data: JsonReader) {
data.beginObject()
while (data.hasNext()) {
val key = data.nextName()
when (key.toString()) {
"playlistUrl" -> {
this.playlistUrl = data.nextString()
}
"segmentsSha256Url" -> {
this.segmentsSha256Url = data.nextString()
}
else -> data.skipValue()
}
}
data.endObject()
}
}

View File

@ -1,7 +1,7 @@
package org.libre.agosto.p2play.models package org.libre.agosto.p2play.models
class TokenModel ( class TokenModel(
var token: String = "", var token: String = "",
var refresh_token: String = "", var refresh_token: String = "",
var status: Int = -1 var status: Int = -1,
) )

View File

@ -1,12 +1,12 @@
package org.libre.agosto.p2play.models package org.libre.agosto.p2play.models
class UserModel ( class UserModel(
var id: Int = 0, var id: Int = 0,
var uuid: Int = 0, var uuid: Int = 0,
var username: String = "", var username: String = "",
var email: String = "", var email: String = "",
var nsfw: Boolean = true, var nsfw: Boolean = true,
var followers: Int = 0, var followers: Int = 0,
var avatar: String = "", var avatar: String = "",
var status: Int = -1 var status: Int = -1,
) )

View File

@ -5,21 +5,23 @@ import android.util.JsonToken
import java.io.Serializable import java.io.Serializable
class VideoModel( class VideoModel(
var id: Int = 0, var id: Int = 0,
var uuid: String = "", var uuid: String = "",
var name: String = "", var name: String = "",
var description: String = "", var description: String = "",
var thumbUrl: String = "", var thumbUrl: String = "",
var userImageUrl: String = "", var userImageUrl: String = "",
var embedUrl: String = "", var embedUrl: String = "",
var duration: Number = 0, var duration: Number = 0,
var username: String = "", var username: String = "",
var views: Number = 0, var views: Number = 0,
var userUuid: String = "", var userUuid: String = "",
var userHost: String = "", var userHost: String = "",
var nameChannel: String = "" var nameChannel: String = "",
):Serializable { var isLive: Boolean = false,
fun getAccount(): String { var streamingData: StreamingModel? = null,
) : Serializable {
fun getChannel(): String {
return "$nameChannel@$userHost" return "$nameChannel@$userHost"
} }
@ -27,65 +29,100 @@ class VideoModel(
return "https://$userHost/videos/watch/$uuid" return "https://$userHost/videos/watch/$uuid"
} }
fun parseVideo(data: JsonReader){ fun parseVideo(data: JsonReader) {
data.beginObject() data.beginObject()
while (data.hasNext()){ while (data.hasNext()) {
val key = data.nextName() val key = data.nextName()
when (key.toString()) { when (key.toString()) {
"id"-> this.id = data.nextInt() "id" -> this.id = data.nextInt()
"uuid" -> this.uuid = data.nextString() "uuid" -> this.uuid = data.nextString()
"name"->{ "name" -> {
this.name= data.nextString() this.name = data.nextString()
} }
"description"->{ "description" -> {
if(data.peek() == JsonToken.STRING) if (data.peek() == JsonToken.STRING) {
this.description = data.nextString() this.description = data.nextString()
else } else {
data.skipValue() data.skipValue()
}
} }
"duration"->{ "duration" -> {
this.duration = data.nextInt() this.duration = data.nextInt()
} }
"thumbnailPath"->{ "thumbnailPath" -> {
this.thumbUrl = data.nextString() this.thumbUrl = data.nextString()
} }
"embedPath"->{ "embedPath" -> {
this.embedUrl = data.nextString() this.embedUrl = data.nextString()
} }
"views"->{ "views" -> {
this.views = data.nextInt() this.views = data.nextInt()
} }
"channel"->{ "isLive" -> {
this.isLive = data.nextBoolean()
}
"streamingPlaylists" -> {
data.beginArray()
if (data.hasNext()) {
val streamingData = StreamingModel()
streamingData.parse(data)
this.streamingData = streamingData
}
data.endArray()
}
"files" -> {
data.beginArray()
if (streamingData === null) {
if (data.hasNext()) {
data.beginObject()
while (data.hasNext()) {
val key2 = data.nextName()
when (key2.toString()) {
"fileUrl" -> {
streamingData = StreamingModel()
streamingData!!.playlistUrl = data.nextString()
}
else -> data.skipValue()
}
}
data.endObject()
}
while (data.hasNext()) {
data.skipValue()
}
}
data.endArray()
}
"channel" -> {
data.beginObject() data.beginObject()
while (data.hasNext()){ while (data.hasNext()) {
val acKey = data.nextName() val acKey = data.nextName()
when(acKey.toString()){ when (acKey.toString()) {
"displayName"-> this.username=data.nextString() "displayName" -> this.username = data.nextString()
"avatar"-> { "avatar" -> {
if(data.peek() == JsonToken.BEGIN_OBJECT){ if (data.peek() == JsonToken.BEGIN_OBJECT) {
data.beginObject() data.beginObject()
while (data.hasNext()){ while (data.hasNext()) {
val avKey = data.nextName() val avKey = data.nextName()
when(avKey){ when (avKey) {
"path"-> this.userImageUrl = data.nextString() "path" -> this.userImageUrl = data.nextString()
else-> data.skipValue() else -> data.skipValue()
} }
} }
data.endObject() data.endObject()
} } else {
else
data.skipValue() data.skipValue()
}
} }
"uuid" -> this.userUuid = data.nextString() "uuid" -> this.userUuid = data.nextString()
"host" -> this.userHost = data.nextString() "host" -> this.userHost = data.nextString()
"name" -> this.nameChannel = data.nextString() "name" -> this.nameChannel = data.nextString()
else-> data.skipValue() else -> data.skipValue()
} }
} }
data.endObject() data.endObject()
} }
else->{ else -> {
data.skipValue() data.skipValue()
} }
} }

View File

@ -0,0 +1,47 @@
package org.libre.agosto.p2play.services
import android.app.PendingIntent
import android.content.Intent
import androidx.media3.session.MediaSession
import androidx.media3.session.MediaSessionService
import org.libre.agosto.p2play.ReproductorActivity
import org.libre.agosto.p2play.singletons.PlaybackSingleton
class PlaybackService : MediaSessionService() {
private var mediaSession: MediaSession? = null
// Create your Player and MediaSession in the onCreate lifecycle event
override fun onCreate() {
super.onCreate()
val player = PlaybackSingleton.player!!
mediaSession = MediaSession.Builder(this, player)
.build()
val contentIntent = Intent(this, ReproductorActivity::class.java)
contentIntent.putExtra("resume", true)
val pendingIntent = PendingIntent.getActivity(
this,
0,
contentIntent,
PendingIntent.FLAG_MUTABLE,
)
mediaSession!!.setSessionActivity(pendingIntent)
}
// Remember to release the player and media session in onDestroy
override fun onDestroy() {
mediaSession?.run {
release()
mediaSession = null
}
super.onDestroy()
}
override fun onTaskRemoved(rootIntent: Intent?) {
this.mediaSession!!.player.stop()
super.onTaskRemoved(rootIntent)
}
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
return mediaSession
}
}

View File

@ -0,0 +1,51 @@
package org.libre.agosto.p2play.singletons
import android.content.ComponentName
import android.content.Context
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.session.MediaController
import androidx.media3.session.SessionToken
import com.google.common.util.concurrent.MoreExecutors
import org.libre.agosto.p2play.models.VideoModel
import org.libre.agosto.p2play.services.PlaybackService
object PlaybackSingleton {
var player: ExoPlayer? = null
var video: VideoModel? = null
private var withMediaSession = false
fun setData(mediaItem: MediaItem, video: VideoModel): ExoPlayer? {
player?.let {
if (it.isPlaying) {
it.pause()
}
it.setMediaItem(mediaItem)
it.prepare()
this.video = video
return it
}
return null
}
fun release() {
player?.release()
}
fun runMediaSession(context: Context) {
if (!this.withMediaSession) {
val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java))
val controllerFuture = MediaController.Builder(context, sessionToken).buildAsync()
controllerFuture.addListener(
{
val med = controllerFuture.get()
},
MoreExecutors.directExecutor(),
)
this.withMediaSession = true
}
}
}

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>

View File

@ -8,18 +8,18 @@
android:strokeLineJoin="round" android:strokeLineJoin="round"
android:strokeWidth="2" android:strokeWidth="2"
android:fillColor="#00000000" android:fillColor="#00000000"
android:strokeColor="#000000" android:strokeColor="#585858"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path <path
android:pathData="M12,17.75C12.6904,17.75 13.25,17.1904 13.25,16.5C13.25,15.8096 12.6904,15.25 12,15.25C11.3096,15.25 10.75,15.8096 10.75,16.5C10.75,17.1904 11.3096,17.75 12,17.75Z" android:pathData="M12,17.75C12.6904,17.75 13.25,17.1904 13.25,16.5C13.25,15.8096 12.6904,15.25 12,15.25C11.3096,15.25 10.75,15.8096 10.75,16.5C10.75,17.1904 11.3096,17.75 12,17.75Z"
android:strokeWidth="1" android:strokeWidth="1"
android:fillColor="#000000" android:fillColor="#585858"
android:fillType="evenOdd" android:strokeColor="#585858"
android:strokeColor="#00000000"/> android:fillType="evenOdd" />
<path <path
android:pathData="M12,9L12,9A1,1 0,0 1,13 10L13,13A1,1 0,0 1,12 14L12,14A1,1 0,0 1,11 13L11,10A1,1 0,0 1,12 9z" android:pathData="M12,9L12,9A1,1 0,0 1,13 10L13,13A1,1 0,0 1,12 14L12,14A1,1 0,0 1,11 13L11,10A1,1 0,0 1,12 9z"
android:strokeWidth="1" android:strokeWidth="1"
android:fillColor="#000000" android:fillColor="#585858"
android:fillType="evenOdd" android:strokeColor="#585858"
android:strokeColor="#00000000"/> android:fillType="evenOdd" />
</vector> </vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#fff" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M18,13c0,3.31 -2.69,6 -6,6s-6,-2.69 -6,-6s2.69,-6 6,-6v4l5,-5l-5,-5v4c-4.42,0 -8,3.58 -8,8c0,4.42 3.58,8 8,8s8,-3.58 8,-8H18z"/>
<path android:fillColor="@android:color/white" android:pathData="M10.86,15.94l0,-4.27l-0.09,0l-1.77,0.63l0,0.69l1.01,-0.31l0,3.26z"/>
<path android:fillColor="@android:color/white" android:pathData="M12.25,13.44v0.74c0,1.9 1.31,1.82 1.44,1.82c0.14,0 1.44,0.09 1.44,-1.82v-0.74c0,-1.9 -1.31,-1.82 -1.44,-1.82C13.55,11.62 12.25,11.53 12.25,13.44zM14.29,13.32v0.97c0,0.77 -0.21,1.03 -0.59,1.03c-0.38,0 -0.6,-0.26 -0.6,-1.03v-0.97c0,-0.75 0.22,-1.01 0.59,-1.01C14.07,12.3 14.29,12.57 14.29,13.32z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#fff" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M7,14L5,14v5h5v-2L7,17v-3zM5,10h2L7,7h3L10,5L5,5v5zM17,17h-3v2h5v-5h-2v3zM14,5v2h3v3h2L19,5h-5z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#fff" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#fff" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M8,5v14l11,-7z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#fff" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M11.99,5V1l-5,5l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6s-6,-2.69 -6,-6h-2c0,4.42 3.58,8 8,8s8,-3.58 8,-8S16.41,5 11.99,5z"/>
<path android:fillColor="@android:color/white" android:pathData="M10.89,16h-0.85v-3.26l-1.01,0.31v-0.69l1.77,-0.63h0.09V16z"/>
<path android:fillColor="@android:color/white" android:pathData="M15.17,14.24c0,0.32 -0.03,0.6 -0.1,0.82s-0.17,0.42 -0.29,0.57s-0.28,0.26 -0.45,0.33s-0.37,0.1 -0.59,0.1s-0.41,-0.03 -0.59,-0.1s-0.33,-0.18 -0.46,-0.33s-0.23,-0.34 -0.3,-0.57s-0.11,-0.5 -0.11,-0.82V13.5c0,-0.32 0.03,-0.6 0.1,-0.82s0.17,-0.42 0.29,-0.57s0.28,-0.26 0.45,-0.33s0.37,-0.1 0.59,-0.1s0.41,0.03 0.59,0.1c0.18,0.07 0.33,0.18 0.46,0.33s0.23,0.34 0.3,0.57s0.11,0.5 0.11,0.82V14.24zM14.32,13.38c0,-0.19 -0.01,-0.35 -0.04,-0.48s-0.07,-0.23 -0.12,-0.31s-0.11,-0.14 -0.19,-0.17s-0.16,-0.05 -0.25,-0.05s-0.18,0.02 -0.25,0.05s-0.14,0.09 -0.19,0.17s-0.09,0.18 -0.12,0.31s-0.04,0.29 -0.04,0.48v0.97c0,0.19 0.01,0.35 0.04,0.48s0.07,0.24 0.12,0.32s0.11,0.14 0.19,0.17s0.16,0.05 0.25,0.05s0.18,-0.02 0.25,-0.05s0.14,-0.09 0.19,-0.17s0.09,-0.19 0.11,-0.32s0.04,-0.29 0.04,-0.48V13.38z"/>
</vector>

View File

@ -7,7 +7,7 @@
android:pathData="M20,15L20,18.0026C20,19.1057 19.1074,20 18.0049,20L5.9951,20C4.8932,20 4,19.1074 4,18.0049L4,5.9951C4,4.8932 4.8959,4 5.9974,4L9,4" android:pathData="M20,15L20,18.0026C20,19.1057 19.1074,20 18.0049,20L5.9951,20C4.8932,20 4,19.1074 4,18.0049L4,5.9951C4,4.8932 4.8959,4 5.9974,4L9,4"
android:strokeWidth="2" android:strokeWidth="2"
android:fillColor="#00000000" android:fillColor="#00000000"
android:strokeColor="#000000" android:strokeColor="#585858"
android:fillType="evenOdd" android:fillType="evenOdd"
android:strokeLineCap="round"/> android:strokeLineCap="round"/>
<path <path
@ -15,7 +15,7 @@
android:strokeLineJoin="round" android:strokeLineJoin="round"
android:strokeWidth="2" android:strokeWidth="2"
android:fillColor="#00000000" android:fillColor="#00000000"
android:strokeColor="#000000" android:strokeColor="#585858"
android:fillType="evenOdd" android:fillType="evenOdd"
android:strokeLineCap="round"/> android:strokeLineCap="round"/>
<path <path
@ -23,7 +23,7 @@
android:strokeLineJoin="round" android:strokeLineJoin="round"
android:strokeWidth="2" android:strokeWidth="2"
android:fillColor="#00000000" android:fillColor="#00000000"
android:strokeColor="#000000" android:strokeColor="#585858"
android:fillType="evenOdd" android:fillType="evenOdd"
android:strokeLineCap="round"/> android:strokeLineCap="round"/>
</vector> </vector>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="5dp" />
<!-- This is the border color -->
<!--- This is the background color -->
<solid android:color="@color/colorDislike" />
</shape>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="5dp" />
<!-- This is the border color -->
<!--- This is the background color -->
<solid android:color="?attr/colorSecondary" />
</shape>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -29,7 +29,7 @@
android:contentDescription="Logo" android:contentDescription="Logo"
app:srcCompat="@mipmap/ic_launcher" /> app:srcCompat="@mipmap/ic_launcher" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30dp" /> android:layout_height="30dp" />
@ -39,11 +39,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/aboutLabel" android:text="@string/aboutLabel"
android:textAlignment="center" android:textAlignment="center"
android:textAppearance="@android:style/TextAppearance.Material.Medium.Inverse" android:textAppearance="@android:style/TextAppearance.Material.Medium"
android:textColor="@android:color/black"
android:textStyle="bold" /> android:textStyle="bold" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30dp" /> android:layout_height="30dp" />
@ -55,7 +54,7 @@
android:textAlignment="textStart" android:textAlignment="textStart"
android:textSize="16sp" /> android:textSize="16sp" />
<TextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/gitlabUrl" android:id="@+id/gitlabUrl"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -66,7 +65,7 @@
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" /> android:textStyle="bold" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="20dp" /> android:layout_height="20dp" />
@ -88,7 +87,7 @@
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" /> android:textStyle="bold" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30dp" /> android:layout_height="30dp" />
@ -98,11 +97,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/aboutInstance" android:text="@string/aboutInstance"
android:textAlignment="center" android:textAlignment="center"
android:textAppearance="@android:style/TextAppearance.Material.Medium.Inverse" android:textAppearance="@android:style/TextAppearance.Material.Medium"
android:textColor="@android:color/black"
android:textStyle="bold" /> android:textStyle="bold" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="20dp" /> android:layout_height="20dp" />
@ -124,7 +122,7 @@
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" /> android:textStyle="bold" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30dp" /> android:layout_height="30dp" />
@ -135,7 +133,11 @@
android:text="@string/aboutLicense" android:text="@string/aboutLicense"
android:textAlignment="textStart" android:textAlignment="textStart"
android:textSize="16sp" /> android:textSize="16sp" />
<androidx.legacy.widget.Space
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -141,14 +141,13 @@
tools:layout_editor_absoluteY="214dp" /> tools:layout_editor_absoluteY="214dp" />
</FrameLayout> </FrameLayout>
<android.support.v7.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/listVideosChannel" android:id="@+id/listVideosChannel"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content" />
android:scrollbars="none" />
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -29,38 +29,46 @@
android:contentDescription="Logo" android:contentDescription="Logo"
app:srcCompat="@drawable/icon" /> app:srcCompat="@drawable/icon" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30dp" /> android:layout_height="30dp" />
<TextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/hostInfoText" android:id="@+id/hostInfoText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/hostInfoText" android:text="@string/hostInfoText"
android:textAlignment="center" android:textAlignment="center"
android:textAppearance="@android:style/TextAppearance.Material.Medium.Inverse" />
android:textColor="@android:color/black" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30dp" /> android:layout_height="30dp" />
<EditText <com.google.android.material.textfield.TextInputLayout
android:id="@+id/hostText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ems="10" android:layout_margin="4dp"
android:hint="@string/hostText" android:hint="@string/instance"
android:inputType="text" /> app:expandedHintEnabled="false">
<Button <com.google.android.material.textfield.TextInputEditText
android:id="@+id/hostText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/hostText"
android:inputType="text" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/button" android:id="@+id/button"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/okButton" /> android:text="@string/okButton" />
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -29,7 +29,7 @@
android:contentDescription="Logo" android:contentDescription="Logo"
app:srcCompat="@drawable/icon" /> app:srcCompat="@drawable/icon" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30dp" /> android:layout_height="30dp" />
@ -39,62 +39,64 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/loginInfo" android:text="@string/loginInfo"
android:textAlignment="center" android:textAlignment="center"
android:textAppearance="@android:style/TextAppearance.Material.Medium.Inverse" android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium"/>
android:textColor="@android:color/black" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30dp" /> android:layout_height="30dp" />
<TextView <com.google.android.material.textfield.TextInputLayout
android:id="@+id/userTxt" android:id="@+id/userTxt"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/userTxt" /> android:layout_margin="4dp"
android:hint="@string/userTxt">
<EditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/userText" android:id="@+id/userText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ems="10" android:ems="10"
android:hint="@string/userText" android:hint="@string/userText"
android:inputType="text" /> android:inputType="text" />
</com.google.android.material.textfield.TextInputLayout>
<TextView <com.google.android.material.textfield.TextInputLayout
android:id="@+id/passwordTxt" android:id="@+id/passwordTxt"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/passwordTxt" /> android:layout_margin="4dp"
app:endIconMode="password_toggle"
android:hint="@string/passwordTxt" >
<EditText <EditText
android:id="@+id/passwordText" android:id="@+id/passwordText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ems="10" android:ems="10"
android:hint="@string/passwordText" android:inputType="textPassword"/>
android:inputType="text|textPassword" /> </com.google.android.material.textfield.TextInputLayout>
<Button <com.google.android.material.button.MaterialButton
android:id="@+id/loginBtn" android:id="@+id/loginBtn"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/loginBtn" android:text="@string/loginBtn"
android:textAlignment="center" /> android:textAlignment="center" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30dp" /> android:layout_height="30dp" />
<Button <com.google.android.material.button.MaterialButton
android:id="@+id/registerActionBtn" android:id="@+id/registerActionBtn"
style="@style/Widget.AppCompat.Button.Borderless" style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/registerActionBtn" android:text="@string/registerActionBtn"
android:visibility="visible" /> android:visibility="invisible" />
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout" android:id="@+id/drawer_layout"
@ -13,7 +13,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
<android.support.design.widget.NavigationView <com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view" android:id="@+id/nav_view"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -26,4 +26,4 @@
app:headerLayout="@layout/nav_header_main" app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer" /> app:menu="@menu/activity_main_drawer" />
</android.support.v4.widget.DrawerLayout> </androidx.drawerlayout.widget.DrawerLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -10,11 +10,39 @@
android:id="@+id/fullScreen" android:id="@+id/fullScreen"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true" android:visibility="gone"
android:layout_alignParentRight="true" app:layout_constraintBottom_toBottomOf="parent"
android:visibility="gone" /> app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<RelativeLayout
android:id="@+id/fullScreenExo"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" >
<androidx.media3.ui.PlayerView
android:id="@+id/fullscreenPlayer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"
app:use_controller="true"
app:controller_layout_id="@layout/custom_player_controls"
app:player_layout_id="@layout/exo_player_view"/>
</RelativeLayout>
<ScrollView <ScrollView
android:id="@+id/nonFullScreen" android:id="@+id/nonFullScreen"
@ -37,17 +65,24 @@
<WebView <WebView
android:id="@+id/videoView" android:id="@+id/videoView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="270dp" android:layout_height="205dp"
android:layout_weight="1"> android:layout_weight="1"
android:visibility="gone" />
</WebView> <androidx.media3.ui.PlayerView
android:id="@+id/exoPlayer"
android:layout_width="match_parent"
android:layout_height="205dp"
app:use_controller="true"
app:controller_layout_id="@layout/custom_player_controls"
app:player_layout_id="@layout/exo_player_view"/>
<TextView <TextView
android:id="@+id/tittleVideoTxt" android:id="@+id/tittleVideoTxt"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="5dp" android:paddingStart="5dp"
android:textAppearance="@android:style/TextAppearance.Material.Large" android:textAppearance="@android:style/TextAppearance.Material.Display1"
android:textSize="18sp" android:textSize="18sp"
android:textStyle="bold" /> android:textStyle="bold" />
@ -59,7 +94,7 @@
android:paddingRight="5dp" android:paddingRight="5dp"
android:textSize="12sp" /> android:textSize="12sp" />
<LinearLayout <androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/actionsLayout" android:id="@+id/actionsLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="50dp" android:layout_height="50dp"
@ -67,7 +102,7 @@
android:orientation="horizontal" android:orientation="horizontal"
android:visibility="gone"> android:visibility="gone">
<LinearLayout <androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/likeLayout" android:id="@+id/likeLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -94,7 +129,7 @@
android:layout_weight="1" android:layout_weight="1"
android:text="@string/likeBtn" android:text="@string/likeBtn"
android:textAlignment="center" /> android:textAlignment="center" />
</LinearLayout> </androidx.appcompat.widget.LinearLayoutCompat>
<LinearLayout <LinearLayout
android:id="@+id/dislikeLayout" android:id="@+id/dislikeLayout"
@ -166,7 +201,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="1"
android:adjustViewBounds="false" android:adjustViewBounds="false"
android:contentDescription="@string/dislikeBtn" android:contentDescription="@string/shareBtn"
android:cropToPadding="false" android:cropToPadding="false"
android:visibility="visible" android:visibility="visible"
app:srcCompat="@drawable/ic_share" /> app:srcCompat="@drawable/ic_share" />
@ -179,38 +214,39 @@
android:text="@string/shareBtn" android:text="@string/shareBtn"
android:textAlignment="center" /> android:textAlignment="center" />
</LinearLayout> </LinearLayout>
</LinearLayout> </androidx.appcompat.widget.LinearLayoutCompat>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp" android:layout_height="60dp"
android:layout_weight="1" android:layout_weight="1"
android:gravity="center|center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView <ImageView
android:id="@+id/userImg" android:id="@+id/userImg"
android:layout_width="40dp" android:layout_width="35dp"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_margin="0dp" android:layout_margin="0dp"
android:layout_weight="1" android:layout_weight="1"
android:adjustViewBounds="false" android:adjustViewBounds="true"
android:cropToPadding="false" android:cropToPadding="false"
android:padding="5dp" android:padding="5dp"
android:scaleType="centerInside" android:scaleType="fitCenter"
app:srcCompat="@drawable/default_avatar" /> app:srcCompat="@drawable/default_avatar" />
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_weight="2" android:layout_weight="3"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
android:id="@+id/userTxt" android:id="@+id/userTxt"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxWidth="300dp"
android:textAppearance="@android:style/TextAppearance.Material.Large" android:textAppearance="@android:style/TextAppearance.Material.Large"
android:textColor="@android:color/black"
android:textSize="18sp" android:textSize="18sp"
android:textStyle="bold" /> android:textStyle="bold" />
@ -223,14 +259,14 @@
</LinearLayout> </LinearLayout>
<Button <com.google.android.material.button.MaterialButton
android:id="@+id/subscribeBtn" android:id="@+id/subscribeBtn"
style="@style/Widget.AppCompat.Button.Colored" style="@style/Widget.MaterialComponents.Button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/subscribeBtn" android:text="@string/subscribeBtn"
android:textSize="10sp" android:textSize="12sp"
android:visibility="invisible" /> android:visibility="invisible" />
</LinearLayout> </LinearLayout>
@ -257,9 +293,9 @@
android:paddingLeft="10dp" android:paddingLeft="10dp"
android:paddingRight="10dp" /> android:paddingRight="10dp" />
<Button <com.google.android.material.button.MaterialButton
android:id="@+id/showMoreBtn" android:id="@+id/showMoreBtn"
style="@style/Widget.AppCompat.Button.Borderless.Colored" style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
@ -272,7 +308,7 @@
android:layout_height="1dp" android:layout_height="1dp"
android:background="?android:attr/listDivider" /> android:background="?android:attr/listDivider" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="10dp" android:layout_height="10dp"
android:layout_weight="1" /> android:layout_weight="1" />
@ -285,51 +321,13 @@
android:text="@string/commentariesTxt" android:text="@string/commentariesTxt"
android:textStyle="bold" /> android:textStyle="bold" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="20dp" /> android:layout_height="20dp" />
<LinearLayout <include layout="@layout/comment_component" />
android:id="@+id/commentaryLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<LinearLayout <androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/userImgCom"
android:layout_width="10dp"
android:layout_height="60dp"
android:layout_weight="1"
app:srcCompat="@drawable/default_avatar" />
<EditText
android:id="@+id/commentaryText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="2"
android:ems="10"
android:hint="@string/commentHolder"
android:inputType="textMultiLine" />
</LinearLayout>
<Button
android:id="@+id/commentaryBtn"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="@string/commentaryText" />
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/listCommentaries" android:id="@+id/listCommentaries"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -340,4 +338,5 @@
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
</android.support.constraint.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -29,7 +29,7 @@
android:contentDescription="@string/app_name" android:contentDescription="@string/app_name"
app:srcCompat="@mipmap/ic_launcher" /> app:srcCompat="@mipmap/ic_launcher" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30dp" /> android:layout_height="30dp" />
@ -39,10 +39,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/charging" android:text="@string/charging"
android:textAlignment="center" android:textAlignment="center"
android:textAppearance="@android:style/TextAppearance.Material.Medium.Inverse" android:textAppearance="@android:style/TextAppearance.Material.Medium" />
android:textColor="@android:color/black" />
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30dp" /> android:layout_height="30dp" />
@ -55,4 +54,4 @@
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,25 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".MainActivity"> tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:theme="@style/P2playTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"/>
android:background="?attr/colorPrimary"
app:popupTheme="@style/P2playTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_main" /> <include layout="@layout/content_main" />
</android.support.design.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/commentaryLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/userImgCom"
android:layout_width="5dp"
android:layout_height="57dp"
android:layout_weight="1"
app:srcCompat="@drawable/default_avatar" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:layout_weight="3"
android:hint="@string/commentHolder">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/commentaryText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ems="10"
android:inputType="textMultiLine" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/commentaryBtn"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="@string/commentaryText" />
</androidx.appcompat.widget.LinearLayoutCompat>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -8,7 +8,7 @@
tools:context=".MainActivity" tools:context=".MainActivity"
tools:showIn="@layout/app_bar_main"> tools:showIn="@layout/app_bar_main">
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeContainer" android:id="@+id/swipeContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -16,7 +16,7 @@
android:visibility="visible" android:visibility="visible"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<android.support.v7.widget.RecyclerView <view class="androidx.recyclerview.widget.RecyclerView"
android:id="@+id/list" android:id="@+id/list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -24,5 +24,17 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
</android.support.v4.widget.SwipeRefreshLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</android.support.constraint.ConstraintLayout>
<include
android:id="@+id/mini"
layout="@layout/mini_player"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:background="#aa000000"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:id="@+id/exo_rew"
android:src="@drawable/ic_replay_10_24"
/>
<ImageView
android:id="@+id/exo_play_pause"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginHorizontal="30dp"/>
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:id="@+id/exo_ffwd"
android:src="@drawable/ic_forward_10_24"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:layout_marginBottom="0dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="20dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/exo_position"
android:textColor="#fff"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="/"
android:textColor="#CBCDC8"
android:layout_marginHorizontal="4dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/exo_duration"
android:textColor="#CBCDC8"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<androidx.media3.ui.DefaultTimeBar
android:id="@+id/exo_progress"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="2" />
<ImageView
android:id="@+id/exo_fullscreen_custom"
android:layout_width="42dp"
android:layout_height="42dp"
android:src="@drawable/ic_fullscreen_24"
android:layout_weight="0"
android:layout_gravity="center"/>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:background="?attr/colorSurface"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/linearLayout3"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="130dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/materialToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize" />
<include
layout="@layout/view_commentary"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/listCommentaries"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="10dp"/>
</LinearLayout>
<include
layout="@layout/comment_component"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/androidBackgroundSecondary"
android:elevation="5dp"
android:clickable="true"
android:id="@+id/mini_player">
<ImageView
android:id="@+id/mini_player_image"
android:layout_width="wrap_content"
android:layout_height="60dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/default_avatar"
tools:srcCompat="@drawable/default_avatar" />
<TextView
android:id="@+id/mini_player_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:maxWidth="180dp"
android:text="Video"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
app:layout_constraintStart_toEndOf="@+id/mini_player_image"
app:layout_constraintTop_toTopOf="parent"
android:maxLines="1"
android:ellipsize="end"
android:textColor="?attr/androidOnBackgroundSecondary"/>
<TextView
android:id="@+id/mini_player_author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginBottom="8dp"
android:maxWidth="180dp"
android:text="Author"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/mini_player_image"
android:maxLines="1"
android:ellipsize="end" />
<ImageView
android:id="@+id/mini_play_pause"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginEnd="16dp"
android:layout_weight="1"
android:adjustViewBounds="false"
android:contentDescription="@string/likeBtn"
android:cropToPadding="false"
android:scaleType="center"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_pause_24"
app:tint="@color/colorAccent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,26 +1,28 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_root" android:id="@+id/layout_root"
android:layout_width="fill_parent" android:layout_width="match_parent"
android:layout_height="fill_parent" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:padding="10dp" > android:padding="10dp" >
<TextView
android:id="@+id/reportTxt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/reportDialog"
android:textAppearance="?android:attr/textAppearanceLarge" />
<EditText <com.google.android.material.textfield.TextInputLayout
android:id="@+id/reportText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textMultiLine"> style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/reportText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine" >
<requestFocus />
</com.google.android.material.textfield.TextInputEditText>
</com.google.android.material.textfield.TextInputLayout>
<requestFocus />
</EditText>
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,9 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/settings"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp" >
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="0dp"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/twoFactorText"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<requestFocus />
</com.google.android.material.textfield.TextInputEditText>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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">
@ -7,6 +7,7 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingLeft="10dp" android:paddingLeft="10dp"
android:paddingRight="10dp" android:paddingRight="10dp"
@ -19,6 +20,7 @@
android:layout_height="60dp" android:layout_height="60dp"
android:layout_weight="1" android:layout_weight="1"
android:contentDescription="@string/app_name" android:contentDescription="@string/app_name"
android:scaleType="fitCenter"
app:srcCompat="@drawable/default_avatar" /> app:srcCompat="@drawable/default_avatar" />
<LinearLayout <LinearLayout
@ -45,6 +47,14 @@
android:linksClickable="true" android:linksClickable="true"
android:maxLength="1000" android:maxLength="1000"
android:maxLines="10" /> android:maxLines="10" />
<TextView
android:id="@+id/replyBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/reply"
android:textColor="?attr/colorSecondary"
android:textStyle="bold" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -14,27 +14,70 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<ImageView <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/thumb"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="230dp" android:layout_height="wrap_content"
app:srcCompat="@android:drawable/ic_menu_gallery" /> app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/thumb"
android:layout_width="match_parent"
android:layout_height="205dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@android:drawable/ic_menu_gallery" />
<TextView
android:id="@+id/isLive"
android:layout_width="65dp"
android:layout_height="19dp"
android:layout_marginStart="8dp"
android:layout_marginBottom="16dp"
android:background="@drawable/live_shape"
android:ems="10"
android:paddingHorizontal="3dp"
android:text="@string/is_live_video"
android:textAlignment="center"
android:textColor="@color/durationColor"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="invisible" />
<TextView
android:id="@+id/duration"
android:layout_width="65dp"
android:layout_height="19dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
android:background="@drawable/round_text"
android:ems="10"
android:paddingHorizontal="3dp"
android:text="00:00"
android:textAlignment="center"
android:textColor="?attr/colorOnSecondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="63dp" android:layout_height="55dp"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView <ImageView
android:id="@+id/userImg" android:id="@+id/userImg"
android:layout_width="140dp" android:layout_width="120dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="1"
android:adjustViewBounds="false" android:adjustViewBounds="true"
android:clickable="false" android:clickable="false"
android:cropToPadding="false" android:cropToPadding="false"
android:padding="5dp" android:padding="5dp"
android:scaleType="centerInside" android:scaleType="fitCenter"
app:srcCompat="@drawable/default_avatar" /> app:srcCompat="@drawable/default_avatar" />
<LinearLayout <LinearLayout
@ -50,7 +93,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxLength="70" android:maxLength="70"
android:textSize="14sp" android:textSize="14sp"
android:textStyle="bold" /> android:textStyle="bold"
android:theme="@style/MaterialAlertDialog.MaterialComponents.Title.Text" />
<TextView <TextView
android:id="@+id/descriptionTxt" android:id="@+id/descriptionTxt"
@ -64,10 +108,10 @@
</LinearLayout> </LinearLayout>
<Space <androidx.legacy.widget.Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="15dp" android:layout_height="5dp"
android:layout_weight="1" android:layout_weight="1"
app:layout_constraintTop_toBottomOf="@+id/linearLayout" /> app:layout_constraintTop_toBottomOf="@+id/linearLayout" />
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -5,7 +5,8 @@
<item <item
android:id="@+id/app_bar_search" android:id="@+id/app_bar_search"
android:icon="@drawable/ic_search_black_24dp" android:icon="@drawable/ic_search_black_24dp"
app:actionViewClass="android.widget.SearchView" app:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/action_login"
app:showAsAction="always"/> app:showAsAction="always"/>
<item <item

View File

@ -9,7 +9,7 @@
<string name="aboutLabel">Sobre P2Play</string> <string name="aboutLabel">Sobre P2Play</string>
<string name="aboutText">P2Play es una aplicacion no-oficial de PeerTube. Tu puedes ver y cntribuir con el codigo en GitLab:</string> <string name="aboutText">P2Play es una aplicacion no-oficial de PeerTube. Tu puedes ver y cntribuir con el codigo en GitLab:</string>
<string name="aboutStatus">Puedes seguir nuestro blog:</string> <string name="aboutStatus">Puedes seguir nuestro blog:</string>
<string name="aboutLicense">Copyleft GNU GPLv3 License</string> <string name="aboutLicense">GNU GPLv3 License</string>
<string name="aboutInstance">Sobre la instancia</string> <string name="aboutInstance">Sobre la instancia</string>
<string name="aboutInWeb">Para ver los terminos y mas visita la web:</string> <string name="aboutInWeb">Para ver los terminos y mas visita la web:</string>
<!-- End About strings --> <!-- End About strings -->
@ -25,6 +25,7 @@
<string name="passwordTxt">Contraseña:</string> <string name="passwordTxt">Contraseña:</string>
<string name="loginBtn">Ingresar</string> <string name="loginBtn">Ingresar</string>
<string name="registerActionBtn">Crear una cuenta</string> <string name="registerActionBtn">Crear una cuenta</string>
<string name="instance">Instancia</string>
<!-- Toast msg --> <!-- Toast msg -->
<string name="loginSuccess_msg">Te has identificado</string> <string name="loginSuccess_msg">Te has identificado</string>
<string name="loginError_msg">Ha ocurrido un error</string> <string name="loginError_msg">Ha ocurrido un error</string>
@ -47,6 +48,7 @@
<string name="timeMin_text">minutos</string> <string name="timeMin_text">minutos</string>
<string name="timeHrs_text">horas</string> <string name="timeHrs_text">horas</string>
<string name="nav_header_title">Inicia session</string> <string name="nav_header_title">Inicia session</string>
<string name="is_live_video">En vivo</string>
<!-- Toast msg --> <!-- Toast msg -->
<string name="logout_msg">Te has desconectado</string> <string name="logout_msg">Te has desconectado</string>
<!-- End Main strings --> <!-- End Main strings -->

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="comming">In arrivo!</string>
<string name="charging">Caricamento...</string>
<string name="aboutLabel">Informazioni su P2Play</string>
<string name="aboutText">P2Play è un\'applicazione Android non ufficiale per PeerTube. Puoi visualizzare e contribuire al codice su GitLab:</string>
<string name="aboutStatus">Puoi seguire il nostro blog:</string>
<string name="aboutLicense">GNU GPLv3 License</string>
<string name="aboutInstance">Informazioni sull\'istanza</string>
<string name="aboutInWeb">Puoi vedere in termini ed altro sul web:</string>
<string name="hostInfoText">Scegliere l\'istanza</string>
<string name="okButton">Accetta</string>
<string name="errorMsg">Errore, prova di nuovo</string>
<string name="finallyMsg">Istanza salvata</string>
<string name="loginInfo">Accedere o registrare un nuovo account</string>
<string name="userTxt">Utente:</string>
<string name="passwordTxt">Password:</string>
<string name="loginBtn">Accedere ora</string>
<string name="registerActionBtn">Creare un account</string>
<string name="loginSuccess_msg">Accesso effettuato</string>
<string name="loginError_msg">È stato rilevato un errore</string>
<string name="loginFailed_msg">Credenziali invalide</string>
<string name="registerSuccess_msg">Registrazione effettuata</string>
<string name="registerError_msg">È stato rilevato un errore</string>
<string name="registerFailed_msg">Dati non validi</string>
<string name="registerBtn">Registrarsi ora</string>
<string name="emailTxt">Email:</string>
<string name="title_subscriptions">Iscrizioni</string>
<string name="title_recent">Recenti</string>
<string name="title_popular">Popolari</string>
<string name="title_trending">Di tendenza</string>
<string name="title_local">Video locali</string>
<string name="title_myVideos">I miei video</string>
<string name="view_text">visualizzazioni</string>
<string name="timeSec_text">secondi</string>
<string name="timeMin_text">minuti</string>
<string name="timeHrs_text">ore</string>
<string name="nav_header_title">Accedere</string>
<string name="nav_subscriptions">Iscrizioni</string>
<string name="logout_msg">Ti sei disconnesso</string>
<string name="nav_popular">Popolari</string>
<string name="nav_trending">Di tendenza</string>
<string name="nav_recent">Recenti</string>
<string name="nav_local">Locali</string>
<string name="nav_about">Informazioni</string>
<string name="nav_history">Cronologia</string>
<string name="nav_menu_myLibrary">La mia libreria</string>
<string name="nav_menu_videos">Video</string>
<string name="nav_menu_more">Di più</string>
<string name="nav_likes">Più piaciuti</string>
<string name="action_settings">Impostazioni</string>
<string name="action_login">Accedere</string>
<string name="action_logout">Uscire</string>
<string name="descriptionTxt">Descrizione:</string>
<string name="commentariesTxt">Commenti:</string>
<string name="commentHolder">Lascia un commento</string>
<string name="subscribeBtn">Iscriversi</string>
<string name="likeBtn">Mi piace</string>
<string name="dislikeBtn">Non mi piace</string>
<string name="shareBtn">Condividere</string>
<string name="reportBtn">Segnalare</string>
<string name="unSubscribeBtn">Annulla iscrizione</string>
<string name="commentaryText">Commentare</string>
<string name="showMore">Mostra di più</string>
<string name="subscribeMsg">Ti sei iscritto a questo canale</string>
<string name="rateMsg">Hai valutato il video</string>
<string name="unSubscribeMsg">Hai annullato l\'iscrizione a questo canale</string>
<string name="makedCommentaryMsg">Hai commentato questo video</string>
<string name="errorCommentaryMsg">È stato rilevato un errore, riprovare</string>
<string name="emptyCommentaryMsg">Per favore, lascia prima un commento</string>
<string name="title_activity_settings">Impostazioni</string>
<string name="pref_header_general">Generali</string>
<string name="pref_nfsw_title">Contenuti NSFW</string>
<string name="pref_nfsw_description">Se attivo, può mostrare contenuti sensibili o per adulti.</string>
<string name="pref_hostname_title">Istanza Peertube</string>
<string name="pref_message_exit">Riaprire l\'app per rendere effettive le modifiche</string>
<string name="pref_videos_count_title">Video per pagina</string>
<string name="followersIndicator">Seguaci:</string>
<string name="hostIndicator">Host:</string>
<string name="reportDialog">Motivo per la segnalazione di questo video:</string>
<string name="reportDialogMsg">Hai segnalato il video</string>
</resources>

View File

@ -0,0 +1,25 @@
<resources>
<style name="Theme.P2play" parent="Theme.MaterialComponents.DayNight">
<item name="colorPrimary">@color/md_theme_dark_primary</item>
<item name="colorOnPrimary">@color/md_theme_dark_onPrimary</item>
<item name="colorSecondary">@color/md_theme_dark_secondary</item>
<item name="colorOnSecondary">@color/md_theme_dark_onSecondary</item>
<item name="colorError">@color/md_theme_dark_error</item>
<item name="colorOnError">@color/md_theme_dark_onError</item>
<item name="android:colorBackground">@color/md_theme_dark_background</item>
<item name="colorOnBackground">@color/md_theme_dark_onBackground</item>
<item name="colorSurface">@color/md_theme_dark_surface</item>
<item name="colorOnSurface">@color/md_theme_dark_onSurface</item>
<item name="colorPrimaryDark">@color/colorAccent</item>
<item name="colorAccent">@color/md_theme_dark_onBackground</item>
<item name="android:textColorLink">@color/md_theme_dark_secondary</item>
<item name="androidBackgroundSecondary">@color/md_theme_dark_tertiaryContainer</item>
<item name="androidOnBackgroundSecondary">@color/md_theme_dark_onTertiaryContainer</item>
</style>
<style name="Theme.P2play.NoActionBar" parent="Theme.P2play">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
</resources>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="androidBackgroundSecondary" format="reference" />
<attr name="androidOnBackgroundSecondary" format="reference" />
</resources>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="colorPrimary">#3F51B5</color> <color name="colorPrimary">#9F4200</color>
<color name="colorPrimaryDark">#303F9F</color> <color name="colorPrimaryDark">#F4DED5</color>
<color name="colorAccent">#FF4081</color> <color name="colorAccent">#0f3239</color>
<color name="colorHeader">#ffffff</color> <color name="colorHeader">#ffffff</color>
<color name="colorBody">#fff</color> <color name="colorBody">#fff</color>
<color name="colorMenu">#000</color> <color name="colorMenu">#000</color>
@ -10,4 +10,69 @@
<color name="colorLike">#FF3C9100</color> <color name="colorLike">#FF3C9100</color>
<color name="colorDislike">#ec020e</color> <color name="colorDislike">#ec020e</color>
<color name="colorProfile">#262626</color> <color name="colorProfile">#262626</color>
<color name="durationBackground">#88000000</color>
<color name="durationColor">#ffffff</color>
<color name="seed">#f16805</color>
<color name="md_theme_light_primary">#9F4200</color>
<color name="md_theme_light_onPrimary">#FFFFFF</color>
<color name="md_theme_light_primaryContainer">#FFDBCB</color>
<color name="md_theme_light_onPrimaryContainer">#341100</color>
<color name="md_theme_light_secondary">#566500</color>
<color name="md_theme_light_onSecondary">#FFFFFF</color>
<color name="md_theme_light_secondaryContainer">#D9EC7B</color>
<color name="md_theme_light_onSecondaryContainer">#181E00</color>
<color name="md_theme_light_tertiary">#904D00</color>
<color name="md_theme_light_onTertiary">#FFFFFF</color>
<color name="md_theme_light_tertiaryContainer">#FFDCC3</color>
<color name="md_theme_light_onTertiaryContainer">#2F1500</color>
<color name="md_theme_light_error">#BA1A1A</color>
<color name="md_theme_light_errorContainer">#FFDAD6</color>
<color name="md_theme_light_onError">#FFFFFF</color>
<color name="md_theme_light_onErrorContainer">#410002</color>
<color name="md_theme_light_background">#F8FDFF</color>
<color name="md_theme_light_onBackground">#001F25</color>
<color name="md_theme_light_surface">#F8FDFF</color>
<color name="md_theme_light_onSurface">#001F25</color>
<color name="md_theme_light_surfaceVariant">#F4DED5</color>
<color name="md_theme_light_onSurfaceVariant">#52443D</color>
<color name="md_theme_light_outline">#85736C</color>
<color name="md_theme_light_inverseOnSurface">#D6F6FF</color>
<color name="md_theme_light_inverseSurface">#00363F</color>
<color name="md_theme_light_inversePrimary">#FFB692</color>
<color name="md_theme_light_shadow">#000000</color>
<color name="md_theme_light_surfaceTint">#9F4200</color>
<color name="md_theme_light_outlineVariant">#D7C2B9</color>
<color name="md_theme_light_scrim">#000000</color>
<color name="md_theme_dark_primary">#FFB692</color>
<color name="md_theme_dark_onPrimary">#562000</color>
<color name="md_theme_dark_primaryContainer">#793000</color>
<color name="md_theme_dark_onPrimaryContainer">#FFDBCB</color>
<color name="md_theme_dark_secondary">#BDD062</color>
<color name="md_theme_dark_onSecondary">#2C3400</color>
<color name="md_theme_dark_secondaryContainer">#404C00</color>
<color name="md_theme_dark_onSecondaryContainer">#D9EC7B</color>
<color name="md_theme_dark_tertiary">#FFB77C</color>
<color name="md_theme_dark_onTertiary">#4D2600</color>
<color name="md_theme_dark_tertiaryContainer">#6E3900</color>
<color name="md_theme_dark_onTertiaryContainer">#FFDCC3</color>
<color name="md_theme_dark_error">#FFB4AB</color>
<color name="md_theme_dark_errorContainer">#93000A</color>
<color name="md_theme_dark_onError">#690005</color>
<color name="md_theme_dark_onErrorContainer">#FFDAD6</color>
<color name="md_theme_dark_background">#001F25</color>
<color name="md_theme_dark_onBackground">#A6EEFF</color>
<color name="md_theme_dark_surface">#001F25</color>
<color name="md_theme_dark_onSurface">#A6EEFF</color>
<color name="md_theme_dark_surfaceVariant">#52443D</color>
<color name="md_theme_dark_onSurfaceVariant">#D7C2B9</color>
<color name="md_theme_dark_outline">#A08D85</color>
<color name="md_theme_dark_inverseOnSurface">#001F25</color>
<color name="md_theme_dark_inverseSurface">#A6EEFF</color>
<color name="md_theme_dark_inversePrimary">#9F4200</color>
<color name="md_theme_dark_shadow">#000000</color>
<color name="md_theme_dark_surfaceTint">#FFB692</color>
<color name="md_theme_dark_outlineVariant">#52443D</color>
<color name="md_theme_dark_scrim">#000000</color>
</resources> </resources>

View File

@ -9,14 +9,13 @@
<string name="charging">Loading…</string> <string name="charging">Loading…</string>
<!-- End Global string --> <!-- End Global string -->
<!-- Start About strings --> <!-- Start About strings -->
<string name="aboutGitUrl" translatable="false">https://gitlab.com/agosto182/p2play/</string> <string name="aboutGitUrl" translatable="false">https://gitlab.com/agosto182/p2play/</string>
<string name="aboutGnuUrl" translatable="false">https://personaljournal.ca/p2play/</string> <string name="aboutGnuUrl" translatable="false">https://personaljournal.ca/p2play/</string>
<string name="aboutLabel">About P2Play</string> <string name="aboutLabel">About P2Play</string>
<string name="aboutText">P2Play is an Android application unnoficial of PeerTube. You can watch and contribute with the code on GitLab:</string> <string name="aboutText">P2Play is an unofficial Android application for PeerTube. You can watch and contribute with the code on GitLab:</string>
<string name="aboutStatus">You can follow our blog:</string> <string name="aboutStatus">You can follow our blog:</string>
<string name="aboutLicense">Copyleft GNU GPLv3 License</string> <string name="aboutLicense">Licence GNU GPLv3 License</string>
<string name="aboutInstance">About instance</string> <string name="aboutInstance">About instance</string>
<string name="aboutInWeb">You can see terms and more on the web:</string> <string name="aboutInWeb">You can see terms and more on the web:</string>
<!-- End About strings --> <!-- End About strings -->
@ -25,6 +24,7 @@
<string name="okButton">Accept</string> <string name="okButton">Accept</string>
<string name="errorMsg">Error, try again</string> <string name="errorMsg">Error, try again</string>
<string name="finallyMsg">Host saved</string> <string name="finallyMsg">Host saved</string>
<string name="instance">Instance</string>
<!-- End Host strings --> <!-- End Host strings -->
<!-- Start Login strings --> <!-- Start Login strings -->
<string name="loginInfo">Login or register new account</string> <string name="loginInfo">Login or register new account</string>
@ -34,11 +34,12 @@
<string name="passwordText" translatable="false"> </string> <string name="passwordText" translatable="false"> </string>
<string name="loginBtn">Login now</string> <string name="loginBtn">Login now</string>
<string name="registerActionBtn">Create new account</string> <string name="registerActionBtn">Create new account</string>
<string name="twoFactorLabel">Two factor code</string>
<!-- Toast msg --> <!-- Toast msg -->
<string name="loginSuccess_msg">You are logged</string> <string name="loginSuccess_msg">You are now logged in</string>
<string name="loginError_msg">An error has occurred</string> <string name="loginError_msg">An error has occurred</string>
<string name="loginFailed_msg">Invalid credentials</string> <string name="loginFailed_msg">Invalid credentials</string>
<string name="registerSuccess_msg">You are registered</string> <string name="registerSuccess_msg">You are now registered</string>
<string name="registerError_msg">An error has occurred</string> <string name="registerError_msg">An error has occurred</string>
<string name="registerFailed_msg">Invalid data</string> <string name="registerFailed_msg">Invalid data</string>
<!-- Register msg --> <!-- Register msg -->
@ -59,8 +60,9 @@
<string name="timeHrs_text">hours</string> <string name="timeHrs_text">hours</string>
<string name="nav_header_title">Log In</string> <string name="nav_header_title">Log In</string>
<string name="nav_header_subtitle" translatable="false">P2Play</string> <string name="nav_header_subtitle" translatable="false">P2Play</string>
<string name="is_live_video">Live</string>
<!-- Toast msg --> <!-- Toast msg -->
<string name="logout_msg">You are disconnected</string> <string name="logout_msg">You are now disconnected</string>
<!-- End Main strings --> <!-- End Main strings -->
<!-- Start Menu strings --> <!-- Start Menu strings -->
<string name="nav_subscriptions">Subscriptions</string> <string name="nav_subscriptions">Subscriptions</string>
@ -82,8 +84,8 @@
<!-- End MiniMenu strings --> <!-- End MiniMenu strings -->
<!-- Start Reproductor strings --> <!-- Start Reproductor strings -->
<string name="descriptionTxt">Description:</string> <string name="descriptionTxt">Description:</string>
<string name="commentariesTxt">Commentaries:</string> <string name="commentariesTxt">Comments:</string>
<string name="commentHolder">Make a commentary</string> <string name="commentHolder">Make a comment</string>
<!-- Actions --> <!-- Actions -->
<string name="subscribeBtn">Subscribe</string> <string name="subscribeBtn">Subscribe</string>
<string name="likeBtn">Like</string> <string name="likeBtn">Like</string>
@ -93,11 +95,14 @@
<string name="unSubscribeBtn">Unsubscribe</string> <string name="unSubscribeBtn">Unsubscribe</string>
<string name="commentaryText">Comment</string> <string name="commentaryText">Comment</string>
<string name="showMore">Show more</string> <string name="showMore">Show more</string>
<!-- Comments -->
<string name="reply">Reply</string>
<string name="see_replies">See replies (%1$d)</string>
<!-- Messages --> <!-- Messages -->
<string name="subscribeMsg">You are subscribed to this channel</string> <string name="subscribeMsg">You have subscribed to this channel</string>
<string name="rateMsg">You are rated the video</string> <string name="rateMsg">You have rated the video</string>
<string name="unSubscribeMsg">You are unsubscribed to this channel</string> <string name="unSubscribeMsg">You have unsubscribed from this channel</string>
<string name="makedCommentaryMsg">You are commented this video</string> <string name="makedCommentaryMsg">You have commented on this video</string>
<string name="errorCommentaryMsg">An error has occurred, try again</string> <string name="errorCommentaryMsg">An error has occurred, try again</string>
<string name="emptyCommentaryMsg">Please make a comment first</string> <string name="emptyCommentaryMsg">Please make a comment first</string>
<!-- End Reproductor strings --> <!-- End Reproductor strings -->
@ -106,10 +111,10 @@
<!-- Example General settings --> <!-- Example General settings -->
<string name="pref_header_general">General</string> <string name="pref_header_general">General</string>
<string name="pref_nfsw_title">NFSW content</string> <string name="pref_nfsw_title">NFSW content</string>
<string name="pref_nfsw_description">If is active may show the adult and sensitive content.</string> <string name="pref_nfsw_description">When active, it may show adult and sensitive contents.</string>
<string name="pref_hostname_title">Peertube instance</string> <string name="pref_hostname_title">Peertube instance</string>
<string name="pref_hostname_error" translatable="false">-</string> <string name="pref_hostname_error" translatable="false">-</string>
<string name="pref_message_exit">Restart app to apply changes</string> <string name="pref_message_exit">Restart the app to apply changes</string>
<string name="pref_videos_count_title">Videos per page</string> <string name="pref_videos_count_title">Videos per page</string>
<!-- End Settings strings --> <!-- End Settings strings -->
<!-- Start Channel strings --> <!-- Start Channel strings -->
@ -117,7 +122,7 @@
<string name="hostIndicator">Host:</string> <string name="hostIndicator">Host:</string>
<!-- End Channel strings --> <!-- End Channel strings -->
<!-- Start Prompt string --> <!-- Start Prompt string -->
<string name="reportDialog">Reason for report video:</string> <string name="reportDialog">Reason to report this video:</string>
<string name="reportDialogMsg">You are reported the video</string> <string name="reportDialogMsg">You reported the video</string>
<!-- End Prompt strings --> <!-- End Prompt strings -->
</resources> </resources>

View File

@ -0,0 +1,25 @@
<resources>
<style name="Theme.P2play" parent="Theme.MaterialComponents.DayNight">
<item name="colorPrimary">@color/md_theme_light_primary</item>
<item name="colorOnPrimary">@color/md_theme_light_onPrimary</item>
<item name="colorSecondary">@color/md_theme_light_secondary</item>
<item name="colorOnSecondary">@color/md_theme_light_onSecondary</item>
<item name="colorError">@color/md_theme_light_error</item>
<item name="colorOnError">@color/md_theme_light_onError</item>
<item name="android:colorBackground">@color/md_theme_light_background</item>
<item name="colorOnBackground">@color/md_theme_light_onBackground</item>
<item name="colorSurface">@color/md_theme_light_surface</item>
<item name="colorOnSurface">@color/md_theme_light_onSurface</item>
<item name="colorPrimaryDark">@color/md_theme_light_primary</item>
<item name="colorAccent">@color/md_theme_light_secondary</item>
<item name="android:textColorLink">@color/md_theme_light_secondary</item>
<item name="androidBackgroundSecondary">@color/md_theme_light_tertiaryContainer</item>
<item name="androidOnBackgroundSecondary">@color/md_theme_light_onTertiaryContainer</item>
</style>
<style name="Theme.P2play.NoActionBar" parent="Theme.P2play">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
</resources>

View File

@ -5,7 +5,7 @@
<EditTextPreference <EditTextPreference
android:capitalize="words" android:capitalize="words"
android:defaultValue="@string/pref_hostname_error" android:defaultValue="@string/pref_hostname_error"
android:inputType="textCapWords" android:inputType="text"
android:key="hostP2play" android:key="hostP2play"
android:maxLines="1" android:maxLines="1"
android:selectAllOnFocus="true" android:selectAllOnFocus="true"

View File

@ -1,11 +1,14 @@
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> <preference-headers xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- These settings headers are only used on tablets. --> <!-- These settings headers are only used on tablets. -->
<header <header
android:fragment="org.libre.agosto.p2play.SettingsActivity$GeneralPreferenceFragment" android:fragment="org.libre.agosto.p2play.SettingsActivity$GeneralPreferenceFragment"
android:icon="@drawable/ic_info_black_24dp" android:icon="@drawable/ic_info_black_24dp"
android:title="@string/pref_header_general" /> android:title="@string/pref_header_general"
android:textColor="@color/md_theme_light_secondary"/>
<!-- <header <!-- <header
android:fragment="org.libre.agosto.p2play.SettingsActivity$NotificationPreferenceFragment" android:fragment="org.libre.agosto.p2play.SettingsActivity$NotificationPreferenceFragment"

View File

@ -0,0 +1,31 @@
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory app:title="@string/pref_header_general">
<EditTextPreference
app:defaultValue="@string/pref_hostname_error"
android:inputType="text"
app:key="hostP2play"
android:maxLines="1"
android:selectAllOnFocus="true"
android:singleLine="true"
app:useSimpleSummaryProvider="true"
app:title="@string/pref_hostname_title" />
<SwitchPreference
app:defaultValue="false"
app:key="show_nsfw"
app:summary="@string/pref_nfsw_description"
app:title="@string/pref_nfsw_title" />
<EditTextPreference
app:defaultValue="15"
android:inputType="number"
app:key="videos_count"
android:selectAllOnFocus="true"
app:singleLine="true"
app:useSimpleSummaryProvider="true"
app:title="@string/pref_videos_count_title" />
</PreferenceCategory>
</PreferenceScreen>

View File

@ -1,9 +1,8 @@
package org.libre.agosto.p2play package org.libre.agosto.p2play
import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
import org.junit.Assert.*
/** /**
* Example local unit test, which will execute on the development machine (host). * Example local unit test, which will execute on the development machine (host).
* *

View File

@ -1,15 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.3.50' ext.kotlin_version = '1.6.21'
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.4.2' classpath 'com.android.tools.build:gradle:8.3.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
} }
@ -18,7 +17,7 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@ -1,20 +0,0 @@
P2Play es una aplicacion android para Peertube.
¿Que es Peertube? https://github.com/Chocobozzz/PeerTube/
Caracteristicas
* Show recent, popular and local list of videos.
* Reproduce videos
* Login and register in your instance
* Show uploaded videos
* Subscribe to accounts
* Show your subscription videos
* Show your history
* Rate videos
* Show and make commentaries
* Splash screen
* Search videos
* Infinite scroll
* Share videos
* Report videos
* Peertube profiles

View File

@ -7,6 +7,13 @@
# Specifies the JVM arguments used for the daemon process. # Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings. # The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m org.gradle.jvmargs=-Xmx1536m
android.enableJetifier=true
android.useAndroidX=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false
org.gradle.configuration-cache=true
# When configured, Gradle will run in incubating parallel mode. # When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit # This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects

Binary file not shown.

View File

@ -1,6 +1,6 @@
#Thu Oct 17 20:10:56 CDT 2019 #Mon Mar 18 13:17:32 CST 2024
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

286
gradlew vendored
View File

@ -1,78 +1,129 @@
#!/usr/bin/env sh #!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# 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
#
# https://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.
#
############################################################################## ##############################################################################
## #
## Gradle start up script for UN*X # Gradle start up script for POSIX generated by Gradle.
## #
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Attempt to set APP_HOME # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
PRG="$0" app_path=$0
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do # Need this for daisy-chained symlinks.
ls=`ls -ld "$PRG"` while
link=`expr "$ls" : '.*-> \(.*\)$'` APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
if expr "$link" : '/.*' > /dev/null; then [ -h "$app_path" ]
PRG="$link" do
else ls=$( ls -ld "$app_path" )
PRG=`dirname "$PRG"`"/$link" link=${ls#*' -> '}
fi case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS="" DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD=maximum
warn () { warn () {
echo "$*" echo "$*"
} } >&2
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "`uname`" in case "$( uname )" in #(
CYGWIN* ) CYGWIN* ) cygwin=true ;; #(
cygwin=true Darwin* ) darwin=true ;; #(
;; MSYS* | MINGW* ) msys=true ;; #(
Darwin* ) NONSTOP* ) nonstop=true ;;
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -81,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD="java" JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
@ -89,84 +140,95 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD=$( ulimit -H -n ) ||
MAX_FD="$MAX_FD_LIMIT" warn "Could not query maximum file descriptor limit"
fi esac
ulimit -n $MAX_FD case $MAX_FD in #(
if [ $? -ne 0 ] ; then '' | soft) :;; #(
warn "Could not set maximum file descriptor limit: $MAX_FD" *)
fi ulimit -n "$MAX_FD" ||
else warn "Could not set maximum file descriptor limit to $MAX_FD"
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
# Escape application args # Collect all arguments for the java command, stacking in reverse order:
save () { # * args from the command line
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done # * the main class name
echo " " # * -classpath
} # * -D...appname settings
APP_ARGS=$(save "$@") # * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules # For Cygwin or MSYS, switch paths to Windows format before running java
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong JAVACMD=$( cygpath --unix "$JAVACMD" )
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")" # Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

43
gradlew.bat vendored
View File

@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS= set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init if "%ERRORLEVEL%" == "0" goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -35,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init if exist "%JAVA_EXE%" goto execute
echo. echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@ -45,28 +64,14 @@ echo location of your Java installation.
goto fail goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell

View File

@ -0,0 +1,6 @@
- 2FA login support!
- Background player!
- See and reply video threads!
- New Setting menu added
- Code improved
- Bugs fixed

View File

@ -0,0 +1,6 @@
* Update dependecies
* Compile SDK updated to version 30
* Min SDK updated to version 26
* Target SDK updated to version 28
* Back to road baby
* Comming soon!

View File

@ -0,0 +1,5 @@
* Updated to androidx libraries
* Day/Night theme
* Exoplayer added
* Material2 implemented
* Obviusly bug fixes

View File

@ -1,20 +1,16 @@
P2Play is an Android Application for Peertube. P2Play is an Unofficial Android client for Peertube.
What is Peertube? https://github.com/Chocobozzz/PeerTube/
Features Features
* Show recent, popular and local list of videos. * Show recent, popular and local list of videos.
* Reproduce videos * Reproduce videos
* Login and register in your instance * Login in your instance
* Show uploaded videos * Show uploaded videos
* Subscribe to accounts * Subscribe to accounts
* Show your subscription videos * Show your subscription videos
* Show your history * Show your history
* Rate videos * Rate videos
* Show and make commentaries * Show and make commentaries
* Splash screen
* Search videos * Search videos
* Infinite scroll
* Share videos * Share videos
* Report videos * Report videos
* Peertube profiles * Peertube channels

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 889 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 KiB

Some files were not shown because too many files have changed in this diff Show More