1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-10-24 11:57:38 +00:00

redesign channel activity

This commit is contained in:
Christian Schabesberger
2017-02-27 15:58:09 +01:00
parent 89e70626eb
commit c14771534f
14 changed files with 528 additions and 162 deletions

View File

@@ -156,8 +156,7 @@
<activity
android:name=".ChannelActivity"
android:label="@string/title_activity_channel"
android:launchMode="singleTask"
android:theme="@style/AppTheme.NoActionBar" />
android:launchMode="singleTask"/>
<activity
android:name=".ReCaptchaActivity"

View File

@@ -1,31 +1,21 @@
package org.schabi.newpipe;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.nostra13.universalimageloader.core.ImageLoader;
import org.schabi.newpipe.detail.VideoItemDetailActivity;
import org.schabi.newpipe.detail.VideoItemDetailFragment;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
@@ -39,10 +29,6 @@ import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.NavStack;
import java.io.IOException;
import java.util.Objects;
import static android.os.Build.VERSION.SDK;
import static android.os.Build.VERSION.SDK_INT;
/**
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
@@ -62,7 +48,7 @@ import static android.os.Build.VERSION.SDK_INT;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class ChannelActivity extends AppCompatActivity {
public class ChannelActivity extends ThemableActivity {
private static final String TAG = ChannelActivity.class.toString();
private View rootView = null;
@@ -75,22 +61,17 @@ public class ChannelActivity extends AppCompatActivity {
private ImageLoader imageLoader = ImageLoader.getInstance();
private InfoListAdapter infoListAdapter = null;
private String subS = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//since we set themeing we have to set translucent statusBar by hand
if (PreferenceManager.getDefaultSharedPreferences(this)
.getString("theme", getResources().getString(R.string.light_theme_title)).
equals(getResources().getString(R.string.dark_theme_title))) {
setTheme(R.style.DarkTheme_NoActionBar);
}
setTranslucentStatusBar(getWindow());
setContentView(R.layout.activity_channel);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
rootView = findViewById(R.id.rootView);
setSupportActionBar(toolbar);
rootView = findViewById(android.R.id.content);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowTitleEnabled(true);
if(savedInstanceState == null) {
Intent i = getIntent();
channelUrl = i.getStringExtra(NavStack.URL);
@@ -107,6 +88,7 @@ public class ChannelActivity extends AppCompatActivity {
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.channel_streams_view);
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
infoListAdapter.setHeader(getLayoutInflater().inflate(R.layout.channel_header, recyclerView, false));
recyclerView.setAdapter(infoListAdapter);
infoListAdapter.setOnStreamInfoItemSelectedListener(
new InfoItemBuilder.OnInfoItemSelectedListener() {
@@ -140,6 +122,8 @@ public class ChannelActivity extends AppCompatActivity {
}
});
subS = getString(R.string.subscriber);
requestData(false);
}
@@ -153,22 +137,24 @@ public class ChannelActivity extends AppCompatActivity {
}
private void updateUi(final ChannelInfo info) {
CollapsingToolbarLayout ctl = (CollapsingToolbarLayout) findViewById(R.id.channel_toolbar_layout);
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
ImageView channelBanner = (ImageView) findViewById(R.id.channel_banner_image);
final FloatingActionButton feedButton = (FloatingActionButton) findViewById(R.id.channel_rss_fab);
ImageView avatarView = (ImageView) findViewById(R.id.channel_avatar_view);
ImageView haloView = (ImageView) findViewById(R.id.channel_avatar_halo);
TextView titleView = (TextView) findViewById(R.id.channel_title_view);
TextView subscirberView = (TextView) findViewById(R.id.channel_subscriber_view);
Button subscriberButton = (Button) findViewById(R.id.channel_subscribe_button);
progressBar.setVisibility(View.GONE);
if(info.channel_name != null && !info.channel_name.isEmpty()) {
ctl.setTitle(info.channel_name);
getSupportActionBar().setTitle(info.channel_name);
titleView.setText(info.channel_name);
}
if(info.banner_url != null && !info.banner_url.isEmpty()) {
imageLoader.displayImage(info.banner_url, channelBanner,
new ImageErrorLoadingListener(this, rootView ,info.service_id));
new ImageErrorLoadingListener(this, rootView ,info.service_id));
}
if(info.avatar_url != null && !info.avatar_url.isEmpty()) {
@@ -178,8 +164,17 @@ public class ChannelActivity extends AppCompatActivity {
new ImageErrorLoadingListener(this, rootView ,info.service_id));
}
if(info.subscriberCount != -1) {
subscirberView.setText(buildSubscriberString(info.subscriberCount));
}
if((info.feed_url != null && !info.feed_url.isEmpty()) ||
(info.subscriberCount != -1)) {
findViewById(R.id.channel_subscriber_layout).setVisibility(View.VISIBLE);
}
if(info.feed_url != null && !info.feed_url.isEmpty()) {
feedButton.setOnClickListener(new View.OnClickListener() {
subscriberButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d(TAG, info.feed_url);
@@ -188,8 +183,9 @@ public class ChannelActivity extends AppCompatActivity {
}
});
} else {
feedButton.setVisibility(View.GONE);
subscriberButton.setVisibility(View.GONE);
}
}
private void addVideos(final ChannelInfo info) {
@@ -297,30 +293,6 @@ public class ChannelActivity extends AppCompatActivity {
channelExtractorThread.start();
}
// fix transparent statusbar fuckup (fuck google why can't they just leave something that worked
// as it is, and everyone gets happy)
public static void setTranslucentStatusBar(Window window) {
if (window == null) return;
int sdkInt = Build.VERSION.SDK_INT;
if (sdkInt >= Build.VERSION_CODES.LOLLIPOP) {
setTranslucentStatusBarLollipop(window);
} else if (sdkInt >= Build.VERSION_CODES.KITKAT) {
setTranslucentStatusBarKiKat(window);
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static void setTranslucentStatusBarLollipop(Window window) {
window.setStatusBarColor(
ContextCompat.getColor(window.getContext(), android.R.color.transparent));
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private static void setTranslucentStatusBarKiKat(Window window) {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
@Override
public void onBackPressed() {
try {
@@ -331,4 +303,19 @@ public class ChannelActivity extends AppCompatActivity {
}
}
private String buildSubscriberString(long count) {
String out = "";
if(count >= 1000000000){
out += Long.toString((count/1000000000)%1000)+".";
}
if(count>=1000000){
out += Long.toString((count/1000000)%1000) + ".";
}
if(count>=1000){
out += Long.toString((count/1000)%1000)+".";
}
out += Long.toString(count%1000) + " " + subS;
return out;
}
}

View File

@@ -54,6 +54,7 @@ public abstract class ChannelExtractor {
public abstract String getBannerUrl() throws ParsingException;
public abstract String getFeedUrl() throws ParsingException;
public abstract StreamInfoItemCollector getStreams() throws ParsingException;
public abstract long getSubscriberCount() throws ParsingException;
public abstract boolean hasNextPage() throws ParsingException;
public int getServiceId() {
return serviceId;

View File

@@ -29,8 +29,6 @@ import java.util.Vector;
*/
public class ChannelInfo {
public void addException(Exception e) {
errors.add(e);
}
@@ -66,6 +64,11 @@ public class ChannelInfo {
} catch(Exception e) {
info.errors.add(e);
}
try {
info.subscriberCount = extractor.getSubscriberCount();
} catch (Exception e) {
info.errors.add(e);
}
return info;
}
@@ -76,6 +79,7 @@ public class ChannelInfo {
public String banner_url = "";
public String feed_url = "";
public List<InfoItem> related_streams = null;
public long subscriberCount = -1;
public boolean hasNextPage = false;
public List<Throwable> errors = new Vector<>();

View File

@@ -294,6 +294,13 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
return collector;
}
@Override
public long getSubscriberCount() throws ParsingException {
String countRaw = doc.select("span[class*=\"yt-subscription-button-subscriber-count\"]").first()
.text();
return Long.parseLong(countRaw.replaceAll("\\D+",""));
}
@Override
public String getFeedUrl() throws ParsingException {
try {

View File

@@ -105,7 +105,7 @@ public class InfoItemBuilder {
switch(info.infoType()) {
case STREAM:
itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.stream_item, parent, false);
.inflate(R.layout.stream_item, parent, false);
holder = new StreamInfoItemHolder(itemView);
break;
case CHANNEL:
@@ -202,15 +202,15 @@ public class InfoItemBuilder {
}
}
public String shortSubscriber(Long viewCount){
if(viewCount >= 1000000000){
return Long.toString(viewCount/1000000000)+ billion + " " + subsS;
}else if(viewCount>=1000000){
return Long.toString(viewCount/1000000)+ million + " " + subsS;
}else if(viewCount>=1000){
return Long.toString(viewCount/1000)+ thousand + " " + subsS;
public String shortSubscriber(Long count){
if(count >= 1000000000){
return Long.toString(count/1000000000)+ billion + " " + subsS;
}else if(count>=1000000){
return Long.toString(count/1000000)+ million + " " + subsS;
}else if(count>=1000){
return Long.toString(count/1000)+ thousand + " " + subsS;
}else {
return Long.toString(viewCount)+ " " + subsS;
return Long.toString(count)+ " " + subsS;
}
}

View File

@@ -33,11 +33,20 @@ import java.util.Vector;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class InfoListAdapter extends RecyclerView.Adapter<InfoItemHolder> {
public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final String TAG = InfoListAdapter.class.toString();
private final InfoItemBuilder infoItemBuilder;
private final List<InfoItem> infoItemList;
private View header = null;
public class HeaderHolder extends RecyclerView.ViewHolder {
public HeaderHolder(View v) {
super(v);
view = v;
}
public View view;
}
public InfoListAdapter(Activity a, View rootView) {
infoItemBuilder = new InfoItemBuilder(a, rootView);
@@ -66,21 +75,30 @@ public class InfoListAdapter extends RecyclerView.Adapter<InfoItemHolder> {
notifyDataSetChanged();
}
public void setHeader(View header) {
this.header = header;
}
@Override
public int getItemCount() {
return infoItemList.size();
return (header == null) ? infoItemList.size() : (infoItemList.size() + 1);
}
// don't ask why we have to do that this way... it's android accept it -.-
@Override
public int getItemViewType(int position) {
if(header != null && position == 0) {
return 0;
} else if(header != null) {
position--;
}
switch(infoItemList.get(position).infoType()) {
case STREAM:
return 0;
case CHANNEL:
return 1;
case PLAYLIST:
case CHANNEL:
return 2;
case PLAYLIST:
return 3;
default:
Log.e(TAG, "Trollolo");
return -1;
@@ -88,15 +106,17 @@ public class InfoListAdapter extends RecyclerView.Adapter<InfoItemHolder> {
}
@Override
public InfoItemHolder onCreateViewHolder(ViewGroup parent, int type) {
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int type) {
switch(type) {
case 0:
return new HeaderHolder(header);
case 1:
return new StreamInfoItemHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.stream_item, parent, false));
case 1:
case 2:
return new ChannelInfoItemHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.channel_item, parent, false));
case 2:
case 3:
Log.e(TAG, "Playlist is not yet implemented");
return null;
default:
@@ -106,7 +126,15 @@ public class InfoListAdapter extends RecyclerView.Adapter<InfoItemHolder> {
}
@Override
public void onBindViewHolder(InfoItemHolder holder, int i) {
infoItemBuilder.buildByHolder(holder, infoItemList.get(i));
public void onBindViewHolder(RecyclerView.ViewHolder holder, int i) {
//god damen f*** ANDROID SH**
if(holder instanceof InfoItemHolder) {
if(header != null) {
i--;
}
infoItemBuilder.buildByHolder((InfoItemHolder) holder, infoItemList.get(i));
} else if(holder instanceof HeaderHolder && i == 0 && header != null) {
((HeaderHolder) holder).view = header;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View File

@@ -1,81 +1,17 @@
<?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"
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:id="@+id/rootView"
tools:context="org.schabi.newpipe.ChannelActivity">
android:orientation="vertical"
android:title="Channel">
<android.support.design.widget.AppBarLayout
android:id="@+id/channel_app_bar"
<android.support.v7.widget.RecyclerView
android:id="@+id/channel_streams_view"
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/channel_toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="@color/light_youtube_primary_color"
app:statusBarScrim="@color/light_youtube_dark_color"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/channel_banner_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:background="@color/light_youtube_dark_color"
app:layout_collapseMode="parallax" />
<ImageView
android:id="@+id/channel_avatar_halo"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="28dp"
android:layout_marginStart="28dp"
android:layout_marginTop="38dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:src="@drawable/white_circle"/>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/channel_avatar_view"
android:visibility="gone"
android:layout_width="@dimen/channel_avatar_size"
android:layout_height="@dimen/channel_avatar_size"
android:src="@drawable/buddy"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:layout_marginTop="40dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"/>
<android.support.v7.widget.Toolbar
android:id="@+id/cannel_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>
<android.support.design.widget.FloatingActionButton
android:id="@+id/channel_rss_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_margin"
android:src="?attr/rss"
app:layout_anchor="@id/channel_app_bar"
app:layout_anchorGravity="bottom|end" />
android:layout_height="match_parent"
android:background="?android:windowBackground"
android:scrollbars="vertical"/>
<RelativeLayout
android:layout_width="match_parent"
@@ -87,11 +23,4 @@
android:layout_centerInParent="true"
android:indeterminate="true"/>
</RelativeLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/channel_streams_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:windowBackground"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:scrollbars="vertical"/>
</android.support.design.widget.CoordinatorLayout>
</RelativeLayout>

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/channel_banner_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:scaleType="center"
android:src="@drawable/channel_banner"
android:background="@android:color/black"
android:adjustViewBounds="true"/>
<RelativeLayout
android:id="@+id/channel_avatar_layout"
android:layout_alignTop="@id/channel_banner_image"
android:layout_width="@dimen/channel_avatar_halo_size"
android:layout_height="@dimen/channel_avatar_halo_size"
android:layout_marginLeft="20dp"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp">
<ImageView
android:id="@+id/channel_avatar_halo"
android:layout_width="@dimen/channel_avatar_halo_size"
android:layout_height="@dimen/channel_avatar_halo_size"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:src="@drawable/white_circle" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/channel_avatar_view"
android:layout_width="@dimen/channel_avatar_size"
android:layout_height="@dimen/channel_avatar_size"
android:src="@drawable/buddy"
android:layout_centerInParent="true"/>
</RelativeLayout>
<TextView
android:id="@+id/channel_title_view"
android:layout_below="@id/channel_banner_image"
android:layout_toEndOf="@id/channel_avatar_layout"
android:layout_toRightOf="@id/channel_avatar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/video_item_detail_title_text_size"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<LinearLayout
android:id="@+id/channel_subscriber_layout"
android:visibility="gone"
android:layout_below="@id/channel_avatar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/channel_subscribe_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/subscribe"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"/>
<TextView
android:id="@+id/channel_subscriber_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"/>
</LinearLayout>
</RelativeLayout>

View File

@@ -151,6 +151,7 @@
<string name="use_exoplayer_summary">Experimental</string>
<string name="videos">videos</string>
<string name="subscriber">subscriber</string>
<string name="subscribe">Subscribe</string>
<string name="views">views</string>
<string name="short_thousand">K</string>
<string name="short_million">M</string>

View File

@@ -87,6 +87,11 @@ public class YoutubeChannelExtractorTest {
assertTrue("no next page link found", extractor.hasNextPage());
}
@Test
public void testGetSubscriberCount() throws Exception {
assertTrue("wrong subscriber count", extractor.getSubscriberCount() >= 0);
}
@Test
public void testGetNextPage() throws Exception {
extractor = NewPipe.getService("Youtube")