mirror of
https://github.com/jellyfin/jellyfin-androidtv.git
synced 2026-10-26 16:45:23 +00:00
Start of basic playback
This commit is contained in:
1 parent
393ba2ca8d
commit
64e7f32714
7 files changed
+105
-52
No files matched your search
+4
-8
@@ -1,8 +1,6 @@
|
||||
package tv.mediabrowser.mediabrowsertv;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
@@ -16,7 +14,6 @@ import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||
import android.support.v17.leanback.widget.ClassPresenterSelector;
|
||||
import android.support.v17.leanback.widget.DetailsOverviewRow;
|
||||
import android.support.v17.leanback.widget.DetailsOverviewRowPresenter;
|
||||
import android.support.v17.leanback.widget.HeaderItem;
|
||||
import android.support.v17.leanback.widget.ImageCardView;
|
||||
import android.support.v17.leanback.widget.ListRow;
|
||||
import android.support.v17.leanback.widget.ListRowPresenter;
|
||||
@@ -40,7 +37,7 @@ import mediabrowser.model.dto.BaseItemDto;
|
||||
public class BaseItemDetailsFragment extends DetailsFragment {
|
||||
private static final String TAG = "BaseItemDetailsFragment";
|
||||
|
||||
private static final int ACTION_WATCH_TRAILER = 1;
|
||||
private static final int ACTION_PLAY = 1;
|
||||
private static final int ACTION_RENT = 2;
|
||||
private static final int ACTION_BUY = 3;
|
||||
|
||||
@@ -110,8 +107,7 @@ public class BaseItemDetailsFragment extends DetailsFragment {
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
row.addAction(new Action(ACTION_WATCH_TRAILER, getResources().getString(
|
||||
R.string.watch_trailer_1), getResources().getString(R.string.watch_trailer_2)));
|
||||
row.addAction(new Action(ACTION_PLAY, "Play", "From Beginning"));
|
||||
row.addAction(new Action(ACTION_RENT, getResources().getString(R.string.rent_1),
|
||||
getResources().getString(R.string.rent_2)));
|
||||
row.addAction(new Action(ACTION_BUY, getResources().getString(R.string.buy_1),
|
||||
@@ -128,13 +124,13 @@ public class BaseItemDetailsFragment extends DetailsFragment {
|
||||
mDorPresenter.setOnActionClickedListener(new OnActionClickedListener() {
|
||||
@Override
|
||||
public void onActionClicked(Action action) {
|
||||
if (action.getId() == ACTION_WATCH_TRAILER) {
|
||||
if (action.getId() == ACTION_PLAY) {
|
||||
Intent intent = new Intent(getActivity(), PlaybackOverlayActivity.class);
|
||||
intent.putExtra("BaseItemDto", TvApp.getApplication().getSerializer().SerializeToString(mBaseItem));
|
||||
intent.putExtra(getResources().getString(R.string.should_start), true);
|
||||
startActivity(intent);
|
||||
} else {
|
||||
Toast.makeText(getActivity(), action.toString(), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getActivity(), action.toString() + " not implemented", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
+17
-4
@@ -15,12 +15,20 @@ package tv.mediabrowser.mediabrowsertv;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.session.MediaController;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.VideoView;
|
||||
|
||||
import mediabrowser.apiinteraction.ApiClient;
|
||||
import mediabrowser.apiinteraction.EmptyResponse;
|
||||
import mediabrowser.model.dlna.StreamBuilder;
|
||||
import mediabrowser.model.dlna.VideoOptions;
|
||||
import mediabrowser.model.dto.BaseItemDto;
|
||||
import mediabrowser.model.session.PlaybackStopInfo;
|
||||
|
||||
/**
|
||||
* PlaybackOverlayActivity for video playback that loads PlaybackOverlayFragment
|
||||
*/
|
||||
@@ -35,6 +43,7 @@ public class PlaybackOverlayActivity extends Activity implements
|
||||
private static final double MEDIA_BOTTOM_MARGIN = 0.025;
|
||||
private static final double MEDIA_LEFT_MARGIN = 0.025;
|
||||
|
||||
private ApiClient mApiClient;
|
||||
|
||||
private VideoView mVideoView;
|
||||
private PlaybackState mPlaybackState = PlaybackState.IDLE;
|
||||
@@ -45,23 +54,25 @@ public class PlaybackOverlayActivity extends Activity implements
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mApiClient = TvApp.getApplication().getApiClient();
|
||||
setContentView(R.layout.playback_controls);
|
||||
loadViews();
|
||||
//setupCallbacks();
|
||||
overScan();
|
||||
setupCallbacks();
|
||||
//overScan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
Utils.Stop(TvApp.getApplication().getCurrentPlayingItem(), mVideoView.getCurrentPosition());
|
||||
mVideoView.suspend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of OnPlayPauseClickedListener
|
||||
*/
|
||||
public void onFragmentPlayPause(Movie movie, int position, Boolean playPause) {
|
||||
mVideoView.setVideoPath(movie.getVideoUrl());
|
||||
public void onFragmentPlayPause(BaseItemDto movie, int position, Boolean playPause) {
|
||||
Utils.Play(movie, mVideoView);
|
||||
|
||||
if (position == 0 || mPlaybackState == PlaybackState.IDLE) {
|
||||
setupCallbacks();
|
||||
@@ -133,6 +144,8 @@ public class PlaybackOverlayActivity extends Activity implements
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
mPlaybackState = PlaybackState.IDLE;
|
||||
Utils.Stop(TvApp.getApplication().getCurrentPlayingItem(), mVideoView.getCurrentPosition());
|
||||
mVideoView.suspend();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+31
-35
@@ -58,6 +58,8 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import mediabrowser.model.dto.BaseItemDto;
|
||||
|
||||
/*
|
||||
* Class for video playback with media control
|
||||
*/
|
||||
@@ -90,18 +92,19 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
|
||||
private SkipNextAction mSkipNextAction;
|
||||
private SkipPreviousAction mSkipPreviousAction;
|
||||
private PlaybackControlsRow mPlaybackControlsRow;
|
||||
private ArrayList<Movie> mItems = new ArrayList<Movie>();
|
||||
private ArrayList<BaseItemDto> mItems = new ArrayList<>();
|
||||
private int mCurrentItem;
|
||||
private Handler mHandler;
|
||||
private Runnable mRunnable;
|
||||
private Movie mSelectedMovie;
|
||||
private BaseItemDto mSelectedMovie;
|
||||
private TvApp mApplication;
|
||||
private PicassoPlaybackControlsRowTarget mPlaybackControlsRowTarget;
|
||||
|
||||
OnPlayPauseClickedListener mCallback;
|
||||
|
||||
// Container Activity must implement this interface
|
||||
public interface OnPlayPauseClickedListener {
|
||||
public void onFragmentPlayPause(Movie movie, int position, Boolean playPause);
|
||||
public void onFragmentPlayPause(BaseItemDto movie, int position, Boolean playPause);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,21 +112,17 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
|
||||
Log.i(TAG, "onCreate");
|
||||
super.onCreate(savedInstanceState);
|
||||
sContext = getActivity();
|
||||
mApplication = TvApp.getApplication();
|
||||
|
||||
mItems = new ArrayList<Movie>();
|
||||
mSelectedMovie = (Movie) getActivity()
|
||||
.getIntent().getSerializableExtra(DetailsActivity.MOVIE);
|
||||
mItems = new ArrayList<>();
|
||||
mSelectedMovie = mApplication.getSerializer().DeserializeFromString(getActivity()
|
||||
.getIntent().getStringExtra("BaseItemDto"), BaseItemDto.class);
|
||||
|
||||
List<Movie> movies = MovieList.list;
|
||||
//TODO get additional items to play from intent
|
||||
mItems.add(mSelectedMovie);
|
||||
mCurrentItem = 0;
|
||||
|
||||
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter());
|
||||
|
||||
for (int j = 0; j < movies.size(); j++) {
|
||||
mItems.add(movies.get(j));
|
||||
if (mSelectedMovie.getTitle().contentEquals(movies.get(j).getTitle())) {
|
||||
mCurrentItem = j;
|
||||
}
|
||||
}
|
||||
//ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter());
|
||||
|
||||
mHandler = new Handler();
|
||||
|
||||
@@ -146,6 +145,10 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
|
||||
Log.i(TAG, "onItemClicked: " + item + " row " + row);
|
||||
}
|
||||
});
|
||||
|
||||
if (getActivity().getIntent().getBooleanExtra("ShouldStart", false)) {
|
||||
mCallback.onFragmentPlayPause(mSelectedMovie, 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -215,16 +218,8 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
|
||||
}
|
||||
|
||||
private int getDuration() {
|
||||
Movie movie = mItems.get(mCurrentItem);
|
||||
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
mmr.setDataSource(movie.getVideoUrl(), new HashMap<String, String>());
|
||||
} else {
|
||||
mmr.setDataSource(movie.getVideoUrl());
|
||||
}
|
||||
String time = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
|
||||
long duration = Long.parseLong(time);
|
||||
return (int) duration;
|
||||
BaseItemDto movie = mItems.get(mCurrentItem);
|
||||
return movie != null ? movie.getRunTimeTicks().intValue() : 0;
|
||||
}
|
||||
|
||||
private void addPlaybackControlsRow() {
|
||||
@@ -294,13 +289,13 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
|
||||
|
||||
private void updatePlaybackRow(int index) {
|
||||
if (mPlaybackControlsRow.getItem() != null) {
|
||||
Movie item = (Movie) mPlaybackControlsRow.getItem();
|
||||
item.setTitle(mItems.get(mCurrentItem).getTitle());
|
||||
item.setStudio(mItems.get(mCurrentItem).getStudio());
|
||||
BaseItemDto item = (BaseItemDto) mPlaybackControlsRow.getItem();
|
||||
// item.setTitle(mItems.get(mCurrentItem).getTitle());
|
||||
// item.setStudio(mItems.get(mCurrentItem).getStudio());
|
||||
}
|
||||
if (SHOW_IMAGE) {
|
||||
mPlaybackControlsRowTarget = new PicassoPlaybackControlsRowTarget(mPlaybackControlsRow);
|
||||
updateVideoImage(mItems.get(mCurrentItem).getCardImageURI());
|
||||
updateVideoImage(Utils.getPrimaryImageUrl(mItems.get(mCurrentItem), mApplication.getApiClient()));
|
||||
}
|
||||
mRowsAdapter.notifyArrayItemRangeChanged(0, 1);
|
||||
mPlaybackControlsRow.setTotalTime(getDuration());
|
||||
@@ -310,10 +305,10 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
|
||||
|
||||
private void addOtherRows() {
|
||||
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter());
|
||||
for (Movie movie : mItems) {
|
||||
for (BaseItemDto movie : mItems) {
|
||||
listRowAdapter.add(movie);
|
||||
}
|
||||
HeaderItem header = new HeaderItem(0, getString(R.string.related_movies), null);
|
||||
HeaderItem header = new HeaderItem(0, getString(R.string.current_queue), null);
|
||||
mRowsAdapter.add(new ListRow(header, listRowAdapter));
|
||||
|
||||
}
|
||||
@@ -384,8 +379,9 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
|
||||
static class DescriptionPresenter extends AbstractDetailsDescriptionPresenter {
|
||||
@Override
|
||||
protected void onBindDescription(ViewHolder viewHolder, Object item) {
|
||||
viewHolder.getTitle().setText(((Movie) item).getTitle());
|
||||
viewHolder.getSubtitle().setText(((Movie) item).getStudio());
|
||||
BaseItemDto movie = (BaseItemDto) item;
|
||||
viewHolder.getTitle().setText(movie.getName());
|
||||
viewHolder.getSubtitle().setText(movie.getOfficialRating());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,9 +409,9 @@ public class PlaybackOverlayFragment extends android.support.v17.leanback.app.Pl
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateVideoImage(URI uri) {
|
||||
protected void updateVideoImage(String url) {
|
||||
Picasso.with(sContext)
|
||||
.load(uri.toString())
|
||||
.load(url)
|
||||
.resize(Utils.convertDpToPixel(sContext, CARD_WIDTH),
|
||||
Utils.convertDpToPixel(sContext, CARD_HEIGHT))
|
||||
.into(mPlaybackControlsRowTarget);
|
||||
|
||||
+5
-3
@@ -81,16 +81,18 @@ public class StartupActivity extends Activity {
|
||||
break;
|
||||
case ServerSignIn:
|
||||
logger.Debug("Sign in with server "+ response.getServers().get(0).getName() + " total: " + response.getServers().size());
|
||||
connectionManager.Connect(response.getServers().get(0), new Response<ConnectionResult>() {
|
||||
//connectionManager.Connect(response.getServers().get(0), new Response<ConnectionResult>() {
|
||||
//force to dev server
|
||||
connectionManager.Connect("eric-office:8096", new Response<ConnectionResult>() {
|
||||
@Override
|
||||
public void onResponse(ConnectionResult serverResult) {
|
||||
switch (serverResult.getState()) {
|
||||
case ServerSignIn:
|
||||
try {
|
||||
serverResult.getApiClient().AuthenticateUserAsync("Dad","124", new Response<AuthenticationResult>() {
|
||||
serverResult.getApiClient().AuthenticateUserAsync("ebr","0101", new Response<AuthenticationResult>() {
|
||||
@Override
|
||||
public void onResponse(AuthenticationResult authenticationResult) {
|
||||
logger.Debug("Signed in as Dad.");
|
||||
logger.Debug("Signed in as " + authenticationResult.getUser().getName());
|
||||
application.setCurrentUser(authenticationResult.getUser());
|
||||
Intent intent = new Intent(activity, MainActivity.class);
|
||||
startActivity(intent);
|
||||
|
||||
@@ -7,6 +7,7 @@ import mediabrowser.apiinteraction.ApiClient;
|
||||
import mediabrowser.apiinteraction.IConnectionManager;
|
||||
import mediabrowser.apiinteraction.android.GsonJsonSerializer;
|
||||
import mediabrowser.logging.ConsoleLogger;
|
||||
import mediabrowser.model.dto.BaseItemDto;
|
||||
import mediabrowser.model.dto.UserDto;
|
||||
import mediabrowser.model.logging.ILogger;
|
||||
import mediabrowser.model.serialization.IJsonSerializer;
|
||||
@@ -21,6 +22,7 @@ public class TvApp extends Application {
|
||||
private GsonJsonSerializer serializer;
|
||||
private static TvApp app;
|
||||
private UserDto currentUser;
|
||||
private BaseItemDto currentPlayingItem;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@@ -77,4 +79,12 @@ public class TvApp extends Application {
|
||||
public ApiClient getApiClient() {
|
||||
return connectionManager.GetApiClient(currentUser);
|
||||
}
|
||||
|
||||
public BaseItemDto getCurrentPlayingItem() {
|
||||
return currentPlayingItem;
|
||||
}
|
||||
|
||||
public void setCurrentPlayingItem(BaseItemDto currentPlayingItem) {
|
||||
this.currentPlayingItem = currentPlayingItem;
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,21 @@ import android.media.ToneGenerator;
|
||||
import android.view.Display;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
import android.widget.VideoView;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import mediabrowser.apiinteraction.ApiClient;
|
||||
import mediabrowser.apiinteraction.EmptyResponse;
|
||||
import mediabrowser.model.dlna.StreamBuilder;
|
||||
import mediabrowser.model.dlna.StreamInfo;
|
||||
import mediabrowser.model.dlna.VideoOptions;
|
||||
import mediabrowser.model.dlna.profiles.AndroidProfile;
|
||||
import mediabrowser.model.dto.BaseItemDto;
|
||||
import mediabrowser.model.dto.ImageOptions;
|
||||
import mediabrowser.model.entities.ImageType;
|
||||
import mediabrowser.model.session.PlaybackStartInfo;
|
||||
import mediabrowser.model.session.PlaybackStopInfo;
|
||||
|
||||
/**
|
||||
* A collection of utility methods, all static.
|
||||
@@ -135,7 +143,7 @@ public class Utils {
|
||||
|
||||
public static String getPrimaryImageUrl(BaseItemDto item, ApiClient apiClient) {
|
||||
ImageOptions options = new ImageOptions();
|
||||
options.setMaxWidth(320);
|
||||
options.setMaxWidth(500);
|
||||
options.setImageType(ImageType.Primary);
|
||||
options.setTag(item.getImageTags().get(ImageType.Primary));
|
||||
|
||||
@@ -172,6 +180,34 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void Play(BaseItemDto item, VideoView view) {
|
||||
StreamBuilder builder = new StreamBuilder();
|
||||
VideoOptions options = new VideoOptions();
|
||||
ApiClient apiClient = TvApp.getApplication().getApiClient();
|
||||
options.setDeviceId(apiClient.getDeviceId());
|
||||
options.setItemId(item.getId());
|
||||
options.setMediaSources(item.getMediaSources());
|
||||
options.setProfile(new AndroidProfile());
|
||||
StreamInfo info = builder.BuildVideoItem(options);
|
||||
view.setVideoPath(info.ToUrl(apiClient.getApiUrl()));
|
||||
TvApp.getApplication().setCurrentPlayingItem(item);
|
||||
|
||||
PlaybackStartInfo startInfo = new PlaybackStartInfo();
|
||||
startInfo.setItemId(item.getId());
|
||||
apiClient.ReportPlaybackStartAsync(startInfo, new EmptyResponse());
|
||||
|
||||
}
|
||||
|
||||
public static void Stop(BaseItemDto item, long pos) {
|
||||
PlaybackStopInfo info = new PlaybackStopInfo();
|
||||
ApiClient apiClient = TvApp.getApplication().getApiClient();
|
||||
info.setItemId(item.getId());
|
||||
info.setPositionTicks(pos);
|
||||
apiClient.ReportPlaybackStoppedAsync(info, new EmptyResponse());
|
||||
apiClient.StopTranscodingProcesses(apiClient.getDeviceId(), new EmptyResponse());
|
||||
|
||||
}
|
||||
|
||||
public static void Beep() {
|
||||
// send the tone to the "alarm" stream (classic beeps go there) with 50% volume
|
||||
ToneGenerator toneG = new ToneGenerator(AudioManager.STREAM_ALARM, 50);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<string name="app_name">MediaBrowserTv</string>
|
||||
<string name="browse_title"><![CDATA[Videos by Your Company]]></string>
|
||||
<string name="related_movies">Related Videos</string>
|
||||
<string name="current_queue">Current Queue</string>
|
||||
<string name="vertical_grid_title"><![CDATA[Vertical Video Grid]]></string>
|
||||
<string name="error">Error</string>
|
||||
<string name="ok">OK</string>
|
||||
|
||||
Reference in new issue
Block a user