1
0
mirror of https://github.com/TeamNewPipe/NewPipe.git synced 2024-11-21 18:42:35 +01:00

Merge remote-tracking branch 'upstream/master'

Conflicts:
	app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java -- still used class-based passing
	app/src/main/java/org/schabi/newpipe/VideoItemListActivity.java -- just some whitespace
This commit is contained in:
Adam Howard 2015-11-02 21:29:43 +00:00
commit c32c267889
15 changed files with 226 additions and 69 deletions

View File

@ -2,7 +2,7 @@
[![Translation Status](https://hosted.weblate.org/widgets/NewPipe/-/svg-badge.svg)](https://hosted.weblate.org/engage/NewPipe/)
[![NewPipe](https://f-droid.org/repo/icons/org.schabi.newpipe.4.png)](http://dasochan.nl/newpipe/)
[![NewPipe](https://f-droid.org/repo/icons/org.schabi.newpipe.5.png)](http://dasochan.nl/newpipe/)
NewPipe: A free lightweight Youtube frontend for Android.
[![F-Droid](https://f-droid.org/wiki/images/0/06/F-Droid-button_get-it-on.png)](https://f-droid.org/repository/browse/?fdfilter=newpipe&fdid=org.schabi.newpipe)
@ -21,10 +21,10 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
* Download videos (working, but it could be better)
* Download audio only (working but, but it could be better)
* Open a video in Kodi
* Show Next/Related videos
## Coming Features
* Shows Next/Related videos
* Improved Downloading
* Bookmarks
* View history

View File

@ -23,7 +23,7 @@ dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.android.support:support-v4:23.1.0'
compile 'com.android.support:design:23.1.0'
compile 'org.jsoup:jsoup:1.8.3'
compile 'org.mozilla:rhino:1.7.7'
compile 'com.android.support:design:23.1.0'
}

View File

@ -69,6 +69,7 @@ public class ActionBarHandler {
public void setStreams(VideoInfo.VideoStream[] videoStreams, VideoInfo.AudioStream[] audioStreams) {
this.videoStreams = videoStreams;
selectedStream = 0;
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
String[] itemArray = new String[videoStreams.length];
String defaultResolution = defaultPreferences
.getString(activity.getString(R.string.defaultResolutionPreference),
@ -93,7 +94,7 @@ public class ActionBarHandler {
// set audioStream
audioStream = null;
String preferedFormat = PreferenceManager.getDefaultSharedPreferences(activity)
String preferedFormat = defaultPreferences
.getString(activity.getString(R.string.defaultAudioFormatPreference), "webm");
if(preferedFormat.equals("webm")) {
for(VideoInfo.AudioStream s : audioStreams) {

View File

@ -143,6 +143,6 @@ public class VideoInfo {
public VideoStream[] videoStreams = null;
public AudioStream[] audioStreams = null;
public VideoInfoItem nextVideo = null;
public Vector<VideoInfoItem> relatedVideos = null;
public VideoInfoItem[] relatedVideos = null;
public int videoAvailableStatus = VIDEO_AVAILABLE;
}

View File

@ -1,6 +1,8 @@
package org.schabi.newpipe;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by Christian Schabesberger on 26.08.15.
@ -22,7 +24,7 @@ import android.graphics.Bitmap;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class VideoInfoItem {
public class VideoInfoItem implements Parcelable {
public String id = "";
public String title = "";
public String uploader = "";
@ -32,4 +34,51 @@ public class VideoInfoItem {
public String webpage_url = "";
public String upload_date = "";
public String view_count = "";
protected VideoInfoItem(Parcel in) {
id = in.readString();
title = in.readString();
uploader = in.readString();
duration = in.readString();
thumbnail_url = in.readString();
thumbnail = (Bitmap) in.readValue(Bitmap.class.getClassLoader());
webpage_url = in.readString();
upload_date = in.readString();
view_count = in.readString();
}
public VideoInfoItem() {
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(title);
dest.writeString(uploader);
dest.writeString(duration);
dest.writeString(thumbnail_url);
dest.writeValue(thumbnail);
dest.writeString(webpage_url);
dest.writeString(upload_date);
dest.writeString(view_count);
}
@SuppressWarnings("unused")
public static final Parcelable.Creator<VideoInfoItem> CREATOR = new Parcelable.Creator<VideoInfoItem>() {
@Override
public VideoInfoItem createFromParcel(Parcel in) {
return new VideoInfoItem(in);
}
@Override
public VideoInfoItem[] newArray(int size) {
return new VideoInfoItem[size];
}
};
}

View File

@ -38,7 +38,6 @@ public class VideoItemDetailActivity extends AppCompatActivity {
private String videoUrl;
private int currentStreamingService = -1;
private Menu menu = null;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

View File

@ -81,6 +81,7 @@ public class VideoItemDetailFragment extends Fragment {
private Handler h = new Handler();
private Extractor extractor;
private String videoUrl;
public ExtractorRunnable(String videoUrl, Extractor extractor, VideoItemDetailFragment f) {
this.extractor = extractor;
this.videoUrl = videoUrl;
@ -95,18 +96,21 @@ public class VideoItemDetailFragment extends Fragment {
BitmapFactory.decodeStream(
new URL(videoInfo.thumbnail_url)
.openConnection()
.getInputStream()), SetThumbnailRunnable.VIDEO_THUMBNAIL));
.getInputStream()),
SetThumbnailRunnable.VIDEO_THUMBNAIL));
h.post(new SetThumbnailRunnable(
BitmapFactory.decodeStream(
new URL(videoInfo.uploader_thumbnail_url)
.openConnection()
.getInputStream()), SetThumbnailRunnable.CHANNEL_THUMBNAIL));
.getInputStream()),
SetThumbnailRunnable.CHANNEL_THUMBNAIL));
if(showNextVideoItem) {
h.post(new SetThumbnailRunnable(
BitmapFactory.decodeStream(
new URL(videoInfo.nextVideo.thumbnail_url)
.openConnection()
.getInputStream()), SetThumbnailRunnable.NEXT_VIDEO_THUMBNAIL));
.getInputStream()),
SetThumbnailRunnable.NEXT_VIDEO_THUMBNAIL));
}
}
} catch (Exception e) {
@ -175,29 +179,28 @@ public class VideoItemDetailFragment extends Fragment {
}
public void updateInfo(VideoInfo info) {
Activity a = getActivity();
currentVideoInfo = info;
try {
VideoInfoItemViewCreator videoItemViewCreator =
new VideoInfoItemViewCreator(LayoutInflater.from(getActivity()));
ScrollView contentMainView = (ScrollView) a.findViewById(R.id.detailMainContent);
ProgressBar progressBar = (ProgressBar) a.findViewById(R.id.detailProgressBar);
TextView videoTitleView = (TextView) a.findViewById(R.id.detailVideoTitleView);
TextView uploaderView = (TextView) a.findViewById(R.id.detailUploaderView);
TextView viewCountView = (TextView) a.findViewById(R.id.detailViewCountView);
TextView thumbsUpView = (TextView) a.findViewById(R.id.detailThumbsUpCountView);
TextView thumbsDownView = (TextView) a.findViewById(R.id.detailThumbsDownCountView);
TextView uploadDateView = (TextView) a.findViewById(R.id.detailUploadDateView);
TextView descriptionView = (TextView) a.findViewById(R.id.detailDescriptionView);
ImageView thumbnailView = (ImageView) a.findViewById(R.id.detailThumbnailView);
FrameLayout nextVideoFrame = (FrameLayout) a.findViewById(R.id.detailNextVideoFrame);
ScrollView contentMainView = (ScrollView) activity.findViewById(R.id.detailMainContent);
ProgressBar progressBar = (ProgressBar) activity.findViewById(R.id.detailProgressBar);
TextView videoTitleView = (TextView) activity.findViewById(R.id.detailVideoTitleView);
TextView uploaderView = (TextView) activity.findViewById(R.id.detailUploaderView);
TextView viewCountView = (TextView) activity.findViewById(R.id.detailViewCountView);
TextView thumbsUpView = (TextView) activity.findViewById(R.id.detailThumbsUpCountView);
TextView thumbsDownView = (TextView) activity.findViewById(R.id.detailThumbsDownCountView);
TextView uploadDateView = (TextView) activity.findViewById(R.id.detailUploadDateView);
TextView descriptionView = (TextView) activity.findViewById(R.id.detailDescriptionView);
ImageView thumbnailView = (ImageView) activity.findViewById(R.id.detailThumbnailView);
FrameLayout nextVideoFrame = (FrameLayout) activity.findViewById(R.id.detailNextVideoFrame);
RelativeLayout nextVideoRootFrame =
(RelativeLayout) a.findViewById(R.id.detailNextVideoRootLayout);
(RelativeLayout) activity.findViewById(R.id.detailNextVideoRootLayout);
View nextVideoView = videoItemViewCreator
.getViewByVideoInfoItem(null, nextVideoFrame, info.nextVideo);
nextVideoFrame.addView(nextVideoView);
Button nextVideoButton = (Button) a.findViewById(R.id.detailNextVideoButton);
Button nextVideoButton = (Button) activity.findViewById(R.id.detailNextVideoButton);
contentMainView.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
@ -209,10 +212,12 @@ public class VideoItemDetailFragment extends Fragment {
case VideoInfo.VIDEO_AVAILABLE: {
videoTitleView.setText(info.title);
uploaderView.setText(info.uploader);
viewCountView.setText(info.view_count + " " + a.getString(R.string.viewSufix));
viewCountView.setText(info.view_count
+ " " + activity.getString(R.string.viewSufix));
thumbsUpView.setText(info.like_count);
thumbsDownView.setText(info.dislike_count);
uploadDateView.setText(a.getString(R.string.uploadDatePrefix) + " " + info.upload_date);
uploadDateView.setText(
activity.getString(R.string.uploadDatePrefix) + " " + info.upload_date);
descriptionView.setText(Html.fromHtml(info.description));
descriptionView.setMovementMethod(LinkMovementMethod.getInstance());
@ -235,9 +240,12 @@ public class VideoItemDetailFragment extends Fragment {
nextVideoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent detailIntent = new Intent(getActivity(), VideoItemDetailActivity.class);
detailIntent.putExtra(VideoItemDetailFragment.ARG_ITEM_ID, currentVideoInfo.nextVideo.id);
detailIntent.putExtra(VideoItemDetailFragment.VIDEO_URL, currentVideoInfo.nextVideo.webpage_url);
Intent detailIntent =
new Intent(getActivity(), VideoItemDetailActivity.class);
detailIntent.putExtra(
VideoItemDetailFragment.ARG_ITEM_ID, currentVideoInfo.nextVideo.id);
detailIntent.putExtra(
VideoItemDetailFragment.VIDEO_URL, currentVideoInfo.nextVideo.webpage_url);
//todo: make id dynamic the following line is crap
detailIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, 0);
startActivity(detailIntent);
@ -245,10 +253,12 @@ public class VideoItemDetailFragment extends Fragment {
});
break;
case VideoInfo.VIDEO_UNAVAILABLE_GEMA:
thumbnailView.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.gruese_die_gema_unangebracht));
thumbnailView.setImageBitmap(BitmapFactory.decodeResource(
getResources(), R.drawable.gruese_die_gema_unangebracht));
break;
case VideoInfo.VIDEO_UNAVAILABLE:
thumbnailView.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.not_available_monkey));
thumbnailView.setImageBitmap(BitmapFactory.decodeResource(
getResources(), R.drawable.not_available_monkey));
break;
default:
Log.e(TAG, "Video Available Status not known.");
@ -303,15 +313,19 @@ public class VideoItemDetailFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceBundle) {
super.onActivityCreated(savedInstanceBundle);
FloatingActionButton playVideoButton = (FloatingActionButton) getActivity().findViewById(R.id.playVideoButton);
FloatingActionButton playVideoButton =
(FloatingActionButton) getActivity().findViewById(R.id.playVideoButton);
// Sometimes when this fragment is not visible it still gets initiated
// then we must not try to access objects of this fragment.
// Otherwise the applications would crash.
if(playVideoButton != null) {
try {
StreamingService streamingService = ServiceList.getService(
getArguments().getInt(STREAMING_SERVICE));
extractorThread = new Thread(new ExtractorRunnable(
getArguments().getString(VIDEO_URL), streamingService.getExtractorInstance(), this));
autoPlayEnabled = getArguments().getBoolean(AUTO_PLAY);
extractorThread.start();
} catch (Exception e) {
@ -320,13 +334,15 @@ public class VideoItemDetailFragment extends Fragment {
if (PreferenceManager.getDefaultSharedPreferences(getActivity())
.getBoolean(getString(R.string.leftHandLayout), false) && checkIfLandscape()) {
RelativeLayout.LayoutParams oldLayout = (RelativeLayout.LayoutParams) playVideoButton.getLayoutParams();
RelativeLayout.LayoutParams oldLayout =
(RelativeLayout.LayoutParams) playVideoButton.getLayoutParams();
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
layoutParams.setMargins(oldLayout.leftMargin, oldLayout.topMargin, oldLayout.rightMargin, oldLayout.bottomMargin);
layoutParams.setMargins(oldLayout.leftMargin, oldLayout.topMargin,
oldLayout.rightMargin, oldLayout.bottomMargin);
playVideoButton.setLayoutParams(layoutParams);
}
@ -336,6 +352,16 @@ public class VideoItemDetailFragment extends Fragment {
actionBarHandler.playVideo();
}
});
Button similarVideosButton = (Button) activity.findViewById(R.id.detailShowSimilarButton);
similarVideosButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, VideoItemListActivity.class);
intent.putExtra(VideoItemListActivity.VIDEO_INFO_ITEMS, currentVideoInfo.relatedVideos);
activity.startActivity(intent);
}
});
}
}

View File

@ -3,8 +3,12 @@ package org.schabi.newpipe;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.app.NavUtils;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@ -12,6 +16,8 @@ import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImageView;
import java.util.Arrays;
/**
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
* VideoItemListActivity.java is part of NewPipe.
@ -34,9 +40,19 @@ public class VideoItemListActivity extends AppCompatActivity
implements VideoItemListFragment.Callbacks {
private static final String TAG = VideoItemListFragment.class.toString();
// arguments to give to this activity
public static final String VIDEO_INFO_ITEMS = "video_info_items";
// savedInstanceBundle arguments
private static final String QUERY = "query";
private static final String STREAMING_SERVICE = "streaming_service";
// activity modes
private static final int SEARCH_MODE = 0;
private static final int PRESENT_VIDEOS_MODE = 1;
private int mode = SEARCH_MODE;
private int currentStreamingServiceId = -1;
private String searchQuery = "";
@ -95,7 +111,22 @@ public class VideoItemListActivity extends AppCompatActivity
.findFragmentById(R.id.videoitem_list);
listFragment.setStreamingService(ServiceList.getService(currentStreamingServiceId));
if(savedInstanceState != null) {
Bundle arguments = getIntent().getExtras();
if(arguments != null) {
Parcelable[] p = arguments.getParcelableArray(VIDEO_INFO_ITEMS);
if(p != null) {
mode = PRESENT_VIDEOS_MODE;
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//todo: make this more efficient
listFragment.present(Arrays.copyOf(p, p.length, VideoInfoItem[].class));
}
}
if(savedInstanceState != null
&& mode != PRESENT_VIDEOS_MODE) {
searchQuery = savedInstanceState.getString(QUERY);
currentStreamingServiceId = savedInstanceState.getInt(STREAMING_SERVICE);
if(!searchQuery.isEmpty()) {
@ -118,15 +149,18 @@ public class VideoItemListActivity extends AppCompatActivity
.setActivateOnItemClick(true);
SearchView searchView = (SearchView)findViewById(R.id.searchViewTablet);
// Somehow the seticonifiedbydefault property set by the layout xml is not working on
// the support version on SearchView, so it needs to be set programmatically.
searchView.setIconifiedByDefault(false);
searchView.setIconified(false);
searchView.setOnQueryTextListener(new SearchVideoQueryListener());
if(mode != PRESENT_VIDEOS_MODE) {
// Somehow the seticonifiedbydefault property set by the layout xml is not working on
// the support version on SearchView, so it needs to be set programmatically.
searchView.setIconifiedByDefault(false);
searchView.setIconified(false);
searchView.setOnQueryTextListener(new SearchVideoQueryListener());
} else {
searchView.setVisibility(View.GONE);
}
}
SettingsActivity.initSettings(this);
}
/**
@ -176,7 +210,8 @@ public class VideoItemListActivity extends AppCompatActivity
super.onCreateOptionsMenu(menu);
this.menu = menu;
MenuInflater inflater = getMenuInflater();
if(findViewById(R.id.videoitem_detail_container) == null) {
if(mode != PRESENT_VIDEOS_MODE &&
findViewById(R.id.videoitem_detail_container) == null) {
inflater.inflate(R.menu.videoitem_list, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) searchItem.getActionView();
@ -196,14 +231,23 @@ public class VideoItemListActivity extends AppCompatActivity
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if(id == R.id.action_settings) {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
} else {
return videoFragment.onOptionsItemSelected(item) ||
switch(id) {
case android.R.id.home: {
Intent intent = new Intent(this, VideoItemListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
NavUtils.navigateUpTo(this, intent);
return true;
}
case R.id.action_settings: {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
return true;
}
default:
return videoFragment.onOptionsItemSelected(item) ||
super.onOptionsItemSelected(item);
}
return true;
}
@Override

View File

@ -13,6 +13,7 @@ import android.widget.ListView;
import android.widget.Toast;
import java.net.URL;
import java.util.Arrays;
import java.util.Vector;
@ -41,6 +42,11 @@ public class VideoItemListFragment extends ListFragment {
private StreamingService streamingService = null;
private VideoListAdapter videoListAdapter;
// activity modes
private static final int SEARCH_MODE = 0;
private static final int PRESENT_VIDEOS_MODE = 1;
private int mode = SEARCH_MODE;
private String query = "";
private int lastPage = 0;
@ -50,6 +56,7 @@ public class VideoItemListFragment extends ListFragment {
private LoadThumbsRunnable loadThumbsRunnable = null;
// used to track down if results posted by threads ar still valid
private int currentRequestId = -1;
private ListView list;
private class ResultRunnable implements Runnable {
private SearchEngine.Result result;
@ -154,7 +161,18 @@ public class VideoItemListFragment extends ListFragment {
}
}
public void present(VideoInfoItem[] videoList) {
mode = PRESENT_VIDEOS_MODE;
setListShown(true);
getListView().smoothScrollToPosition(0);
// inefficient like hell i know (welcome to the world of java)
//todo: make this more efficient
updateList(new Vector<>(Arrays.asList(videoList)));
}
public void search(String query) {
mode = SEARCH_MODE;
this.query = query;
this.lastPage = 1;
videoListAdapter.clearVideoList();
@ -261,6 +279,7 @@ public class VideoItemListFragment extends ListFragment {
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
list = getListView();
videoListAdapter = new VideoListAdapter(getActivity(), this);
setListAdapter(videoListAdapter);
@ -272,8 +291,6 @@ public class VideoItemListFragment extends ListFragment {
}
getListView().setOnScrollListener(new AbsListView.OnScrollListener() {
private static final float OVERSCROLL_THRESHOLD_IN_PIXELS = 100;
private float downY;
long lastScrollDate = 0;
@Override
@ -282,8 +299,8 @@ public class VideoItemListFragment extends ListFragment {
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
ListView list = getListView();
if (list.getChildAt(0) != null
if (mode != PRESENT_VIDEOS_MODE
&& list.getChildAt(0) != null
&& list.getLastVisiblePosition() == list.getAdapter().getCount() - 1
&& list.getChildAt(list.getChildCount() - 1).getBottom() <= list.getHeight()) {
long time = System.currentTimeMillis();

View File

@ -233,10 +233,8 @@ public class YoutubeExtractor implements Extractor {
resolveResolutionString(itag)));
}
}
videoInfo.videoStreams = new VideoInfo.VideoStream[videoStreams.size()];
for(int i = 0; i < videoStreams.size(); i++) {
videoInfo.videoStreams[i] = videoStreams.get(i);
}
videoInfo.videoStreams =
videoStreams.toArray(new VideoInfo.VideoStream[videoStreams.size()]);
} catch (Exception e) {
e.printStackTrace();
@ -310,15 +308,15 @@ public class YoutubeExtractor implements Extractor {
int i = 0;
// related videos
videoInfo.relatedVideos = new Vector<>();
Vector<VideoInfoItem> relatedVideos = new Vector<>();
for(Element li : doc.select("ul[id=\"watch-related\"]").first().children()) {
// first check if we have a playlist. If so leave them out
if(li.select("a[class*=\"content-link\"]").first() != null) {
videoInfo.relatedVideos.add(extractVideoInfoItem(li));
relatedVideos.add(extractVideoInfoItem(li));
i++;
}
}
videoInfo.relatedVideos = relatedVideos.toArray(new VideoInfoItem[relatedVideos.size()]);
return videoInfo;
}
@ -390,11 +388,7 @@ public class YoutubeExtractor implements Extractor {
} catch(Exception e) {
e.printStackTrace();
}
VideoInfo.AudioStream[] output = new VideoInfo.AudioStream[audioStreams.size()];
for(int i = 0; i < output.length; i++) {
output[i] = audioStreams.get(i);
}
return output;
return audioStreams.toArray(new VideoInfo.AudioStream[audioStreams.size()]);
}
private VideoInfoItem extractVideoInfoItem(Element li) {

View File

@ -188,10 +188,18 @@
android:layout_alignParentLeft="true" />
</RelativeLayout>
<Button android:id="@+id/detailShowSimilarButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:layout_below="@id/detailNextVideoRootLayout"
android:text="@string/showSimilarVideosButtonText"/>
<View
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_below="@id/detailNextVideoRootLayout"/>
android:layout_below="@id/detailShowSimilarButton"/>
</RelativeLayout>
</ScrollView>

View File

@ -188,10 +188,18 @@
android:layout_alignParentLeft="true" />
</RelativeLayout>
<Button android:id="@+id/detailShowSimilarButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:layout_below="@id/detailNextVideoRootLayout"
android:text="@string/showSimilarVideosButtonText"/>
<View
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_below="@id/detailNextVideoRootLayout"/>
android:layout_below="@id/detailShowSimilarButton"/>
</RelativeLayout>
</ScrollView>

View File

@ -185,13 +185,22 @@
android:layout_alignParentLeft="true" />
</RelativeLayout>
<Button android:id="@+id/detailShowSimilarButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:layout_below="@id/detailNextVideoRootLayout"
android:text="@string/showSimilarVideosButtonText"/>
<View
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_below="@id/detailNextVideoRootLayout"/>
android:layout_below="@id/detailShowSimilarButton"/>
</RelativeLayout>
</ScrollView>
<android.support.design.widget.FloatingActionButton
android:id="@+id/playVideoButton"
android:layout_alignParentBottom="true"

View File

@ -48,4 +48,5 @@
<string name="nextVideoTitle">Nächstes Video</string>
<string name="showNextVideoTitle">Zeige \"Nächstes Video\" Auswahl.</string>
<string name="urlNotSupportedText">Url wird nicht unterstützt.</string>
</resources>
<string name="showSimilarVideosButtonText">Ähnliche Videos</string>
</resources>

View File

@ -48,4 +48,5 @@
<string name="nextVideoTitle">Next Video</string>
<string name="showNextVideoTitle">Show \"Next video\" item.</string>
<string name="urlNotSupportedText">Url not Supported.</string>
<string name="showSimilarVideosButtonText">Similar Videos</string>
</resources>