Merge "Implemented Card-, DetailView-, GridView-, Music- & Video, Wizard-, Dialog examples" into mnc-ub-dev

This commit is contained in:
Robert Hahn
2015-07-31 21:06:06 +00:00
committed by Android (Google) Code Review
226 changed files with 7243 additions and 0 deletions

View File

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

View File

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

View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="SupportLeanbackShowcase" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":app" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
<option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
<option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugAndroidTestSources" />
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/leanback-v17/22.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/palette-v7/21.0.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/22.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/22.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="appcompat-v7-22.1.1" level="project" />
<orderEntry type="library" exported="" name="leanback-v17-22.1.1" level="project" />
<orderEntry type="library" exported="" name="palette-v7-21.0.0" level="project" />
<orderEntry type="library" exported="" name="gson-1.7.2" level="project" />
<orderEntry type="library" exported="" name="recyclerview-v7-22.1.1" level="project" />
<orderEntry type="library" exported="" name="support-v4-22.1.1" level="project" />
<orderEntry type="library" exported="" name="support-annotations-22.1.1" level="project" />
<orderEntry type="library" exported="" name="picasso-2.3.2" level="project" />
</component>
</module>

View File

@@ -0,0 +1,36 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 'android-MNC'
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "android.support.v17.leanback.supportleanbackshowcase"
minSdkVersion 17
targetSdkVersion 'MNC'
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:recyclerview-v7:23.0.0-rc1'
compile 'com.android.support:leanback-v17:23.0.0-rc1'
compile 'com.android.support:appcompat-v7:23.0.0-rc1'
compile 'com.android.support:preference-v7:23.0.0-rc1'
compile 'com.android.support:preference-leanback-v17:23.0.0-rc1'
compile 'com.android.support:preference-v14:23.0.0-rc1'
compile 'com.google.code.gson:gson:1.7.2'
compile 'com.squareup.picasso:picasso:2.3.2'
compile 'com.android.support:palette-v7:23.0.0-rc1'
}

View File

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

View File

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

View File

@@ -0,0 +1,44 @@
<manifest
package="android.support.v17.leanback.supportleanbackshowcase"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false"/>
<uses-feature
android:name="android.software.leanback"
android:required="true"/>
<application
android:allowBackup="true"
android:icon="@mipmap/app_banner_sample_app"
android:label="@string/app_name"
android:largeHeap="true"
android:theme="@style/Theme.Example.Leanback">
<activity
android:name=".MainActivity"
android:icon="@mipmap/app_banner_sample_app"
android:label="@string/app_name"
android:logo="@mipmap/app_banner_sample_app"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".WizardExampleActivity"
android:theme="@style/Theme.Example.LeanbackWizard"></activity>
<activity
android:name=".SettingsExampleActivity"
android:theme="@style/Theme.Example.LeanbackPreferences"></activity>
<activity
android:name=".DialogExampleActivity"
android:theme="@style/Theme.Example.LeanbackDialog"></activity>
</application>
</manifest>

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.os.Bundle;
import android.support.v17.leanback.app.BrowseFragment;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.CardRow;
import android.support.v17.leanback.supportleanbackshowcase.cards.presenters.CardPresenterSelector;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.PresenterSelector;
import android.support.v17.leanback.widget.SearchOrbView;
import android.view.View;
import android.widget.Toast;
import com.google.gson.Gson;
public class BrowserExample01Fragment extends BrowseFragment {
private static final String TAG = "BrowserExample01Fragment";
private ArrayObjectAdapter mRowsAdapter;
@Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setupUi();
setupRowAdapter();
}
private void setupUi() {
setHeadersState(HEADERS_ENABLED);
setHeadersTransitionOnBackEnabled(true);
setSearchAffordanceColors(
new SearchOrbView.Colors(getResources().getColor(R.color.search_color),
getResources().getColor(R.color.search_bright_color),
getResources().getColor(R.color.search_icon_color)));
setBrandColor(getResources().getColor(R.color.fastlane_background));
setTitle("Browser Example 01");
setOnSearchClickedListener(new View.OnClickListener() {
@Override public void onClick(View v) {
Toast.makeText(getActivity(), getString(R.string.implement_search),
Toast.LENGTH_LONG).show();
}
});
}
private void setupRowAdapter() {
mRowsAdapter = new ArrayObjectAdapter(new ShadowRowPresenterSelector());
createRows();
setAdapter(mRowsAdapter);
}
private void createRows() {
String json = Utils
.inputStreamToString(getResources().openRawResource(R.raw.browsing_example_01));
CardRow[] rows = new Gson().fromJson(json, CardRow[].class);
for (CardRow row : rows) {
mRowsAdapter.add(createCardRow(row));
}
}
private ListRow createCardRow(final CardRow cardRow) {
// Build main row using the ImageCardViewPresenter.
PresenterSelector presenterSelector = new CardPresenterSelector(getActivity());
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(presenterSelector);
for (Card card : cardRow.getCards()) {
listRowAdapter.add(card);
}
HeaderItem header = new HeaderItem(cardRow.getTitle());
return new CardListRow(header, listRowAdapter, cardRow);
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.os.Bundle;
import android.support.v17.leanback.app.BrowseFragment;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.CardRow;
import android.support.v17.leanback.supportleanbackshowcase.cards.presenters.CardPresenterSelector;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.PresenterSelector;
import android.support.v17.leanback.widget.SearchOrbView;
import android.view.View;
import android.widget.Toast;
import com.google.gson.Gson;
/**
* This fragment will be shown when the "Card Examples" card is selected at the home menu. It will
* display multiple card types.
*/
public class CardExampleFragment extends BrowseFragment {
private static final String TAG = "CardExampleFragment";
private ArrayObjectAdapter mRowsAdapter;
@Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setupUi();
setupRowAdapter();
}
private void setupUi() {
setHeadersState(HEADERS_ENABLED);
setHeadersTransitionOnBackEnabled(true);
setSearchAffordanceColors(
new SearchOrbView.Colors(getResources().getColor(R.color.search_color),
getResources().getColor(R.color.search_bright_color),
getResources().getColor(R.color.search_icon_color)));
setBrandColor(getResources().getColor(R.color.fastlane_background));
setTitle(getString(R.string.card_examples_title));
setOnSearchClickedListener(new View.OnClickListener() {
@Override public void onClick(View v) {
Toast.makeText(getActivity(), getString(R.string.implement_search),
Toast.LENGTH_LONG).show();
}
});
}
private void setupRowAdapter() {
mRowsAdapter = new ArrayObjectAdapter(new ShadowRowPresenterSelector());
createRows();
setAdapter(mRowsAdapter);
}
private void createRows() {
String json = Utils
.inputStreamToString(getResources().openRawResource(R.raw.cards_card_example));
CardRow[] rows = new Gson().fromJson(json, CardRow[].class);
for (CardRow row : rows) {
mRowsAdapter.add(createCardRow(row));
}
}
private ListRow createCardRow(final CardRow cardRow) {
// Build main row using the ImageCardViewPresenter.
PresenterSelector presenterSelector = new CardPresenterSelector(getActivity());
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(presenterSelector);
for (Card card : cardRow.getCards()) {
listRowAdapter.add(card);
}
HeaderItem header = new HeaderItem(cardRow.getTitle());
return new CardListRow(header, listRowAdapter, cardRow);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.CardRow;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ObjectAdapter;
/**
* The {@link CardListRow} allows the {@link ShadowRowPresenterSelector} to access the {@link CardRow}
* held by the row and determine whether to use a {@link android.support.v17.leanback.widget.Presenter}
* with or without a shadow.
*/
public class CardListRow extends ListRow {
private CardRow mCardRow;
public CardListRow(HeaderItem header, ObjectAdapter adapter, CardRow cardRow) {
super(header, adapter);
setCardRow(cardRow);
}
public CardRow getCardRow() {
return mCardRow;
}
public void setCardRow(CardRow cardRow) {
this.mCardRow = cardRow;
}
}

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
public class Constants {
public static final boolean LOCAL_LOGD = true;
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.os.Bundle;
import android.support.v17.leanback.app.DetailsFragment;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.presenters.CardPresenterSelector;
import android.support.v17.leanback.widget.Action;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.ClassPresenterSelector;
import android.support.v17.leanback.widget.DetailsOverviewRow;
import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.OnItemViewClickedListener;
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.google.gson.Gson;
/**
* Displays a card with more details.
*/
public class DetailViewExampleFragment extends DetailsFragment implements OnItemViewClickedListener,
OnItemViewSelectedListener {
private static final String TAG = "DetailViewExampleFragment";
private ArrayObjectAdapter mRowsAdapter;
@Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setupUi();
setupEventListeners();
}
private void setupUi() {
String json = Utils
.inputStreamToString(getResources().openRawResource(R.raw.detail_example));
DetailedCard data = new Gson().fromJson(json, DetailedCard.class);
setTitle(getString(R.string.detail_view_title));
FullWidthDetailsOverviewRowPresenter rowPresenter = new FullWidthDetailsOverviewRowPresenter(
new DetailsDescriptionPresenter(getActivity())) {
@Override protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
RowPresenter.ViewHolder viewHolder = super.createRowViewHolder(parent);
// TODO: hahnr@ replace with API calls (they don't exist yet)
View actionsView = viewHolder.view.findViewById(R.id.details_overview_actions);
actionsView.setBackgroundColor(getActivity().getResources().getColor(
R.color.detail_view_actionbar_background));
View detailsView = viewHolder.view.findViewById(R.id.details_frame);
detailsView.setBackgroundColor(
getActivity().getResources().getColor(R.color.detail_view_background));
return viewHolder;
}
};
ListRowPresenter shadowDisabledRowPresenter = new ListRowPresenter();
shadowDisabledRowPresenter.setShadowEnabled(false);
// Setup PresenterSelector to distinguish between the different rows.
ClassPresenterSelector rowPresenterSelector = new ClassPresenterSelector();
rowPresenterSelector.addClassPresenter(DetailsOverviewRow.class, rowPresenter);
rowPresenterSelector.addClassPresenter(CardListRow.class, shadowDisabledRowPresenter);
rowPresenterSelector.addClassPresenter(ListRow.class, new ListRowPresenter());
mRowsAdapter = new ArrayObjectAdapter(rowPresenterSelector);
// Setup action and detail row.
DetailsOverviewRow detailsOverview = new DetailsOverviewRow(data);
int imageResId = data.getLocalImageResourceId(getActivity());
detailsOverview.setImageDrawable(getResources().getDrawable(imageResId, null));
ArrayObjectAdapter actionAdapter = new ArrayObjectAdapter();
actionAdapter.add(new Action(1, getString(R.string.action_buy) + data.getPrice()));
actionAdapter.add(new Action(2, getString(R.string.action_wishlist)));
actionAdapter.add(new Action(3, getString(R.string.action_related)));
detailsOverview.setActionsAdapter(actionAdapter);
mRowsAdapter.add(detailsOverview);
// Setup related row.
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
new CardPresenterSelector(getActivity()));
for (Card characterCard : data.getCharacters()) listRowAdapter.add(characterCard);
HeaderItem header = new HeaderItem(0, getString(R.string.header_related));
mRowsAdapter.add(new CardListRow(header, listRowAdapter, null));
// Setup recommended row.
listRowAdapter = new ArrayObjectAdapter(new CardPresenterSelector(getActivity()));
for (Card card : data.getRecommended()) listRowAdapter.add(card);
header = new HeaderItem(1, getString(R.string.header_recommended));
mRowsAdapter.add(new ListRow(header, listRowAdapter));
setAdapter(mRowsAdapter);
}
private void setupEventListeners() {
// FIXME: leanbackteam@ The item & itemViewHolder parameters in onItemSelected are null in the DetailsOverviewRow as long as the user navigates top or down. After navigating left or right it will be the actual item rather than null.
setOnItemViewSelectedListener(this);
setOnItemViewClickedListener(this);
}
@Override public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
if (!(item instanceof Action)) return;
Action action = (Action) item;
if (action.getId() == 3) {
setSelectedPosition(1);
} else Toast.makeText(getActivity(), getString(R.string.action_cicked), Toast.LENGTH_LONG)
.show();
}
@Override public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
if (mRowsAdapter.indexOf(row) > 0) {
getView().setBackgroundColor(
getResources().getColor(R.color.detail_view_related_background));
} else {
getView().setBackgroundResource(R.drawable.background_canyon);
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.content.Context;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import com.google.gson.annotations.SerializedName;
public class DetailedCard {
@SerializedName("title") private String mTitle = "";
@SerializedName("description") private String mDescription = "";
@SerializedName("text") private String mText = "";
@SerializedName("localImageResource") private String mLocalImageResource = null;
@SerializedName("price") private String mPrice = null;
@SerializedName("characters") private Card[] mCharacters = null;
@SerializedName("recommended") private Card[] mRecommended = null;
@SerializedName("year") private int mYear = 0;
public String getPrice() {
return mPrice;
}
public int getYear() {
return mYear;
}
public String getLocalImageResource() {
return mLocalImageResource;
}
public String getText() {
return mText;
}
public String getDescription() {
return mDescription;
}
public String getTitle() {
return mTitle;
}
public Card[] getCharacters() {
return mCharacters;
}
public Card[] getRecommended() {
return mRecommended;
}
public int getLocalImageResourceId(Context context) {
return context.getResources()
.getIdentifier(getLocalImageResource(), "drawable", context.getPackageName());
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.content.Context;
import android.support.v17.leanback.widget.Presenter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
/**
* This presenter is used to render a {@link DetailedCard} in the {@link
* DetailViewExampleFragment}.
*/
public class DetailsDescriptionPresenter extends Presenter {
private ResourceCache mResourceCache = new ResourceCache();
private Context mContext;
public DetailsDescriptionPresenter(Context context) {
mContext = context;
}
@Override public ViewHolder onCreateViewHolder(ViewGroup parent) {
View view = LayoutInflater.from(mContext).inflate(R.layout.detail_view, null);
return new ViewHolder(view);
}
@Override public void onBindViewHolder(ViewHolder viewHolder, Object item) {
TextView primaryText = mResourceCache.getViewById(viewHolder.view, R.id.primary_text);
TextView sndText1 = mResourceCache.getViewById(viewHolder.view, R.id.secondary_text_first);
TextView sndText2 = mResourceCache.getViewById(viewHolder.view, R.id.secondary_text_second);
TextView extraText = mResourceCache.getViewById(viewHolder.view, R.id.extra_text);
DetailedCard card = (DetailedCard) item;
primaryText.setText(card.getTitle());
sndText1.setText(card.getDescription());
sndText2.setText(card.getYear() + "");
extraText.setText(card.getText());
}
@Override public void onUnbindViewHolder(ViewHolder viewHolder) {
// Nothing to do here.
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.v17.leanback.app.GuidedStepFragment;
/**
* TODO: Javadoc
*/
public class DialogExampleActivity extends Activity {
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#21272A")));
GuidedStepFragment fragment = new DialogExampleFragment();
GuidedStepFragment.add(getFragmentManager(), fragment);
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v17.leanback.app.GuidedStepFragment;
import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;
import android.widget.Toast;
import java.util.List;
/**
* TODO: Javadoc
*/
public class DialogExampleFragment extends GuidedStepFragment {
private static final int ACTION_ID_POSITIVE = 1;
private static final int ACTION_ID_NEGATIVE = ACTION_ID_POSITIVE + 1;
@NonNull
@Override
public Guidance onCreateGuidance(Bundle savedInstanceState) {
Guidance guidance = new Guidance(getString(R.string.dialog_example_title),
getString(R.string.dialog_example_description),
"", null);
return guidance;
}
@Override
public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
GuidedAction action = new GuidedAction.Builder()
.id(ACTION_ID_POSITIVE)
.title(getString(R.string.dialog_example_button_positive)).build();
actions.add(action);
action = new GuidedAction.Builder()
.id(ACTION_ID_NEGATIVE)
.title(getString(R.string.dialog_example_button_negative)).build();
actions.add(action);
}
@Override
public void onGuidedActionClicked(GuidedAction action) {
if (ACTION_ID_POSITIVE == action.getId()) {
Toast.makeText(getActivity(), R.string.dialog_example_button_toast_positive_clicked,
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), R.string.dialog_example_button_toast_negative_clicked,
Toast.LENGTH_SHORT).show();
}
getActivity().finish();
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.os.Bundle;
import android.support.v17.leanback.app.VerticalGridFragment;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.CardRow;
import android.support.v17.leanback.supportleanbackshowcase.cards.presenters.CardPresenterSelector;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.FocusHighlight;
import android.support.v17.leanback.widget.PresenterSelector;
import android.support.v17.leanback.widget.VerticalGridPresenter;
import com.google.gson.Gson;
/**
* An example how to use leanback's {@link VerticalGridFragment}.
*/
public class GridExample extends VerticalGridFragment {
private static final String TAG = "GridExample";
private static final int COLUMNS = 4;
private static final int ZOOM_FACTOR = FocusHighlight.ZOOM_FACTOR_NONE;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(getString(R.string.grid_example_title));
setupRowAdapter();
}
private void setupRowAdapter() {
VerticalGridPresenter gridPresenter = new VerticalGridPresenter(ZOOM_FACTOR);
gridPresenter.setNumberOfColumns(COLUMNS);
setGridPresenter(gridPresenter);
PresenterSelector cardPresenterSelector = new CardPresenterSelector(getActivity());
ArrayObjectAdapter adapter = new ArrayObjectAdapter(cardPresenterSelector);
String json = Utils.inputStreamToString(getResources().openRawResource(R.raw.grid_example));
CardRow row = new Gson().fromJson(json, CardRow.class);
adapter.addAll(0, row.getCards());
setAdapter(adapter);
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
/*
* MainActivity class that loads MainFragment
*/
public class MainActivity extends Activity {
/**
* Called when the activity is first created.
*/
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
Fragment fragment = new MainFragment();
getFragmentManager().beginTransaction().replace(R.id.fragmentContainer, fragment)
.commit();
}
}
}

View File

@@ -0,0 +1,266 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.app.Fragment;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.support.v17.leanback.app.BackgroundManager;
import android.support.v17.leanback.app.BrowseFragment;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.CardRow;
import android.support.v17.leanback.supportleanbackshowcase.cards.presenters.CardPresenterSelector;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.OnItemViewClickedListener;
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.PresenterSelector;
import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter;
import android.util.DisplayMetrics;
import android.util.Log;
import com.google.gson.Gson;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import java.net.URI;
import java.util.Timer;
import java.util.TimerTask;
public class MainFragment extends BrowseFragment {
public static final String VIDEO_SURFACE_FRAGMENT_TAG = "VIDEO_SURFACE";
private static final String TAG = "MainFragment";
private static final int BACKGROUND_UPDATE_DELAY = 300;
private static final int DEFAULT_BACKGROUND_IMAGE = R.drawable.default_background;
private final Handler mHandler = new Handler();
private ArrayObjectAdapter mRowsAdapter;
private Target mBackgroundTarget;
private Timer mBackgroundTimer;
private URI mBackgroundURI;
private BackgroundManager mBackgroundManager;
private DisplayMetrics mMetrics;
@Override public void onActivityCreated(Bundle savedInstanceState) {
if (Constants.LOCAL_LOGD) Log.d(TAG, "onActivityCreated");
super.onActivityCreated(savedInstanceState);
setupBackgroundManager();
setupUIElements();
setupRowAdapter();
setupEventListeners();
}
@Override public void onDestroy() {
super.onDestroy();
if (null != mBackgroundTimer) {
if (Constants.LOCAL_LOGD) Log.d(TAG, "onDestroy: " + mBackgroundTimer.toString());
mBackgroundTimer.cancel();
}
}
private void setupRowAdapter() {
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
createRows();
setAdapter(mRowsAdapter);
}
private void createRows() {
String json = Utils
.inputStreamToString(getResources().openRawResource(R.raw.cards_launcher));
CardRow[] rows = new Gson().fromJson(json, CardRow[].class);
for (CardRow row : rows) {
mRowsAdapter.add(createCardRow(row));
}
}
private ListRow createCardRow(CardRow cardRow) {
PresenterSelector presenterSelector = new CardPresenterSelector(getActivity());
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(presenterSelector);
for (Card card : cardRow.getCards()) {
listRowAdapter.add(card);
}
return new ListRow(listRowAdapter);
}
private void setupBackgroundManager() {
mBackgroundManager = BackgroundManager.getInstance(getActivity());
mBackgroundManager.setThemeDrawableResourceId(DEFAULT_BACKGROUND_IMAGE);
mBackgroundManager.attach(getActivity().getWindow());
mBackgroundTarget = new PicassoBackgroundManagerTarget(mBackgroundManager);
mMetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
getView().setBackgroundResource(R.drawable.bg_living_room_wide);
//updateBackgroundImage(R.drawable.bg_living_room_wide);
}
private void setupUIElements() {
setTitle(getString(R.string.browse_title));
setBadgeDrawable(getResources().getDrawable(R.drawable.title_android_tv, null));
setHeadersState(HEADERS_DISABLED);
setHeadersTransitionOnBackEnabled(false);
setBrandColor(getResources().getColor(R.color.fastlane_background));
}
private void setupEventListeners() {
setOnItemViewClickedListener(new ItemViewClickedListener());
setOnItemViewSelectedListener(new ItemViewSelectedListener());
}
protected void updateBackgroundImage(URI uri) {
// Deactivated until we decide whether to load a background image from an URL or resource. @hahnr
if (true) return;
Picasso.with(getActivity()).load(uri.toString())
.resize(mMetrics.widthPixels, mMetrics.heightPixels).centerCrop()
.error(DEFAULT_BACKGROUND_IMAGE).into(mBackgroundTarget);
}
protected void updateBackgroundImage(Drawable drawable) {
mBackgroundManager.setDrawable(drawable);
}
protected void updateBackgroundImage(int resId) {
mBackgroundManager.setDrawable(getResources().getDrawable(resId, null));
}
private void startBackgroundTimer() {
if (null != mBackgroundTimer) {
mBackgroundTimer.cancel();
}
mBackgroundTimer = new Timer();
mBackgroundTimer.schedule(new UpdateBackgroundTask(), BACKGROUND_UPDATE_DELAY);
}
private final class ItemViewClickedListener implements OnItemViewClickedListener {
@Override public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
Card card = (Card) item;
int id = card.getId();
switch (id) {
case 0: {
updateBackgroundImage(new ColorDrawable(
getResources().getColor(R.color.card_examples_background)));
Fragment fragment = new CardExampleFragment();
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.addToBackStack(null).commit();
break;
}
case 2: {
updateBackgroundImage(
getResources().getDrawable(R.drawable.background_canyon, null));
Fragment fragment = new GridExample();
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.addToBackStack(null).commit();
break;
}
case 3: {
updateBackgroundImage(
getResources().getDrawable(R.drawable.background_canyon, null));
Fragment fragment = new DetailViewExampleFragment();
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.addToBackStack(null).commit();
break;
}
case 4: {
updateBackgroundImage(
getResources().getDrawable(R.drawable.background_canyon, null));
Fragment fragment = new VideoConsumptionExampleFragment();
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, new VideoSurfaceFragment(),
VIDEO_SURFACE_FRAGMENT_TAG)
.add(R.id.fragmentContainer, fragment).addToBackStack(null)
.commit();
break;
}
case 5: {
updateBackgroundImage(
getResources().getDrawable(R.drawable.background_sax, null));
Fragment fragment = new MusicConsumptionExampleFragment();
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.addToBackStack(null).commit();
break;
}
case 6: {
// Let's create a new Wizard for a given Movie. The movie can come from any sort
// of data source. To simplify this example we decode it from a JSON source
// which might be loaded from a server in a real world example.
Intent intent = new Intent(getActivity().getBaseContext(),
WizardExampleActivity.class);
// Prepare extras which contains the Movie and will be passed to the Activity
// which is started through the Intent/.
Bundle extras = new Bundle();
String json = Utils.inputStreamToString(
getResources().openRawResource(R.raw.wizard_example));
Movie movie = new Gson().fromJson(json, Movie.class);
extras.putSerializable("movie", movie);
intent.putExtras(extras);
// Finally, start the wizard Activity.
startActivity(intent);
break;
}
case 7: {
Intent intent = new Intent(getActivity().getBaseContext(),
SettingsExampleActivity.class);
startActivity(intent);
break;
}
case 8: {
Intent intent = new Intent(getActivity().getBaseContext(),
DialogExampleActivity.class);
startActivity(intent);
break;
}
default:
break;
}
}
}
private final class ItemViewSelectedListener implements OnItemViewSelectedListener {
@Override public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
}
}
private class UpdateBackgroundTask extends TimerTask {
@Override public void run() {
mHandler.post(new Runnable() {
@Override public void run() {
if (mBackgroundURI != null) {
updateBackgroundImage(mBackgroundURI);
}
}
});
}
}
}

View File

@@ -0,0 +1,446 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Handler;
import android.support.v17.leanback.app.PlaybackControlGlue;
import android.support.v17.leanback.app.PlaybackOverlayFragment;
import android.support.v17.leanback.widget.Action;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
import android.support.v17.leanback.widget.OnItemViewSelectedListener;
import android.support.v17.leanback.widget.PlaybackControlsRow;
import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter;
import android.util.Log;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.View;
import java.io.IOException;
/**
* This glue extends the {@link PlaybackControlGlue} with a {@link MediaPlayer} synchronization. It
* supports 7 actions: <ul> <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.FastForwardAction}</li>
* <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.RewindAction}</li> <li>{@link
* android.support.v17.leanback.widget.PlaybackControlsRow.PlayPauseAction}</li> <li>{@link
* android.support.v17.leanback.widget.PlaybackControlsRow.ShuffleAction}</li> <li>{@link
* android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction}</li> <li>{@link
* android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsDownAction}</li> <li>{@link
* android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsUpAction}</li> </ul>
* <p/>
*/
public abstract class MediaPlayerGlue extends PlaybackControlGlue implements
OnItemViewSelectedListener {
public static final int FAST_FORWARD_REWIND_STEP = 10 * 1000; // in milliseconds
public static final int FAST_FORWARD_REWIND_REPEAT_DELAY = 200; // in milliseconds
private static final String TAG = "MediaPlayerGlue";
protected final PlaybackControlsRow.ThumbsDownAction mThumbsDownAction;
protected final PlaybackControlsRow.ThumbsUpAction mThumbsUpAction;
private final Context mContext;
private final MediaPlayer mPlayer = new MediaPlayer();
private final PlaybackControlsRow.RepeatAction mRepeatAction;
private final PlaybackControlsRow.ShuffleAction mShuffleAction;
private PlaybackControlsRow mControlsRow;
private Runnable mRunnable;
private Handler mHandler = new Handler();
private boolean mPaused = false;
private boolean mInitialized = false; // true when the MediaPlayer is prepared/initialized
private OnMediaFileFinishedPlayingListener mMediaFileFinishedPlayingListener;
private Action mSelectedAction; // the action which is currently selected by the user
private long mLastKeyDownEvent = 0L; // timestamp when the last DPAD_CENTER KEY_DOWN occurred
private MetaData mMetaData;
private Uri mMediaSourceUri = null;
private String mMediaSourcePath = null;
public MediaPlayerGlue(Context context, PlaybackOverlayFragment fragment) {
super(context, fragment, new int[]{1});
mContext = context;
// Instantiate secondary actions
mShuffleAction = new PlaybackControlsRow.ShuffleAction(mContext);
mRepeatAction = new PlaybackControlsRow.RepeatAction(mContext);
mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(mContext);
mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(mContext);
mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
// Setup controls and notify UI about change.
setFadingEnabled(false);
onStateChanged();
// Register selected listener such that we know what action the user currently has focused.
fragment.setOnItemViewSelectedListener(this);
}
/**
* Will reset the {@link MediaPlayer} and the glue such that a new file can be played. You are
* not required to call this method before playing the first file. However you have to call it
* before playing a second one.
*/
public void reset() {
mPaused = mInitialized = false;
mPlayer.reset();
}
public void setOnMediaFileFinishedPlayingListener(OnMediaFileFinishedPlayingListener listener) {
mMediaFileFinishedPlayingListener = listener;
}
/**
* Override this method in case you need to add different secondary actions.
*
* @param secondaryActionsAdapter The adapter you need to add the {@link Action}s to.
*/
protected void addSecondaryActions(ArrayObjectAdapter secondaryActionsAdapter) {
secondaryActionsAdapter.add(mShuffleAction);
secondaryActionsAdapter.add(mRepeatAction);
secondaryActionsAdapter.add(mThumbsDownAction);
secondaryActionsAdapter.add(mThumbsUpAction);
}
/**
* @see MediaPlayer#setDisplay(SurfaceHolder)
*/
public void setDisplay(SurfaceHolder surfaceHolder) {
mPlayer.setDisplay(surfaceHolder);
}
/**
* Use this method to setup the {@link PlaybackControlsRowPresenter}. It'll be called
* <u>after</u> the {@link PlaybackControlsRowPresenter} has been created and the primary and
* secondary actions have been added.
*
* @param presenter The PlaybackControlsRowPresenter used to display the controls.
*/
public void setupControlsRowPresenter(PlaybackControlsRowPresenter presenter) {
// TODO: hahnr@ move into resources
presenter.setProgressColor(Color.parseColor("#feab91"));
presenter.setBackgroundColor(Color.parseColor("#db2a0f"));
}
@Override public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
PlaybackControlsRowPresenter presenter = super.createControlsRowAndPresenter();
mControlsRow = getControlsRow();
// Add secondary actions and change the control row color.
ArrayObjectAdapter secondaryActions = new ArrayObjectAdapter(
new ControlButtonPresenterSelector());
mControlsRow.setSecondaryActionsAdapter(secondaryActions);
addSecondaryActions(secondaryActions);
setupControlsRowPresenter(presenter);
return presenter;
}
@Override public void enableProgressUpdating(final boolean enabled) {
if (!enabled) {
if (mRunnable != null) mHandler.removeCallbacks(mRunnable);
return;
}
mRunnable = new Runnable() {
@Override public void run() {
updateProgress();
Log.d(TAG, "enableProgressUpdating(boolean)");
mHandler.postDelayed(this, getUpdatePeriod());
}
};
mHandler.postDelayed(mRunnable, getUpdatePeriod());
}
@Override public void onActionClicked(Action action) {
// If either 'Shuffle' or 'Repeat' has been clicked we need to make sure the acitons index
// is incremented and the UI updated such that we can display the new state.
super.onActionClicked(action);
if (action instanceof PlaybackControlsRow.ShuffleAction) {
mShuffleAction.nextIndex();
} else if (action instanceof PlaybackControlsRow.RepeatAction) {
mRepeatAction.nextIndex();
} else if (action instanceof PlaybackControlsRow.ThumbsUpAction) {
if (mThumbsUpAction.getIndex() == PlaybackControlsRow.ThumbsAction.SOLID) {
mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
} else {
mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.SOLID);
mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
}
} else if (action instanceof PlaybackControlsRow.ThumbsDownAction) {
if (mThumbsDownAction.getIndex() == PlaybackControlsRow.ThumbsAction.SOLID) {
mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
} else {
mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.SOLID);
mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
}
}
onMetadataChanged();
}
@Override public boolean onKey(View v, int keyCode, KeyEvent event) {
// This method is overridden in order to make implement fast forwarding and rewinding when
// the user keeps the corresponding action pressed.
// We only consume DPAD_CENTER Action_DOWN events on the Fast-Forward and Rewind action and
// only if it has not been pressed in the last X milliseconds.
boolean consume = mSelectedAction instanceof PlaybackControlsRow.RewindAction;
consume = consume || mSelectedAction instanceof PlaybackControlsRow.FastForwardAction;
consume = consume && mInitialized;
consume = consume && event.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER;
consume = consume && event.getAction() == KeyEvent.ACTION_DOWN;
consume = consume && System
.currentTimeMillis() - mLastKeyDownEvent > FAST_FORWARD_REWIND_REPEAT_DELAY;
if (consume) {
mLastKeyDownEvent = System.currentTimeMillis();
int newPosition = getCurrentPosition() + FAST_FORWARD_REWIND_STEP;
if (mSelectedAction instanceof PlaybackControlsRow.RewindAction) {
newPosition = getCurrentPosition() - FAST_FORWARD_REWIND_STEP;
}
// Make sure the new calculated duration is in the range 0 >= X >= MediaDuration
if (newPosition < 0) newPosition = 0;
if (newPosition > getMediaDuration()) newPosition = getMediaDuration();
seekTo(newPosition);
return true;
}
return super.onKey(v, keyCode, event);
}
@Override public boolean hasValidMedia() {
return mMetaData != null;
}
@Override public boolean isMediaPlaying() {
return mPlayer.isPlaying();
}
@Override public CharSequence getMediaTitle() {
return hasValidMedia() ? mMetaData.getTitle() : "N/a";
}
@Override public CharSequence getMediaSubtitle() {
return hasValidMedia() ? mMetaData.getArtist() : "N/a";
}
@Override public int getMediaDuration() {
return mInitialized ? mPlayer.getDuration() : 0;
}
@Override public Drawable getMediaArt() {
return hasValidMedia() ? mMetaData.getCover() : null;
}
@Override public long getSupportedActions() {
return PlaybackControlGlue.ACTION_PLAY_PAUSE | PlaybackControlGlue.ACTION_FAST_FORWARD | PlaybackControlGlue.ACTION_REWIND;
}
@Override public int getCurrentSpeedId() {
// 0 = Pause, 1 = Normal Playback Speed
return mPlayer.isPlaying() ? 1 : 0;
}
@Override public int getCurrentPosition() {
return mInitialized ? mPlayer.getCurrentPosition() : 0;
}
@Override protected void startPlayback(int speed) throws IllegalStateException {
if (mPaused) {
mPlayer.start();
} else if (!isMediaPlaying()) {
reset();
try {
if (mMediaSourceUri != null) mPlayer.setDataSource(getContext(), mMediaSourceUri);
else mPlayer.setDataSource(mMediaSourcePath);
} catch (IOException e) {
throw new RuntimeException(e);
}
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override public void onPrepared(MediaPlayer mp) {
mInitialized = true;
mPaused = false;
mPlayer.start();
onMetadataChanged();
onStateChanged();
updateProgress();
}
});
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override public void onCompletion(MediaPlayer mp) {
if (mInitialized && mMediaFileFinishedPlayingListener != null)
mMediaFileFinishedPlayingListener.onMediaFileFinishedPlaying(mMetaData);
}
});
mPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
@Override public void onBufferingUpdate(MediaPlayer mp, int percent) {
mControlsRow.setBufferedProgress((int) (mp.getDuration() * (percent / 100f)));
}
});
mPlayer.prepareAsync();
onStateChanged();
}
}
@Override protected void pausePlayback() {
if (mPlayer.isPlaying()) {
mPlayer.pause();
mPaused = true;
}
}
@Override protected void skipToNext() {
// Not supported.
}
@Override protected void skipToPrevious() {
// Not supported.
}
/**
* Called whenever the user presses fast-forward/rewind or when the user keeps the corresponding
* action pressed.
*
* @param newPosition The new position of the media track in milliseconds.
*/
protected void seekTo(int newPosition) {
mPlayer.seekTo(newPosition);
}
/**
* @see MediaPlayer#setDataSource(Context, Uri)
*/
public void setMediaSource(Uri uri) {
mMediaSourceUri = uri;
}
/**
* @see MediaPlayer#setDataSource(String)
*/
public void setMediaSource(String path) {
mMediaSourcePath = path;
}
/**
* Call to <code>startPlayback(1)</code>.
*
* @throws IllegalStateException See {@link MediaPlayer} for further information about it's
* different states when setting a data source and preparing it to be played.
*/
public void startPlayback() throws IllegalStateException {
startPlayback(1);
}
/**
* @return Returns <code>true</code> iff 'Shuffle' is <code>ON</code>.
*/
public boolean useShuffle() {
return mShuffleAction.getIndex() == PlaybackControlsRow.ShuffleAction.ON;
}
/**
* @return Returns <code>true</code> iff 'Repeat-One' is <code>ON</code>.
*/
public boolean repeatOne() {
return mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.ONE;
}
/**
* @return Returns <code>true</code> iff 'Repeat-All' is <code>ON</code>.
*/
public boolean repeatAll() {
return mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.ALL;
}
public void setMetaData(MetaData metaData) {
mMetaData = metaData;
onMetadataChanged();
}
/**
* This is a listener implementation for the {@link OnItemViewSelectedListener} of the {@link
* PlaybackOverlayFragment}. This implementation is required in order to detect KEY_DOWN events
* on the {@link android.support.v17.leanback.widget.PlaybackControlsRow.FastForwardAction} and
* {@link android.support.v17.leanback.widget.PlaybackControlsRow.RewindAction}. Thus you should
* <u>NOT</u> set another {@link OnItemViewSelectedListener} on your {@link
* PlaybackOverlayFragment}. Instead, override this method and call its super (this)
* implementation.
*
* @see OnItemViewSelectedListener#onItemSelected(Presenter.ViewHolder, Object,
* RowPresenter.ViewHolder, Row)
*/
@Override public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
if (item instanceof Action) {
mSelectedAction = (Action) item;
} else {
mSelectedAction = null;
}
}
/**
* A listener which will be called whenever a track is finished playing.
*/
public interface OnMediaFileFinishedPlayingListener {
/**
* Called when a track is finished playing.
*
* @param metaData The track's {@link MetaData} which just finished playing.
*/
void onMediaFileFinishedPlaying(MetaData metaData);
}
/**
* Holds the meta data such as track title, artist and cover art. It'll be used by the {@link
* MediaPlayerGlue}.
*/
public static class MetaData {
private String mTitle;
private String mArtist;
private Drawable mCover;
public String getTitle() {
return mTitle;
}
public void setTitle(String title) {
this.mTitle = title;
}
public String getArtist() {
return mArtist;
}
public void setArtist(String artist) {
this.mArtist = artist;
}
public Drawable getCover() {
return mCover;
}
public void setCover(Drawable cover) {
this.mCover = cover;
}
}
}

View File

@@ -0,0 +1,36 @@
package android.support.v17.leanback.supportleanbackshowcase;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
public class Movie implements Serializable {
private static final long serialVersionUID = 133742L;
@SerializedName("title")
private String mTitle = "";
@SerializedName("price_hd")
private String mPriceHd = "n/a";
@SerializedName("price_sd")
private String mPriceSd = "n/a";
@SerializedName("breadcrump")
private String mBreadcrump = "";
public String getTitle() {
return mTitle;
}
public String getBreadcrump() {
return mBreadcrump;
}
public String getPriceHd() {
return mPriceHd;
}
public String getPriceSd() {
return mPriceSd;
}
}

View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.net.Uri;
import android.os.Bundle;
import android.support.v17.leanback.app.PlaybackOverlayFragment;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Song;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.SongList;
import android.support.v17.leanback.widget.Action;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.ClassPresenterSelector;
import android.support.v17.leanback.widget.OnItemViewClickedListener;
import android.support.v17.leanback.widget.PlaybackControlsRow;
import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter;
import android.util.Log;
import com.google.gson.Gson;
import java.util.List;
/**
* This example shows how to play music files and build a simple track list.
*/
public class MusicConsumptionExampleFragment extends PlaybackOverlayFragment implements
OnItemViewClickedListener, Song.OnSongRowClickListener,
MediaPlayerGlue.OnMediaFileFinishedPlayingListener {
private static final String TAG = "MusicConsumptionExampleFragment";
private ArrayObjectAdapter mRowsAdapter;
private MediaPlayerGlue mGlue;
private int mCurrentSongIndex = 0;
private List<Song> mSongList;
private boolean mAdapterNotified = false;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Constants.LOCAL_LOGD) Log.d(TAG, "onCreate");
mGlue = new MediaPlayerGlue(getActivity(), this) {
@Override protected void onRowChanged(PlaybackControlsRow row) {
if (mRowsAdapter == null || mAdapterNotified) return;
//mAdapterNotified = true;
mRowsAdapter.notifyArrayItemRangeChanged(0, 1);
}
};
mGlue.setOnMediaFileFinishedPlayingListener(this);
String json = Utils.inputStreamToString(
getResources().openRawResource(R.raw.music_consumption_example));
mSongList = new Gson().fromJson(json, SongList.class).getSongs();
Song song = mSongList.get(mCurrentSongIndex);
MediaPlayerGlue.MetaData metaData = new MediaPlayerGlue.MetaData();
metaData.setArtist(song.getDescription());
metaData.setTitle(song.getTitle());
metaData.setCover(getResources().getDrawable(song.getImageResource(getActivity()), null));
Uri uri = Utils.getResourceUri(getActivity(), song.getFileResource(getActivity()));
mGlue.setMetaData(metaData);
mGlue.setMediaSource(uri);
setBackgroundType(PlaybackOverlayFragment.BG_LIGHT);
addPlaybackControlsRow();
}
@Override public void onStart() {
super.onStart();
mGlue.enableProgressUpdating(mGlue.hasValidMedia() && mGlue.isMediaPlaying());
}
@Override public void onStop() {
super.onStop();
mGlue.enableProgressUpdating(false);
mGlue.reset();
}
private void addPlaybackControlsRow() {
final PlaybackControlsRowPresenter controlsPresenter = mGlue
.createControlsRowAndPresenter();
ClassPresenterSelector selector = new ClassPresenterSelector();
Song.Presenter songPresenter = new Song.Presenter(getActivity());
songPresenter.setOnClickListener(this);
selector.addClassPresenter(Song.class, songPresenter);
selector.addClassPresenter(TrackListHeader.class,
new TrackListHeader.Presenter(getActivity()));
selector.addClassPresenter(PlaybackControlsRow.class, controlsPresenter);
mRowsAdapter = new ArrayObjectAdapter(selector);
mRowsAdapter.add(mGlue.getControlsRow());
mRowsAdapter.add(new TrackListHeader());
mRowsAdapter.addAll(2, mSongList);
setAdapter(mRowsAdapter);
setOnItemViewClickedListener(this);
}
@Override public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
if (!(item instanceof Action)) return;
mGlue.onActionClicked((Action) item);
}
@Override public void onSongRowClicked(Song song) {
mCurrentSongIndex = mSongList.indexOf(song);
startPlayback();
}
@Override public void onMediaFileFinishedPlaying(MediaPlayerGlue.MetaData song) {
if (mGlue.repeatOne()) {
startPlayback();
return;
}
if (mGlue.useShuffle()) {
mCurrentSongIndex = (int) (Math.random() * mSongList.size());
} else mCurrentSongIndex++;
if (mCurrentSongIndex >= mSongList.size()) {
mCurrentSongIndex = 0;
if (!mGlue.repeatAll()) return;
}
startPlayback();
}
private void startPlayback() {
Song song = mSongList.get(mCurrentSongIndex);
MediaPlayerGlue.MetaData metaData = new MediaPlayerGlue.MetaData();
metaData.setArtist(song.getDescription());
metaData.setTitle(song.getTitle());
metaData.setCover(getResources().getDrawable(song.getImageResource(getActivity()), null));
Uri uri = Utils.getResourceUri(getActivity(), song.getFileResource(getActivity()));
mGlue.setMetaData(metaData);
mGlue.setMediaSource(uri);
mGlue.startPlayback();
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.support.v17.leanback.app.BackgroundManager;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
/**
* Picasso target for updating default_background images
*/
public class PicassoBackgroundManagerTarget implements Target {
private BackgroundManager mBackgroundManager;
public PicassoBackgroundManagerTarget(BackgroundManager backgroundManager) {
this.mBackgroundManager = backgroundManager;
}
@Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
this.mBackgroundManager.setBitmap(bitmap);
}
@Override public void onBitmapFailed(Drawable drawable) {
this.mBackgroundManager.setDrawable(drawable);
}
@Override public void onPrepareLoad(Drawable drawable) {
// Do nothing, default_background manager has its own transitions
}
@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PicassoBackgroundManagerTarget that = (PicassoBackgroundManagerTarget) o;
if (!mBackgroundManager.equals(that.mBackgroundManager)) return false;
return true;
}
@Override public int hashCode() {
return mBackgroundManager.hashCode();
}
}

View File

@@ -0,0 +1,22 @@
package android.support.v17.leanback.supportleanbackshowcase;
import android.util.SparseArray;
import android.view.View;
/**
* ResourceCache allows retrieving children from a given view and caches the resulting views in
* order to prevent future lookups.
*/
public class ResourceCache {
private final SparseArray<View> mCachedViews = new SparseArray<View>();
public <ViewType extends View> ViewType getViewById(View view, int resId) {
View child = mCachedViews.get(resId, null);
if (child == null) {
child = view.findViewById(resId);
mCachedViews.put(resId, child);
}
return (ViewType) child;
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
/*
* TODO: Javadoc
*/
public class SettingsExampleActivity extends Activity {
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings_example);
}
}

View File

@@ -0,0 +1,87 @@
package android.support.v17.leanback.supportleanbackshowcase;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.support.v14.preference.PreferenceFragment;
import android.support.v17.preference.LeanbackPreferenceDialogFragment;
import android.support.v17.preference.LeanbackPreferenceFragment;
import android.support.v17.preference.LeanbackSettingsFragment;
import android.support.v7.preference.DialogPreference;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.util.Log;
import java.util.Stack;
public class SettingsExampleFragment extends LeanbackSettingsFragment implements DialogPreference.TargetFragment {
public static final String TAG = "SettingsExampleFragment";
private final Stack<Fragment> fragments = new Stack<Fragment>();
@Override
public void onPreferenceStartInitialScreen() {
startPreferenceFragment(buildPreferenceFragment(R.xml.prefs, null));
}
@Override
public boolean onPreferenceStartFragment(PreferenceFragment preferenceFragment,
Preference preference) {
return false;
}
@Override
public boolean onPreferenceStartScreen(PreferenceFragment preferenceFragment,
PreferenceScreen preferenceScreen) {
PreferenceFragment frag = buildPreferenceFragment(R.xml.prefs, preferenceScreen.getKey());
if ("prefs_wifi_screen_key".equals(preferenceScreen.getKey())) {
ListPreference pref = (ListPreference)preferenceScreen.findPreference("prefs_wifi_key");
pref.setEntries(new String[] {"Wi-Fi Network 01"});
pref.setEntryValues(new String[] {"01"});
if (Constants.LOCAL_LOGD) Log.d(TAG, "pref: " + pref);
}
startPreferenceFragment(frag);
return true;
}
@Override
public Preference findPreference(CharSequence prefKey) {
return ((PreferenceFragment) fragments.peek()).findPreference(prefKey);
}
private PreferenceFragment buildPreferenceFragment(int preferenceResId, String root) {
PreferenceFragment fragment = new PrefFragment();
Bundle args = new Bundle();
args.putInt("preferenceResource", preferenceResId);
args.putString("root", root);
fragment.setArguments(args);
return fragment;
}
private class PrefFragment extends LeanbackPreferenceFragment {
@Override
public void onCreatePreferences(Bundle bundle, String s) {
String root = getArguments().getString("root", null);
int prefResId = getArguments().getInt("preferenceResource");
if (root == null) {
addPreferencesFromResource(prefResId);
} else {
setPreferencesFromResource(prefResId, root);
}
}
@Override
public void onAttach(Context context) {
fragments.push(this);
super.onAttach(context);
}
@Override
public void onDetach() {
fragments.pop();
super.onDetach();
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.CardRow;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.PresenterSelector;
/**
* This {@link PresenterSelector} will return a {@link ListRowPresenter} which has shadow support
* enabled or not depending on {@link CardRow#useShadow()} for a given row.
*/
public class ShadowRowPresenterSelector extends PresenterSelector {
private ListRowPresenter mShadowEnabledRowPresenter = new ListRowPresenter();
private ListRowPresenter mShadowDisabledRowPresenter = new ListRowPresenter();
public ShadowRowPresenterSelector() {
mShadowDisabledRowPresenter.setShadowEnabled(false);
}
@Override public Presenter getPresenter(Object item) {
if (!(item instanceof CardListRow)) return mShadowDisabledRowPresenter;
CardListRow listRow = (CardListRow) item;
CardRow row = listRow.getCardRow();
if (row.useShadow()) return mShadowEnabledRowPresenter;
return mShadowDisabledRowPresenter;
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.support.v17.leanback.widget.Row;
public class SongListRow extends Row {}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.content.Context;
import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class TrackListHeader extends Row {
public static class Presenter extends RowPresenter {
private final Context mContext;
public Presenter(Context context) {
mContext = context;
setHeaderPresenter(null);
}
@Override public boolean isUsingDefaultSelectEffect() {
return false;
}
@Override protected ViewHolder createRowViewHolder(ViewGroup parent) {
View view = LayoutInflater.from(mContext).inflate(R.layout.row_track_list_header, parent, false);
view.setFocusable(false);
view.setFocusableInTouchMode(false);
return new ViewHolder(view);
}
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.RequestCreator;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
/**
* A collection of utility methods, all static.
*/
public class Utils {
public static int convertDpToPixel(Context ctx, int dp) {
float density = ctx.getResources().getDisplayMetrics().density;
return Math.round((float) dp * density);
}
/**
* Will read the content from a given {@link InputStream} and return it as a {@link String}.
*
* @param inputStream The {@link InputStream} which should be read.
* @return Returns <code>null</code> if the the {@link InputStream} could not be read. Else
* returns the content of the {@link InputStream} as {@link String}.
*/
public static String inputStreamToString(InputStream inputStream) {
try {
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes, 0, bytes.length);
String json = new String(bytes);
return json;
} catch (IOException e) {
return null;
}
}
/**
* The method uses {@link Picasso} to fetch an image from a given url, resize it (if required)
* and display it inside an {@link ImageView}.
*
* @param context Context which is used to create a {@link Picasso} instance.
* @param uri The {@link URI} to fetch the image from.
* @param target The {@link ImageView} which shall display the image.
* @param resizeWidthInDp The target width of the image. Pass <code>-1</code> if you don't want
* to resize the image.
* @param resizeHeightInDp The target height of the image. Pass <code>-1</code> if you don't
* want to resize the image.
* @param centerCrop Centers and scales an image to fit the requested bounds.
* @param errorDrawable A drawable which will be shown in case the image could not be fetched
* from the server.
* @see {@link Picasso#with(Context)}
* @see {@link RequestCreator#resize(int, int)}
* @see {@link RequestCreator#centerCrop()}
* @see {@link RequestCreator#error(Drawable)}
*/
public static void loadImageFromUri(Context context, URI uri, ImageView target,
int resizeWidthInDp, int resizeHeightInDp,
boolean centerCrop, Drawable errorDrawable) {
if (uri == null) return;
RequestCreator builder = Picasso.with(context).load(uri.toString());
if (resizeHeightInDp != -1 && resizeWidthInDp != -1)
builder.resize(Utils.convertDpToPixel(context, resizeWidthInDp),
Utils.convertDpToPixel(context, resizeHeightInDp));
if (centerCrop) builder.centerCrop();
builder.error(errorDrawable).into(target);
}
public static Uri getResourceUri(Context context, int resID) {
return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
context.getResources().getResourcePackageName(resID) + '/' +
context.getResources().getResourceTypeName(resID) + '/' +
context.getResources().getResourceEntryName(resID));
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.app.Fragment;
import android.os.Bundle;
import android.support.v17.leanback.app.PlaybackOverlayFragment;
import android.support.v17.leanback.widget.Action;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.OnItemViewClickedListener;
import android.support.v17.leanback.widget.PlaybackControlsRow;
import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class VideoConsumptionExampleFragment extends PlaybackOverlayFragment implements
OnItemViewClickedListener, MediaPlayerGlue.OnMediaFileFinishedPlayingListener {
private static final String URL = "http://techslides.com/demos/sample-videos/small.mp4";
private static final String TAG = "VideoConsumptionExampleFragment";
private ArrayObjectAdapter mRowsAdapter;
private MediaPlayerGlue mGlue;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Constants.LOCAL_LOGD) Log.d(TAG, "onCreate");
mGlue = new VideoMediaPlayerGlue(getActivity(), this) {
@Override protected void onRowChanged(PlaybackControlsRow row) {
if (mRowsAdapter == null) return;
mRowsAdapter.notifyArrayItemRangeChanged(0, 1);
}
};
mGlue.setOnMediaFileFinishedPlayingListener(this);
MediaPlayerGlue.MetaData metaData = new MediaPlayerGlue.MetaData();
metaData.setArtist("A Googler");
metaData.setTitle("Diving with Sharks");
mGlue.setMetaData(metaData);
mGlue.setMediaSource(URL);
Fragment videoSurfaceFragment = getFragmentManager()
.findFragmentByTag(MainFragment.VIDEO_SURFACE_FRAGMENT_TAG);
SurfaceView surface = (SurfaceView) videoSurfaceFragment.getView();
surface.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG, "surfaceCreated(SurfaceHolder)");
mGlue.setDisplay(holder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// Nothing to do
}
@Override public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "surfaceDestroyed(SurfaceHolder)");
}
});
setBackgroundType(PlaybackOverlayFragment.BG_LIGHT);
addPlaybackControlsRow();
}
@Override public void onStart() {
super.onStart();
mGlue.enableProgressUpdating(mGlue.hasValidMedia() && mGlue.isMediaPlaying());
}
@Override public void onStop() {
super.onStop();
mGlue.enableProgressUpdating(false);
mGlue.reset();
}
private void addPlaybackControlsRow() {
final PlaybackControlsRowPresenter controlsPresenter = mGlue
.createControlsRowAndPresenter();
mRowsAdapter = new ArrayObjectAdapter(controlsPresenter);
mRowsAdapter.add(mGlue.getControlsRow());
setAdapter(mRowsAdapter);
setOnItemViewClickedListener(this);
}
@Override public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
if (!(item instanceof Action)) return;
mGlue.onActionClicked((Action) item);
}
@Override public void onMediaFileFinishedPlaying(MediaPlayerGlue.MetaData metaData) {
mGlue.startPlayback();
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.content.Context;
import android.graphics.Color;
import android.support.v17.leanback.app.PlaybackOverlayFragment;
import android.support.v17.leanback.widget.Action;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.PlaybackControlsRow;
import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
public abstract class VideoMediaPlayerGlue extends MediaPlayerGlue {
private final PlaybackControlsRow.ClosedCaptioningAction mClosedCaptioningAction;
public VideoMediaPlayerGlue(Context context, PlaybackOverlayFragment fragment) {
super(context, fragment);
// Instantiate secondary actions
mClosedCaptioningAction = new PlaybackControlsRow.ClosedCaptioningAction(context);
setFadingEnabled(true);
}
@Override protected void addSecondaryActions(ArrayObjectAdapter secondaryActionsAdapter) {
secondaryActionsAdapter.add(mClosedCaptioningAction);
secondaryActionsAdapter.add(mThumbsDownAction);
secondaryActionsAdapter.add(mThumbsUpAction);
}
@Override public void onActionClicked(Action action) {
super.onActionClicked(action);
if (action == mClosedCaptioningAction) {
mClosedCaptioningAction.nextIndex();
}
}
public void setupControlsRowPresenter(PlaybackControlsRowPresenter presenter) {
// TODO: hahnr@ move into resources
presenter.setProgressColor(Color.parseColor("#EEFF41"));
presenter.setBackgroundColor(Color.parseColor("#007236"));
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class VideoSurfaceFragment extends Fragment {
private static final String TAG = "VideoSurfaceFragment";
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.video_surface_fragment, null);
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v17.leanback.app.GuidedStepFragment;
import android.support.v17.leanback.widget.GuidanceStylist;
import android.support.v17.leanback.widget.GuidedAction;
import android.support.v17.leanback.widget.GuidedActionsStylist;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
/**
* TODO: JavaDoc
*/
public class WizardExample1stStepFragment extends WizardExampleBaseStepFragment {
private static final int ACTION_ID_BUY_HD = 1;
private static final int ACTION_ID_BUY_SD = ACTION_ID_BUY_HD + 1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWizardActivity().setStep(1);
}
@NonNull
@Override
public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(mMovie.getTitle(),
getString(R.string.wizard_example_choose_rent_options),
mMovie.getBreadcrump(), null);
return guidance;
}
@Override
public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
GuidedAction action = new GuidedAction.Builder()
.id(ACTION_ID_BUY_HD)
.title(getString(R.string.wizard_example_rent_hd))
.description(mMovie.getPriceHd() + " " +
getString(R.string.wizard_example_watch_hd))
.build();
actions.add(action);
action = new GuidedAction.Builder()
.id(ACTION_ID_BUY_SD)
.title(getString(R.string.wizard_example_rent_sd))
.description(mMovie.getPriceSd() + " " +
getString(R.string.wizard_example_watch_sd))
.build();
actions.add(action);
}
@Override
public void onGuidedActionClicked(GuidedAction action) {
GuidedStepFragment fragment = new WizardExample2ndStepFragment();
Bundle args = getArguments(); // Reuse the same arguments this fragment was given.
args.putBoolean("hd", ACTION_ID_BUY_HD == action.getId());
fragment.setArguments(args);
add(getFragmentManager(), fragment);
}
}

View File

@@ -0,0 +1,94 @@
package android.support.v17.leanback.supportleanbackshowcase;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v17.leanback.app.GuidedStepFragment;
import android.support.v17.leanback.widget.GuidanceStylist;
import android.support.v17.leanback.widget.GuidedAction;
import android.support.v17.leanback.widget.GuidedActionsStylist;
import android.widget.Toast;
import java.util.List;
/**
* TODO: JavaDoc
*/
public class WizardExample2ndStepFragment extends WizardExampleBaseStepFragment {
private static final int ACTION_ID_CONFIRM = 1;
private static final int ACTION_ID_PAYMENT_METHOD = ACTION_ID_CONFIRM + 1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWizardActivity().setStep(2);
}
@NonNull
@Override
public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(mMovie.getTitle(),
getString(R.string.wizard_example_rental_period),
mMovie.getBreadcrump(), null);
return guidance;
}
@Override
public GuidedActionsStylist onCreateActionsStylist() {
GuidedActionsStylist stylist = new GuidedActionsStylist() {
@Override
public void onBindViewHolder(ViewHolder vh, GuidedAction action) {
super.onBindViewHolder(vh, action);
if (ACTION_ID_CONFIRM == action.getId()) {
Drawable background = getResources().getDrawable(
R.drawable.wizard_important_action_item_background, null);
vh.view.setBackground(background);
vh.getTitleView().setTextColor(Color.parseColor("#666666"));
vh.getDescriptionView().setTextColor(Color.parseColor("#666666"));
} else {
vh.view.setBackground(null);
vh.getTitleView().setTextColor(getResources().getColor(android.support.v17.leanback.R.color.lb_guidedactions_item_unselected_text_color,
getActivity().getTheme()));
vh.getDescriptionView().setTextColor(getResources().getColor(android.support.v17.leanback.R.color.lb_guidedactions_item_unselected_text_color,
getActivity().getTheme()));
}
}
};
return stylist;
}
@Override
public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
boolean rentHighDefinition = getArguments().getBoolean("hd");
GuidedAction action = new GuidedAction.Builder()
.id(ACTION_ID_CONFIRM)
.title(getString(R.string.wizard_example_rent))
.description(rentHighDefinition ? mMovie.getPriceHd() : mMovie.getPriceSd())
.build();
actions.add(action);
action = new GuidedAction.Builder()
.id(ACTION_ID_PAYMENT_METHOD)
.title(getString(R.string.wizard_example_payment_method))
.description("Visa - 1234 Balance $60.00")
.build();
actions.add(action);
}
@Override
public void onGuidedActionClicked(GuidedAction action) {
if (ACTION_ID_PAYMENT_METHOD == action.getId()) {
Toast.makeText(getActivity(),
getString(R.string.wizard_example_toast_payment_method_clicked),
Toast.LENGTH_SHORT).show();
} else {
GuidedStepFragment fragment = new WizardExample3rdStepFragment();
fragment.setArguments(getArguments());
add(getFragmentManager(), fragment);
}
}
}

View File

@@ -0,0 +1,92 @@
package android.support.v17.leanback.supportleanbackshowcase;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v17.leanback.app.GuidedStepFragment;
import android.support.v17.leanback.widget.GuidanceStylist;
import android.support.v17.leanback.widget.GuidedAction;
import android.support.v17.leanback.widget.GuidedActionsStylist;
import android.util.Log;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
/**
* TODO: JavaDoc
*/
public class WizardExample3rdStepFragment extends WizardExampleBaseStepFragment {
private static final int ACTION_ID_PROCESSING = 1;
private final Handler mFakeHttpHandler = new Handler();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWizardActivity().setStep(3);
}
@Override
public void onStart() {
super.onStart();
// Fake Http call by creating some sort of delay.
mFakeHttpHandler.postDelayed(fakeHttpRequestRunnable, 4000L);
}
@Override
public GuidedActionsStylist onCreateActionsStylist() {
GuidedActionsStylist stylist = new GuidedActionsStylist() {
@Override
public int onProvideItemLayoutId() {
return R.layout.wizard_progress_action_item;
}
@Override
public int onProvideLayoutId() {
return R.layout.wizard_progress_action_container;
}
};
return stylist;
}
@Override
public void onStop() {
super.onStop();
// Make sure to cancel the execution of the Runnable in case the fragment is stopped.
mFakeHttpHandler.removeCallbacks(fakeHttpRequestRunnable);
}
@NonNull
@Override
public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(mMovie.getTitle(),
"Just a second...",
mMovie.getBreadcrump(), null);
return guidance;
}
@Override
public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
GuidedAction action = new GuidedAction.Builder()
.id(ACTION_ID_PROCESSING)
.title(getString(R.string.wizard_example_processing))
.infoOnly(true)
.build();
actions.add(action);
}
private final Runnable fakeHttpRequestRunnable = new Runnable() {
@Override
public void run() {
GuidedStepFragment fragment = new WizardExample4thStepFragment();
fragment.setArguments(getArguments());
add(getFragmentManager(), fragment);
}
};
}

View File

@@ -0,0 +1,57 @@
package android.support.v17.leanback.supportleanbackshowcase;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v17.leanback.app.GuidedStepFragment;
import android.support.v17.leanback.widget.GuidanceStylist;
import android.support.v17.leanback.widget.GuidedAction;
import android.widget.Toast;
import java.util.List;
/**
* TODO: JavaDoc
*/
public class WizardExample4thStepFragment extends WizardExampleBaseStepFragment {
private static final int ACTION_ID_WATCH = 1;
private static final int ACTION_ID_LATER = ACTION_ID_WATCH + 1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWizardActivity().setStep(4);
}
@NonNull
@Override
public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(mMovie.getTitle(),
getString(R.string.wizard_example_rental_period),
mMovie.getBreadcrump(), null);
return guidance;
}
@Override
public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
GuidedAction action = new GuidedAction.Builder()
.id(ACTION_ID_WATCH)
.title(getString(R.string.wizard_example_watch_now))
.build();
actions.add(action);
action = new GuidedAction.Builder()
.id(ACTION_ID_LATER)
.title(getString(R.string.wizard_example_later))
.build();
actions.add(action);
}
@Override
public void onGuidedActionClicked(GuidedAction action) {
if (ACTION_ID_WATCH == action.getId()) {
Toast.makeText(getActivity(), getString(R.string.wizard_example_watch_now_clicked),
Toast.LENGTH_SHORT).show();
}
getActivity().finish();
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.v17.leanback.app.BackgroundManager;
import android.support.v17.leanback.app.GuidedStepFragment;
/**
* TODO: Javadoc
*/
public class WizardExampleActivity extends Activity {
// When the user 'bought' the product and presses back, we don't want to show the 'Processing..'
// screen again, instead we want to go back to the very first step or close the wizard. Thus, we
// have to save the current step of the wizard and make it accessible to it's children.
private int mStep = 0;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setBackgroundDrawableResource(R.drawable.wizard_background_blackned);
// Recover old step state.
if (savedInstanceState != null) {
mStep = savedInstanceState.getInt("step");
}
GuidedStepFragment fragment = new WizardExample1stStepFragment();
fragment.setArguments(getIntent().getExtras()); // Delegate Movie to first step.
GuidedStepFragment.add(getFragmentManager(), fragment);
}
public int getStep() {
return mStep;
}
public void setStep(int step) {
mStep = step;
}
@Override
public void onBackPressed() {
if (4 == getStep()) {
// The user 'bought' the product. When he presses 'Back' the Wizard will be closed and
// he will not be send back to 'Processing Payment...'-Screen.
finish();
} else super.onBackPressed();
}
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
// Save current step persitently.
outPersistentState.putInt("step", mStep);
super.onSaveInstanceState(outState, outPersistentState);
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v17.leanback.app.GuidedStepFragment;
import android.support.v17.leanback.widget.GuidanceStylist;
import android.support.v17.leanback.widget.GuidedAction;
import java.util.List;
/**
* TODO: JavaDoc
*/
public class WizardExampleBaseStepFragment extends GuidedStepFragment {
protected Movie mMovie;
@Override
public int onProvideTheme() {
return R.style.Theme_Example_LeanbackWizard;
}
WizardExampleActivity getWizardActivity() {
if (!(getActivity() instanceof WizardExampleActivity)) {
throw new IllegalStateException(WizardExampleActivity.class.getName() + " expected.");
}
return (WizardExampleActivity)getActivity();
}
@Override
public void onCreate(Bundle savedInstanceState) {
mMovie = (Movie)getArguments().getSerializable("movie");
super.onCreate(savedInstanceState);
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.models;
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import com.google.gson.annotations.SerializedName;
import java.net.URI;
import java.net.URISyntaxException;
/**
* This is a generic example of a custom data object, containing info we might want to keep with
* each card on the home screen
*/
public class Card {
@SerializedName("title") private String mTitle = "";
@SerializedName("description") private String mDescription = "";
@SerializedName("extraText") private String mExtraText = "";
@SerializedName("imageUrl") private String mImageUrl;
@SerializedName("footerColor") private String mFooterColor = null;
@SerializedName("selectedColor") private String mSelectedColor = null;
@SerializedName("localImageResource") private String mLocalImageResource = null;
@SerializedName("footerIconLocalImageResource") private String mFooterResource = null;
@SerializedName("type") private Card.Type mType;
@SerializedName("id") private int mId;
@SerializedName("width") private int mWidth;
@SerializedName("height") private int mHeight;
public String getTitle() {
return mTitle;
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
public int getId() {
return mId;
}
public Card.Type getType() {
return mType;
}
public String getDescription() {
return mDescription;
}
public String getExtraText() {
return mExtraText;
}
public int getFooterColor() {
if (mFooterColor == null) return -1;
return Color.parseColor(mFooterColor);
}
public int getSelectedColor() {
if (mSelectedColor == null) return -1;
return Color.parseColor(mSelectedColor);
}
public String getImageUrl() {
return mImageUrl;
}
public URI getImageURI() {
if (getImageUrl() == null) return null;
try {
return new URI(getImageUrl());
} catch (URISyntaxException e) {
Log.d("URI exception: ", getImageUrl());
return null;
}
}
public int getLocalImageResourceId(Context context) {
return context.getResources().getIdentifier(getLocalImageResourceName(), "drawable",
context.getPackageName());
}
public String getLocalImageResourceName() {
return mLocalImageResource;
}
public String getFooterLocalImageResourceName() {
return mFooterResource;
}
public enum Type {
THIN,
THIN_RATING,
ICON,
SQUARE_BIG,
SQUARE,
WIDE_SHORT,
SQUARE_SMALL,
DEFAULT,
SIDE_INFO,
SIDE_INFO_TEST_1,
TEXT,
CHARACTER,
GRID_SQUARE
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.models;
import com.google.gson.annotations.SerializedName;
import java.util.List;
/**
* This class represents a row of cards. In a real world application you might want to store more
* data than in this example.
*/
public class CardRow {
// Used to determine whether the row shall use shadows when displaying its cards or not.
@SerializedName("shadow") private boolean mShadow = true;
@SerializedName("title") private String mTitle;
@SerializedName("cards") private List<Card> mCards;
public String getTitle() {
return mTitle;
}
public boolean useShadow() {
return mShadow;
}
public List<Card> getCards() {
return mCards;
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.models;
import android.content.Context;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.google.gson.annotations.SerializedName;
public class Song extends Row {
@SerializedName("title") private String mTitle = "";
@SerializedName("description") private String mDescription = "";
@SerializedName("text") private String mText = "";
@SerializedName("image") private String mImage = null;
@SerializedName("file") private String mFile = null;
@SerializedName("duration") private String mDuration = null;
@SerializedName("number") private int mNumber = 0;
public String getDuration() {
return mDuration;
}
public int getNumber() {
return mNumber;
}
public String getText() {
return mText;
}
public String getDescription() {
return mDescription;
}
public String getTitle() {
return mTitle;
}
public int getFileResource(Context context) {
return context.getResources()
.getIdentifier(mFile, "raw", context.getPackageName());
}
public int getImageResource(Context context) {
return context.getResources()
.getIdentifier(mImage, "drawable", context.getPackageName());
}
public interface OnSongRowClickListener {
void onSongRowClicked(Song song);
}
public static class Presenter extends RowPresenter implements View.OnClickListener {
private final Context mContext;
private OnSongRowClickListener mClickListener;
public Presenter(Context context) {
mContext = context;
setHeaderPresenter(null);
}
public void setOnClickListener(OnSongRowClickListener listener) {
mClickListener = listener;
}
@Override protected ViewHolder createRowViewHolder(ViewGroup parent) {
View view = LayoutInflater.from(mContext).inflate(R.layout.row_song, parent, false);
view.findViewById(R.id.rowContainer).setOnClickListener(this);
return new ViewHolder(view);
}
@Override public boolean isUsingDefaultSelectEffect() {
return false;
}
@Override protected void onBindRowViewHolder(ViewHolder vh, Object item) {
super.onBindRowViewHolder(vh, item);
Song song = (Song) item;
((TextView) vh.view.findViewById(R.id.trackNumber)).setText("" + song.getNumber());
((TextView) vh.view.findViewById(R.id.trackDuration)).setText(song.getDuration());
String text = song.getTitle() + " / " + song.getDescription();
((TextView) vh.view.findViewById(R.id.trackName)).setText(text);
vh.view.findViewById(R.id.rowContainer).setTag(item);
}
@Override public void onClick(View v) {
if (mClickListener == null) return;
mClickListener.onSongRowClicked((Song) v.getTag());
}
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.models;
import com.google.gson.annotations.SerializedName;
import java.util.List;
/**
*/
public class SongList {
@SerializedName("songs") private List<Song> mSongs;
public List<Song> getSongs() {
return mSongs;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.presenters;
import android.content.Context;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.views.BaseCardViewEx;
import android.support.v17.leanback.widget.BaseCardView;
import android.support.v17.leanback.widget.Presenter;
import android.view.ViewGroup;
/**
* This abstract, generic class will create and manage the
* ViewHolder and will provide typed Presenter callbacks such that you do not have to perform casts
* on your own.
*
* @param <CardViewType> The cards' View Type.
*/
public abstract class AbstractCardPresenter<CardViewType extends BaseCardView> extends Presenter {
private static final String TAG = "AbstractCardPresenter";
private final Context mContext;
/**
* @param context The current context.
*/
public AbstractCardPresenter(Context context) {
mContext = context;
}
public Context getContext() {
return mContext;
}
@Override public final ViewHolder onCreateViewHolder(ViewGroup parent) {
CardViewType cardView = onCreateView();
return new ViewHolder(cardView);
}
@Override public final void onBindViewHolder(ViewHolder viewHolder, Object item) {
Card card = (Card) item;
onBindViewHolder(card, (CardViewType) viewHolder.view);
}
@Override public final void onUnbindViewHolder(ViewHolder viewHolder) {
onUnbindViewHolder((CardViewType) viewHolder.view);
}
public void onUnbindViewHolder(CardViewType cardView) {
// Nothing to clean up. Override if necessary.
}
/**
* Invoked when a new view is created.
*
* @return Returns the newly created view.
*/
protected abstract CardViewType onCreateView();
/**
* Implement this method to update your card's view with the data bound to it.
*
* @param card The model containing the data for the card.
* @param cardView The view the card is bound to.
* @see Presenter#onBindViewHolder(Presenter.ViewHolder, Object)
*/
public abstract void onBindViewHolder(Card card, CardViewType cardView);
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.presenters;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.supportleanbackshowcase.Utils;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.views.FooterLayoutCardView;
import android.support.v17.leanback.widget.Presenter;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;
/**
* A custom and abstract card {@link Presenter} which allows to create a card consisting of an image
* and a custom footer. The footer is passed as a layout and will be inflated automatically.
*/
public abstract class AbstractFooterCardPresenter extends
AbstractCardPresenter<FooterLayoutCardView> {
private static final String TAG = "AbstractFooterCardPresenter";
private final Drawable mLoadingErrorDrawable;
private final int mFooterLayoutd;
private final int mImageWidth;
private final int mImageHeight;
/**
* @param context Used to retrieve default values such as error drawables.
* @param footerLayoutId The layout which represents the footer.
* @param imageWidth The width of the card's main image. This width defines the card's width
* too.
* @param imageHeight The height of the card's main image. The card's height will be
* <code>imageHeight + footerHeight</code>
*/
public AbstractFooterCardPresenter(Context context, int footerLayoutId, int imageWidth,
int imageHeight) {
super(context);
mFooterLayoutd = footerLayoutId;
mImageWidth = imageWidth;
mImageHeight = imageHeight;
// In case the image could not be fetched from the server we want to make sure something is show. In this case, a solid color.
int color = context.getResources().getColor(R.color.loading_error_card_background);
mLoadingErrorDrawable = new ColorDrawable(color);
}
@Override public final FooterLayoutCardView onCreateView() {
FooterLayoutCardView cardView = new FooterLayoutCardView(getContext(), mFooterLayoutd,
mImageWidth, mImageHeight);
onViewCreated(cardView);
return cardView;
}
@Override public void onBindViewHolder(Card card, FooterLayoutCardView cardView) {
// Load the card's image. This can be either an image on a remote server or a local one stored in the resources.
ImageView imageView = cardView.getImageView();
if (card.getImageURI() != null) {
Utils.loadImageFromUri(getContext(), card.getImageURI(), imageView, mImageWidth,
mImageHeight, true, mLoadingErrorDrawable);
} else if (card.getLocalImageResourceName() != null) {
int resourceId = getContext().getResources()
.getIdentifier(card.getLocalImageResourceName(),
"drawable", getContext().getPackageName());
Picasso.with(getContext()).load(resourceId).resize(mImageWidth, mImageHeight)
.centerCrop().into(imageView);
}
}
/**
* Override this method to react to creations of new card views.
*
* @param cardView The view which has been created.
* @see Presenter#onCreateViewHolder(ViewGroup)
*/
public void onViewCreated(FooterLayoutCardView cardView) {
// Nothing to clean up. Override if necessary.
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.presenters;
import android.content.Context;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.PresenterSelector;
import java.util.HashMap;
/**
* This PresenterSelector will decide what Presenter to use depending on a given card's type.
* <p/>
* TODO: leanbackteam@ Discuss whether leanback's PresenterSelector should be renamed to
* AbstractPresenterSelector.
*/
public class CardPresenterSelector extends PresenterSelector {
private final Context mContext;
private final HashMap<Card.Type, Presenter> presenters = new HashMap<Card.Type, Presenter>();
public CardPresenterSelector(Context context) {
mContext = context;
}
@Override public Presenter getPresenter(Object item) {
if (!(item instanceof Card)) throw new RuntimeException(
String.format("The PresenterSelector only supports data items of type '%s'",
Card.class.getName()));
Card card = (Card) item;
Presenter presenter = presenters.get(card.getType());
if (presenter == null) switch (card.getType()) {
case SQUARE:
presenter = new SingleLineCardPresenter(mContext);
break;
case THIN_RATING:
presenter = new MovieRatingCardPresenter(mContext);
break;
case SIDE_INFO:
presenter = new SideInfoCardPresenter(mContext);
break;
case SIDE_INFO_TEST_1:
presenter = new LauncherCardPresenter(mContext);
break;
case TEXT:
presenter = new TextCardPresenter(mContext);
break;
case ICON:
presenter = new IconCardPresenter(mContext);
break;
case CHARACTER:
presenter = new CharacterCardPresenter(mContext);
break;
case THIN: {
int width = (int) mContext.getResources()
.getDimension(R.dimen.thin_image_card_width);
int height = (int) mContext.getResources()
.getDimension(R.dimen.thin_image_card_height);
presenter = new ImageCardViewPresenter1(mContext, width, height);
}
break;
case SQUARE_BIG: {
int width = (int) mContext.getResources()
.getDimension(R.dimen.big_square_image_card_width);
int height = (int) mContext.getResources()
.getDimension(R.dimen.big_square_image_card_height);
presenter = new ImageCardViewPresenter(mContext, width, height);
}
break;
case GRID_SQUARE: {
int width = (int) mContext.getResources().getDimension(R.dimen.grid_card_width);
int height = (int) mContext.getResources().getDimension(R.dimen.grid_card_height);
presenter = new ImageCardViewPresenter(mContext, width, height);
}
break;
case WIDE_SHORT: {
presenter = new GameBannerCardPresenter(mContext);
}
break;
default: {
int width = (int) mContext.getResources()
.getDimension(R.dimen.default_image_card_width);
int height = (int) mContext.getResources()
.getDimension(R.dimen.default_image_card_height);
presenter = new ImageCardViewPresenter(mContext, width, height);
}
break;
}
presenters.put(card.getType(), presenter);
return presenter;
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.presenters;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v17.leanback.supportleanbackshowcase.Constants;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.views.BaseCardViewEx;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
/**
* This Presenter is used to display the characters card row in the DetailView examples.
*/
public class CharacterCardPresenter extends AbstractCardPresenter<BaseCardViewEx> {
private static final String TAG = "CharacterCardPresenter";
private final SparseArray<RoundedBitmapDrawable> mImageCache = new SparseArray<RoundedBitmapDrawable>();
public CharacterCardPresenter(Context context) {
super(context);
}
@Override protected BaseCardViewEx onCreateView() {
final BaseCardViewEx cardView = new BaseCardViewEx(getContext());
LayoutInflater.from(getContext()).inflate(R.layout.character_card, cardView);
cardView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override public void onFocusChange(View v, boolean hasFocus) {
if (Constants.LOCAL_LOGD) Log.d(TAG, "onFocusChanged(" + hasFocus + ")");
ImageView mainImage = cardView.getViewById(R.id.main_image);
View container = cardView.getViewById(R.id.container);
if (hasFocus) {
container.setBackgroundResource(R.drawable.character_focused);
mainImage.setBackgroundResource(R.drawable.character_focused);
} else {
container.setBackgroundResource(R.drawable.character_not_focused_padding);
mainImage.setBackgroundResource(R.drawable.character_not_focused);
}
}
});
return cardView;
}
@Override public void onBindViewHolder(Card card, BaseCardViewEx cardView) {
if (Constants.LOCAL_LOGD) Log.d(TAG, "onBindViewHolder");
TextView primaryText = cardView.getViewById(R.id.primary_text);
final ImageView imageView = cardView.getViewById(R.id.main_image);
primaryText.setText(card.getTitle());
if (card.getLocalImageResourceName() != null) {
int resourceId = card.getLocalImageResourceId(getContext());
RoundedBitmapDrawable drawable = mImageCache.get(resourceId, null);
if (drawable == null) {
Bitmap bitmap = BitmapFactory
.decodeResource(getContext().getResources(), resourceId);
drawable = RoundedBitmapDrawableFactory.create(getContext().getResources(), bitmap);
drawable.setAntiAlias(true);
drawable.setCornerRadius(Math.max(bitmap.getWidth(), bitmap.getHeight()) / 2.0f);
mImageCache.put(resourceId, drawable);
}
imageView.setImageDrawable(drawable);
if (Constants.LOCAL_LOGD) Log.d(TAG, "Round image created and set.");
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.presenters;
import android.content.Context;
import android.graphics.Color;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.supportleanbackshowcase.cards.views.ImageCardViewReplacement;
import android.widget.TextView;
/**
* This presenter will display the cards representing a gamer banners. It inherits from {@link
* ImageCardViewPresenter} and will set a footer icon as well as a secondary text color.
*/
public class GameBannerCardPresenter extends ImageCardViewPresenter {
private static final String TAG = "GameBannerCardPresenter";
public GameBannerCardPresenter(Context context) {
super(context,
(int) context.getResources().getDimension(R.dimen.wide_short_image_card_width),
(int) context.getResources().getDimension(R.dimen.wide_short_image_card_height));
}
@Override protected ImageCardViewReplacement onCreateView() {
ImageCardViewReplacement cardView = super.onCreateView();
TextView secondaryText = cardView.getSecondaryTextView();
secondaryText.setTextColor(Color.parseColor("#80c349"));
return cardView;
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.presenters;
import android.content.Context;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.views.BaseCardViewEx;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
/**
* This Presenter will display cards which consists of a single icon which will be highlighted by a
* surrounding circle when the card is focused. AndroidTV uses these cards for entering settings
* menu.
*/
public class IconCardPresenter extends AbstractCardPresenter<BaseCardViewEx> {
private static final String TAG = "IconCardPresenter";
public IconCardPresenter(Context context) {
super(context);
}
@Override protected BaseCardViewEx onCreateView() {
final BaseCardViewEx cardView = new BaseCardViewEx(getContext());
LayoutInflater.from(getContext()).inflate(R.layout.icon_card, cardView);
LayoutInflater.from(getContext()).inflate(R.layout.icon_card_footer, cardView);
cardView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) cardView.getViewById(R.id.container)
.setBackgroundResource(R.drawable.icon_focused);
else cardView.getViewById(R.id.container).setBackground(null);
}
});
return cardView;
}
@Override public void onBindViewHolder(Card card, BaseCardViewEx cardView) {
TextView primaryText = cardView.getViewById(R.id.primary_text);
ImageView imageView = cardView.getViewById(R.id.main_image);
primaryText.setText(card.getTitle());
if (card.getLocalImageResourceName() != null) {
int resourceId = card.getLocalImageResourceId(getContext());
Picasso.with(getContext()).load(resourceId).into(imageView);
}
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.presenters;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.v17.leanback.supportleanbackshowcase.Constants;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.supportleanbackshowcase.Utils;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.views.ImageCardViewReplacement;
import android.util.Log;
import android.view.View;
import com.squareup.picasso.Picasso;
import java.util.HashMap;
/**
* A ImageCardViewPresenter is used to generate Views and bind Objects to them on demand. It
* contains an {@link ImageCardViewReplacement}.
*/
public class ImageCardViewPresenter extends AbstractCardPresenter<ImageCardViewReplacement> {
private static final String TAG = "ImageCardViewPresenter";
private final int mImageWidthInDp;
private final int mImageHeightDp;
private final Drawable mLoadingErrorDrawable;
private final HashMap<Object, Integer> mSelectedColors = new HashMap<Object, Integer>();
private int mDefaultFooterColor;
public ImageCardViewPresenter(Context context, int imageWidthInDp, int imageHeightInDp) {
super(context);
int color = context.getResources().getColor(R.color.loading_error_card_background);
mLoadingErrorDrawable = new ColorDrawable(color);
mDefaultFooterColor = context.getResources()
.getColor(R.color.default_card_footer_background_color);
mImageWidthInDp = imageWidthInDp;
mImageHeightDp = imageHeightInDp;
}
@Override protected ImageCardViewReplacement onCreateView() {
if (Constants.LOCAL_LOGD) Log.d(TAG, "onCreateView()");
final ImageCardViewReplacement cardView = new ImageCardViewReplacement(getContext(),
mImageWidthInDp,
mImageHeightDp);
cardView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override public void onFocusChange(View v, boolean hasFocus) {
udateCardUi(cardView);
}
});
return cardView;
}
@Override public void onBindViewHolder(Card card, ImageCardViewReplacement cardView) {
if (Constants.LOCAL_LOGD) Log.d(TAG, "onBindViewHolder(Card,ImageCardViewReplacement)");
cardView.setTag(card);
// Display description iff there is one.
if (card.getDescription() == null || card.getDescription().length() == 0) {
cardView.getSecondaryTextView().setVisibility(View.GONE);
cardView.getPrimaryTextView().setLines(2);
cardView.getPrimaryTextView().setMaxLines(2);
} else {
cardView.getPrimaryTextView().setLines(1);
cardView.getPrimaryTextView().setMaxLines(1);
cardView.getSecondaryTextView().setText(card.getDescription());
cardView.getSecondaryTextView().setVisibility(View.VISIBLE);
}
// Display title iff there is one.
if (card.getTitle() == null || card.getTitle().length() == 0) {
cardView.getPrimaryTextView().setVisibility(View.GONE);
cardView.getSecondaryTextView().setLines(2);
cardView.getSecondaryTextView().setMaxLines(2);
} else {
cardView.getSecondaryTextView().setLines(1);
cardView.getSecondaryTextView().setMaxLines(1);
cardView.getPrimaryTextView().setText(card.getTitle());
cardView.getPrimaryTextView().setVisibility(View.VISIBLE);
}
// Load main image from an URI or a local resource.
if (card.getImageURI() != null) {
Utils.loadImageFromUri(getContext(), card.getImageURI(), cardView.getImageView(),
mImageWidthInDp, mImageHeightDp, true, mLoadingErrorDrawable);
} else if (card.getLocalImageResourceName() != null) {
int resourceId = getContext().getResources()
.getIdentifier(card.getLocalImageResourceName(),
"drawable", getContext().getPackageName());
Picasso.with(getContext()).load(resourceId).resize(mImageWidthInDp, mImageHeightDp)
.centerCrop().into(cardView.getImageView());
}
// Load footer icon from a local resource or hide it.
cardView.getViewById(R.id.container).setVisibility(View.VISIBLE);
if (card.getFooterLocalImageResourceName() != null) {
int resourceId = getContext().getResources()
.getIdentifier(card.getFooterLocalImageResourceName(),
"drawable", getContext().getPackageName());
Picasso.with(getContext()).load(resourceId).into(cardView.getIconView());
cardView.getIconView().setVisibility(View.VISIBLE);
} else {
if (card.getDescription() == null || card.getDescription().isEmpty()) {
cardView.getViewById(R.id.container).setVisibility(View.GONE);
}
cardView.getIconView().setVisibility(View.GONE);
}
// Update background color depending on the card's focused state.
udateCardUi(cardView);
}
private void udateCardUi(ImageCardViewReplacement view) {
int color = mDefaultFooterColor;
if (view.getTag() != null) {
Card card = (Card) view.getTag();
if (card.getSelectedColor() != -1 && view.isSelected()) {
color = card.getSelectedColor();
}
if (card.getFooterColor() != -1) {
color = card.getFooterColor();
}
}
view.getInfoBoxView().setBackgroundColor(color);
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.presenters;
import android.content.Context;
import android.support.v17.leanback.supportleanbackshowcase.Constants;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.views.ImageCardViewReplacement;
import android.support.v17.leanback.widget.ImageCardView;
import android.util.Log;
/**
* A ImageCardViewPresenter is used to generate Views and bind Objects to them on demand. It
* contains an {@link ImageCardViewReplacement}.
*/
public class ImageCardViewPresenter1 extends AbstractCardPresenter<ImageCardView> {
private static final String TAG = "ImageCardViewPresenter";
private final int mImageWidthInDp;
private final int mImageHeightDp;
public ImageCardViewPresenter1(Context context, int imageWidthInDp, int imageHeightInDp) {
super(context);
mImageWidthInDp = imageWidthInDp;
mImageHeightDp = imageHeightInDp;
}
@Override protected ImageCardView onCreateView() {
if (Constants.LOCAL_LOGD) Log.d(TAG, "onCreateView()");
final ImageCardView cardView = new ImageCardView(getContext(), R.style.ImageCardViewColoredTextStyle);
cardView.setMainImageDimensions(mImageWidthInDp, mImageHeightDp);
cardView.setFocusable(true);
return cardView;
}
@Override public void onBindViewHolder(Card card, final ImageCardView cardView) {
if (Constants.LOCAL_LOGD) Log.d(TAG, "onBindViewHolder(Card,ImageCardViewReplacement)");
cardView.setTag(card);
cardView.setTitleText(card.getTitle());
//cardView.setContentText("Hello");
if (card.getLocalImageResourceName() != null) {
int resourceId = getContext().getResources()
.getIdentifier(card.getLocalImageResourceName(),
"drawable", getContext().getPackageName());
cardView.getMainImageView().setImageResource(resourceId);
}
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.presenters;
import android.animation.ValueAnimator;
import android.content.Context;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.supportleanbackshowcase.Utils;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.views.ImageCardViewReplacement;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;
/**
* THis Presenter displays an expandable card which is used e.g. in the AndroidTV launcher. Once
* such a card gets focused it expands and will show more details of the image.
*/
public class LauncherCardPresenter extends ImageCardViewPresenter {
private static final String TAG = "LauncherCardPresenter";
public LauncherCardPresenter(Context context) {
super(context, 1 /* val > 0 required by Picasso */,
(int) context.getResources().getDimension(R.dimen.default_image_card_height));
}
@Override protected ImageCardViewReplacement onCreateView() {
ImageCardViewReplacement cardView = super.onCreateView();
final ImageView imageView = cardView.getImageView();
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
cardView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override public void onFocusChange(View v, final boolean hasFocus) {
int expandedWidth = (int) getContext().getResources().getDimension(
R.dimen.default_image_card_width);
int collapsedWidth = (int) getContext().getResources().getDimension(
R.dimen.default_image_card_height);
expandedWidth = collapsedWidth;
ValueAnimator animator = new ValueAnimator();
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.setIntValues(hasFocus ? collapsedWidth : expandedWidth,
hasFocus ? expandedWidth : collapsedWidth);
animator.setDuration(500);
animator.setStartDelay(0);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override public void onAnimationUpdate(ValueAnimator animation) {
ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams();
layoutParams.width = (Integer) animation.getAnimatedValue();
imageView.setLayoutParams(layoutParams);
}
});
animator.start();
}
});
return cardView;
}
@Override public void onBindViewHolder(Card card, ImageCardViewReplacement cardView) {
super.onBindViewHolder(card, cardView);
ImageView imageView = cardView.getImageView();
cardView.setTag(card);
int width = (int) getContext().getResources()
.getDimension(R.dimen.default_image_card_height);
ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams();
layoutParams.width = Utils.convertDpToPixel(getContext(), width);
imageView.setLayoutParams(layoutParams);
if (card.getLocalImageResourceName() != null) {
int height = (int) getContext().getResources()
.getDimension(R.dimen.sidetext_image_card_height);
int resourceId = getContext().getResources()
.getIdentifier(card.getLocalImageResourceName(),
"drawable", getContext().getPackageName());
Picasso.with(getContext()).load(resourceId).into(imageView);
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.presenters;
import android.content.Context;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.views.ImageCardViewReplacement;
import android.view.Gravity;
/**
* This Presenter inherits from ImageCardViewPresenter and will set the secondary text alignment as
* well as a footer icon to display the movie's rating.
*/
public class MovieRatingCardPresenter extends ImageCardViewPresenter {
private static final String TAG = "MovieRatingCardPresenter";
public MovieRatingCardPresenter(Context context) {
super(context, (int) context.getResources().getDimension(R.dimen.thin_image_card_width),
(int) context.getResources().getDimension(R.dimen.thin_image_card_height));
}
@Override public void onBindViewHolder(Card card, ImageCardViewReplacement cardView) {
super.onBindViewHolder(card, cardView);
cardView.getPrimaryTextView().setLines(2);
cardView.getPrimaryTextView().setMaxLines(2);
cardView.getSecondaryTextView().setGravity(Gravity.RIGHT);
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.presenters;
import android.content.Context;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.views.BaseCardViewEx;
import android.support.v17.leanback.supportleanbackshowcase.cards.views.OnActivateStateChangeHandler;
import android.support.v17.leanback.widget.BaseCardView;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
/**
* This Presenter will display a card consisting of an image on the left side of the card followed
* by text on the right side. The image and text have equal width. The text will work like a info
* box, thus it will be hidden if the parent row is inactive. This behavior is unique to this card
* and requires a special focus handler.
*/
public class SideInfoCardPresenter extends AbstractCardPresenter<BaseCardViewEx> implements
OnActivateStateChangeHandler {
private static final String TAG = "SideInfoCardPresenter";
public SideInfoCardPresenter(Context context) {
super(context);
}
@Override protected BaseCardViewEx onCreateView() {
BaseCardViewEx cardView = new BaseCardViewEx(getContext());
cardView.setCardType(BaseCardView.CARD_TYPE_MAIN_ONLY);
cardView.addView(LayoutInflater.from(getContext()).inflate(R.layout.side_info_card, null));
cardView.setOnActivateStateChangeHandler(this);
onActivateStateChanged(cardView, cardView.isActivated());
return cardView;
}
@Override public void onBindViewHolder(Card card, BaseCardViewEx cardView) {
ImageView imageView = cardView.getViewById(R.id.main_image);
if (card.getLocalImageResourceName() != null) {
int width = (int) getContext().getResources()
.getDimension(R.dimen.sidetext_image_card_width);
int height = (int) getContext().getResources()
.getDimension(R.dimen.sidetext_image_card_height);
int resourceId = getContext().getResources()
.getIdentifier(card.getLocalImageResourceName(),
"drawable", getContext().getPackageName());
Picasso.with(getContext()).load(resourceId).resize(width, height).centerCrop()
.into(imageView);
}
TextView primaryText = cardView.getViewById(R.id.primary_text);
primaryText.setText(card.getTitle());
TextView secondaryText = cardView.getViewById(R.id.secondary_text);
secondaryText.setText(card.getDescription());
TextView extraText = cardView.getViewById(R.id.extra_text);
extraText.setText(card.getExtraText());
}
@Override public void onActivateStateChanged(final BaseCardViewEx cardView, boolean activated) {
cardView.getViewById(R.id.info).setVisibility(activated ? View.VISIBLE : View.GONE);
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.presenters;
import android.content.Context;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.views.BaseCardViewEx;
import android.support.v17.leanback.widget.BaseCardView;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.TextView;
/**
* This Presenter will display a card which consists of a big image followed by a colored footer.
* Not only the colored footer is unique to this card, but also it's footer (info) will be visible
* even when its parent row is inactive.
*/
public class SingleLineCardPresenter extends AbstractCardPresenter<BaseCardViewEx> {
private static final String TAG = "SingleLineCardPresenter";
public SingleLineCardPresenter(Context context) {
super(context);
}
@Override protected BaseCardViewEx onCreateView() {
BaseCardViewEx cardView = new BaseCardViewEx(getContext());
cardView.setCardType(BaseCardView.CARD_TYPE_MAIN_ONLY);
cardView.addView(
LayoutInflater.from(getContext()).inflate(R.layout.single_line_card_footer, null));
return cardView;
}
@Override public void onBindViewHolder(Card card, BaseCardViewEx cardView) {
TextView primaryText = cardView.getViewById(R.id.primary_text);
primaryText.setText(card.getTitle());
int resourceId = getContext().getResources()
.getIdentifier(card.getLocalImageResourceName(), "drawable",
getContext().getPackageName());
ImageView mainImage = cardView.getViewById(R.id.main_image);
mainImage.setImageResource(resourceId);
cardView.getViewById(R.id.container).setBackgroundColor(card.getFooterColor());
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.presenters;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.widget.Presenter;
import android.util.Log;
import android.view.ViewGroup;
import android.widget.TextView;
public class StringPresenter extends Presenter {
private static final String TAG = "StringPresenter";
public ViewHolder onCreateViewHolder(ViewGroup parent) {
Log.d(TAG, "onCreateViewHolder");
TextView tv = new TextView(parent.getContext());
tv.setFocusable(true);
tv.setFocusableInTouchMode(true);
tv.setBackground(parent.getContext().getResources().getDrawable(R.drawable.icon_focused));
return new ViewHolder(tv);
}
public void onBindViewHolder(ViewHolder viewHolder, Object item) {
Log.d(TAG, "onBindViewHolder for " + item.toString());
((TextView) viewHolder.view).setText(item.toString());
}
public void onUnbindViewHolder(ViewHolder viewHolder) {
Log.d(TAG, "onUnbindViewHolder");
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.presenters;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.supportleanbackshowcase.cards.models.Card;
import android.support.v17.leanback.supportleanbackshowcase.cards.views.BaseCardViewEx;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
/**
* The Presenter displays a card consisting of text as a replacement for a big image. The footer is
* also quite unique since it does contain two images rather than one or non.
*/
public class TextCardPresenter extends AbstractCardPresenter<BaseCardViewEx> {
private static final String TAG = "AbstractFooterCardPresenter";
public TextCardPresenter(Context context) {
super(context);
}
@Override protected BaseCardViewEx onCreateView() {
BaseCardViewEx cardView = new BaseCardViewEx(getContext());
LayoutInflater.from(getContext()).inflate(R.layout.text_icon_card, cardView);
LayoutInflater.from(getContext()).inflate(R.layout.text_icon_card_footer, cardView);
return cardView;
}
@Override public void onBindViewHolder(Card card, BaseCardViewEx cardView) {
TextView extraText = cardView.getViewById(R.id.extra_text);
TextView primaryText = cardView.getViewById(R.id.primary_text);
ImageView footerIcon = cardView.getViewById(R.id.footer_icon);
final ImageView imageView = cardView.getViewById(R.id.main_image);
extraText.setText(card.getExtraText());
primaryText.setText(card.getTitle());
if (card.getLocalImageResourceName() != null) {
int width = (int) getContext().getResources()
.getDimension(R.dimen.sidetext_image_card_width);
int height = (int) getContext().getResources()
.getDimension(R.dimen.sidetext_image_card_height);
int resourceId = card.getLocalImageResourceId(getContext());
// TODO: hahnr@ load the image without Picasso
Picasso.with(getContext()).load(resourceId).resize(width, height).centerCrop()
.into(new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory
.create(getContext().getResources(), bitmap);
drawable.setCornerRadius(
Math.max(bitmap.getWidth(), bitmap.getHeight()) / 2.0f);
imageView.setImageDrawable(drawable);
}
@Override public void onBitmapFailed(Drawable errorDrawable) {
}
@Override public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
}
footerIcon.setImageResource(R.drawable.stars_white);
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.views;
import android.content.Context;
import android.support.v17.leanback.supportleanbackshowcase.ResourceCache;
import android.support.v17.leanback.widget.BaseCardView;
import android.view.View;
/**
* This class is an extension for the BaseCardView which is focusable by default. This behavior has
* to be merged into the original BaseCardView at some point in the development. After merging those
* two classes, this one, the BaseCardViewEx, can be removed.
*/
public class BaseCardViewEx extends BaseCardView {
protected Context mContext;
private ResourceCache mResourceCache = new ResourceCache();
private OnActivateStateChangeHandler mActivationCallback;
public BaseCardViewEx(Context context) {
super(context);
mContext = context;
setCardType(BaseCardView.CARD_TYPE_INFO_UNDER);
// TODO: @hahnr BaseCardView should be focusable by default. Merge!
setFocusable(true);
setFocusableInTouchMode(true);
}
@Override public void setActivated(boolean activated) {
super.setActivated(activated);
if (mActivationCallback != null)
mActivationCallback.onActivateStateChanged(this, activated);
}
public void setOnActivateStateChangeHandler(OnActivateStateChangeHandler handler) {
mActivationCallback = handler;
}
public <ViewType extends View> ViewType getViewById(int resId) {
return mResourceCache.getViewById(this, resId);
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.views;
import android.content.Context;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.view.LayoutInflater;
import android.widget.ImageView;
/**
* The FooterLayoutCardView creates a card view consisting of a big image followed by a footer
* passed as a layout resource.
*/
public class FooterLayoutCardView extends BaseCardViewEx {
private final ImageView mImageView;
/**
* The footer passed as a layout resource id will be inflated and added automatically to this
* view.
* <p/>
* <u>Note:</u> If you want your footer to expand/collapse when its parent row is
* activated/deactivated, you have to add the <code>layout_viewType="info"</code> property to
* your footers root view.
* <p/>
* <u>Example footer layout:</u>
* <pre>{@code <?xml version="1.0" encoding="utf-8"?>
* <FrameLayout
* xmlns:android="http://schemas.android.com/apk/res/android"
* xmlns:lb="http://schemas.android.com/apk/res-auto"
* lb:layout_viewType="info"
* android:layout_width="match_parent"
* android:layout_height="match_parent">
* <TextView
* android:id="@+id/primary_text"
* android:layout_width="match_parent"
* android:layout_height="wrap_content"/>
* </FrameLayout>}</pre>
*
* @param context The current context.
* @param layoutId The footers layout resource id.
* @param imageWidthInDp The width of the ImageView used in this card. The card's width always
* equals the image's width.
* @param imageHeightInDp The height of the ImageView used in this card.
* @see android.support.v17.leanback.widget.BaseCardView.LayoutParams
*/
public FooterLayoutCardView(Context context, int layoutId, int imageWidthInDp,
int imageHeightInDp) {
super(context);
setCardType(CARD_TYPE_INFO_UNDER);
setBackgroundColor(context.getResources().getColor(R.color.default_card_background_color));
LayoutInflater.from(context).inflate(R.layout.image_card, this);
mImageView = getViewById(R.id.image_card_view_main_image);
LayoutInflater.from(context).inflate(layoutId, this);
}
public ImageView getImageView() {
return mImageView;
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.views;
import android.content.Context;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.widget.ImageCardView;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
/**
* Leanback's {@link ImageCardView} will need some layout changes to fit all design requirements
* from the specs. This class is a temporary implementation of the "new" {@link ImageCardView} and
* will replace the existing one at some point in the development process. The original
* implementation also requires some refactoring to be more flexible in order to be used for various
* card types.
* <p/>
* Besides from the refactoring of the ImageCardView I suggest to change not only the BaseCardView
* but also to add at least one additional CardView, the "FooterLayoutCardView". More about this
* topic can be discussed later in the development process.
*/
public class ImageCardViewReplacement extends FooterLayoutCardView {
public static final int PRIMARY_TEXTVIEW_ID = R.id.primary_text;
public static final int SECONDARY_TEXTVIEW_ID = R.id.secondary_text;
public static final int FOOTER_ICON_ID = R.id.footer_icon;
public static final int INFO_BOX_ID = R.id.info_field;
public ImageCardViewReplacement(Context context, int widthInDp, int heightInDp) {
super(context, R.layout.image_card_footer, widthInDp, heightInDp);
setBackgroundColor(context.getResources().getColor(R.color.default_card_background_color));
}
public TextView getPrimaryTextView() {
return getViewById(PRIMARY_TEXTVIEW_ID);
}
public TextView getSecondaryTextView() {
return getViewById(SECONDARY_TEXTVIEW_ID);
}
public ImageView getIconView() {
return getViewById(FOOTER_ICON_ID);
}
public View getInfoBoxView() {
return getViewById(INFO_BOX_ID);
}
}

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
package android.support.v17.leanback.supportleanbackshowcase.cards.views;
public interface OnActivateStateChangeHandler {
void onActivateStateChanged(BaseCardViewEx cardView, boolean activated);
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="#384248"></color>
</item>
<item>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="#FFFFFF"/>
</shape>
</item>
</ripple>
</item>
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

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