add all the generated example code
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.nicidienase.chaosflix;
|
||||||
|
|
||||||
|
import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
|
||||||
|
|
||||||
|
import de.nicidienase.chaosflix.entities.Movie;
|
||||||
|
|
||||||
|
public class DetailsDescriptionPresenter extends AbstractDetailsDescriptionPresenter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindDescription(ViewHolder viewHolder, Object item) {
|
||||||
|
Movie movie = (Movie) item;
|
||||||
|
|
||||||
|
if (movie != null) {
|
||||||
|
viewHolder.getTitle().setText(movie.getTitle());
|
||||||
|
viewHolder.getSubtitle().setText(movie.getStudio());
|
||||||
|
viewHolder.getBody().setText(movie.getDescription());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
91
app/src/main/java/de/nicidienase/chaosflix/Utils.java
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.nicidienase.chaosflix;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of utility methods, all static.
|
||||||
|
*/
|
||||||
|
public class Utils {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Making sure public utility methods remain static
|
||||||
|
*/
|
||||||
|
private Utils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the screen/display size
|
||||||
|
*/
|
||||||
|
public static Point getDisplaySize(Context context) {
|
||||||
|
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||||
|
Display display = wm.getDefaultDisplay();
|
||||||
|
Point size = new Point();
|
||||||
|
display.getSize(size);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a (long) toast
|
||||||
|
*/
|
||||||
|
public static void showToast(Context context, String msg) {
|
||||||
|
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a (long) toast.
|
||||||
|
*/
|
||||||
|
public static void showToast(Context context, int resourceId) {
|
||||||
|
Toast.makeText(context, context.getString(resourceId), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int convertDpToPixel(Context ctx, int dp) {
|
||||||
|
float density = ctx.getResources().getDisplayMetrics().density;
|
||||||
|
return Math.round((float) dp * density);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats time in milliseconds to hh:mm:ss string format.
|
||||||
|
*/
|
||||||
|
public static String formatMillis(int millis) {
|
||||||
|
String result = "";
|
||||||
|
int hr = millis / 3600000;
|
||||||
|
millis %= 3600000;
|
||||||
|
int min = millis / 60000;
|
||||||
|
millis %= 60000;
|
||||||
|
int sec = millis / 1000;
|
||||||
|
if (hr > 0) {
|
||||||
|
result += hr + ":";
|
||||||
|
}
|
||||||
|
if (min >= 0) {
|
||||||
|
if (min > 9) {
|
||||||
|
result += min + ":";
|
||||||
|
} else {
|
||||||
|
result += "0" + min + ":";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sec > 9) {
|
||||||
|
result += sec;
|
||||||
|
} else {
|
||||||
|
result += "0" + sec;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package de.nicidienase.chaosflix.activities;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Fragment;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
|
import de.nicidienase.chaosflix.fragments.ErrorFragment;
|
||||||
|
import de.nicidienase.chaosflix.R;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BrowseErrorActivity shows how to use ErrorFragment
|
||||||
|
*/
|
||||||
|
public class BrowseErrorActivity extends Activity {
|
||||||
|
private static int TIMER_DELAY = 3000;
|
||||||
|
private static int SPINNER_WIDTH = 100;
|
||||||
|
private static int SPINNER_HEIGHT = 100;
|
||||||
|
|
||||||
|
private ErrorFragment mErrorFragment;
|
||||||
|
private SpinnerFragment mSpinnerFragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the activity is first created.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
testError();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testError() {
|
||||||
|
mErrorFragment = new ErrorFragment();
|
||||||
|
getFragmentManager().beginTransaction().add(R.id.main_browse_fragment, mErrorFragment).commit();
|
||||||
|
|
||||||
|
mSpinnerFragment = new SpinnerFragment();
|
||||||
|
getFragmentManager().beginTransaction().add(R.id.main_browse_fragment, mSpinnerFragment).commit();
|
||||||
|
|
||||||
|
final Handler handler = new Handler();
|
||||||
|
handler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
getFragmentManager().beginTransaction().remove(mSpinnerFragment).commit();
|
||||||
|
mErrorFragment.setErrorContent();
|
||||||
|
}
|
||||||
|
}, TIMER_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public class SpinnerFragment extends Fragment {
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
ProgressBar progressBar = new ProgressBar(container.getContext());
|
||||||
|
if (container instanceof FrameLayout) {
|
||||||
|
FrameLayout.LayoutParams layoutParams =
|
||||||
|
new FrameLayout.LayoutParams(SPINNER_WIDTH, SPINNER_HEIGHT, Gravity.CENTER);
|
||||||
|
progressBar.setLayoutParams(layoutParams);
|
||||||
|
}
|
||||||
|
return progressBar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.nicidienase.chaosflix.activities;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import de.nicidienase.chaosflix.R;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Details activity class that loads LeanbackDetailsFragment class
|
||||||
|
*/
|
||||||
|
public class DetailsActivity extends Activity {
|
||||||
|
public static final String SHARED_ELEMENT_NAME = "hero";
|
||||||
|
public static final String MOVIE = "Movie";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the activity is first created.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_details);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,257 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.nicidienase.chaosflix.activities;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.media.MediaMetadata;
|
||||||
|
import android.media.MediaPlayer;
|
||||||
|
import android.media.session.MediaSession;
|
||||||
|
import android.media.session.PlaybackState;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.widget.VideoView;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.request.animation.GlideAnimation;
|
||||||
|
import com.bumptech.glide.request.target.SimpleTarget;
|
||||||
|
|
||||||
|
import de.nicidienase.chaosflix.R;
|
||||||
|
import de.nicidienase.chaosflix.entities.Movie;
|
||||||
|
import de.nicidienase.chaosflix.fragments.PlaybackOverlayFragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PlaybackOverlayActivity for video playback that loads PlaybackOverlayFragment
|
||||||
|
*/
|
||||||
|
public class PlaybackOverlayActivity extends Activity implements
|
||||||
|
PlaybackOverlayFragment.OnPlayPauseClickedListener {
|
||||||
|
private static final String TAG = "PlaybackOverlayActivity";
|
||||||
|
|
||||||
|
private VideoView mVideoView;
|
||||||
|
private LeanbackPlaybackState mPlaybackState = LeanbackPlaybackState.IDLE;
|
||||||
|
private MediaSession mSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the activity is first created.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.playback_controls);
|
||||||
|
loadViews();
|
||||||
|
setupCallbacks();
|
||||||
|
mSession = new MediaSession(this, "LeanbackSampleApp");
|
||||||
|
mSession.setCallback(new MediaSessionCallback());
|
||||||
|
mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
|
||||||
|
MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
|
||||||
|
|
||||||
|
mSession.setActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
mVideoView.suspend();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||||
|
PlaybackOverlayFragment playbackOverlayFragment = (PlaybackOverlayFragment) getFragmentManager().findFragmentById(R.id.playback_controls_fragment);
|
||||||
|
switch (keyCode) {
|
||||||
|
case KeyEvent.KEYCODE_MEDIA_PLAY:
|
||||||
|
playbackOverlayFragment.togglePlayback(false);
|
||||||
|
return true;
|
||||||
|
case KeyEvent.KEYCODE_MEDIA_PAUSE:
|
||||||
|
playbackOverlayFragment.togglePlayback(false);
|
||||||
|
return true;
|
||||||
|
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
|
||||||
|
if (mPlaybackState == LeanbackPlaybackState.PLAYING) {
|
||||||
|
playbackOverlayFragment.togglePlayback(false);
|
||||||
|
} else {
|
||||||
|
playbackOverlayFragment.togglePlayback(true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return super.onKeyUp(keyCode, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of OnPlayPauseClickedListener
|
||||||
|
*/
|
||||||
|
public void onFragmentPlayPause(Movie movie, int position, Boolean playPause) {
|
||||||
|
mVideoView.setVideoPath(movie.getVideoUrl());
|
||||||
|
|
||||||
|
if (position == 0 || mPlaybackState == LeanbackPlaybackState.IDLE) {
|
||||||
|
setupCallbacks();
|
||||||
|
mPlaybackState = LeanbackPlaybackState.IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playPause && mPlaybackState != LeanbackPlaybackState.PLAYING) {
|
||||||
|
mPlaybackState = LeanbackPlaybackState.PLAYING;
|
||||||
|
if (position > 0) {
|
||||||
|
mVideoView.seekTo(position);
|
||||||
|
mVideoView.start();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mPlaybackState = LeanbackPlaybackState.PAUSED;
|
||||||
|
mVideoView.pause();
|
||||||
|
}
|
||||||
|
updatePlaybackState(position);
|
||||||
|
updateMetadata(movie);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePlaybackState(int position) {
|
||||||
|
PlaybackState.Builder stateBuilder = new PlaybackState.Builder()
|
||||||
|
.setActions(getAvailableActions());
|
||||||
|
int state = PlaybackState.STATE_PLAYING;
|
||||||
|
if (mPlaybackState == LeanbackPlaybackState.PAUSED) {
|
||||||
|
state = PlaybackState.STATE_PAUSED;
|
||||||
|
}
|
||||||
|
stateBuilder.setState(state, position, 1.0f);
|
||||||
|
mSession.setPlaybackState(stateBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getAvailableActions() {
|
||||||
|
long actions = PlaybackState.ACTION_PLAY |
|
||||||
|
PlaybackState.ACTION_PLAY_FROM_MEDIA_ID |
|
||||||
|
PlaybackState.ACTION_PLAY_FROM_SEARCH;
|
||||||
|
|
||||||
|
if (mPlaybackState == LeanbackPlaybackState.PLAYING) {
|
||||||
|
actions |= PlaybackState.ACTION_PAUSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMetadata(final Movie movie) {
|
||||||
|
final MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder();
|
||||||
|
|
||||||
|
String title = movie.getTitle().replace("_", " -");
|
||||||
|
|
||||||
|
metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, title);
|
||||||
|
metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE,
|
||||||
|
movie.getDescription());
|
||||||
|
metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI,
|
||||||
|
movie.getCardImageUrl());
|
||||||
|
|
||||||
|
// And at minimum the title and artist for legacy support
|
||||||
|
metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, title);
|
||||||
|
metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, movie.getStudio());
|
||||||
|
|
||||||
|
Glide.with(this)
|
||||||
|
.load(Uri.parse(movie.getCardImageUrl()))
|
||||||
|
.asBitmap()
|
||||||
|
.into(new SimpleTarget<Bitmap>(500, 500) {
|
||||||
|
@Override
|
||||||
|
public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
|
||||||
|
metadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_ART, bitmap);
|
||||||
|
mSession.setMetadata(metadataBuilder.build());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadViews() {
|
||||||
|
mVideoView = (VideoView) findViewById(R.id.videoView);
|
||||||
|
mVideoView.setFocusable(false);
|
||||||
|
mVideoView.setFocusableInTouchMode(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupCallbacks() {
|
||||||
|
|
||||||
|
mVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onError(MediaPlayer mp, int what, int extra) {
|
||||||
|
String msg = "";
|
||||||
|
if (extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) {
|
||||||
|
msg = getString(R.string.video_error_media_load_timeout);
|
||||||
|
} else if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
|
||||||
|
msg = getString(R.string.video_error_server_inaccessible);
|
||||||
|
} else {
|
||||||
|
msg = getString(R.string.video_error_unknown_error);
|
||||||
|
}
|
||||||
|
mVideoView.stopPlayback();
|
||||||
|
mPlaybackState = LeanbackPlaybackState.IDLE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
|
||||||
|
@Override
|
||||||
|
public void onPrepared(MediaPlayer mp) {
|
||||||
|
if (mPlaybackState == LeanbackPlaybackState.PLAYING) {
|
||||||
|
mVideoView.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
|
||||||
|
@Override
|
||||||
|
public void onCompletion(MediaPlayer mp) {
|
||||||
|
mPlaybackState = LeanbackPlaybackState.IDLE;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
mSession.setActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
if (mVideoView.isPlaying()) {
|
||||||
|
if (!requestVisibleBehind(true)) {
|
||||||
|
// Try to play behind launcher, but if it fails, stop playback.
|
||||||
|
stopPlayback();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
requestVisibleBehind(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
mSession.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onVisibleBehindCanceled() {
|
||||||
|
super.onVisibleBehindCanceled();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopPlayback() {
|
||||||
|
if (mVideoView != null) {
|
||||||
|
mVideoView.stopPlayback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* List of various states that we can be in
|
||||||
|
*/
|
||||||
|
public enum LeanbackPlaybackState {
|
||||||
|
PLAYING, PAUSED, BUFFERING, IDLE
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MediaSessionCallback extends MediaSession.Callback {
|
||||||
|
}
|
||||||
|
}
|
143
app/src/main/java/de/nicidienase/chaosflix/entities/Movie.java
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.nicidienase.chaosflix.entities;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Movie class represents video entity with title, description, image thumbs and video url.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Movie implements Serializable {
|
||||||
|
static final long serialVersionUID = 727566175075960653L;
|
||||||
|
private static long count = 0;
|
||||||
|
private long id;
|
||||||
|
private String title;
|
||||||
|
private String description;
|
||||||
|
private String bgImageUrl;
|
||||||
|
private String cardImageUrl;
|
||||||
|
private String videoUrl;
|
||||||
|
private String studio;
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
public Movie() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void incCount() {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStudio() {
|
||||||
|
return studio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStudio(String studio) {
|
||||||
|
this.studio = studio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVideoUrl() {
|
||||||
|
return videoUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoUrl(String videoUrl) {
|
||||||
|
this.videoUrl = videoUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBackgroundImageUrl() {
|
||||||
|
return bgImageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBackgroundImageUrl(String bgImageUrl) {
|
||||||
|
this.bgImageUrl = bgImageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCardImageUrl() {
|
||||||
|
return cardImageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCardImageUrl(String cardImageUrl) {
|
||||||
|
this.cardImageUrl = cardImageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCategory() {
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategory(String category) {
|
||||||
|
this.category = category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URI getBackgroundImageURI() {
|
||||||
|
try {
|
||||||
|
Log.d("BACK MOVIE: ", bgImageUrl);
|
||||||
|
return new URI(getBackgroundImageUrl());
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
Log.d("URI exception: ", bgImageUrl);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public URI getCardImageURI() {
|
||||||
|
try {
|
||||||
|
return new URI(getCardImageUrl());
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Movie{" +
|
||||||
|
"id=" + id +
|
||||||
|
", title='" + title + '\'' +
|
||||||
|
", videoUrl='" + videoUrl + '\'' +
|
||||||
|
", backgroundImageUrl='" + bgImageUrl + '\'' +
|
||||||
|
", backgroundImageURI='" + getBackgroundImageURI().toString() + '\'' +
|
||||||
|
", cardImageUrl='" + cardImageUrl + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package de.nicidienase.chaosflix.entities;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class MovieList {
|
||||||
|
public static final String MOVIE_CATEGORY[] = {
|
||||||
|
"Category Zero",
|
||||||
|
"Category One",
|
||||||
|
"Category Two",
|
||||||
|
"Category Three",
|
||||||
|
"Category Four",
|
||||||
|
"Category Five",
|
||||||
|
};
|
||||||
|
|
||||||
|
public static List<Movie> list;
|
||||||
|
|
||||||
|
public static List<Movie> setupMovies() {
|
||||||
|
list = new ArrayList<Movie>();
|
||||||
|
String title[] = {
|
||||||
|
"Zeitgeist 2010_ Year in Review",
|
||||||
|
"Google Demo Slam_ 20ft Search",
|
||||||
|
"Introducing Gmail Blue",
|
||||||
|
"Introducing Google Fiber to the Pole",
|
||||||
|
"Introducing Google Nose"
|
||||||
|
};
|
||||||
|
|
||||||
|
String description = "Fusce id nisi turpis. Praesent viverra bibendum semper. "
|
||||||
|
+ "Donec tristique, orci sed semper lacinia, quam erat rhoncus massa, non congue tellus est "
|
||||||
|
+ "quis tellus. Sed mollis orci venenatis quam scelerisque accumsan. Curabitur a massa sit "
|
||||||
|
+ "amet mi accumsan mollis sed et magna. Vivamus sed aliquam risus. Nulla eget dolor in elit "
|
||||||
|
+ "facilisis mattis. Ut aliquet luctus lacus. Phasellus nec commodo erat. Praesent tempus id "
|
||||||
|
+ "lectus ac scelerisque. Maecenas pretium cursus lectus id volutpat.";
|
||||||
|
|
||||||
|
String videoUrl[] = {
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Zeitgeist/Zeitgeist%202010_%20Year%20in%20Review.mp4",
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%2020ft%20Search.mp4",
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Gmail%20Blue.mp4",
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Fiber%20to%20the%20Pole.mp4",
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Nose.mp4"
|
||||||
|
};
|
||||||
|
String bgImageUrl[] = {
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Zeitgeist/Zeitgeist%202010_%20Year%20in%20Review/bg.jpg",
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%2020ft%20Search/bg.jpg",
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Gmail%20Blue/bg.jpg",
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Fiber%20to%20the%20Pole/bg.jpg",
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Nose/bg.jpg",
|
||||||
|
};
|
||||||
|
String cardImageUrl[] = {
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Zeitgeist/Zeitgeist%202010_%20Year%20in%20Review/card.jpg",
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%2020ft%20Search/card.jpg",
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Gmail%20Blue/card.jpg",
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Fiber%20to%20the%20Pole/card.jpg",
|
||||||
|
"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Nose/card.jpg"
|
||||||
|
};
|
||||||
|
|
||||||
|
list.add(buildMovieInfo("category", title[0],
|
||||||
|
description, "Studio Zero", videoUrl[0], cardImageUrl[0], bgImageUrl[0]));
|
||||||
|
list.add(buildMovieInfo("category", title[1],
|
||||||
|
description, "Studio One", videoUrl[1], cardImageUrl[1], bgImageUrl[1]));
|
||||||
|
list.add(buildMovieInfo("category", title[2],
|
||||||
|
description, "Studio Two", videoUrl[2], cardImageUrl[2], bgImageUrl[2]));
|
||||||
|
list.add(buildMovieInfo("category", title[3],
|
||||||
|
description, "Studio Three", videoUrl[3], cardImageUrl[3], bgImageUrl[3]));
|
||||||
|
list.add(buildMovieInfo("category", title[4],
|
||||||
|
description, "Studio Four", videoUrl[4], cardImageUrl[4], bgImageUrl[4]));
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Movie buildMovieInfo(String category, String title,
|
||||||
|
String description, String studio, String videoUrl, String cardImageUrl,
|
||||||
|
String bgImageUrl) {
|
||||||
|
Movie movie = new Movie();
|
||||||
|
movie.setId(Movie.getCount());
|
||||||
|
Movie.incCount();
|
||||||
|
movie.setTitle(title);
|
||||||
|
movie.setDescription(description);
|
||||||
|
movie.setStudio(studio);
|
||||||
|
movie.setCategory(category);
|
||||||
|
movie.setCardImageUrl(cardImageUrl);
|
||||||
|
movie.setBackgroundImageUrl(bgImageUrl);
|
||||||
|
movie.setVideoUrl(videoUrl);
|
||||||
|
return movie;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package de.nicidienase.chaosflix.fragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import de.nicidienase.chaosflix.R;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This class demonstrates how to extend ErrorFragment
|
||||||
|
*/
|
||||||
|
public class ErrorFragment extends android.support.v17.leanback.app.ErrorFragment {
|
||||||
|
private static final String TAG = "ErrorFragment";
|
||||||
|
private static final boolean TRANSLUCENT = true;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
Log.d(TAG, "onCreate");
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setTitle(getResources().getString(R.string.app_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorContent() {
|
||||||
|
setImageDrawable(getResources().getDrawable(R.drawable.lb_ic_sad_cloud));
|
||||||
|
setMessage(getResources().getString(R.string.error_fragment_message));
|
||||||
|
setDefaultBackground(TRANSLUCENT);
|
||||||
|
|
||||||
|
setButtonText(getResources().getString(R.string.dismiss_error));
|
||||||
|
setButtonClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View arg0) {
|
||||||
|
getFragmentManager().beginTransaction().remove(ErrorFragment.this).commit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,406 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.nicidienase.chaosflix.fragments;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import android.media.MediaMetadataRetriever;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
|
||||||
|
import android.support.v17.leanback.widget.Action;
|
||||||
|
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||||
|
import android.support.v17.leanback.widget.ClassPresenterSelector;
|
||||||
|
import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
|
||||||
|
import android.support.v17.leanback.widget.HeaderItem;
|
||||||
|
import android.support.v17.leanback.widget.ListRow;
|
||||||
|
import android.support.v17.leanback.widget.ListRowPresenter;
|
||||||
|
import android.support.v17.leanback.widget.OnActionClickedListener;
|
||||||
|
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||||
|
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
|
||||||
|
import android.support.v17.leanback.widget.PlaybackControlsRow;
|
||||||
|
import android.support.v17.leanback.widget.PlaybackControlsRow.FastForwardAction;
|
||||||
|
import android.support.v17.leanback.widget.PlaybackControlsRow.PlayPauseAction;
|
||||||
|
import android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction;
|
||||||
|
import android.support.v17.leanback.widget.PlaybackControlsRow.RewindAction;
|
||||||
|
import android.support.v17.leanback.widget.PlaybackControlsRow.ShuffleAction;
|
||||||
|
import android.support.v17.leanback.widget.PlaybackControlsRow.SkipNextAction;
|
||||||
|
import android.support.v17.leanback.widget.PlaybackControlsRow.SkipPreviousAction;
|
||||||
|
import android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsDownAction;
|
||||||
|
import android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsUpAction;
|
||||||
|
import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
|
||||||
|
import android.support.v17.leanback.widget.Presenter;
|
||||||
|
import android.support.v17.leanback.widget.Row;
|
||||||
|
import android.support.v17.leanback.widget.RowPresenter;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
|
||||||
|
import com.bumptech.glide.request.animation.GlideAnimation;
|
||||||
|
import com.bumptech.glide.request.target.SimpleTarget;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.nicidienase.chaosflix.CardPresenter;
|
||||||
|
import de.nicidienase.chaosflix.R;
|
||||||
|
import de.nicidienase.chaosflix.activities.DetailsActivity;
|
||||||
|
import de.nicidienase.chaosflix.entities.Movie;
|
||||||
|
import de.nicidienase.chaosflix.entities.MovieList;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class for video playback with media control
|
||||||
|
*/
|
||||||
|
public class PlaybackOverlayFragment extends android.support.v17.leanback.app.PlaybackOverlayFragment {
|
||||||
|
private static final String TAG = "PlaybackControlsFragmnt";
|
||||||
|
|
||||||
|
private static final boolean SHOW_DETAIL = true;
|
||||||
|
private static final boolean HIDE_MORE_ACTIONS = false;
|
||||||
|
private static final int PRIMARY_CONTROLS = 5;
|
||||||
|
private static final boolean SHOW_IMAGE = PRIMARY_CONTROLS <= 5;
|
||||||
|
private static final int BACKGROUND_TYPE = PlaybackOverlayFragment.BG_LIGHT;
|
||||||
|
private static final int CARD_WIDTH = 200;
|
||||||
|
private static final int CARD_HEIGHT = 240;
|
||||||
|
private static final int DEFAULT_UPDATE_PERIOD = 1000;
|
||||||
|
private static final int UPDATE_PERIOD = 16;
|
||||||
|
private static final int SIMULATED_BUFFERED_TIME = 10000;
|
||||||
|
|
||||||
|
private ArrayObjectAdapter mRowsAdapter;
|
||||||
|
private ArrayObjectAdapter mPrimaryActionsAdapter;
|
||||||
|
private ArrayObjectAdapter mSecondaryActionsAdapter;
|
||||||
|
private PlayPauseAction mPlayPauseAction;
|
||||||
|
private RepeatAction mRepeatAction;
|
||||||
|
private ThumbsUpAction mThumbsUpAction;
|
||||||
|
private ThumbsDownAction mThumbsDownAction;
|
||||||
|
private ShuffleAction mShuffleAction;
|
||||||
|
private FastForwardAction mFastForwardAction;
|
||||||
|
private RewindAction mRewindAction;
|
||||||
|
private SkipNextAction mSkipNextAction;
|
||||||
|
private SkipPreviousAction mSkipPreviousAction;
|
||||||
|
private PlaybackControlsRow mPlaybackControlsRow;
|
||||||
|
private ArrayList<Movie> mItems = new ArrayList<Movie>();
|
||||||
|
private int mCurrentItem;
|
||||||
|
private Handler mHandler;
|
||||||
|
private Runnable mRunnable;
|
||||||
|
private Movie mSelectedMovie;
|
||||||
|
|
||||||
|
private OnPlayPauseClickedListener mCallback;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
mItems = new ArrayList<Movie>();
|
||||||
|
mSelectedMovie = (Movie) getActivity()
|
||||||
|
.getIntent().getSerializableExtra(DetailsActivity.MOVIE);
|
||||||
|
|
||||||
|
List<Movie> movies = MovieList.list;
|
||||||
|
|
||||||
|
for (int j = 0; j < movies.size(); j++) {
|
||||||
|
mItems.add(movies.get(j));
|
||||||
|
if (mSelectedMovie.getTitle().contentEquals(movies.get(j).getTitle())) {
|
||||||
|
mCurrentItem = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mHandler = new Handler();
|
||||||
|
|
||||||
|
setBackgroundType(BACKGROUND_TYPE);
|
||||||
|
setFadingEnabled(false);
|
||||||
|
|
||||||
|
setupRows();
|
||||||
|
|
||||||
|
setOnItemViewSelectedListener(new OnItemViewSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
|
||||||
|
RowPresenter.ViewHolder rowViewHolder, Row row) {
|
||||||
|
Log.i(TAG, "onItemSelected: " + item + " row " + row);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setOnItemViewClickedListener(new OnItemViewClickedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
|
||||||
|
RowPresenter.ViewHolder rowViewHolder, Row row) {
|
||||||
|
Log.i(TAG, "onItemClicked: " + item + " row " + row);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Override
|
||||||
|
public void onAttach(Activity context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof OnPlayPauseClickedListener) {
|
||||||
|
mCallback = (OnPlayPauseClickedListener) context;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException(context.toString()
|
||||||
|
+ " must implement OnPlayPauseClickedListener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupRows() {
|
||||||
|
|
||||||
|
ClassPresenterSelector ps = new ClassPresenterSelector();
|
||||||
|
|
||||||
|
PlaybackControlsRowPresenter playbackControlsRowPresenter;
|
||||||
|
if (SHOW_DETAIL) {
|
||||||
|
playbackControlsRowPresenter = new PlaybackControlsRowPresenter(
|
||||||
|
new DescriptionPresenter());
|
||||||
|
} else {
|
||||||
|
playbackControlsRowPresenter = new PlaybackControlsRowPresenter();
|
||||||
|
}
|
||||||
|
playbackControlsRowPresenter.setOnActionClickedListener(new OnActionClickedListener() {
|
||||||
|
public void onActionClicked(Action action) {
|
||||||
|
if (action.getId() == mPlayPauseAction.getId()) {
|
||||||
|
togglePlayback(mPlayPauseAction.getIndex() == PlayPauseAction.PLAY);
|
||||||
|
} else if (action.getId() == mSkipNextAction.getId()) {
|
||||||
|
next();
|
||||||
|
} else if (action.getId() == mSkipPreviousAction.getId()) {
|
||||||
|
prev();
|
||||||
|
} else if (action.getId() == mFastForwardAction.getId()) {
|
||||||
|
Toast.makeText(getActivity(), "TODO: Fast Forward", Toast.LENGTH_SHORT).show();
|
||||||
|
} else if (action.getId() == mRewindAction.getId()) {
|
||||||
|
Toast.makeText(getActivity(), "TODO: Rewind", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
if (action instanceof PlaybackControlsRow.MultiAction) {
|
||||||
|
((PlaybackControlsRow.MultiAction) action).nextIndex();
|
||||||
|
notifyChanged(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
playbackControlsRowPresenter.setSecondaryActionsHidden(HIDE_MORE_ACTIONS);
|
||||||
|
|
||||||
|
ps.addClassPresenter(PlaybackControlsRow.class, playbackControlsRowPresenter);
|
||||||
|
ps.addClassPresenter(ListRow.class, new ListRowPresenter());
|
||||||
|
mRowsAdapter = new ArrayObjectAdapter(ps);
|
||||||
|
|
||||||
|
addPlaybackControlsRow();
|
||||||
|
addOtherRows();
|
||||||
|
|
||||||
|
setAdapter(mRowsAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void togglePlayback(boolean playPause) {
|
||||||
|
if (playPause) {
|
||||||
|
startProgressAutomation();
|
||||||
|
setFadingEnabled(true);
|
||||||
|
mCallback.onFragmentPlayPause(mItems.get(mCurrentItem),
|
||||||
|
mPlaybackControlsRow.getCurrentTime(), true);
|
||||||
|
mPlayPauseAction.setIcon(mPlayPauseAction.getDrawable(PlayPauseAction.PAUSE));
|
||||||
|
} else {
|
||||||
|
stopProgressAutomation();
|
||||||
|
setFadingEnabled(false);
|
||||||
|
mCallback.onFragmentPlayPause(mItems.get(mCurrentItem),
|
||||||
|
mPlaybackControlsRow.getCurrentTime(), false);
|
||||||
|
mPlayPauseAction.setIcon(mPlayPauseAction.getDrawable(PlayPauseAction.PLAY));
|
||||||
|
}
|
||||||
|
notifyChanged(mPlayPauseAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPlaybackControlsRow() {
|
||||||
|
if (SHOW_DETAIL) {
|
||||||
|
mPlaybackControlsRow = new PlaybackControlsRow(mSelectedMovie);
|
||||||
|
} else {
|
||||||
|
mPlaybackControlsRow = new PlaybackControlsRow();
|
||||||
|
}
|
||||||
|
mRowsAdapter.add(mPlaybackControlsRow);
|
||||||
|
|
||||||
|
updatePlaybackRow(mCurrentItem);
|
||||||
|
|
||||||
|
ControlButtonPresenterSelector presenterSelector = new ControlButtonPresenterSelector();
|
||||||
|
mPrimaryActionsAdapter = new ArrayObjectAdapter(presenterSelector);
|
||||||
|
mSecondaryActionsAdapter = new ArrayObjectAdapter(presenterSelector);
|
||||||
|
mPlaybackControlsRow.setPrimaryActionsAdapter(mPrimaryActionsAdapter);
|
||||||
|
mPlaybackControlsRow.setSecondaryActionsAdapter(mSecondaryActionsAdapter);
|
||||||
|
|
||||||
|
mPlayPauseAction = new PlayPauseAction(getActivity());
|
||||||
|
mRepeatAction = new RepeatAction(getActivity());
|
||||||
|
mThumbsUpAction = new ThumbsUpAction(getActivity());
|
||||||
|
mThumbsDownAction = new ThumbsDownAction(getActivity());
|
||||||
|
mShuffleAction = new ShuffleAction(getActivity());
|
||||||
|
mSkipNextAction = new PlaybackControlsRow.SkipNextAction(getActivity());
|
||||||
|
mSkipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(getActivity());
|
||||||
|
mFastForwardAction = new PlaybackControlsRow.FastForwardAction(getActivity());
|
||||||
|
mRewindAction = new PlaybackControlsRow.RewindAction(getActivity());
|
||||||
|
|
||||||
|
if (PRIMARY_CONTROLS > 5) {
|
||||||
|
mPrimaryActionsAdapter.add(mThumbsUpAction);
|
||||||
|
} else {
|
||||||
|
mSecondaryActionsAdapter.add(mThumbsUpAction);
|
||||||
|
}
|
||||||
|
mPrimaryActionsAdapter.add(mSkipPreviousAction);
|
||||||
|
if (PRIMARY_CONTROLS > 3) {
|
||||||
|
mPrimaryActionsAdapter.add(new PlaybackControlsRow.RewindAction(getActivity()));
|
||||||
|
}
|
||||||
|
mPrimaryActionsAdapter.add(mPlayPauseAction);
|
||||||
|
if (PRIMARY_CONTROLS > 3) {
|
||||||
|
mPrimaryActionsAdapter.add(new PlaybackControlsRow.FastForwardAction(getActivity()));
|
||||||
|
}
|
||||||
|
mPrimaryActionsAdapter.add(mSkipNextAction);
|
||||||
|
|
||||||
|
mSecondaryActionsAdapter.add(mRepeatAction);
|
||||||
|
mSecondaryActionsAdapter.add(mShuffleAction);
|
||||||
|
if (PRIMARY_CONTROLS > 5) {
|
||||||
|
mPrimaryActionsAdapter.add(mThumbsDownAction);
|
||||||
|
} else {
|
||||||
|
mSecondaryActionsAdapter.add(mThumbsDownAction);
|
||||||
|
}
|
||||||
|
mSecondaryActionsAdapter.add(new PlaybackControlsRow.HighQualityAction(getActivity()));
|
||||||
|
mSecondaryActionsAdapter.add(new PlaybackControlsRow.ClosedCaptioningAction(getActivity()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyChanged(Action action) {
|
||||||
|
ArrayObjectAdapter adapter = mPrimaryActionsAdapter;
|
||||||
|
if (adapter.indexOf(action) >= 0) {
|
||||||
|
adapter.notifyArrayItemRangeChanged(adapter.indexOf(action), 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
adapter = mSecondaryActionsAdapter;
|
||||||
|
if (adapter.indexOf(action) >= 0) {
|
||||||
|
adapter.notifyArrayItemRangeChanged(adapter.indexOf(action), 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
if (SHOW_IMAGE) {
|
||||||
|
updateVideoImage(mItems.get(mCurrentItem).getCardImageURI().toString());
|
||||||
|
}
|
||||||
|
mRowsAdapter.notifyArrayItemRangeChanged(0, 1);
|
||||||
|
mPlaybackControlsRow.setTotalTime(getDuration());
|
||||||
|
mPlaybackControlsRow.setCurrentTime(0);
|
||||||
|
mPlaybackControlsRow.setBufferedProgress(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addOtherRows() {
|
||||||
|
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter());
|
||||||
|
for (Movie movie : mItems) {
|
||||||
|
listRowAdapter.add(movie);
|
||||||
|
}
|
||||||
|
HeaderItem header = new HeaderItem(0, getString(R.string.related_movies));
|
||||||
|
mRowsAdapter.add(new ListRow(header, listRowAdapter));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getUpdatePeriod() {
|
||||||
|
if (getView() == null || mPlaybackControlsRow.getTotalTime() <= 0) {
|
||||||
|
return DEFAULT_UPDATE_PERIOD;
|
||||||
|
}
|
||||||
|
return Math.max(UPDATE_PERIOD, mPlaybackControlsRow.getTotalTime() / getView().getWidth());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startProgressAutomation() {
|
||||||
|
mRunnable = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
int updatePeriod = getUpdatePeriod();
|
||||||
|
int currentTime = mPlaybackControlsRow.getCurrentTime() + updatePeriod;
|
||||||
|
int totalTime = mPlaybackControlsRow.getTotalTime();
|
||||||
|
mPlaybackControlsRow.setCurrentTime(currentTime);
|
||||||
|
mPlaybackControlsRow.setBufferedProgress(currentTime + SIMULATED_BUFFERED_TIME);
|
||||||
|
|
||||||
|
if (totalTime > 0 && totalTime <= currentTime) {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
mHandler.postDelayed(this, updatePeriod);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mHandler.postDelayed(mRunnable, getUpdatePeriod());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void next() {
|
||||||
|
if (++mCurrentItem >= mItems.size()) {
|
||||||
|
mCurrentItem = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPlayPauseAction.getIndex() == PlayPauseAction.PLAY) {
|
||||||
|
mCallback.onFragmentPlayPause(mItems.get(mCurrentItem), 0, false);
|
||||||
|
} else {
|
||||||
|
mCallback.onFragmentPlayPause(mItems.get(mCurrentItem), 0, true);
|
||||||
|
}
|
||||||
|
updatePlaybackRow(mCurrentItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prev() {
|
||||||
|
if (--mCurrentItem < 0) {
|
||||||
|
mCurrentItem = mItems.size() - 1;
|
||||||
|
}
|
||||||
|
if (mPlayPauseAction.getIndex() == PlayPauseAction.PLAY) {
|
||||||
|
mCallback.onFragmentPlayPause(mItems.get(mCurrentItem), 0, false);
|
||||||
|
} else {
|
||||||
|
mCallback.onFragmentPlayPause(mItems.get(mCurrentItem), 0, true);
|
||||||
|
}
|
||||||
|
updatePlaybackRow(mCurrentItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopProgressAutomation() {
|
||||||
|
if (mHandler != null && mRunnable != null) {
|
||||||
|
mHandler.removeCallbacks(mRunnable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
stopProgressAutomation();
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateVideoImage(String uri) {
|
||||||
|
Glide.with(getActivity())
|
||||||
|
.load(uri)
|
||||||
|
.centerCrop()
|
||||||
|
.into(new SimpleTarget<GlideDrawable>(CARD_WIDTH, CARD_HEIGHT) {
|
||||||
|
@Override
|
||||||
|
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
|
||||||
|
mPlaybackControlsRow.setImageDrawable(resource);
|
||||||
|
mRowsAdapter.notifyArrayItemRangeChanged(0, mRowsAdapter.size());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container Activity must implement this interface
|
||||||
|
public interface OnPlayPauseClickedListener {
|
||||||
|
void onFragmentPlayPause(Movie movie, int position, Boolean playPause);
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,239 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.nicidienase.chaosflix.fragments;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v17.leanback.app.BackgroundManager;
|
||||||
|
import android.support.v17.leanback.app.DetailsFragment;
|
||||||
|
import android.support.v17.leanback.widget.Action;
|
||||||
|
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;
|
||||||
|
import android.support.v17.leanback.widget.OnActionClickedListener;
|
||||||
|
import android.support.v17.leanback.widget.OnItemViewClickedListener;
|
||||||
|
import android.support.v17.leanback.widget.Presenter;
|
||||||
|
import android.support.v17.leanback.widget.Row;
|
||||||
|
import android.support.v17.leanback.widget.RowPresenter;
|
||||||
|
import android.support.v4.app.ActivityOptionsCompat;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
|
||||||
|
import com.bumptech.glide.request.animation.GlideAnimation;
|
||||||
|
import com.bumptech.glide.request.target.SimpleTarget;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.nicidienase.chaosflix.CardPresenter;
|
||||||
|
import de.nicidienase.chaosflix.DetailsDescriptionPresenter;
|
||||||
|
import de.nicidienase.chaosflix.activities.PlaybackOverlayActivity;
|
||||||
|
import de.nicidienase.chaosflix.R;
|
||||||
|
import de.nicidienase.chaosflix.Utils;
|
||||||
|
import de.nicidienase.chaosflix.activities.DetailsActivity;
|
||||||
|
import de.nicidienase.chaosflix.activities.MainActivity;
|
||||||
|
import de.nicidienase.chaosflix.entities.Movie;
|
||||||
|
import de.nicidienase.chaosflix.entities.MovieList;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LeanbackDetailsFragment extends DetailsFragment, a Wrapper fragment for leanback details screens.
|
||||||
|
* It shows a detailed view of video and its meta plus related videos.
|
||||||
|
*/
|
||||||
|
public class VideoDetailsFragment extends DetailsFragment {
|
||||||
|
private static final String TAG = "VideoDetailsFragment";
|
||||||
|
|
||||||
|
private static final int ACTION_WATCH_TRAILER = 1;
|
||||||
|
private static final int ACTION_RENT = 2;
|
||||||
|
private static final int ACTION_BUY = 3;
|
||||||
|
|
||||||
|
private static final int DETAIL_THUMB_WIDTH = 274;
|
||||||
|
private static final int DETAIL_THUMB_HEIGHT = 274;
|
||||||
|
|
||||||
|
private static final int NUM_COLS = 10;
|
||||||
|
|
||||||
|
private Movie mSelectedMovie;
|
||||||
|
|
||||||
|
private ArrayObjectAdapter mAdapter;
|
||||||
|
private ClassPresenterSelector mPresenterSelector;
|
||||||
|
|
||||||
|
private BackgroundManager mBackgroundManager;
|
||||||
|
private Drawable mDefaultBackground;
|
||||||
|
private DisplayMetrics mMetrics;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
Log.d(TAG, "onCreate DetailsFragment");
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
prepareBackgroundManager();
|
||||||
|
|
||||||
|
mSelectedMovie = (Movie) getActivity().getIntent()
|
||||||
|
.getSerializableExtra(DetailsActivity.MOVIE);
|
||||||
|
if (mSelectedMovie != null) {
|
||||||
|
setupAdapter();
|
||||||
|
setupDetailsOverviewRow();
|
||||||
|
setupDetailsOverviewRowPresenter();
|
||||||
|
setupMovieListRow();
|
||||||
|
setupMovieListRowPresenter();
|
||||||
|
updateBackground(mSelectedMovie.getBackgroundImageUrl());
|
||||||
|
setOnItemViewClickedListener(new ItemViewClickedListener());
|
||||||
|
} else {
|
||||||
|
Intent intent = new Intent(getActivity(), MainActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareBackgroundManager() {
|
||||||
|
mBackgroundManager = BackgroundManager.getInstance(getActivity());
|
||||||
|
mBackgroundManager.attach(getActivity().getWindow());
|
||||||
|
mDefaultBackground = getResources().getDrawable(R.drawable.default_background);
|
||||||
|
mMetrics = new DisplayMetrics();
|
||||||
|
getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateBackground(String uri) {
|
||||||
|
Glide.with(getActivity())
|
||||||
|
.load(uri)
|
||||||
|
.centerCrop()
|
||||||
|
.error(mDefaultBackground)
|
||||||
|
.into(new SimpleTarget<GlideDrawable>(mMetrics.widthPixels, mMetrics.heightPixels) {
|
||||||
|
@Override
|
||||||
|
public void onResourceReady(GlideDrawable resource,
|
||||||
|
GlideAnimation<? super GlideDrawable> glideAnimation) {
|
||||||
|
mBackgroundManager.setDrawable(resource);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupAdapter() {
|
||||||
|
mPresenterSelector = new ClassPresenterSelector();
|
||||||
|
mAdapter = new ArrayObjectAdapter(mPresenterSelector);
|
||||||
|
setAdapter(mAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupDetailsOverviewRow() {
|
||||||
|
Log.d(TAG, "doInBackground: " + mSelectedMovie.toString());
|
||||||
|
final DetailsOverviewRow row = new DetailsOverviewRow(mSelectedMovie);
|
||||||
|
row.setImageDrawable(getResources().getDrawable(R.drawable.default_background));
|
||||||
|
int width = Utils.convertDpToPixel(getActivity()
|
||||||
|
.getApplicationContext(), DETAIL_THUMB_WIDTH);
|
||||||
|
int height = Utils.convertDpToPixel(getActivity()
|
||||||
|
.getApplicationContext(), DETAIL_THUMB_HEIGHT);
|
||||||
|
Glide.with(getActivity())
|
||||||
|
.load(mSelectedMovie.getCardImageUrl())
|
||||||
|
.centerCrop()
|
||||||
|
.error(R.drawable.default_background)
|
||||||
|
.into(new SimpleTarget<GlideDrawable>(width, height) {
|
||||||
|
@Override
|
||||||
|
public void onResourceReady(GlideDrawable resource,
|
||||||
|
GlideAnimation<? super GlideDrawable>
|
||||||
|
glideAnimation) {
|
||||||
|
Log.d(TAG, "details overview card image url ready: " + resource);
|
||||||
|
row.setImageDrawable(resource);
|
||||||
|
mAdapter.notifyArrayItemRangeChanged(0, mAdapter.size());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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_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),
|
||||||
|
getResources().getString(R.string.buy_2)));
|
||||||
|
|
||||||
|
mAdapter.add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupDetailsOverviewRowPresenter() {
|
||||||
|
// Set detail background and style.
|
||||||
|
DetailsOverviewRowPresenter detailsPresenter =
|
||||||
|
new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter());
|
||||||
|
detailsPresenter.setBackgroundColor(getResources().getColor(R.color.selected_background));
|
||||||
|
detailsPresenter.setStyleLarge(true);
|
||||||
|
|
||||||
|
// Hook up transition element.
|
||||||
|
detailsPresenter.setSharedElementEnterTransition(getActivity(),
|
||||||
|
DetailsActivity.SHARED_ELEMENT_NAME);
|
||||||
|
|
||||||
|
detailsPresenter.setOnActionClickedListener(new OnActionClickedListener() {
|
||||||
|
@Override
|
||||||
|
public void onActionClicked(Action action) {
|
||||||
|
if (action.getId() == ACTION_WATCH_TRAILER) {
|
||||||
|
Intent intent = new Intent(getActivity(), PlaybackOverlayActivity.class);
|
||||||
|
intent.putExtra(DetailsActivity.MOVIE, mSelectedMovie);
|
||||||
|
startActivity(intent);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getActivity(), action.toString(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mPresenterSelector.addClassPresenter(DetailsOverviewRow.class, detailsPresenter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupMovieListRow() {
|
||||||
|
String subcategories[] = {getString(R.string.related_movies)};
|
||||||
|
List<Movie> list = MovieList.list;
|
||||||
|
|
||||||
|
Collections.shuffle(list);
|
||||||
|
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter());
|
||||||
|
for (int j = 0; j < NUM_COLS; j++) {
|
||||||
|
listRowAdapter.add(list.get(j % 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderItem header = new HeaderItem(0, subcategories[0]);
|
||||||
|
mAdapter.add(new ListRow(header, listRowAdapter));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupMovieListRowPresenter() {
|
||||||
|
mPresenterSelector.addClassPresenter(ListRow.class, new ListRowPresenter());
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class ItemViewClickedListener implements OnItemViewClickedListener {
|
||||||
|
@Override
|
||||||
|
public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
|
||||||
|
RowPresenter.ViewHolder rowViewHolder, Row row) {
|
||||||
|
|
||||||
|
if (item instanceof Movie) {
|
||||||
|
Movie movie = (Movie) item;
|
||||||
|
Log.d(TAG, "Item: " + item.toString());
|
||||||
|
Intent intent = new Intent(getActivity(), DetailsActivity.class);
|
||||||
|
intent.putExtra(getResources().getString(R.string.movie), mSelectedMovie);
|
||||||
|
intent.putExtra(getResources().getString(R.string.should_start), true);
|
||||||
|
startActivity(intent);
|
||||||
|
|
||||||
|
|
||||||
|
Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
||||||
|
getActivity(),
|
||||||
|
((ImageCardView) itemViewHolder.view).getMainImageView(),
|
||||||
|
DetailsActivity.SHARED_ELEMENT_NAME).toBundle();
|
||||||
|
getActivity().startActivity(intent, bundle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
app/src/main/res/drawable-hdpi/app_icon_quantum.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
app/src/main/res/drawable-hdpi/app_icon_quantum_card.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable-hdpi/card_background_default.9.png
Normal file
After Width: | Height: | Size: 15 KiB |
9
app/src/main/res/drawable-hdpi/default_background.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<gradient
|
||||||
|
android:angle="-270"
|
||||||
|
android:endColor="@color/background_gradient_end"
|
||||||
|
android:startColor="@color/background_gradient_start"/>
|
||||||
|
</shape>
|
BIN
app/src/main/res/drawable-hdpi/grid_bg.png
Normal file
After Width: | Height: | Size: 391 KiB |
BIN
app/src/main/res/drawable-hdpi/movie.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
app/src/main/res/drawable-hdpi/scrubber_disabled.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-hdpi/scrubber_focussed.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/drawable-hdpi/scrubber_normal.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable-hdpi/scrubber_pressed.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/main/res/drawable-hdpi/shadow7.9.png
Normal file
After Width: | Height: | Size: 233 B |
BIN
app/src/main/res/drawable-hdpi/star_icon.png
Normal file
After Width: | Height: | Size: 4 KiB |
BIN
app/src/main/res/drawable-hdpi/videos_by_google_banner.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
app/src/main/res/drawable-hdpi/videos_by_google_icon.png
Normal file
After Width: | Height: | Size: 882 B |
BIN
app/src/main/res/drawable-mdpi/app_icon_quantum.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
app/src/main/res/drawable-mdpi/app_icon_quantum_card.png
Normal file
After Width: | Height: | Size: 750 B |
BIN
app/src/main/res/drawable-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
app/src/main/res/drawable-mdpi/videos_by_google_banner.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
app/src/main/res/drawable-mdpi/videos_by_google_icon.png
Normal file
After Width: | Height: | Size: 535 B |
BIN
app/src/main/res/drawable-xhdpi/app_icon_quantum.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
app/src/main/res/drawable-xhdpi/app_icon_quantum_card.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
9
app/src/main/res/drawable-xhdpi/default_background.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<gradient
|
||||||
|
android:angle="-270"
|
||||||
|
android:endColor="@color/background_gradient_end"
|
||||||
|
android:startColor="@color/background_gradient_start"/>
|
||||||
|
</shape>
|
BIN
app/src/main/res/drawable-xhdpi/grid_bg.png
Normal file
After Width: | Height: | Size: 391 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 1,004 B |
BIN
app/src/main/res/drawable-xhdpi/ic_pause_playcontrol_normal.png
Normal file
After Width: | Height: | Size: 677 B |
BIN
app/src/main/res/drawable-xhdpi/ic_pause_playcontrol_pressed.png
Normal file
After Width: | Height: | Size: 885 B |
BIN
app/src/main/res/drawable-xhdpi/ic_play_action_focussed.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_play_action_normal.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_play_action_pressed.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_play_playcontrol_focussed.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_play_playcontrol_normal.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_play_playcontrol_pressed.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/drawable-xhdpi/movie.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
app/src/main/res/drawable-xhdpi/star_icon.png
Normal file
After Width: | Height: | Size: 4 KiB |
BIN
app/src/main/res/drawable-xhdpi/videos_by_google_banner.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
app/src/main/res/drawable-xhdpi/videos_by_google_icon.png
Normal file
After Width: | Height: | Size: 882 B |
BIN
app/src/main/res/drawable-xxhdpi/app_icon_quantum.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
app/src/main/res/drawable-xxhdpi/app_icon_quantum_card.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable-xxhdpi/videos_by_google_banner.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
app/src/main/res/drawable-xxhdpi/videos_by_google_icon.png
Normal file
After Width: | Height: | Size: 632 B |
BIN
app/src/main/res/drawable/app_icon_quantum.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
app/src/main/res/drawable/app_icon_quantum_card.png
Normal file
After Width: | Height: | Size: 1,003 B |
BIN
app/src/main/res/drawable/app_icon_your_company.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
app/src/main/res/drawable/details_img.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
app/src/main/res/drawable/ic_action_a.png
Normal file
After Width: | Height: | Size: 720 B |
BIN
app/src/main/res/drawable/ic_title.png
Normal file
After Width: | Height: | Size: 243 B |
BIN
app/src/main/res/drawable/movie.png
Normal file
After Width: | Height: | Size: 13 KiB |
11
app/src/main/res/drawable/player_bg_gradient_dark.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<gradient
|
||||||
|
android:angle="90"
|
||||||
|
android:centerColor="#00000000"
|
||||||
|
android:endColor="#B2000000"
|
||||||
|
android:startColor="#B2000000"
|
||||||
|
android:type="linear"/>
|
||||||
|
|
||||||
|
</shape>
|
BIN
app/src/main/res/drawable/shadow7.9.png
Normal file
After Width: | Height: | Size: 233 B |
BIN
app/src/main/res/drawable/videos_by_google_banner.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
app/src/main/res/drawable/videos_by_google_icon.png
Normal file
After Width: | Height: | Size: 707 B |
9
app/src/main/res/layout/activity_details.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<fragment android:id="@+id/details_fragment"
|
||||||
|
android:name="de.nicidienase.chaosflix.fragments.VideoDetailsFragment"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".activities.DetailsActivity"
|
||||||
|
tools:deviceIds="tv"/>
|
10
app/src/main/res/layout/activity_main.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<fragment android:id="@+id/main_browse_fragment"
|
||||||
|
android:name="de.nicidienase.chaosflix.fragments.MainFragment"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".activities.MainActivity"
|
||||||
|
tools:deviceIds="tv"
|
||||||
|
tools:ignore="MergeRootFrame"/>
|
34
app/src/main/res/layout/playback_controls.xml
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2014 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<VideoView
|
||||||
|
android:id="@+id/videoView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"/>
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/playback_controls_fragment"
|
||||||
|
android:name="de.nicidienase.chaosflix.fragments.PlaybackOverlayFragment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
18
app/src/main/res/values/colors.xml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<resources>
|
||||||
|
<color name="background_gradient_start">#000000</color>
|
||||||
|
<color name="background_gradient_end">#DDDDDD</color>
|
||||||
|
<color name="fastlane_background">#0096a6</color>
|
||||||
|
<color name="search_opaque">#ffaa3f</color>
|
||||||
|
<color name="selected_background">#ffaa3f</color>
|
||||||
|
<color name="detail_background">#0096a6</color>
|
||||||
|
<color name="soft_opaque">#30000000</color>
|
||||||
|
<color name="img_soft_opaque">#30FF0000</color>
|
||||||
|
<color name="img_full_opaque">#00000000</color>
|
||||||
|
<color name="black_opaque">#AA000000</color>
|
||||||
|
<color name="black">#59000000</color>
|
||||||
|
<color name="white">#FFFFFF</color>
|
||||||
|
<color name="orange_transparent">#AAFADCA7</color>
|
||||||
|
<color name="orange">#FADCA7</color>
|
||||||
|
<color name="yellow">#EEFF41</color>
|
||||||
|
<color name="default_background">#3d3d3d</color>
|
||||||
|
</resources>
|
40
app/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Chaosflix</string>
|
||||||
|
<string name="browse_title">33c3 - Works for Me</string>
|
||||||
|
<string name="related_movies">Related Videos</string>
|
||||||
|
<string name="vertical_grid_title"><![CDATA[Vertical Video Grid]]></string>
|
||||||
|
<string name="error">Error</string>
|
||||||
|
<string name="ok">OK</string>
|
||||||
|
<string name="pause">Pause</string>
|
||||||
|
<string name="play">Play</string>
|
||||||
|
<string name="stop">Stop</string>
|
||||||
|
<string name="init_text">00:00</string>
|
||||||
|
<string name="play_pause_description">Play Pause Button</string>
|
||||||
|
<string name="loading">Loading…</string>
|
||||||
|
<string name="no_video_found">No video was found</string>
|
||||||
|
<string name="about_app">About DemoCast Player</string>
|
||||||
|
<string name="version">Version: %1$s</string>
|
||||||
|
<string name="popular_header">Popular Videos</string>
|
||||||
|
<string name="preferences">PREFERENCES</string>
|
||||||
|
<string name="grid_view">Grid View</string>
|
||||||
|
<string name="error_fragment">Error Fragment</string>
|
||||||
|
<string name="personal_settings">Personal Settings</string>
|
||||||
|
<string name="watch_trailer_1">Watch trailer</string>
|
||||||
|
<string name="watch_trailer_2">FREE</string>
|
||||||
|
<string name="rent_1">Rent By Day</string>
|
||||||
|
<string name="rent_2">From $1.99</string>
|
||||||
|
<string name="buy_1">Buy and Own</string>
|
||||||
|
<string name="buy_2">AT $9.99</string>
|
||||||
|
<string name="movie">Movie</string>
|
||||||
|
<string name="should_start">shouldStart</string>
|
||||||
|
<string name="start_position">startPosition</string>
|
||||||
|
<string name="search_results">Search Results</string>
|
||||||
|
|
||||||
|
<!-- Error messages -->
|
||||||
|
<string name="video_error_media_load_timeout">Media loading timed out</string>
|
||||||
|
<string name="video_error_server_inaccessible">Media server was not reachable</string>
|
||||||
|
<string name="video_error_unknown_error">Failed to load video</string>
|
||||||
|
<string name="error_fragment_message">An error occurred</string>
|
||||||
|
<string name="dismiss_error">Dismiss</string>
|
||||||
|
<string name="oops">Oops</string>
|
||||||
|
</resources>
|
6
app/src/main/res/values/styles.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="AppTheme" parent="@style/Theme.Leanback">
|
||||||
|
</style>
|
||||||
|
</resources>
|
14
app/src/main/res/values/themes.xml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="Theme.Example.Leanback" parent="Theme.Leanback">
|
||||||
|
<item name="android:windowEnterTransition">@android:transition/fade</item>
|
||||||
|
<item name="android:windowExitTransition">@android:transition/fade</item>
|
||||||
|
<item name="android:windowSharedElementExitTransition">@android:transition/move</item>
|
||||||
|
<item name="android:windowSharedElementEnterTransition">@android:transition/move</item>
|
||||||
|
<item name="android:windowAllowReturnTransitionOverlap">true</item>
|
||||||
|
<item name="android:windowAllowEnterTransitionOverlap">true</item>
|
||||||
|
<item name="android:windowContentTransitions">true</item>
|
||||||
|
<item name="android:colorPrimary">@color/search_opaque</item>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</resources>
|