From 5b3f6978841057f4d40f6f8c2bfec45f4e64d26d Mon Sep 17 00:00:00 2001 From: wb9688 Date: Sun, 24 Dec 2017 19:29:14 +0100 Subject: [PATCH 1/3] Add Android TV version --- app/build.gradle | 53 ++-- .../subscription/SubscriptionEntity.java | 5 +- app/src/tv/AndroidManifest.xml | 35 +++ .../newpipe/tv/BrowseErrorActivity.java | 62 +++++ .../org/schabi/newpipe/tv/CardPresenter.java | 76 ++++++ .../schabi/newpipe/tv/DetailsActivity.java | 18 ++ .../tv/DetailsDescriptionPresenter.java | 17 ++ .../org/schabi/newpipe/tv/ErrorFragment.java | 33 +++ .../org/schabi/newpipe/tv/MainActivity.java | 15 ++ .../org/schabi/newpipe/tv/MainFragment.java | 240 ++++++++++++++++++ .../tv/java/org/schabi/newpipe/tv/Movie.java | 84 ++++++ .../java/org/schabi/newpipe/tv/MovieList.java | 63 +++++ .../schabi/newpipe/tv/PlaybackActivity.java | 17 ++ .../newpipe/tv/PlaybackVideoFragment.java | 46 ++++ .../newpipe/tv/VideoDetailsFragment.java | 206 +++++++++++++++ .../tv/res/drawable/app_icon_your_company.png | Bin 0 -> 12416 bytes .../tv/res/drawable/default_background.xml | 9 + app/src/tv/res/drawable/movie.png | Bin 0 -> 13007 bytes app/src/tv/res/layout/activity_details.xml | 9 + app/src/tv/res/layout/activity_main.xml | 10 + app/src/tv/res/values/colors.xml | 8 + app/src/tv/res/values/strings.xml | 23 ++ build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 3 +- 24 files changed, 1004 insertions(+), 30 deletions(-) create mode 100644 app/src/tv/AndroidManifest.xml create mode 100644 app/src/tv/java/org/schabi/newpipe/tv/BrowseErrorActivity.java create mode 100644 app/src/tv/java/org/schabi/newpipe/tv/CardPresenter.java create mode 100644 app/src/tv/java/org/schabi/newpipe/tv/DetailsActivity.java create mode 100644 app/src/tv/java/org/schabi/newpipe/tv/DetailsDescriptionPresenter.java create mode 100644 app/src/tv/java/org/schabi/newpipe/tv/ErrorFragment.java create mode 100644 app/src/tv/java/org/schabi/newpipe/tv/MainActivity.java create mode 100644 app/src/tv/java/org/schabi/newpipe/tv/MainFragment.java create mode 100644 app/src/tv/java/org/schabi/newpipe/tv/Movie.java create mode 100644 app/src/tv/java/org/schabi/newpipe/tv/MovieList.java create mode 100644 app/src/tv/java/org/schabi/newpipe/tv/PlaybackActivity.java create mode 100644 app/src/tv/java/org/schabi/newpipe/tv/PlaybackVideoFragment.java create mode 100644 app/src/tv/java/org/schabi/newpipe/tv/VideoDetailsFragment.java create mode 100644 app/src/tv/res/drawable/app_icon_your_company.png create mode 100644 app/src/tv/res/drawable/default_background.xml create mode 100644 app/src/tv/res/drawable/movie.png create mode 100644 app/src/tv/res/layout/activity_details.xml create mode 100644 app/src/tv/res/layout/activity_main.xml create mode 100644 app/src/tv/res/values/colors.xml create mode 100644 app/src/tv/res/values/strings.xml diff --git a/app/build.gradle b/app/build.gradle index 2f1dd400544..4172dec4105 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,9 +1,10 @@ apply plugin: 'com.android.application' android { + signingConfigs { + } compileSdkVersion 26 - buildToolsVersion '26.0.1' - + buildToolsVersion '26.0.2' defaultConfig { applicationId "org.schabi.newpipe" minSdkVersion 15 @@ -30,7 +31,17 @@ android { applicationIdSuffix ".beta" } } - + flavorDimensions "platform" + productFlavors { + normal { + dimension "platform" + } + tv { + dimension "platform" + minSdkVersion 21 + versionNameSuffix "-tv" + } + } lintOptions { checkReleaseBuilds false // Or, if you prefer, you can continue to check for errors in release builds, @@ -47,39 +58,33 @@ dependencies { androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2') { exclude module: 'support-annotations' } - - compile 'com.github.TeamNewPipe:NewPipeExtractor:1df3f67' - + compile 'com.github.wb9688:NewPipeExtractor:97c55eed31197b68c0601501c7ac5223c138df0b' testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19' - - compile 'com.android.support:appcompat-v7:26.0.1' - compile 'com.android.support:support-v4:26.0.1' - compile 'com.android.support:design:26.0.1' - compile 'com.android.support:recyclerview-v7:26.0.1' - compile 'com.android.support:preference-v14:26.0.1' - - compile 'com.google.code.gson:gson:2.7' + compile 'com.android.support:appcompat-v7:26.1.0' + compile 'com.android.support:support-v4:26.1.0' + compile 'com.android.support:design:26.1.0' + compile 'com.android.support:recyclerview-v7:26.1.0' + compile 'com.android.support:preference-v14:26.1.0' + tvCompile 'com.android.support.constraint:constraint-layout:1.0.2' + tvCompile 'com.android.support:leanback-v17:26.1.0' + tvCompile 'com.github.bumptech.glide:glide:3.8.0' + compile 'com.google.code.gson:gson:2.8.0' compile 'ch.acra:acra:4.9.0' - compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' compile 'de.hdodenhof:circleimageview:2.1.0' compile 'com.github.nirhart:parallaxscroll:1.0' compile 'com.nononsenseapps:filepicker:3.0.1' compile 'com.google.android.exoplayer:exoplayer:r2.5.1' - debugCompile 'com.facebook.stetho:stetho:1.5.0' debugCompile 'com.facebook.stetho:stetho-urlconnection:1.5.0' - debugCompile 'com.android.support:multidex:1.0.1' - + debugCompile 'com.android.support:multidex:1.0.2' compile 'io.reactivex.rxjava2:rxjava:2.1.2' compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0' - - compile 'android.arch.persistence.room:runtime:1.0.0-alpha8' - compile 'android.arch.persistence.room:rxjava2:1.0.0-alpha8' - annotationProcessor 'android.arch.persistence.room:compiler:1.0.0-alpha8' - + compile 'android.arch.persistence.room:runtime:1.0.0-beta2' + compile 'android.arch.persistence.room:rxjava2:1.0.0-beta2' + annotationProcessor 'android.arch.persistence.room:compiler:1.0.0-beta2' compile 'frankiesardo:icepick:3.2.0' - provided 'frankiesardo:icepick-processor:3.2.0' + annotationProcessor 'frankiesardo:icepick-processor:3.2.0' } diff --git a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java index 12d1764cc62..e71088ac9e8 100644 --- a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java @@ -116,10 +116,7 @@ public void setData(final String name, @Ignore public ChannelInfoItem toChannelInfoItem() { - ChannelInfoItem item = new ChannelInfoItem(); - item.url = getUrl(); - item.service_id = getServiceId(); - item.name = getName(); + ChannelInfoItem item = new ChannelInfoItem(getServiceId(), getUrl(), getName()); item.thumbnail_url = getAvatarUrl(); item.subscriber_count = getSubscriberCount(); item.description = getDescription(); diff --git a/app/src/tv/AndroidManifest.xml b/app/src/tv/AndroidManifest.xml new file mode 100644 index 00000000000..54f8b4cbae9 --- /dev/null +++ b/app/src/tv/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/tv/java/org/schabi/newpipe/tv/BrowseErrorActivity.java b/app/src/tv/java/org/schabi/newpipe/tv/BrowseErrorActivity.java new file mode 100644 index 00000000000..16d9ddacfba --- /dev/null +++ b/app/src/tv/java/org/schabi/newpipe/tv/BrowseErrorActivity.java @@ -0,0 +1,62 @@ +package org.schabi.newpipe.tv; + +import android.app.Activity; +import android.app.Fragment; +import android.os.Bundle; +import android.os.Handler; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ProgressBar; + +import org.schabi.newpipe.R; + +public class BrowseErrorActivity extends Activity { + private static int TIMER_DELAY = 3000; + private static int SPINNER_WIDTH = 100; + private static int SPINNER_HEIGHT = 100; + + private ErrorFragment mErrorFragment; + private SpinnerFragment mSpinnerFragment; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + testError(); + } + + private void testError() { + mErrorFragment = new ErrorFragment(); + getFragmentManager().beginTransaction().add(R.id.main_browse_fragment, mErrorFragment).commit(); + + mSpinnerFragment = new SpinnerFragment(); + getFragmentManager().beginTransaction().add(R.id.main_browse_fragment, mSpinnerFragment).commit(); + + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + getFragmentManager().beginTransaction().remove(mSpinnerFragment).commit(); + mErrorFragment.setErrorContent(); + } + }, TIMER_DELAY); + } + + static public class SpinnerFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + ProgressBar progressBar = new ProgressBar(container.getContext()); + if (container instanceof FrameLayout) { + FrameLayout.LayoutParams layoutParams = + new FrameLayout.LayoutParams(SPINNER_WIDTH, SPINNER_HEIGHT, Gravity.CENTER); + progressBar.setLayoutParams(layoutParams); + } + return progressBar; + } + } +} diff --git a/app/src/tv/java/org/schabi/newpipe/tv/CardPresenter.java b/app/src/tv/java/org/schabi/newpipe/tv/CardPresenter.java new file mode 100644 index 00000000000..b7b1180d7a7 --- /dev/null +++ b/app/src/tv/java/org/schabi/newpipe/tv/CardPresenter.java @@ -0,0 +1,76 @@ +package org.schabi.newpipe.tv; + +import android.graphics.drawable.Drawable; +import android.support.v17.leanback.widget.ImageCardView; +import android.support.v17.leanback.widget.Presenter; +import android.util.Log; +import android.view.ViewGroup; + +import com.bumptech.glide.Glide; + +import org.schabi.newpipe.R; + +public class CardPresenter extends Presenter { + private static final String TAG = "CardPresenter"; + + private static final int CARD_WIDTH = 313; + private static final int CARD_HEIGHT = 176; + private static int sSelectedBackgroundColor; + private static int sDefaultBackgroundColor; + private Drawable mDefaultCardImage; + + private static void updateCardBackgroundColor(ImageCardView view, boolean selected) { + int color = selected ? sSelectedBackgroundColor : sDefaultBackgroundColor; + + view.setBackgroundColor(color); + view.findViewById(R.id.info_field).setBackgroundColor(color); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent) { + Log.d(TAG, "onCreateViewHolder"); + + sDefaultBackgroundColor = parent.getResources().getColor(R.color.default_background); + sSelectedBackgroundColor = parent.getResources().getColor(R.color.selected_background); + mDefaultCardImage = parent.getResources().getDrawable(R.drawable.movie); + + ImageCardView cardView = new ImageCardView(parent.getContext()) { + @Override + public void setSelected(boolean selected) { + updateCardBackgroundColor(this, selected); + super.setSelected(selected); + } + }; + + cardView.setFocusable(true); + cardView.setFocusableInTouchMode(true); + updateCardBackgroundColor(cardView, false); + return new ViewHolder(cardView); + } + + @Override + public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { + Movie movie = (Movie) item; + ImageCardView cardView = (ImageCardView) viewHolder.view; + + Log.d(TAG, "onBindViewHolder"); + if (movie.getImageUrl() != null) { + cardView.setTitleText(movie.getTitle()); + cardView.setContentText(movie.getStudio()); + cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT); + Glide.with(viewHolder.view.getContext()) + .load(movie.getImageUrl()) + .centerCrop() + .error(mDefaultCardImage) + .into(cardView.getMainImageView()); + } + } + + @Override + public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) { + Log.d(TAG, "onUnbindViewHolder"); + ImageCardView cardView = (ImageCardView) viewHolder.view; + cardView.setBadgeImage(null); + cardView.setMainImage(null); + } +} diff --git a/app/src/tv/java/org/schabi/newpipe/tv/DetailsActivity.java b/app/src/tv/java/org/schabi/newpipe/tv/DetailsActivity.java new file mode 100644 index 00000000000..349b3063103 --- /dev/null +++ b/app/src/tv/java/org/schabi/newpipe/tv/DetailsActivity.java @@ -0,0 +1,18 @@ +package org.schabi.newpipe.tv; + +import android.app.Activity; +import android.os.Bundle; + +import org.schabi.newpipe.R; + +public class DetailsActivity extends Activity { + public static final String SHARED_ELEMENT_NAME = "hero"; + public static final String MOVIE = "Movie"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_details); + } + +} diff --git a/app/src/tv/java/org/schabi/newpipe/tv/DetailsDescriptionPresenter.java b/app/src/tv/java/org/schabi/newpipe/tv/DetailsDescriptionPresenter.java new file mode 100644 index 00000000000..3afc52c1d94 --- /dev/null +++ b/app/src/tv/java/org/schabi/newpipe/tv/DetailsDescriptionPresenter.java @@ -0,0 +1,17 @@ +package org.schabi.newpipe.tv; + +import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter; + +public class DetailsDescriptionPresenter extends AbstractDetailsDescriptionPresenter { + + @Override + protected void onBindDescription(ViewHolder viewHolder, Object item) { + Movie movie = (Movie) item; + + if (movie != null) { + viewHolder.getTitle().setText(movie.getTitle()); + viewHolder.getSubtitle().setText(movie.getStudio()); + viewHolder.getBody().setText(movie.getDescription()); + } + } +} diff --git a/app/src/tv/java/org/schabi/newpipe/tv/ErrorFragment.java b/app/src/tv/java/org/schabi/newpipe/tv/ErrorFragment.java new file mode 100644 index 00000000000..4726438c1e0 --- /dev/null +++ b/app/src/tv/java/org/schabi/newpipe/tv/ErrorFragment.java @@ -0,0 +1,33 @@ +package org.schabi.newpipe.tv; + +import android.os.Bundle; +import android.util.Log; +import android.view.View; + +import org.schabi.newpipe.R; + +public class ErrorFragment extends android.support.v17.leanback.app.ErrorFragment { + private static final String TAG = "ErrorFragment"; + private static final boolean TRANSLUCENT = true; + + @Override + public void onCreate(Bundle savedInstanceState) { + Log.d(TAG, "onCreate"); + super.onCreate(savedInstanceState); + setTitle(getString(R.string.app_name)); + } + + void setErrorContent() { + setImageDrawable(getResources().getDrawable(R.drawable.lb_ic_sad_cloud)); + setMessage(getString(R.string.error_fragment_message)); + setDefaultBackground(TRANSLUCENT); + + setButtonText(getString(R.string.dismiss_error)); + setButtonClickListener(new View.OnClickListener() { + @Override + public void onClick(View arg0) { + getFragmentManager().beginTransaction().remove(ErrorFragment.this).commit(); + } + }); + } +} diff --git a/app/src/tv/java/org/schabi/newpipe/tv/MainActivity.java b/app/src/tv/java/org/schabi/newpipe/tv/MainActivity.java new file mode 100644 index 00000000000..b3596c95cc7 --- /dev/null +++ b/app/src/tv/java/org/schabi/newpipe/tv/MainActivity.java @@ -0,0 +1,15 @@ +package org.schabi.newpipe.tv; + +import android.app.Activity; +import android.os.Bundle; + +import org.schabi.newpipe.R; + +public class MainActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + } +} diff --git a/app/src/tv/java/org/schabi/newpipe/tv/MainFragment.java b/app/src/tv/java/org/schabi/newpipe/tv/MainFragment.java new file mode 100644 index 00000000000..f504c2e0929 --- /dev/null +++ b/app/src/tv/java/org/schabi/newpipe/tv/MainFragment.java @@ -0,0 +1,240 @@ +package org.schabi.newpipe.tv; + +import android.content.Intent; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.support.v17.leanback.app.BackgroundManager; +import android.support.v17.leanback.app.BrowseFragment; +import android.support.v17.leanback.widget.ArrayObjectAdapter; +import android.support.v17.leanback.widget.HeaderItem; +import android.support.v17.leanback.widget.ImageCardView; +import android.support.v17.leanback.widget.ListRow; +import android.support.v17.leanback.widget.ListRowPresenter; +import android.support.v17.leanback.widget.OnItemViewClickedListener; +import android.support.v17.leanback.widget.OnItemViewSelectedListener; +import android.support.v17.leanback.widget.Presenter; +import android.support.v17.leanback.widget.Row; +import android.support.v17.leanback.widget.RowPresenter; +import android.support.v4.app.ActivityOptionsCompat; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import android.widget.Toast; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.resource.drawable.GlideDrawable; +import com.bumptech.glide.request.animation.GlideAnimation; +import com.bumptech.glide.request.target.SimpleTarget; + +import org.schabi.newpipe.R; + +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +public class MainFragment extends BrowseFragment { + private static final String TAG = "MainFragment"; + + private static final int BACKGROUND_UPDATE_DELAY = 300; + private static final int GRID_ITEM_WIDTH = 200; + private static final int GRID_ITEM_HEIGHT = 200; + private static final int NUM_ROWS = 3; + private static final int NUM_COLS = 15; + + private final Handler mHandler = new Handler(); + private ArrayObjectAdapter mRowsAdapter; + private Drawable mDefaultBackground; + private DisplayMetrics mMetrics; + private Timer mBackgroundTimer; + private String mBackgroundUri; + private BackgroundManager mBackgroundManager; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + Log.i(TAG, "onCreate"); + super.onActivityCreated(savedInstanceState); + + prepareBackgroundManager(); + + setupUIElements(); + + loadRows(); + + setupEventListeners(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (null != mBackgroundTimer) { + Log.d(TAG, "onDestroy: " + mBackgroundTimer.toString()); + mBackgroundTimer.cancel(); + } + } + + private void loadRows() { + mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); + CardPresenter cardPresenter = new CardPresenter(); + + List> kiosksItems = MovieList.getKiosksItems(); + int i = 0; + for (List kioskItems : kiosksItems) { + ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter); + for (Movie kioskItem : kioskItems) { + listRowAdapter.add(kioskItem); + } + HeaderItem header = new HeaderItem(i, MovieList.getKiosks().get(i)); + mRowsAdapter.add(new ListRow(header, listRowAdapter)); + i++; + } + + HeaderItem gridHeader = new HeaderItem(i, getString(R.string.settings)); + + GridItemPresenter mGridPresenter = new GridItemPresenter(); + ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter); + gridRowAdapter.add(getString(R.string.grid_view)); + gridRowAdapter.add(getString(R.string.error_fragment)); + gridRowAdapter.add(getString(R.string.personal_settings)); + mRowsAdapter.add(new ListRow(gridHeader, gridRowAdapter)); + + setAdapter(mRowsAdapter); + } + + private void prepareBackgroundManager() { + + mBackgroundManager = BackgroundManager.getInstance(getActivity()); + mBackgroundManager.attach(getActivity().getWindow()); + mDefaultBackground = getResources().getDrawable(R.drawable.default_background); + mMetrics = new DisplayMetrics(); + getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics); + } + + private void setupUIElements() { + setTitle(getString(R.string.browse_title)); + setHeadersState(HEADERS_ENABLED); + setHeadersTransitionOnBackEnabled(true); + + setBrandColor(getResources().getColor(R.color.fastlane_background)); + setSearchAffordanceColor(getResources().getColor(R.color.search_opaque)); + } + + private void setupEventListeners() { + setOnSearchClickedListener(new View.OnClickListener() { + + @Override + public void onClick(View view) { + Toast.makeText(getActivity(), "Implement your own in-app search", Toast.LENGTH_LONG) + .show(); + } + }); + + setOnItemViewClickedListener(new ItemViewClickedListener()); + setOnItemViewSelectedListener(new ItemViewSelectedListener()); + } + + protected void updateBackground(String uri) { + int width = mMetrics.widthPixels; + int height = mMetrics.heightPixels; + Glide.with(getActivity()) + .load(uri) + .centerCrop() + .error(mDefaultBackground) + .into(new SimpleTarget(width, height) { + @Override + public void onResourceReady(GlideDrawable resource, + GlideAnimation + glideAnimation) { + mBackgroundManager.setDrawable(resource); + } + }); + mBackgroundTimer.cancel(); + } + + 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) { + + if (item instanceof Movie) { + Movie movie = (Movie) item; + Log.d(TAG, "Item: " + item.toString()); + Intent intent = new Intent(getActivity(), DetailsActivity.class); + intent.putExtra(DetailsActivity.MOVIE, movie); + + Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation( + getActivity(), + ((ImageCardView) itemViewHolder.view).getMainImageView(), + DetailsActivity.SHARED_ELEMENT_NAME).toBundle(); + getActivity().startActivity(intent, bundle); + } else if (item instanceof String) { + if (((String) item).contains(getString(R.string.error_fragment))) { + Intent intent = new Intent(getActivity(), BrowseErrorActivity.class); + startActivity(intent); + } else { + Toast.makeText(getActivity(), ((String) item), Toast.LENGTH_SHORT) + .show(); + } + } + } + } + + private final class ItemViewSelectedListener implements OnItemViewSelectedListener { + @Override + public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, + RowPresenter.ViewHolder rowViewHolder, Row row) { + if (item instanceof Movie) { + mBackgroundUri = ((Movie) item).getImageUrl(); + startBackgroundTimer(); + } + } + } + + private class UpdateBackgroundTask extends TimerTask { + + @Override + public void run() { + mHandler.post(new Runnable() { + @Override + public void run() { + updateBackground(mBackgroundUri); + } + }); + } + } + + private class GridItemPresenter extends Presenter { + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent) { + TextView view = new TextView(parent.getContext()); + view.setLayoutParams(new ViewGroup.LayoutParams(GRID_ITEM_WIDTH, GRID_ITEM_HEIGHT)); + view.setFocusable(true); + view.setFocusableInTouchMode(true); + view.setBackgroundColor(getResources().getColor(R.color.default_background)); + view.setTextColor(Color.WHITE); + view.setGravity(Gravity.CENTER); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(ViewHolder viewHolder, Object item) { + ((TextView) viewHolder.view).setText((String) item); + } + + @Override + public void onUnbindViewHolder(ViewHolder viewHolder) { + } + } +} diff --git a/app/src/tv/java/org/schabi/newpipe/tv/Movie.java b/app/src/tv/java/org/schabi/newpipe/tv/Movie.java new file mode 100644 index 00000000000..a6c340fa99f --- /dev/null +++ b/app/src/tv/java/org/schabi/newpipe/tv/Movie.java @@ -0,0 +1,84 @@ +package org.schabi.newpipe.tv; + +import java.io.Serializable; + +public class Movie implements Serializable { + static final long serialVersionUID = 727566175075960653L; + private long id; + private String title; + private String description; + private String imageUrl; + private String videoUrl; + private String studio; + private String category; + + public Movie() { + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getStudio() { + return studio; + } + + public void setStudio(String studio) { + this.studio = studio; + } + + public String getVideoUrl() { + return videoUrl; + } + + public void setVideoUrl(String videoUrl) { + this.videoUrl = videoUrl; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + + @Override + public String toString() { + return "Movie{" + + "id=" + id + + ", title='" + title + '\'' + + ", videoUrl='" + videoUrl + '\'' + + ", imageUrl='" + imageUrl + '\'' + + '}'; + } +} diff --git a/app/src/tv/java/org/schabi/newpipe/tv/MovieList.java b/app/src/tv/java/org/schabi/newpipe/tv/MovieList.java new file mode 100644 index 00000000000..783b90827f8 --- /dev/null +++ b/app/src/tv/java/org/schabi/newpipe/tv/MovieList.java @@ -0,0 +1,63 @@ +package org.schabi.newpipe.tv; + +import android.os.StrictMode; + +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.extractor.kiosk.KioskExtractor; +import org.schabi.newpipe.extractor.stream.StreamInfoItem; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public final class MovieList { + private static List list; + private static long count = 0; + + public static List getKiosks() { + List kiosks = new ArrayList(); + for (StreamingService service : NewPipe.getServices()) { + try { + for (String kiosk : service.getKioskList().getAvailableKiosks()) { + kiosks.add(service.getServiceInfo().name + "/" + kiosk); + } + } catch (ExtractionException e) {} + } + return kiosks; + } + + public static List> getKiosksItems() { + List> kiosksItems = new ArrayList>(); + for (StreamingService service : NewPipe.getServices()) { + try { + for (String kiosk : service.getKioskList().getAvailableKiosks()) { + List kioskItems = new ArrayList(); + // FIXME: Don't use this hack + StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); + StrictMode.setThreadPolicy(policy); + KioskExtractor kioskExtractor = service.getKioskList().getExtractorById(kiosk, null); + for (StreamInfoItem stream : kioskExtractor.getStreams().getStreamInfoItemList()) { + kioskItems.add(buildMovieInfo("category", stream.name, "description", stream.uploader_name, "videoUrl", stream.thumbnail_url)); + } + kiosksItems.add(kioskItems); + } + } catch (IOException | ExtractionException e) {} + } + return kiosksItems; + } + + private static Movie buildMovieInfo(String category, String title, + String description, String studio, String videoUrl, String imageUrl) { + Movie movie = new Movie(); + movie.setId(count++); + movie.setTitle(title); + movie.setDescription(description); + movie.setStudio(studio); + movie.setCategory(category); + movie.setImageUrl(imageUrl); + movie.setVideoUrl(videoUrl); + return movie; + } +} \ No newline at end of file diff --git a/app/src/tv/java/org/schabi/newpipe/tv/PlaybackActivity.java b/app/src/tv/java/org/schabi/newpipe/tv/PlaybackActivity.java new file mode 100644 index 00000000000..5a63f039bf0 --- /dev/null +++ b/app/src/tv/java/org/schabi/newpipe/tv/PlaybackActivity.java @@ -0,0 +1,17 @@ +package org.schabi.newpipe.tv; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +public class PlaybackActivity extends FragmentActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState == null) { + getSupportFragmentManager().beginTransaction() + .replace(android.R.id.content, new PlaybackVideoFragment()) + .commit(); + } + } +} \ No newline at end of file diff --git a/app/src/tv/java/org/schabi/newpipe/tv/PlaybackVideoFragment.java b/app/src/tv/java/org/schabi/newpipe/tv/PlaybackVideoFragment.java new file mode 100644 index 00000000000..c14becdcff8 --- /dev/null +++ b/app/src/tv/java/org/schabi/newpipe/tv/PlaybackVideoFragment.java @@ -0,0 +1,46 @@ +package org.schabi.newpipe.tv; + +import android.os.Bundle; +import android.support.v17.leanback.app.VideoSupportFragment; +import android.support.v17.leanback.app.VideoSupportFragmentGlueHost; +import android.support.v17.leanback.media.MediaPlayerGlue; +import android.support.v17.leanback.media.PlaybackGlue; + +public class PlaybackVideoFragment extends VideoSupportFragment { + private static final String TAG = "PlaybackVideoFragment"; + + private MediaPlayerGlue mMediaPlayerGlue; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final Movie movie = (Movie) getActivity() + .getIntent().getSerializableExtra(DetailsActivity.MOVIE); + + VideoSupportFragmentGlueHost glueHost = + new VideoSupportFragmentGlueHost(PlaybackVideoFragment.this); + + mMediaPlayerGlue = new MediaPlayerGlue(getActivity()); + mMediaPlayerGlue.setHost(glueHost); + mMediaPlayerGlue.setMode(MediaPlayerGlue.NO_REPEAT); + mMediaPlayerGlue.setPlayerCallback(new PlaybackGlue.PlayerCallback() { + @Override + public void onReadyForPlayback() { + mMediaPlayerGlue.play(); + + } + }); + mMediaPlayerGlue.setTitle(movie.getTitle()); + mMediaPlayerGlue.setArtist(movie.getDescription()); + mMediaPlayerGlue.setVideoUrl(movie.getVideoUrl()); + } + + @Override + public void onPause() { + super.onPause(); + if (mMediaPlayerGlue != null) { + mMediaPlayerGlue.pause(); + } + } +} \ No newline at end of file diff --git a/app/src/tv/java/org/schabi/newpipe/tv/VideoDetailsFragment.java b/app/src/tv/java/org/schabi/newpipe/tv/VideoDetailsFragment.java new file mode 100644 index 00000000000..ede3b609a1d --- /dev/null +++ b/app/src/tv/java/org/schabi/newpipe/tv/VideoDetailsFragment.java @@ -0,0 +1,206 @@ +package org.schabi.newpipe.tv; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.support.v17.leanback.app.DetailsFragment; +import android.support.v17.leanback.app.DetailsFragmentBackgroundController; +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.FullWidthDetailsOverviewSharedElementHelper; +import android.support.v17.leanback.widget.HeaderItem; +import android.support.v17.leanback.widget.ImageCardView; +import android.support.v17.leanback.widget.ListRow; +import android.support.v17.leanback.widget.ListRowPresenter; +import android.support.v17.leanback.widget.OnActionClickedListener; +import android.support.v17.leanback.widget.OnItemViewClickedListener; +import android.support.v17.leanback.widget.Presenter; +import android.support.v17.leanback.widget.Row; +import android.support.v17.leanback.widget.RowPresenter; +import android.support.v4.app.ActivityOptionsCompat; +import android.support.v4.content.ContextCompat; +import android.util.Log; +import android.widget.Toast; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.resource.drawable.GlideDrawable; +import com.bumptech.glide.request.animation.GlideAnimation; +import com.bumptech.glide.request.target.SimpleTarget; + +import org.schabi.newpipe.R; + +public class VideoDetailsFragment extends DetailsFragment { + private static final String TAG = "VideoDetailsFragment"; + + private static final int ACTION_WATCH_TRAILER = 1; + private static final int ACTION_RENT = 2; + private static final int ACTION_BUY = 3; + + private static final int DETAIL_THUMB_WIDTH = 274; + private static final int DETAIL_THUMB_HEIGHT = 274; + + private static final int NUM_COLS = 10; + + private Movie mSelectedMovie; + + private ArrayObjectAdapter mAdapter; + private ClassPresenterSelector mPresenterSelector; + + private DetailsFragmentBackgroundController mDetailsBackground; + + @Override + public void onCreate(Bundle savedInstanceState) { + Log.d(TAG, "onCreate DetailsFragment"); + super.onCreate(savedInstanceState); + + mDetailsBackground = new DetailsFragmentBackgroundController(this); + + mSelectedMovie = + (Movie) getActivity().getIntent().getSerializableExtra(DetailsActivity.MOVIE); + if (mSelectedMovie != null) { + mPresenterSelector = new ClassPresenterSelector(); + mAdapter = new ArrayObjectAdapter(mPresenterSelector); + setupDetailsOverviewRow(); + setupDetailsOverviewRowPresenter(); + setupRelatedMovieListRow(); + setAdapter(mAdapter); + initializeBackground(mSelectedMovie); + setOnItemViewClickedListener(new ItemViewClickedListener()); + } else { + Intent intent = new Intent(getActivity(), MainActivity.class); + startActivity(intent); + } + } + + private void initializeBackground(Movie data) { + mDetailsBackground.enableParallax(); + Glide.with(getActivity()) + .load(data.getImageUrl()) + .asBitmap() + .centerCrop() + .error(R.drawable.default_background) + .into(new SimpleTarget() { + @Override + public void onResourceReady(Bitmap bitmap, + GlideAnimation glideAnimation) { + mDetailsBackground.setCoverBitmap(bitmap); + mAdapter.notifyArrayItemRangeChanged(0, mAdapter.size()); + } + }); + } + + private void setupDetailsOverviewRow() { + Log.d(TAG, "doInBackground: " + mSelectedMovie.toString()); + final DetailsOverviewRow row = new DetailsOverviewRow(mSelectedMovie); + row.setImageDrawable( + ContextCompat.getDrawable(getActivity(), R.drawable.default_background)); + int width = convertDpToPixel(getActivity().getApplicationContext(), DETAIL_THUMB_WIDTH); + int height = convertDpToPixel(getActivity().getApplicationContext(), DETAIL_THUMB_HEIGHT); + Glide.with(getActivity()) + .load(mSelectedMovie.getImageUrl()) + .centerCrop() + .error(R.drawable.default_background) + .into(new SimpleTarget(width, height) { + @Override + public void onResourceReady(GlideDrawable resource, + GlideAnimation + glideAnimation) { + Log.d(TAG, "details overview card image url ready: " + resource); + row.setImageDrawable(resource); + mAdapter.notifyArrayItemRangeChanged(0, mAdapter.size()); + } + }); + + ArrayObjectAdapter actionAdapter = new ArrayObjectAdapter(); + + actionAdapter.add( + new Action( + ACTION_WATCH_TRAILER, + getString(R.string.watch_trailer_1), + getString(R.string.watch_trailer_2))); + actionAdapter.add( + new Action( + ACTION_RENT, + getString(R.string.rent_1), + getString(R.string.rent_2))); + actionAdapter.add( + new Action( + ACTION_BUY, + getString(R.string.buy_1), + getString(R.string.buy_2))); + row.setActionsAdapter(actionAdapter); + + mAdapter.add(row); + } + + private void setupDetailsOverviewRowPresenter() { + FullWidthDetailsOverviewRowPresenter detailsPresenter = + new FullWidthDetailsOverviewRowPresenter(new DetailsDescriptionPresenter()); + detailsPresenter.setBackgroundColor( + ContextCompat.getColor(getActivity(), R.color.selected_background)); + + FullWidthDetailsOverviewSharedElementHelper sharedElementHelper = + new FullWidthDetailsOverviewSharedElementHelper(); + sharedElementHelper.setSharedElementEnterTransition( + getActivity(), DetailsActivity.SHARED_ELEMENT_NAME); + detailsPresenter.setListener(sharedElementHelper); + detailsPresenter.setParticipatingEntranceTransition(true); + + detailsPresenter.setOnActionClickedListener(new OnActionClickedListener() { + @Override + public void onActionClicked(Action action) { + if (action.getId() == ACTION_WATCH_TRAILER) { + Intent intent = new Intent(getActivity(), PlaybackActivity.class); + intent.putExtra(DetailsActivity.MOVIE, mSelectedMovie); + startActivity(intent); + } else { + Toast.makeText(getActivity(), action.toString(), Toast.LENGTH_SHORT).show(); + } + } + }); + mPresenterSelector.addClassPresenter(DetailsOverviewRow.class, detailsPresenter); + } + + private void setupRelatedMovieListRow() { + String subcategories[] = {getString(R.string.related_movies)}; +// List list = MovieList.getList(); +// +// Collections.shuffle(list); + ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter()); +// for (int j = 0; j < NUM_COLS; j++) { +// listRowAdapter.add(list.get(j % 5)); +// } + + HeaderItem header = new HeaderItem(0, subcategories[0]); + mAdapter.add(new ListRow(header, listRowAdapter)); + mPresenterSelector.addClassPresenter(ListRow.class, new ListRowPresenter()); + } + + public int convertDpToPixel(Context context, int dp) { + float density = context.getResources().getDisplayMetrics().density; + return Math.round((float) dp * density); + } + + private final class ItemViewClickedListener implements OnItemViewClickedListener { + @Override + public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, + RowPresenter.ViewHolder rowViewHolder, Row row) { + + if (item instanceof Movie) { + Log.d(TAG, "Item: " + item.toString()); + Intent intent = new Intent(getActivity(), DetailsActivity.class); + intent.putExtra(getString(R.string.movie), mSelectedMovie); + + Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation( + getActivity(), + ((ImageCardView) itemViewHolder.view).getMainImageView(), + DetailsActivity.SHARED_ELEMENT_NAME).toBundle(); + getActivity().startActivity(intent, bundle); + } + } + } +} diff --git a/app/src/tv/res/drawable/app_icon_your_company.png b/app/src/tv/res/drawable/app_icon_your_company.png new file mode 100644 index 0000000000000000000000000000000000000000..0a47b018cc8d6d966550f5d150d349b82924152e GIT binary patch literal 12416 zcmch8byOVB*6rZI-7P?H26qSq3j~4(cXtc!1cF-<+=7J=d~kOU?lRawaCe8V^Sf`| z_rA5hch`FVyjj!JO?7p5b=9dl=j^?SR8f+}L?c53fk2q=~2Wy~t)8h9~n>-ra{2n|6S{!2mZ&Re>dx&TK~PJ|3lIL&&L1r*8jK0$Hv&w9=YWi zM(a%`K)7j$T*xj{?aynYgj;k~1=bvn29BZQ|zPaPHM(%JZ_aA?z) z;I5V%r%o?PNpbYq8}!*D@&p;6%Vk8!*h;lQ-pH7%d}h)S<7W9>WXhviVXKD4ePd&Z zCpkD1e=s1#TI?NtpREP$`5XFtHk7v4>T$FD`eSHR2@1sDv~htkUGwQqDo~=lj3cZ+ zBF$G2ZJUH}p<#t@oQVdFpz#Xa1!`S18ox)hKiF#0r@%s)?Uz~CiatLn+t~Jc6Jf1_ zpBG`~`2N+=JLRCML85Wyba^Lh^sY1+uV{&pu{rXU3PT=fQyv z`9-Lf^q34%s2n@Q%Z)h)qz4Q(_Nylg0%MqARwn14`*MYMiR@5mVzE*z42acO z(%Ks{bX!Hs7QZ6A>^k?8psB1Mj6RKJmy1<-QRY)vimvIuE(A&K@ z>q#`+>24D_5E~PusghSU_c<>aOVqO*(F)oe){=EF;OuAP!LG?j{*@3HSt9q1jweTy z?&9ggZIy{bG++iw)doGgj;`%X-%#fHutvj_yQ}Nl-l1i1`4&65iET#~ zvckYhi{ZItiNW0QO(23veBlg@95bNd>6fZ$ zt4G>osMKEc;~JvJEI(F5);^Eg2~;B*z8DPml2sH#2WIm3@-f1cb=m$gtR`jbO~w9e z;?YkzDu@q(j^=_N>AGEH9g)8aX$5D0U*mqAfZ5*>!JhBQ@~~mthMyp%V;&<@I@`W} z9k+L<`ud7`mTi~V@T0CoWT6&2#o3j|>PN-)lVZL!6a2?*mAAz+UXF)qe|p2de`$Ql zha0Hh{OV=Puo5mMzi-E38@Cl%K*L2b-1YAFT^%n!Rl_%4?jjUBN7X*P-QH8D)LJ8d z$iIO(X~~KaFh@8=WOj${t}@;1!4;@JdfxMy`#X|~72ZAZ&9ux^_2vcg?E9 z6!z9{Qx68HitZFGhz7aWBdp+9f74`!SRc&Q2i? z0VXxAx!uueej&>8Y_g8fINJA$D31(E4x`c+KF#$*cH`{($ZT?ltNx8Tv+GceIRZB{ zwuJ6Tt7rujO#jnrv(NG1Ru0zT*eOv5&p=j+YQ_c8n9<3hW2W>JZ5i8E<|A? z)N*gmp`sy`kI5Z*W&~9V#0vkZYxDo}>gZ z76~4-_^npUS4!OZ-+}_a@jiNMt@Xwo!8z$oY&aneHlx8AYrg+6{f*qdJsqrBqwVnI zA91isd26FrDBdw``{2C0lmlhi-Rp$)X;L`aTL?YA>ZXlz!PCzT(7Is8{296XxaU(O z9bjM!AK-bV)^T}ui|vW27A!^*A`IbO7C7Ql8$@n((<#A&C+aw0m7m4?wu=4cO(;F6 z25OHW=BQXf`ZjB}x{TAmeIJ{bH~&|yl;+9T=+M*38=VOu_TfQLhVIcw#-~dmQi<|Y z6uBxxU!CBRP12h>3xf5^RnRNZ0LPF;f7>AY95m$fea87?Do&q z4jlDHmUNm+e65!(KTeHnxvZ^k^PY|;WVo*)UQP(iYTjRg#;#^I5^yr`Gcurw>+xe3 z9<{(3QJSvd=^68Wn@xen`h&9srx!!@zHOY0WD=0n+A<}{GZCQe8r2Oj)WzG87aWzl zGe=HxGb2@@Rdt9;w-VP9?ZgC?AHVOe_eib& z)?%m|YoRCJHB`knA0rI@uAMpNJersPtn=-fG&5UAweLY7_ zaVhKv^e_xc=$Dx{bJUTLVtXoYz}ogT=`*|;FQP<{TG40dA(r*m#^oc_88TPlENeSE zsdozGSQpyPzt^Q3mqQr}1&hR;p~ZWr&TUrj`vhE@#>y8fQ6ayNI>b+Y%E-hN@0b3Z zW6R16rtjPY+fou@ z7QULOeL)T8gf$WB>FPoXo2c}fO9f3m-WOpnl`r-Mff52yvE6&OIxh^uENmo~e!(A@ zTGbO651}ySQb*|~=5RYmQs%`DEUU}!}x35nh#h%pRSRe1DM@dQftE`Ojq%c3k+Pxk1U^7VH{7EHGY-ehe1myl*pjD?*r;opozEnD zkgGu4O{C?V; z7pt@mlLz-BVo`)PVT`JWFNPA2h}?7T&*N zlLX{TPG*Frew4SPSX!E?TcXAB`fPE(#}+X5ULS8p2=sek3#hjJN+5c?0zpP0*E_MX zW3?MvRe1Ns5~`hLk0E@eIk6l~%#pmYYPksy;USY+30CN$VEOM;m)L?8Q=xixS(Kr@sokID4D zmpd$F_^-|Lb+LO~A!fbvuc2Dfim2x=M-yZ$KE5+@(we4S$RXz)2FB;5W?`p#^y4{Z zxjeV2#usl&nR;>~XrSny1}J{0)t4W0e>()E%+;^8^?u*8nWzNQXvx)WZM&f!hl7AE zA=ov?-{;R-CFWkWa zM9WcDC3dH==~w}ick^_tY1I3(Datll*nsEa*tv4ZLvI&yMKuz4rdXEBWIa{xwPu{@ z>RM=gK3Gp&lygUHqUSFjk(GFk59a%Ix;`oTIUKv};CeTq*Xz!_@?ikcP=)Da=V8HX z+Lw0EdO+FOLpV!tX)zK-DJgFEyuFhow8Dy>PDG!>o%Q$7kw3U{Xn}VB@URylbX&#R z<0RpVQ~_(s>?;=$a#9dh2BnA{nQZGw@U(Q%bZf6Ex_0VE!L8nYIfJbH1uRgh95*dk zUz`D&cj`1b+M{aiVXOxPRu)I4%(qhGv63-9%*~HYdV3!sy;$Vlt6iR61JZc{zQEkV~*d^^Qx_jfFP?KXr4&HJNV3 zz^$3gh(m+%hL8XN*N|6YXDr^b_-g3c-(20=E@`--$}TqjIPdoZb9+a`@(*||Mecme zxTu~W_RxD7wVp$Ni47j;CN3vyctiJI#q_3-GG4r}d1U^~hV*Z$4{c-~K$}8IT#2t6 zkRp(wIOIN`l99%MOf(p~b~)BdRm@>~iBK+$qlWnFD3=G1EITaEudalMw1iT*=wc#D zd`coJk!Ct~#N$Z+5GpZ$wSO|@s+k-kw!H^zu^WZF<>hoF@zV>GT zXi@f8ei1!{kX_(S6)ot>pZ|!~&DKj2^ag>*mA<$Od$mMH!0|Io>w=ATX>4m2PJO(= zN*bD%=#@0>MZBUb(r&j!Kf4m%^Y7fqV@ov2f4c8RWaIvoTc@LHq!nIsaRH-tdJP~) zB*tc4zcjGDN6Lzrbm5kAUGv`*25FXkR?GeCQb#PMq@)nk)YQWGxT;hOVG{JfAXtyY z#Ra|14YKz3dgE$0zBXg1n`QA zigHRyR3GW{-sX1j0eNOaTUs_Jt!Yr~bX9}3P1BE0Ee5Mm;#rM5&+#u4ZUck@k*P8f z;TV{LG=d|bGweOZi7G`Oa6Yj7LWE;+3PP2qPeh|nlt((?Cr?jL|B{%<`vb?dQ9qR! zzY{?0fM(julj^R$uR#LK+Lo_%EtBDrvH0e;Om_=jwqa?P9CUL|y`N z)Do%GJxqp|-($E4OX0l_6KEko^D0bKlZPRN!lQXfmX?)ay12L?grzb?-JI39wP3^Q=cUGBrbCpGR)B&Pf{R?ySQ zC!=SCjN}}m+Y@U`?6$%#Awi|T;!+MQvx(o)q(dcP%hF@pdb%;MQ5Wb2cRh zgocP)Z>t}HmxzN{OJK;7^xJg-dUl4CqK4A~hZIui*D=aMR77T@|t!^jNLLgD3|BHZ2GLq$g&>&dy$1Oi&!*u`z|hby519gpM7 z4OY5aLG@ECg!A>@ua~fwV~zA*$D_oN80Vi>hgQ4D%0DEM-T&5TI`z-OkKZ$poBEYA zVGZ9sbnm+gG@A6T7&9E-ux(YQ5dZaZE;^sIYEPas*buD!TN#RYw$!Kw>IiZDi1Kje zb=Z`oo$xJ!m?LpRL{{mv12CT-Po6GRb>{TSX~o^r1>{Ck!lyr0BK=~yy`qP-XgOHt zPikut(H0hs=w5KFS`;#2dEqs5!#eI#om(mcNX1?7d+Z7{cIp-?`T1~p@@a!tw$f&z zUF?*ES1L-oQ?4kG-a~04sa;?e z-w$V3NWd-SeOwogCD$#$3(i5)?pH8OrE#)hz9X>qI4{4yj$YC*@LOy{Ai5LZgUy_LvULEjYTw3oDzF9EzF5hh}8KpA^QAu=3!TGdOSwN zgW~gm)LRUQN!^eW0n$FGBggT^I)GP~obhKApVQbx9kI`|lje7Pa&+?zQpJC+ZmawoG^B-Zm6Z_7l zo(A~enM6aP{n327KEMmNd5L7Q3*+WC^Ywf1m-3e z>EE>IN>_OzV@eoCz{*}-D0ZpKdt5h)B1LJZi6-?z3lERaNw zt=5p;>xrDk|Moesv6gVJ@Rs-6NoMHFeMyh|4w(`FtB6IKPbWczWTkRvV+^hhmF^u5 zLjZVSCK)owz5e)6(~2%ju2;#%OqD%0*illTF`tY@3LQ;5 z`)YU~5MQe$MXNu;$JXVn{`oGepx6}6+4G@oiGmNbtncH!V$t%Lx^~^V9Bbe2gor0H zr&+#mTq+>qGC*PqePX0S_lF;vJ3Fgwx{Gr$7(F>V3>n%C93wt03iW!AzmS6&a_07g&K`JGAC^bhbTK4nK7q-u${btGr z>I43IDSN2J0EF%TwCcM0FwZ2arypfCsxJuuto<7##v37ka|?FPvXV5MfQ#rzpjuZqIhEO8e?zq8yh?H=mz45d{QqPLRA8jN^Rpx z?Ht4}Wgx^d(8#NYC+|mKZAr4n@W}!&Lg=}b6y1xP-a*sdXY%(%`UeM{!`8|}rrv5h zjjvM}?<#miWGJjjR?L6Phr*j?tJ6Li>V`fWRD$f(@J^p%?O<-r&Kq^D4bwC2`*Sau zPWx-W+fG3}D0;5;kawFxV#{tOSMmkjpva|{b?FbC`{;$^_j)+AfBe7@!sq!&b%(4r zY?Af`Ua2cXZv5q*7w}O%k*~Idm$zV^Za60N@Y0LXY&pvH?!# zE6kcR?GYw}!P!(*#uX|F7c2Qm(TCn~0?wx%9W7Ltv($V!H0ot8jniOk_{rJw=%I%c zL9YooLP)1p+u`ORE(EUn+ry@$kmK+7y~lHGVL2*qRG1(ZnpjjK2NieFfAI)kB)9Hj z%>77Sz5D~KE2(ptzc18e0j1z!)wAZXR{koW1VPH0s6_0fJkOWEWubw*PEEbX_uW~> zd~*~v*=mIsUL?IVHj+Z8IFjgVCrd}r)oPoa6Jv*VgEIESqLIM& zfZ4%;D}$WLx^A@EkED+Muem!&jMbNi_(r^R`LA&7UqfWK$NKWkT5<{`bKm3{hX2a6 z|Dd7Oz3c4Nc3`pXh=g7qiD#JGck?qHzzRFKiU5@N2Pk^RmJ`fE`|{vwRzP8{;srMr z1NAe@{*lbc3l`z!Pfia3;H=KzE@FtL)d~9=isdE>=8r)-Ax!PpQ@KZ)MZ_plS$m3FjC))1}~%%kz&i9;#FptLtCUiB6|ukLO%{-E+yDaM!2Q(%yt{3QXO$ydQ|WI9 z5V0jY36b#z1pyRgU{Gx5 z4Ka=%W*=;nQG012R>Zv89;0vT?)At@vGl(>E|SF9Z9$R^{MsOWBpAl$P3kVZ`_VN5wRm$m0*M(FZo`NU zpg?qu{ZIknK}rFAa`F~6zL}Szuqqi}P4M%^yo2ZG7o35_u(KaGL8lcHZq#^+(i8~i zcSlvXH)Dj+K(xmcMQ6zAS~E?CsWvsKVx)zl>c-W=kk&Ha;oAJiAoL>&S-Ocw6z4C* zAEBfWA*hj3dpHti57GRzc|Ej1XF}@HPZLHTnyMR{IQ`+*2bQoo#02|svvkk0Vq^Zn z(S2?a8U39dEN?yUca0SX0lb^%0N>waN|T)V4MU>9)JySYu;i!Ja^26I?$1i`0Q=S8 ztNZif9$jPH{^A#v<-eQ7_y^k@Sx+~2%#G+4;% zIII@vju3YYR$@?$O)OOSgFmM*g-ZV5>RR2 zwb>HSYxI3Je4LV*4u&S@@>cX?%~>B!a;iL^YH^6Rm@)>0xxBQT%H81*VTFN@ov5*h zd4s=4co7Oa1BVTs+zn^h9iuH$-q^mQHv|_^%+zW5&P~7vwQA+ubAsQ;i@5SYn0b&I zzry5rch;O6Rc6fka{Gu{G}S`=F~NQlk#G##Zo(A zUTze~B%l|;L8uU1!JnyAJPE`(BMh)+Vi+$UzW%q)Hh>|k)=;$cO zLYF~zXb9$<(>V%4-vk_+P2Js)P77|u%-SRQoT-kQsBkI&!&nizS79Czz!Nw(=*wa9 z7z3D-QD#=i{$zrQN~KgdiSc7i#;_c~qzm=r<>ie}O>JOTq7oQ_#!dLoLn%4{&O{+) zwdhx~=rJAWc%=b(=-c|Ksyo8bf;)*rT?hWHLX)rR_F23&;UeP?xY z$LPqd5+H9#N07zJ|^WqH^xlSXXJEhP%?=EDWAYWKUu4zH4x@ zDB*F}&R~_w;FuW+F$nPvKcw@r%3~QjmW}Mdw~3B4AUIz8;h?6w^GU`TfhX1~EEP z>}Ob^)|hPT zAf8+0$g^%@c#^+vQneYMmnc8lE(}=|vTZ%yFON30@g|$FNWDwr<}A}jg*9oOws|$J zPU#id#Xx*48kuptAs>E;I-UyDtZVLydOJM#_V2Ijm?dw4#+S>10h|2?;2|vUJr%d(wws$RnLh zSMm&a?Pha6oVu4j^($pM9ZM!^hESsjrTH2Z7jpKlJFnlatFigpq122$Qbw;l;EY(J zC(6@5c`T!SF41W4+%}PZ{mfu$=!G6AWQ5#dG>>PW5zBxc@VJY_wt9zqG1eManzGvI z;k17Vdmxp_4)0}N-$#ivTVy?!d_1s|dl-V=c&l=O)_x8reu=m6i~>|YXymKVScrIA zTq5!Fr)w5FI8X*R{IO@BUV*Scc_8-GFw@w(WA+quy(ZAx(YbRh9vey0AV5YE+QM zQwV+{=TYZ+%_6$v+IaNX3fC^6=kWK^wq|E6eF@!ywtn3zcNC?et5y5T^##|01|e3k z@o!EebPUi|-`i_Dod@^Fneh^ho5S7c`QA`@Mvox44us9Jo5+Q*zw0|OasTfg8Z{;Y zAV`|DzuRDLIq(o!TwH`ymy^Z|T%Giy9el$#EvD*H39ea-2J- zzk8#CPQHb6fr_Fg8F`k100L(EIvt^Fh|zHSHvh7$YA@&nK1eWY+jp%-03+BzoyzbD z=R8xN9T8QmVYz`Hj0%XCOalCPx#_xaDyC@VHx`b~zz#Ij`)*$zuQjZRu5Y`$>&I^W zlcxZJZ9zMkARe94;AZrz`UB4UFc3meKe_KoJUc5|7rAqiW@ya73D@oV;lTk(mxHjX zsw$U>k^SneMq6=@F}RAgkzSJExm%B8k6;6?Gew3-Md8U(dt`{1OiZT0KUiGp7bp3F&>%5yEoKT(i^-_VW$V zvU+?PT_<#@nj^mr*-pzDBS0neLo*}+d??ZXY1RiFe08;JdG5e7t)1({w=Wg@wAL(4 zgC*j`9;8{&HJXkLXnHz}X0fI((kD!+^KZ%(2cj_gk8t7%Z8bnfDK@MAl@aWT?w6)- z-@meymd*K5f)dzjDOAHk_?=BOcB72aPfDo{g#INHjd%K1DkkD>!Kk@};|D}EHT;t+ z&M}_o8K0)}QYEgPOYGs_aG;jC!iUwlU{ro^@^jM@EJuR{!p4P#rP|Yp1BR&Np3SuE zf`SBw9)_kwYhO|8CZWYsiGDK5ZX|H_H4-oZvJiFuCRUWiXU?P`nLd&+G5)J;$9*?n zMxe84#resexxx78v6W2rw!MMbg8c#;FB$snF9wT1+q-mR_|!iAlKCgZTp&D<66*Fm5_Hugi$Y{iw|-Ts$gXB5;rmHT#X zW-42Wj>;{M)L_KsiZ2`J0LGk+zTIK&xz-jR|K^XLOB5oBRE+iT!@4R*V-JpZekvK_%ZdXqxmN`x->A6)_ft&mF4gU>%pc?;>V@<*IqfPXz)@ z?{|Z0M22Pr?1QIQYrfr^hl2~;tSG?p<(w2`r;dNRf@ya$oK6o4T6T|>pk|%EnbSQM z3roKmA~qJVD3AodxEW@F=+Xr1S;Kgd!4hAbiuVEvYC7iE?@c^6g z9)R+vv)z=G@?A>9NbTj6yAI}=4vt;9D*!J_d`)pEVAlU;LpB=U6gL<3_0#4t1BMs_ z9}fc>_0os&;{XRDR>xHrh*9sFug0a9u_jhPy(MbEHYOWr2eSCGNgpTiH*QQI>1#M5 zQ#K{T*Nyp2RPEw=UE+O(=0RLN5q&+KeVW6!_xyg|O6-5(ip*3wY1?T4k(s_qcd2Q_ z1VUL;Uk#}@I=(Je0_7`B_~QD)#6WuTLaSulxX#qdfiA#%&u=`rS34%~vP|h>`M6R_ zhDlDbr=`KVXTSE>A>T&UZ{dgaKi<43MO6=do%kvpu6b2&Re+_ESZ~M<(ppR<GN;Diu>bjt@x18QA`Hi~prj|F_iq zf6LbYd(nSVpZ_Cn|F`=5e-v!A0G+y;lFEH@7+HCwC2|M V(Xagh_+J6gI~gVEa!KRh{{r^yYhC~V literal 0 HcmV?d00001 diff --git a/app/src/tv/res/drawable/default_background.xml b/app/src/tv/res/drawable/default_background.xml new file mode 100644 index 00000000000..82e51a2b328 --- /dev/null +++ b/app/src/tv/res/drawable/default_background.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/tv/res/drawable/movie.png b/app/src/tv/res/drawable/movie.png new file mode 100644 index 0000000000000000000000000000000000000000..cb5cb6d390be45afc839df3db089ed29c9de5dd2 GIT binary patch literal 13007 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 + diff --git a/app/src/tv/res/layout/activity_main.xml b/app/src/tv/res/layout/activity_main.xml new file mode 100644 index 00000000000..7b3b709b42a --- /dev/null +++ b/app/src/tv/res/layout/activity_main.xml @@ -0,0 +1,10 @@ + + diff --git a/app/src/tv/res/values/colors.xml b/app/src/tv/res/values/colors.xml new file mode 100644 index 00000000000..d4aba647376 --- /dev/null +++ b/app/src/tv/res/values/colors.xml @@ -0,0 +1,8 @@ + + #000000 + #DDDDDD + #CD322E + #ffaa3f + #ffaa3f + #3d3d3d + diff --git a/app/src/tv/res/values/strings.xml b/app/src/tv/res/values/strings.xml new file mode 100644 index 00000000000..8211599bab8 --- /dev/null +++ b/app/src/tv/res/values/strings.xml @@ -0,0 +1,23 @@ + + NewPipe + NewPipe + Related Videos + Grid View + Error Fragment + Personal Settings + Watch trailer + FREE + Rent By Day + From $1.99 + Buy and Own + AT $9.99 + Movie + shouldStart + + + Media loading timed out + Media server was not reachable + Failed to load video + An error occurred + Dismiss + diff --git a/build.gradle b/build.gradle index 5c494fe5951..c0c46fdc2df 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.android.tools.build:gradle:3.0.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 74bb77845e0..8563dbd51af 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Tue Oct 17 10:17:24 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-all.zip From 881e39dd4446d8e1fa12eed627337e77a2f98397 Mon Sep 17 00:00:00 2001 From: Schabi Date: Sun, 24 Dec 2017 21:05:17 +0100 Subject: [PATCH 2/3] fix build and travis script --- .travis.yml | 3 ++- app/build.gradle | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index fcdfeb9b23c..55703d4a016 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,8 @@ android: before_install: - yes | sdkmanager "platforms;android-27" -script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest +script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleNormalDebug lintNormalDebug testNormalDebugUnitTest && \ + ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleTvDebug lintTvDebug testTvDebugUnitTest licenses: - '.+' diff --git a/app/build.gradle b/app/build.gradle index 72f972c9194..cac26454d7b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,9 +76,9 @@ dependencies { implementation "com.android.support:recyclerview-v7:$supportLibVersion" implementation "com.android.support:preference-v14:$supportLibVersion" - tvCompile "com.android.support:leanback-v17:$supportLibVersion" - tvCompile 'com.android.support.constraint:constraint-layout:1.0.2' - tvCompile 'com.github.bumptech.glide:glide:3.8.0' + tvImplementation "com.android.support:leanback-v17:$supportLibVersion" + tvImplementation 'com.android.support.constraint:constraint-layout:1.0.2' + tvImplementation 'com.github.bumptech.glide:glide:3.8.0' implementation 'com.google.code.gson:gson:2.8.2' implementation 'ch.acra:acra:4.9.2' From 7c7d81cd579cbd4bf02efafe9d4751827887406c Mon Sep 17 00:00:00 2001 From: Schabi Date: Sun, 24 Dec 2017 21:08:38 +0100 Subject: [PATCH 3/3] trying to fix travis script second try --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 55703d4a016..22acd4050a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,8 +12,9 @@ android: before_install: - yes | sdkmanager "platforms;android-27" -script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleNormalDebug lintNormalDebug testNormalDebugUnitTest && \ - ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleTvDebug lintTvDebug testTvDebugUnitTest +script: + - ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleNormalDebug lintNormalDebug testNormalDebugUnitTest + - ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleTvDebug lintTvDebug testTvDebugUnitTest licenses: - '.+'