package app.fedilab.fedilabtube; /* Copyright 2020 Thomas Schneider * * This file is a part of TubeLab * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * TubeLab 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 General * Public License for more details. * * You should have received a copy of the GNU General Public License along with TubeLab; if not, * see . */ import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.sqlite.SQLiteDatabase; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.v4.media.session.MediaSessionCompat; import android.text.Html; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.AppCompatImageView; import androidx.appcompat.widget.PopupMenu; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.core.graphics.drawable.DrawableCompat; import androidx.core.widget.NestedScrollView; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MergingMediaSource; import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.source.SingleSampleMediaSource; import com.google.android.exoplayer2.source.hls.HlsMediaSource; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.PlayerControlView; import com.google.android.exoplayer2.ui.PlayerView; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import app.fedilab.fedilabtube.client.APIResponse; import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; import app.fedilab.fedilabtube.client.data.AccountData.Account; import app.fedilab.fedilabtube.client.data.CaptionData.Caption; import app.fedilab.fedilabtube.client.data.CommentData.Comment; import app.fedilab.fedilabtube.client.data.PlaylistData; import app.fedilab.fedilabtube.client.data.VideoData; import app.fedilab.fedilabtube.client.entities.File; import app.fedilab.fedilabtube.client.entities.ItemStr; import app.fedilab.fedilabtube.client.entities.PlaylistExist; import app.fedilab.fedilabtube.client.entities.Report; import app.fedilab.fedilabtube.drawer.CommentListAdapter; import app.fedilab.fedilabtube.helper.CacheDataSourceFactory; import app.fedilab.fedilabtube.helper.FullScreenMediaController; import app.fedilab.fedilabtube.helper.Helper; import app.fedilab.fedilabtube.sqlite.AccountDAO; import app.fedilab.fedilabtube.sqlite.Sqlite; import app.fedilab.fedilabtube.viewmodel.CaptionsVM; import app.fedilab.fedilabtube.viewmodel.CommentVM; import app.fedilab.fedilabtube.viewmodel.PlaylistsVM; import app.fedilab.fedilabtube.viewmodel.PostActionsVM; import app.fedilab.fedilabtube.viewmodel.SearchVM; import app.fedilab.fedilabtube.viewmodel.TimelineVM; import app.fedilab.fedilabtube.webview.CustomWebview; import app.fedilab.fedilabtube.webview.MastalabWebChromeClient; import app.fedilab.fedilabtube.webview.MastalabWebViewClient; import es.dmoral.toasty.Toasty; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.ADD_COMMENT; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.RATEVIDEO; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.REPORT_ACCOUNT; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.REPORT_VIDEO; import static app.fedilab.fedilabtube.helper.Helper.getAttColor; import static app.fedilab.fedilabtube.helper.Helper.getLiveInstance; import static app.fedilab.fedilabtube.helper.Helper.isLoggedIn; public class PeertubeActivity extends AppCompatActivity implements CommentListAdapter.AllCommentRemoved { public static String video_id; private String peertubeInstance, videoUuid; private FullScreenMediaController.fullscreen fullscreen; private RelativeLayout loader; private TextView peertube_view_count, peertube_playlist, peertube_bookmark, peertube_like_count, peertube_dislike_count, peertube_description, peertube_title, more_actions; private NestedScrollView peertube_information_container; private VideoData.Video peertube; private PlayerView playerView; private SimpleExoPlayer player; private boolean fullScreenMode; private Dialog fullScreenDialog; private AppCompatImageView fullScreenIcon; private TextView resolution; private int mode; private LinearLayout write_comment_container; private ImageView send; private TextView add_comment_read; private EditText add_comment_write; private Map> playlists; private boolean playInMinimized; private boolean onStopCalled; private List captions; private TextView no_action_text; private String max_id; private RecyclerView lv_comments; private boolean flag_loading; private boolean isMyVideo; private List comments; private CommentListAdapter commentListAdapter; private boolean sepiaSearch; public static void hideKeyboard(Activity activity) { if (activity != null && activity.getWindow() != null) { activity.getWindow().getDecorView(); InputMethodManager imm = (InputMethodManager) activity.getSystemService(INPUT_METHOD_SERVICE); assert imm != null; imm.hideSoftInputFromWindow(activity.getWindow().getDecorView().getWindowToken(), 0); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_peertube); peertube_view_count = findViewById(R.id.peertube_view_count); peertube_bookmark = findViewById(R.id.peertube_bookmark); peertube_like_count = findViewById(R.id.peertube_like_count); peertube_dislike_count = findViewById(R.id.peertube_dislike_count); more_actions = findViewById(R.id.more_actions); peertube_description = findViewById(R.id.peertube_description); peertube_title = findViewById(R.id.peertube_title); peertube_information_container = findViewById(R.id.peertube_information_container); add_comment_read = findViewById(R.id.add_comment_read); add_comment_write = findViewById(R.id.add_comment_write); peertube_playlist = findViewById(R.id.peertube_playlist); send = findViewById(R.id.send); CustomWebview webview_video = findViewById(R.id.webview_video); playerView = findViewById(R.id.media_video); write_comment_container = findViewById(R.id.write_comment_container); max_id = "0"; loader = findViewById(R.id.loader); ImageView my_pp = findViewById(R.id.my_pp); SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); if (Helper.isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { Account account = new AccountDAO(PeertubeActivity.this, db).getAccountByToken(token); Helper.loadGiF(PeertubeActivity.this, account.getAvatar() != null ? account.getAvatar().getPath() : null, my_pp); } if (Helper.isTablet(PeertubeActivity.this)) { RelativeLayout video_container = findViewById(R.id.video_container); LinearLayout.LayoutParams param = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.0f ); video_container.setLayoutParams(param); } if (getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true); mode = sharedpreferences.getInt(Helper.SET_VIDEO_MODE, Helper.VIDEO_MODE_NORMAL); Bundle b = getIntent().getExtras(); if (b != null) { peertubeInstance = b.getString("peertube_instance", Helper.getLiveInstance(PeertubeActivity.this)); videoUuid = b.getString("video_uuid", null); isMyVideo = b.getBoolean("isMyVideo", false); sepiaSearch = b.getBoolean("sepia_search", false); peertube = b.getParcelable("video"); } playInMinimized = sharedpreferences.getBoolean(getString(R.string.set_video_minimize_choice), true); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N && !getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) { playInMinimized = false; } if (mode == Helper.VIDEO_MODE_WEBVIEW) { webview_video.setVisibility(View.VISIBLE); playerView.setVisibility(View.GONE); webview_video = Helper.initializeWebview(PeertubeActivity.this, R.id.webview_video, null); FrameLayout webview_container = findViewById(R.id.main_media_frame); final ViewGroup videoLayout = findViewById(R.id.videoLayout); MastalabWebChromeClient mastalabWebChromeClient = new MastalabWebChromeClient(PeertubeActivity.this, webview_video, webview_container, videoLayout); mastalabWebChromeClient.setOnToggledFullscreen(fullscreen -> { if (fullscreen) { videoLayout.setVisibility(View.VISIBLE); WindowManager.LayoutParams attrs = getWindow().getAttributes(); attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; getWindow().setAttributes(attrs); getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); peertube_information_container.setVisibility(View.GONE); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } else { WindowManager.LayoutParams attrs = getWindow().getAttributes(); attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN; attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; getWindow().setAttributes(attrs); getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); videoLayout.setVisibility(View.GONE); peertube_information_container.setVisibility(View.VISIBLE); } }); webview_video.getSettings().setAllowFileAccess(true); webview_video.setWebChromeClient(mastalabWebChromeClient); webview_video.getSettings().setDomStorageEnabled(true); webview_video.getSettings().setAppCacheEnabled(true); webview_video.getSettings().setMediaPlaybackRequiresUserGesture(false); webview_video.setWebViewClient(new MastalabWebViewClient(PeertubeActivity.this)); webview_video.loadUrl("https://" + peertubeInstance + "/videos/embed/" + videoUuid); } else { webview_video.setVisibility(View.GONE); playerView.setVisibility(View.VISIBLE); loader.setVisibility(View.VISIBLE); } if (mode != Helper.VIDEO_MODE_WEBVIEW) { playerView.setControllerShowTimeoutMs(1000); playerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT); initFullscreenDialog(); initFullscreenButton(); } flag_loading = true; comments = new ArrayList<>(); lv_comments = findViewById(R.id.peertube_comments); commentListAdapter = new CommentListAdapter(comments, Helper.isVideoOwner(PeertubeActivity.this, peertube)); commentListAdapter.allCommentRemoved = PeertubeActivity.this; LinearLayoutManager mLayoutManager = new LinearLayoutManager(PeertubeActivity.this); lv_comments.setLayoutManager(mLayoutManager); lv_comments.setNestedScrollingEnabled(false); lv_comments.setAdapter(commentListAdapter); lv_comments.addOnScrollListener(new RecyclerView.OnScrollListener() { public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { if (dy > 0) { int visibleItemCount = mLayoutManager.getChildCount(); int totalItemCount = mLayoutManager.getItemCount(); int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition(); if (firstVisibleItem + visibleItemCount == totalItemCount) { if (!flag_loading) { CommentVM commentViewModel = new ViewModelProvider(PeertubeActivity.this).get(CommentVM.class); commentViewModel.getThread(sepiaSearch?peertubeInstance:null, videoUuid, max_id).observe(PeertubeActivity.this, apiresponse -> manageVIewComment(apiresponse)); } } } } }); if( peertube != null && sepiaSearch && peertube.getEmbedUrl() != null && Helper.isLoggedIn(PeertubeActivity.this)) { SearchVM viewModelSearch = new ViewModelProvider(PeertubeActivity.this).get(SearchVM.class); viewModelSearch.getVideos("0", peertube.getEmbedUrl()).observe(PeertubeActivity.this, this::manageVIewVideos); }else { playVideo(); } } private void manageVIewVideos(APIResponse apiResponse) { if( apiResponse == null || apiResponse.getPeertubes() == null || apiResponse.getPeertubes().size() == 0) { playVideo(); return; } peertube = apiResponse.getPeertubes().get(0); sepiaSearch = false; playVideo(); } public void manageVIewComment(APIResponse apiResponse) { flag_loading = false; if (apiResponse == null || (apiResponse.getError() != null)) { if (apiResponse == null) Toasty.error(PeertubeActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); else Toasty.error(PeertubeActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show(); return; } int oldSize = comments.size(); int newComments = 0; for (Comment comment : apiResponse.getComments()) { if (comment.getText() != null && comment.getText().trim().length() > 0) { comments.add(comment); newComments++; } } if (comments.size() > 0) { lv_comments.setVisibility(View.VISIBLE); commentListAdapter.notifyItemRangeInserted(oldSize, newComments); } } @Override public void onNewIntent(Intent intent) { super.onNewIntent(intent); Bundle b = intent.getExtras(); if (b != null) { peertubeInstance = b.getString("peertube_instance", Helper.getLiveInstance(PeertubeActivity.this)); videoUuid = b.getString("video_uuid", null); playVideo(); } } private void playVideo() { if (player != null) { player.setPlayWhenReady(false); player.release(); player = ExoPlayerFactory.newSimpleInstance(PeertubeActivity.this); playerView.setPlayer(player); loader.setVisibility(View.GONE); player.setPlayWhenReady(true); captions = null; } fullscreen = FullScreenMediaController.fullscreen.OFF; setFullscreen(FullScreenMediaController.fullscreen.OFF); fullScreenMode = false; peertube_playlist.setVisibility(View.VISIBLE); peertube_bookmark.setVisibility(View.GONE); TimelineVM feedsViewModel = new ViewModelProvider(PeertubeActivity.this).get(TimelineVM.class); feedsViewModel.getVideo(sepiaSearch?peertubeInstance:null, videoUuid, isMyVideo).observe(PeertubeActivity.this, this::manageVIewVideo); CaptionsVM captionsViewModel = new ViewModelProvider(PeertubeActivity.this).get(CaptionsVM.class); captionsViewModel.getCaptions(sepiaSearch?peertubeInstance:null, videoUuid).observe(PeertubeActivity.this, this::manageCaptions); } public void change() { if (fullscreen == FullScreenMediaController.fullscreen.ON) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); Objects.requireNonNull(getSupportActionBar()).hide(); peertube_information_container.setVisibility(View.GONE); } else { getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN); Objects.requireNonNull(getSupportActionBar()).show(); peertube_information_container.setVisibility(View.VISIBLE); } } @Override public boolean dispatchTouchEvent(MotionEvent ev) { View v = getCurrentFocus(); if ((ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_MOVE) && v instanceof EditText && v.getId() == R.id.add_comment_write) { int[] scrcoords = new int[2]; v.getLocationOnScreen(scrcoords); float x = ev.getRawX() + v.getLeft() - scrcoords[0]; float y = ev.getRawY() + v.getTop() - scrcoords[1]; if (x < v.getLeft() || x > v.getRight() || y < v.getTop() || y > v.getBottom()) { add_comment_read.setVisibility(View.VISIBLE); add_comment_write.setVisibility(View.GONE); send.setVisibility(View.GONE); hideKeyboard(PeertubeActivity.this); } } return super.dispatchTouchEvent(ev); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { if (playInMinimized && player != null) { enterVideoMode(); } else { finish(); } return true; } return super.onOptionsItemSelected(item); } private void reportAlert(RetrofitPeertubeAPI.ActionType type, androidx.appcompat.app.AlertDialog alertDialog) { androidx.appcompat.app.AlertDialog.Builder dialogBuilder = new androidx.appcompat.app.AlertDialog.Builder(PeertubeActivity.this); LayoutInflater inflater1 = getLayoutInflater(); View dialogView = inflater1.inflate(R.layout.popup_report, new LinearLayout(PeertubeActivity.this), false); dialogBuilder.setView(dialogView); EditText report_content = dialogView.findViewById(R.id.report_content); dialogBuilder.setNeutralButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); dialogBuilder.setPositiveButton(R.string.report, (dialog, id) -> { if (report_content.getText().toString().trim().length() == 0) { Toasty.info(PeertubeActivity.this, getString(R.string.report_comment_size), Toasty.LENGTH_LONG).show(); } else { PostActionsVM viewModel = new ViewModelProvider(PeertubeActivity.this).get(PostActionsVM.class); if (type == RetrofitPeertubeAPI.ActionType.REPORT_VIDEO) { Report report = new Report(); Report.VideoReport videoReport = new Report.VideoReport(); videoReport.setId(peertube.getId()); report.setVideo(videoReport); report.setReason(report_content.getText().toString()); viewModel.report(report).observe(PeertubeActivity.this, apiResponse -> manageVIewPostActions(RetrofitPeertubeAPI.ActionType.REPORT_VIDEO, apiResponse)); alertDialog.dismiss(); dialog.dismiss(); } else if (type == RetrofitPeertubeAPI.ActionType.REPORT_ACCOUNT) { Report report = new Report(); Report.AccountReport accountReport = new Report.AccountReport(); accountReport.setId(peertube.getAccount().getId()); report.setAccount(accountReport); report.setReason(report_content.getText().toString()); viewModel.report(report).observe(PeertubeActivity.this, apiResponse -> manageVIewPostActions(RetrofitPeertubeAPI.ActionType.REPORT_ACCOUNT, apiResponse)); alertDialog.dismiss(); dialog.dismiss(); } } }); androidx.appcompat.app.AlertDialog alertDialog2 = dialogBuilder.create(); alertDialog2.show(); } public FullScreenMediaController.fullscreen getFullscreen() { return fullscreen; } public void setFullscreen(FullScreenMediaController.fullscreen fullscreen) { this.fullscreen = fullscreen; } public void manageCaptions(APIResponse apiResponse) { if (apiResponse == null || (apiResponse.getError() != null) || apiResponse.getCaptions() == null || apiResponse.getCaptions().size() == 0) { return; } captions = apiResponse.getCaptions(); } @SuppressLint("ClickableViewAccessibility") public void manageVIewVideo(APIResponse apiResponse) { if (apiResponse == null || (apiResponse.getError() != null) || apiResponse.getPeertubes() == null || apiResponse.getPeertubes().size() == 0) { Toasty.error(PeertubeActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); loader.setVisibility(View.GONE); return; } if (apiResponse.getPeertubes() == null || apiResponse.getPeertubes().get(0) == null || apiResponse.getPeertubes().get(0).getFileUrl(null, PeertubeActivity.this) == null) { Toasty.error(PeertubeActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); loader.setVisibility(View.GONE); return; } peertube = apiResponse.getPeertubes().get(0); if( sepiaSearch) { peertubeInstance = peertube.getAccount().getHost(); } List videoIds = new ArrayList<>(); videoIds.add(peertube.getId()); PlaylistsVM viewModel = new ViewModelProvider(this).get(PlaylistsVM.class); viewModel.videoExists(videoIds).observe(this, this::manageVIewPlaylist); add_comment_read.setOnClickListener(v -> { if (isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { add_comment_read.setVisibility(View.GONE); add_comment_write.setVisibility(View.VISIBLE); send.setVisibility(View.VISIBLE); add_comment_write.requestFocus(); add_comment_write.setSelection(add_comment_write.getText().length()); } else { if( sepiaSearch) { Toasty.info(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_SHORT).show(); }else { Toasty.error(PeertubeActivity.this, getString(R.string.not_logged_in), Toast.LENGTH_SHORT).show(); } } }); send.setOnClickListener(v -> { if (isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { String comment = add_comment_write.getText().toString(); if (comment.trim().length() > 0) { PostActionsVM viewModelComment = new ViewModelProvider(PeertubeActivity.this).get(PostActionsVM.class); viewModelComment.comment(ADD_COMMENT, peertube.getId(), null, comment).observe(PeertubeActivity.this, apiResponse1 -> manageVIewPostActions(ADD_COMMENT, apiResponse1)); add_comment_write.setText(""); add_comment_read.setVisibility(View.VISIBLE); add_comment_write.setVisibility(View.GONE); send.setVisibility(View.GONE); add_comment_read.requestFocus(); } } else { if( sepiaSearch) { Toasty.info(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_SHORT).show(); }else { Toasty.error(PeertubeActivity.this, getString(R.string.not_logged_in), Toast.LENGTH_SHORT).show(); } } }); peertube_playlist.setOnClickListener(v -> { PlaylistsVM viewModelOwnerPlaylist = new ViewModelProvider(PeertubeActivity.this).get(PlaylistsVM.class); viewModelOwnerPlaylist.manage(PlaylistsVM.action.GET_PLAYLISTS, null, null).observe(PeertubeActivity.this, this::manageVIewPlaylists); }); no_action_text = findViewById(R.id.no_action_text); if (peertube.isCommentsEnabled()) { CommentVM commentViewModel = new ViewModelProvider(PeertubeActivity.this).get(CommentVM.class); commentViewModel.getThread(sepiaSearch?peertubeInstance:null, videoUuid, max_id).observe(PeertubeActivity.this, this::manageVIewComment); write_comment_container.setVisibility(View.VISIBLE); } else { RelativeLayout no_action = findViewById(R.id.no_action); no_action_text.setText(getString(R.string.comment_no_allowed_peertube)); no_action.setVisibility(View.VISIBLE); write_comment_container.setVisibility(View.GONE); } SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); setTitle(peertube.getName()); peertube_description.setText(peertube.getDescription()); peertube_title.setText(peertube.getName()); peertube_dislike_count.setText(String.valueOf(peertube.getDislikes())); peertube_like_count.setText(String.valueOf(peertube.getLikes())); peertube_view_count.setText(String.valueOf(peertube.getViews())); video_id = peertube.getId(); changeColor(); initResolution(); peertube_like_count.setOnClickListener(v -> { if (isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { String newState = peertube.getMyRating().equals("like") ? "none" : "like"; PostActionsVM viewModelLike = new ViewModelProvider(PeertubeActivity.this).get(PostActionsVM.class); viewModelLike.post(RATEVIDEO, peertube.getId(), newState).observe(PeertubeActivity.this, apiResponse1 -> manageVIewPostActions(RATEVIDEO, apiResponse1)); peertube.setMyRating(newState); int count = Integer.parseInt(peertube_like_count.getText().toString()); if (newState.compareTo("none") == 0) { count--; if (count - 1 < 0) { count = 0; } } else { count++; } peertube_like_count.setText(String.valueOf(count)); changeColor(); } else { if( sepiaSearch) { Toasty.info(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_SHORT).show(); }else { Toasty.error(PeertubeActivity.this, getString(R.string.not_logged_in), Toast.LENGTH_SHORT).show(); } } }); peertube_dislike_count.setOnClickListener(v -> { if (isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { String newState = peertube.getMyRating().equals("dislike") ? "none" : "dislike"; PostActionsVM viewModelLike = new ViewModelProvider(PeertubeActivity.this).get(PostActionsVM.class); viewModelLike.post(RATEVIDEO, peertube.getId(), newState).observe(PeertubeActivity.this, apiResponse1 -> manageVIewPostActions(RATEVIDEO, apiResponse1)); peertube.setMyRating(newState); int count = Integer.parseInt(peertube_dislike_count.getText().toString()); if (newState.compareTo("none") == 0) { count--; if (count - 1 < 0) { count = 0; } } else { count++; } peertube_dislike_count.setText(String.valueOf(count)); changeColor(); } else { if( sepiaSearch) { Toasty.info(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_SHORT).show(); }else { Toasty.error(PeertubeActivity.this, getString(R.string.not_logged_in), Toast.LENGTH_SHORT).show(); } } }); if (mode == Helper.VIDEO_MODE_NORMAL) { int video_cache = sharedpreferences.getInt(Helper.SET_VIDEO_CACHE, Helper.DEFAULT_VIDEO_CACHE_MB); ProgressiveMediaSource videoSource; player = ExoPlayerFactory.newSimpleInstance(PeertubeActivity.this); playerView.setPlayer(player); loader.setVisibility(View.GONE); if (apiResponse.getPeertubes().get(0).getFiles() != null && apiResponse.getPeertubes().get(0).getFiles().size() > 0) { if (video_cache == 0) { DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(PeertubeActivity.this, Util.getUserAgent(PeertubeActivity.this, null), null); videoSource = new ProgressiveMediaSource.Factory(dataSourceFactory) .createMediaSource(Uri.parse(apiResponse.getPeertubes().get(0).getFileUrl(null, PeertubeActivity.this))); } else { CacheDataSourceFactory cacheDataSourceFactory = new CacheDataSourceFactory(PeertubeActivity.this); videoSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory) .createMediaSource(Uri.parse(apiResponse.getPeertubes().get(0).getFileUrl(null, PeertubeActivity.this))); } player.prepare(videoSource); } else { HlsMediaSource hlsMediaSource = new HlsMediaSource.Factory(new DefaultHttpDataSourceFactory(System.getProperty("http.agent"))) .createMediaSource(Uri.parse(apiResponse.getPeertubes().get(0).getStreamingPlaylists().get(0).getPlaylistUrl())); player.prepare(hlsMediaSource); } player.setPlayWhenReady(true); } more_actions.setOnClickListener(view -> { PopupMenu popup = new PopupMenu(PeertubeActivity.this, more_actions); popup.getMenuInflater() .inflate(R.menu.main_video, popup.getMenu()); if (captions == null) { popup.getMenu().findItem(R.id.action_captions).setEnabled(false); } popup.setOnMenuItemClickListener(item -> { int itemId = item.getItemId(); if (itemId == R.id.action_download) { if (Build.VERSION.SDK_INT >= 23) { if (ContextCompat.checkSelfPermission(PeertubeActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(PeertubeActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(PeertubeActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Helper.EXTERNAL_STORAGE_REQUEST_CODE); } else { Helper.manageDownloads(PeertubeActivity.this, peertube.getFileDownloadUrl(null)); } } else { Helper.manageDownloads(PeertubeActivity.this, peertube.getFileDownloadUrl(null)); } } else if (itemId == R.id.action_share) { Intent sendIntent = new Intent(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.shared_via)); String url; url = peertube.getFiles().get(0).getFileDownloadUrl(); boolean share_details = sharedpreferences.getBoolean(Helper.SET_SHARE_DETAILS, true); String extra_text; if (share_details) { extra_text = "@" + peertube.getAccount().getAcct(); extra_text += "\r\n\r\n" + peertube.getName(); extra_text += "\n\n\uD83D\uDD17 " + url + "\r\n-\n"; final String contentToot; if (peertube.getDescription() != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) contentToot = Html.fromHtml(peertube.getDescription(), Html.FROM_HTML_MODE_LEGACY).toString(); else contentToot = Html.fromHtml(peertube.getDescription()).toString(); } else { contentToot = ""; } extra_text += contentToot; } else { extra_text = url; } sendIntent.putExtra(Intent.EXTRA_TEXT, extra_text); sendIntent.setType("text/plain"); startActivity(Intent.createChooser(sendIntent, getString(R.string.share_with))); } else if (itemId == R.id.action_captions) { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(PeertubeActivity.this); if (captions == null) { return true; } String[] itemsKeyLanguage = new String[captions.size() + 1]; String[] itemsLabelLanguage = new String[captions.size() + 1]; itemsLabelLanguage[0] = getString(R.string.none); itemsKeyLanguage[0] = "null"; int i = 1; if (captions.size() > 0) { for (Caption caption : captions) { ItemStr lang = caption.getLanguage(); itemsLabelLanguage[i] = lang.getLabel(); itemsKeyLanguage[i] = lang.getId(); i++; } } dialogBuilder.setSingleChoiceItems(itemsLabelLanguage, i, (dialog, which) -> { Uri uri = null; if (which > 0) { if (!sepiaSearch) { uri = Uri.parse("https://" + getLiveInstance(PeertubeActivity.this) + captions.get(which - 1).getCaptionPath()); } else { uri = Uri.parse("https://" + peertubeInstance + captions.get(which - 1).getCaptionPath()); } } int video_cache = sharedpreferences.getInt(Helper.SET_VIDEO_CACHE, Helper.DEFAULT_VIDEO_CACHE_MB); long position = player.getCurrentPosition(); if (player != null) player.release(); TrackSelector trackSelector = new DefaultTrackSelector(new AdaptiveTrackSelection.Factory()); player = ExoPlayerFactory.newSimpleInstance(PeertubeActivity.this, trackSelector); ProgressiveMediaSource videoSource; MediaSource subtitleSource = null; Format subtitleFormat = Format.createTextSampleFormat( null, MimeTypes.TEXT_VTT, Format.NO_VALUE, itemsKeyLanguage[which]); if (apiResponse.getPeertubes().get(0).getFiles() != null && apiResponse.getPeertubes().get(0).getFiles().size() > 0) { if (video_cache == 0) { DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(PeertubeActivity.this, Util.getUserAgent(PeertubeActivity.this, null), null); videoSource = new ProgressiveMediaSource.Factory(dataSourceFactory) .createMediaSource(Uri.parse(apiResponse.getPeertubes().get(0).getFileUrl(null, PeertubeActivity.this))); if (uri != null) subtitleSource = new SingleSampleMediaSource.Factory(dataSourceFactory).createMediaSource(uri, subtitleFormat, C.TIME_UNSET); } else { CacheDataSourceFactory cacheDataSourceFactory = new CacheDataSourceFactory(PeertubeActivity.this); videoSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory) .createMediaSource(Uri.parse(apiResponse.getPeertubes().get(0).getFileUrl(null, PeertubeActivity.this))); if (uri != null) subtitleSource = new SingleSampleMediaSource.Factory(cacheDataSourceFactory).createMediaSource(uri, subtitleFormat, C.TIME_UNSET); } playerView.setPlayer(player); if (which > 0 && subtitleSource != null) { MergingMediaSource mergedSource = new MergingMediaSource(videoSource, subtitleSource); player.prepare(mergedSource); } else { player.prepare(videoSource); } player.seekTo(0, position); player.setPlayWhenReady(true); } dialog.dismiss(); }); dialogBuilder.setOnDismissListener(dialogInterface -> { }); dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); AlertDialog alertDialog = dialogBuilder.create(); alertDialog.setTitle(getString(R.string.pickup_captions)); alertDialog.show(); } else if (itemId == R.id.action_report) { AlertDialog alertDialog; AlertDialog.Builder dialogBuilder; dialogBuilder = new AlertDialog.Builder(PeertubeActivity.this); LayoutInflater inflater1 = getLayoutInflater(); View dialogView = inflater1.inflate(R.layout.popup_report_choice, new LinearLayout(PeertubeActivity.this), false); dialogBuilder.setView(dialogView); Button report_video = dialogView.findViewById(R.id.report_video); Button report_account = dialogView.findViewById(R.id.report_account); dialogBuilder.setNeutralButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); alertDialog = dialogBuilder.create(); alertDialog.show(); report_video.setOnClickListener(v -> reportAlert(REPORT_VIDEO, alertDialog)); report_account.setOnClickListener(v -> reportAlert(REPORT_ACCOUNT, alertDialog)); } return true; }); popup.show(); }); } @Override public void onConfigurationChanged(@NotNull Configuration newConfig) { super.onConfigurationChanged(newConfig); if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { if (mode != Helper.VIDEO_MODE_WEBVIEW) { openFullscreenDialog(); } setFullscreen(FullScreenMediaController.fullscreen.ON); } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { if (mode != Helper.VIDEO_MODE_WEBVIEW) { closeFullscreenDialog(); } setFullscreen(FullScreenMediaController.fullscreen.OFF); } change(); } @Override public void onDestroy() { super.onDestroy(); if (player != null) { player.setPlayWhenReady(false); player.release(); } if (fullScreenDialog != null && fullScreenDialog.isShowing()) { fullScreenDialog.dismiss(); } } @Override protected void onPause() { super.onPause(); if (player != null && !playInMinimized) { player.setPlayWhenReady(false); } } @RequiresApi(api = Build.VERSION_CODES.N) @Override public void onUserLeaveHint() { enterVideoMode(); } private void enterVideoMode() { if (playInMinimized && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && player != null) { MediaSessionCompat mediaSession = new MediaSessionCompat(this, getPackageName()); MediaSessionConnector mediaSessionConnector = new MediaSessionConnector(mediaSession); mediaSessionConnector.setPlayer(player); PlayerControlView controlView = playerView.findViewById(R.id.exo_controller); controlView.hide(); playerView.setControllerAutoShow(false); mediaSession.setActive(true); enterPictureInPictureMode(); } } @Override public void onBackPressed() { if (playInMinimized && player != null) { enterVideoMode(); } else { super.onBackPressed(); } } @Override public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) { if (isInPictureInPictureMode) { setFullscreen(FullScreenMediaController.fullscreen.ON); } else { setFullscreen(FullScreenMediaController.fullscreen.OFF); if (onStopCalled) { finishAndRemoveTask(); } } } @Override public void onStop() { super.onStop(); onStopCalled = true; } @Override public void onResume() { super.onResume(); onStopCalled = false; if (player != null && !playInMinimized) { player.setPlayWhenReady(true); } } public void displayResolution() { SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); AlertDialog.Builder builderSingle = new AlertDialog.Builder(PeertubeActivity.this); builderSingle.setTitle(R.string.pickup_resolution); final ArrayAdapter arrayAdapter = new ArrayAdapter<>(PeertubeActivity.this, android.R.layout.select_dialog_item); for (File file : peertube.getFiles()) { if (file.getResolutions() != null) { if (file.getResolutions().getLabel().compareTo("0p") != 0) { arrayAdapter.add(file.getResolutions().getLabel()); } } } builderSingle.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); builderSingle.setAdapter(arrayAdapter, (dialog, which) -> { String res = Objects.requireNonNull(arrayAdapter.getItem(which)).substring(0, Objects.requireNonNull(arrayAdapter.getItem(which)).length() - 1); if (playerView != null) { loader.setVisibility(View.VISIBLE); long position = player.getCurrentPosition(); PlayerControlView controlView = playerView.findViewById(R.id.exo_controller); resolution = controlView.findViewById(R.id.resolution); resolution.setText(String.format("%sp", res)); if (mode == Helper.VIDEO_MODE_NORMAL) { if (player != null) player.release(); player = ExoPlayerFactory.newSimpleInstance(PeertubeActivity.this); playerView.setPlayer(player); loader.setVisibility(View.GONE); int video_cache = sharedpreferences.getInt(Helper.SET_VIDEO_CACHE, Helper.DEFAULT_VIDEO_CACHE_MB); ProgressiveMediaSource videoSource; if (video_cache == 0) { DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(PeertubeActivity.this, Util.getUserAgent(PeertubeActivity.this, null), null); videoSource = new ProgressiveMediaSource.Factory(dataSourceFactory) .createMediaSource(Uri.parse(peertube.getFileUrl(res, PeertubeActivity.this))); } else { CacheDataSourceFactory cacheDataSourceFactory = new CacheDataSourceFactory(PeertubeActivity.this); videoSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory) .createMediaSource(Uri.parse(peertube.getFileUrl(res, PeertubeActivity.this))); } player.prepare(videoSource); player.seekTo(0, position); player.setPlayWhenReady(true); } } }); builderSingle.show(); } @SuppressWarnings({"unused", "RedundantSuppression"}) public void manageVIewPostActions(RetrofitPeertubeAPI.ActionType statusAction, APIResponse apiResponse) { if (peertube.isCommentsEnabled() && statusAction == ADD_COMMENT) { CommentVM commentViewModel = new ViewModelProvider(PeertubeActivity.this).get(CommentVM.class); commentViewModel.getThread(sepiaSearch?peertubeInstance:null, videoUuid, max_id).observe(PeertubeActivity.this, this::manageVIewComment); } else if (statusAction == RetrofitPeertubeAPI.ActionType.REPORT_ACCOUNT) { Toasty.success(PeertubeActivity.this, getString(R.string.successful_report), Toasty.LENGTH_LONG).show(); } else if (statusAction == RetrofitPeertubeAPI.ActionType.REPORT_VIDEO) { Toasty.success(PeertubeActivity.this, getString(R.string.successful_video_report), Toasty.LENGTH_LONG).show(); } } private void initFullscreenDialog() { fullScreenDialog = new Dialog(this, android.R.style.Theme_Black_NoTitleBar_Fullscreen) { public void onBackPressed() { if (fullScreenMode) { closeFullscreenDialog(); } super.onBackPressed(); } }; } private void openFullscreenDialog() { ((ViewGroup) playerView.getParent()).removeView(playerView); fullScreenDialog.addContentView(playerView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); fullScreenIcon.setImageDrawable(ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_fullscreen_exit_24)); fullScreenMode = true; fullScreenDialog.show(); } private void closeFullscreenDialog() { ((ViewGroup) playerView.getParent()).removeView(playerView); ((FrameLayout) findViewById(R.id.main_media_frame)).addView(playerView); fullScreenMode = false; fullScreenDialog.dismiss(); fullScreenIcon.setImageDrawable(ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_fullscreen_24)); } private void initFullscreenButton() { PlayerControlView controlView = playerView.findViewById(R.id.exo_controller); fullScreenIcon = controlView.findViewById(R.id.exo_fullscreen_icon); View fullScreenButton = controlView.findViewById(R.id.exo_fullscreen_button); fullScreenButton.setOnClickListener(v -> { if (!fullScreenMode) { openFullscreenDialog(); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } else { closeFullscreenDialog(); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); Handler handler = new Handler(); handler.postDelayed(() -> setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR), 2000); } }); } private void initResolution() { PlayerControlView controlView = playerView.findViewById(R.id.exo_controller); resolution = controlView.findViewById(R.id.resolution); if (peertube.getFiles() != null && peertube.getFiles().size() > 0) { resolution.setText(String.format("%s", Helper.defaultFile(PeertubeActivity.this, peertube.getFiles()).getResolutions().getLabel())); resolution.setOnClickListener(v -> displayResolution()); } else { resolution.setVisibility(View.GONE); } } private void changeColor() { Drawable thumbUp = ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_thumb_up_alt_24); Drawable thumbDown = ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_thumb_down_alt_24); int color = getAttColor(this, android.R.attr.colorControlNormal); if (thumbUp != null) { thumbUp.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); DrawableCompat.setTint(thumbUp, color); } if (thumbDown != null) { thumbDown.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); DrawableCompat.setTint(thumbDown, color); } if (peertube.getMyRating() != null && peertube.getMyRating().compareTo("like") == 0) { if (thumbUp != null) { thumbUp.setColorFilter(getResources().getColor(R.color.positive_thumbs), PorterDuff.Mode.SRC_ATOP); DrawableCompat.setTint(thumbUp, getResources().getColor(R.color.positive_thumbs)); } } else if (peertube.getMyRating() != null && peertube.getMyRating().compareTo("dislike") == 0) { if (thumbDown != null) { thumbDown.setColorFilter(getResources().getColor(R.color.negative_thumbs), PorterDuff.Mode.SRC_ATOP); DrawableCompat.setTint(thumbDown, getResources().getColor(R.color.negative_thumbs)); } } peertube_like_count.setCompoundDrawablesWithIntrinsicBounds(null, thumbUp, null, null); peertube_dislike_count.setCompoundDrawablesWithIntrinsicBounds(null, thumbDown, null, null); } public void manageVIewPlaylists(APIResponse apiResponse) { if (apiResponse.getError() != null) { return; } if (apiResponse.getPlaylists() != null && apiResponse.getPlaylists().size() > 0) { androidx.appcompat.app.AlertDialog.Builder builder = new androidx.appcompat.app.AlertDialog.Builder(PeertubeActivity.this); builder.setTitle(R.string.modify_playlists); List ownerPlaylists = apiResponse.getPlaylists(); if( ownerPlaylists == null){ return; } String[] label = new String[ownerPlaylists.size()]; boolean[] checked = new boolean[ownerPlaylists.size()]; int i = 0; List playlistsForVideo = playlists.get(peertube.getId()); for (PlaylistData.Playlist playlist : ownerPlaylists) { checked[i] = false; if (playlistsForVideo != null) { for (PlaylistExist playlistExist : playlistsForVideo) { if (playlistExist != null && playlistExist.getPlaylistId().compareTo(playlist.getId()) == 0) { checked[i] = true; break; } } } label[i] = playlist.getDisplayName(); i++; } builder.setMultiChoiceItems(label, checked, (dialog, which, isChecked) -> { PlaylistsVM playlistsViewModel = new ViewModelProvider(PeertubeActivity.this).get(PlaylistsVM.class); if (isChecked) { //Add to playlist playlistsViewModel.manage(PlaylistsVM.action.ADD_VIDEOS, ownerPlaylists.get(which), peertube.getUuid()).observe(PeertubeActivity.this, apiResponse3 -> addElement(ownerPlaylists.get(which).getId(), peertube.getId(), apiResponse3)); } else { //Remove from playlist String elementInPlaylistId = null; for (PlaylistExist playlistExist : peertube.getPlaylistExists()) { if (playlistExist.getPlaylistId().compareTo(ownerPlaylists.get(which).getId()) == 0) { elementInPlaylistId = playlistExist.getPlaylistElementId(); } } playlistsViewModel.manage(PlaylistsVM.action.DELETE_VIDEOS, ownerPlaylists.get(which), elementInPlaylistId); playlists.remove(peertube.getId()); } }); builder.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss()); androidx.appcompat.app.AlertDialog dialog = builder.create(); dialog.show(); } } public void manageVIewPlaylist(APIResponse apiResponse) { if (apiResponse.getError() != null || apiResponse.getVideoExistPlaylist() == null) { return; } if (playlists == null) { playlists = new HashMap<>(); } playlists.putAll(apiResponse.getVideoExistPlaylist()); peertube.setPlaylistExists(playlists.get(peertube.getId())); } public void addElement(String playlistId, String videoId, APIResponse apiResponse) { if (apiResponse != null && apiResponse.getActionReturn() != null) { PlaylistExist playlistExist = new PlaylistExist(); playlistExist.setPlaylistId(playlistId); playlistExist.setPlaylistElementId(apiResponse.getActionReturn()); List playlistExistList = playlists.get(videoId); if (playlistExistList == null) { playlistExistList = new ArrayList<>(); } playlistExistList.add(playlistExist); playlists.put(videoId, playlistExistList); } } @Override public void onAllCommentRemoved() { no_action_text.setVisibility(View.VISIBLE); } }