1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2024-12-23 16:40:32 +00:00

add basics

This commit is contained in:
Christian Schabesberger 2016-07-26 13:50:52 +02:00
parent c5583bd77b
commit 9a0f61e60b
26 changed files with 528 additions and 103 deletions

View File

@ -80,10 +80,12 @@
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/VideoPlayerTheme"
tools:ignore="UnusedAttribute" />
<service
android:name=".player.BackgroundPlayer"
android:exported="false"
android:label="@string/background_player_name" />
<activity
android:name=".player.ExoPlayerActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
@ -102,10 +104,12 @@
<data android:scheme="file" />
</intent-filter>
</activity>
<service
android:name=".player.BackgroundPlayer"
android:label="@string/background_player_name"
android:exported="false" />
android:exported="false"
android:label="@string/background_player_name" />
<activity
android:name=".SettingsActivity"
android:label="@string/settings_activity_title" />
@ -130,19 +134,20 @@
<activity
android:name=".download.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:launchMode="singleTask">
</activity>
android:launchMode="singleTask"
android:theme="@style/AppTheme"></activity>
<service
android:name="us.shandian.giga.service.DownloadManagerService"/>
<service android:name="us.shandian.giga.service.DownloadManagerService" />
<activity
android:name="com.nononsenseapps.filepicker.FilePickerActivity"
android:label="@string/app_name"
android:theme="@style/FilePickerTheme"
android:launchMode="singleTask">
</activity>
android:launchMode="singleTask"
android:theme="@style/FilePickerTheme"></activity>
<activity
android:name=".ChannelActivity"
android:label="@string/title_activity_channel"
android:theme="@style/AppTheme.NoActionBar"></activity>
</application>
</manifest>

View File

@ -0,0 +1,28 @@
package org.schabi.newpipe;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
public class ChannelActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_channel);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
}

View File

@ -85,7 +85,7 @@ public class VideoItemDetailActivity extends AppCompatActivity {
.show();
}
//arguments.putString(VideoItemDetailFragment.VIDEO_URL,
// videoExtractor.getVideoUrl(videoExtractor.getVideoId(videoUrl)));//cleans URL
// videoExtractor.getUrl(videoExtractor.getId(videoUrl)));//cleans URL
arguments.putString(VideoItemDetailFragment.VIDEO_URL, videoUrl);
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY,

View File

@ -50,6 +50,7 @@ import java.util.Vector;
import org.schabi.newpipe.download.DownloadDialog;
import org.schabi.newpipe.extractor.AudioStream;
import org.schabi.newpipe.extractor.ChannelExtractor;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.ParsingException;
import org.schabi.newpipe.extractor.ServiceList;
@ -306,6 +307,7 @@ public class VideoItemDetailFragment extends Fragment {
activity.findViewById(R.id.detailVideoThumbnailWindowBackgroundButton);
View topView = activity.findViewById(R.id.detailTopView);
View nextVideoView = null;
Button channelButton = (Button) activity.findViewById(R.id.channelButton);
if(info.next_video != null) {
nextVideoView = videoItemViewCreator
.getViewFromVideoInfoItem(null, nextVideoFrame, info.next_video);
@ -447,6 +449,14 @@ public class VideoItemDetailFragment extends Fragment {
}
});
channelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent i = new Intent(activity, ChannelActivity.class);
startActivity(i);
}
});
} catch (java.lang.NullPointerException e) {
Log.w(TAG, "updateInfo(): Fragment closed before thread ended work... or else");
e.printStackTrace();

View File

@ -0,0 +1,46 @@
package org.schabi.newpipe.extractor;
import java.io.IOException;
/**
* Created by Christian Schabesberger on 25.07.16.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* ChannelExtractor.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public abstract class ChannelExtractor {
private int serviceId;
private String url;
private UrlIdHandler urlIdHandler;
private Downloader downloader;
private StreamPreviewInfoCollector previewInfoCollector;
public ChannelExtractor(UrlIdHandler urlIdHandler, String url, Downloader dl, int serviceId)
throws ExtractionException, IOException {
this.serviceId = serviceId;
this.urlIdHandler = urlIdHandler;
previewInfoCollector = new StreamPreviewInfoCollector(urlIdHandler, serviceId);
}
public String getUrl() { return url; }
public UrlIdHandler getUrlIdHandler() { return urlIdHandler; }
public Downloader getDownloader() { return downloader; }
public abstract String getChannelName() throws ParsingException;
public abstract String getAvatarUrl() throws ParsingException;
public abstract String getBannerUrl() throws ParsingException;
}

View File

@ -33,7 +33,7 @@ public abstract class SearchEngine {
private StreamPreviewInfoSearchCollector collector;
public SearchEngine(StreamUrlIdHandler urlIdHandler, int serviceId) {
public SearchEngine(UrlIdHandler urlIdHandler, int serviceId) {
collector = new StreamPreviewInfoSearchCollector(urlIdHandler, serviceId);
}

View File

@ -30,7 +30,7 @@ public abstract class StreamExtractor {
private int serviceId;
private String url;
private StreamUrlIdHandler urlIdHandler;
private UrlIdHandler urlIdHandler;
private Downloader downloader;
private StreamPreviewInfoCollector previewInfoCollector;
@ -55,7 +55,7 @@ public abstract class StreamExtractor {
}
}
public StreamExtractor(StreamUrlIdHandler urlIdHandler, String url, Downloader dl, int serviceId) {
public StreamExtractor(UrlIdHandler urlIdHandler, String url, Downloader dl, int serviceId) {
this.serviceId = serviceId;
this.urlIdHandler = urlIdHandler;
previewInfoCollector = new StreamPreviewInfoCollector(urlIdHandler, serviceId);
@ -69,7 +69,7 @@ public abstract class StreamExtractor {
return url;
}
public StreamUrlIdHandler getUrlIdHandler() {
public UrlIdHandler getUrlIdHandler() {
return urlIdHandler;
}

View File

@ -85,12 +85,12 @@ public class StreamInfo extends AbstractVideoInfo {
/* ---- importand data, withoug the video can't be displayed goes here: ---- */
// if one of these is not available an exception is ment to be thrown directly into the frontend.
StreamUrlIdHandler uiconv = extractor.getUrlIdHandler();
UrlIdHandler uiconv = extractor.getUrlIdHandler();
streamInfo.service_id = extractor.getServiceId();
streamInfo.webpage_url = extractor.getPageUrl();
streamInfo.stream_type = extractor.getStreamType();
streamInfo.id = uiconv.getVideoId(extractor.getPageUrl());
streamInfo.id = uiconv.getId(extractor.getPageUrl());
streamInfo.title = extractor.getTitle();
streamInfo.age_limit = extractor.getAgeLimit();

View File

@ -28,10 +28,10 @@ import java.util.Vector;
public class StreamPreviewInfoCollector {
private List<StreamPreviewInfo> itemList = new Vector<>();
private List<Exception> errors = new Vector<>();
private StreamUrlIdHandler urlIdHandler;
private UrlIdHandler urlIdHandler;
private int serviceId = -1;
public StreamPreviewInfoCollector(StreamUrlIdHandler handler, int serviceId) {
public StreamPreviewInfoCollector(UrlIdHandler handler, int serviceId) {
urlIdHandler = handler;
this.serviceId = serviceId;
}
@ -57,7 +57,7 @@ public class StreamPreviewInfoCollector {
if (urlIdHandler == null) {
throw new ParsingException("Error: UrlIdHandler not set");
} else if(!resultItem.webpage_url.isEmpty()) {
resultItem.id = (new YoutubeStreamUrlIdHandler()).getVideoId(resultItem.webpage_url);
resultItem.id = (new YoutubeStreamUrlIdHandler()).getId(resultItem.webpage_url);
}
resultItem.title = extractor.getTitle();
resultItem.stream_type = extractor.getStreamType();

View File

@ -24,7 +24,7 @@ public class StreamPreviewInfoSearchCollector extends StreamPreviewInfoCollector
private String suggestion = "";
public StreamPreviewInfoSearchCollector(StreamUrlIdHandler handler, int serviceId) {
public StreamPreviewInfoSearchCollector(UrlIdHandler handler, int serviceId) {
super(handler, serviceId);
}

View File

@ -38,7 +38,10 @@ public abstract class StreamingService {
public abstract StreamExtractor getExtractorInstance(String url, Downloader downloader)
throws IOException, ExtractionException;
public abstract SearchEngine getSearchEngineInstance(Downloader downloader);
public abstract StreamUrlIdHandler getUrlIdHandlerInstance();
public abstract UrlIdHandler getUrlIdHandlerInstance();
public abstract UrlIdHandler getChannelUrlIdHandlerInstance();
public abstract ChannelExtractor getChannelExtractorInstance(String url, Downloader downloader)
throws ExtractionException, IOException;
public final int getServiceId() {
return serviceId;

View File

@ -1,10 +1,10 @@
package org.schabi.newpipe.extractor;
/**
* Created by Christian Schabesberger on 02.02.16.
* Created by Christian Schabesberger on 26.07.16.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* StreamUrlIdHandler.java is part of NewPipe.
* UrlIdHandler.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -20,9 +20,9 @@ package org.schabi.newpipe.extractor;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public interface StreamUrlIdHandler {
String getVideoUrl(String videoId);
String getVideoId(String siteUrl) throws ParsingException;
public interface UrlIdHandler {
String getUrl(String videoId);
String getId(String siteUrl) throws ParsingException;
String cleanUrl(String siteUrl) throws ParsingException;
/**When a VIEW_ACTION is caught this function will test if the url delivered within the calling

View File

@ -0,0 +1,75 @@
package org.schabi.newpipe.extractor.services.youtube;
import android.util.Log;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.schabi.newpipe.extractor.ChannelExtractor;
import org.schabi.newpipe.extractor.Downloader;
import org.schabi.newpipe.extractor.ExtractionException;
import org.schabi.newpipe.extractor.ParsingException;
import org.schabi.newpipe.extractor.UrlIdHandler;
import java.io.IOException;
/**
* Created by Christian Schabesberger on 25.07.16.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* YoutubeChannelExtractor.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class YoutubeChannelExtractor extends ChannelExtractor {
private static final String TAG = YoutubeChannelExtractor.class.toString();
private Downloader downloader;
private final Document doc;
private final String siteUrl;
public YoutubeChannelExtractor(UrlIdHandler urlIdHandler, String url, Downloader dl, int serviceId)
throws ExtractionException, IOException {
super(urlIdHandler, url, dl, serviceId);
siteUrl = url;
downloader = dl;
String pageContent = downloader.download(url);
doc = Jsoup.parse(pageContent, url);
Log.d(TAG, pageContent);
}
@Override
public String getChannelName() throws ParsingException {
return getUrlIdHandler().getId(siteUrl);
}
@Override
public String getAvatarUrl() throws ParsingException {
try {
return doc.select("img[class=\"channel-header-profile-image\"]")
.first().attr("abs:src");
} catch(Exception e) {
throw new ParsingException("Could not get avatar", e);
}
}
@Override
public String getBannerUrl() throws ParsingException {
return "https://yt3.ggpht.com/-oF0YbeAGkaA/VBgrKvEGY1I/AAAAAAAACdw/nx02iZSseFw/w2120-fcrop64=1,00005a57ffffa5a8-nd-c0xffffffff-rj-k-no/Channel-Art-Template-%2528Photoshop%2529.png";
}
}

View File

@ -0,0 +1,50 @@
package org.schabi.newpipe.extractor.services.youtube;
import org.schabi.newpipe.extractor.Parser;
import org.schabi.newpipe.extractor.ParsingException;
import org.schabi.newpipe.extractor.UrlIdHandler;
/**
* Created by Christian Schabesberger on 25.07.16.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* YoutubeChannelUrlIdHandler.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class YoutubeChannelUrlIdHandler implements UrlIdHandler {
public String getUrl(String channelId) {
return "https://www.youtube.com/user/" + channelId + "/videos";
}
public String getId(String siteUrl) throws ParsingException {
try {
return Parser.matchGroup1("/user/(.*)", siteUrl);
} catch(Exception e) {
throw new ParsingException("Could not get channel/user id", e);
}
}
public String cleanUrl(String siteUrl) throws ParsingException {
return getUrl(getId(siteUrl));
}
public boolean acceptUrl(String videoUrl) {
return (videoUrl.contains("youtube") ||
videoUrl.contains("youtu.be")) &&
videoUrl.contains("/user/");
}
}

View File

@ -14,7 +14,7 @@ import org.schabi.newpipe.extractor.SearchEngine;
import org.schabi.newpipe.extractor.StreamPreviewInfoCollector;
import org.schabi.newpipe.extractor.StreamPreviewInfoExtractor;
import org.schabi.newpipe.extractor.StreamPreviewInfoSearchCollector;
import org.schabi.newpipe.extractor.StreamUrlIdHandler;
import org.schabi.newpipe.extractor.UrlIdHandler;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
@ -55,7 +55,7 @@ public class YoutubeSearchEngine extends SearchEngine {
private static final String TAG = YoutubeSearchEngine.class.toString();
public static final String CHARSET_UTF_8 = "UTF-8";
public YoutubeSearchEngine(StreamUrlIdHandler urlIdHandler, int serviceId) {
public YoutubeSearchEngine(UrlIdHandler urlIdHandler, int serviceId) {
super(urlIdHandler, serviceId);
}

View File

@ -1,10 +1,11 @@
package org.schabi.newpipe.extractor.services.youtube;
import org.schabi.newpipe.extractor.ChannelExtractor;
import org.schabi.newpipe.extractor.ExtractionException;
import org.schabi.newpipe.extractor.Downloader;
import org.schabi.newpipe.extractor.StreamExtractor;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.StreamUrlIdHandler;
import org.schabi.newpipe.extractor.UrlIdHandler;
import org.schabi.newpipe.extractor.SearchEngine;
import java.io.IOException;
@ -45,7 +46,7 @@ public class YoutubeService extends StreamingService {
@Override
public StreamExtractor getExtractorInstance(String url, Downloader downloader)
throws ExtractionException, IOException {
StreamUrlIdHandler urlIdHandler = new YoutubeStreamUrlIdHandler();
UrlIdHandler urlIdHandler = new YoutubeStreamUrlIdHandler();
if(urlIdHandler.acceptUrl(url)) {
return new YoutubeStreamExtractor(urlIdHandler, url, downloader, getServiceId());
}
@ -59,7 +60,18 @@ public class YoutubeService extends StreamingService {
}
@Override
public StreamUrlIdHandler getUrlIdHandlerInstance() {
public UrlIdHandler getUrlIdHandlerInstance() {
return new YoutubeStreamUrlIdHandler();
}
@Override
public UrlIdHandler getChannelUrlIdHandlerInstance() {
return new YoutubeChannelUrlIdHandler();
}
@Override
public ChannelExtractor getChannelExtractorInstance(String url, Downloader downloader)
throws ExtractionException, IOException {
return new YoutubeChannelExtractor(getChannelUrlIdHandlerInstance(), url, downloader, getServiceId());
}
}

View File

@ -15,10 +15,9 @@ import org.schabi.newpipe.extractor.Downloader;
import org.schabi.newpipe.extractor.Parser;
import org.schabi.newpipe.extractor.ParsingException;
import org.schabi.newpipe.extractor.StreamInfo;
import org.schabi.newpipe.extractor.StreamPreviewInfo;
import org.schabi.newpipe.extractor.StreamPreviewInfoCollector;
import org.schabi.newpipe.extractor.StreamPreviewInfoExtractor;
import org.schabi.newpipe.extractor.StreamUrlIdHandler;
import org.schabi.newpipe.extractor.UrlIdHandler;
import org.schabi.newpipe.extractor.StreamExtractor;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.VideoStream;
@ -183,12 +182,12 @@ public class YoutubeStreamExtractor extends StreamExtractor {
// cached values
private static volatile String decryptionCode = "";
StreamUrlIdHandler urlidhandler = new YoutubeStreamUrlIdHandler();
UrlIdHandler urlidhandler = new YoutubeStreamUrlIdHandler();
String pageUrl = "";
private Downloader downloader;
public YoutubeStreamExtractor(StreamUrlIdHandler urlIdHandler, String pageUrl,
public YoutubeStreamExtractor(UrlIdHandler urlIdHandler, String pageUrl,
Downloader dl, int serviceId)
throws ExtractionException, IOException {
super(urlIdHandler ,pageUrl, dl, serviceId);
@ -203,7 +202,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
// Check if the video is age restricted
if (pageContent.contains("<meta property=\"og:restrictions:age")) {
String videoInfoUrl = GET_VIDEO_INFO_URL.replace("%%video_id%%",
urlidhandler.getVideoId(pageUrl)).replace("$$el_type$$", "&" + EL_INFO);
urlidhandler.getId(pageUrl)).replace("$$el_type$$", "&" + EL_INFO);
String videoInfoPageString = downloader.download(videoInfoUrl);
videoInfoPage = Parser.compatParseMap(videoInfoPageString);
playerUrl = getPlayerUrlFromRestrictedVideo(pageUrl);
@ -286,7 +285,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
private String getPlayerUrlFromRestrictedVideo(String pageUrl) throws ParsingException {
try {
String playerUrl = "";
String videoId = urlidhandler.getVideoId(pageUrl);
String videoId = urlidhandler.getId(pageUrl);
String embedUrl = "https://www.youtube.com/embed/" + videoId;
String embedPageContent = downloader.download(embedUrl);
//todo: find out if this can be reapaced by Parser.matchGroup1()

View File

@ -2,7 +2,7 @@ package org.schabi.newpipe.extractor.services.youtube;
import org.schabi.newpipe.extractor.Parser;
import org.schabi.newpipe.extractor.ParsingException;
import org.schabi.newpipe.extractor.StreamUrlIdHandler;
import org.schabi.newpipe.extractor.UrlIdHandler;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
@ -27,16 +27,16 @@ import java.net.URLDecoder;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class YoutubeStreamUrlIdHandler implements StreamUrlIdHandler {
public class YoutubeStreamUrlIdHandler implements UrlIdHandler {
@SuppressWarnings("WeakerAccess")
@Override
public String getVideoUrl(String videoId) {
public String getUrl(String videoId) {
return "https://www.youtube.com/watch?v=" + videoId;
}
@SuppressWarnings("WeakerAccess")
@Override
public String getVideoId(String url) throws ParsingException, IllegalArgumentException {
public String getId(String url) throws ParsingException, IllegalArgumentException {
if(url.isEmpty())
{
throw new IllegalArgumentException("The url parameter should not be empty");
@ -81,7 +81,7 @@ public class YoutubeStreamUrlIdHandler implements StreamUrlIdHandler {
}
public String cleanUrl(String complexUrl) throws ParsingException {
return getVideoUrl(getVideoId(complexUrl));
return getUrl(getId(complexUrl));
}
@Override

View File

@ -179,13 +179,18 @@
android:text="100" />
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/detailUploaderFrame"
android:layout_below="@+id/linearLayout">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/linearLayout"
android:id="@+id/detailUploaderWrapView"
android:id="@+id/detailUploaderLayout"
android:layout_marginTop="12dp">
<View
android:background="#000"
android:layout_width="match_parent"
@ -223,11 +228,21 @@
android:layout_below="@id/detailUploaderThumbnailView"/>
</RelativeLayout>
<Button
android:layout_marginTop="11dp"
android:id="@+id/channelButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"/>
</FrameLayout>
<RelativeLayout android:id="@+id/detailNextVideoRootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal|bottom"
android:layout_below="@+id/detailUploaderWrapView"
android:layout_below="@+id/detailUploaderFrame"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp">

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="org.schabi.newpipe.ChannelActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/app_bar_height"
android:fitsSystemWindows="true"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_channel" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email"
app:layout_anchor="@id/app_bar"
app:layout_anchorGravity="bottom|end" />
</android.support.design.widget.CoordinatorLayout>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="org.schabi.newpipe.ChannelActivity"
tools:showIn="@layout/activity_channel">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:text="@string/large_text" />
</android.support.v4.widget.NestedScrollView>

View File

@ -0,0 +1,10 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="org.schabi.newpipe.ChannelActivity">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="never" />
</menu>

View File

@ -35,4 +35,11 @@
<item name="background">@color/video_overlay_color</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>

View File

@ -37,5 +37,8 @@
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="app_bar_height">180dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="text_margin">16dp</dimen>
</resources>

View File

@ -176,6 +176,97 @@
<!-- Checksum types -->
<string name="md5" translatable="false">MD5</string>
<string name="sha1" translatable="false">SHA1</string>
<string name="title_activity_channel">ChannelActivity</string>
<string name="large_text">
"Material is the metaphor.\n\n"
"A material metaphor is the unifying theory of a rationalized space and a system of motion."
"The material is grounded in tactile reality, inspired by the study of paper and ink, yet "
"technologically advanced and open to imagination and magic.\n"
"Surfaces and edges of the material provide visual cues that are grounded in reality. The "
"use of familiar tactile attributes helps users quickly understand affordances. Yet the "
"flexibility of the material creates new affordances that supercede those in the physical "
"world, without breaking the rules of physics.\n"
"The fundamentals of light, surface, and movement are key to conveying how objects move, "
"interact, and exist in space and in relation to each other. Realistic lighting shows "
"seams, divides space, and indicates moving parts.\n\n"
"Bold, graphic, intentional.\n\n"
"The foundational elements of print based design typography, grids, space, scale, color, "
"and use of imagery guide visual treatments. These elements do far more than please the "
"eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge "
"imagery, large scale typography, and intentional white space create a bold and graphic "
"interface that immerse the user in the experience.\n"
"An emphasis on user actions makes core functionality immediately apparent and provides "
"waypoints for the user.\n\n"
"Motion provides meaning.\n\n"
"Motion respects and reinforces the user as the prime mover. Primary user actions are "
"inflection points that initiate motion, transforming the whole design.\n"
"All action takes place in a single environment. Objects are presented to the user without "
"breaking the continuity of experience even as they transform and reorganize.\n"
"Motion is meaningful and appropriate, serving to focus attention and maintain continuity. "
"Feedback is subtle yet clear. Transitions are efficient yet coherent.\n\n"
"3D world.\n\n"
"The material environment is a 3D space, which means all objects have x, y, and z "
"dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the "
"positive z-axis extending towards the viewer. Every sheet of material occupies a single "
"position along the z-axis and has a standard 1dp thickness.\n"
"On the web, the z-axis is used for layering and not for perspective. The 3D world is "
"emulated by manipulating the y-axis.\n\n"
"Light and shadow.\n\n"
"Within the material environment, virtual lights illuminate the scene. Key lights create "
"directional shadows, while ambient light creates soft shadows from all angles.\n"
"Shadows in the material environment are cast by these two light sources. In Android "
"development, shadows occur when light sources are blocked by sheets of material at "
"various positions along the z-axis. On the web, shadows are depicted by manipulating the "
"y-axis only. The following example shows the card with a height of 6dp.\n\n"
"Resting elevation.\n\n"
"All material objects, regardless of size, have a resting elevation, or default elevation "
"that does not change. If an object changes elevation, it should return to its resting "
"elevation as soon as possible.\n\n"
"Component elevations.\n\n"
"The resting elevation for a component type is consistent across apps (e.g., FAB elevation "
"does not vary from 6dp in one app to 16dp in another app).\n"
"Components may have different resting elevations across platforms, depending on the depth "
"of the environment (e.g., TV has a greater depth than mobile or desktop).\n\n"
"Responsive elevation and dynamic elevation offsets.\n\n"
"Some component types have responsive elevation, meaning they change elevation in response "
"to user input (e.g., normal, focused, and pressed) or system events. These elevation "
"changes are consistently implemented using dynamic elevation offsets.\n"
"Dynamic elevation offsets are the goal elevation that a component moves towards, relative "
"to the components resting state. They ensure that elevation changes are consistent "
"across actions and component types. For example, all components that lift on press have "
"the same elevation change relative to their resting elevation.\n"
"Once the input event is completed or cancelled, the component will return to its resting "
"elevation.\n\n"
"Avoiding elevation interference.\n\n"
"Components with responsive elevations may encounter other components as they move between "
"their resting elevations and dynamic elevation offsets. Because material cannot pass "
"through other material, components avoid interfering with one another any number of ways, "
"whether on a per component basis or using the entire app layout.\n"
"On a component level, components can move or be removed before they cause interference. "
"For example, a floating action button (FAB) can disappear or move off screen before a "
"user picks up a card, or it can move if a snackbar appears.\n"
"On the layout level, design your app layout to minimize opportunities for interference. "
"For example, position the FAB to one side of stream of a cards so the FAB wont interfere "
"when a user tries to pick up one of cards.\n\n"
</string>
<string name="action_settings">Settings</string>
<!-- End of GigaGet's Strings -->

View File

@ -1,7 +1,6 @@
<resources>
<style name="RootTheme" parent="android:Theme.Holo">
</style>
<style name="RootTheme" parent="android:Theme.Holo"></style>
<style name="PlayerTheme" parent="@style/RootTheme">
<item name="android:windowNoTitle">true</item>
@ -69,4 +68,13 @@
<item name="colorAccent">@color/light_youtube_accent_color</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>