Surf mode experiment (#1003)
* surf mode implementation * occupy entire vertical area * fix onResume logic * shuffle mode with selected channel ids
This commit is contained in:
parent
722c829502
commit
9b9ef9ab74
13 changed files with 904 additions and 32 deletions
|
@ -188,9 +188,10 @@ import io.lbry.browser.ui.other.AboutFragment;
|
|||
import io.lbry.browser.ui.publish.PublishFormFragment;
|
||||
import io.lbry.browser.ui.publish.PublishFragment;
|
||||
import io.lbry.browser.ui.publish.PublishesFragment;
|
||||
import io.lbry.browser.ui.findcontent.SearchFragment;
|
||||
import io.lbry.browser.ui.other.SettingsFragment;
|
||||
import io.lbry.browser.ui.findcontent.AllContentFragment;
|
||||
import io.lbry.browser.ui.findcontent.SearchFragment;
|
||||
import io.lbry.browser.ui.findcontent.ShuffleFragment;
|
||||
import io.lbry.browser.ui.other.SettingsFragment;
|
||||
import io.lbry.browser.ui.wallet.InvitesFragment;
|
||||
import io.lbry.browser.ui.wallet.RewardsFragment;
|
||||
import io.lbry.browser.ui.wallet.WalletFragment;
|
||||
|
@ -214,6 +215,9 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
private static final String SPECIAL_URL_PREFIX = "lbry://?";
|
||||
private static final int REMOTE_NOTIFICATION_REFRESH_TTL = 300000; // 5 minutes
|
||||
public static final String SKU_SKIP = "lbryskip";
|
||||
|
||||
public static final int SOURCE_NOW_PLAYING_FILE = 1;
|
||||
public static final int SOURCE_NOW_PLAYING_SHUFFLE = 2;
|
||||
public static MainActivity instance;
|
||||
|
||||
private boolean shuttingDown;
|
||||
|
@ -232,6 +236,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
public static boolean playerReassigned;
|
||||
public CastContext castContext;
|
||||
public static CastPlayer castPlayer;
|
||||
public static int nowPlayingSource;
|
||||
public static Claim nowPlayingClaim;
|
||||
public static String nowPlayingClaimUrl;
|
||||
public static boolean startingFilePickerActivity = false;
|
||||
|
@ -264,6 +269,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
fragmentClassNavIdMap.put(FollowingFragment.class, NavMenuItem.ID_ITEM_FOLLOWING);
|
||||
fragmentClassNavIdMap.put(EditorsChoiceFragment.class, NavMenuItem.ID_ITEM_EDITORS_CHOICE);
|
||||
fragmentClassNavIdMap.put(AllContentFragment.class, NavMenuItem.ID_ITEM_ALL_CONTENT);
|
||||
fragmentClassNavIdMap.put(ShuffleFragment.class, NavMenuItem.ID_ITEM_SHUFFLE);
|
||||
|
||||
fragmentClassNavIdMap.put(PublishFragment.class, NavMenuItem.ID_ITEM_NEW_PUBLISH);
|
||||
fragmentClassNavIdMap.put(ChannelManagerFragment.class, NavMenuItem.ID_ITEM_CHANNELS);
|
||||
|
@ -580,9 +586,13 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
public void onClick(View view) {
|
||||
if (nowPlayingClaim != null && !Helper.isNullOrEmpty(nowPlayingClaimUrl)) {
|
||||
hideNotifications();
|
||||
if (nowPlayingSource == SOURCE_NOW_PLAYING_SHUFFLE) {
|
||||
openFragment(ShuffleFragment.class, true, NavMenuItem.ID_ITEM_SHUFFLE);
|
||||
} else {
|
||||
openFileUrl(nowPlayingClaimUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// display custom navigation menu
|
||||
|
@ -658,6 +668,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
specialRouteFragmentClassMap.put("settings", SettingsFragment.class);
|
||||
specialRouteFragmentClassMap.put("subscription", FollowingFragment.class);
|
||||
specialRouteFragmentClassMap.put("subscriptions", FollowingFragment.class);
|
||||
specialRouteFragmentClassMap.put("surf", ShuffleFragment.class);
|
||||
specialRouteFragmentClassMap.put("wallet", WalletFragment.class);
|
||||
specialRouteFragmentClassMap.put("discover", FollowingFragment.class);
|
||||
}
|
||||
|
@ -813,6 +824,9 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
case NavMenuItem.ID_ITEM_ALL_CONTENT:
|
||||
openFragment(AllContentFragment.class, true, NavMenuItem.ID_ITEM_ALL_CONTENT);
|
||||
break;
|
||||
case NavMenuItem.ID_ITEM_SHUFFLE:
|
||||
openFragment(ShuffleFragment.class, true, NavMenuItem.ID_ITEM_SHUFFLE);
|
||||
break;
|
||||
|
||||
case NavMenuItem.ID_ITEM_NEW_PUBLISH:
|
||||
openFragment(PublishFragment.class, true, NavMenuItem.ID_ITEM_NEW_PUBLISH);
|
||||
|
@ -1027,7 +1041,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
fragment instanceof LibraryFragment ||
|
||||
fragment instanceof SearchFragment;
|
||||
findViewById(R.id.floating_balance_main_container).setVisibility(!canShowFloatingBalance || inFullscreenMode ? View.INVISIBLE : View.VISIBLE);
|
||||
if (!(fragment instanceof FileViewFragment) && !inFullscreenMode) {
|
||||
if (!(fragment instanceof FileViewFragment) && !(fragment instanceof ShuffleFragment) && !inFullscreenMode) {
|
||||
findViewById(R.id.global_now_playing_card).setVisibility(View.VISIBLE);
|
||||
}
|
||||
/*if (!Lbry.SDK_READY && !inFullscreenMode) {
|
||||
|
@ -3025,7 +3039,8 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
findContentGroup.setItems(Arrays.asList(
|
||||
new NavMenuItem(NavMenuItem.ID_ITEM_FOLLOWING, R.string.fa_heart, R.string.following, "Following", context),
|
||||
new NavMenuItem(NavMenuItem.ID_ITEM_EDITORS_CHOICE, R.string.fa_star, R.string.editors_choice, "EditorsChoice", context),
|
||||
new NavMenuItem(NavMenuItem.ID_ITEM_ALL_CONTENT, R.string.fa_globe_americas, R.string.all_content, "AllContent", context)
|
||||
new NavMenuItem(NavMenuItem.ID_ITEM_ALL_CONTENT, R.string.fa_globe_americas, R.string.all_content, "AllContent", context),
|
||||
new NavMenuItem(NavMenuItem.ID_ITEM_SHUFFLE, R.string.fa_random, R.string.shuffle, "Shuffle",context)
|
||||
));
|
||||
|
||||
yourContentGroup.setItems(Arrays.asList(
|
||||
|
|
|
@ -20,6 +20,7 @@ public class NavMenuItem {
|
|||
public static final int ID_ITEM_FOLLOWING = 101;
|
||||
public static final int ID_ITEM_EDITORS_CHOICE = 102;
|
||||
public static final int ID_ITEM_ALL_CONTENT = 103;
|
||||
public static final int ID_ITEM_SHUFFLE = 104;
|
||||
|
||||
// Your Content
|
||||
public static final int ID_ITEM_CHANNELS = 201;
|
||||
|
|
|
@ -51,8 +51,8 @@ public class CommentListTask extends AsyncTask<Void, Void, List<Comment>> {
|
|||
options.put("skip_validation", true);
|
||||
options.put("visible", true);
|
||||
|
||||
|
||||
JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_COMMENT_LIST, options);
|
||||
android.util.Log.d("LbryMain", result.toString(2));
|
||||
JSONArray items = result.getJSONArray("items");
|
||||
|
||||
List<Comment> children = new ArrayList<>();
|
||||
|
@ -78,6 +78,7 @@ public class CommentListTask extends AsyncTask<Void, Void, List<Comment>> {
|
|||
}
|
||||
} catch (Exception ex) {
|
||||
error = ex;
|
||||
android.util.Log.d("LbryMain", ex.toString(), ex);
|
||||
}
|
||||
return comments;
|
||||
}
|
||||
|
|
|
@ -227,6 +227,8 @@ public class ChannelContentFragment extends Fragment implements DownloadActionLi
|
|||
null,
|
||||
getContentSortOrder(),
|
||||
contentReleaseTime,
|
||||
0,
|
||||
0,
|
||||
currentClaimSearchPage == 0 ? 1 : currentClaimSearchPage,
|
||||
Helper.CONTENT_PAGE_SIZE);
|
||||
}
|
||||
|
|
|
@ -414,6 +414,8 @@ public class AllContentFragment extends BaseFragment implements DownloadActionLi
|
|||
null,
|
||||
getContentSortOrder(),
|
||||
contentReleaseTime,
|
||||
0,
|
||||
0,
|
||||
currentClaimSearchPage == 0 ? 1 : currentClaimSearchPage,
|
||||
Helper.CONTENT_PAGE_SIZE);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import android.content.Intent;
|
|||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Outline;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
|
@ -37,7 +36,6 @@ import androidx.annotation.NonNull;
|
|||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.AppCompatSpinner;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.widget.NestedScrollView;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
@ -167,7 +165,7 @@ public class FileViewFragment extends BaseFragment implements
|
|||
WalletBalanceListener {
|
||||
private static final int RELATED_CONTENT_SIZE = 16;
|
||||
private static final String DEFAULT_PLAYBACK_SPEED = "1x";
|
||||
private static final String CDN_PREFIX = "https://cdn.lbryplayer.xyz";
|
||||
public static final String CDN_PREFIX = "https://cdn.lbryplayer.xyz";
|
||||
|
||||
private PlayerControlView castControlView;
|
||||
private Player currentPlayer;
|
||||
|
@ -741,6 +739,13 @@ public class FileViewFragment extends BaseFragment implements
|
|||
}
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
if (MainActivity.appPlayer != null) {
|
||||
MainActivity.nowPlayingSource = MainActivity.SOURCE_NOW_PLAYING_FILE;
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
Context context = getContext();
|
||||
|
@ -2781,7 +2786,7 @@ public class FileViewFragment extends BaseFragment implements
|
|||
}
|
||||
}
|
||||
|
||||
private static class StreamLoadErrorPolicy extends DefaultLoadErrorHandlingPolicy {
|
||||
public static class StreamLoadErrorPolicy extends DefaultLoadErrorHandlingPolicy {
|
||||
@Override
|
||||
public long getRetryDelayMsFor(int dataType, long loadDurationMs, IOException exception, int errorCount) {
|
||||
return exception instanceof ParserException
|
||||
|
|
|
@ -434,6 +434,8 @@ public class FollowingFragment extends BaseFragment implements
|
|||
null,
|
||||
getContentSortOrder(),
|
||||
contentReleaseTime,
|
||||
0,
|
||||
0,
|
||||
currentClaimSearchPage == 0 ? 1 : currentClaimSearchPage,
|
||||
Helper.CONTENT_PAGE_SIZE);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,748 @@
|
|||
package io.lbry.browser.ui.findcontent;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes;
|
||||
import com.google.android.exoplayer2.database.ExoDatabaseProvider;
|
||||
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;
|
||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.lbry.browser.MainActivity;
|
||||
import io.lbry.browser.R;
|
||||
import io.lbry.browser.exceptions.LbryUriException;
|
||||
import io.lbry.browser.model.Claim;
|
||||
import io.lbry.browser.model.lbryinc.Reward;
|
||||
import io.lbry.browser.tasks.BufferEventTask;
|
||||
import io.lbry.browser.tasks.GenericTaskHandler;
|
||||
import io.lbry.browser.tasks.claim.ClaimSearchResultHandler;
|
||||
import io.lbry.browser.tasks.claim.ClaimSearchTask;
|
||||
import io.lbry.browser.tasks.lbryinc.ClaimRewardTask;
|
||||
import io.lbry.browser.tasks.lbryinc.LogFileViewTask;
|
||||
import io.lbry.browser.ui.BaseFragment;
|
||||
import io.lbry.browser.utils.Helper;
|
||||
import io.lbry.browser.utils.Lbry;
|
||||
import io.lbry.browser.utils.LbryAnalytics;
|
||||
import io.lbry.browser.utils.LbryUri;
|
||||
import io.lbry.browser.utils.Lbryio;
|
||||
import io.lbry.browser.utils.Predefined;
|
||||
|
||||
public class ShuffleFragment extends BaseFragment {
|
||||
|
||||
private static final int PAGE_SIZE = 50;
|
||||
|
||||
private int currentClaimSearchPage;
|
||||
private int playlistIndex;
|
||||
private Claim current;
|
||||
private List<Claim> playlist;
|
||||
|
||||
private long sessionStart;
|
||||
private ProgressBar surfModeLoading;
|
||||
private TextView textTitle;
|
||||
private TextView textPublisher;
|
||||
private Player player;
|
||||
private long elapsedDuration = 0;
|
||||
private long totalDuration = 0;
|
||||
private boolean elapsedPlaybackScheduled;
|
||||
private ScheduledExecutorService elapsedPlaybackScheduler;
|
||||
private boolean playbackStarted;
|
||||
private long startTimeMillis;
|
||||
private boolean isPlaying;
|
||||
private boolean newPlayerCreated;
|
||||
private String currentUrl;
|
||||
private Player.EventListener playerListener;
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
View root = inflater.inflate(R.layout.fragment_shuffle, container, false);
|
||||
|
||||
surfModeLoading = root.findViewById(R.id.shuffle_loading);
|
||||
textTitle = root.findViewById(R.id.shuffle_content_title);
|
||||
textPublisher = root.findViewById(R.id.shuffle_content_publisher);
|
||||
playerListener = new Player.EventListener() {
|
||||
@Override
|
||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||
if (playbackState == Player.STATE_READY) {
|
||||
elapsedDuration = MainActivity.appPlayer.getCurrentPosition();
|
||||
totalDuration = MainActivity.appPlayer.getDuration() < 0 ? 0 : MainActivity.appPlayer.getDuration();
|
||||
if (!playbackStarted) {
|
||||
logPlay(currentUrl, startTimeMillis);
|
||||
playbackStarted = true;
|
||||
isPlaying = true;
|
||||
}
|
||||
|
||||
renderTotalDuration();
|
||||
scheduleElapsedPlayback();
|
||||
hideBuffering();
|
||||
} else if (playbackState == Player.STATE_BUFFERING) {
|
||||
Context ctx = getContext();
|
||||
boolean sendBufferingEvents = true;
|
||||
|
||||
if (ctx != null) {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||
sendBufferingEvents = sp.getBoolean(MainActivity.PREFERENCE_KEY_SEND_BUFFERING_EVENTS, true);
|
||||
}
|
||||
|
||||
if (MainActivity.appPlayer != null && MainActivity.appPlayer.getCurrentPosition() > 0 && sendBufferingEvents) {
|
||||
// we only want to log a buffer event after the media has already started playing
|
||||
String mediaSourceUrl = getStreamingUrl();
|
||||
long duration = MainActivity.appPlayer.getDuration();
|
||||
long position = MainActivity.appPlayer.getCurrentPosition();
|
||||
// TODO: Determine a hash for the userId
|
||||
String userIdHash = Helper.SHA256(Lbryio.currentUser != null ? String.valueOf(Lbryio.currentUser.getId()) : "0");
|
||||
if (mediaSourceUrl.startsWith(FileViewFragment.CDN_PREFIX)) {
|
||||
BufferEventTask bufferEvent = new BufferEventTask(current.getPermanentUrl(), duration, position, 1, userIdHash);
|
||||
bufferEvent.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
||||
showBuffering();
|
||||
} else if (playbackState == Player.STATE_ENDED) {
|
||||
playNextClaim();
|
||||
} else {
|
||||
hideBuffering();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Context context = getContext();
|
||||
PlayerView playerView = root.findViewById(R.id.shuffle_exoplayer_view);
|
||||
playerView.setOnTouchListener(new SwipeListener(playerView, context) {
|
||||
@Override
|
||||
public void onSwipeLeft() { playNextClaim(); }
|
||||
@Override
|
||||
public void onSwipeRight() { playPreviousClaim(); }
|
||||
});
|
||||
|
||||
root.findViewById(R.id.shuffle_share_button).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (current != null) {
|
||||
try {
|
||||
String shareUrl = LbryUri.parse(
|
||||
!Helper.isNullOrEmpty(current.getCanonicalUrl()) ? current.getCanonicalUrl() :
|
||||
(!Helper.isNullOrEmpty(current.getShortUrl()) ? current.getShortUrl() : current.getPermanentUrl())).toTvString();
|
||||
Intent shareIntent = new Intent();
|
||||
shareIntent.setAction(Intent.ACTION_SEND);
|
||||
shareIntent.setType("text/plain");
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, shareUrl);
|
||||
|
||||
MainActivity.startingShareActivity = true;
|
||||
Intent shareUrlIntent = Intent.createChooser(shareIntent, getString(R.string.share_lbry_content));
|
||||
shareUrlIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(shareUrlIntent);
|
||||
} catch (LbryUriException ex) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
root.setOnTouchListener(new SwipeListener(root, context) {
|
||||
@Override
|
||||
public void onSwipeLeft() { playNextClaim(); }
|
||||
@Override
|
||||
public void onSwipeRight() { playPreviousClaim(); }
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private String getStreamingUrl() {
|
||||
return current != null ? String.format("https://cdn.lbryplayer.xyz/content/claims/%s/%s/stream", current.getName(), current.getClaimId()) : "";
|
||||
}
|
||||
|
||||
private Map<String, Object> buildContentOptions() {
|
||||
Context context = getContext();
|
||||
boolean canShowMatureContent = false;
|
||||
if (context != null) {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
canShowMatureContent = sp.getBoolean(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT, false);
|
||||
}
|
||||
|
||||
return Lbry.buildClaimSearchOptions(
|
||||
Claim.TYPE_STREAM,
|
||||
canShowMatureContent ? null : new ArrayList<>(Predefined.MATURE_TAGS),
|
||||
null/*contentChannelIds*/,
|
||||
Arrays.asList(Claim.ORDER_BY_TRENDING_GROUP),
|
||||
121, // 2 minutes or less
|
||||
1,
|
||||
currentClaimSearchPage == 0 ? 1 : currentClaimSearchPage,
|
||||
PAGE_SIZE);
|
||||
}
|
||||
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
MainActivity activity = (MainActivity) getContext();
|
||||
if (activity != null) {
|
||||
activity.hideFloatingWalletBalance();
|
||||
}
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
sessionStart = System.currentTimeMillis();
|
||||
MainActivity activity = (MainActivity) getContext();
|
||||
if (activity != null) {
|
||||
LbryAnalytics.setCurrentScreen(activity, "Shuffle", "Shuffle");
|
||||
}
|
||||
if (MainActivity.appPlayer != null && MainActivity.nowPlayingSource != MainActivity.SOURCE_NOW_PLAYING_SHUFFLE) {
|
||||
MainActivity.appPlayer.setPlayWhenReady(false);
|
||||
}
|
||||
|
||||
if (playlist == null) {
|
||||
loadContent();
|
||||
} else {
|
||||
if (current != null) {
|
||||
playbackCurrentClaim();
|
||||
} else {
|
||||
startPlaylist();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
if (MainActivity.appPlayer != null && MainActivity.appPlayer.isPlaying()) {
|
||||
MainActivity.nowPlayingSource = MainActivity.SOURCE_NOW_PLAYING_SHUFFLE;
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
public void onStop() {
|
||||
long sessionDuration = System.currentTimeMillis() - sessionStart;
|
||||
if (sessionStart > 0 && sessionDuration > 0) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putLong("duration_ms", sessionDuration);
|
||||
bundle.putInt("duration", Double.valueOf(Math.ceil(sessionDuration / 1000.0)).intValue());
|
||||
LbryAnalytics.logEvent(LbryAnalytics.EVENT_SHUFFLE_SESSION, bundle);
|
||||
}
|
||||
sessionStart = 0;
|
||||
|
||||
MainActivity activity = (MainActivity) getContext();
|
||||
if (activity != null) {
|
||||
activity.hideFloatingWalletBalance();
|
||||
|
||||
}
|
||||
|
||||
super.onStop();
|
||||
|
||||
}
|
||||
|
||||
private void loadContent() {
|
||||
if (playlist == null || playlist.size() == 0) {
|
||||
Helper.setViewVisibility(surfModeLoading, View.VISIBLE);
|
||||
}
|
||||
|
||||
Map<String, Object> claimSearchOptions = buildContentOptions();
|
||||
ClaimSearchTask task = new ClaimSearchTask(claimSearchOptions, Lbry.LBRY_TV_CONNECTION_STRING, null, new ClaimSearchResultHandler() {
|
||||
@Override
|
||||
public void onSuccess(List<Claim> claims, boolean hasReachedEnd) {
|
||||
if (playlist == null) {
|
||||
playlist = new ArrayList<>(claims);
|
||||
startPlaylist();
|
||||
} else {
|
||||
for (Claim claim : claims) {
|
||||
if (!playlist.contains(claim)) {
|
||||
playlist.add(claim);
|
||||
}
|
||||
}
|
||||
}
|
||||
Helper.setViewVisibility(surfModeLoading, View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception error) {
|
||||
Helper.setViewVisibility(surfModeLoading, View.GONE);
|
||||
}
|
||||
});
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private void startPlaylist() {
|
||||
if (playlist == null || playlist.size() == 0) {
|
||||
return;
|
||||
}
|
||||
playlistIndex = 0;
|
||||
current = playlist.get(playlistIndex);
|
||||
checkCurrentClaimIsVideo(false);
|
||||
playbackCurrentClaim();
|
||||
}
|
||||
|
||||
private void checkCurrentClaimIsVideo(boolean previous) {
|
||||
while (current == null || current.getMediaType() == null || !current.getMediaType().startsWith("video")) {
|
||||
// only play videos
|
||||
if (previous) {
|
||||
playlistIndex--;
|
||||
} else {
|
||||
playlistIndex++;
|
||||
}
|
||||
current = playlist.get(playlistIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private void playPreviousClaim() {
|
||||
if (playlist == null || playlist.size() == 0) {
|
||||
return;
|
||||
}
|
||||
if (playlistIndex > 0) {
|
||||
playlistIndex--;
|
||||
}
|
||||
current = playlist.get(playlistIndex);
|
||||
checkCurrentClaimIsVideo(true);
|
||||
playbackCurrentClaim();
|
||||
}
|
||||
private void playNextClaim() {
|
||||
if (playlist == null || playlist.size() == 0) {
|
||||
return;
|
||||
}
|
||||
if (playlistIndex < playlist.size() - 1) {
|
||||
playlistIndex++;
|
||||
}
|
||||
if (playlist.size() - playlistIndex < 10) {
|
||||
currentClaimSearchPage++;
|
||||
loadContent();
|
||||
}
|
||||
current = playlist.get(playlistIndex);
|
||||
checkCurrentClaimIsVideo(false);
|
||||
playbackCurrentClaim();
|
||||
}
|
||||
|
||||
private void playbackCurrentClaim() {
|
||||
resetPlayer();
|
||||
String publisherText = !Helper.isNullOrEmpty(current.getPublisherTitle()) ?
|
||||
String.format("%s (%s)", current.getPublisherTitle(), current.getPublisherName()) :
|
||||
!Helper.isNullOrEmpty(current.getPublisherName()) ? current.getPublisherName() : getString(R.string.anonymous);
|
||||
|
||||
textTitle.setText(current.getTitle());
|
||||
textPublisher.setText(publisherText);
|
||||
|
||||
Context context = getContext();
|
||||
if (MainActivity.appPlayer == null && context != null) {
|
||||
AudioAttributes audioAttributes = new AudioAttributes.Builder()
|
||||
.setUsage(C.USAGE_MEDIA)
|
||||
.setContentType(C.CONTENT_TYPE_MOVIE)
|
||||
.build();
|
||||
|
||||
MainActivity.appPlayer = new SimpleExoPlayer.Builder(context).build();
|
||||
MainActivity.appPlayer.setAudioAttributes(audioAttributes, true);
|
||||
MainActivity.playerCache =
|
||||
new SimpleCache(context.getCacheDir(),
|
||||
new LeastRecentlyUsedCacheEvictor(1024 * 1024 * 256), new ExoDatabaseProvider(context));
|
||||
if (context instanceof MainActivity) {
|
||||
MainActivity activity = (MainActivity) context;
|
||||
activity.initMediaSession();
|
||||
activity.initPlaybackNotification();
|
||||
}
|
||||
if (playerListener != null) {
|
||||
MainActivity.appPlayer.addListener(playerListener);
|
||||
}
|
||||
|
||||
newPlayerCreated = true;
|
||||
}
|
||||
|
||||
if (context instanceof MainActivity) {
|
||||
MainActivity activity = (MainActivity) context;
|
||||
activity.initPlaybackNotification();
|
||||
}
|
||||
|
||||
View root = getView();
|
||||
if (root != null) {
|
||||
PlayerView view = root.findViewById(R.id.shuffle_exoplayer_view);
|
||||
view.setShutterBackgroundColor(Color.TRANSPARENT);
|
||||
view.setPlayer(MainActivity.appPlayer);
|
||||
view.setUseController(true);
|
||||
if (context instanceof MainActivity) {
|
||||
((MainActivity) context).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
if (MainActivity.nowPlayingClaim != null &&
|
||||
MainActivity.nowPlayingClaim.getClaimId().equalsIgnoreCase(current.getClaimId()) &&
|
||||
!newPlayerCreated) {
|
||||
// if the claim is already playing, we don't need to reload the media source
|
||||
return;
|
||||
}
|
||||
|
||||
if (MainActivity.appPlayer != null) {
|
||||
showBuffering();
|
||||
if (context instanceof MainActivity) {
|
||||
((MainActivity) context).setNowPlayingClaim(current, current.getPermanentUrl());
|
||||
}
|
||||
|
||||
MainActivity.appPlayer.setPlayWhenReady(true);
|
||||
String userAgent = Util.getUserAgent(context, getString(R.string.app_name));
|
||||
String mediaSourceUrl = getStreamingUrl();
|
||||
MediaSource mediaSource = new ProgressiveMediaSource.Factory(
|
||||
new CacheDataSourceFactory(MainActivity.playerCache, new DefaultDataSourceFactory(context, userAgent)),
|
||||
new DefaultExtractorsFactory()
|
||||
).setLoadErrorHandlingPolicy(new FileViewFragment.StreamLoadErrorPolicy()).createMediaSource(Uri.parse(mediaSourceUrl));
|
||||
|
||||
MainActivity.appPlayer.prepare(mediaSource, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resetPlayer() {
|
||||
elapsedDuration = 0;
|
||||
totalDuration = 0;
|
||||
renderElapsedDuration();
|
||||
renderTotalDuration();
|
||||
|
||||
elapsedPlaybackScheduled = false;
|
||||
if (elapsedPlaybackScheduler != null) {
|
||||
elapsedPlaybackScheduler.shutdownNow();
|
||||
elapsedPlaybackScheduler = null;
|
||||
}
|
||||
|
||||
playbackStarted = false;
|
||||
startTimeMillis = 0;
|
||||
|
||||
/*
|
||||
if (MainActivity.appPlayer != null) {
|
||||
MainActivity.appPlayer.stop(true);
|
||||
MainActivity.appPlayer.removeListener(fileViewPlayerListener);
|
||||
PlaybackParameters params = new PlaybackParameters(1.0f);
|
||||
MainActivity.appPlayer.setPlaybackParameters(params);
|
||||
}*/
|
||||
}
|
||||
|
||||
private void logPlay(String url, long startTimeMillis) {
|
||||
long timeToStartMillis = startTimeMillis > 0 ? System.currentTimeMillis() - startTimeMillis : 0;
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("uri", url);
|
||||
bundle.putLong("time_to_start_ms", timeToStartMillis);
|
||||
bundle.putLong("time_to_start_seconds", Double.valueOf(timeToStartMillis / 1000.0).longValue());
|
||||
LbryAnalytics.logEvent(LbryAnalytics.EVENT_PLAY, bundle);
|
||||
|
||||
logFileView(current.getPermanentUrl(), timeToStartMillis);
|
||||
}
|
||||
|
||||
private void logFileView(String url, long timeToStart) {
|
||||
if (current != null) {
|
||||
LogFileViewTask task = new LogFileViewTask(url, current, timeToStart, new GenericTaskHandler() {
|
||||
@Override
|
||||
public void beforeStart() { }
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
claimEligibleRewards();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception error) { }
|
||||
});
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
||||
private void showBuffering() {
|
||||
View root = getView();
|
||||
if (root != null) {
|
||||
root.findViewById(R.id.player_buffering_progress).setVisibility(View.VISIBLE);
|
||||
|
||||
PlayerView playerView = root.findViewById(R.id.shuffle_exoplayer_view);
|
||||
playerView.findViewById(R.id.player_skip_back_10).setVisibility(View.INVISIBLE);
|
||||
playerView.findViewById(R.id.player_skip_forward_10).setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void hideBuffering() {
|
||||
View root = getView();
|
||||
if (root != null) {
|
||||
root.findViewById(R.id.player_buffering_progress).setVisibility(View.INVISIBLE);
|
||||
|
||||
PlayerView playerView = root.findViewById(R.id.shuffle_exoplayer_view);
|
||||
playerView.findViewById(R.id.player_skip_back_10).setVisibility(View.VISIBLE);
|
||||
playerView.findViewById(R.id.player_skip_forward_10).setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void renderElapsedDuration() {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Helper.setViewText(view.findViewById(R.id.player_duration_elapsed), Helper.formatDuration(Double.valueOf(elapsedDuration / 1000.0).longValue()));
|
||||
}
|
||||
}
|
||||
|
||||
private void renderTotalDuration() {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Helper.setViewText(view.findViewById(R.id.player_duration_total), Helper.formatDuration(Double.valueOf(totalDuration / 1000.0).longValue()));
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAndScheduleDurations() {
|
||||
if (MainActivity.appPlayer != null && playbackStarted) {
|
||||
elapsedDuration = MainActivity.appPlayer.getCurrentPosition() < 0 ? 0 : MainActivity.appPlayer.getCurrentPosition();
|
||||
totalDuration = MainActivity.appPlayer.getDuration() < 0 ? 0 : MainActivity.appPlayer.getDuration();
|
||||
|
||||
renderElapsedDuration();
|
||||
renderTotalDuration();
|
||||
scheduleElapsedPlayback();
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleElapsedPlayback() {
|
||||
if (!elapsedPlaybackScheduled) {
|
||||
elapsedPlaybackScheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
elapsedPlaybackScheduler.scheduleAtFixedRate(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Context context = getContext();
|
||||
if (context instanceof MainActivity) {
|
||||
((MainActivity) context).runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (MainActivity.appPlayer != null) {
|
||||
elapsedDuration = MainActivity.appPlayer.getCurrentPosition();
|
||||
int elapsedSeconds = Double.valueOf(elapsedDuration / 1000.0).intValue();
|
||||
renderElapsedDuration();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 0, 500, TimeUnit.MILLISECONDS);
|
||||
elapsedPlaybackScheduled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move this call to MainActivity?
|
||||
private void claimEligibleRewards() {
|
||||
// attempt to claim eligible rewards after viewing or playing a file (fail silently)
|
||||
Context context = getContext();
|
||||
ClaimRewardTask firstStreamTask = new ClaimRewardTask(Reward.TYPE_FIRST_STREAM, null, null, context, eligibleRewardHandler);
|
||||
ClaimRewardTask dailyViewTask = new ClaimRewardTask(Reward.TYPE_DAILY_VIEW, null, null, context, eligibleRewardHandler);
|
||||
firstStreamTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
dailyViewTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private ClaimRewardTask.ClaimRewardHandler eligibleRewardHandler = new ClaimRewardTask.ClaimRewardHandler() {
|
||||
@Override
|
||||
public void onSuccess(double amountClaimed, String message) {
|
||||
if (Helper.isNullOrEmpty(message)) {
|
||||
message = getResources().getQuantityString(
|
||||
R.plurals.claim_reward_message,
|
||||
amountClaimed == 1 ? 1 : 2,
|
||||
new DecimalFormat(Helper.LBC_CURRENCY_FORMAT_PATTERN).format(amountClaimed));
|
||||
}
|
||||
Context context = getContext();
|
||||
if (context instanceof MainActivity) {
|
||||
((MainActivity) context).showMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception error) {
|
||||
// pass
|
||||
}
|
||||
};
|
||||
|
||||
private static class SwipeListener implements View.OnTouchListener {
|
||||
private final GestureDetector gestureDetector;
|
||||
private final View control;
|
||||
public SwipeListener(View control, Context context) {
|
||||
this.control = control;
|
||||
gestureDetector = new GestureDetector(context, new SwipeGestureListener());
|
||||
}
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
||||
return gestureDetector.onTouchEvent(motionEvent);
|
||||
}
|
||||
|
||||
public void onSwipeLeft() { }
|
||||
public void onSwipeRight() { }
|
||||
public void onSwipeTop() { }
|
||||
public void onSwipeBottom() { }
|
||||
|
||||
private final class SwipeGestureListener extends GestureDetector.SimpleOnGestureListener {
|
||||
private static final int SWIPE_THRESHOLD = 100;
|
||||
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
if (control instanceof PlayerView) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
boolean result = false;
|
||||
try {
|
||||
float diffY = e2.getY() - e1.getY();
|
||||
float diffX = e2.getX() - e1.getX();
|
||||
if (Math.abs(diffX) > Math.abs(diffY)) {
|
||||
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
|
||||
if (diffX > 0) {
|
||||
onSwipeRight();
|
||||
} else {
|
||||
onSwipeLeft();
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
|
||||
if (diffY > 0) {
|
||||
onSwipeBottom();
|
||||
} else {
|
||||
onSwipeTop();
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
// pass
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldHideGlobalPlayer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final List<String> contentChannelIds = Arrays.asList(
|
||||
"3fec094c5937e9eb4e8f5e71e4ca430e8a993d03",
|
||||
"6184648aab0431c4c95c649072d1f9ff08b9bb7c",
|
||||
"b5d31cde873073718c033076656a27471e392afc",
|
||||
"7317cdf6f62be93b22295062e191f6ba59a5db26",
|
||||
"1cdb5d0bdcb484907d0a2fea4efdfe0153838642",
|
||||
"b516294f541a18ce00b71a60b2c82ad2f87ff78d",
|
||||
"91e42cc450075f2c4c245bac7617bf903f16b4ce",
|
||||
"b6e207c5f8c58e7c8362cd05a1501bf2f5b694f2",
|
||||
"25f384bd95e218f6ac37fcaca99ed40f36760d8c",
|
||||
"f33657a2fcbab2dc3ce555d5d6728f8758af7bc7",
|
||||
"294f5c164da5ac9735658b2d58d8fee6745dfc45",
|
||||
"119a2e8c0b50f78d3861636d37c3b44ba8e689b5",
|
||||
"7b23cca3f49059f005e812be03931c81272eaac4",
|
||||
"fb0efeaa3788d1292bb49a94d77622503fe08129",
|
||||
"797a528c49b6535560f7fd8222b121b0223287c8",
|
||||
"bc490776f367b8afccf0ea7349d657431ba1ded6",
|
||||
"48c7ea8bc2c4adba09bf21a29689e3b8c2967522",
|
||||
"bf7490f905904e79de5c90e472bb9e6f26e634a0",
|
||||
"df961194a798cc76306b9290701130c592530fb6",
|
||||
"cf0be9078d76951e2e228df68b5b0bbf71313aaa",
|
||||
"d746ac8d782f94d12d176c7a591f5bf8365bef3d",
|
||||
"1f30267438257020f08abf452746a48e53a71ad5",
|
||||
"4ad942982e43326c7700b1b6443049b3cfd82161",
|
||||
"1cdb5d0bdcb484907d0a2fea4efdfe0153838642",
|
||||
"6616707e1109aaa1c11b9f399f914d0cfb4f5303",
|
||||
"4ee7cfaf1fc50a6df858ed0b99c278d633bccca9",
|
||||
"b7d02b4a0036114732c072269adb891dc5e34ca4",
|
||||
"9c51c1a119137cd17ed5ae09daa80c1cab6ac01d",
|
||||
"5f2a5c14b971a6f5eed0a67dc7af3a3fe5c0b6a4",
|
||||
"0e2b5b4cf59e859860000ff123dc12a317ad416b",
|
||||
"3fe68ad3da93065e35c37b14fbeef88b4b7785ed",
|
||||
"fd7ffcbafb74412a8812df4720feaf11fe70fe12",
|
||||
"b4c30fe36b79870a79c55e1e909adb5ad23f323f",
|
||||
"92c0f2f3239f1f61496997bd2cdc197ec51bd423",
|
||||
"29193e9240a71a735639c66ee954e68414f11236",
|
||||
"25f384bd95e218f6ac37fcaca99ed40f36760d8c",
|
||||
"87b13b074936b1f42a7c6758c7c2995f58c602e7",
|
||||
"8d935c6c30510e1dfc10f803a9646fa8aa128b07",
|
||||
"8f4fecfc836ea33798ee3e5cef56926fa54e2cf9",
|
||||
"9a5dfcb1a4b29c3a1598392d039744b9938b5a26",
|
||||
"c5724e280283cd985186af9a62494aae377daabd",
|
||||
"b39833be3032bbe1005f4f719f379a4621faeb13",
|
||||
"589276465a23c589801d874f484cc39f307d7ec7",
|
||||
"fb364ef587872515f545a5b4b3182b58073f230f",
|
||||
"6c0bf1fed2705d675da950a94e4af004ec975a06",
|
||||
"b924ac36b7499591f7929d9c4903de79b07b1cb9",
|
||||
"113515e893b8186595595e594ecc410bae50c026",
|
||||
"72f9815b087b6d346745e3de71a6ce5fe73a8677",
|
||||
"b0198a465290f065378f3535666bee0653d6a9bb",
|
||||
"020ebeb40642bfb4bc3d9f6d28c098afc0a47481",
|
||||
"5c15e604c4207f52c8cf58fe21e63164c230e257",
|
||||
"273a2fa759f1a9f56b078633ea2f08fc2406002a",
|
||||
"930fc43ca7bae20d4706543e97175d1872b0671f",
|
||||
"0cb2ec46f06ba85520a1c1a56706acf35d5176dd",
|
||||
"057053dfb657aaa98553e2c544b06e1a2371557e",
|
||||
"64e091964a611a48424d254a3de2b952d0d6565a",
|
||||
"50ebba2b06908f93d7963b1c6826cc0fd6104477",
|
||||
"374ff82251a384601da73f30485c3ac8d7f4176b",
|
||||
"1487afc813124abbeb0629d2172be0f01ccec3bf",
|
||||
"6a4fa1a68b92336e64006a4310cb160b07854329",
|
||||
"15f986a262fc6eff5774050c94d174c0533d505d",
|
||||
"6184648aab0431c4c95c649072d1f9ff08b9bb7c",
|
||||
"1efa9b640ad980b2ec53834d60e9cff9554979cd",
|
||||
"064d4999ea15e433e06f16f391922390acab01cb",
|
||||
"4884e30b93b3c4c123a83154516196095f9e831e",
|
||||
"2827bfc459c12d7c6d280cbacee750811291d4ba",
|
||||
"9626816275585ac3443e7cddd1272c8652c23f1d",
|
||||
"a2e1bb1fed32c6a6290b679785dd31ca5c59cb5f",
|
||||
"d9535951222dd7a1ff7f763872cb0df44f7962bf",
|
||||
"243b6f18093ff97c861d0568c7d3379606201a4b",
|
||||
"1ce5ac7bab7f2e82af02305ced3c095e320b52e5",
|
||||
"3e63119a8503a6f666b0a736c8fdeb9e79d11eb4",
|
||||
"e33372c0d8b2cdd3e12252962ee1671d66143075",
|
||||
"7364ba2ac9090e468855ce9074bb50c306196f4c",
|
||||
"d6350f9158825662b99e4b5e0442bcc94d39bc11",
|
||||
"2a294ea41312c6da8de5ebd0f18dbfb2963bb1a4",
|
||||
"44c49bbab8a3e9999f3ba9dee0186288b1d960a7",
|
||||
"2305db36455aa0d18571015b9e9bd0950262aa0f",
|
||||
"82f1d8c257d3e76b711a5cecd1e49bd3fa6a9de9",
|
||||
"faed2b028a9b5a712d5180eaa6fd2aa619f941bc",
|
||||
"39ac239b5687f7d1c2ba74cd020b3547545dfdaf",
|
||||
"5737eb22f6119f27c9afccfe73ba710afd885371",
|
||||
"5f22b6daf7204d73cf79d3ff0b46fc4fe237c7f7",
|
||||
"3583f5a570af6870504eea5a5f7afad6e1508508",
|
||||
"b0e489f986c345aef23c4a48d91cbcf5a6fdb9ac",
|
||||
"ba79c80788a9e1751e49ad401f5692d86f73a2db",
|
||||
"c54323436f50c633c870298bb374ac8e7560e6cd",
|
||||
"83725c7ee23bd4a8ca28a4fab0e313409def1dc7",
|
||||
"61c4e8636704f2f38bbe88b1f30ef0d74d6c0f49",
|
||||
"87ef9ba36019f7f3bf217cf47511645893b13f2e",
|
||||
"1bd0adfcf1c75bafc1ba3fc9b65a1a0470df6a91",
|
||||
"1be15348c51955179b7bf9aa90230a9425927ef6",
|
||||
"1f45ab3495df2c3be7d9c882bca9966305115cbb",
|
||||
"7317777e3751efa66218f6da5ef0d01dda69af48",
|
||||
"3346a4ff80b70ee7eea8a79fc79f19c43bb4464a",
|
||||
"452916a904030d2ce4ea2440fad2d0774e7296d9",
|
||||
"2675ef3adf52ebf8a44ff5da4306c293dfa6f901",
|
||||
"e2a76643735f8611a561794509c6bb2aac70eb04",
|
||||
"dc577db3caf5ff83a3b573ba92f2d447f067eee1",
|
||||
"89c4cf244099918b1d3ed413df27d4216e97b499",
|
||||
"4b2b5822c8af3074c6ef9b789a8142d0ef623402",
|
||||
"3c5794f775975669745c412b0c30f48991d9e455",
|
||||
"1df464dbb302ced815c61431a5548a273e6de8e1"
|
||||
);
|
||||
}
|
|
@ -375,6 +375,23 @@ public final class Lbry {
|
|||
"any_tags", "channel_ids", "order_by", "not_tags", "not_channel_ids", "urls"
|
||||
};
|
||||
|
||||
// build claim search for surf mode
|
||||
public static Map<String, Object> buildClaimSearchOptions(
|
||||
String claimType, List<String> notTags, List<String> channelIds, List<String> orderBy, long maxDuration, int limitClaimsPerChannel, int page, int pageSize) {
|
||||
return buildClaimSearchOptions(
|
||||
Collections.singletonList(claimType),
|
||||
null,
|
||||
notTags,
|
||||
channelIds,
|
||||
null,
|
||||
orderBy,
|
||||
null,
|
||||
maxDuration,
|
||||
limitClaimsPerChannel,
|
||||
page,
|
||||
pageSize);
|
||||
}
|
||||
|
||||
public static Map<String, Object> buildClaimSearchOptions(
|
||||
String claimType,
|
||||
List<String> anyTags,
|
||||
|
@ -393,6 +410,8 @@ public final class Lbry {
|
|||
notChannelIds,
|
||||
orderBy,
|
||||
releaseTime,
|
||||
0,
|
||||
0,
|
||||
page,
|
||||
pageSize);
|
||||
}
|
||||
|
@ -405,6 +424,8 @@ public final class Lbry {
|
|||
List<String> notChannelIds,
|
||||
List<String> orderBy,
|
||||
String releaseTime,
|
||||
long maxDuration,
|
||||
int limitClaimsPerChannel,
|
||||
int page,
|
||||
int pageSize) {
|
||||
Map<String, Object> options = new HashMap<>();
|
||||
|
@ -417,6 +438,12 @@ public final class Lbry {
|
|||
if (!Helper.isNullOrEmpty(releaseTime)) {
|
||||
options.put("release_time", releaseTime);
|
||||
}
|
||||
if (maxDuration > 0) {
|
||||
options.put("duration", String.format("<%d", maxDuration));
|
||||
}
|
||||
if (limitClaimsPerChannel > 0) {
|
||||
options.put("limit_claims_per_channel", limitClaimsPerChannel);
|
||||
}
|
||||
|
||||
addClaimSearchListOption("any_tags", anyTags, options);
|
||||
addClaimSearchListOption("not_tags", notTags, options);
|
||||
|
|
|
@ -28,6 +28,7 @@ public class LbryAnalytics {
|
|||
public static final String EVENT_PUBLISH_UPDATE = "publish_update";
|
||||
public static final String EVENT_CHANNEL_CREATE = "channel_create";
|
||||
public static final String EVENT_SEARCH = "search";
|
||||
public static final String EVENT_SHUFFLE_SESSION = "shuffle_session";
|
||||
|
||||
private static FirebaseAnalytics analytics;
|
||||
|
||||
|
|
87
app/src/main/res/layout/fragment_shuffle.xml
Normal file
87
app/src/main/res/layout/fragment_shuffle.xml
Normal file
|
@ -0,0 +1,87 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<RelativeLayout
|
||||
android:background="@android:color/black"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/shuffle_exoplayer_container"
|
||||
android:background="@android:color/black"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_above="@id/shuffle_content_details">
|
||||
<com.google.android.exoplayer2.ui.PlayerView
|
||||
android:id="@+id/shuffle_exoplayer_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:controller_layout_id="@layout/exo_playback_control_view"/>
|
||||
<ProgressBar
|
||||
android:id="@+id/player_buffering_progress"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:visibility="gone" />
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/shuffle_content_details"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_alignParentBottom="true">
|
||||
<TextView
|
||||
android:id="@+id/shuffle_content_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/inter"
|
||||
android:textColor="@color/white"
|
||||
android:textFontWeight="300"
|
||||
android:minLines="1"
|
||||
android:maxLines="3"
|
||||
android:textSize="20sp"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/shuffle_content_publisher"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/inter"
|
||||
android:textColor="@color/white"
|
||||
android:textFontWeight="300"
|
||||
android:minLines="1"
|
||||
android:maxLines="1"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/shuffle_loading"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/shuffle_share_button"
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="72dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:clickable="true"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:tint="@color/white"
|
||||
android:src="@drawable/ic_share" />
|
||||
</RelativeLayout>
|
||||
</RelativeLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.slideshow.SlideshowFragment">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_slideshow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -20,6 +20,7 @@
|
|||
<string name="settings">Settings</string>
|
||||
<string name="about">About</string>
|
||||
<string name="sign_in">Sign In</string>
|
||||
<string name="shuffle">Shuffle</string>
|
||||
<string name="startup_failed">App startup failed. Please check your data connection and try again. If this problem persists, please email hello@lbry.com</string>
|
||||
<string name="no_claim_search_content">No content to display at this time. Please refine your selection or check back later.</string>
|
||||
|
||||
|
@ -650,4 +651,6 @@
|
|||
|
||||
<string name="fa_asterisk" translatable="false"></string>
|
||||
<string name="fa_comment_alt" translatable="false"></string>
|
||||
<string name="fa_broadcast_tower" translatable="false"></string>
|
||||
<string name="fa_random" translatable="false"></string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue