quick skip and playback speed media controls

This commit is contained in:
Akinwale Ariwodola 2020-05-22 07:31:47 +01:00
parent b901ff7b0d
commit 7879ab738b
14 changed files with 211 additions and 34 deletions

View file

@ -13,7 +13,9 @@ import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.view.ContextMenu;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowManager; import android.view.WindowManager;
@ -37,6 +39,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.github.chrisbanes.photoview.PhotoView; import com.github.chrisbanes.photoview.PhotoView;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.database.ExoDatabaseProvider; import com.google.android.exoplayer2.database.ExoDatabaseProvider;
@ -125,6 +128,8 @@ import io.lbry.lbrysdk.Utils;
public class FileViewFragment extends BaseFragment implements public class FileViewFragment extends BaseFragment implements
MainActivity.BackPressInterceptor, DownloadActionListener, FetchClaimsListener, SdkStatusListener, StoragePermissionListener { MainActivity.BackPressInterceptor, DownloadActionListener, FetchClaimsListener, SdkStatusListener, StoragePermissionListener {
private static final int RELATED_CONTENT_SIZE = 16; private static final int RELATED_CONTENT_SIZE = 16;
private static final String DEFAULT_PLAYBACK_SPEED = "1x";
private PlayerControlView castControlView; private PlayerControlView castControlView;
private Player currentPlayer; private Player currentPlayer;
private boolean loadingNewClaim; private boolean loadingNewClaim;
@ -791,7 +796,28 @@ public class FileViewFragment extends BaseFragment implements
} }
}); });
root.findViewById(R.id.player_toggle_fullscreen).setOnClickListener(new View.OnClickListener() { PlayerView playerView = root.findViewById(R.id.file_view_exoplayer_view);
View playbackSpeedContainer = playerView.findViewById(R.id.player_playback_speed);
TextView textPlaybackSpeed = playerView.findViewById(R.id.player_playback_speed_label);
textPlaybackSpeed.setText(DEFAULT_PLAYBACK_SPEED);
playbackSpeedContainer.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
Helper.buildPlaybackSpeedMenu(contextMenu);
}
});
playbackSpeedContainer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Context context = getContext();
if (context instanceof MainActivity) {
((MainActivity) context).openContextMenu(playbackSpeedContainer);
}
}
});
playerView.findViewById(R.id.player_toggle_fullscreen).setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
// check full screen mode // check full screen mode
@ -802,6 +828,22 @@ public class FileViewFragment extends BaseFragment implements
} }
} }
}); });
playerView.findViewById(R.id.player_skip_back_10).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (MainActivity.appPlayer != null) {
MainActivity.appPlayer.seekTo(Math.max(0, MainActivity.appPlayer.getCurrentPosition() - 10000));
}
}
});
playerView.findViewById(R.id.player_skip_forward_10).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (MainActivity.appPlayer != null) {
MainActivity.appPlayer.seekTo(MainActivity.appPlayer.getCurrentPosition() + 10000);
}
}
});
root.findViewById(R.id.file_view_publisher_name).setOnClickListener(new View.OnClickListener() { root.findViewById(R.id.file_view_publisher_name).setOnClickListener(new View.OnClickListener() {
@Override @Override
@ -1667,8 +1709,10 @@ public class FileViewFragment extends BaseFragment implements
((ViewGroup) exoplayerContainer.getParent()).removeView(exoplayerContainer); ((ViewGroup) exoplayerContainer.getParent()).removeView(exoplayerContainer);
globalLayout.addView(exoplayerContainer); globalLayout.addView(exoplayerContainer);
View playerView = root.findViewById(R.id.file_view_exoplayer_view);
((ImageView) playerView.findViewById(R.id.player_image_full_screen_toggle)).setImageResource(R.drawable.ic_fullscreen_exit);
root.findViewById(R.id.player_image_full_screen_toggle).setVisibility(View.GONE); root.findViewById(R.id.player_image_full_screen_toggle).setVisibility(View.GONE);
root.findViewById(R.id.player_image_full_screen_exit_toggle).setVisibility(View.VISIBLE);
MainActivity activity = (MainActivity) context; MainActivity activity = (MainActivity) context;
activity.enterFullScreenMode(); activity.enterFullScreenMode();
@ -1691,8 +1735,8 @@ public class FileViewFragment extends BaseFragment implements
((ViewGroup) exoplayerContainer.getParent()).removeView(exoplayerContainer); ((ViewGroup) exoplayerContainer.getParent()).removeView(exoplayerContainer);
mediaContainer.addView(exoplayerContainer); mediaContainer.addView(exoplayerContainer);
root.findViewById(R.id.player_image_full_screen_toggle).setVisibility(View.VISIBLE); View playerView = root.findViewById(R.id.file_view_exoplayer_view);
root.findViewById(R.id.player_image_full_screen_exit_toggle).setVisibility(View.GONE); ((ImageView) playerView.findViewById(R.id.player_image_full_screen_toggle)).setImageResource(R.drawable.ic_fullscreen);
exoplayerContainer.setPadding(0, 0, 0, 0); exoplayerContainer.setPadding(0, 0, 0, 0);
activity.exitFullScreenMode(); activity.exitFullScreenMode();
@ -1750,6 +1794,8 @@ public class FileViewFragment extends BaseFragment implements
if (MainActivity.appPlayer != null) { if (MainActivity.appPlayer != null) {
MainActivity.appPlayer.removeListener(fileViewPlayerListener); MainActivity.appPlayer.removeListener(fileViewPlayerListener);
PlaybackParameters params = new PlaybackParameters(1.0f);
MainActivity.appPlayer.setPlaybackParameters(params);
} }
} }
@ -2021,6 +2067,23 @@ public class FileViewFragment extends BaseFragment implements
} }
} }
@Override
public boolean onContextItemSelected(MenuItem item) {
View root = getView();
if (root != null) {
float speed = item.getItemId() / 100.0f;
String speedString = String.format("%sx", new DecimalFormat("0.##").format(speed));
PlayerView playerView = root.findViewById(R.id.file_view_exoplayer_view);
((TextView) playerView.findViewById(R.id.player_playback_speed_label)).setText(speedString);
if (MainActivity.appPlayer != null) {
PlaybackParameters params = new PlaybackParameters(speed);
MainActivity.appPlayer.setPlaybackParameters(params);
}
}
return true;
}
@Override @Override
public boolean shouldHideGlobalPlayer() { public boolean shouldHideGlobalPlayer() {
return true; return true;

View file

@ -26,12 +26,10 @@ import com.google.android.material.snackbar.Snackbar;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.nio.channels.AsynchronousChannel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import io.lbry.browser.MainActivity; import io.lbry.browser.MainActivity;
import io.lbry.browser.R; import io.lbry.browser.R;
@ -42,20 +40,16 @@ import io.lbry.browser.listener.SdkStatusListener;
import io.lbry.browser.listener.SelectionModeListener; import io.lbry.browser.listener.SelectionModeListener;
import io.lbry.browser.model.Claim; import io.lbry.browser.model.Claim;
import io.lbry.browser.model.LbryFile; import io.lbry.browser.model.LbryFile;
import io.lbry.browser.model.NavMenuItem;
import io.lbry.browser.model.ViewHistory; import io.lbry.browser.model.ViewHistory;
import io.lbry.browser.tasks.claim.AbandonChannelTask;
import io.lbry.browser.tasks.claim.AbandonHandler;
import io.lbry.browser.tasks.claim.ClaimListResultHandler; import io.lbry.browser.tasks.claim.ClaimListResultHandler;
import io.lbry.browser.tasks.claim.ClaimListTask;
import io.lbry.browser.tasks.claim.ClaimSearchResultHandler; import io.lbry.browser.tasks.claim.ClaimSearchResultHandler;
import io.lbry.browser.tasks.claim.PurchaseListTask; import io.lbry.browser.tasks.claim.PurchaseListTask;
import io.lbry.browser.tasks.claim.ResolveTask; import io.lbry.browser.tasks.claim.ResolveTask;
import io.lbry.browser.tasks.file.BulkDeleteFilesTask; import io.lbry.browser.tasks.file.BulkDeleteFilesTask;
import io.lbry.browser.tasks.file.DeleteFileTask;
import io.lbry.browser.tasks.file.FileListTask; import io.lbry.browser.tasks.file.FileListTask;
import io.lbry.browser.tasks.localdata.FetchViewHistoryTask; import io.lbry.browser.tasks.localdata.FetchViewHistoryTask;
import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.ui.BaseFragment;
import io.lbry.browser.ui.channel.ChannelFormFragment;
import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.Lbry;
import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.LbryAnalytics;
@ -85,6 +79,7 @@ public class LibraryFragment extends BaseFragment implements
private Date lastDate; private Date lastDate;
private boolean listReachedEnd; private boolean listReachedEnd;
private boolean contentListLoading; private boolean contentListLoading;
private boolean initialOwnClaimsFetched;
private CardView cardStats; private CardView cardStats;
private TextView linkStats; private TextView linkStats;
@ -252,6 +247,8 @@ public class LibraryFragment extends BaseFragment implements
showDownloads(); showDownloads();
} else if (currentFilter == FILTER_HISTORY) { } else if (currentFilter == FILTER_HISTORY) {
showHistory(); showHistory();
} else if (currentFilter == FILTER_PURCHASES) {
showPurchases();
} }
} }
@ -271,9 +268,41 @@ public class LibraryFragment extends BaseFragment implements
layoutSdkInitializing.setVisibility(Lbry.SDK_READY ? View.GONE : View.VISIBLE); layoutSdkInitializing.setVisibility(Lbry.SDK_READY ? View.GONE : View.VISIBLE);
currentPage = 1; currentPage = 1;
if (Lbry.SDK_READY) { if (Lbry.SDK_READY) {
if (!initialOwnClaimsFetched) {
fetchOwnClaimsAndShowDownloads();
} else {
fetchDownloads(); fetchDownloads();
} }
} }
}
private void fetchOwnClaimsAndShowDownloads() {
if (Lbry.ownClaims != null && Lbry.ownClaims.size() > 0) {
initialOwnClaimsFetched = true;
fetchDownloads();
return;
}
linkStats.setVisibility(View.INVISIBLE);
ClaimListTask task = new ClaimListTask(Arrays.asList(Claim.TYPE_STREAM, Claim.TYPE_REPOST), listLoading, new ClaimListResultHandler() {
@Override
public void onSuccess(List<Claim> claims) {
Lbry.ownClaims = Helper.filterDeletedClaims(new ArrayList<>(claims));
initialOwnClaimsFetched = true;
if (currentFilter == FILTER_DOWNLOADS) {
fetchDownloads();
}
checkStatsLink();
}
@Override
public void onError(Exception error) {
initialOwnClaimsFetched = true;
checkStatsLink();
}
});
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void showPurchases() { private void showPurchases() {
currentFilter = FILTER_PURCHASES; currentFilter = FILTER_PURCHASES;

View file

@ -18,6 +18,7 @@ import android.os.Environment;
import android.provider.DocumentsContract; import android.provider.DocumentsContract;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.view.ContextMenu;
import android.view.View; import android.view.View;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ProgressBar; import android.widget.ProgressBar;
@ -77,6 +78,8 @@ public final class Helper {
public static final SimpleDateFormat FILESTAMP_FORMAT = new SimpleDateFormat("yyyyMMdd_HHmmss"); public static final SimpleDateFormat FILESTAMP_FORMAT = new SimpleDateFormat("yyyyMMdd_HHmmss");
public static final String EXPLORER_TX_PREFIX = "https://explorer.lbry.com/tx"; public static final String EXPLORER_TX_PREFIX = "https://explorer.lbry.com/tx";
public static final List<Double> PLAYBACK_SPEEDS = Arrays.asList(0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0);
public static boolean isNull(String value) { public static boolean isNull(String value) {
return value == null; return value == null;
} }
@ -84,6 +87,14 @@ public final class Helper {
return value == null || value.trim().length() == 0; return value == null || value.trim().length() == 0;
} }
public static void buildPlaybackSpeedMenu(ContextMenu menu) {
int order = 0;
DecimalFormat formatter = new DecimalFormat("0.##");
for (Double speed : PLAYBACK_SPEEDS) {
menu.add(0, Double.valueOf(speed * 100).intValue(), ++order, String.format("%sx", formatter.format(speed)));
}
}
public static String capitalize(String value) { public static String capitalize(String value) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
boolean capitalizeNext = true; boolean capitalizeNext = true;

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF"
android:alpha="0.8">
<path
android:fillColor="#FF000000"
android:pathData="M4,13c0,4.4 3.6,8 8,8s8,-3.6 8,-8h-2c0,3.3 -2.7,6 -6,6s-6,-2.7 -6,-6 2.7,-6 6,-6v4l5,-5 -5,-5v4c-4.4,0 -8,3.6 -8,8zM10.8,16L10,16v-3.3L9,13v-0.7l1.8,-0.6h0.1L10.9,16zM15.1,14.2c0,0.3 0,0.6 -0.1,0.8l-0.3,0.6s-0.3,0.3 -0.5,0.3 -0.4,0.1 -0.6,0.1 -0.4,0 -0.6,-0.1 -0.3,-0.2 -0.5,-0.3 -0.2,-0.3 -0.3,-0.6 -0.1,-0.5 -0.1,-0.8v-0.7c0,-0.3 0,-0.6 0.1,-0.8l0.3,-0.6s0.3,-0.3 0.5,-0.3 0.4,-0.1 0.6,-0.1 0.4,0 0.6,0.1 0.3,0.2 0.5,0.3 0.2,0.3 0.3,0.6 0.1,0.5 0.1,0.8v0.7zM14.3,13.4v-0.5s-0.1,-0.2 -0.1,-0.3 -0.1,-0.1 -0.2,-0.2 -0.2,-0.1 -0.3,-0.1 -0.2,0 -0.3,0.1l-0.2,0.2s-0.1,0.2 -0.1,0.3v2s0.1,0.2 0.1,0.3 0.1,0.1 0.2,0.2 0.2,0.1 0.3,0.1 0.2,0 0.3,-0.1l0.2,-0.2s0.1,-0.2 0.1,-0.3v-1.5z"/>
</vector>

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF"
android:alpha="0.8">
<path
android:fillColor="#FF000000"
android:pathData="M12,5L12,1L7,6l5,5L12,7c3.3,0 6,2.7 6,6s-2.7,6 -6,6 -6,-2.7 -6,-6L4,13c0,4.4 3.6,8 8,8s8,-3.6 8,-8 -3.6,-8 -8,-8zM10.9,16L10,16v-3.3L9,13v-0.7l1.8,-0.6h0.1L10.9,16zM15.2,14.2c0,0.3 0,0.6 -0.1,0.8l-0.3,0.6s-0.3,0.3 -0.5,0.3 -0.4,0.1 -0.6,0.1 -0.4,0 -0.6,-0.1 -0.3,-0.2 -0.5,-0.3 -0.2,-0.3 -0.3,-0.6 -0.1,-0.5 -0.1,-0.8v-0.7c0,-0.3 0,-0.6 0.1,-0.8l0.3,-0.6s0.3,-0.3 0.5,-0.3 0.4,-0.1 0.6,-0.1 0.4,0 0.6,0.1c0.2,0.1 0.3,0.2 0.5,0.3s0.2,0.3 0.3,0.6 0.1,0.5 0.1,0.8v0.7zM14.3,13.4v-0.5s-0.1,-0.2 -0.1,-0.3 -0.1,-0.1 -0.2,-0.2 -0.2,-0.1 -0.3,-0.1 -0.2,0 -0.3,0.1l-0.2,0.2s-0.1,0.2 -0.1,0.3v2s0.1,0.2 0.1,0.3 0.1,0.1 0.2,0.2 0.2,0.1 0.3,0.1 0.2,0 0.3,-0.1l0.2,-0.2s0.1,-0.2 0.1,-0.3v-1.5z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 887 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

View file

@ -6,19 +6,56 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/player_skip_back_10"
android:clickable="true"
android:focusable="true"
android:layout_centerVertical="true"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_toLeftOf="@id/player_play_pause">
<ImageView
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_centerInParent="true"
android:src="@drawable/ic_replay_10"
android:tint="@color/white" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/player_play_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<ImageButton <ImageButton
android:id="@id/exo_play" android:id="@id/exo_play"
android:layout_width="96dp" android:layout_width="72dp"
android:layout_height="96dp" android:layout_height="72dp"
android:layout_centerInParent="true"
style="@style/ExoMediaButton.Play" /> style="@style/ExoMediaButton.Play" />
<ImageButton <ImageButton
android:id="@id/exo_pause" android:id="@id/exo_pause"
android:layout_width="96dp" android:layout_width="72dp"
android:layout_height="96dp" android:layout_height="72dp"
android:layout_centerInParent="true"
style="@style/ExoMediaButton.Pause" /> style="@style/ExoMediaButton.Pause" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/player_skip_forward_10"
android:clickable="true"
android:focusable="true"
android:layout_centerVertical="true"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_toRightOf="@id/player_play_pause">
<ImageView
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_centerInParent="true"
android:src="@drawable/ic_forward_10"
android:tint="@color/white" />
</RelativeLayout>
<RelativeLayout <RelativeLayout
android:id="@+id/player_toggle_cast" android:id="@+id/player_toggle_cast"
@ -38,9 +75,30 @@
android:tint="@color/white" /> android:tint="@color/white" />
</RelativeLayout> </RelativeLayout>
<RelativeLayout
android:id="@+id/player_playback_speed"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:layout_alignParentBottom="true"
android:layout_width="64dp"
android:layout_height="48dp"
android:layout_toLeftOf="@id/player_toggle_fullscreen">
<TextView
android:id="@+id/player_playback_speed_label"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter"
android:singleLine="true"
android:textColor="@color/white"
android:textSize="14sp" />
</RelativeLayout>
<RelativeLayout <RelativeLayout
android:id="@+id/player_toggle_fullscreen" android:id="@+id/player_toggle_fullscreen"
android:clickable="true" android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
@ -53,21 +111,13 @@
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:src="@drawable/ic_fullscreen" android:src="@drawable/ic_fullscreen"
android:tint="@color/white" /> android:tint="@color/white" />
<ImageView
android:id="@+id/player_image_full_screen_exit_toggle"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerInParent="true"
android:src="@drawable/ic_fullscreen_exit"
android:tint="@color/white"
android:visibility="gone" />
</RelativeLayout> </RelativeLayout>
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_toLeftOf="@id/player_toggle_fullscreen" android:layout_toLeftOf="@id/player_playback_speed"
android:paddingLeft="16dp" android:paddingLeft="16dp"
android:paddingRight="4dp"> android:paddingRight="4dp">
<TextView <TextView
@ -76,6 +126,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:fontFamily="@font/inter" android:fontFamily="@font/inter"
android:singleLine="true"
android:text="@string/zero_duration" android:text="@string/zero_duration"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="14sp" /> android:textSize="14sp" />
@ -99,6 +150,7 @@
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:gravity="end" android:gravity="end"
android:fontFamily="@font/inter" android:fontFamily="@font/inter"
android:singleLine="true"
android:text="@string/zero_duration" android:text="@string/zero_duration"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="14sp" /> android:textSize="14sp" />