Start initial framework

This commit is contained in:
Eric Reed 2014-12-04 16:49:48 -05:00
parent b1a19b2d81
commit 33329189de
121 changed files with 3806 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build

3
AdbConnect.bat Normal file
View File

@ -0,0 +1,3 @@
cd C:\Users\Eric\AppData\Local\Android\android-sdk\platform-tools
adb connect 192.168.1.106:4321
pause

6
MediaBrowserTv/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build

1
MediaBrowserTv/.idea/.name generated Normal file
View File

@ -0,0 +1 @@
MediaBrowserTv

23
MediaBrowserTv/.idea/compiler.xml generated Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<option name="DEFAULT_COMPILER" value="Javac" />
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>

View File

@ -0,0 +1,3 @@
<component name="CopyrightManager">
<settings default="" />
</component>

5
MediaBrowserTv/.idea/encodings.xml generated Normal file
View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>

19
MediaBrowserTv/.idea/gradle.xml generated Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="LOCAL" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="C:\Program Files (x86)\android-studio\gradle\gradle-2.2.1" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

10
MediaBrowserTv/.idea/misc.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
</project>

10
MediaBrowserTv/.idea/modules.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/MediaBrowserTv.iml" filepath="$PROJECT_DIR$/MediaBrowserTv.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,5 @@
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>

7
MediaBrowserTv/.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="" />
</component>
</project>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/classes/main" />
<output-test url="file://$MODULE_DIR$/build/classes/test" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

1
MediaBrowserTv/app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

102
MediaBrowserTv/app/app.iml Normal file
View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":app" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugTest" />
<option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
<option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugTestSources" />
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/test/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/test/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/test/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/test/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/test/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/test/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="Java-WebSocket-1.3.0" level="project" />
<orderEntry type="library" exported="" name="support-v4-21.0.2" level="project" />
<orderEntry type="library" exported="" name="okio-1.0.1" level="project" />
<orderEntry type="library" exported="" name="MediaBrowser.ApiClient.Java" level="project" />
<orderEntry type="library" exported="" name="recyclerview-v7-21.0.2" level="project" />
<orderEntry type="library" exported="" name="picasso-2.3.2" level="project" />
<orderEntry type="library" exported="" name="disklrucache-2.0.2" level="project" />
<orderEntry type="library" exported="" name="support-annotations-21.0.2" level="project" />
<orderEntry type="library" exported="" name="okhttp-urlconnection-2.1.0" level="project" />
<orderEntry type="library" exported="" name="volley-2014.10.9" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-21.0.2" level="project" />
<orderEntry type="library" exported="" name="gson-2.3" level="project" />
<orderEntry type="library" exported="" name="okhttp-2.1.0" level="project" />
<orderEntry type="library" exported="" name="leanback-v17-21.0.2" level="project" />
</component>
</module>

View File

@ -0,0 +1,38 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1.1"
defaultConfig {
applicationId "tv.mediabrowser.mediabrowsertv"
minSdkVersion 21
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:recyclerview-v7:21.0.2'
compile 'com.android.support:leanback-v17:21.0.2'
compile 'com.android.support:appcompat-v7:21.0.2'
compile 'com.squareup.picasso:picasso:2.3.2'
compile 'com.google.code.gson:gson:2.3'
compile 'com.squareup.okhttp:okhttp:2.1.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.1.0'
compile 'com.squareup.okio:okio:1.0.1'
compile 'me.neavo:volley:2014.10.9'}

17
MediaBrowserTv/app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Users\Eric\AppData\Local\Android\android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@ -0,0 +1,13 @@
package tv.mediabrowser.mediabrowsertv;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="tv.mediabrowser.mediabrowsertv"
android:versionCode="1"
android:versionName="0.0.1" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<application
android:name="tv.mediabrowser.mediabrowsertv.TvApp"
android:banner="@drawable/app_icon"
android:label="@string/app_name"
android:theme="@style/Theme.Leanback" >
<activity
android:name="tv.mediabrowser.mediabrowsertv.MainActivity"
android:label="@string/app_name"
android:logo="@drawable/app_icon"
android:screenOrientation="landscape" >
</activity>
<activity
android:name="tv.mediabrowser.mediabrowsertv.PlayerActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" />
<activity android:name="tv.mediabrowser.mediabrowsertv.DetailsActivity" />
<activity
android:name="tv.mediabrowser.mediabrowsertv.StartupActivity"
android:label="@string/app_name"
android:logo="@drawable/app_icon"
android:noHistory="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="tv.mediabrowser.mediabrowsertv.ConnectActivity"
android:label="@string/title_activity_connect" >
</activity>
<activity
android:name="tv.mediabrowser.mediabrowsertv.SelectUserActivity"
android:label="@string/title_activity_select_user" >
</activity>
</application>
</manifest>

View File

@ -0,0 +1,79 @@
/*
* 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 tv.mediabrowser.mediabrowsertv;
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;
/*
* 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_home);
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;
}
}
}

View File

@ -0,0 +1,29 @@
package tv.mediabrowser.mediabrowsertv;
import java.util.List;
import mediabrowser.model.querying.ItemQuery;
/**
* Created by Eric on 12/4/2014.
*/
public class BrowseRowDef {
private String headerText;
private List<ItemQuery> itemQueries;
public String getHeaderText() {
return headerText;
}
public void setHeaderText(String headerText) {
this.headerText = headerText;
}
public List<ItemQuery> getItemQueries() {
return itemQueries;
}
public void setItemQueries(List<ItemQuery> itemQueries) {
this.itemQueries = itemQueries;
}
}

View File

@ -0,0 +1,133 @@
package tv.mediabrowser.mediabrowsertv;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v17.leanback.widget.ImageCardView;
import android.support.v17.leanback.widget.Presenter;
import android.view.View;
import android.view.ViewGroup;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import java.net.URI;
import mediabrowser.model.dto.BaseItemDto;
public class CardPresenter extends Presenter {
private static final String TAG = "CardPresenter";
private static Context mContext;
private static int CARD_WIDTH = 313;
private static int CARD_HEIGHT = 176;
private TvApp application;
public CardPresenter() {
super();
application = TvApp.getApplication();
}
static class ViewHolder extends Presenter.ViewHolder {
private BaseItemDto mItem;
private ImageCardView mCardView;
private Drawable mDefaultCardImage;
private PicassoImageCardViewTarget mImageCardViewTarget;
public ViewHolder(View view) {
super(view);
mCardView = (ImageCardView) view;
mImageCardViewTarget = new PicassoImageCardViewTarget(mCardView);
mDefaultCardImage = mContext.getResources().getDrawable(R.drawable.movie);
}
public void setItem(BaseItemDto m) {
mItem = m;
}
public BaseItemDto getItem() {
return mItem;
}
public ImageCardView getCardView() {
return mCardView;
}
protected void updateCardViewImage(String url) {
Picasso.with(mContext)
.load(url)
.resize(CARD_WIDTH, CARD_HEIGHT)
.centerCrop()
.error(mDefaultCardImage)
.into(mImageCardViewTarget);
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
//Log.d(TAG, "onCreateViewHolder");
mContext = parent.getContext();
ImageCardView cardView = new ImageCardView(mContext);
cardView.setFocusable(true);
cardView.setFocusableInTouchMode(true);
cardView.setBackgroundColor(mContext.getResources().getColor(R.color.fastlane_background));
return new ViewHolder(cardView);
}
@Override
public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
BaseItemDto baseItem = (BaseItemDto) item;
((ViewHolder) viewHolder).setItem(baseItem);
//Log.d(TAG, "onBindViewHolder");
if (baseItem != null) {
((ViewHolder) viewHolder).mCardView.setTitleText(baseItem.getName());
((ViewHolder) viewHolder).mCardView.setContentText(baseItem.getProductionYear().toString());
((ViewHolder) viewHolder).mCardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT);
//((ViewHolder) viewHolder).mCardView.setBadgeImage(mContext.getResources().getDrawable(
// R.drawable.videos_by_google_icon));
((ViewHolder) viewHolder).updateCardViewImage(Utils.getPrimaryImageUrl(baseItem, application.getConnectionManager().GetApiClient(baseItem)));
}
}
@Override
public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
//Log.d(TAG, "onUnbindViewHolder");
}
@Override
public void onViewAttachedToWindow(Presenter.ViewHolder viewHolder) {
//Log.d(TAG, "onViewAttachedToWindow");
}
public static class PicassoImageCardViewTarget implements Target {
private ImageCardView mImageCardView;
public PicassoImageCardViewTarget(ImageCardView mImageCardView) {
this.mImageCardView = mImageCardView;
}
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
Drawable bitmapDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
mImageCardView.setMainImage(bitmapDrawable);
}
@Override
public void onBitmapFailed(Drawable drawable) {
mImageCardView.setMainImage(drawable);
}
@Override
public void onPrepareLoad(Drawable drawable) {
// Do nothing, default_background manager has its own transitions
}
}
}

View File

@ -0,0 +1,50 @@
package tv.mediabrowser.mediabrowsertv;
import android.app.Activity;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build;
import android.widget.TextView;
import mediabrowser.apiinteraction.IConnectionManager;
import mediabrowser.apiinteraction.connectionmanager.ConnectionManager;
public class ConnectActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_connect);
IConnectionManager connectionManager = ((TvApp) getApplicationContext()).getConnectionManager();
TextView pin = (TextView) findViewById(R.id.textViewPin);
pin.setText("34545");
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_connect, container, false);
return rootView;
}
}
}

View File

@ -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 tv.mediabrowser.mediabrowsertv;
import android.app.Activity;
import android.os.Bundle;
import tv.mediabrowser.mediabrowsertv.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);
}
}

View File

@ -0,0 +1,16 @@
package tv.mediabrowser.mediabrowsertv;
import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
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());
}
}
}

View File

@ -0,0 +1,48 @@
/*
* 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 tv.mediabrowser.mediabrowsertv;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
/*
* 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));
}
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();
}
});
}
}

View File

@ -0,0 +1,16 @@
package tv.mediabrowser.mediabrowsertv;
import android.os.Bundle;
/**
* Created by Eric on 12/4/2014.
*/
public class HomeFragment extends StdBrowseFragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
MainTitle = this.getString(R.string.home_title);
super.onActivityCreated(savedInstanceState);
}
}

View File

@ -0,0 +1,13 @@
package tv.mediabrowser.mediabrowsertv;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
}
}

View File

@ -0,0 +1,262 @@
package tv.mediabrowser.mediabrowsertv;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v17.leanback.app.BackgroundManager;
import android.support.v17.leanback.app.BrowseFragment;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
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.OnItemClickedListener;
import android.support.v17.leanback.widget.OnItemSelectedListener;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.Row;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import mediabrowser.apiinteraction.ApiClient;
import mediabrowser.apiinteraction.ApiEventListener;
import mediabrowser.apiinteraction.ConnectionResult;
import mediabrowser.apiinteraction.IConnectionManager;
import mediabrowser.apiinteraction.android.AndroidConnectionManager;
import mediabrowser.apiinteraction.android.GsonJsonSerializer;
import mediabrowser.logging.ConsoleLogger;
import mediabrowser.apiinteraction.Response;
import mediabrowser.apiinteraction.android.AndroidApiClient;
import mediabrowser.apiinteraction.android.AndroidDevice;
import mediabrowser.apiinteraction.android.VolleyHttpClient;
import mediabrowser.apiinteraction.device.IDevice;
import mediabrowser.apiinteraction.http.IAsyncHttpClient;
import mediabrowser.apiinteraction.serialization.BoonJsonSerializer;
import mediabrowser.model.logging.ILogger;
import mediabrowser.model.serialization.IJsonSerializer;
import mediabrowser.model.session.ClientCapabilities;
import mediabrowser.model.users.AuthenticationResult;
public class MainFragment extends BrowseFragment {
private static final String TAG = "MainFragment";
private static final int NUM_ROWS = 6;
private static final int NUM_COLS = 15;
private ArrayObjectAdapter mRowsAdapter;
private Drawable mDefaultBackground;
private Target mBackgroundTarget;
private DisplayMetrics mMetrics;
private Timer mBackgroundTimer;
private final Handler mHandler = new Handler();
private URI mBackgroundURI;
Movie mMovie;
CardPresenter mCardPresenter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
//Log.i(TAG, "onCreate");
super.onActivityCreated(savedInstanceState);
prepareBackgroundManager();
setupUIElements();
loadRows();
setupEventListeners();
}
private void loadRows() {
List<Movie> list = MovieList.setupMovies();
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
mCardPresenter = new CardPresenter();
int i;
for (i = 0; i < NUM_ROWS; i++) {
if (i != 0) {
Collections.shuffle(list);
}
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(mCardPresenter);
for (int j = 0; j < NUM_COLS; j++) {
listRowAdapter.add(list.get(j % 5));
}
HeaderItem header = new HeaderItem(i, MovieList.MOVIE_CATEGORY[i], null);
mRowsAdapter.add(new ListRow(header, listRowAdapter));
}
HeaderItem gridHeader = new HeaderItem(i, "PREFERENCES", null);
GridItemPresenter mGridPresenter = new GridItemPresenter();
ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter);
gridRowAdapter.add(getResources().getString(R.string.grid_view));
gridRowAdapter.add(getResources().getString(R.string.send_feeback));
gridRowAdapter.add(getResources().getString(R.string.personal_settings));
mRowsAdapter.add(new ListRow(gridHeader, gridRowAdapter));
setAdapter(mRowsAdapter);
}
private void prepareBackgroundManager() {
BackgroundManager backgroundManager = BackgroundManager.getInstance(getActivity());
backgroundManager.attach(getActivity().getWindow());
mBackgroundTarget = new PicassoBackgroundManagerTarget(backgroundManager);
mDefaultBackground = getResources().getDrawable(R.drawable.default_background);
mMetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
}
private void setupUIElements() {
// setBadgeDrawable(getActivity().getResources().getDrawable(
// R.drawable.videos_by_google_banner));
setTitle(getString(R.string.browse_title)); // Badge, when set, takes precedent
// over title
setHeadersState(HEADERS_ENABLED);
setHeadersTransitionOnBackEnabled(true);
// set fastLane (or headers) background color
setBrandColor(getResources().getColor(R.color.fastlane_background));
// set search icon color
setSearchAffordanceColor(getResources().getColor(R.color.search_opaque));
}
private void setupEventListeners() {
setOnItemSelectedListener(getDefaultItemSelectedListener());
setOnItemClickedListener(getDefaultItemClickedListener());
setOnSearchClickedListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getActivity(), "Implement your own in-app search", Toast.LENGTH_LONG)
.show();
}
});
}
protected OnItemSelectedListener getDefaultItemSelectedListener() {
return new OnItemSelectedListener() {
@Override
public void onItemSelected(Object item, Row row) {
if (item instanceof Movie) {
mBackgroundURI = ((Movie) item).getBackgroundImageURI();
startBackgroundTimer();
}
}
};
}
protected OnItemClickedListener getDefaultItemClickedListener() {
return new OnItemClickedListener() {
@Override
public void onItemClicked(Object item, 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(getString(R.string.movie), movie);
startActivity(intent);
} else if (item instanceof String) {
Toast.makeText(getActivity(), (String) item, Toast.LENGTH_SHORT)
.show();
}
}
};
}
protected void setDefaultBackground(Drawable background) {
mDefaultBackground = background;
}
protected void setDefaultBackground(int resourceId) {
mDefaultBackground = getResources().getDrawable(resourceId);
}
protected void updateBackground(URI uri) {
Picasso.with(getActivity())
.load(uri.toString())
.resize(mMetrics.widthPixels, mMetrics.heightPixels)
.centerCrop()
.error(mDefaultBackground)
.into(mBackgroundTarget);
}
protected void updateBackground(Drawable drawable) {
BackgroundManager.getInstance(getActivity()).setDrawable(drawable);
}
protected void clearBackground() {
BackgroundManager.getInstance(getActivity()).setDrawable(mDefaultBackground);
}
private void startBackgroundTimer() {
if (null != mBackgroundTimer) {
mBackgroundTimer.cancel();
}
mBackgroundTimer = new Timer();
mBackgroundTimer.schedule(new UpdateBackgroundTask(), 300);
}
private class UpdateBackgroundTask extends TimerTask {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
if (mBackgroundURI != null) {
updateBackground(mBackgroundURI);
}
}
});
}
}
private class GridItemPresenter extends Presenter {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
TextView view = new TextView(parent.getContext());
view.setLayoutParams(new ViewGroup.LayoutParams(200, 200));
view.setFocusable(true);
view.setFocusableInTouchMode(true);
view.setBackgroundColor(getResources().getColor(R.color.default_background));
view.setTextColor(Color.WHITE);
view.setGravity(Gravity.CENTER);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, Object item) {
((TextView) viewHolder.view).setText((String) item);
}
@Override
public void onUnbindViewHolder(ViewHolder viewHolder) {
}
}
}

View File

@ -0,0 +1,125 @@
package tv.mediabrowser.mediabrowsertv;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import android.util.Log;
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 + '\'' +
'}';
}
}

View File

@ -0,0 +1,86 @@
package tv.mediabrowser.mediabrowsertv;
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;
}
}

View File

@ -0,0 +1,54 @@
package tv.mediabrowser.mediabrowsertv;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.support.v17.leanback.app.BackgroundManager;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
/**
* Picasso target for updating default_background images
*/
public class PicassoBackgroundManagerTarget implements Target {
BackgroundManager mBackgroundManager;
public PicassoBackgroundManagerTarget(BackgroundManager backgroundManager) {
this.mBackgroundManager = backgroundManager;
}
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
this.mBackgroundManager.setBitmap(bitmap);
}
@Override
public void onBitmapFailed(Drawable drawable) {
this.mBackgroundManager.setDrawable(drawable);
}
@Override
public void onPrepareLoad(Drawable drawable) {
// Do nothing, default_background manager has its own transitions
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
PicassoBackgroundManagerTarget that = (PicassoBackgroundManagerTarget) o;
if (!mBackgroundManager.equals(that.mBackgroundManager))
return false;
return true;
}
@Override
public int hashCode() {
return mBackgroundManager.hashCode();
}
}

View File

@ -0,0 +1,148 @@
/*
* 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 tv.mediabrowser.mediabrowsertv;
import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.widget.FrameLayout;
import android.widget.VideoView;
/**
* PlaybackOverlayActivity for video playback that loads PlaybackOverlayFragment
*/
public class PlaybackOverlayActivity extends Activity implements
PlaybackOverlayFragment.OnPlayPauseClickedListener {
private static final String TAG = "PlaybackOverlayActivity";
private static final double MEDIA_HEIGHT = 0.95;
private static final double MEDIA_WIDTH = 0.95;
private static final double MEDIA_TOP_MARGIN = 0.025;
private static final double MEDIA_RIGHT_MARGIN = 0.025;
private static final double MEDIA_BOTTOM_MARGIN = 0.025;
private static final double MEDIA_LEFT_MARGIN = 0.025;
private VideoView mVideoView;
private PlaybackState mPlaybackState = PlaybackState.IDLE;
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.playback_controls);
loadViews();
//setupCallbacks();
overScan();
}
@Override
public void onDestroy() {
super.onDestroy();
mVideoView.suspend();
}
/**
* Implementation of OnPlayPauseClickedListener
*/
public void onFragmentPlayPause(Movie movie, int position, Boolean playPause) {
mVideoView.setVideoPath(movie.getVideoUrl());
if (position == 0 || mPlaybackState == PlaybackState.IDLE) {
setupCallbacks();
mPlaybackState = PlaybackState.IDLE;
}
if (playPause && mPlaybackState != PlaybackState.PLAYING) {
mPlaybackState = PlaybackState.PLAYING;
if (position > 0) {
mVideoView.seekTo(position);
mVideoView.start();
}
} else {
mPlaybackState = PlaybackState.PAUSED;
mVideoView.pause();
}
}
private void loadViews() {
mVideoView = (VideoView) findViewById(R.id.videoView);
}
private void overScan() {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int w = (int) (metrics.widthPixels * MEDIA_WIDTH);
int h = (int) (metrics.heightPixels * MEDIA_HEIGHT);
int marginLeft = (int) (metrics.widthPixels * MEDIA_LEFT_MARGIN);
int marginTop = (int) (metrics.heightPixels * MEDIA_TOP_MARGIN);
int marginRight = (int) (metrics.widthPixels * MEDIA_RIGHT_MARGIN);
int marginBottom = (int) (metrics.heightPixels * MEDIA_BOTTOM_MARGIN);
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(w, h);
lp.setMargins(marginLeft, marginTop, marginRight, marginBottom);
mVideoView.setLayoutParams(lp);
}
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 = PlaybackState.IDLE;
return false;
}
});
mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
if (mPlaybackState == PlaybackState.PLAYING) {
mVideoView.start();
}
}
});
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mPlaybackState = PlaybackState.IDLE;
}
});
}
/*
* List of various states that we can be in
*/
public static enum PlaybackState {
PLAYING, PAUSED, BUFFERING, IDLE;
}
}

View File

@ -0,0 +1,424 @@
/*
* 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 tv.mediabrowser.mediabrowsertv;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
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.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/*
* Class for video playback with media control
*/
public class PlaybackOverlayFragment extends android.support.v17.leanback.app.PlaybackOverlayFragment {
private static final String TAG = "PlaybackControlsFragment";
private static Context sContext;
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 PicassoPlaybackControlsRowTarget mPlaybackControlsRowTarget;
OnPlayPauseClickedListener mCallback;
// Container Activity must implement this interface
public interface OnPlayPauseClickedListener {
public void onFragmentPlayPause(Movie movie, int position, Boolean playPause);
}
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState);
sContext = getActivity();
mItems = new ArrayList<Movie>();
mSelectedMovie = (Movie) getActivity()
.getIntent().getSerializableExtra(DetailsActivity.MOVIE);
List<Movie> movies = MovieList.list;
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;
}
}
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);
}
});
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnPlayPauseClickedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.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()) {
if (mPlayPauseAction.getIndex() == PlayPauseAction.PLAY) {
startProgressAutomation();
setFadingEnabled(true);
mCallback.onFragmentPlayPause(mItems.get(mCurrentItem),
mPlaybackControlsRow.getCurrentTime(), true);
} else {
stopProgressAutomation();
setFadingEnabled(false);
mCallback.onFragmentPlayPause(mItems.get(mCurrentItem),
mPlaybackControlsRow.getCurrentTime(), false);
}
} 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);
}
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(sContext);
mRepeatAction = new RepeatAction(sContext);
mThumbsUpAction = new ThumbsUpAction(sContext);
mThumbsDownAction = new ThumbsDownAction(sContext);
mShuffleAction = new ShuffleAction(sContext);
mSkipNextAction = new PlaybackControlsRow.SkipNextAction(sContext);
mSkipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(sContext);
mFastForwardAction = new PlaybackControlsRow.FastForwardAction(sContext);
mRewindAction = new PlaybackControlsRow.RewindAction(sContext);
if (PRIMARY_CONTROLS > 5) {
mPrimaryActionsAdapter.add(mThumbsUpAction);
} else {
mSecondaryActionsAdapter.add(mThumbsUpAction);
}
mPrimaryActionsAdapter.add(mSkipPreviousAction);
if (PRIMARY_CONTROLS > 3) {
mPrimaryActionsAdapter.add(new PlaybackControlsRow.RewindAction(sContext));
}
mPrimaryActionsAdapter.add(mPlayPauseAction);
if (PRIMARY_CONTROLS > 3) {
mPrimaryActionsAdapter.add(new PlaybackControlsRow.FastForwardAction(sContext));
}
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(sContext));
mSecondaryActionsAdapter.add(new PlaybackControlsRow.ClosedCaptioningAction(sContext));
}
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) {
mPlaybackControlsRowTarget = new PicassoPlaybackControlsRowTarget(mPlaybackControlsRow);
updateVideoImage(mItems.get(mCurrentItem).getCardImageURI());
}
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), null);
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();
}
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());
}
}
public static class PicassoPlaybackControlsRowTarget implements Target {
PlaybackControlsRow mPlaybackControlsRow;
public PicassoPlaybackControlsRowTarget(PlaybackControlsRow playbackControlsRow) {
mPlaybackControlsRow = playbackControlsRow;
}
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
Drawable bitmapDrawable = new BitmapDrawable(sContext.getResources(), bitmap);
mPlaybackControlsRow.setImageDrawable(bitmapDrawable);
}
@Override
public void onBitmapFailed(Drawable drawable) {
mPlaybackControlsRow.setImageDrawable(drawable);
}
@Override
public void onPrepareLoad(Drawable drawable) {
// Do nothing, default_background manager has its own transitions
}
}
protected void updateVideoImage(URI uri) {
Picasso.with(sContext)
.load(uri.toString())
.resize(Utils.convertDpToPixel(sContext, CARD_WIDTH),
Utils.convertDpToPixel(sContext, CARD_HEIGHT))
.into(mPlaybackControlsRowTarget);
}
}

View File

@ -0,0 +1,420 @@
package tv.mediabrowser.mediabrowsertv;
import android.app.Activity;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.VideoView;
import java.util.Timer;
import java.util.TimerTask;
public class PlayerActivity extends Activity {
private static final String TAG = "PlayerActivity";
private static final int HIDE_CONTROLLER_TIME = 5000;
private static final int SEEKBAR_DELAY_TIME = 100;
private static final int SEEKBAR_INTERVAL_TIME = 1000;
private static final int MIN_SCRUB_TIME = 3000;
private static final int SCRUB_SEGMENT_DIVISOR = 30;
private static final double MEDIA_BAR_TOP_MARGIN = 0.8;
private static final double MEDIA_BAR_RIGHT_MARGIN = 0.2;
private static final double MEDIA_BAR_BOTTOM_MARGIN = 0.0;
private static final double MEDIA_BAR_LEFT_MARGIN = 0.2;
private static final double MEDIA_BAR_HEIGHT = 0.1;
private static final double MEDIA_BAR_WIDTH = 0.9;
private VideoView mVideoView;
private TextView mStartText;
private TextView mEndText;
private SeekBar mSeekbar;
private ImageView mPlayPause;
private ProgressBar mLoading;
private View mControllers;
private View mContainer;
private Timer mSeekbarTimer;
private Timer mControllersTimer;
private PlaybackState mPlaybackState;
private final Handler mHandler = new Handler();
private Movie mSelectedMovie;
private boolean mShouldStartPlayback;
private boolean mControlersVisible;
private int mDuration;
private DisplayMetrics mMetrics;
/*
* List of various states that we can be in
*/
public static enum PlaybackState {
PLAYING, PAUSED, BUFFERING, IDLE;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player);
mMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
loadViews();
setupController();
setupControlsCallbacks();
startVideoPlayer();
updateMetadata(true);
}
private void startVideoPlayer() {
Bundle b = getIntent().getExtras();
mSelectedMovie = (Movie) getIntent().getSerializableExtra(getResources().getString(R.string.movie));
if (null != b) {
mShouldStartPlayback = b.getBoolean(getResources().getString(R.string.should_start));
int startPosition = b.getInt(getResources().getString(R.string.start_position), 0);
mVideoView.setVideoPath(mSelectedMovie.getVideoUrl());
if (mShouldStartPlayback) {
mPlaybackState = PlaybackState.PLAYING;
updatePlayButton(mPlaybackState);
if (startPosition > 0) {
mVideoView.seekTo(startPosition);
}
mVideoView.start();
mPlayPause.requestFocus();
startControllersTimer();
} else {
updatePlaybackLocation();
mPlaybackState = PlaybackState.PAUSED;
updatePlayButton(mPlaybackState);
}
}
}
private void updatePlaybackLocation() {
if (mPlaybackState == PlaybackState.PLAYING ||
mPlaybackState == PlaybackState.BUFFERING) {
startControllersTimer();
} else {
stopControllersTimer();
}
}
private void play(int position) {
startControllersTimer();
mVideoView.seekTo(position);
mVideoView.start();
restartSeekBarTimer();
}
private void stopSeekBarTimer() {
Log.d(TAG, "Stopped TrickPlay Timer");
if (null != mSeekbarTimer) {
mSeekbarTimer.cancel();
}
}
private void restartSeekBarTimer() {
stopSeekBarTimer();
mSeekbarTimer = new Timer();
mSeekbarTimer.scheduleAtFixedRate(new UpdateSeekbarTask(), SEEKBAR_DELAY_TIME,
SEEKBAR_INTERVAL_TIME);
}
private void stopControllersTimer() {
if (null != mControllersTimer) {
mControllersTimer.cancel();
}
}
private void startControllersTimer() {
if (null != mControllersTimer) {
mControllersTimer.cancel();
}
mControllersTimer = new Timer();
mControllersTimer.schedule(new HideControllersTask(), HIDE_CONTROLLER_TIME);
}
private void updateControlersVisibility(boolean show) {
if (show) {
mControllers.setVisibility(View.VISIBLE);
} else {
mControllers.setVisibility(View.INVISIBLE);
}
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause() was called");
if (null != mSeekbarTimer) {
mSeekbarTimer.cancel();
mSeekbarTimer = null;
}
if (null != mControllersTimer) {
mControllersTimer.cancel();
}
mVideoView.pause();
mPlaybackState = PlaybackState.PAUSED;
updatePlayButton(PlaybackState.PAUSED);
}
@Override
protected void onStop() {
Log.d(TAG, "onStop() was called");
super.onStop();
}
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy() is called");
stopControllersTimer();
stopSeekBarTimer();
super.onDestroy();
}
@Override
protected void onStart() {
Log.d(TAG, "onStart was called");
super.onStart();
}
@Override
protected void onResume() {
Log.d(TAG, "onResume() was called");
super.onResume();
}
private class HideControllersTask extends TimerTask {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
updateControlersVisibility(false);
mControlersVisible = false;
}
});
}
}
private class UpdateSeekbarTask extends TimerTask {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
int currentPos = 0;
currentPos = mVideoView.getCurrentPosition();
updateSeekbar(currentPos, mDuration);
}
});
}
}
private class BackToDetailTask extends TimerTask {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(PlayerActivity.this, DetailsActivity.class);
intent.putExtra(getResources().getString(R.string.movie), mSelectedMovie);
startActivity(intent);
}
});
}
}
private void setupController() {
int w = (int) (mMetrics.widthPixels * MEDIA_BAR_WIDTH);
int h = (int) (mMetrics.heightPixels * MEDIA_BAR_HEIGHT);
int marginLeft = (int) (mMetrics.widthPixels * MEDIA_BAR_LEFT_MARGIN);
int marginTop = (int) (mMetrics.heightPixels * MEDIA_BAR_TOP_MARGIN);
int marginRight = (int) (mMetrics.widthPixels * MEDIA_BAR_RIGHT_MARGIN);
int marginBottom = (int) (mMetrics.heightPixels * MEDIA_BAR_BOTTOM_MARGIN);
LayoutParams lp = new LayoutParams(w, h);
lp.setMargins(marginLeft, marginTop, marginRight, marginBottom);
mControllers.setLayoutParams(lp);
mStartText.setText(getResources().getString(R.string.init_text));
mEndText.setText(getResources().getString(R.string.init_text));
}
private void setupControlsCallbacks() {
mVideoView.setOnErrorListener(new 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_unaccessible);
} else {
msg = getString(R.string.video_error_unknown_error);
}
mVideoView.stopPlayback();
mPlaybackState = PlaybackState.IDLE;
return false;
}
});
mVideoView.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
Log.d(TAG, "onPrepared is reached");
mDuration = mp.getDuration();
mEndText.setText(Utils.formatMillis(mDuration));
mSeekbar.setMax(mDuration);
restartSeekBarTimer();
}
});
mVideoView.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
stopSeekBarTimer();
mPlaybackState = PlaybackState.IDLE;
updatePlayButton(PlaybackState.IDLE);
mControllersTimer = new Timer();
mControllersTimer.schedule(new BackToDetailTask(), HIDE_CONTROLLER_TIME);
}
});
}
/*
* @Override public boolean onKeyDown(int keyCode, KeyEvent event) { return
* super.onKeyDown(keyCode, event); }
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
int currentPos = 0;
int delta = (int) (mDuration / SCRUB_SEGMENT_DIVISOR);
if (delta < MIN_SCRUB_TIME)
delta = MIN_SCRUB_TIME;
Log.v("keycode", "duration " + mDuration + " delta:" + delta);
if (!mControlersVisible) {
updateControlersVisibility(true);
}
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_CENTER:
return true;
case KeyEvent.KEYCODE_DPAD_DOWN:
return true;
case KeyEvent.KEYCODE_DPAD_LEFT:
currentPos = mVideoView.getCurrentPosition();
currentPos -= delta;
if (currentPos > 0)
play(currentPos);
return true;
case KeyEvent.KEYCODE_DPAD_RIGHT:
currentPos = mVideoView.getCurrentPosition();
currentPos += delta;
if (currentPos < mDuration)
play(currentPos);
return true;
case KeyEvent.KEYCODE_DPAD_UP:
return true;
}
return super.onKeyDown(keyCode, event);
}
private void updateSeekbar(int position, int duration) {
mSeekbar.setProgress(position);
mSeekbar.setMax(duration);
mStartText.setText(Utils.formatMillis(position));
mEndText.setText(Utils.formatMillis(duration));
}
private void updatePlayButton(PlaybackState state) {
switch (state) {
case PLAYING:
mLoading.setVisibility(View.INVISIBLE);
mPlayPause.setVisibility(View.VISIBLE);
mPlayPause.setImageDrawable(
getResources().getDrawable(R.drawable.ic_pause_playcontrol_normal));
break;
case PAUSED:
case IDLE:
mLoading.setVisibility(View.INVISIBLE);
mPlayPause.setVisibility(View.VISIBLE);
mPlayPause.setImageDrawable(
getResources().getDrawable(R.drawable.ic_play_playcontrol_normal));
break;
case BUFFERING:
mPlayPause.setVisibility(View.INVISIBLE);
mLoading.setVisibility(View.VISIBLE);
break;
default:
break;
}
}
private void updateMetadata(boolean visible) {
mVideoView.invalidate();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return true;
}
private void loadViews() {
mVideoView = (VideoView) findViewById(R.id.videoView);
mStartText = (TextView) findViewById(R.id.startText);
mEndText = (TextView) findViewById(R.id.endText);
mSeekbar = (SeekBar) findViewById(R.id.seekBar);
mPlayPause = (ImageView) findViewById(R.id.playpause);
mLoading = (ProgressBar) findViewById(R.id.progressBar);
mControllers = findViewById(R.id.controllers);
mContainer = findViewById(R.id.container);
mVideoView.setOnClickListener(mPlayPauseHandler);
}
View.OnClickListener mPlayPauseHandler = new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG, "clicked play pause button");
if (!mControlersVisible) {
updateControlersVisibility(true);
}
if (mPlaybackState == PlaybackState.PAUSED) {
mPlaybackState = PlaybackState.PLAYING;
updatePlayButton(mPlaybackState);
mVideoView.start();
startControllersTimer();
} else {
mVideoView.pause();
mPlaybackState = PlaybackState.PAUSED;
updatePlayButton(PlaybackState.PAUSED);
stopControllersTimer();
}
}
};
}

View File

@ -0,0 +1,39 @@
package tv.mediabrowser.mediabrowsertv;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
public class SelectUserActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_select_user);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_select_user, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}

View File

@ -0,0 +1,128 @@
package tv.mediabrowser.mediabrowsertv;
import android.app.Activity;
import android.content.Intent;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import mediabrowser.apiinteraction.ApiEventListener;
import mediabrowser.apiinteraction.ConnectionResult;
import mediabrowser.apiinteraction.IConnectionManager;
import mediabrowser.apiinteraction.Response;
import mediabrowser.apiinteraction.android.AndroidConnectionManager;
import mediabrowser.apiinteraction.android.GsonJsonSerializer;
import mediabrowser.apiinteraction.android.VolleyHttpClient;
import mediabrowser.model.dto.UserDto;
import mediabrowser.model.logging.ILogger;
import mediabrowser.model.serialization.IJsonSerializer;
import mediabrowser.model.session.ClientCapabilities;
import mediabrowser.model.users.AuthenticationResult;
public class StartupActivity extends Activity {
private TvApp application;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_startup);
application = (TvApp) getApplicationContext();
final Activity activity = this;
connectToServer(activity);
}
private void connectToServer(final Activity activity){
final ILogger logger = application.getLogger();
// The underlying http stack. Developers can inject their own if desired
VolleyHttpClient volleyHttpClient = new VolleyHttpClient(logger, application);
ClientCapabilities capabilities = new ClientCapabilities();
IJsonSerializer jsonSerializer = new GsonJsonSerializer();
ApiEventListener apiEventListener = new ApiEventListener();
final IConnectionManager connectionManager = new AndroidConnectionManager(application,
jsonSerializer,
logger,
volleyHttpClient,
"MediaBrowserTv",
BuildConfig.VERSION_NAME,
capabilities,
apiEventListener);
application.setConnectionManager(connectionManager);
connectionManager.Connect(new Response<ConnectionResult>() {
@Override
public void onResponse(ConnectionResult response) {
switch (response.getState()) {
case ConnectSignIn:
logger.Debug("Sign in with connect...");
Intent intent = new Intent(activity, ConnectActivity.class);
startActivity(intent);
break;
case Unavailable:
logger.Debug("No server available...");
Utils.showToast(application, "No MB Servers available...");
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>() {
@Override
public void onResponse(ConnectionResult serverResult) {
switch (serverResult.getState()) {
case ServerSignIn:
try {
serverResult.getApiClient().AuthenticateUserAsync("Dad","1224", new Response<AuthenticationResult>() {
@Override
public void onResponse(AuthenticationResult authenticationResult) {
logger.Debug("Signed in as Dad.");
application.setCurrentUser(authenticationResult.getUser());
Intent intent = new Intent(activity, MainActivity.class);
startActivity(intent);
}
});
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
break;
}
}
});
break;
case SignedIn:
logger.Debug("Already signed in");
response.getApiClient().GetUserAsync(response.getApiClient().getCurrentUserId(), new Response<UserDto>() {
@Override
public void onResponse(UserDto response) {
application.setCurrentUser(response);
Intent intent = new Intent(activity, MainActivity.class);
startActivity(intent);
}
});
break;
case ServerSelection:
}
}
});
}
}

View File

@ -0,0 +1,297 @@
/*
* 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 tv.mediabrowser.mediabrowsertv;
import android.app.Application;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.support.v17.leanback.app.BackgroundManager;
import android.support.v17.leanback.app.BrowseFragment;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
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.OnItemViewClickedListener;
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
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.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import mediabrowser.apiinteraction.ApiClient;
import mediabrowser.model.dto.BaseItemDto;
public class StdBrowseFragment extends BrowseFragment {
private static final String TAG = "StdBrowseFragment";
private static final int BACKGROUND_UPDATE_DELAY = 300;
private static final int GRID_ITEM_WIDTH = 200;
private static final int GRID_ITEM_HEIGHT = 200;
private static final int NUM_ROWS = 6;
private static final int NUM_COLS = 15;
protected String MainTitle;
protected ApiClient apiClient;
private ArrayObjectAdapter mRowsAdapter;
private Drawable mDefaultBackground;
private Target mBackgroundTarget;
private DisplayMetrics mMetrics;
private Timer mBackgroundTimer;
private final Handler mHandler = new Handler();
private String mBackgroundUrl;
protected ArrayList<BrowseRowDef> mRows = new ArrayList<>();
CardPresenter mCardPresenter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
Log.i(TAG, "onCreate");
super.onActivityCreated(savedInstanceState);
prepareBackgroundManager();
setupUIElements();
setupQueries();
loadRows();
setupEventListeners();
}
private void setupQueries() {
}
@Override
public void onDestroy() {
super.onDestroy();
if (null != mBackgroundTimer) {
Log.d(TAG, "onDestroy: " + mBackgroundTimer.toString());
mBackgroundTimer.cancel();
}
}
private void loadRows() {
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
mCardPresenter = new CardPresenter();
for (BrowseRowDef def : mRows) {
}
int i = 0;
// for (i = 0; i < NUM_ROWS; i++) {
// if (i != 0) {
// Collections.shuffle(list);
// }
// ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(mCardPresenter);
// for (int j = 0; j < NUM_COLS; j++) {
// listRowAdapter.add(list.get(j % 5));
// }
// HeaderItem header = new HeaderItem(i, MovieList.MOVIE_CATEGORY[i], null);
// mRowsAdapter.add(new ListRow(header, listRowAdapter));
// }
HeaderItem gridHeader = new HeaderItem(i, "SETTINGS", null);
GridItemPresenter mGridPresenter = new GridItemPresenter();
ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter);
gridRowAdapter.add(getResources().getString(R.string.grid_view));
gridRowAdapter.add(getResources().getString(R.string.error_fragment));
gridRowAdapter.add(getResources().getString(R.string.personal_settings));
gridRowAdapter.add("Logout " + TvApp.getApplication().getCurrentUser().getName());
mRowsAdapter.add(new ListRow(gridHeader, gridRowAdapter));
setAdapter(mRowsAdapter);
}
private void prepareBackgroundManager() {
BackgroundManager backgroundManager = BackgroundManager.getInstance(getActivity());
backgroundManager.attach(getActivity().getWindow());
mBackgroundTarget = new PicassoBackgroundManagerTarget(backgroundManager);
mDefaultBackground = getResources().getDrawable(R.drawable.default_background);
mMetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
}
private void setupUIElements() {
setBadgeDrawable(getActivity().getResources().getDrawable(
R.drawable.mblogo));
setTitle(MainTitle); // Badge, when set, takes precedent
// over title
setHeadersState(HEADERS_ENABLED);
setHeadersTransitionOnBackEnabled(true);
// set fastLane (or headers) background color
setBrandColor(getResources().getColor(R.color.fastlane_background));
// set search icon color
setSearchAffordanceColor(getResources().getColor(R.color.search_opaque));
}
private void setupEventListeners() {
setOnSearchClickedListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getActivity(), "Implement your own in-app search", Toast.LENGTH_LONG)
.show();
}
});
setOnItemViewClickedListener(new ItemViewClickedListener());
setOnItemViewSelectedListener(new ItemViewSelectedListener());
}
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(DetailsActivity.MOVIE, movie);
Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
getActivity(),
((ImageCardView) itemViewHolder.view).getMainImageView(),
DetailsActivity.SHARED_ELEMENT_NAME).toBundle();
getActivity().startActivity(intent, bundle);
} else if (item instanceof String) {
if (((String) item).indexOf(getString(R.string.error_fragment)) >= 0) {
Intent intent = new Intent(getActivity(), BrowseErrorActivity.class);
startActivity(intent);
} else {
Toast.makeText(getActivity(), ((String) item), Toast.LENGTH_SHORT)
.show();
}
}
}
}
private final class ItemViewSelectedListener implements OnItemViewSelectedListener {
@Override
public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
if (item instanceof BaseItemDto) {
BaseItemDto baseItem = (BaseItemDto)item;
mBackgroundUrl = Utils.getBackdropImageUrl(baseItem, TvApp.getApplication().getConnectionManager().GetApiClient(baseItem));
startBackgroundTimer();
}
}
}
protected void setDefaultBackground(Drawable background) {
mDefaultBackground = background;
}
protected void setDefaultBackground(int resourceId) {
mDefaultBackground = getResources().getDrawable(resourceId);
}
protected void updateBackground(String url) {
Picasso.with(getActivity())
.load(url)
.resize(mMetrics.widthPixels, mMetrics.heightPixels)
.centerCrop()
.error(mDefaultBackground)
.into(mBackgroundTarget);
}
protected void updateBackground(Drawable drawable) {
BackgroundManager.getInstance(getActivity()).setDrawable(drawable);
}
protected void clearBackground() {
BackgroundManager.getInstance(getActivity()).setDrawable(mDefaultBackground);
}
private void startBackgroundTimer() {
if (null != mBackgroundTimer) {
mBackgroundTimer.cancel();
}
mBackgroundTimer = new Timer();
mBackgroundTimer.schedule(new UpdateBackgroundTask(), BACKGROUND_UPDATE_DELAY);
}
private class UpdateBackgroundTask extends TimerTask {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
if (mBackgroundUrl != null) {
updateBackground(mBackgroundUrl);
}
}
});
}
}
private class GridItemPresenter extends Presenter {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
TextView view = new TextView(parent.getContext());
view.setLayoutParams(new ViewGroup.LayoutParams(GRID_ITEM_WIDTH, GRID_ITEM_HEIGHT));
view.setFocusable(true);
view.setFocusableInTouchMode(true);
view.setBackgroundColor(getResources().getColor(R.color.default_background));
view.setTextColor(Color.WHITE);
view.setGravity(Gravity.CENTER);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, Object item) {
((TextView) viewHolder.view).setText((String) item);
}
@Override
public void onUnbindViewHolder(ViewHolder viewHolder) {
}
}
}

View File

@ -0,0 +1,55 @@
package tv.mediabrowser.mediabrowsertv;
import android.app.Application;
import mediabrowser.apiinteraction.ApiClient;
import mediabrowser.apiinteraction.IConnectionManager;
import mediabrowser.logging.ConsoleLogger;
import mediabrowser.model.dto.UserDto;
import mediabrowser.model.logging.ILogger;
/**
* Created by Eric on 11/24/2014.
*/
public class TvApp extends Application {
private ILogger logger;
private IConnectionManager connectionManager;
private static TvApp app;
private UserDto currentUser;
@Override
public void onCreate() {
super.onCreate();
logger = new ConsoleLogger();
app = (TvApp)getApplicationContext();
}
public static TvApp getApplication() {
return app;
}
public ILogger getLogger() {
return logger;
}
public void setLogger(ILogger value) {
logger = value;
}
public IConnectionManager getConnectionManager() {
return connectionManager;
}
public void setConnectionManager(IConnectionManager connectionManager) {
this.connectionManager = connectionManager;
}
public UserDto getCurrentUser() {
return currentUser;
}
public void setCurrentUser(UserDto currentUser) {
this.currentUser = currentUser;
}
}

View File

@ -0,0 +1,151 @@
package tv.mediabrowser.mediabrowsertv;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Point;
import android.view.Display;
import android.view.WindowManager;
import android.widget.Toast;
import java.net.URI;
import mediabrowser.apiinteraction.ApiClient;
import mediabrowser.model.dto.BaseItemDto;
import mediabrowser.model.dto.ImageOptions;
import mediabrowser.model.entities.ImageType;
/**
* A collection of utility methods, all static.
*/
public class Utils {
/*
* Making sure public utility methods remain static
*/
private Utils() {
}
/**
* Returns the screen/display size
*
* @param context
* @return
*/
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);
int width = size.x;
int height = size.y;
return new Point(width, height);
}
/**
* Shows an error dialog with a given text message.
*
* @param context
* @param errorString
*/
public static final void showErrorDialog(Context context, String errorString) {
new AlertDialog.Builder(context).setTitle(R.string.error)
.setMessage(errorString)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
})
.create()
.show();
}
/**
* Shows a (long) toast
*
* @param context
* @param msg
*/
public static void showToast(Context context, String msg) {
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
}
/**
* Shows a (long) toast.
*
* @param context
* @param resourceId
*/
public static void showToast(Context context, int resourceId) {
Toast.makeText(context, context.getString(resourceId), Toast.LENGTH_LONG).show();
}
public static byte[] encrypt(String x) throws Exception {
java.security.MessageDigest d = null;
d = java.security.MessageDigest.getInstance("SHA-1");
d.reset();
d.update(x.getBytes());
return d.digest();
}
/**
* Formats time in milliseconds to hh:mm:ss string format.
*
* @param millis
* @return
*/
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;
}
public static int convertDpToPixel(Context ctx, int dp) {
float density = ctx.getResources().getDisplayMetrics().density;
return Math.round((float) dp * density);
}
public static int dpToPx(int dp, Context ctx) {
float density = ctx.getResources().getDisplayMetrics().density;
return Math.round((float) dp * density);
}
public static String getPrimaryImageUrl(BaseItemDto item, ApiClient apiClient) {
ImageOptions options = new ImageOptions();
options.setMaxWidth(300);
options.setImageType(ImageType.Primary);
options.setTag(item.getImageTags().get(ImageType.Primary));
return apiClient.GetImageUrl(item, options);
}
public static String getBackdropImageUrl(BaseItemDto item, ApiClient apiClient) {
ImageOptions options = new ImageOptions();
options.setMaxWidth(300);
options.setImageType(ImageType.Backdrop);
options.setImageIndex(0);
options.setTag(item.getImageTags().get(ImageType.Backdrop));
return apiClient.GetImageUrl(item, options);
}
}

View File

@ -0,0 +1,177 @@
package tv.mediabrowser.mediabrowsertv;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
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.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.OnActionClickedListener;
import android.support.v17.leanback.widget.OnItemClickedListener;
import android.support.v17.leanback.widget.Row;
import android.util.DisplayMetrics;
import android.util.Log;
import android.widget.Toast;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
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 static final String MOVIE = "Movie";
private Movie selectedMovie;
private Drawable mDefaultBackground;
private Target mBackgroundTarget;
private DisplayMetrics mMetrics;
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate DetailsFragment");
super.onCreate(savedInstanceState);
BackgroundManager backgroundManager = BackgroundManager.getInstance(getActivity());
backgroundManager.attach(getActivity().getWindow());
mBackgroundTarget = new PicassoBackgroundManagerTarget(backgroundManager);
mDefaultBackground = getResources().getDrawable(R.drawable.default_background);
mMetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
selectedMovie = (Movie) getActivity().getIntent().getSerializableExtra(MOVIE);
new DetailRowBuilderTask().execute(selectedMovie);
setOnItemClickedListener(getDefaultItemClickedListener());
updateBackground(selectedMovie.getBackgroundImageURI());
}
private class DetailRowBuilderTask extends AsyncTask<Movie, Integer, DetailsOverviewRow> {
@Override
protected DetailsOverviewRow doInBackground(Movie... movies) {
selectedMovie = movies[0];
DetailsOverviewRow row = new DetailsOverviewRow(selectedMovie);
try {
Bitmap poster = Picasso.with(getActivity())
.load(selectedMovie.getCardImageUrl())
.resize(dpToPx(DETAIL_THUMB_WIDTH, getActivity().getApplicationContext()),
dpToPx(DETAIL_THUMB_HEIGHT, getActivity().getApplicationContext()))
.centerCrop()
.get();
row.setImageBitmap(getActivity(), poster);
} 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_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)));
return row;
}
@Override
protected void onPostExecute(DetailsOverviewRow detailRow) {
ClassPresenterSelector ps = new ClassPresenterSelector();
DetailsOverviewRowPresenter dorPresenter =
new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter());
// set detail background and style
dorPresenter.setBackgroundColor(getResources().getColor(R.color.detail_background));
dorPresenter.setStyleLarge(true);
dorPresenter.setOnActionClickedListener(new OnActionClickedListener() {
@Override
public void onActionClicked(Action action) {
if (action.getId() == ACTION_WATCH_TRAILER) {
Intent intent = new Intent(getActivity(), PlayerActivity.class);
intent.putExtra(getResources().getString(R.string.movie), selectedMovie);
intent.putExtra(getResources().getString(R.string.should_start), true);
startActivity(intent);
} else {
Toast.makeText(getActivity(), action.toString(), Toast.LENGTH_SHORT).show();
}
}
});
ps.addClassPresenter(DetailsOverviewRow.class, dorPresenter);
ps.addClassPresenter(ListRow.class,
new ListRowPresenter());
ArrayObjectAdapter adapter = new ArrayObjectAdapter(ps);
adapter.add(detailRow);
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], null);
adapter.add(new ListRow(header, listRowAdapter));
setAdapter(adapter);
}
}
protected OnItemClickedListener getDefaultItemClickedListener() {
return new OnItemClickedListener() {
@Override
public void onItemClicked(Object item, Row row) {
if (item instanceof Movie) {
Movie movie = (Movie) item;
Intent intent = new Intent(getActivity(), DetailsActivity.class);
intent.putExtra(MOVIE, movie);
startActivity(intent);
}
}
};
}
protected void updateBackground(URI uri) {
Log.d(TAG, "uri" + uri);
Log.d(TAG, "metrics" + mMetrics.toString());
Picasso.with(getActivity())
.load(uri.toString())
.resize(mMetrics.widthPixels, mMetrics.heightPixels)
.error(mDefaultBackground)
.into(mBackgroundTarget);
}
public static int dpToPx(int dp, Context ctx) {
float density = ctx.getResources().getDisplayMetrics().density;
return Math.round((float) dp * density);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View 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:startColor="@color/background_gradient_start"
android:endColor="@color/background_gradient_end"
android:angle="-270" />
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 882 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View 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:startColor="@color/background_gradient_start"
android:endColor="@color/background_gradient_end"
android:angle="-270" />
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 885 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 882 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/details_fragment"
android:name="tv.mediabrowser.mediabrowsertv.VideoDetailsFragment"
android:layout_width="match_parent" android:layout_height="match_parent"
tools:context=".DetailsActivity" tools:deviceIds="tv" />

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main_browse_fragment"
android:name="tv.mediabrowser.mediabrowsertv.HomeFragment" android:layout_width="match_parent"
android:layout_height="match_parent" tools:context=".MainActivity" tools:deviceIds="tv"
tools:ignore="MergeRootFrame"
tools:layout="@layout/activity_home" />

Some files were not shown because too many files have changed in this diff Show More