Merge branch 'develop' into 'master'
Release See merge request sschueller/peertube!58
This commit is contained in:
commit
eb3b1eb7ad
@ -1,78 +1,83 @@
|
|||||||
<?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"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="net.schueller.peertube">
|
package="net.schueller.peertube">
|
||||||
<!-- required to play video in background via notification -->
|
<!-- required to play video in background via notification -->
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- connect to peertube server -->
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <!-- connect to peertube server -->
|
||||||
<uses-permission android:name="android.permission.INTERNET" /> <!-- required for torrent downloading -->
|
<uses-permission android:name="android.permission.INTERNET"/> <!-- required for torrent downloading -->
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".application.AppApplication"
|
android:name=".application.AppApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:fullBackupContent="@xml/backup_descriptor"
|
android:fullBackupContent="@xml/backup_descriptor"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
tools:ignore="GoogleAppIndexingWarning">
|
tools:ignore="GoogleAppIndexingWarning">
|
||||||
|
|
||||||
<!-- Server Address Book -->
|
<!-- Server Address Book -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.ServerAddressBookActivity"
|
android:name=".activity.ServerAddressBookActivity"
|
||||||
android:label="@string/title_activity_server_address_book"
|
android:label="@string/title_activity_server_address_book"
|
||||||
android:theme="@style/AppTheme.NoActionBar" /> <!-- Video Lists -->
|
android:theme="@style/AppTheme.NoActionBar"/> <!-- Video Lists -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.VideoListActivity"
|
android:name=".activity.VideoListActivity"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:theme="@style/AppTheme.NoActionBar"
|
android:theme="@style/AppTheme.NoActionBar"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<action android:name="android.intent.action.SEARCH" />
|
<action android:name="android.intent.action.SEARCH"/>
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.app.searchable"
|
android:name="android.app.searchable"
|
||||||
android:resource="@xml/searchable" />
|
android:resource="@xml/searchable"/>
|
||||||
</activity> <!-- Video Player -->
|
</activity> <!-- Video Player -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.VideoPlayActivity"
|
android:name=".activity.VideoPlayActivity"
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
|
||||||
android:label="@string/title_activity_video_play"
|
android:label="@string/title_activity_video_play"
|
||||||
android:launchMode="singleInstance"
|
android:launchMode="singleInstance"
|
||||||
android:supportsPictureInPicture="true"
|
android:supportsPictureInPicture="true"
|
||||||
android:theme="@style/AppTheme.NoActionBar" /> <!-- Settings -->
|
android:theme="@style/AppTheme.NoActionBar"/>
|
||||||
|
<!-- Playlist -->
|
||||||
|
<activity android:name=".activity.PlaylistActivity"
|
||||||
|
android:label="Playlist"
|
||||||
|
android:theme="@style/AppTheme.NoActionBar"/>
|
||||||
|
<!-- Settings -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.SettingsActivity"
|
android:name=".activity.SettingsActivity"
|
||||||
android:label="@string/title_activity_settings"
|
android:label="@string/title_activity_settings"
|
||||||
android:theme="@style/AppTheme.NoActionBar" /> <!-- Server Selection -->
|
android:theme="@style/AppTheme.NoActionBar"/> <!-- Server Selection -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.SearchServerActivity"
|
android:name=".activity.SearchServerActivity"
|
||||||
android:label="@string/title_activity_select_server"
|
android:label="@string/title_activity_select_server"
|
||||||
android:theme="@style/AppTheme.NoActionBar" /> <!-- Me -->
|
android:theme="@style/AppTheme.NoActionBar"/> <!-- Me -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.MeActivity"
|
android:name=".activity.MeActivity"
|
||||||
android:label="@string/title_activity_me"
|
android:label="@string/title_activity_me"
|
||||||
android:theme="@style/AppTheme.NoActionBar" /> <!-- Account -->
|
android:theme="@style/AppTheme.NoActionBar"/> <!-- Account -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.AccountActivity"
|
android:name=".activity.AccountActivity"
|
||||||
android:label="@string/title_activity_account"
|
android:label="@string/title_activity_account"
|
||||||
android:theme="@style/AppTheme.NoActionBar" /> <!-- Content provider for search suggestions -->
|
android:theme="@style/AppTheme.NoActionBar"/> <!-- Content provider for search suggestions -->
|
||||||
<provider
|
<provider
|
||||||
android:name=".provider.SearchSuggestionsProvider"
|
android:name=".provider.SearchSuggestionsProvider"
|
||||||
android:authorities="net.schueller.peertube.provider.SearchSuggestionsProvider"
|
android:authorities="net.schueller.peertube.provider.SearchSuggestionsProvider"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false" />
|
android:exported="false"/>
|
||||||
|
|
||||||
<service android:name=".service.VideoPlayerService" />
|
<service android:name=".service.VideoPlayerService"/>
|
||||||
|
|
||||||
<receiver android:name="androidx.media.session.MediaButtonReceiver"
|
<receiver android:name="androidx.media.session.MediaButtonReceiver"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
<action android:name="android.intent.action.MEDIA_BUTTON"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
@ -27,7 +27,9 @@ import android.view.View;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import com.squareup.picasso.Picasso;
|
||||||
import net.schueller.peertube.R;
|
import net.schueller.peertube.R;
|
||||||
import net.schueller.peertube.helper.APIUrlHelper;
|
import net.schueller.peertube.helper.APIUrlHelper;
|
||||||
import net.schueller.peertube.helper.ErrorHelper;
|
import net.schueller.peertube.helper.ErrorHelper;
|
||||||
@ -36,18 +38,12 @@ import net.schueller.peertube.model.Me;
|
|||||||
import net.schueller.peertube.network.GetUserService;
|
import net.schueller.peertube.network.GetUserService;
|
||||||
import net.schueller.peertube.network.RetrofitInstance;
|
import net.schueller.peertube.network.RetrofitInstance;
|
||||||
import net.schueller.peertube.network.Session;
|
import net.schueller.peertube.network.Session;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
|
|
||||||
import com.squareup.picasso.Picasso;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import static net.schueller.peertube.application.AppApplication.getContext;
|
import static net.schueller.peertube.application.AppApplication.getContext;
|
||||||
|
|
||||||
public class MeActivity extends CommonActivity {
|
public class MeActivity extends CommonActivity {
|
||||||
@ -85,11 +81,16 @@ public class MeActivity extends CommonActivity {
|
|||||||
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_baseline_close_24);
|
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_baseline_close_24);
|
||||||
|
|
||||||
LinearLayout account = findViewById(R.id.a_me_account_line);
|
LinearLayout account = findViewById(R.id.a_me_account_line);
|
||||||
|
LinearLayout playlist = findViewById(R.id.a_me_playlist);
|
||||||
LinearLayout settings = findViewById(R.id.a_me_settings);
|
LinearLayout settings = findViewById(R.id.a_me_settings);
|
||||||
LinearLayout help = findViewById(R.id.a_me_helpnfeedback);
|
LinearLayout help = findViewById(R.id.a_me_helpnfeedback);
|
||||||
|
|
||||||
TextView logout = findViewById(R.id.a_me_logout);
|
TextView logout = findViewById(R.id.a_me_logout);
|
||||||
|
|
||||||
|
playlist.setOnClickListener(view -> {
|
||||||
|
Intent playlistActivity = new Intent(getContext(), PlaylistActivity.class);
|
||||||
|
startActivity(playlistActivity);
|
||||||
|
});
|
||||||
|
|
||||||
settings.setOnClickListener(view -> {
|
settings.setOnClickListener(view -> {
|
||||||
Intent settingsActivity = new Intent(getContext(), SettingsActivity.class);
|
Intent settingsActivity = new Intent(getContext(), SettingsActivity.class);
|
||||||
@ -124,7 +125,7 @@ public class MeActivity extends CommonActivity {
|
|||||||
|
|
||||||
call.enqueue(new Callback<Me>() {
|
call.enqueue(new Callback<Me>() {
|
||||||
|
|
||||||
LinearLayout account = findViewById(R.id.a_me_account_line);
|
final LinearLayout account = findViewById(R.id.a_me_account_line);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<Me> call, @NonNull Response<Me> response) {
|
public void onResponse(@NonNull Call<Me> call, @NonNull Response<Me> response) {
|
||||||
@ -162,7 +163,7 @@ public class MeActivity extends CommonActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NonNull Call<Me> call, @NonNull Throwable t) {
|
public void onFailure(@NonNull Call<Me> call, @NonNull Throwable t) {
|
||||||
ErrorHelper.showToastFromCommunicationError( MeActivity.this, t );
|
ErrorHelper.showToastFromCommunicationError(MeActivity.this, t);
|
||||||
account.setVisibility(View.GONE);
|
account.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
|
||||||
|
*
|
||||||
|
* 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 distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.schueller.peertube.activity
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import net.schueller.peertube.R
|
||||||
|
import net.schueller.peertube.adapter.MultiViewRecyclerViewHolder
|
||||||
|
import net.schueller.peertube.adapter.PlaylistAdapter
|
||||||
|
import net.schueller.peertube.database.Video
|
||||||
|
import net.schueller.peertube.database.VideoViewModel
|
||||||
|
import net.schueller.peertube.databinding.ActivityPlaylistBinding
|
||||||
|
|
||||||
|
class PlaylistActivity : CommonActivity() {
|
||||||
|
|
||||||
|
private val TAG = "PlaylistAct"
|
||||||
|
|
||||||
|
private val mVideoViewModel: VideoViewModel by viewModels()
|
||||||
|
|
||||||
|
private lateinit var mBinding: ActivityPlaylistBinding
|
||||||
|
|
||||||
|
override fun onSupportNavigateUp(): Boolean {
|
||||||
|
finish() // close this activity as oppose to navigating up
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
mBinding = ActivityPlaylistBinding.inflate(layoutInflater)
|
||||||
|
setContentView(mBinding.root)
|
||||||
|
|
||||||
|
// Setting toolbar as the ActionBar with setSupportActionBar() call
|
||||||
|
setSupportActionBar(mBinding.toolBarServerAddressBook)
|
||||||
|
supportActionBar?.apply {
|
||||||
|
setDisplayHomeAsUpEnabled(true)
|
||||||
|
setHomeAsUpIndicator(R.drawable.ic_baseline_close_24)
|
||||||
|
}
|
||||||
|
|
||||||
|
showServers()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onVideoClick(video: Video) {
|
||||||
|
val intent = Intent(this, VideoPlayActivity::class.java)
|
||||||
|
intent.putExtra(MultiViewRecyclerViewHolder.EXTRA_VIDEOID, video.videoUUID)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showServers() {
|
||||||
|
val adapter = PlaylistAdapter(mutableListOf(), { onVideoClick(it) }).also {
|
||||||
|
mBinding.serverListRecyclerview.adapter = it
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete items on swipe
|
||||||
|
val helper = ItemTouchHelper(
|
||||||
|
object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
|
||||||
|
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||||
|
AlertDialog.Builder(this@PlaylistActivity)
|
||||||
|
.setTitle(getString(R.string.remove_video))
|
||||||
|
.setMessage(getString(R.string.remove_video_warning_message))
|
||||||
|
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
|
||||||
|
val position = viewHolder.bindingAdapterPosition
|
||||||
|
val video = adapter.getVideoAtPosition(position)
|
||||||
|
// Delete the video
|
||||||
|
mVideoViewModel.delete(video)
|
||||||
|
}
|
||||||
|
.setNegativeButton(android.R.string.cancel) { _: DialogInterface?, _: Int -> adapter.notifyItemChanged(viewHolder.bindingAdapterPosition) }
|
||||||
|
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
helper.attachToRecyclerView(mBinding.serverListRecyclerview)
|
||||||
|
|
||||||
|
// Update the cached copy of the words in the adapter.
|
||||||
|
mVideoViewModel.allVideos.observe(this, { videos: List<Video> ->
|
||||||
|
adapter.setVideos(videos)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object
|
||||||
|
}
|
@ -30,28 +30,26 @@ import androidx.core.content.ContextCompat
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
|
import com.mikepenz.iconics.Iconics.Builder
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import net.schueller.peertube.R.color
|
import net.schueller.peertube.R
|
||||||
import net.schueller.peertube.R.string
|
import net.schueller.peertube.R.*
|
||||||
import net.schueller.peertube.activity.AccountActivity
|
import net.schueller.peertube.activity.AccountActivity
|
||||||
import net.schueller.peertube.activity.VideoListActivity
|
import net.schueller.peertube.activity.VideoListActivity
|
||||||
import net.schueller.peertube.activity.VideoPlayActivity
|
import net.schueller.peertube.activity.VideoPlayActivity
|
||||||
|
import net.schueller.peertube.databinding.*
|
||||||
|
import net.schueller.peertube.fragment.VideoMetaDataFragment
|
||||||
import net.schueller.peertube.helper.APIUrlHelper
|
import net.schueller.peertube.helper.APIUrlHelper
|
||||||
|
import net.schueller.peertube.helper.MetaDataHelper.getCreatorAvatar
|
||||||
|
import net.schueller.peertube.helper.MetaDataHelper.getCreatorString
|
||||||
import net.schueller.peertube.helper.MetaDataHelper.getDuration
|
import net.schueller.peertube.helper.MetaDataHelper.getDuration
|
||||||
import net.schueller.peertube.helper.MetaDataHelper.getMetaString
|
import net.schueller.peertube.helper.MetaDataHelper.getMetaString
|
||||||
import net.schueller.peertube.helper.MetaDataHelper.getOwnerString
|
import net.schueller.peertube.helper.MetaDataHelper.getOwnerString
|
||||||
import com.mikepenz.iconics.Iconics.Builder
|
import net.schueller.peertube.helper.MetaDataHelper.isChannel
|
||||||
import net.schueller.peertube.R
|
|
||||||
import net.schueller.peertube.R.id
|
|
||||||
import net.schueller.peertube.R.menu
|
|
||||||
import net.schueller.peertube.databinding.*
|
|
||||||
import net.schueller.peertube.fragment.VideoMetaDataFragment
|
|
||||||
import net.schueller.peertube.helper.MetaDataHelper.getCreatorAvatar
|
|
||||||
import net.schueller.peertube.helper.MetaDataHelper.getCreatorString
|
|
||||||
import net.schueller.peertube.helper.MetaDataHelper.getTagsString
|
|
||||||
import net.schueller.peertube.intents.Intents
|
import net.schueller.peertube.intents.Intents
|
||||||
import net.schueller.peertube.model.*
|
import net.schueller.peertube.model.*
|
||||||
import net.schueller.peertube.model.ui.VideoMetaViewItem
|
import net.schueller.peertube.model.ui.VideoMetaViewItem
|
||||||
|
import net.schueller.peertube.network.GetUserService
|
||||||
import net.schueller.peertube.network.GetVideoDataService
|
import net.schueller.peertube.network.GetVideoDataService
|
||||||
import net.schueller.peertube.network.RetrofitInstance
|
import net.schueller.peertube.network.RetrofitInstance
|
||||||
import net.schueller.peertube.network.Session
|
import net.schueller.peertube.network.Session
|
||||||
@ -61,8 +59,6 @@ import okhttp3.ResponseBody
|
|||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import net.schueller.peertube.helper.MetaDataHelper.isChannel
|
|
||||||
import net.schueller.peertube.network.GetUserService
|
|
||||||
|
|
||||||
|
|
||||||
sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) {
|
sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||||
@ -70,13 +66,13 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
var videoRating: Rating? = null
|
var videoRating: Rating? = null
|
||||||
var isLeaveAppExpected = false
|
var isLeaveAppExpected = false
|
||||||
|
|
||||||
class CategoryViewHolder(private val binding: ItemCategoryTitleBinding): MultiViewRecyclerViewHolder(binding) {
|
class CategoryViewHolder(private val binding: ItemCategoryTitleBinding) : MultiViewRecyclerViewHolder(binding) {
|
||||||
fun bind(category: Category) {
|
fun bind(category: Category) {
|
||||||
binding.textViewTitle.text = category.label
|
binding.textViewTitle.text = category.label
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class VideoCommentsViewHolder(private val binding: ItemVideoCommentsOverviewBinding): MultiViewRecyclerViewHolder(binding) {
|
class VideoCommentsViewHolder(private val binding: ItemVideoCommentsOverviewBinding) : MultiViewRecyclerViewHolder(binding) {
|
||||||
fun bind(commentThread: CommentThread) {
|
fun bind(commentThread: CommentThread) {
|
||||||
|
|
||||||
binding.videoCommentsTotalCount.text = commentThread.total.toString()
|
binding.videoCommentsTotalCount.text = commentThread.total.toString()
|
||||||
@ -90,8 +86,8 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
val baseUrl = APIUrlHelper.getUrl(binding.videoHighlightedAvatar.context)
|
val baseUrl = APIUrlHelper.getUrl(binding.videoHighlightedAvatar.context)
|
||||||
val avatarPath = avatar.path
|
val avatarPath = avatar.path
|
||||||
Picasso.get()
|
Picasso.get()
|
||||||
.load(baseUrl + avatarPath)
|
.load(baseUrl + avatarPath)
|
||||||
.into(binding.videoHighlightedAvatar)
|
.into(binding.videoHighlightedAvatar)
|
||||||
}
|
}
|
||||||
binding.videoHighlightedComment.text = highlightedComment.text
|
binding.videoHighlightedComment.text = highlightedComment.text
|
||||||
}
|
}
|
||||||
@ -99,7 +95,7 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class VideoMetaViewHolder(private val binding: ItemVideoMetaBinding, private val videoMetaDataFragment: VideoMetaDataFragment?): MultiViewRecyclerViewHolder(binding) {
|
class VideoMetaViewHolder(private val binding: ItemVideoMetaBinding, private val videoMetaDataFragment: VideoMetaDataFragment?) : MultiViewRecyclerViewHolder(binding) {
|
||||||
fun bind(videoMetaViewItem: VideoMetaViewItem) {
|
fun bind(videoMetaViewItem: VideoMetaViewItem) {
|
||||||
|
|
||||||
val video = videoMetaViewItem.video
|
val video = videoMetaViewItem.video
|
||||||
@ -109,16 +105,16 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
val context = binding.avatar.context
|
val context = binding.avatar.context
|
||||||
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
|
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
|
||||||
val videoDataService = RetrofitInstance.getRetrofitInstance(
|
val videoDataService = RetrofitInstance.getRetrofitInstance(
|
||||||
apiBaseURL,
|
apiBaseURL,
|
||||||
APIUrlHelper.useInsecureConnection(context)
|
APIUrlHelper.useInsecureConnection(context)
|
||||||
).create(
|
).create(
|
||||||
GetVideoDataService::class.java
|
GetVideoDataService::class.java
|
||||||
)
|
)
|
||||||
val userService = RetrofitInstance.getRetrofitInstance(
|
val userService = RetrofitInstance.getRetrofitInstance(
|
||||||
apiBaseURL,
|
apiBaseURL,
|
||||||
APIUrlHelper.useInsecureConnection(context)
|
APIUrlHelper.useInsecureConnection(context)
|
||||||
).create(
|
).create(
|
||||||
GetUserService::class.java
|
GetUserService::class.java
|
||||||
)
|
)
|
||||||
|
|
||||||
// Title
|
// Title
|
||||||
@ -137,27 +133,25 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
rateVideo(false, video, context, binding)
|
rateVideo(false, video, context, binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add to playlist
|
||||||
binding.videoAddToPlaylistWrapper.setOnClickListener {
|
binding.videoAddToPlaylistWrapper.setOnClickListener {
|
||||||
Toast.makeText(
|
videoMetaDataFragment.saveToPlaylist(video)
|
||||||
context,
|
Toast.makeText(context, context.getString(string.saved_to_playlist), Toast.LENGTH_SHORT).show()
|
||||||
context.getString(string.video_feature_not_yet_implemented),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.videoBlockWrapper.setOnClickListener {
|
binding.videoBlockWrapper.setOnClickListener {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
context,
|
context,
|
||||||
context.getString(string.video_feature_not_yet_implemented),
|
context.getString(string.video_feature_not_yet_implemented),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.videoFlagWrapper.setOnClickListener {
|
binding.videoFlagWrapper.setOnClickListener {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
context,
|
context,
|
||||||
context.getString(string.video_feature_not_yet_implemented),
|
context.getString(string.video_feature_not_yet_implemented),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,10 +192,10 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
|
|
||||||
// created at / views
|
// created at / views
|
||||||
binding.videoMeta.text = getMetaString(
|
binding.videoMeta.text = getMetaString(
|
||||||
video.createdAt,
|
video.createdAt,
|
||||||
video.views,
|
video.views,
|
||||||
context,
|
context,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
||||||
// owner / creator
|
// owner / creator
|
||||||
@ -220,8 +214,8 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
val baseUrl = APIUrlHelper.getUrl(context)
|
val baseUrl = APIUrlHelper.getUrl(context)
|
||||||
val avatarPath = avatar.path
|
val avatarPath = avatar.path
|
||||||
Picasso.get()
|
Picasso.get()
|
||||||
.load(baseUrl + avatarPath)
|
.load(baseUrl + avatarPath)
|
||||||
.into(binding.avatar)
|
.into(binding.avatar)
|
||||||
}
|
}
|
||||||
|
|
||||||
// videoOwnerSubscribers
|
// videoOwnerSubscribers
|
||||||
@ -243,7 +237,6 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// get subscription status
|
// get subscription status
|
||||||
var isSubscribed = false
|
var isSubscribed = false
|
||||||
|
|
||||||
@ -262,6 +255,7 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
|
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
@ -278,52 +272,54 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
val call = userService.subscribe(body)
|
val call = userService.subscribe(body)
|
||||||
call.enqueue(object : Callback<ResponseBody?> {
|
call.enqueue(object : Callback<ResponseBody?> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<ResponseBody?>,
|
call: Call<ResponseBody?>,
|
||||||
response: Response<ResponseBody?>
|
response: Response<ResponseBody?>
|
||||||
) {
|
) {
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
binding.videoOwnerSubscribeButton.setText(string.unsubscribe)
|
binding.videoOwnerSubscribeButton.setText(string.unsubscribe)
|
||||||
isSubscribed = true
|
isSubscribed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
|
override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
AlertDialog.Builder(context)
|
AlertDialog.Builder(context)
|
||||||
.setTitle(context.getString(string.video_sub_del_alert_title))
|
.setTitle(context.getString(string.video_sub_del_alert_title))
|
||||||
.setMessage(context.getString(string.video_sub_del_alert_msg))
|
.setMessage(context.getString(string.video_sub_del_alert_msg))
|
||||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
|
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
|
||||||
// Yes
|
// Yes
|
||||||
val payload = video.channel.name + "@" + video.channel.host
|
val payload = video.channel.name + "@" + video.channel.host
|
||||||
val call = userService.unsubscribe(payload)
|
val call = userService.unsubscribe(payload)
|
||||||
call.enqueue(object : Callback<ResponseBody?> {
|
call.enqueue(object : Callback<ResponseBody?> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<ResponseBody?>,
|
call: Call<ResponseBody?>,
|
||||||
response: Response<ResponseBody?>
|
response: Response<ResponseBody?>
|
||||||
) {
|
) {
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
binding.videoOwnerSubscribeButton.setText(string.subscribe)
|
binding.videoOwnerSubscribeButton.setText(string.subscribe)
|
||||||
isSubscribed = false
|
isSubscribed = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
|
override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
.setNegativeButton(android.R.string.cancel) { _: DialogInterface?, _: Int ->
|
.setNegativeButton(android.R.string.cancel) { _: DialogInterface?, _: Int ->
|
||||||
// No
|
// No
|
||||||
}
|
}
|
||||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
context,
|
context,
|
||||||
context.getString(string.video_login_required_for_service),
|
context.getString(string.video_login_required_for_service),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -333,7 +329,7 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChannelViewHolder(private val binding: ItemChannelTitleBinding): MultiViewRecyclerViewHolder(binding) {
|
class ChannelViewHolder(private val binding: ItemChannelTitleBinding) : MultiViewRecyclerViewHolder(binding) {
|
||||||
fun bind(channel: Channel) {
|
fun bind(channel: Channel) {
|
||||||
|
|
||||||
val context = binding.avatar.context
|
val context = binding.avatar.context
|
||||||
@ -344,22 +340,22 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
if (avatar != null) {
|
if (avatar != null) {
|
||||||
val avatarPath = avatar.path
|
val avatarPath = avatar.path
|
||||||
Picasso.get()
|
Picasso.get()
|
||||||
.load(baseUrl + avatarPath)
|
.load(baseUrl + avatarPath)
|
||||||
.placeholder(R.drawable.test_image)
|
.placeholder(R.drawable.test_image)
|
||||||
.into(binding.avatar)
|
.into(binding.avatar)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.textViewTitle.text = channel.displayName
|
binding.textViewTitle.text = channel.displayName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TagViewHolder(private val binding: ItemTagTitleBinding): MultiViewRecyclerViewHolder(binding) {
|
class TagViewHolder(private val binding: ItemTagTitleBinding) : MultiViewRecyclerViewHolder(binding) {
|
||||||
fun bind(tag: TagVideo) {
|
fun bind(tag: TagVideo) {
|
||||||
binding.textViewTitle.text = tag.tag
|
binding.textViewTitle.text = tag.tag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class VideoViewHolder(private val binding: RowVideoListBinding): MultiViewRecyclerViewHolder(binding) {
|
class VideoViewHolder(private val binding: RowVideoListBinding) : MultiViewRecyclerViewHolder(binding) {
|
||||||
|
|
||||||
fun bind(video: Video) {
|
fun bind(video: Video) {
|
||||||
|
|
||||||
@ -368,18 +364,18 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
|
|
||||||
// Temp Loading Image
|
// Temp Loading Image
|
||||||
Picasso.get()
|
Picasso.get()
|
||||||
.load(baseUrl + video.previewPath)
|
.load(baseUrl + video.previewPath)
|
||||||
.placeholder(R.drawable.test_image)
|
.placeholder(R.drawable.test_image)
|
||||||
.error(R.drawable.test_image)
|
.error(R.drawable.test_image)
|
||||||
.into(binding.thumb)
|
.into(binding.thumb)
|
||||||
|
|
||||||
// Avatar
|
// Avatar
|
||||||
val avatar = getCreatorAvatar(video, context)
|
val avatar = getCreatorAvatar(video, context)
|
||||||
if (avatar != null) {
|
if (avatar != null) {
|
||||||
val avatarPath = avatar.path
|
val avatarPath = avatar.path
|
||||||
Picasso.get()
|
Picasso.get()
|
||||||
.load(baseUrl + avatarPath)
|
.load(baseUrl + avatarPath)
|
||||||
.into(binding.avatar)
|
.into(binding.avatar)
|
||||||
}
|
}
|
||||||
// set Name
|
// set Name
|
||||||
binding.slRowName.text = video.name
|
binding.slRowName.text = video.name
|
||||||
@ -395,9 +391,9 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
|
|
||||||
// set age and view count
|
// set age and view count
|
||||||
binding.videoMeta.text = getMetaString(
|
binding.videoMeta.text = getMetaString(
|
||||||
video.createdAt,
|
video.createdAt,
|
||||||
video.views,
|
video.views,
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
|
|
||||||
// set owner
|
// set owner
|
||||||
@ -431,8 +427,8 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
|
|
||||||
binding.moreButton.setOnClickListener { v: View? ->
|
binding.moreButton.setOnClickListener { v: View? ->
|
||||||
val popup = PopupMenu(
|
val popup = PopupMenu(
|
||||||
context,
|
context,
|
||||||
v!!
|
v!!
|
||||||
)
|
)
|
||||||
popup.setOnMenuItemClickListener { menuItem: MenuItem ->
|
popup.setOnMenuItemClickListener { menuItem: MenuItem ->
|
||||||
when (menuItem.itemId) {
|
when (menuItem.itemId) {
|
||||||
@ -492,17 +488,17 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
|
|
||||||
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
|
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
|
||||||
val videoDataService = RetrofitInstance.getRetrofitInstance(
|
val videoDataService = RetrofitInstance.getRetrofitInstance(
|
||||||
apiBaseURL, APIUrlHelper.useInsecureConnection(
|
apiBaseURL, APIUrlHelper.useInsecureConnection(
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
).create(
|
).create(
|
||||||
GetVideoDataService::class.java
|
GetVideoDataService::class.java
|
||||||
)
|
)
|
||||||
val call = videoDataService.rateVideo(video.id, body)
|
val call = videoDataService.rateVideo(video.id, body)
|
||||||
call.enqueue(object : Callback<ResponseBody?> {
|
call.enqueue(object : Callback<ResponseBody?> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<ResponseBody?>,
|
call: Call<ResponseBody?>,
|
||||||
response: Response<ResponseBody?>
|
response: Response<ResponseBody?>
|
||||||
) {
|
) {
|
||||||
// if 20x, update likes/dislikes
|
// if 20x, update likes/dislikes
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
@ -539,17 +535,17 @@ sealed class MultiViewRecyclerViewHolder(binding: ViewBinding) : RecyclerView.Vi
|
|||||||
|
|
||||||
override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
|
override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
context,
|
context,
|
||||||
context.getString(string.video_rating_failed),
|
context.getString(string.video_rating_failed),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
context,
|
context,
|
||||||
context.getString(string.video_login_required_for_service),
|
context.getString(string.video_login_required_for_service),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
|
||||||
|
*
|
||||||
|
* 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 distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.schueller.peertube.adapter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import net.schueller.peertube.database.Video
|
||||||
|
import net.schueller.peertube.databinding.RowPlaylistBinding
|
||||||
|
|
||||||
|
class PlaylistAdapter(private val mVideos: MutableList<Video>, private val onClick: (Video) -> Unit) : RecyclerView.Adapter<PlaylistAdapter.VideoViewHolder>() {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideoViewHolder {
|
||||||
|
|
||||||
|
val binding = RowPlaylistBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
|
||||||
|
return VideoViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: VideoViewHolder, position: Int) {
|
||||||
|
holder.bind(mVideos[position])
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setVideos(videos: List<Video>) {
|
||||||
|
mVideos.clear()
|
||||||
|
mVideos.addAll(videos)
|
||||||
|
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return mVideos.size
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class VideoViewHolder(private val binding: RowPlaylistBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
|
fun bind(video: Video) {
|
||||||
|
|
||||||
|
binding.videoName.text = video.videoName
|
||||||
|
binding.videoDescription.text = video.videoDescription
|
||||||
|
|
||||||
|
binding.root.setOnClickListener { onClick(video) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVideoAtPosition(position: Int): Video {
|
||||||
|
return mVideos[position]
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,9 @@ package net.schueller.peertube.database;
|
|||||||
import androidx.room.Database;
|
import androidx.room.Database;
|
||||||
import androidx.room.RoomDatabase;
|
import androidx.room.RoomDatabase;
|
||||||
|
|
||||||
@Database(entities = {Server.class}, version = 1)
|
@Database(entities = {Server.class, Video.class}, version = 1)
|
||||||
public abstract class AppDatabase extends RoomDatabase {
|
public abstract class AppDatabase extends RoomDatabase {
|
||||||
public abstract ServerDao serverDao();
|
public abstract ServerDao serverDao();
|
||||||
|
|
||||||
|
public abstract VideoDao videoDao();
|
||||||
}
|
}
|
25
app/src/main/java/net/schueller/peertube/database/Video.kt
Normal file
25
app/src/main/java/net/schueller/peertube/database/Video.kt
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package net.schueller.peertube.database
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
@Entity(tableName = "watch_later")
|
||||||
|
data class Video(
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Int = 0,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "video_uuid")
|
||||||
|
var videoUUID: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "video_name")
|
||||||
|
var videoName: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "video_description")
|
||||||
|
var videoDescription: String?
|
||||||
|
|
||||||
|
) : Parcelable
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
|
||||||
|
*
|
||||||
|
* 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 distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.schueller.peertube.database
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.room.*
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface VideoDao {
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
suspend fun insert(video: Video)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
suspend fun update(video: Video)
|
||||||
|
|
||||||
|
@Query("DELETE FROM watch_later")
|
||||||
|
suspend fun deleteAll()
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
suspend fun delete(video: Video)
|
||||||
|
|
||||||
|
@get:Query("SELECT * from watch_later ORDER BY video_name DESC")
|
||||||
|
val allVideos: LiveData<List<Video>>
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
|
||||||
|
*
|
||||||
|
* 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 distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.schueller.peertube.database
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
internal class VideoRepository(application: Application) {
|
||||||
|
|
||||||
|
private val mVideoDao: VideoDao
|
||||||
|
|
||||||
|
val allVideos: LiveData<List<Video>>
|
||||||
|
get() = mVideoDao.allVideos
|
||||||
|
|
||||||
|
init {
|
||||||
|
val db = VideoRoomDatabase.getDatabase(application)
|
||||||
|
mVideoDao = db.videoDao()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun update(video: Video) = withContext(Dispatchers.IO) {
|
||||||
|
mVideoDao.update(video)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun insert(video: Video) = withContext(Dispatchers.IO) {
|
||||||
|
mVideoDao.insert(video)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun delete(video: Video) = withContext(Dispatchers.IO) {
|
||||||
|
mVideoDao.delete(video)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
|
||||||
|
*
|
||||||
|
* 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 distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.schueller.peertube.database;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import androidx.room.Database;
|
||||||
|
import androidx.room.Room;
|
||||||
|
import androidx.room.RoomDatabase;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
@Database(entities = {Video.class}, version = 1, exportSchema = false)
|
||||||
|
public abstract class VideoRoomDatabase extends RoomDatabase {
|
||||||
|
|
||||||
|
public abstract VideoDao videoDao();
|
||||||
|
|
||||||
|
private static volatile VideoRoomDatabase INSTANCE;
|
||||||
|
|
||||||
|
private static final int NUMBER_OF_THREADS = 4;
|
||||||
|
|
||||||
|
static final ExecutorService databaseWriteExecutor =
|
||||||
|
Executors.newFixedThreadPool(NUMBER_OF_THREADS);
|
||||||
|
|
||||||
|
public static VideoRoomDatabase getDatabase(final Context context) {
|
||||||
|
if (INSTANCE == null) {
|
||||||
|
synchronized (VideoRoomDatabase.class) {
|
||||||
|
if (INSTANCE == null) {
|
||||||
|
if (INSTANCE == null) {
|
||||||
|
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
|
||||||
|
VideoRoomDatabase.class, "playlist_database")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
|
||||||
|
*
|
||||||
|
* 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 distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package net.schueller.peertube.database
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class VideoViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
|
||||||
|
private val mRepository: VideoRepository = VideoRepository(application)
|
||||||
|
val allVideos: LiveData<List<Video>> = mRepository.allVideos
|
||||||
|
|
||||||
|
fun insert(video: Video) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
mRepository.insert(video)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(video: Video) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
mRepository.update(video)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun delete(video: Video) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
mRepository.delete(video)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,54 +16,36 @@
|
|||||||
*/
|
*/
|
||||||
package net.schueller.peertube.fragment
|
package net.schueller.peertube.fragment
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import net.schueller.peertube.helper.MetaDataHelper.getMetaString
|
|
||||||
import net.schueller.peertube.helper.MetaDataHelper.getOwnerString
|
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.os.Bundle
|
|
||||||
import net.schueller.peertube.R
|
|
||||||
import net.schueller.peertube.service.VideoPlayerService
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import net.schueller.peertube.helper.APIUrlHelper
|
import android.content.res.ColorStateList
|
||||||
import net.schueller.peertube.network.GetVideoDataService
|
import android.os.Bundle
|
||||||
import net.schueller.peertube.network.RetrofitInstance
|
|
||||||
import net.schueller.peertube.helper.ErrorHelper
|
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.view.LayoutInflater
|
||||||
import com.squareup.picasso.Picasso
|
|
||||||
import android.widget.TextView
|
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Button
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.TextView
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.fragment.app.FragmentManager
|
|
||||||
import androidx.fragment.app.FragmentTransaction
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.mikepenz.iconics.Iconics
|
import com.mikepenz.iconics.Iconics
|
||||||
|
import net.schueller.peertube.R
|
||||||
import net.schueller.peertube.adapter.MultiViewRecycleViewAdapter
|
import net.schueller.peertube.adapter.MultiViewRecycleViewAdapter
|
||||||
import net.schueller.peertube.intents.Intents
|
import net.schueller.peertube.database.VideoViewModel
|
||||||
|
import net.schueller.peertube.helper.APIUrlHelper
|
||||||
|
import net.schueller.peertube.helper.ErrorHelper
|
||||||
import net.schueller.peertube.model.CommentThread
|
import net.schueller.peertube.model.CommentThread
|
||||||
import net.schueller.peertube.model.Rating
|
import net.schueller.peertube.model.Rating
|
||||||
import net.schueller.peertube.model.Video
|
import net.schueller.peertube.model.Video
|
||||||
import net.schueller.peertube.model.VideoList
|
import net.schueller.peertube.model.VideoList
|
||||||
import net.schueller.peertube.model.ui.VideoMetaViewItem
|
import net.schueller.peertube.model.ui.VideoMetaViewItem
|
||||||
import net.schueller.peertube.network.GetUserService
|
import net.schueller.peertube.network.GetVideoDataService
|
||||||
import net.schueller.peertube.network.Session
|
import net.schueller.peertube.network.RetrofitInstance
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import net.schueller.peertube.service.VideoPlayerService
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
|
||||||
import okhttp3.ResponseBody
|
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import java.lang.Exception
|
|
||||||
|
|
||||||
class VideoMetaDataFragment : Fragment() {
|
class VideoMetaDataFragment : Fragment() {
|
||||||
private var videoRating: Rating? = null
|
private var videoRating: Rating? = null
|
||||||
@ -73,12 +55,14 @@ class VideoMetaDataFragment : Fragment() {
|
|||||||
|
|
||||||
private lateinit var videoDescriptionFragment: VideoDescriptionFragment
|
private lateinit var videoDescriptionFragment: VideoDescriptionFragment
|
||||||
|
|
||||||
|
private val mVideoViewModel: VideoViewModel by activityViewModels()
|
||||||
|
|
||||||
var isLeaveAppExpected = false
|
var isLeaveAppExpected = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
|
|
||||||
// Inflate the layout for this fragment
|
// Inflate the layout for this fragment
|
||||||
@ -94,11 +78,11 @@ class VideoMetaDataFragment : Fragment() {
|
|||||||
// show full description fragment
|
// show full description fragment
|
||||||
videoDescriptionFragment = VideoDescriptionFragment.newInstance(video, this)
|
videoDescriptionFragment = VideoDescriptionFragment.newInstance(video, this)
|
||||||
childFragmentManager.beginTransaction()
|
childFragmentManager.beginTransaction()
|
||||||
.add(R.id.video_meta_data_fragment, videoDescriptionFragment, VideoDescriptionFragment.TAG).commit()
|
.add(R.id.video_meta_data_fragment, videoDescriptionFragment, VideoDescriptionFragment.TAG).commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hideDescriptionFragment() {
|
fun hideDescriptionFragment() {
|
||||||
val fragment: Fragment? = childFragmentManager.findFragmentByTag(VideoDescriptionFragment.TAG)
|
val fragment: Fragment? = childFragmentManager.findFragmentByTag(VideoDescriptionFragment.TAG)
|
||||||
if (fragment != null) {
|
if (fragment != null) {
|
||||||
childFragmentManager.beginTransaction().remove(fragment).commit()
|
childFragmentManager.beginTransaction().remove(fragment).commit()
|
||||||
}
|
}
|
||||||
@ -113,10 +97,10 @@ class VideoMetaDataFragment : Fragment() {
|
|||||||
val activity: Activity? = activity
|
val activity: Activity? = activity
|
||||||
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
|
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
|
||||||
val videoDataService = RetrofitInstance.getRetrofitInstance(
|
val videoDataService = RetrofitInstance.getRetrofitInstance(
|
||||||
apiBaseURL,
|
apiBaseURL,
|
||||||
APIUrlHelper.useInsecureConnection(context)
|
APIUrlHelper.useInsecureConnection(context)
|
||||||
).create(
|
).create(
|
||||||
GetVideoDataService::class.java
|
GetVideoDataService::class.java
|
||||||
)
|
)
|
||||||
|
|
||||||
// related videos
|
// related videos
|
||||||
@ -149,8 +133,8 @@ class VideoMetaDataFragment : Fragment() {
|
|||||||
videoOptions.setOnClickListener {
|
videoOptions.setOnClickListener {
|
||||||
val videoOptionsFragment = VideoOptionsFragment.newInstance(mService, video.files)
|
val videoOptionsFragment = VideoOptionsFragment.newInstance(mService, video.files)
|
||||||
videoOptionsFragment.show(
|
videoOptionsFragment.show(
|
||||||
getActivity()!!.supportFragmentManager,
|
getActivity()!!.supportFragmentManager,
|
||||||
VideoOptionsFragment.TAG
|
VideoOptionsFragment.TAG
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,9 +150,9 @@ class VideoMetaDataFragment : Fragment() {
|
|||||||
// We set this to default to null so that on initial start there are videos listed.
|
// We set this to default to null so that on initial start there are videos listed.
|
||||||
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
|
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
|
||||||
val service =
|
val service =
|
||||||
RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(context)).create(
|
RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(context)).create(
|
||||||
GetVideoDataService::class.java
|
GetVideoDataService::class.java
|
||||||
)
|
)
|
||||||
val call: Call<CommentThread> = service.getCommentThreads(videoId, start, count, sort)
|
val call: Call<CommentThread> = service.getCommentThreads(videoId, start, count, sort)
|
||||||
|
|
||||||
call.enqueue(object : Callback<CommentThread?> {
|
call.enqueue(object : Callback<CommentThread?> {
|
||||||
@ -176,7 +160,7 @@ class VideoMetaDataFragment : Fragment() {
|
|||||||
if (response.body() != null) {
|
if (response.body() != null) {
|
||||||
val commentThread = response.body()
|
val commentThread = response.body()
|
||||||
if (commentThread != null) {
|
if (commentThread != null) {
|
||||||
mMultiViewAdapter!!.setVideoComment(commentThread);
|
mMultiViewAdapter!!.setVideoComment(commentThread)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -197,8 +181,8 @@ class VideoMetaDataFragment : Fragment() {
|
|||||||
val filter: String? = null
|
val filter: String? = null
|
||||||
|
|
||||||
val sharedPref = context?.getSharedPreferences(
|
val sharedPref = context?.getSharedPreferences(
|
||||||
context.packageName + "_preferences",
|
context.packageName + "_preferences",
|
||||||
Context.MODE_PRIVATE
|
Context.MODE_PRIVATE
|
||||||
)
|
)
|
||||||
|
|
||||||
var nsfw = "false"
|
var nsfw = "false"
|
||||||
@ -211,9 +195,9 @@ class VideoMetaDataFragment : Fragment() {
|
|||||||
// We set this to default to null so that on initial start there are videos listed.
|
// We set this to default to null so that on initial start there are videos listed.
|
||||||
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
|
val apiBaseURL = APIUrlHelper.getUrlWithVersion(context)
|
||||||
val service =
|
val service =
|
||||||
RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(context)).create(
|
RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(context)).create(
|
||||||
GetVideoDataService::class.java
|
GetVideoDataService::class.java
|
||||||
)
|
)
|
||||||
val call: Call<VideoList> = service.getVideosData(start, count, sort, nsfw, filter, languages)
|
val call: Call<VideoList> = service.getVideosData(start, count, sort, nsfw, filter, languages)
|
||||||
|
|
||||||
/*Log the URL called*/Log.d("URL Called", call.request().url.toString() + "")
|
/*Log the URL called*/Log.d("URL Called", call.request().url.toString() + "")
|
||||||
@ -234,6 +218,12 @@ class VideoMetaDataFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveToPlaylist(video: Video) {
|
||||||
|
val playlistVideo: net.schueller.peertube.database.Video = net.schueller.peertube.database.Video(videoUUID = video.uuid, videoName = video.name, videoDescription = video.description)
|
||||||
|
mVideoViewModel.insert(playlistVideo)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "VMDF"
|
const val TAG = "VMDF"
|
||||||
}
|
}
|
||||||
|
@ -1,175 +1,188 @@
|
|||||||
<?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"
|
||||||
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"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
tools:context=".activity.MeActivity"
|
||||||
tools:context=".activity.MeActivity"
|
android:orientation="vertical">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/appbar_me"
|
android:id="@+id/appbar_me"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/tool_bar_me"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:elevation="4dp" />
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/tool_bar_me"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:elevation="4dp"/>
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:visibility="gone"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
android:id="@+id/a_me_account_line"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<de.hdodenhof.circleimageview.CircleImageView
|
|
||||||
android:id="@+id/a_me_avatar"
|
<LinearLayout
|
||||||
android:layout_width="72dp"
|
android:visibility="gone"
|
||||||
android:layout_height="72dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_marginTop="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="12dp"
|
android:layout_marginTop="12dp"
|
||||||
android:paddingTop="12dp"
|
android:layout_marginStart="12dp"
|
||||||
android:paddingEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:layout_marginLeft="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
android:layout_marginRight="12dp"/>
|
android:id="@+id/a_me_account_line"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/a_me_username"
|
|
||||||
android:drawablePadding="16dp"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text=""
|
android:orientation="horizontal">
|
||||||
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title" />
|
|
||||||
|
|
||||||
<TextView
|
<de.hdodenhof.circleimageview.CircleImageView
|
||||||
android:id="@+id/a_me_email"
|
android:id="@+id/a_me_avatar"
|
||||||
android:drawablePadding="16dp"
|
android:layout_width="72dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="72dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_marginTop="0dp"
|
||||||
android:text=""
|
android:paddingStart="12dp"
|
||||||
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title" />
|
android:paddingTop="12dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:layout_marginLeft="12dp"
|
||||||
|
android:layout_marginRight="12dp"/>
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:drawablePadding="16dp"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/me_logout_button"
|
android:orientation="vertical">
|
||||||
android:id="@+id/a_me_logout"
|
|
||||||
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title" />
|
<TextView
|
||||||
|
android:id="@+id/a_me_username"
|
||||||
|
android:drawablePadding="16dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/a_me_email"
|
||||||
|
android:drawablePadding="16dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:drawablePadding="16dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/me_logout_button"
|
||||||
|
android:id="@+id/a_me_logout"
|
||||||
|
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title"/>
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:background="@android:color/darker_gray"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/a_me_playlist"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:drawableStart="@drawable/ic_baseline_settings_24"
|
||||||
|
android:drawablePadding="16dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/playlist"
|
||||||
|
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/a_me_settings"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:drawableStart="@drawable/ic_baseline_settings_24"
|
||||||
|
android:drawablePadding="16dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/title_activity_settings"
|
||||||
|
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/a_me_helpnfeedback"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:drawableStart="@drawable/ic_baseline_help_24"
|
||||||
|
android:drawablePadding="16dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/me_help_and_feedback_button"
|
||||||
|
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<View
|
</ScrollView>
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:layout_marginBottom="4dp"
|
|
||||||
android:background="@android:color/darker_gray" />
|
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<!-- <LinearLayout-->
|
||||||
android:id="@+id/a_me_settings"
|
<!-- android:layout_marginBottom="0dp"-->
|
||||||
android:layout_width="match_parent"
|
<!-- android:layout_width="match_parent"-->
|
||||||
android:layout_height="wrap_content"
|
<!-- android:layout_height="wrap_content"-->
|
||||||
android:layout_marginTop="12dp"
|
<!-- android:orientation="vertical">-->
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<TextView
|
<!-- <androidx.appcompat.widget.AppCompatTextView-->
|
||||||
android:drawableStart="@drawable/ic_baseline_settings_24"
|
<!-- android:id="@+id/account_username"-->
|
||||||
android:drawablePadding="16dp"
|
<!-- android:layout_width="match_parent"-->
|
||||||
android:layout_width="match_parent"
|
<!-- android:layout_height="wrap_content"-->
|
||||||
android:layout_height="wrap_content"
|
<!-- android:text="" />-->
|
||||||
android:text="@string/title_activity_settings"
|
|
||||||
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
<!-- <androidx.appcompat.widget.AppCompatTextView-->
|
||||||
<LinearLayout
|
<!-- android:id="@+id/account_email"-->
|
||||||
android:id="@+id/a_me_helpnfeedback"
|
<!-- android:layout_width="match_parent"-->
|
||||||
android:layout_width="match_parent"
|
<!-- android:layout_height="wrap_content"-->
|
||||||
android:layout_height="wrap_content"
|
<!-- android:text="" />-->
|
||||||
android:layout_marginTop="12dp"
|
<!-- </LinearLayout>-->
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:drawableStart="@drawable/ic_baseline_help_24"
|
|
||||||
android:drawablePadding="16dp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/me_help_and_feedback_button"
|
|
||||||
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <LinearLayout-->
|
|
||||||
<!-- android:layout_marginBottom="0dp"-->
|
|
||||||
<!-- android:layout_width="match_parent"-->
|
|
||||||
<!-- android:layout_height="wrap_content"-->
|
|
||||||
<!-- android:orientation="vertical">-->
|
|
||||||
|
|
||||||
<!-- <androidx.appcompat.widget.AppCompatTextView-->
|
|
||||||
<!-- android:id="@+id/account_username"-->
|
|
||||||
<!-- android:layout_width="match_parent"-->
|
|
||||||
<!-- android:layout_height="wrap_content"-->
|
|
||||||
<!-- android:text="" />-->
|
|
||||||
|
|
||||||
<!-- <androidx.appcompat.widget.AppCompatTextView-->
|
|
||||||
<!-- android:id="@+id/account_email"-->
|
|
||||||
<!-- android:layout_width="match_parent"-->
|
|
||||||
<!-- android:layout_height="wrap_content"-->
|
|
||||||
<!-- android:text="" />-->
|
|
||||||
<!-- </LinearLayout>-->
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
44
app/src/main/res/layout/activity_playlist.xml
Normal file
44
app/src/main/res/layout/activity_playlist.xml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout 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="match_parent"
|
||||||
|
tools:context=".activity.ServerAddressBookActivity"
|
||||||
|
android:id="@+id/server_book">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/appbar_server_address_book"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/tool_bar_server_address_book"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways"
|
||||||
|
android:elevation="4dp"/>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/server_list_recyclerview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@android:color/darker_gray"
|
||||||
|
tools:listitem="@layout/row_playlist"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
43
app/src/main/res/layout/row_playlist.xml
Normal file
43
app/src/main/res/layout/row_playlist.xml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:card_view="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:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
card_view:cardCornerRadius="0dp"
|
||||||
|
card_view:cardElevation="0dp"
|
||||||
|
card_view:cardUseCompatPadding="true">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:padding="12dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/video_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
card_view:layout_constraintTop_toTopOf="parent"
|
||||||
|
card_view:layout_constraintStart_toStartOf="parent"
|
||||||
|
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title"
|
||||||
|
tools:text="@tools:sample/lorem"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/video_description"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:ellipsize="end"
|
||||||
|
card_view:layout_constraintTop_toBottomOf="@id/video_name"
|
||||||
|
card_view:layout_constraintStart_toStartOf="parent"
|
||||||
|
android:textAppearance="@style/Base.TextAppearance.AppCompat.Subhead"
|
||||||
|
tools:text="@tools:sample/lorem"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</androidx.cardview.widget.CardView>
|
@ -384,4 +384,8 @@
|
|||||||
<string name="video_owner_fqdn_line">%1$s@%2$s</string>
|
<string name="video_owner_fqdn_line">%1$s@%2$s</string>
|
||||||
<string name="video_sub_del_alert_title">Unsubscribe</string>
|
<string name="video_sub_del_alert_title">Unsubscribe</string>
|
||||||
<string name="video_sub_del_alert_msg">Are you sure you would like to unsubscribe?</string>
|
<string name="video_sub_del_alert_msg">Are you sure you would like to unsubscribe?</string>
|
||||||
|
<string name="saved_to_playlist">Saved to playlist</string>
|
||||||
|
<string name="remove_video">Remove Video</string>
|
||||||
|
<string name="remove_video_warning_message">Are you sure you want to remove this video from playlist?</string>
|
||||||
|
<string name="playlist">Playlist</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue
Block a user