├── README.md └── TvDemo ├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── build.gradle ├── device-2016-03-23-124613.png ├── device-2016-03-23-124641.png ├── device-2016-03-23-124703.png ├── device-2016-03-23-124725.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── mobile ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── liuyongkui │ │ └── tvdemo │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ └── res │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── liuyongkui │ └── tvdemo │ └── ExampleUnitTest.java ├── settings.gradle └── tv ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src ├── androidTest └── java │ └── com │ └── example │ └── liuyongkui │ └── tvdemo │ └── ApplicationTest.java └── main ├── AndroidManifest.xml ├── java └── com │ └── example │ └── liuyongkui │ └── tvdemo │ ├── BrowseErrorActivity.java │ ├── CardPresenter.java │ ├── DetailsActivity.java │ ├── DetailsDescriptionPresenter.java │ ├── ErrorFragment.java │ ├── MainActivity.java │ ├── MainFragment.java │ ├── Movie.java │ ├── MovieList.java │ ├── MyTestView.java │ ├── PlaybackOverlayActivity.java │ ├── PlaybackOverlayFragment.java │ ├── Utils.java │ └── VideoDetailsFragment.java └── res ├── drawable-hdpi ├── app_icon_quantum.png ├── app_icon_quantum_card.png ├── card_background_default.9.png ├── default_background.xml ├── grid_bg.png ├── scrubber_disabled.png ├── scrubber_focussed.png ├── scrubber_normal.png ├── scrubber_pressed.png ├── shadow7.9.png ├── star_icon.png ├── videos_by_google_banner.png └── videos_by_google_icon.png ├── drawable-mdpi ├── app_icon_quantum.png ├── app_icon_quantum_card.png ├── ic_launcher.png ├── movie.jpg ├── videos_by_google_banner.png └── videos_by_google_icon.png ├── drawable-xhdpi ├── app_icon_quantum.png ├── app_icon_quantum_card.png ├── default_background.xml ├── grid_bg.png ├── ic_launcher.png ├── ic_pause_playcontrol_focussed.png ├── ic_pause_playcontrol_normal.png ├── ic_pause_playcontrol_pressed.png ├── ic_play_action_focussed.png ├── ic_play_action_normal.png ├── ic_play_action_pressed.png ├── ic_play_playcontrol_focussed.png ├── ic_play_playcontrol_normal.png ├── ic_play_playcontrol_pressed.png ├── star_icon.png ├── videos_by_google_banner.png └── videos_by_google_icon.png ├── drawable-xxhdpi ├── app_icon_quantum.png ├── app_icon_quantum_card.png ├── videos_by_google_banner.png └── videos_by_google_icon.png ├── drawable ├── app_icon_quantum.png ├── app_icon_quantum_card.png ├── app_icon_your_company.png ├── details_img.png ├── ic_action_a.png ├── ic_title.png ├── player_bg_gradient_dark.xml ├── shadow7.9.png ├── videos_by_google_banner.png └── videos_by_google_icon.png ├── layout ├── activity_details.xml ├── activity_main.xml └── playback_controls.xml ├── mipmap-hdpi └── ic_launcher.png ├── mipmap-mdpi └── ic_launcher.png ├── mipmap-xhdpi └── ic_launcher.png ├── mipmap-xxhdpi └── ic_launcher.png └── values ├── colors.xml ├── strings.xml ├── styles.xml └── themes.xml /README.md: -------------------------------------------------------------------------------- 1 | # -TV_FrameWork- 2 | 基于6.0最新的API 支持TV的框架 3 | 4 | 基于6.0最新的API 加入MD设计,依赖最新support libs 的Android TV框架 5 | 6 | >**.主要包含** 7 | 8 | 9 | 1. UI效果 10 | 2. 事件和动画处理 11 | 3. 远程视频播放 12 | 13 | 效果如下: 14 | 15 | ![这里写图片描述](https://github.com/NeglectedByBoss/TV_FrameWork/blob/master/TvDemo/device-2016-03-23-124613.png?raw=true) 16 | 17 | 18 | ![这里写图片描述](https://github.com/NeglectedByBoss/TV_FrameWork/blob/master/TvDemo/device-2016-03-23-124641.png?raw=true) 19 | 20 | ![这里写图片描述](https://github.com/NeglectedByBoss/TV_FrameWork/blob/master/TvDemo/device-2016-03-23-124703.png?raw=true) 21 | 22 | 23 | ![这里写图片描述](https://github.com/NeglectedByBoss/TV_FrameWork/blob/master/TvDemo/device-2016-03-23-124725.png?raw=true) 24 | 25 | 作者:码小白 26 | -------------------------------------------------------------------------------- /TvDemo/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /TvDemo/.idea/.name: -------------------------------------------------------------------------------- 1 | TvDemo -------------------------------------------------------------------------------- /TvDemo/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /TvDemo/.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /TvDemo/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /TvDemo/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /TvDemo/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /TvDemo/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /TvDemo/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /TvDemo/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /TvDemo/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.3.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /TvDemo/device-2016-03-23-124613.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/device-2016-03-23-124613.png -------------------------------------------------------------------------------- /TvDemo/device-2016-03-23-124641.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/device-2016-03-23-124641.png -------------------------------------------------------------------------------- /TvDemo/device-2016-03-23-124703.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/device-2016-03-23-124703.png -------------------------------------------------------------------------------- /TvDemo/device-2016-03-23-124725.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/device-2016-03-23-124725.png -------------------------------------------------------------------------------- /TvDemo/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /TvDemo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /TvDemo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Mar 21 10:11:59 CST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip 7 | -------------------------------------------------------------------------------- /TvDemo/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /TvDemo/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /TvDemo/mobile/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /TvDemo/mobile/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.example.liuyongkui.tvdemo" 9 | minSdkVersion 21 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.2.1' 26 | } 27 | -------------------------------------------------------------------------------- /TvDemo/mobile/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Tools\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /TvDemo/mobile/src/androidTest/java/com/example/liuyongkui/tvdemo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.example.liuyongkui.tvdemo; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /TvDemo/mobile/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /TvDemo/mobile/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/mobile/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /TvDemo/mobile/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/mobile/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /TvDemo/mobile/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/mobile/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /TvDemo/mobile/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/mobile/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /TvDemo/mobile/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/mobile/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /TvDemo/mobile/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /TvDemo/mobile/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | TvDemo 3 | 4 | -------------------------------------------------------------------------------- /TvDemo/mobile/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /TvDemo/mobile/src/test/java/com/example/liuyongkui/tvdemo/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.liuyongkui.tvdemo; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /TvDemo/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':mobile', ':tv' 2 | -------------------------------------------------------------------------------- /TvDemo/tv/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /TvDemo/tv/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | 4 | android { 5 | compileSdkVersion 23 6 | buildToolsVersion "23.0.2" 7 | 8 | defaultConfig { 9 | applicationId "com.example.liuyongkui.tvdemo" 10 | minSdkVersion 21 11 | targetSdkVersion 23 12 | versionCode 1 13 | versionName "1.0" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(dir: 'libs', include: ['*.jar']) 25 | compile 'com.android.support:recyclerview-v7:23.2.1' 26 | compile 'com.android.support:leanback-v17:23.2.1' 27 | compile 'com.android.support:appcompat-v7:23.2.1' 28 | compile 'com.github.bumptech.glide:glide:3.4.+' 29 | } 30 | -------------------------------------------------------------------------------- /TvDemo/tv/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Tools\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /TvDemo/tv/src/androidTest/java/com/example/liuyongkui/tvdemo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.example.liuyongkui.tvdemo; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /TvDemo/tv/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 11 | 14 | 15 | 21 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 39 | 40 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/java/com/example/liuyongkui/tvdemo/BrowseErrorActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.example.liuyongkui.tvdemo; 15 | 16 | import android.app.Activity; 17 | import android.app.Fragment; 18 | import android.os.Bundle; 19 | import android.os.Handler; 20 | import android.view.Gravity; 21 | import android.view.LayoutInflater; 22 | import android.view.View; 23 | import android.view.ViewGroup; 24 | import android.widget.FrameLayout; 25 | import android.widget.ProgressBar; 26 | 27 | /* 28 | * BrowseErrorActivity shows how to use ErrorFragment 29 | */ 30 | public class BrowseErrorActivity extends Activity { 31 | private static int TIMER_DELAY = 3000; 32 | private static int SPINNER_WIDTH = 100; 33 | private static int SPINNER_HEIGHT = 100; 34 | 35 | private ErrorFragment mErrorFragment; 36 | private SpinnerFragment mSpinnerFragment; 37 | 38 | /** 39 | * Called when the activity is first created. 40 | */ 41 | @Override 42 | public void onCreate(Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_main); 45 | 46 | testError(); 47 | } 48 | 49 | private void testError() { 50 | mErrorFragment = new ErrorFragment(); 51 | getFragmentManager().beginTransaction().add(R.id.main_browse_fragment, mErrorFragment).commit(); 52 | 53 | mSpinnerFragment = new SpinnerFragment(); 54 | getFragmentManager().beginTransaction().add(R.id.main_browse_fragment, mSpinnerFragment).commit(); 55 | 56 | final Handler handler = new Handler(); 57 | handler.postDelayed(new Runnable() { 58 | @Override 59 | public void run() { 60 | getFragmentManager().beginTransaction().remove(mSpinnerFragment).commit(); 61 | mErrorFragment.setErrorContent(); 62 | } 63 | }, TIMER_DELAY); 64 | } 65 | 66 | static public class SpinnerFragment extends Fragment { 67 | @Override 68 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 69 | Bundle savedInstanceState) { 70 | ProgressBar progressBar = new ProgressBar(container.getContext()); 71 | if (container instanceof FrameLayout) { 72 | FrameLayout.LayoutParams layoutParams = 73 | new FrameLayout.LayoutParams(SPINNER_WIDTH, SPINNER_HEIGHT, Gravity.CENTER); 74 | progressBar.setLayoutParams(layoutParams); 75 | } 76 | return progressBar; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/java/com/example/liuyongkui/tvdemo/CardPresenter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.example.liuyongkui.tvdemo; 16 | 17 | import android.graphics.drawable.Drawable; 18 | import android.support.v17.leanback.widget.ImageCardView; 19 | import android.support.v17.leanback.widget.Presenter; 20 | import android.util.Log; 21 | import android.view.ViewGroup; 22 | 23 | import com.bumptech.glide.Glide; 24 | 25 | /* 26 | * A CardPresenter is used to generate Views and bind Objects to them on demand. 27 | * It contains an Image CardView 28 | */ 29 | public class CardPresenter extends Presenter { 30 | private static final String TAG = "CardPresenter"; 31 | 32 | private static int CARD_WIDTH = 313; 33 | private static int CARD_HEIGHT = 176; 34 | private static int sSelectedBackgroundColor; 35 | private static int sDefaultBackgroundColor; 36 | private Drawable mDefaultCardImage; 37 | 38 | private static void updateCardBackgroundColor(ImageCardView view, boolean selected) { 39 | int color = selected ? sSelectedBackgroundColor : sDefaultBackgroundColor; 40 | // Both background colors should be set because the view's background is temporarily visible 41 | // during animations. 42 | view.setBackgroundColor(color); 43 | view.findViewById(R.id.info_field).setBackgroundColor(color); 44 | } 45 | 46 | @Override 47 | public ViewHolder onCreateViewHolder(ViewGroup parent) { 48 | Log.d(TAG, "onCreateViewHolder"); 49 | 50 | sDefaultBackgroundColor = parent.getResources().getColor(R.color.default_background); 51 | sSelectedBackgroundColor = parent.getResources().getColor(R.color.selected_background); 52 | mDefaultCardImage = parent.getResources().getDrawable(R.drawable.movie); 53 | 54 | ImageCardView cardView = new ImageCardView(parent.getContext()) { 55 | @Override 56 | public void setSelected(boolean selected) { 57 | updateCardBackgroundColor(this, selected); 58 | super.setSelected(selected); 59 | } 60 | }; 61 | 62 | cardView.setFocusable(true); 63 | cardView.setFocusableInTouchMode(true); 64 | updateCardBackgroundColor(cardView, false); 65 | return new ViewHolder(cardView); 66 | } 67 | 68 | @Override 69 | public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { 70 | Movie movie = (Movie) item; 71 | ImageCardView cardView = (ImageCardView) viewHolder.view; 72 | 73 | Log.d(TAG, "onBindViewHolder"); 74 | if (movie.getCardImageUrl() != null) { 75 | cardView.setTitleText(movie.getTitle()); 76 | cardView.setContentText(movie.getStudio()); 77 | cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT); 78 | Glide.with(viewHolder.view.getContext()) 79 | .load(movie.getCardImageUrl()) 80 | .centerCrop() 81 | .error(mDefaultCardImage) 82 | .into(cardView.getMainImageView()); 83 | } 84 | } 85 | 86 | @Override 87 | public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) { 88 | Log.d(TAG, "onUnbindViewHolder"); 89 | ImageCardView cardView = (ImageCardView) viewHolder.view; 90 | // Remove references to images so that the garbage collector can free up memory 91 | cardView.setBadgeImage(null); 92 | cardView.setMainImage(null); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/java/com/example/liuyongkui/tvdemo/DetailsActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.example.liuyongkui.tvdemo; 16 | 17 | import android.app.Activity; 18 | import android.os.Bundle; 19 | 20 | /* 21 | * Details activity class that loads LeanbackDetailsFragment class 22 | */ 23 | public class DetailsActivity extends Activity { 24 | public static final String SHARED_ELEMENT_NAME = "hero"; 25 | public static final String MOVIE = "Movie"; 26 | 27 | /** 28 | * Called when the activity is first created. 29 | */ 30 | @Override 31 | public void onCreate(Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | setContentView(R.layout.activity_details); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/java/com/example/liuyongkui/tvdemo/DetailsDescriptionPresenter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.example.liuyongkui.tvdemo; 16 | 17 | import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter; 18 | 19 | public class DetailsDescriptionPresenter extends AbstractDetailsDescriptionPresenter { 20 | 21 | @Override 22 | protected void onBindDescription(ViewHolder viewHolder, Object item) { 23 | Movie movie = (Movie) item; 24 | 25 | if (movie != null) { 26 | viewHolder.getTitle().setText(movie.getTitle()); 27 | viewHolder.getSubtitle().setText(movie.getStudio()); 28 | viewHolder.getBody().setText(movie.getDescription()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/java/com/example/liuyongkui/tvdemo/ErrorFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.example.liuyongkui.tvdemo; 15 | 16 | import android.content.res.Resources; 17 | import android.os.Bundle; 18 | import android.util.Log; 19 | import android.view.View; 20 | 21 | /* 22 | * This class demonstrates how to extend ErrorFragment 23 | */ 24 | public class ErrorFragment extends android.support.v17.leanback.app.ErrorFragment { 25 | private static final String TAG = "ErrorFragment"; 26 | private static final boolean TRANSLUCENT = true; 27 | 28 | @Override 29 | public void onCreate(Bundle savedInstanceState) { 30 | Log.d(TAG, "onCreate"); 31 | super.onCreate(savedInstanceState); 32 | setTitle(getResources().getString(R.string.app_name)); 33 | } 34 | 35 | void setErrorContent() { 36 | setImageDrawable(getResources().getDrawable(R.drawable.lb_ic_sad_cloud)); 37 | setMessage(getResources().getString(R.string.error_fragment_message)); 38 | setDefaultBackground(TRANSLUCENT); 39 | 40 | setButtonText(getResources().getString(R.string.dismiss_error)); 41 | setButtonClickListener(new View.OnClickListener() { 42 | @Override 43 | public void onClick(View arg0) { 44 | getFragmentManager().beginTransaction().remove(ErrorFragment.this).commit(); 45 | } 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/java/com/example/liuyongkui/tvdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.example.liuyongkui.tvdemo; 16 | 17 | import android.app.Activity; 18 | import android.app.Fragment; 19 | import android.os.Bundle; 20 | import android.view.View; 21 | import android.view.ViewGroup; 22 | 23 | /* 24 | * MainActivity class that loads MainFragment 25 | */ 26 | public class MainActivity extends Activity { 27 | /** 28 | * Called when the activity is first created. 29 | */ 30 | View view; 31 | @Override 32 | public void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | 35 | setContentView(R.layout.activity_main); 36 | 37 | } 38 | 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/java/com/example/liuyongkui/tvdemo/MainFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.example.liuyongkui.tvdemo; 16 | 17 | import java.net.URI; 18 | import java.util.Collections; 19 | import java.util.List; 20 | import java.util.Timer; 21 | import java.util.TimerTask; 22 | 23 | import android.content.Intent; 24 | import android.graphics.Color; 25 | import android.graphics.drawable.Drawable; 26 | import android.os.Bundle; 27 | import android.os.Handler; 28 | import android.support.v17.leanback.app.BackgroundManager; 29 | import android.support.v17.leanback.app.BrowseFragment; 30 | import android.support.v17.leanback.widget.ArrayObjectAdapter; 31 | import android.support.v17.leanback.widget.HeaderItem; 32 | import android.support.v17.leanback.widget.ImageCardView; 33 | import android.support.v17.leanback.widget.ListRow; 34 | import android.support.v17.leanback.widget.ListRowPresenter; 35 | import android.support.v17.leanback.widget.OnItemViewClickedListener; 36 | import android.support.v17.leanback.widget.OnItemViewSelectedListener; 37 | import android.support.v17.leanback.widget.Presenter; 38 | import android.support.v17.leanback.widget.Row; 39 | import android.support.v17.leanback.widget.RowPresenter; 40 | import android.support.v4.app.ActivityOptionsCompat; 41 | import android.util.DisplayMetrics; 42 | import android.util.Log; 43 | import android.view.Gravity; 44 | import android.view.View; 45 | import android.view.ViewGroup; 46 | import android.widget.TextView; 47 | import android.widget.Toast; 48 | 49 | import com.bumptech.glide.Glide; 50 | import com.bumptech.glide.load.resource.drawable.GlideDrawable; 51 | import com.bumptech.glide.request.animation.GlideAnimation; 52 | import com.bumptech.glide.request.target.SimpleTarget; 53 | 54 | public class MainFragment extends BrowseFragment { 55 | private static final String TAG = "MainFragment"; 56 | 57 | private static final int BACKGROUND_UPDATE_DELAY = 300; 58 | private static final int GRID_ITEM_WIDTH = 200; 59 | private static final int GRID_ITEM_HEIGHT = 200; 60 | private static final int NUM_ROWS = 6; 61 | private static final int NUM_COLS = 15; 62 | 63 | private final Handler mHandler = new Handler(); 64 | private ArrayObjectAdapter mRowsAdapter; 65 | private Drawable mDefaultBackground; 66 | private DisplayMetrics mMetrics; 67 | private Timer mBackgroundTimer; 68 | private URI mBackgroundURI; 69 | private BackgroundManager mBackgroundManager; 70 | TextView myView; 71 | 72 | @Override 73 | public void onActivityCreated(Bundle savedInstanceState) { 74 | Log.i(TAG, "onCreate"); 75 | super.onActivityCreated(savedInstanceState); 76 | 77 | prepareBackgroundManager(); 78 | 79 | setupUIElements(); 80 | 81 | loadRows(); 82 | 83 | setupEventListeners(); 84 | } 85 | 86 | @Override 87 | public void onDestroy() { 88 | super.onDestroy(); 89 | if (null != mBackgroundTimer) { 90 | Log.d(TAG, "onDestroy: " + mBackgroundTimer.toString()); 91 | mBackgroundTimer.cancel(); 92 | } 93 | } 94 | 95 | private void loadRows() { 96 | List list = MovieList.setupMovies(); 97 | 98 | mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); 99 | CardPresenter cardPresenter = new CardPresenter(); 100 | 101 | int i; 102 | for (i = 0; i < NUM_ROWS; i++) { 103 | if (i != 0) { 104 | Collections.shuffle(list); 105 | } 106 | ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter); 107 | for (int j = 0; j < NUM_COLS; j++) { 108 | listRowAdapter.add(list.get(j % 5)); 109 | } 110 | HeaderItem header = new HeaderItem(i, MovieList.MOVIE_CATEGORY[i]); 111 | mRowsAdapter.add(new ListRow(header, listRowAdapter)); 112 | } 113 | 114 | HeaderItem gridHeader = new HeaderItem(i, "设置"); 115 | 116 | GridItemPresenter mGridPresenter = new GridItemPresenter(); 117 | ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter); 118 | gridRowAdapter.add(getResources().getString(R.string.grid_view)); 119 | gridRowAdapter.add(getString(R.string.error_fragment)); 120 | gridRowAdapter.add(getResources().getString(R.string.personal_settings)); 121 | 122 | myView = new MyTestView(getActivity().getBaseContext()); 123 | /*myView.setText("dsdsds"); 124 | gridRowAdapter.add(myView);*/ 125 | mRowsAdapter.add(new ListRow(gridHeader, gridRowAdapter)); 126 | 127 | setAdapter(mRowsAdapter); 128 | 129 | } 130 | 131 | private void prepareBackgroundManager() { 132 | 133 | mBackgroundManager = BackgroundManager.getInstance(getActivity()); 134 | mBackgroundManager.attach(getActivity().getWindow()); 135 | mDefaultBackground = getResources().getDrawable(R.drawable.default_background); 136 | mMetrics = new DisplayMetrics(); 137 | getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics); 138 | } 139 | 140 | private void setupUIElements() { 141 | // setBadgeDrawable(getActivity().getResources().getDrawable( 142 | // R.drawable.videos_by_google_banner)); 143 | setTitle(getString(R.string.browse_title)); // Badge, when set, takes precedent 144 | // over title 145 | setHeadersState(HEADERS_ENABLED); 146 | setHeadersTransitionOnBackEnabled(true); 147 | 148 | // set fastLane (or headers) background color 149 | setBrandColor(getResources().getColor(R.color.fastlane_background)); 150 | // set search icon color 151 | setSearchAffordanceColor(getResources().getColor(R.color.search_opaque)); 152 | } 153 | 154 | private void setupEventListeners() { 155 | setOnSearchClickedListener(new View.OnClickListener() { 156 | 157 | @Override 158 | public void onClick(View view) { 159 | Toast.makeText(getActivity(), "实现你自己的应用内搜索 /Implement your own in-app search", Toast.LENGTH_LONG) 160 | .show(); 161 | } 162 | }); 163 | 164 | setOnItemViewClickedListener(new ItemViewClickedListener()); 165 | setOnItemViewSelectedListener(new ItemViewSelectedListener()); 166 | } 167 | 168 | protected void updateBackground(String uri) { 169 | int width = mMetrics.widthPixels; 170 | int height = mMetrics.heightPixels; 171 | Glide.with(getActivity()) 172 | .load(uri) 173 | .centerCrop() 174 | .error(mDefaultBackground) 175 | .into(new SimpleTarget(width, height) { 176 | @Override 177 | public void onResourceReady(GlideDrawable resource, 178 | GlideAnimation 179 | glideAnimation) { 180 | mBackgroundManager.setDrawable(resource); 181 | } 182 | }); 183 | mBackgroundTimer.cancel(); 184 | } 185 | 186 | private void startBackgroundTimer() { 187 | if (null != mBackgroundTimer) { 188 | mBackgroundTimer.cancel(); 189 | } 190 | mBackgroundTimer = new Timer(); 191 | mBackgroundTimer.schedule(new UpdateBackgroundTask(), BACKGROUND_UPDATE_DELAY); 192 | } 193 | 194 | private final class ItemViewClickedListener implements OnItemViewClickedListener { 195 | @Override 196 | public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, 197 | RowPresenter.ViewHolder rowViewHolder, Row row) { 198 | 199 | if (item instanceof Movie) { 200 | Movie movie = (Movie) item; 201 | Log.d(TAG, "Item: " + item.toString()); 202 | Intent intent = new Intent(getActivity(), DetailsActivity.class); 203 | intent.putExtra(DetailsActivity.MOVIE, movie); 204 | 205 | Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation( 206 | getActivity(), 207 | ((ImageCardView) itemViewHolder.view).getMainImageView(), 208 | DetailsActivity.SHARED_ELEMENT_NAME).toBundle(); 209 | getActivity().startActivity(intent, bundle); 210 | } else if (item instanceof String) { 211 | if (((String) item).indexOf(getString(R.string.error_fragment)) >= 0) { 212 | Intent intent = new Intent(getActivity(), BrowseErrorActivity.class); 213 | startActivity(intent); 214 | //myView.invalidate(); 215 | } else { 216 | // myView.requestLayout(); 217 | Toast.makeText(getActivity(), ((String) item), Toast.LENGTH_SHORT) 218 | .show(); 219 | } 220 | } 221 | } 222 | } 223 | 224 | private final class ItemViewSelectedListener implements OnItemViewSelectedListener { 225 | @Override 226 | public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, 227 | RowPresenter.ViewHolder rowViewHolder, Row row) { 228 | if (item instanceof Movie) { 229 | mBackgroundURI = ((Movie) item).getBackgroundImageURI(); 230 | startBackgroundTimer(); 231 | } 232 | 233 | } 234 | } 235 | 236 | private class UpdateBackgroundTask extends TimerTask { 237 | 238 | @Override 239 | public void run() { 240 | mHandler.post(new Runnable() { 241 | @Override 242 | public void run() { 243 | if (mBackgroundURI != null) { 244 | updateBackground(mBackgroundURI.toString()); 245 | } 246 | } 247 | }); 248 | 249 | } 250 | } 251 | 252 | private class GridItemPresenter extends Presenter { 253 | @Override 254 | public ViewHolder onCreateViewHolder(ViewGroup parent) { 255 | TextView view = new TextView(parent.getContext()); 256 | view.setLayoutParams(new ViewGroup.LayoutParams(GRID_ITEM_WIDTH, GRID_ITEM_HEIGHT)); 257 | view.setFocusable(true); 258 | view.setFocusableInTouchMode(true); 259 | view.setBackgroundColor(getResources().getColor(R.color.default_background)); 260 | view.setTextColor(Color.WHITE); 261 | view.setGravity(Gravity.CENTER); 262 | return new ViewHolder(view); 263 | } 264 | 265 | @Override 266 | public void onBindViewHolder(ViewHolder viewHolder, Object item) { 267 | ((TextView) viewHolder.view).setText((String) item); 268 | } 269 | 270 | @Override 271 | public void onUnbindViewHolder(ViewHolder viewHolder) { 272 | } 273 | } 274 | 275 | } 276 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/java/com/example/liuyongkui/tvdemo/Movie.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.example.liuyongkui.tvdemo; 16 | 17 | import android.util.Log; 18 | 19 | import java.io.Serializable; 20 | import java.net.URI; 21 | import java.net.URISyntaxException; 22 | 23 | /* 24 | * Movie class represents video entity with title, description, image thumbs and video url. 25 | * 26 | */ 27 | public class Movie implements Serializable { 28 | static final long serialVersionUID = 727566175075960653L; 29 | private static long count = 0; 30 | private long id; 31 | private String title; 32 | private String description; 33 | private String bgImageUrl; 34 | private String cardImageUrl; 35 | private String videoUrl; 36 | private String studio; 37 | private String category; 38 | 39 | public Movie() { 40 | } 41 | 42 | public static long getCount() { 43 | return count; 44 | } 45 | 46 | public static void incCount() { 47 | count++; 48 | } 49 | 50 | public long getId() { 51 | return id; 52 | } 53 | 54 | public void setId(long id) { 55 | this.id = id; 56 | } 57 | 58 | public String getTitle() { 59 | return title; 60 | } 61 | 62 | public void setTitle(String title) { 63 | this.title = title; 64 | } 65 | 66 | public String getDescription() { 67 | return description; 68 | } 69 | 70 | public void setDescription(String description) { 71 | this.description = description; 72 | } 73 | 74 | public String getStudio() { 75 | return studio; 76 | } 77 | 78 | public void setStudio(String studio) { 79 | this.studio = studio; 80 | } 81 | 82 | public String getVideoUrl() { 83 | return videoUrl; 84 | } 85 | 86 | public void setVideoUrl(String videoUrl) { 87 | this.videoUrl = videoUrl; 88 | } 89 | 90 | public String getBackgroundImageUrl() { 91 | return bgImageUrl; 92 | } 93 | 94 | public void setBackgroundImageUrl(String bgImageUrl) { 95 | this.bgImageUrl = bgImageUrl; 96 | } 97 | 98 | public String getCardImageUrl() { 99 | return cardImageUrl; 100 | } 101 | 102 | public void setCardImageUrl(String cardImageUrl) { 103 | this.cardImageUrl = cardImageUrl; 104 | } 105 | 106 | public String getCategory() { 107 | return category; 108 | } 109 | 110 | public void setCategory(String category) { 111 | this.category = category; 112 | } 113 | 114 | public URI getBackgroundImageURI() { 115 | try { 116 | Log.d("BACK MOVIE: ", bgImageUrl); 117 | return new URI(getBackgroundImageUrl()); 118 | } catch (URISyntaxException e) { 119 | Log.d("URI exception: ", bgImageUrl); 120 | return null; 121 | } 122 | } 123 | 124 | public URI getCardImageURI() { 125 | try { 126 | return new URI(getCardImageUrl()); 127 | } catch (URISyntaxException e) { 128 | return null; 129 | } 130 | } 131 | 132 | @Override 133 | public String toString() { 134 | return "Movie{" + 135 | "id=" + id + 136 | ", title='" + title + '\'' + 137 | ", videoUrl='" + videoUrl + '\'' + 138 | ", backgroundImageUrl='" + bgImageUrl + '\'' + 139 | ", backgroundImageURI='" + getBackgroundImageURI().toString() + '\'' + 140 | ", cardImageUrl='" + cardImageUrl + '\'' + 141 | '}'; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/java/com/example/liuyongkui/tvdemo/MovieList.java: -------------------------------------------------------------------------------- 1 | package com.example.liuyongkui.tvdemo; 2 | 3 | import android.net.Uri; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public final class MovieList { 9 | public static final String MOVIE_CATEGORY[] = { 10 | "电影 Zero", 11 | "音乐 One", 12 | "电视剧 Two", 13 | "体育 Three", 14 | "话剧 Four", 15 | "综艺 Five", 16 | }; 17 | 18 | public static List list; 19 | 20 | public static List setupMovies() { 21 | list = new ArrayList(); 22 | String title[] = { 23 | "2016 1月最新电影", 24 | "2016 2月最新电影", 25 | "2016 3月最新电影", 26 | "2016 4月最新电影", 27 | "2016 5月最新电影" 28 | }; 29 | 30 | String description = "Fusce id nisi turpis. Praesent viverra bibendum semper. " 31 | + "Donec tristique, orci sed semper lacinia, quam erat rhoncus massa, non congue tellus est " 32 | + "quis tellus. Sed mollis orci venenatis quam scelerisque accumsan. Curabitur a massa sit " 33 | + "amet mi accumsan mollis sed et magna. Vivamus sed aliquam risus. Nulla eget dolor in elit " 34 | + "facilisis mattis. Ut aliquet luctus lacus. Phasellus nec commodo erat. Praesent tempus id " 35 | + "lectus ac scelerisque. Maecenas pretium cursus lectus id volutpat."; 36 | 37 | String videoUrl[] = { 38 | 39 | "http://f3.3g.56.com/15/15/JGfMspPbHtzoqpzseFTPGUsKCEqMXFTW_smooth.3gp", 40 | "http://daily3gp.com/vids/family_guy_penis_car.3gp", 41 | "http://222.73.3.75/vkp.tc.qq.com/c0013ghdds8.mp4?vkey=CBA83E4B773DB0DBB3C5010896C3E4938C700EF47AC30134F2FD8C300E3D57B857508AF5A0418D53&br=66&platform=0&fmt=mp4&level=3", 42 | "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Zeitgeist/Zeitgeist%202010_%20Year%20in%20Review.mp4", 43 | "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%2020ft%20Search.mp4", 44 | //"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Gmail%20Blue.mp4", 45 | /*"http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Fiber%20to%20the%20Pole.mp4", 46 | "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Nose.mp4"*/ 47 | }; 48 | String bgImageUrl[] = { 49 | "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Zeitgeist/Zeitgeist%202010_%20Year%20in%20Review/bg.jpg", 50 | "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%2020ft%20Search/bg.jpg", 51 | "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Gmail%20Blue/bg.jpg", 52 | "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Fiber%20to%20the%20Pole/bg.jpg", 53 | "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Nose/bg.jpg", 54 | }; 55 | String cardImageUrl[] = { 56 | "http://pic1.qiyipic.com/image/20160214/c2/87/v_110036966_m_601_m3_180_236.jpg", 57 | "http://pic0.qiyipic.com/image/20150317/33/2f/bc/v_105141733_m_601_m5_180_236.jpg", 58 | "http://pic9.qiyipic.com/image/20160224/82/b2/v_109974783_m_601_m2_180_236.jpg", 59 | "http://pic8.qiyipic.com/image/20160316/fe/20/v_110152827_m_601_m1_180_236.jpg", 60 | "http://pic6.qiyipic.com/image/20160310/1e/ac/v_110114872_m_601_m4_180_236.jpg", 61 | /* "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Zeitgeist/Zeitgeist%202010_%20Year%20in%20Review/card.jpg", 62 | "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%2020ft%20Search/card.jpg", 63 | "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Gmail%20Blue/card.jpg", 64 | "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Fiber%20to%20the%20Pole/card.jpg", 65 | "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Nose/card.jpg"*/ 66 | }; 67 | 68 | list.add(buildMovieInfo("category", title[0], 69 | description, "Studio Zero", videoUrl[0], cardImageUrl[0], bgImageUrl[0])); 70 | list.add(buildMovieInfo("category", title[1], 71 | description, "Studio One", videoUrl[1], cardImageUrl[1], bgImageUrl[1])); 72 | list.add(buildMovieInfo("category", title[2], 73 | description, "Studio Two", videoUrl[2], cardImageUrl[2], bgImageUrl[2])); 74 | list.add(buildMovieInfo("category", title[3], 75 | description, "Studio Three", videoUrl[3], cardImageUrl[3], bgImageUrl[3])); 76 | list.add(buildMovieInfo("category", title[4], 77 | description, "Studio Four", videoUrl[4], cardImageUrl[4], bgImageUrl[4])); 78 | 79 | return list; 80 | } 81 | 82 | private static Movie buildMovieInfo(String category, String title, 83 | String description, String studio, String videoUrl, String cardImageUrl, 84 | String bgImageUrl) { 85 | Movie movie = new Movie(); 86 | movie.setId(Movie.getCount()); 87 | Movie.incCount(); 88 | movie.setTitle(title); 89 | movie.setDescription(description); 90 | movie.setStudio(studio); 91 | movie.setCategory(category); 92 | movie.setCardImageUrl(cardImageUrl); 93 | movie.setBackgroundImageUrl(bgImageUrl); 94 | movie.setVideoUrl(videoUrl); 95 | return movie; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/java/com/example/liuyongkui/tvdemo/MyTestView.java: -------------------------------------------------------------------------------- 1 | package com.example.liuyongkui.tvdemo; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.util.AttributeSet; 6 | import android.util.Log; 7 | import android.view.View; 8 | import android.widget.TextView; 9 | import android.widget.Toast; 10 | 11 | /** 12 | * Created by LIUYONGKUI726 on 2016-03-22. 13 | */ 14 | public class MyTestView extends TextView { 15 | 16 | private final static String Tag = "MyTestView"; 17 | 18 | public MyTestView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 19 | super(context, attrs, defStyleAttr, defStyleRes); 20 | } 21 | 22 | public MyTestView(Context context) { 23 | super(context); 24 | } 25 | 26 | public MyTestView(Context context, AttributeSet attrs) { 27 | super(context, attrs); 28 | } 29 | 30 | public MyTestView(Context context, AttributeSet attrs, int defStyleAttr) { 31 | super(context, attrs, defStyleAttr); 32 | } 33 | 34 | @Override 35 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 36 | 37 | 38 | 39 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 40 | Log.d(Tag, "onMeasure()"); 41 | Toast.makeText(getContext(), "onMeasure", Toast.LENGTH_LONG).show(); 42 | } 43 | 44 | @Override 45 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 46 | 47 | super.onLayout(changed, left, top, right, bottom); 48 | Log.d(Tag, "onLayout()"); 49 | } 50 | 51 | @Override 52 | protected void onDraw(Canvas canvas) { 53 | 54 | super.onDraw(canvas); 55 | Log.d(Tag, "onDraw"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/java/com/example/liuyongkui/tvdemo/PlaybackOverlayActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.example.liuyongkui.tvdemo; 16 | 17 | import android.app.Activity; 18 | import android.graphics.Bitmap; 19 | import android.media.MediaMetadata; 20 | import android.media.MediaPlayer; 21 | import android.media.session.MediaSession; 22 | import android.media.session.PlaybackState; 23 | import android.net.Uri; 24 | import android.os.Bundle; 25 | import android.view.KeyEvent; 26 | import android.widget.VideoView; 27 | 28 | import com.bumptech.glide.Glide; 29 | import com.bumptech.glide.request.animation.GlideAnimation; 30 | import com.bumptech.glide.request.target.SimpleTarget; 31 | 32 | /** 33 | * PlaybackOverlayActivity for video playback that loads PlaybackOverlayFragment 34 | */ 35 | public class PlaybackOverlayActivity extends Activity implements 36 | PlaybackOverlayFragment.OnPlayPauseClickedListener { 37 | private static final String TAG = "PlaybackOverlayActivity"; 38 | 39 | private VideoView mVideoView; 40 | private LeanbackPlaybackState mPlaybackState = LeanbackPlaybackState.IDLE; 41 | private MediaSession mSession; 42 | 43 | 44 | /** 45 | * Called when the activity is first created. 46 | */ 47 | @Override 48 | public void onCreate(Bundle savedInstanceState) { 49 | super.onCreate(savedInstanceState); 50 | setContentView(R.layout.playback_controls); 51 | loadViews(); 52 | setupCallbacks(); 53 | mSession = new MediaSession(this, "LeanbackSampleApp"); 54 | mSession.setCallback(new MediaSessionCallback()); 55 | mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS | 56 | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); 57 | 58 | mSession.setActive(true); 59 | } 60 | 61 | 62 | @Override 63 | public void onDestroy() { 64 | super.onDestroy(); 65 | mVideoView.suspend(); 66 | } 67 | 68 | @Override 69 | public boolean onKeyUp(int keyCode, KeyEvent event) { 70 | PlaybackOverlayFragment playbackOverlayFragment = (PlaybackOverlayFragment) getFragmentManager().findFragmentById(R.id.playback_controls_fragment); 71 | switch (keyCode) { 72 | case KeyEvent.KEYCODE_MEDIA_PLAY: 73 | playbackOverlayFragment.togglePlayback(false); 74 | return true; 75 | case KeyEvent.KEYCODE_MEDIA_PAUSE: 76 | playbackOverlayFragment.togglePlayback(false); 77 | return true; 78 | case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 79 | if (mPlaybackState == LeanbackPlaybackState.PLAYING) { 80 | playbackOverlayFragment.togglePlayback(false); 81 | } else { 82 | playbackOverlayFragment.togglePlayback(true); 83 | } 84 | return true; 85 | default: 86 | return super.onKeyUp(keyCode, event); 87 | } 88 | } 89 | 90 | /** 91 | * Implementation of OnPlayPauseClickedListener 92 | */ 93 | public void onFragmentPlayPause(Movie movie, int position, Boolean playPause) { 94 | mVideoView.setVideoPath(movie.getVideoUrl()); 95 | 96 | if (position == 0 || mPlaybackState == LeanbackPlaybackState.IDLE) { 97 | setupCallbacks(); 98 | mPlaybackState = LeanbackPlaybackState.IDLE; 99 | } 100 | 101 | if (playPause && mPlaybackState != LeanbackPlaybackState.PLAYING) { 102 | mPlaybackState = LeanbackPlaybackState.PLAYING; 103 | if (position > 0) { 104 | mVideoView.seekTo(position); 105 | mVideoView.start(); 106 | } 107 | } else { 108 | mPlaybackState = LeanbackPlaybackState.PAUSED; 109 | mVideoView.pause(); 110 | } 111 | updatePlaybackState(position); 112 | updateMetadata(movie); 113 | } 114 | 115 | private void updatePlaybackState(int position) { 116 | PlaybackState.Builder stateBuilder = new PlaybackState.Builder() 117 | .setActions(getAvailableActions()); 118 | int state = PlaybackState.STATE_PLAYING; 119 | if (mPlaybackState == LeanbackPlaybackState.PAUSED) { 120 | state = PlaybackState.STATE_PAUSED; 121 | } 122 | stateBuilder.setState(state, position, 1.0f); 123 | mSession.setPlaybackState(stateBuilder.build()); 124 | } 125 | 126 | private long getAvailableActions() { 127 | long actions = PlaybackState.ACTION_PLAY | 128 | PlaybackState.ACTION_PLAY_FROM_MEDIA_ID | 129 | PlaybackState.ACTION_PLAY_FROM_SEARCH; 130 | 131 | if (mPlaybackState == LeanbackPlaybackState.PLAYING) { 132 | actions |= PlaybackState.ACTION_PAUSE; 133 | } 134 | 135 | return actions; 136 | } 137 | 138 | private void updateMetadata(final Movie movie) { 139 | final MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder(); 140 | 141 | String title = movie.getTitle().replace("_", " -"); 142 | 143 | metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, title); 144 | metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, 145 | movie.getDescription()); 146 | metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, 147 | movie.getCardImageUrl()); 148 | 149 | // And at minimum the title and artist for legacy support 150 | metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, title); 151 | metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, movie.getStudio()); 152 | 153 | Glide.with(this) 154 | .load(Uri.parse(movie.getCardImageUrl())) 155 | .asBitmap() 156 | .into(new SimpleTarget(500, 500) { 157 | @Override 158 | public void onResourceReady(Bitmap bitmap, GlideAnimation anim) { 159 | metadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_ART, bitmap); 160 | mSession.setMetadata(metadataBuilder.build()); 161 | } 162 | }); 163 | } 164 | 165 | private void loadViews() { 166 | mVideoView = (VideoView) findViewById(R.id.videoView); 167 | mVideoView.setFocusable(false); 168 | mVideoView.setFocusableInTouchMode(false); 169 | } 170 | 171 | private void setupCallbacks() { 172 | 173 | mVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { 174 | 175 | @Override 176 | public boolean onError(MediaPlayer mp, int what, int extra) { 177 | String msg = ""; 178 | if (extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) { 179 | msg = getString(R.string.video_error_media_load_timeout); 180 | } else if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) { 181 | msg = getString(R.string.video_error_server_inaccessible); 182 | } else { 183 | msg = getString(R.string.video_error_unknown_error); 184 | } 185 | mVideoView.stopPlayback(); 186 | mPlaybackState = LeanbackPlaybackState.IDLE; 187 | return false; 188 | } 189 | }); 190 | 191 | mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { 192 | @Override 193 | public void onPrepared(MediaPlayer mp) { 194 | if (mPlaybackState == LeanbackPlaybackState.PLAYING) { 195 | mVideoView.start(); 196 | } 197 | } 198 | }); 199 | 200 | mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { 201 | @Override 202 | public void onCompletion(MediaPlayer mp) { 203 | mPlaybackState = LeanbackPlaybackState.IDLE; 204 | } 205 | }); 206 | 207 | } 208 | 209 | @Override 210 | public void onResume() { 211 | super.onResume(); 212 | mSession.setActive(true); 213 | } 214 | 215 | @Override 216 | public void onPause() { 217 | super.onPause(); 218 | if (mVideoView.isPlaying()) { 219 | if (!requestVisibleBehind(true)) { 220 | // Try to play behind launcher, but if it fails, stop playback. 221 | stopPlayback(); 222 | } 223 | } else { 224 | requestVisibleBehind(false); 225 | } 226 | } 227 | 228 | @Override 229 | protected void onStop() { 230 | super.onStop(); 231 | mSession.release(); 232 | } 233 | 234 | 235 | @Override 236 | public void onVisibleBehindCanceled() { 237 | super.onVisibleBehindCanceled(); 238 | } 239 | 240 | private void stopPlayback() { 241 | if (mVideoView != null) { 242 | mVideoView.stopPlayback(); 243 | } 244 | } 245 | 246 | /* 247 | * List of various states that we can be in 248 | */ 249 | public static enum LeanbackPlaybackState { 250 | PLAYING, PAUSED, BUFFERING, IDLE; 251 | } 252 | 253 | private class MediaSessionCallback extends MediaSession.Callback { 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/java/com/example/liuyongkui/tvdemo/PlaybackOverlayFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.example.liuyongkui.tvdemo; 16 | 17 | import android.app.Activity; 18 | import android.content.Context; 19 | import android.media.MediaMetadataRetriever; 20 | import android.os.Build; 21 | import android.os.Bundle; 22 | import android.os.Handler; 23 | import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter; 24 | import android.support.v17.leanback.widget.Action; 25 | import android.support.v17.leanback.widget.ArrayObjectAdapter; 26 | import android.support.v17.leanback.widget.ClassPresenterSelector; 27 | import android.support.v17.leanback.widget.ControlButtonPresenterSelector; 28 | import android.support.v17.leanback.widget.HeaderItem; 29 | import android.support.v17.leanback.widget.ListRow; 30 | import android.support.v17.leanback.widget.ListRowPresenter; 31 | import android.support.v17.leanback.widget.OnActionClickedListener; 32 | import android.support.v17.leanback.widget.OnItemViewClickedListener; 33 | import android.support.v17.leanback.widget.OnItemViewSelectedListener; 34 | import android.support.v17.leanback.widget.PlaybackControlsRow; 35 | import android.support.v17.leanback.widget.PlaybackControlsRow.FastForwardAction; 36 | import android.support.v17.leanback.widget.PlaybackControlsRow.PlayPauseAction; 37 | import android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction; 38 | import android.support.v17.leanback.widget.PlaybackControlsRow.RewindAction; 39 | import android.support.v17.leanback.widget.PlaybackControlsRow.ShuffleAction; 40 | import android.support.v17.leanback.widget.PlaybackControlsRow.SkipNextAction; 41 | import android.support.v17.leanback.widget.PlaybackControlsRow.SkipPreviousAction; 42 | import android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsDownAction; 43 | import android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsUpAction; 44 | import android.support.v17.leanback.widget.PlaybackControlsRowPresenter; 45 | import android.support.v17.leanback.widget.Presenter; 46 | import android.support.v17.leanback.widget.Row; 47 | import android.support.v17.leanback.widget.RowPresenter; 48 | import android.util.Log; 49 | import android.widget.Toast; 50 | 51 | import com.bumptech.glide.Glide; 52 | import com.bumptech.glide.load.resource.drawable.GlideDrawable; 53 | import com.bumptech.glide.request.animation.GlideAnimation; 54 | import com.bumptech.glide.request.target.SimpleTarget; 55 | 56 | import java.util.ArrayList; 57 | import java.util.HashMap; 58 | import java.util.List; 59 | 60 | /* 61 | * Class for video playback with media control 62 | */ 63 | public class PlaybackOverlayFragment extends android.support.v17.leanback.app.PlaybackOverlayFragment { 64 | private static final String TAG = "PlaybackControlsFragmnt"; 65 | private static final boolean SHOW_DETAIL = true; 66 | private static final boolean HIDE_MORE_ACTIONS = false; 67 | private static final int PRIMARY_CONTROLS = 5; 68 | private static final boolean SHOW_IMAGE = PRIMARY_CONTROLS <= 5; 69 | private static final int BACKGROUND_TYPE = PlaybackOverlayFragment.BG_LIGHT; 70 | private static final int CARD_WIDTH = 200; 71 | private static final int CARD_HEIGHT = 240; 72 | private static final int DEFAULT_UPDATE_PERIOD = 1000; 73 | private static final int UPDATE_PERIOD = 16; 74 | private static final int SIMULATED_BUFFERED_TIME = 10000; 75 | private static Context sContext; 76 | OnPlayPauseClickedListener mCallback; 77 | private ArrayObjectAdapter mRowsAdapter; 78 | private ArrayObjectAdapter mPrimaryActionsAdapter; 79 | private ArrayObjectAdapter mSecondaryActionsAdapter; 80 | private PlayPauseAction mPlayPauseAction; 81 | private RepeatAction mRepeatAction; 82 | private ThumbsUpAction mThumbsUpAction; 83 | private ThumbsDownAction mThumbsDownAction; 84 | private ShuffleAction mShuffleAction; 85 | private FastForwardAction mFastForwardAction; 86 | private RewindAction mRewindAction; 87 | private SkipNextAction mSkipNextAction; 88 | private SkipPreviousAction mSkipPreviousAction; 89 | private PlaybackControlsRow mPlaybackControlsRow; 90 | private ArrayList mItems = new ArrayList(); 91 | private int mCurrentItem; 92 | private Handler mHandler; 93 | private Runnable mRunnable; 94 | private Movie mSelectedMovie; 95 | 96 | @Override 97 | public void onCreate(Bundle savedInstanceState) { 98 | super.onCreate(savedInstanceState); 99 | sContext = getActivity(); 100 | 101 | mItems = new ArrayList(); 102 | mSelectedMovie = (Movie) getActivity() 103 | .getIntent().getSerializableExtra(DetailsActivity.MOVIE); 104 | 105 | List movies = MovieList.list; 106 | 107 | ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter()); 108 | 109 | for (int j = 0; j < movies.size(); j++) { 110 | mItems.add(movies.get(j)); 111 | if (mSelectedMovie.getTitle().contentEquals(movies.get(j).getTitle())) { 112 | mCurrentItem = j; 113 | } 114 | } 115 | 116 | mHandler = new Handler(); 117 | 118 | setBackgroundType(BACKGROUND_TYPE); 119 | setFadingEnabled(false); 120 | 121 | setupRows(); 122 | 123 | setOnItemViewSelectedListener(new OnItemViewSelectedListener() { 124 | @Override 125 | public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, 126 | RowPresenter.ViewHolder rowViewHolder, Row row) { 127 | Log.i(TAG, "onItemSelected: " + item + " row " + row); 128 | } 129 | }); 130 | setOnItemViewClickedListener(new OnItemViewClickedListener() { 131 | @Override 132 | public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, 133 | RowPresenter.ViewHolder rowViewHolder, Row row) { 134 | Log.i(TAG, "onItemClicked: " + item + " row " + row); 135 | } 136 | }); 137 | } 138 | 139 | @Override 140 | public void onAttach(Activity activity) { 141 | super.onAttach(activity); 142 | 143 | // This makes sure that the container activity has implemented 144 | // the callback interface. If not, it throws an exception 145 | try { 146 | mCallback = (OnPlayPauseClickedListener) activity; 147 | } catch (ClassCastException e) { 148 | throw new ClassCastException(activity.toString() 149 | + " must implement OnPlayPauseClickedListener"); 150 | } 151 | } 152 | 153 | private void setupRows() { 154 | 155 | ClassPresenterSelector ps = new ClassPresenterSelector(); 156 | 157 | PlaybackControlsRowPresenter playbackControlsRowPresenter; 158 | if (SHOW_DETAIL) { 159 | playbackControlsRowPresenter = new PlaybackControlsRowPresenter( 160 | new DescriptionPresenter()); 161 | } else { 162 | playbackControlsRowPresenter = new PlaybackControlsRowPresenter(); 163 | } 164 | playbackControlsRowPresenter.setOnActionClickedListener(new OnActionClickedListener() { 165 | public void onActionClicked(Action action) { 166 | if (action.getId() == mPlayPauseAction.getId()) { 167 | togglePlayback(mPlayPauseAction.getIndex() == PlayPauseAction.PLAY); 168 | } else if (action.getId() == mSkipNextAction.getId()) { 169 | next(); 170 | } else if (action.getId() == mSkipPreviousAction.getId()) { 171 | prev(); 172 | } else if (action.getId() == mFastForwardAction.getId()) { 173 | Toast.makeText(getActivity(), "TODO: Fast Forward", Toast.LENGTH_SHORT).show(); 174 | } else if (action.getId() == mRewindAction.getId()) { 175 | Toast.makeText(getActivity(), "TODO: Rewind", Toast.LENGTH_SHORT).show(); 176 | } 177 | if (action instanceof PlaybackControlsRow.MultiAction) { 178 | ((PlaybackControlsRow.MultiAction) action).nextIndex(); 179 | notifyChanged(action); 180 | } 181 | } 182 | }); 183 | playbackControlsRowPresenter.setSecondaryActionsHidden(HIDE_MORE_ACTIONS); 184 | 185 | ps.addClassPresenter(PlaybackControlsRow.class, playbackControlsRowPresenter); 186 | ps.addClassPresenter(ListRow.class, new ListRowPresenter()); 187 | mRowsAdapter = new ArrayObjectAdapter(ps); 188 | 189 | addPlaybackControlsRow(); 190 | addOtherRows(); 191 | 192 | setAdapter(mRowsAdapter); 193 | } 194 | 195 | public void togglePlayback(boolean playPause) { 196 | if (playPause) { 197 | startProgressAutomation(); 198 | setFadingEnabled(true); 199 | mCallback.onFragmentPlayPause(mItems.get(mCurrentItem), 200 | mPlaybackControlsRow.getCurrentTime(), true); 201 | mPlayPauseAction.setIcon(mPlayPauseAction.getDrawable(PlayPauseAction.PAUSE)); 202 | } else { 203 | stopProgressAutomation(); 204 | setFadingEnabled(false); 205 | mCallback.onFragmentPlayPause(mItems.get(mCurrentItem), 206 | mPlaybackControlsRow.getCurrentTime(), false); 207 | mPlayPauseAction.setIcon(mPlayPauseAction.getDrawable(PlayPauseAction.PLAY)); 208 | } 209 | notifyChanged(mPlayPauseAction); 210 | } 211 | 212 | private int getDuration() { 213 | Movie movie = mItems.get(mCurrentItem); 214 | MediaMetadataRetriever mmr = new MediaMetadataRetriever(); 215 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 216 | mmr.setDataSource(movie.getVideoUrl(), new HashMap()); 217 | } else { 218 | mmr.setDataSource(movie.getVideoUrl()); 219 | } 220 | String time = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); 221 | long duration = Long.parseLong(time); 222 | return (int) duration; 223 | } 224 | 225 | private void addPlaybackControlsRow() { 226 | if (SHOW_DETAIL) { 227 | mPlaybackControlsRow = new PlaybackControlsRow(mSelectedMovie); 228 | } else { 229 | mPlaybackControlsRow = new PlaybackControlsRow(); 230 | } 231 | mRowsAdapter.add(mPlaybackControlsRow); 232 | 233 | updatePlaybackRow(mCurrentItem); 234 | 235 | ControlButtonPresenterSelector presenterSelector = new ControlButtonPresenterSelector(); 236 | mPrimaryActionsAdapter = new ArrayObjectAdapter(presenterSelector); 237 | mSecondaryActionsAdapter = new ArrayObjectAdapter(presenterSelector); 238 | mPlaybackControlsRow.setPrimaryActionsAdapter(mPrimaryActionsAdapter); 239 | mPlaybackControlsRow.setSecondaryActionsAdapter(mSecondaryActionsAdapter); 240 | 241 | mPlayPauseAction = new PlayPauseAction(sContext); 242 | mRepeatAction = new RepeatAction(sContext); 243 | mThumbsUpAction = new ThumbsUpAction(sContext); 244 | mThumbsDownAction = new ThumbsDownAction(sContext); 245 | mShuffleAction = new ShuffleAction(sContext); 246 | mSkipNextAction = new PlaybackControlsRow.SkipNextAction(sContext); 247 | mSkipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(sContext); 248 | mFastForwardAction = new PlaybackControlsRow.FastForwardAction(sContext); 249 | mRewindAction = new PlaybackControlsRow.RewindAction(sContext); 250 | 251 | if (PRIMARY_CONTROLS > 5) { 252 | mPrimaryActionsAdapter.add(mThumbsUpAction); 253 | } else { 254 | mSecondaryActionsAdapter.add(mThumbsUpAction); 255 | } 256 | mPrimaryActionsAdapter.add(mSkipPreviousAction); 257 | if (PRIMARY_CONTROLS > 3) { 258 | mPrimaryActionsAdapter.add(new PlaybackControlsRow.RewindAction(sContext)); 259 | } 260 | mPrimaryActionsAdapter.add(mPlayPauseAction); 261 | if (PRIMARY_CONTROLS > 3) { 262 | mPrimaryActionsAdapter.add(new PlaybackControlsRow.FastForwardAction(sContext)); 263 | } 264 | mPrimaryActionsAdapter.add(mSkipNextAction); 265 | 266 | mSecondaryActionsAdapter.add(mRepeatAction); 267 | mSecondaryActionsAdapter.add(mShuffleAction); 268 | if (PRIMARY_CONTROLS > 5) { 269 | mPrimaryActionsAdapter.add(mThumbsDownAction); 270 | } else { 271 | mSecondaryActionsAdapter.add(mThumbsDownAction); 272 | } 273 | mSecondaryActionsAdapter.add(new PlaybackControlsRow.HighQualityAction(sContext)); 274 | mSecondaryActionsAdapter.add(new PlaybackControlsRow.ClosedCaptioningAction(sContext)); 275 | } 276 | 277 | private void notifyChanged(Action action) { 278 | ArrayObjectAdapter adapter = mPrimaryActionsAdapter; 279 | if (adapter.indexOf(action) >= 0) { 280 | adapter.notifyArrayItemRangeChanged(adapter.indexOf(action), 1); 281 | return; 282 | } 283 | adapter = mSecondaryActionsAdapter; 284 | if (adapter.indexOf(action) >= 0) { 285 | adapter.notifyArrayItemRangeChanged(adapter.indexOf(action), 1); 286 | return; 287 | } 288 | } 289 | 290 | private void updatePlaybackRow(int index) { 291 | if (mPlaybackControlsRow.getItem() != null) { 292 | Movie item = (Movie) mPlaybackControlsRow.getItem(); 293 | item.setTitle(mItems.get(mCurrentItem).getTitle()); 294 | item.setStudio(mItems.get(mCurrentItem).getStudio()); 295 | } 296 | if (SHOW_IMAGE) { 297 | updateVideoImage(mItems.get(mCurrentItem).getCardImageURI().toString()); 298 | } 299 | mRowsAdapter.notifyArrayItemRangeChanged(0, 1); 300 | mPlaybackControlsRow.setTotalTime(getDuration()); 301 | mPlaybackControlsRow.setCurrentTime(0); 302 | mPlaybackControlsRow.setBufferedProgress(0); 303 | } 304 | 305 | private void addOtherRows() { 306 | ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter()); 307 | for (Movie movie : mItems) { 308 | listRowAdapter.add(movie); 309 | } 310 | HeaderItem header = new HeaderItem(0, getString(R.string.related_movies)); 311 | mRowsAdapter.add(new ListRow(header, listRowAdapter)); 312 | 313 | } 314 | 315 | private int getUpdatePeriod() { 316 | if (getView() == null || mPlaybackControlsRow.getTotalTime() <= 0) { 317 | return DEFAULT_UPDATE_PERIOD; 318 | } 319 | return Math.max(UPDATE_PERIOD, mPlaybackControlsRow.getTotalTime() / getView().getWidth()); 320 | } 321 | 322 | private void startProgressAutomation() { 323 | mRunnable = new Runnable() { 324 | @Override 325 | public void run() { 326 | int updatePeriod = getUpdatePeriod(); 327 | int currentTime = mPlaybackControlsRow.getCurrentTime() + updatePeriod; 328 | int totalTime = mPlaybackControlsRow.getTotalTime(); 329 | mPlaybackControlsRow.setCurrentTime(currentTime); 330 | mPlaybackControlsRow.setBufferedProgress(currentTime + SIMULATED_BUFFERED_TIME); 331 | 332 | if (totalTime > 0 && totalTime <= currentTime) { 333 | next(); 334 | } 335 | mHandler.postDelayed(this, updatePeriod); 336 | } 337 | }; 338 | mHandler.postDelayed(mRunnable, getUpdatePeriod()); 339 | } 340 | 341 | private void next() { 342 | if (++mCurrentItem >= mItems.size()) { 343 | mCurrentItem = 0; 344 | } 345 | 346 | if (mPlayPauseAction.getIndex() == PlayPauseAction.PLAY) { 347 | mCallback.onFragmentPlayPause(mItems.get(mCurrentItem), 0, false); 348 | } else { 349 | mCallback.onFragmentPlayPause(mItems.get(mCurrentItem), 0, true); 350 | } 351 | updatePlaybackRow(mCurrentItem); 352 | } 353 | 354 | private void prev() { 355 | if (--mCurrentItem < 0) { 356 | mCurrentItem = mItems.size() - 1; 357 | } 358 | if (mPlayPauseAction.getIndex() == PlayPauseAction.PLAY) { 359 | mCallback.onFragmentPlayPause(mItems.get(mCurrentItem), 0, false); 360 | } else { 361 | mCallback.onFragmentPlayPause(mItems.get(mCurrentItem), 0, true); 362 | } 363 | updatePlaybackRow(mCurrentItem); 364 | } 365 | 366 | private void stopProgressAutomation() { 367 | if (mHandler != null && mRunnable != null) { 368 | mHandler.removeCallbacks(mRunnable); 369 | } 370 | } 371 | 372 | @Override 373 | public void onStop() { 374 | stopProgressAutomation(); 375 | super.onStop(); 376 | } 377 | 378 | protected void updateVideoImage(String uri) { 379 | Glide.with(sContext) 380 | .load(uri) 381 | .centerCrop() 382 | .into(new SimpleTarget(CARD_WIDTH, CARD_HEIGHT) { 383 | @Override 384 | public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) { 385 | mPlaybackControlsRow.setImageDrawable(resource); 386 | mRowsAdapter.notifyArrayItemRangeChanged(0, mRowsAdapter.size()); 387 | } 388 | }); 389 | } 390 | 391 | // Container Activity must implement this interface 392 | public interface OnPlayPauseClickedListener { 393 | public void onFragmentPlayPause(Movie movie, int position, Boolean playPause); 394 | } 395 | 396 | static class DescriptionPresenter extends AbstractDetailsDescriptionPresenter { 397 | @Override 398 | protected void onBindDescription(ViewHolder viewHolder, Object item) { 399 | viewHolder.getTitle().setText(((Movie) item).getTitle()); 400 | viewHolder.getSubtitle().setText(((Movie) item).getStudio()); 401 | } 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/java/com/example/liuyongkui/tvdemo/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.example.liuyongkui.tvdemo; 16 | 17 | import android.app.AlertDialog; 18 | import android.content.Context; 19 | import android.content.DialogInterface; 20 | import android.graphics.Point; 21 | import android.view.Display; 22 | import android.view.WindowManager; 23 | import android.widget.Toast; 24 | 25 | /** 26 | * A collection of utility methods, all static. 27 | */ 28 | public class Utils { 29 | 30 | /* 31 | * Making sure public utility methods remain static 32 | */ 33 | private Utils() { 34 | } 35 | 36 | /** 37 | * Returns the screen/display size 38 | */ 39 | public static Point getDisplaySize(Context context) { 40 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 41 | Display display = wm.getDefaultDisplay(); 42 | Point size = new Point(); 43 | display.getSize(size); 44 | return size; 45 | } 46 | 47 | /** 48 | * Shows a (long) toast 49 | */ 50 | public static void showToast(Context context, String msg) { 51 | Toast.makeText(context, msg, Toast.LENGTH_LONG).show(); 52 | } 53 | 54 | /** 55 | * Shows a (long) toast. 56 | */ 57 | public static void showToast(Context context, int resourceId) { 58 | Toast.makeText(context, context.getString(resourceId), Toast.LENGTH_LONG).show(); 59 | } 60 | 61 | public static int convertDpToPixel(Context ctx, int dp) { 62 | float density = ctx.getResources().getDisplayMetrics().density; 63 | return Math.round((float) dp * density); 64 | } 65 | 66 | /** 67 | * Formats time in milliseconds to hh:mm:ss string format. 68 | */ 69 | public static String formatMillis(int millis) { 70 | String result = ""; 71 | int hr = millis / 3600000; 72 | millis %= 3600000; 73 | int min = millis / 60000; 74 | millis %= 60000; 75 | int sec = millis / 1000; 76 | if (hr > 0) { 77 | result += hr + ":"; 78 | } 79 | if (min >= 0) { 80 | if (min > 9) { 81 | result += min + ":"; 82 | } else { 83 | result += "0" + min + ":"; 84 | } 85 | } 86 | if (sec > 9) { 87 | result += sec; 88 | } else { 89 | result += "0" + sec; 90 | } 91 | return result; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/java/com/example/liuyongkui/tvdemo/VideoDetailsFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.example.liuyongkui.tvdemo; 16 | 17 | import android.content.Intent; 18 | import android.graphics.drawable.Drawable; 19 | import android.os.Bundle; 20 | import android.support.v17.leanback.app.BackgroundManager; 21 | import android.support.v17.leanback.app.DetailsFragment; 22 | import android.support.v17.leanback.widget.Action; 23 | import android.support.v17.leanback.widget.ArrayObjectAdapter; 24 | import android.support.v17.leanback.widget.ClassPresenterSelector; 25 | import android.support.v17.leanback.widget.DetailsOverviewRow; 26 | import android.support.v17.leanback.widget.DetailsOverviewRowPresenter; 27 | import android.support.v17.leanback.widget.HeaderItem; 28 | import android.support.v17.leanback.widget.ImageCardView; 29 | import android.support.v17.leanback.widget.ListRow; 30 | import android.support.v17.leanback.widget.ListRowPresenter; 31 | import android.support.v17.leanback.widget.OnActionClickedListener; 32 | import android.support.v17.leanback.widget.OnItemViewClickedListener; 33 | import android.support.v17.leanback.widget.Presenter; 34 | import android.support.v17.leanback.widget.Row; 35 | import android.support.v17.leanback.widget.RowPresenter; 36 | import android.support.v4.app.ActivityOptionsCompat; 37 | import android.util.DisplayMetrics; 38 | import android.util.Log; 39 | import android.widget.Toast; 40 | 41 | import com.bumptech.glide.Glide; 42 | import com.bumptech.glide.load.resource.drawable.GlideDrawable; 43 | import com.bumptech.glide.request.animation.GlideAnimation; 44 | import com.bumptech.glide.request.target.SimpleTarget; 45 | 46 | import java.util.Collections; 47 | import java.util.List; 48 | 49 | /* 50 | * LeanbackDetailsFragment extends DetailsFragment, a Wrapper fragment for leanback details screens. 51 | * It shows a detailed view of video and its meta plus related videos. 52 | */ 53 | public class VideoDetailsFragment extends DetailsFragment { 54 | private static final String TAG = "VideoDetailsFragment"; 55 | 56 | private static final int ACTION_WATCH_TRAILER = 1; 57 | private static final int ACTION_RENT = 2; 58 | private static final int ACTION_BUY = 3; 59 | 60 | private static final int DETAIL_THUMB_WIDTH = 274; 61 | private static final int DETAIL_THUMB_HEIGHT = 274; 62 | 63 | private static final int NUM_COLS = 10; 64 | 65 | private Movie mSelectedMovie; 66 | 67 | private ArrayObjectAdapter mAdapter; 68 | private ClassPresenterSelector mPresenterSelector; 69 | 70 | private BackgroundManager mBackgroundManager; 71 | private Drawable mDefaultBackground; 72 | private DisplayMetrics mMetrics; 73 | 74 | @Override 75 | public void onCreate(Bundle savedInstanceState) { 76 | Log.d(TAG, "onCreate DetailsFragment"); 77 | super.onCreate(savedInstanceState); 78 | 79 | prepareBackgroundManager(); 80 | 81 | mSelectedMovie = (Movie) getActivity().getIntent() 82 | .getSerializableExtra(DetailsActivity.MOVIE); 83 | if (mSelectedMovie != null) { 84 | setupAdapter(); 85 | setupDetailsOverviewRow(); 86 | setupDetailsOverviewRowPresenter(); 87 | setupMovieListRow(); 88 | setupMovieListRowPresenter(); 89 | updateBackground(mSelectedMovie.getBackgroundImageUrl()); 90 | setOnItemViewClickedListener(new ItemViewClickedListener()); 91 | } else { 92 | Intent intent = new Intent(getActivity(), MainActivity.class); 93 | startActivity(intent); 94 | } 95 | } 96 | 97 | @Override 98 | public void onStop() { 99 | super.onStop(); 100 | } 101 | 102 | private void prepareBackgroundManager() { 103 | mBackgroundManager = BackgroundManager.getInstance(getActivity()); 104 | mBackgroundManager.attach(getActivity().getWindow()); 105 | mDefaultBackground = getResources().getDrawable(R.drawable.default_background); 106 | mMetrics = new DisplayMetrics(); 107 | getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics); 108 | } 109 | 110 | protected void updateBackground(String uri) { 111 | Glide.with(getActivity()) 112 | .load(uri) 113 | .centerCrop() 114 | .error(mDefaultBackground) 115 | .into(new SimpleTarget(mMetrics.widthPixels, mMetrics.heightPixels) { 116 | @Override 117 | public void onResourceReady(GlideDrawable resource, 118 | GlideAnimation glideAnimation) { 119 | mBackgroundManager.setDrawable(resource); 120 | } 121 | }); 122 | } 123 | 124 | private void setupAdapter() { 125 | mPresenterSelector = new ClassPresenterSelector(); 126 | mAdapter = new ArrayObjectAdapter(mPresenterSelector); 127 | setAdapter(mAdapter); 128 | } 129 | 130 | private void setupDetailsOverviewRow() { 131 | Log.d(TAG, "doInBackground: " + mSelectedMovie.toString()); 132 | final DetailsOverviewRow row = new DetailsOverviewRow(mSelectedMovie); 133 | row.setImageDrawable(getResources().getDrawable(R.drawable.default_background)); 134 | int width = Utils.convertDpToPixel(getActivity() 135 | .getApplicationContext(), DETAIL_THUMB_WIDTH); 136 | int height = Utils.convertDpToPixel(getActivity() 137 | .getApplicationContext(), DETAIL_THUMB_HEIGHT); 138 | Glide.with(getActivity()) 139 | .load(mSelectedMovie.getCardImageUrl()) 140 | .centerCrop() 141 | .error(R.drawable.default_background) 142 | .into(new SimpleTarget(width, height) { 143 | @Override 144 | public void onResourceReady(GlideDrawable resource, 145 | GlideAnimation 146 | glideAnimation) { 147 | Log.d(TAG, "details overview card image url ready: " + resource); 148 | row.setImageDrawable(resource); 149 | mAdapter.notifyArrayItemRangeChanged(0, mAdapter.size()); 150 | } 151 | }); 152 | 153 | row.addAction(new Action(ACTION_WATCH_TRAILER, getResources().getString( 154 | R.string.watch_trailer_1), getResources().getString(R.string.watch_trailer_2))); 155 | row.addAction(new Action(ACTION_RENT, getResources().getString(R.string.rent_1), 156 | getResources().getString(R.string.rent_2))); 157 | row.addAction(new Action(ACTION_BUY, getResources().getString(R.string.buy_1), 158 | getResources().getString(R.string.buy_2))); 159 | 160 | mAdapter.add(row); 161 | } 162 | 163 | private void setupDetailsOverviewRowPresenter() { 164 | // Set detail background and style. 165 | DetailsOverviewRowPresenter detailsPresenter = 166 | new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter()); 167 | detailsPresenter.setBackgroundColor(getResources().getColor(R.color.selected_background)); 168 | detailsPresenter.setStyleLarge(true); 169 | 170 | // Hook up transition element. 171 | detailsPresenter.setSharedElementEnterTransition(getActivity(), 172 | DetailsActivity.SHARED_ELEMENT_NAME); 173 | 174 | detailsPresenter.setOnActionClickedListener(new OnActionClickedListener() { 175 | @Override 176 | public void onActionClicked(Action action) { 177 | if (action.getId() == ACTION_WATCH_TRAILER) { 178 | Intent intent = new Intent(getActivity(), PlaybackOverlayActivity.class); 179 | intent.putExtra(DetailsActivity.MOVIE, mSelectedMovie); 180 | startActivity(intent); 181 | } else { 182 | Toast.makeText(getActivity(), action.toString(), Toast.LENGTH_SHORT).show(); 183 | } 184 | } 185 | }); 186 | mPresenterSelector.addClassPresenter(DetailsOverviewRow.class, detailsPresenter); 187 | } 188 | 189 | private void setupMovieListRow() { 190 | String subcategories[] = {getString(R.string.related_movies)}; 191 | List list = MovieList.list; 192 | 193 | Collections.shuffle(list); 194 | ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter()); 195 | for (int j = 0; j < NUM_COLS; j++) { 196 | listRowAdapter.add(list.get(j % 5)); 197 | } 198 | 199 | HeaderItem header = new HeaderItem(0, subcategories[0]); 200 | mAdapter.add(new ListRow(header, listRowAdapter)); 201 | } 202 | 203 | private void setupMovieListRowPresenter() { 204 | mPresenterSelector.addClassPresenter(ListRow.class, new ListRowPresenter()); 205 | } 206 | 207 | private final class ItemViewClickedListener implements OnItemViewClickedListener { 208 | @Override 209 | public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, 210 | RowPresenter.ViewHolder rowViewHolder, Row row) { 211 | 212 | if (item instanceof Movie) { 213 | Movie movie = (Movie) item; 214 | Log.d(TAG, "Item: " + item.toString()); 215 | Intent intent = new Intent(getActivity(), DetailsActivity.class); 216 | intent.putExtra(getResources().getString(R.string.movie), mSelectedMovie); 217 | intent.putExtra(getResources().getString(R.string.should_start), true); 218 | startActivity(intent); 219 | 220 | 221 | Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation( 222 | getActivity(), 223 | ((ImageCardView) itemViewHolder.view).getMainImageView(), 224 | DetailsActivity.SHARED_ELEMENT_NAME).toBundle(); 225 | getActivity().startActivity(intent, bundle); 226 | } 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-hdpi/app_icon_quantum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-hdpi/app_icon_quantum.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-hdpi/app_icon_quantum_card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-hdpi/app_icon_quantum_card.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-hdpi/card_background_default.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-hdpi/card_background_default.9.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-hdpi/default_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-hdpi/grid_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-hdpi/grid_bg.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-hdpi/scrubber_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-hdpi/scrubber_disabled.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-hdpi/scrubber_focussed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-hdpi/scrubber_focussed.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-hdpi/scrubber_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-hdpi/scrubber_normal.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-hdpi/scrubber_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-hdpi/scrubber_pressed.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-hdpi/shadow7.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-hdpi/shadow7.9.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-hdpi/star_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-hdpi/star_icon.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-hdpi/videos_by_google_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-hdpi/videos_by_google_banner.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-hdpi/videos_by_google_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-hdpi/videos_by_google_icon.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-mdpi/app_icon_quantum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-mdpi/app_icon_quantum.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-mdpi/app_icon_quantum_card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-mdpi/app_icon_quantum_card.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-mdpi/movie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-mdpi/movie.jpg -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-mdpi/videos_by_google_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-mdpi/videos_by_google_banner.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-mdpi/videos_by_google_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-mdpi/videos_by_google_icon.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/app_icon_quantum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/app_icon_quantum.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/app_icon_quantum_card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/app_icon_quantum_card.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/default_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/grid_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/grid_bg.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/ic_pause_playcontrol_focussed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/ic_pause_playcontrol_focussed.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/ic_pause_playcontrol_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/ic_pause_playcontrol_normal.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/ic_pause_playcontrol_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/ic_pause_playcontrol_pressed.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/ic_play_action_focussed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/ic_play_action_focussed.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/ic_play_action_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/ic_play_action_normal.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/ic_play_action_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/ic_play_action_pressed.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/ic_play_playcontrol_focussed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/ic_play_playcontrol_focussed.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/ic_play_playcontrol_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/ic_play_playcontrol_normal.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/ic_play_playcontrol_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/ic_play_playcontrol_pressed.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/star_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/star_icon.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/videos_by_google_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/videos_by_google_banner.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xhdpi/videos_by_google_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xhdpi/videos_by_google_icon.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xxhdpi/app_icon_quantum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xxhdpi/app_icon_quantum.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xxhdpi/app_icon_quantum_card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xxhdpi/app_icon_quantum_card.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xxhdpi/videos_by_google_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xxhdpi/videos_by_google_banner.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable-xxhdpi/videos_by_google_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable-xxhdpi/videos_by_google_icon.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable/app_icon_quantum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable/app_icon_quantum.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable/app_icon_quantum_card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable/app_icon_quantum_card.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable/app_icon_your_company.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable/app_icon_your_company.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable/details_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable/details_img.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable/ic_action_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable/ic_action_a.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable/ic_title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable/ic_title.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable/player_bg_gradient_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable/shadow7.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable/shadow7.9.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable/videos_by_google_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable/videos_by_google_banner.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/drawable/videos_by_google_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/drawable/videos_by_google_icon.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/layout/activity_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/layout/playback_controls.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 | 21 | 31 | 32 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/TvFrameWork/f8613ca033ccc173fb29372d7c38d7e9835880ad/TvDemo/tv/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | #000000 3 | #DDDDDD 4 | #0096a6 5 | #ffaa3f 6 | #ffaa3f 7 | #0096a6 8 | #30000000 9 | #30FF0000 10 | #00000000 11 | #AA000000 12 | #59000000 13 | #FFFFFF 14 | #AAFADCA7 15 | #FADCA7 16 | #EEFF41 17 | #3d3d3d 18 | 19 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | TvDemo 3 | 4 | Related Videos 5 | 6 | Error 7 | OK 8 | Pause 9 | Play 10 | Stop 11 | 00:00 12 | Play Pause Button 13 | Loading… 14 | No video was found 15 | About DemoCast Player 16 | Version: %1$s 17 | Popular Videos 18 | PREFERENCES 19 | Grid View 20 | Error Fragment 21 | Personal Settings 22 | Watch trailer 23 | FREE 24 | Rent By Day 25 | From $1.99 26 | Buy and Own 27 | AT $9.99 28 | Movie 29 | shouldStart 30 | startPosition 31 | Search Results 32 | 33 | 34 | Media loading timed out 35 | Media server was not reachable 36 | Failed to load video 37 | An error occurred 38 | Dismiss 39 | Oops 40 | 41 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /TvDemo/tv/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | --------------------------------------------------------------------------------