Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
8a3cf0d5dc
|
@ -8,8 +8,8 @@ android {
|
||||||
applicationId "org.schabi.newpipe"
|
applicationId "org.schabi.newpipe"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 28
|
versionCode 29
|
||||||
versionName "0.9.1"
|
versionName "0.9.2"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
|
|
@ -19,22 +19,15 @@
|
||||||
tools:ignore="AllowBackup">
|
tools:ignore="AllowBackup">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name"
|
||||||
|
android:launchMode="singleTask">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
|
||||||
android:name=".detail.VideoItemDetailActivity"
|
|
||||||
android:label="@string/title_videoitem_detail"
|
|
||||||
android:launchMode="singleTask"
|
|
||||||
android:theme="@style/AppTheme">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
|
||||||
android:value=".MainActivity" />
|
|
||||||
</activity>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".player.PlayVideoActivity"
|
android:name=".player.PlayVideoActivity"
|
||||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||||
|
@ -50,7 +43,7 @@
|
||||||
android:name=".player.ExoPlayerActivity"
|
android:name=".player.ExoPlayerActivity"
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleInstance"
|
android:launchMode="singleTask"
|
||||||
android:theme="@style/PlayerTheme"/>
|
android:theme="@style/PlayerTheme"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
@ -87,9 +80,6 @@
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:theme="@style/FilePickerTheme"/>
|
android:theme="@style/FilePickerTheme"/>
|
||||||
<activity
|
|
||||||
android:name=".ChannelActivity"
|
|
||||||
android:launchMode="singleTask" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ReCaptchaActivity"
|
android:name=".ReCaptchaActivity"
|
||||||
android:label="@string/reCaptchaActivity" />
|
android:label="@string/reCaptchaActivity" />
|
||||||
|
@ -104,7 +94,9 @@
|
||||||
android:resource="@xml/provider_paths" />
|
android:resource="@xml/provider_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<activity android:name=".RouterActivity"
|
<activity
|
||||||
|
android:name=".RouterActivity"
|
||||||
|
android:taskAffinity=""
|
||||||
android:theme="@android:style/Theme.NoDisplay">
|
android:theme="@android:style/Theme.NoDisplay">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
@ -161,7 +153,9 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".PopupActivity"
|
<activity
|
||||||
|
android:name=".RouterPopupActivity"
|
||||||
|
android:taskAffinity=""
|
||||||
android:theme="@android:style/Theme.NoDisplay"
|
android:theme="@android:style/Theme.NoDisplay"
|
||||||
android:label="@string/popup_mode_share_menu_title">
|
android:label="@string/popup_mode_share_menu_title">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@ -182,9 +176,6 @@
|
||||||
<data android:pathPrefix="/embed/" />
|
<data android:pathPrefix="/embed/" />
|
||||||
<data android:pathPrefix="/watch" />
|
<data android:pathPrefix="/watch" />
|
||||||
<data android:pathPrefix="/attribution_link" />
|
<data android:pathPrefix="/attribution_link" />
|
||||||
<!-- channel prefix -->
|
|
||||||
<data android:pathPrefix="/channel/"/>
|
|
||||||
<data android:pathPrefix="/user/"/>
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
@ -212,9 +203,7 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<data android:mimeType="text/plain" />
|
<data android:mimeType="text/plain" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
|
@ -1,407 +0,0 @@
|
||||||
package org.schabi.newpipe;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
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.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
|
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
|
||||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
|
||||||
import org.schabi.newpipe.util.NavStack;
|
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* ChannelActivity.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 ChannelActivity extends AppCompatActivity {
|
|
||||||
private static final String TAG = ChannelActivity.class.toString();
|
|
||||||
private View rootView = null;
|
|
||||||
|
|
||||||
private int serviceId = -1;
|
|
||||||
private String channelUrl = "";
|
|
||||||
private int pageNumber = 0;
|
|
||||||
private boolean hasNextPage = true;
|
|
||||||
private boolean isLoading = false;
|
|
||||||
|
|
||||||
private ImageLoader imageLoader = ImageLoader.getInstance();
|
|
||||||
private InfoListAdapter infoListAdapter = null;
|
|
||||||
|
|
||||||
private String subS = "";
|
|
||||||
|
|
||||||
ProgressBar progressBar = null;
|
|
||||||
ImageView channelBanner = null;
|
|
||||||
ImageView avatarView = null;
|
|
||||||
TextView titleView = null;
|
|
||||||
TextView subscirberView = null;
|
|
||||||
Button subscriberButton = null;
|
|
||||||
View subscriberLayout = null;
|
|
||||||
|
|
||||||
View header = null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
ThemeHelper.setTheme(this, true);
|
|
||||||
setContentView(R.layout.activity_channel);
|
|
||||||
rootView = findViewById(android.R.id.content);
|
|
||||||
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
getSupportActionBar().setDisplayShowTitleEnabled(true);
|
|
||||||
|
|
||||||
infoListAdapter = new InfoListAdapter(this, rootView);
|
|
||||||
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.channel_streams_view);
|
|
||||||
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
|
|
||||||
recyclerView.setLayoutManager(layoutManager);
|
|
||||||
header = getLayoutInflater().inflate(R.layout.channel_header, recyclerView, false);
|
|
||||||
infoListAdapter.setHeader(header);
|
|
||||||
infoListAdapter.setFooter(
|
|
||||||
getLayoutInflater().inflate(R.layout.pignate_footer, recyclerView, false));
|
|
||||||
recyclerView.setAdapter(infoListAdapter);
|
|
||||||
infoListAdapter.setOnStreamInfoItemSelectedListener(
|
|
||||||
new InfoItemBuilder.OnInfoItemSelectedListener() {
|
|
||||||
@Override
|
|
||||||
public void selected(String url, int serviceId) {
|
|
||||||
NavStack.getInstance()
|
|
||||||
.openDetailActivity(ChannelActivity.this, url, serviceId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// detect if list has ben scrolled to the bottom
|
|
||||||
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
|
|
||||||
@Override
|
|
||||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
|
||||||
int pastVisiblesItems, visibleItemCount, totalItemCount;
|
|
||||||
super.onScrolled(recyclerView, dx, dy);
|
|
||||||
if(dy > 0) //check for scroll down
|
|
||||||
{
|
|
||||||
visibleItemCount = layoutManager.getChildCount();
|
|
||||||
totalItemCount = layoutManager.getItemCount();
|
|
||||||
pastVisiblesItems = layoutManager.findFirstVisibleItemPosition();
|
|
||||||
|
|
||||||
if ( (visibleItemCount + pastVisiblesItems) >= totalItemCount
|
|
||||||
&& !isLoading
|
|
||||||
&& hasNextPage)
|
|
||||||
{
|
|
||||||
pageNumber++;
|
|
||||||
requestData(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
subS = getString(R.string.subscriber);
|
|
||||||
|
|
||||||
progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
|
||||||
channelBanner = (ImageView) header.findViewById(R.id.channel_banner_image);
|
|
||||||
avatarView = (ImageView) header.findViewById(R.id.channel_avatar_view);
|
|
||||||
titleView = (TextView) header.findViewById(R.id.channel_title_view);
|
|
||||||
subscirberView = (TextView) header.findViewById(R.id.channel_subscriber_view);
|
|
||||||
subscriberButton = (Button) header.findViewById(R.id.channel_subscribe_button);
|
|
||||||
subscriberLayout = header.findViewById(R.id.channel_subscriber_layout);
|
|
||||||
|
|
||||||
if(savedInstanceState == null) {
|
|
||||||
handleIntent(getIntent());
|
|
||||||
} else {
|
|
||||||
channelUrl = savedInstanceState.getString(NavStack.URL);
|
|
||||||
serviceId = savedInstanceState.getInt(NavStack.SERVICE_ID);
|
|
||||||
NavStack.getInstance()
|
|
||||||
.restoreSavedInstanceState(savedInstanceState);
|
|
||||||
handleIntent(getIntent());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNewIntent(Intent intent) {
|
|
||||||
super.onNewIntent(intent);
|
|
||||||
handleIntent(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleIntent(Intent i) {
|
|
||||||
channelUrl = i.getStringExtra(NavStack.URL);
|
|
||||||
serviceId = i.getIntExtra(NavStack.SERVICE_ID, -1);
|
|
||||||
requestData(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putString(NavStack.URL, channelUrl);
|
|
||||||
outState.putInt(NavStack.SERVICE_ID, serviceId);
|
|
||||||
NavStack.getInstance()
|
|
||||||
.onSaveInstanceState(outState);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateUi(final ChannelInfo info) {
|
|
||||||
findViewById(R.id.channel_header_layout).setVisibility(View.VISIBLE);
|
|
||||||
progressBar.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
if(info.channel_name != null && !info.channel_name.isEmpty()) {
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(info.avatar_url != null && !info.avatar_url.isEmpty()) {
|
|
||||||
avatarView.setVisibility(View.VISIBLE);
|
|
||||||
imageLoader.displayImage(info.avatar_url, avatarView,
|
|
||||||
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)) {
|
|
||||||
subscriberLayout.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(info.feed_url != null && !info.feed_url.isEmpty()) {
|
|
||||||
subscriberButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
Log.d(TAG, info.feed_url);
|
|
||||||
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(info.feed_url));
|
|
||||||
startActivity(i);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
subscriberButton.setVisibility(View.INVISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addVideos(final ChannelInfo info) {
|
|
||||||
infoListAdapter.addInfoItemList(info.related_streams);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void postNewErrorToast(Handler h, final int stringResource) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Toast.makeText(ChannelActivity.this,
|
|
||||||
stringResource, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void requestData(final boolean onlyVideos) {
|
|
||||||
// start processing
|
|
||||||
isLoading = true;
|
|
||||||
|
|
||||||
if(!onlyVideos) {
|
|
||||||
//delete already displayed content
|
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
|
||||||
infoListAdapter.clearSteamItemList();
|
|
||||||
pageNumber = 0;
|
|
||||||
subscriberLayout.setVisibility(View.GONE);
|
|
||||||
titleView.setText("");
|
|
||||||
getSupportActionBar().setTitle("");
|
|
||||||
if (SDK_INT >= 21) {
|
|
||||||
channelBanner.setImageDrawable(getDrawable(R.drawable.channel_banner));
|
|
||||||
avatarView.setImageDrawable(getDrawable(R.drawable.buddy));
|
|
||||||
}
|
|
||||||
infoListAdapter.showFooter(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread channelExtractorThread = new Thread(new Runnable() {
|
|
||||||
Handler h = new Handler();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
StreamingService service = null;
|
|
||||||
try {
|
|
||||||
service = NewPipe.getService(serviceId);
|
|
||||||
ChannelExtractor extractor = service.getChannelExtractorInstance(
|
|
||||||
channelUrl, pageNumber);
|
|
||||||
|
|
||||||
final ChannelInfo info = ChannelInfo.getInfo(extractor);
|
|
||||||
|
|
||||||
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
isLoading = false;
|
|
||||||
if(!onlyVideos) {
|
|
||||||
updateUi(info);
|
|
||||||
infoListAdapter.showFooter(true);
|
|
||||||
}
|
|
||||||
hasNextPage = info.hasNextPage;
|
|
||||||
if(!hasNextPage) {
|
|
||||||
infoListAdapter.showFooter(false);
|
|
||||||
}
|
|
||||||
addVideos(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// look for non critical errors during extraction
|
|
||||||
if(info != null &&
|
|
||||||
!info.errors.isEmpty()) {
|
|
||||||
Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:");
|
|
||||||
for (Throwable e : info.errors) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Log.e(TAG, "------");
|
|
||||||
}
|
|
||||||
|
|
||||||
View rootView = findViewById(android.R.id.content);
|
|
||||||
ErrorActivity.reportError(h, ChannelActivity.this,
|
|
||||||
info.errors, null, rootView,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL,
|
|
||||||
service.getServiceInfo().name, channelUrl, 0 /* no message for the user */));
|
|
||||||
}
|
|
||||||
} catch(IOException ioe) {
|
|
||||||
postNewErrorToast(h, R.string.network_error);
|
|
||||||
ioe.printStackTrace();
|
|
||||||
} catch(ParsingException pe) {
|
|
||||||
ErrorActivity.reportError(h, ChannelActivity.this, pe, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL,
|
|
||||||
service.getServiceInfo().name, channelUrl, R.string.parsing_error));
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ChannelActivity.this.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
pe.printStackTrace();
|
|
||||||
} catch(ExtractionException ex) {
|
|
||||||
String name = "none";
|
|
||||||
if(service != null) {
|
|
||||||
name = service.getServiceInfo().name;
|
|
||||||
}
|
|
||||||
ErrorActivity.reportError(h, ChannelActivity.this, ex, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL,
|
|
||||||
name, channelUrl, R.string.parsing_error));
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ChannelActivity.this.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ex.printStackTrace();
|
|
||||||
} catch(Exception e) {
|
|
||||||
ErrorActivity.reportError(h, ChannelActivity.this, e, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL,
|
|
||||||
service.getServiceInfo().name, channelUrl, R.string.general_error));
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ChannelActivity.this.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
channelExtractorThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
try {
|
|
||||||
NavStack.getInstance()
|
|
||||||
.navBack(this);
|
|
||||||
} catch (Exception e) {
|
|
||||||
ErrorActivity.reportUiError(this, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
super.onCreateOptionsMenu(menu);
|
|
||||||
getMenuInflater().inflate(R.menu.menu_channel, menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
super.onOptionsItemSelected(item);
|
|
||||||
switch(item.getItemId()) {
|
|
||||||
case R.id.action_settings: {
|
|
||||||
Intent intent = new Intent(this, SettingsActivity.class);
|
|
||||||
startActivity(intent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case R.id.menu_item_openInBrowser: {
|
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
|
||||||
intent.setData(Uri.parse(channelUrl));
|
|
||||||
|
|
||||||
startActivity(Intent.createChooser(intent, getString(R.string.choose_browser)));
|
|
||||||
}
|
|
||||||
case R.id.menu_item_share:
|
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.setAction(Intent.ACTION_SEND);
|
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, channelUrl);
|
|
||||||
intent.setType("text/plain");
|
|
||||||
startActivity(Intent.createChooser(intent, getString(R.string.share_dialog_title)));
|
|
||||||
case android.R.id.home:
|
|
||||||
NavStack.getInstance().openMainActivity(this);
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +1,14 @@
|
||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||||
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
||||||
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 01.08.16.
|
* Created by Christian Schabesberger on 01.08.16.
|
||||||
|
@ -33,11 +33,11 @@ import org.schabi.newpipe.extractor.NewPipe;
|
||||||
public class ImageErrorLoadingListener implements ImageLoadingListener {
|
public class ImageErrorLoadingListener implements ImageLoadingListener {
|
||||||
|
|
||||||
private int serviceId = -1;
|
private int serviceId = -1;
|
||||||
private Activity activity = null;
|
private Context context = null;
|
||||||
private View rootView = null;
|
private View rootView = null;
|
||||||
|
|
||||||
public ImageErrorLoadingListener(Activity activity, View rootView, int serviceId) {
|
public ImageErrorLoadingListener(Context context, View rootView, int serviceId) {
|
||||||
this.activity = activity;
|
this.context = context;
|
||||||
this.serviceId= serviceId;
|
this.serviceId= serviceId;
|
||||||
this.rootView = rootView;
|
this.rootView = rootView;
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ public class ImageErrorLoadingListener implements ImageLoadingListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||||
ErrorActivity.reportError(activity,
|
ErrorActivity.reportError(context,
|
||||||
failReason.getCause(), null, rootView,
|
failReason.getCause(), null, rootView,
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE,
|
ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE,
|
||||||
NewPipe.getNameOfService(serviceId), imageUri,
|
NewPipe.getNameOfService(serviceId), imageUri,
|
||||||
|
|
|
@ -1,54 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Created by Christian Schabesberger on 02.08.16.
|
||||||
|
* <p>
|
||||||
|
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||||
|
* DownloadActivity.java is part of NewPipe.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.NavUtils;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
|
||||||
import org.schabi.newpipe.download.DownloadActivity;
|
import org.schabi.newpipe.download.DownloadActivity;
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
||||||
|
import org.schabi.newpipe.fragments.channel.ChannelFragment;
|
||||||
|
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||||
|
import org.schabi.newpipe.fragments.search.SearchFragment;
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
import org.schabi.newpipe.settings.SettingsActivity;
|
||||||
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
/**
|
public class MainActivity extends AppCompatActivity implements OnItemSelectedListener {
|
||||||
* Created by Christian Schabesberger on 02.08.16.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* DownloadActivity.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 MainActivity extends AppCompatActivity {
|
|
||||||
private Fragment mainFragment = null;
|
|
||||||
private static final String TAG = MainActivity.class.toString();
|
private static final String TAG = MainActivity.class.toString();
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Activity's LifeCycle
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
ThemeHelper.setTheme(this, true);
|
ThemeHelper.setTheme(this, true);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||||
mainFragment = getSupportFragmentManager()
|
if (savedInstanceState == null) initFragments();
|
||||||
.findFragmentById(R.id.search_fragment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent(Intent intent) {
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
handleIntent(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
|
||||||
|
if (fragment instanceof VideoDetailFragment) if (((VideoDetailFragment) fragment).onActivityBackPressed()) return;
|
||||||
|
|
||||||
|
if (getSupportFragmentManager().getBackStackEntryCount() >= 2) {
|
||||||
|
getSupportFragmentManager().popBackStackImmediate();
|
||||||
|
} else {
|
||||||
|
if (fragment instanceof SearchFragment) {
|
||||||
|
SearchFragment searchFragment = (SearchFragment) fragment;
|
||||||
|
if (!searchFragment.isMainBgVisible()) {
|
||||||
|
getSupportFragmentManager().beginTransaction().remove(fragment).commitNow();
|
||||||
|
NavigationHelper.openMainActivity(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Menu
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
super.onCreateOptionsMenu(menu);
|
super.onCreateOptionsMenu(menu);
|
||||||
|
@ -63,9 +104,10 @@ public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case android.R.id.home: {
|
case android.R.id.home: {
|
||||||
Intent intent = new Intent(this, MainActivity.class);
|
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
if (fragment instanceof VideoDetailFragment) ((VideoDetailFragment) fragment).clearHistory();
|
||||||
NavUtils.navigateUpTo(this, intent);
|
|
||||||
|
NavigationHelper.openMainActivity(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.action_settings: {
|
case R.id.action_settings: {
|
||||||
|
@ -85,4 +127,112 @@ public class MainActivity extends AppCompatActivity {
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Init
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private void initFragments() {
|
||||||
|
if (getIntent() != null && getIntent().hasExtra(Constants.KEY_URL)) {
|
||||||
|
handleIntent(getIntent());
|
||||||
|
} else openSearchFragment();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// OnItemSelectedListener
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(StreamingService.LinkType linkType, int serviceId, String url, String name) {
|
||||||
|
switch (linkType) {
|
||||||
|
case STREAM:
|
||||||
|
openVideoDetailFragment(serviceId, url, name, false);
|
||||||
|
break;
|
||||||
|
case CHANNEL:
|
||||||
|
openChannelFragment(serviceId, url, name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private void handleIntent(Intent intent) {
|
||||||
|
if (intent.hasExtra(Constants.KEY_LINK_TYPE)) {
|
||||||
|
String url = intent.getStringExtra(Constants.KEY_URL);
|
||||||
|
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
|
||||||
|
try {
|
||||||
|
switch (((StreamingService.LinkType) intent.getSerializableExtra(Constants.KEY_LINK_TYPE))) {
|
||||||
|
case STREAM:
|
||||||
|
handleVideoDetailIntent(serviceId, url, intent);
|
||||||
|
break;
|
||||||
|
case CHANNEL:
|
||||||
|
handleChannelIntent(serviceId, url, intent);
|
||||||
|
break;
|
||||||
|
case NONE:
|
||||||
|
throw new Exception("Url not known to service. service=" + Integer.toString(serviceId) + " url=" + url);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||||
|
openSearchFragment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openSearchFragment() {
|
||||||
|
ImageLoader.getInstance().clearMemoryCache();
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.fade_in, android.R.anim.fade_out)
|
||||||
|
.replace(R.id.fragment_holder, new SearchFragment())
|
||||||
|
.addToBackStack(null)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openVideoDetailFragment(int serviceId, String url, String title, boolean autoPlay) {
|
||||||
|
ImageLoader.getInstance().clearMemoryCache();
|
||||||
|
|
||||||
|
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
|
||||||
|
if (title == null) title = "";
|
||||||
|
|
||||||
|
if (fragment instanceof VideoDetailFragment && fragment.isVisible()) {
|
||||||
|
VideoDetailFragment detailFragment = (VideoDetailFragment) fragment;
|
||||||
|
detailFragment.setAutoplay(autoPlay);
|
||||||
|
detailFragment.selectAndLoadVideo(serviceId, url, title);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoDetailFragment instance = VideoDetailFragment.getInstance(serviceId, url, title);
|
||||||
|
instance.setAutoplay(autoPlay);
|
||||||
|
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.fade_in, android.R.anim.fade_out)
|
||||||
|
.replace(R.id.fragment_holder, instance)
|
||||||
|
.addToBackStack(null)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openChannelFragment(int serviceId, String url, String name) {
|
||||||
|
ImageLoader.getInstance().clearMemoryCache();
|
||||||
|
if (name == null) name = "";
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.fade_in, android.R.anim.fade_out)
|
||||||
|
.replace(R.id.fragment_holder, ChannelFragment.newInstance(serviceId, url, name))
|
||||||
|
.addToBackStack(null)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleVideoDetailIntent(int serviceId, String url, Intent intent) {
|
||||||
|
boolean autoPlay = intent.getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false);
|
||||||
|
String title = intent.getStringExtra(Constants.KEY_TITLE);
|
||||||
|
openVideoDetailFragment(serviceId, url, title, autoPlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleChannelIntent(int serviceId, String url, Intent intent) {
|
||||||
|
String name = intent.getStringExtra(Constants.KEY_TITLE);
|
||||||
|
openChannelFragment(serviceId, url, name);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,19 +3,14 @@ package org.schabi.newpipe;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.detail.VideoItemDetailActivity;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
import org.schabi.newpipe.util.NavStack;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
||||||
* RouterActivity .java is part of NewPipe.
|
* RouterActivity .java is part of NewPipe.
|
||||||
*
|
*
|
||||||
|
@ -38,7 +33,7 @@ import java.util.HashSet;
|
||||||
* to the part of the service which can handle the url.
|
* to the part of the service which can handle the url.
|
||||||
*/
|
*/
|
||||||
public class RouterActivity extends Activity {
|
public class RouterActivity extends Activity {
|
||||||
private static final String TAG = RouterActivity.class.toString();
|
//private static final String TAG = "RouterActivity"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes invisible separators (\p{Z}) and punctuation characters including
|
* Removes invisible separators (\p{Z}) and punctuation characters including
|
||||||
|
@ -54,6 +49,25 @@ public class RouterActivity extends Activity {
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleIntent(Intent intent) {
|
||||||
|
String videoUrl = "";
|
||||||
|
|
||||||
|
// first gather data and find service
|
||||||
|
if (intent.getData() != null) {
|
||||||
|
// this means the video was called though another app
|
||||||
|
videoUrl = intent.getData().toString();
|
||||||
|
} else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
|
||||||
|
//this means that vidoe was called through share menu
|
||||||
|
String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||||
|
videoUrl = getUris(extraText)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
NavigationHelper.openByLink(this, videoUrl);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String removeHeadingGibberish(final String input) {
|
private static String removeHeadingGibberish(final String input) {
|
||||||
int start = 0;
|
int start = 0;
|
||||||
|
@ -107,50 +121,4 @@ public class RouterActivity extends Activity {
|
||||||
return result.toArray(new String[result.size()]);
|
return result.toArray(new String[result.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleIntent(Intent intent) {
|
|
||||||
String videoUrl = "";
|
|
||||||
StreamingService service = null;
|
|
||||||
|
|
||||||
// first gather data and find service
|
|
||||||
if (intent.getData() != null) {
|
|
||||||
// this means the video was called though another app
|
|
||||||
videoUrl = intent.getData().toString();
|
|
||||||
} else if(intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
|
|
||||||
//this means that vidoe was called through share menu
|
|
||||||
String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
|
||||||
videoUrl = getUris(extraText)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
service = NewPipe.getServiceByUrl(videoUrl);
|
|
||||||
if(service == null) {
|
|
||||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
Intent callIntent = new Intent();
|
|
||||||
switch(service.getLinkTypeByUrl(videoUrl)) {
|
|
||||||
case CHANNEL:
|
|
||||||
callIntent.setClass(this, ChannelActivity.class);
|
|
||||||
break;
|
|
||||||
case STREAM:
|
|
||||||
callIntent.setClass(this, VideoItemDetailActivity.class);
|
|
||||||
callIntent.putExtra(VideoItemDetailActivity.AUTO_PLAY,
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(this)
|
|
||||||
.getBoolean(
|
|
||||||
getString(R.string.autoplay_through_intent_key), false));
|
|
||||||
break;
|
|
||||||
case PLAYLIST:
|
|
||||||
Log.e(TAG, "NOT YET DEFINED");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callIntent.putExtra(NavStack.URL, videoUrl);
|
|
||||||
callIntent.putExtra(NavStack.SERVICE_ID, service.getServiceId());
|
|
||||||
startActivity(callIntent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,24 +4,22 @@ import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||||
import org.schabi.newpipe.util.NavStack;
|
import org.schabi.newpipe.util.Constants;
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This activity is thought to open video streams form an external app using the popup playser.
|
* This activity is thought to open video streams form an external app using the popup player.
|
||||||
*/
|
*/
|
||||||
|
public class RouterPopupActivity extends Activity {
|
||||||
public class PopupActivity extends Activity {
|
//private static final String TAG = "RouterPopupActivity";
|
||||||
private static final String TAG = RouterActivity.class.toString();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes invisible separators (\p{Z}) and punctuation characters including
|
* Removes invisible separators (\p{Z}) and punctuation characters including
|
||||||
|
@ -38,6 +36,45 @@ public class PopupActivity extends Activity {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void handleIntent(Intent intent) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||||
|
&& !PermissionHelper.checkSystemAlertWindowPermission(this)) {
|
||||||
|
Toast.makeText(this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String videoUrl = "";
|
||||||
|
StreamingService service;
|
||||||
|
|
||||||
|
// first gather data and find service
|
||||||
|
if (intent.getData() != null) {
|
||||||
|
// this means the video was called though another app
|
||||||
|
videoUrl = intent.getData().toString();
|
||||||
|
} else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
|
||||||
|
//this means that vidoe was called through share menu
|
||||||
|
String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||||
|
videoUrl = getUris(extraText)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
service = NewPipe.getServiceByUrl(videoUrl);
|
||||||
|
if (service == null) {
|
||||||
|
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent callIntent = new Intent(this, PopupVideoPlayer.class);
|
||||||
|
switch (service.getLinkTypeByUrl(videoUrl)) {
|
||||||
|
case STREAM:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callIntent.putExtra(Constants.KEY_URL, videoUrl);
|
||||||
|
callIntent.putExtra(Constants.KEY_SERVICE_ID, service.getServiceId());
|
||||||
|
startService(callIntent);
|
||||||
|
}
|
||||||
|
|
||||||
private static String removeHeadingGibberish(final String input) {
|
private static String removeHeadingGibberish(final String input) {
|
||||||
int start = 0;
|
int start = 0;
|
||||||
for (int i = input.indexOf("://") - 1; i >= 0; i--) {
|
for (int i = input.indexOf("://") - 1; i >= 0; i--) {
|
||||||
|
@ -90,47 +127,4 @@ public class PopupActivity extends Activity {
|
||||||
return result.toArray(new String[result.size()]);
|
return result.toArray(new String[result.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleIntent(Intent intent) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
|
||||||
&& !PermissionHelper.checkSystemAlertWindowPermission(this)) {
|
|
||||||
Toast.makeText(this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String videoUrl = "";
|
|
||||||
StreamingService service = null;
|
|
||||||
|
|
||||||
// first gather data and find service
|
|
||||||
if (intent.getData() != null) {
|
|
||||||
// this means the video was called though another app
|
|
||||||
videoUrl = intent.getData().toString();
|
|
||||||
} else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
|
|
||||||
//this means that vidoe was called through share menu
|
|
||||||
String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
|
||||||
videoUrl = getUris(extraText)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
service = NewPipe.getServiceByUrl(videoUrl);
|
|
||||||
if (service == null) {
|
|
||||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
Intent callIntent = new Intent();
|
|
||||||
switch (service.getLinkTypeByUrl(videoUrl)) {
|
|
||||||
case STREAM:
|
|
||||||
callIntent.setClass(this, PopupVideoPlayer.class);
|
|
||||||
break;
|
|
||||||
case PLAYLIST:
|
|
||||||
Log.e(TAG, "NOT YET DEFINED");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callIntent.putExtra(NavStack.URL, videoUrl);
|
|
||||||
callIntent.putExtra(NavStack.SERVICE_ID, service.getServiceId());
|
|
||||||
startService(callIntent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,250 +0,0 @@
|
||||||
package org.schabi.newpipe.detail;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
|
||||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
|
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract {@link StreamInfo} with {@link StreamExtractor} from the given url of the given service
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
public class StreamExtractorWorker extends Thread {
|
|
||||||
private static final String TAG = "StreamExtractorWorker";
|
|
||||||
|
|
||||||
private Activity activity;
|
|
||||||
private final String videoUrl;
|
|
||||||
private final int serviceId;
|
|
||||||
private OnStreamInfoReceivedListener callback;
|
|
||||||
|
|
||||||
private final AtomicBoolean isRunning = new AtomicBoolean(false);
|
|
||||||
private final Handler handler = new Handler();
|
|
||||||
|
|
||||||
|
|
||||||
public interface OnStreamInfoReceivedListener {
|
|
||||||
void onReceive(StreamInfo info);
|
|
||||||
void onError(int messageId);
|
|
||||||
void onReCaptchaException();
|
|
||||||
void onBlockedByGemaError();
|
|
||||||
void onContentErrorWithMessage(int messageId);
|
|
||||||
void onContentError();
|
|
||||||
}
|
|
||||||
|
|
||||||
public StreamExtractorWorker(Activity activity, String videoUrl, int serviceId, OnStreamInfoReceivedListener callback) {
|
|
||||||
this.serviceId = serviceId;
|
|
||||||
this.videoUrl = videoUrl;
|
|
||||||
this.activity = activity;
|
|
||||||
this.callback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new instance <b>already</b> started of {@link StreamExtractorWorker}.<br>
|
|
||||||
* The caller is responsible to check if {@link StreamExtractorWorker#isRunning()}, or {@link StreamExtractorWorker#cancel()} it
|
|
||||||
*
|
|
||||||
* @param serviceId id of the request service
|
|
||||||
* @param url videoUrl of the service (e.g. https://www.youtube.com/watch?v=HyHNuVaZJ-k)
|
|
||||||
* @param activity activity for error reporting purposes
|
|
||||||
* @param callback listener that will be called-back when events occur (check {@link OnStreamInfoReceivedListener})
|
|
||||||
* @return new instance already started of {@link StreamExtractorWorker}
|
|
||||||
*/
|
|
||||||
public static StreamExtractorWorker startExtractorThread(int serviceId, String url, Activity activity, OnStreamInfoReceivedListener callback) {
|
|
||||||
StreamExtractorWorker extractorThread = getExtractorThread(serviceId, url, activity, callback);
|
|
||||||
extractorThread.start();
|
|
||||||
return extractorThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new instance of {@link StreamExtractorWorker}.<br>
|
|
||||||
* The caller is responsible to check if {@link StreamExtractorWorker#isRunning()}, or {@link StreamExtractorWorker#cancel()}
|
|
||||||
* when it doesn't need it anymore
|
|
||||||
* <p>
|
|
||||||
* <b>Note:</b> this instance is <b>not</b> started yet
|
|
||||||
*
|
|
||||||
* @param serviceId id of the request service
|
|
||||||
* @param url videoUrl of the service (e.g. https://www.youtube.com/watch?v=HyHNuVaZJ-k)
|
|
||||||
* @param activity activity for error reporting purposes
|
|
||||||
* @param callback listener that will be called-back when events occur (check {@link OnStreamInfoReceivedListener})
|
|
||||||
* @return instance of {@link StreamExtractorWorker}
|
|
||||||
*/
|
|
||||||
public static StreamExtractorWorker getExtractorThread(int serviceId, String url, Activity activity, OnStreamInfoReceivedListener callback) {
|
|
||||||
return new StreamExtractorWorker(activity, url, serviceId, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
//Just ignore the errors for now
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
|
||||||
public void run() {
|
|
||||||
// TODO: Improve error checking
|
|
||||||
// and this method in general
|
|
||||||
|
|
||||||
StreamInfo streamInfo = null;
|
|
||||||
StreamingService service;
|
|
||||||
try {
|
|
||||||
service = NewPipe.getService(serviceId);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
ErrorActivity.reportError(handler, activity, e, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
"", videoUrl, R.string.could_not_get_stream));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
isRunning.set(true);
|
|
||||||
StreamExtractor streamExtractor = service.getExtractorInstance(videoUrl);
|
|
||||||
streamInfo = StreamInfo.getVideoInfo(streamExtractor);
|
|
||||||
|
|
||||||
final StreamInfo info = streamInfo;
|
|
||||||
if (callback != null) handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
callback.onReceive(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
isRunning.set(false);
|
|
||||||
// look for errors during extraction
|
|
||||||
// this if statement only covers extra information.
|
|
||||||
// if these are not available or caused an error, they are just not available
|
|
||||||
// but don't render the stream information unusalbe.
|
|
||||||
if (streamInfo != null && !streamInfo.errors.isEmpty()) {
|
|
||||||
Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:");
|
|
||||||
for (Throwable e : streamInfo.errors) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Log.e(TAG, "------");
|
|
||||||
}
|
|
||||||
|
|
||||||
View rootView = activity != null ? activity.findViewById(R.id.video_item_detail) : null;
|
|
||||||
ErrorActivity.reportError(handler, activity,
|
|
||||||
streamInfo.errors, null, rootView,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, 0 /* no message for the user */));
|
|
||||||
}
|
|
||||||
|
|
||||||
// These errors render the stream information unusable.
|
|
||||||
} catch (ReCaptchaException e) {
|
|
||||||
if (callback != null) handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
callback.onReCaptchaException();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (callback != null) handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
callback.onError(R.string.network_error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (callback != null) e.printStackTrace();
|
|
||||||
} catch (YoutubeStreamExtractor.DecryptException de) {
|
|
||||||
// custom service related exceptions
|
|
||||||
ErrorActivity.reportError(handler, activity, de, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.youtube_signature_decryption_error));
|
|
||||||
handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
activity.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
de.printStackTrace();
|
|
||||||
} catch (YoutubeStreamExtractor.GemaException ge) {
|
|
||||||
if (callback != null) handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
callback.onBlockedByGemaError();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (YoutubeStreamExtractor.LiveStreamException e) {
|
|
||||||
if (callback != null) handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
callback.onContentErrorWithMessage(R.string.live_streams_not_supported);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// ----------------------------------------
|
|
||||||
catch (StreamExtractor.ContentNotAvailableException e) {
|
|
||||||
if (callback != null) handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
callback.onContentError();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (StreamInfo.StreamExctractException e) {
|
|
||||||
if (!streamInfo.errors.isEmpty()) {
|
|
||||||
// !!! if this case ever kicks in someone gets kicked out !!!
|
|
||||||
ErrorActivity.reportError(handler, activity, e, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream));
|
|
||||||
} else {
|
|
||||||
ErrorActivity.reportError(handler, activity, streamInfo.errors, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream));
|
|
||||||
}
|
|
||||||
handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
activity.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (ParsingException e) {
|
|
||||||
ErrorActivity.reportError(handler, activity, e, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.parsing_error));
|
|
||||||
handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
activity.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (Exception e) {
|
|
||||||
ErrorActivity.reportError(handler, activity, e, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.general_error));
|
|
||||||
handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
activity.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the extraction is not completed yet
|
|
||||||
*
|
|
||||||
* @return the value of the AtomicBoolean {@link #isRunning}
|
|
||||||
*/
|
|
||||||
public boolean isRunning() {
|
|
||||||
return isRunning.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel this ExtractorThread, setting the callback to null, the AtomicBoolean {@link #isRunning} to false and interrupt this thread.
|
|
||||||
* <p>
|
|
||||||
* <b>Note:</b> Any I/O that is active in the moment that this method is called will be canceled and a Exception will be thrown, because of the {@link #interrupt()}.<br>
|
|
||||||
* This is useful when you don't want the resulting {@link StreamInfo} anymore, but don't want to waste bandwidth, otherwise it'd run till it receives the StreamInfo.
|
|
||||||
*/
|
|
||||||
public void cancel() {
|
|
||||||
this.callback = null;
|
|
||||||
this.isRunning.set(false);
|
|
||||||
this.interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 6ab3dc876ebab4ed32f4ae60d3d04d000a7ea0e8
|
Subproject commit b587d175bb9cd7e84b9bdb9b594db24e654ec7ae
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.schabi.newpipe.fragments;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for communication purposes between activity and fragment
|
||||||
|
*/
|
||||||
|
public interface OnItemSelectedListener {
|
||||||
|
void onItemSelected(StreamingService.LinkType linkType, int serviceId, String url, String name);
|
||||||
|
}
|
|
@ -0,0 +1,416 @@
|
||||||
|
package org.schabi.newpipe.fragments.channel;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
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.ImageErrorLoadingListener;
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||||
|
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
||||||
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
|
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
import org.schabi.newpipe.workers.ChannelExtractorWorker;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||||
|
* ChannelFragment.java is part of NewPipe.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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 ChannelFragment extends Fragment implements ChannelExtractorWorker.OnChannelInfoReceive {
|
||||||
|
private static final String TAG = "ChannelFragment";
|
||||||
|
|
||||||
|
private AppCompatActivity activity;
|
||||||
|
private OnItemSelectedListener onItemSelectedListener;
|
||||||
|
private InfoListAdapter infoListAdapter;
|
||||||
|
|
||||||
|
private ChannelExtractorWorker currentExtractorWorker;
|
||||||
|
private ChannelInfo currentChannelInfo;
|
||||||
|
private int serviceId = -1;
|
||||||
|
private String channelName = "";
|
||||||
|
private String channelUrl = "";
|
||||||
|
|
||||||
|
private boolean isLoading = false;
|
||||||
|
private int pageNumber = 0;
|
||||||
|
private boolean hasNextPage = true;
|
||||||
|
|
||||||
|
private ImageLoader imageLoader = ImageLoader.getInstance();
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Views
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private View rootView = null;
|
||||||
|
|
||||||
|
private RecyclerView channelVideosList;
|
||||||
|
private LinearLayoutManager layoutManager;
|
||||||
|
private ProgressBar loadingProgressBar;
|
||||||
|
|
||||||
|
private View headerRootLayout;
|
||||||
|
private ImageView headerChannelBanner;
|
||||||
|
private ImageView headerAvatarView;
|
||||||
|
private TextView headerTitleView;
|
||||||
|
private TextView headerSubscriberView;
|
||||||
|
private Button headerSubscriberButton;
|
||||||
|
private View headerSubscriberLayout;
|
||||||
|
|
||||||
|
/*////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
public ChannelFragment() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ChannelFragment newInstance(int serviceId, String url, String name) {
|
||||||
|
ChannelFragment instance = newInstance();
|
||||||
|
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putString(Constants.KEY_URL, url);
|
||||||
|
bundle.putString(Constants.KEY_TITLE, name);
|
||||||
|
bundle.putInt(Constants.KEY_SERVICE_ID, serviceId);
|
||||||
|
|
||||||
|
instance.setArguments(bundle);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ChannelFragment newInstance() {
|
||||||
|
return new ChannelFragment();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Fragment's LifeCycle
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
activity = ((AppCompatActivity) context);
|
||||||
|
onItemSelectedListener = ((OnItemSelectedListener) context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
isLoading = false;
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
channelUrl = savedInstanceState.getString(Constants.KEY_URL);
|
||||||
|
channelName = savedInstanceState.getString(Constants.KEY_TITLE);
|
||||||
|
serviceId = savedInstanceState.getInt(Constants.KEY_SERVICE_ID, -1);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Bundle args = getArguments();
|
||||||
|
if (args != null) {
|
||||||
|
channelUrl = args.getString(Constants.KEY_URL);
|
||||||
|
channelName = args.getString(Constants.KEY_TITLE);
|
||||||
|
serviceId = args.getInt(Constants.KEY_SERVICE_ID, 0);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
ErrorActivity.reportError(getActivity(), e, null,
|
||||||
|
getActivity().findViewById(android.R.id.content),
|
||||||
|
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
||||||
|
NewPipe.getNameOfService(serviceId),
|
||||||
|
"", R.string.general_error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
rootView = inflater.inflate(R.layout.fragment_channel, container, false);
|
||||||
|
return rootView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
loadingProgressBar = (ProgressBar) view.findViewById(R.id.loading_progress_bar);
|
||||||
|
channelVideosList = (RecyclerView) view.findViewById(R.id.channel_streams_view);
|
||||||
|
|
||||||
|
infoListAdapter = new InfoListAdapter(activity, rootView);
|
||||||
|
layoutManager = new LinearLayoutManager(activity);
|
||||||
|
channelVideosList.setLayoutManager(layoutManager);
|
||||||
|
|
||||||
|
headerRootLayout = activity.getLayoutInflater().inflate(R.layout.channel_header, channelVideosList, false);
|
||||||
|
infoListAdapter.setHeader(headerRootLayout);
|
||||||
|
infoListAdapter.setFooter(activity.getLayoutInflater().inflate(R.layout.pignate_footer, channelVideosList, false));
|
||||||
|
channelVideosList.setAdapter(infoListAdapter);
|
||||||
|
|
||||||
|
headerChannelBanner = (ImageView) headerRootLayout.findViewById(R.id.channel_banner_image);
|
||||||
|
headerAvatarView = (ImageView) headerRootLayout.findViewById(R.id.channel_avatar_view);
|
||||||
|
headerTitleView = (TextView) headerRootLayout.findViewById(R.id.channel_title_view);
|
||||||
|
headerSubscriberView = (TextView) headerRootLayout.findViewById(R.id.channel_subscriber_view);
|
||||||
|
headerSubscriberButton = (Button) headerRootLayout.findViewById(R.id.channel_subscribe_button);
|
||||||
|
headerSubscriberLayout = headerRootLayout.findViewById(R.id.channel_subscriber_layout);
|
||||||
|
|
||||||
|
initListeners();
|
||||||
|
|
||||||
|
isLoading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
headerAvatarView.setImageBitmap(null);
|
||||||
|
headerChannelBanner.setImageBitmap(null);
|
||||||
|
channelVideosList.removeAllViews();
|
||||||
|
|
||||||
|
rootView = null;
|
||||||
|
channelVideosList = null;
|
||||||
|
layoutManager = null;
|
||||||
|
loadingProgressBar = null;
|
||||||
|
headerRootLayout = null;
|
||||||
|
headerChannelBanner = null;
|
||||||
|
headerAvatarView = null;
|
||||||
|
headerTitleView = null;
|
||||||
|
headerSubscriberView = null;
|
||||||
|
headerSubscriberButton = null;
|
||||||
|
headerSubscriberLayout = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
if (isLoading) {
|
||||||
|
requestData(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
if (currentExtractorWorker != null) currentExtractorWorker.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
imageLoader.clearMemoryCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
outState.putString(Constants.KEY_URL, channelUrl);
|
||||||
|
outState.putString(Constants.KEY_TITLE, channelName);
|
||||||
|
outState.putInt(Constants.KEY_SERVICE_ID, serviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Menu
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
inflater.inflate(R.menu.menu_channel, menu);
|
||||||
|
|
||||||
|
ActionBar supportActionBar = activity.getSupportActionBar();
|
||||||
|
if (supportActionBar != null) {
|
||||||
|
supportActionBar.setDisplayShowTitleEnabled(true);
|
||||||
|
supportActionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
//noinspection deprecation
|
||||||
|
supportActionBar.setNavigationMode(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
super.onOptionsItemSelected(item);
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_item_openInBrowser: {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse(channelUrl));
|
||||||
|
|
||||||
|
startActivity(Intent.createChooser(intent, getString(R.string.choose_browser)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case R.id.menu_item_share:
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Intent.ACTION_SEND);
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, channelUrl);
|
||||||
|
intent.setType("text/plain");
|
||||||
|
startActivity(Intent.createChooser(intent, getString(R.string.share_dialog_title)));
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Init's
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private void initListeners() {
|
||||||
|
infoListAdapter.setOnStreamInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void selected(int serviceId, String url, String title) {
|
||||||
|
NavigationHelper.openVideoDetail(onItemSelectedListener, serviceId, url, title);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// detect if list has ben scrolled to the bottom
|
||||||
|
channelVideosList.setOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||||
|
int pastVisiblesItems, visibleItemCount, totalItemCount;
|
||||||
|
super.onScrolled(recyclerView, dx, dy);
|
||||||
|
//check for scroll down
|
||||||
|
if (dy > 0) {
|
||||||
|
visibleItemCount = layoutManager.getChildCount();
|
||||||
|
totalItemCount = layoutManager.getItemCount();
|
||||||
|
pastVisiblesItems = layoutManager.findFirstVisibleItemPosition();
|
||||||
|
|
||||||
|
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount && !currentExtractorWorker.isRunning() && hasNextPage) {
|
||||||
|
pageNumber++;
|
||||||
|
requestData(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
headerSubscriberButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
Log.d(TAG, currentChannelInfo.feed_url);
|
||||||
|
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(currentChannelInfo.feed_url));
|
||||||
|
startActivity(i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
private String buildSubscriberString(long count) {
|
||||||
|
String out = NumberFormat.getNumberInstance().format(count);
|
||||||
|
out += " " + getString(count > 1 ? R.string.subscriber_plural : R.string.subscriber);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestData(boolean onlyVideos) {
|
||||||
|
if (currentExtractorWorker != null && currentExtractorWorker.isRunning()) currentExtractorWorker.cancel();
|
||||||
|
|
||||||
|
isLoading = true;
|
||||||
|
if (!onlyVideos) {
|
||||||
|
//delete already displayed content
|
||||||
|
loadingProgressBar.setVisibility(View.VISIBLE);
|
||||||
|
infoListAdapter.clearSteamItemList();
|
||||||
|
pageNumber = 0;
|
||||||
|
headerSubscriberLayout.setVisibility(View.GONE);
|
||||||
|
headerTitleView.setText(channelName != null ? channelName : "");
|
||||||
|
if (activity.getSupportActionBar() != null) activity.getSupportActionBar().setTitle(channelName != null ? channelName : "");
|
||||||
|
if (SDK_INT >= 21) {
|
||||||
|
headerChannelBanner.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.channel_banner));
|
||||||
|
headerAvatarView.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.buddy));
|
||||||
|
}
|
||||||
|
infoListAdapter.showFooter(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentExtractorWorker = new ChannelExtractorWorker(activity, serviceId, channelUrl, pageNumber, this);
|
||||||
|
currentExtractorWorker.setOnlyVideos(onlyVideos);
|
||||||
|
currentExtractorWorker.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addVideos(ChannelInfo info) {
|
||||||
|
infoListAdapter.addInfoItemList(info.related_streams);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// OnChannelInfoReceiveListener
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(ChannelInfo info) {
|
||||||
|
if (info == null || isRemoving() || !isVisible()) return;
|
||||||
|
|
||||||
|
currentChannelInfo = info;
|
||||||
|
|
||||||
|
if (!currentExtractorWorker.isOnlyVideos()) {
|
||||||
|
headerRootLayout.setVisibility(View.VISIBLE);
|
||||||
|
loadingProgressBar.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
if (info.channel_name != null && !info.channel_name.isEmpty()) {
|
||||||
|
if (activity.getSupportActionBar() != null) activity.getSupportActionBar().setTitle(info.channel_name);
|
||||||
|
headerTitleView.setText(info.channel_name);
|
||||||
|
channelName = info.channel_name;
|
||||||
|
} else channelName = "";
|
||||||
|
|
||||||
|
if (info.banner_url != null && !info.banner_url.isEmpty()) {
|
||||||
|
imageLoader.displayImage(info.banner_url, headerChannelBanner,
|
||||||
|
new ImageErrorLoadingListener(activity, rootView, info.service_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.avatar_url != null && !info.avatar_url.isEmpty()) {
|
||||||
|
headerAvatarView.setVisibility(View.VISIBLE);
|
||||||
|
imageLoader.displayImage(info.avatar_url, headerAvatarView,
|
||||||
|
new ImageErrorLoadingListener(activity, rootView, info.service_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.subscriberCount != -1) {
|
||||||
|
headerSubscriberView.setText(buildSubscriberString(info.subscriberCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((info.feed_url != null && !info.feed_url.isEmpty()) || (info.subscriberCount != -1)) {
|
||||||
|
headerSubscriberLayout.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.feed_url == null || info.feed_url.isEmpty()) {
|
||||||
|
headerSubscriberButton.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
infoListAdapter.showFooter(true);
|
||||||
|
}
|
||||||
|
hasNextPage = info.hasNextPage;
|
||||||
|
if (!hasNextPage) infoListAdapter.showFooter(false);
|
||||||
|
addVideos(info);
|
||||||
|
isLoading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(int messageId) {
|
||||||
|
Toast.makeText(activity, messageId, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
package org.schabi.newpipe.detail;
|
package org.schabi.newpipe.fragments.detail;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
|
@ -12,9 +11,9 @@ import android.view.MenuItem;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||||
|
import org.schabi.newpipe.util.Utils;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -50,7 +49,7 @@ class ActionBarHandler {
|
||||||
private Menu menu;
|
private Menu menu;
|
||||||
|
|
||||||
// Only callbacks are listed here, there are more actions which don't need a callback.
|
// Only callbacks are listed here, there are more actions which don't need a callback.
|
||||||
// those are edited directly. Typically VideoItemDetailFragment will implement those callbacks.
|
// those are edited directly. Typically VideoDetailFragment will implement those callbacks.
|
||||||
private OnActionListener onShareListener;
|
private OnActionListener onShareListener;
|
||||||
private OnActionListener onOpenInBrowserListener;
|
private OnActionListener onOpenInBrowserListener;
|
||||||
private OnActionListener onOpenInPopupListener;
|
private OnActionListener onOpenInPopupListener;
|
||||||
|
@ -89,7 +88,7 @@ class ActionBarHandler {
|
||||||
VideoStream item = videoStreams.get(i);
|
VideoStream item = videoStreams.get(i);
|
||||||
itemArray[i] = MediaFormat.getNameById(item.format) + " " + item.resolution;
|
itemArray[i] = MediaFormat.getNameById(item.format) + " " + item.resolution;
|
||||||
}
|
}
|
||||||
int defaultResolution = getDefaultResolution(videoStreams);
|
int defaultResolution = Utils.getPreferredResolution(activity, videoStreams);
|
||||||
|
|
||||||
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(),
|
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(),
|
||||||
android.R.layout.simple_spinner_dropdown_item, itemArray);
|
android.R.layout.simple_spinner_dropdown_item, itemArray);
|
||||||
|
@ -110,43 +109,6 @@ class ActionBarHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private int getDefaultResolution(final List<VideoStream> videoStreams) {
|
|
||||||
if (defaultPreferences == null)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
String defaultResolution = defaultPreferences
|
|
||||||
.getString(activity.getString(R.string.default_resolution_key),
|
|
||||||
activity.getString(R.string.default_resolution_value));
|
|
||||||
|
|
||||||
String preferedFormat = defaultPreferences
|
|
||||||
.getString(activity.getString(R.string.preferred_video_format_key),
|
|
||||||
activity.getString(R.string.preferred_video_format_default));
|
|
||||||
|
|
||||||
// first try to find the one with the right resolution
|
|
||||||
int selectedFormat = 0;
|
|
||||||
for (int i = 0; i < videoStreams.size(); i++) {
|
|
||||||
VideoStream item = videoStreams.get(i);
|
|
||||||
if (defaultResolution.equals(item.resolution)) {
|
|
||||||
selectedFormat = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// than try to find the one with the right resolution and format
|
|
||||||
for (int i = 0; i < videoStreams.size(); i++) {
|
|
||||||
VideoStream item = videoStreams.get(i);
|
|
||||||
if (defaultResolution.equals(item.resolution)
|
|
||||||
&& preferedFormat.equals(MediaFormat.getNameById(item.format))) {
|
|
||||||
selectedFormat = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// this is actually an error,
|
|
||||||
// but maybe there is really no stream fitting to the default value.
|
|
||||||
return selectedFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setupMenu(Menu menu, MenuInflater inflater) {
|
public void setupMenu(Menu menu, MenuInflater inflater) {
|
||||||
this.menu = menu;
|
this.menu = menu;
|
||||||
|
|
||||||
|
@ -187,11 +149,6 @@ class ActionBarHandler {
|
||||||
onDownloadListener.onActionSelected(selectedVideoStream);
|
onDownloadListener.onActionSelected(selectedVideoStream);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case R.id.action_settings: {
|
|
||||||
Intent intent = new Intent(activity, SettingsActivity.class);
|
|
||||||
activity.startActivity(intent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case R.id.action_play_with_kodi:
|
case R.id.action_play_with_kodi:
|
||||||
if(onPlayWithKodiListener != null) {
|
if(onPlayWithKodiListener != null) {
|
||||||
onPlayWithKodiListener.onActionSelected(selectedVideoStream);
|
onPlayWithKodiListener.onActionSelected(selectedVideoStream);
|
||||||
|
@ -202,12 +159,6 @@ class ActionBarHandler {
|
||||||
onPlayAudioListener.onActionSelected(selectedVideoStream);
|
onPlayAudioListener.onActionSelected(selectedVideoStream);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_item_downloads: {
|
|
||||||
Intent intent =
|
|
||||||
new Intent(activity, org.schabi.newpipe.download.DownloadActivity.class);
|
|
||||||
activity.startActivity(intent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case R.id.menu_item_popup: {
|
case R.id.menu_item_popup: {
|
||||||
if(onOpenInPopupListener != null) {
|
if(onOpenInPopupListener != null) {
|
||||||
onOpenInPopupListener.onActionSelected(selectedVideoStream);
|
onOpenInPopupListener.onActionSelected(selectedVideoStream);
|
|
@ -0,0 +1,31 @@
|
||||||
|
package org.schabi.newpipe.fragments.detail;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public class StackItem implements Serializable {
|
||||||
|
private String title, url;
|
||||||
|
|
||||||
|
public StackItem(String url, String title) {
|
||||||
|
this.title = title;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getUrl() + " > " + getTitle();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
package org.schabi.newpipe.detail;
|
package org.schabi.newpipe.fragments.detail;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
@ -11,14 +13,18 @@ import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -49,6 +55,7 @@ import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.stream_info.AudioStream;
|
import org.schabi.newpipe.extractor.stream_info.AudioStream;
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||||
|
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
import org.schabi.newpipe.player.AbstractPlayer;
|
import org.schabi.newpipe.player.AbstractPlayer;
|
||||||
import org.schabi.newpipe.player.BackgroundPlayer;
|
import org.schabi.newpipe.player.BackgroundPlayer;
|
||||||
|
@ -56,30 +63,38 @@ import org.schabi.newpipe.player.ExoPlayerActivity;
|
||||||
import org.schabi.newpipe.player.PlayVideoActivity;
|
import org.schabi.newpipe.player.PlayVideoActivity;
|
||||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
import org.schabi.newpipe.util.NavStack;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.Utils;
|
||||||
|
import org.schabi.newpipe.workers.StreamExtractorWorker;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Stack;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
@SuppressWarnings("FieldCanBeLocal")
|
@SuppressWarnings("FieldCanBeLocal")
|
||||||
public class VideoItemDetailActivity extends AppCompatActivity implements StreamExtractorWorker.OnStreamInfoReceivedListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
public class VideoDetailFragment extends Fragment implements StreamExtractorWorker.OnStreamInfoReceivedListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
|
private final String TAG = "VideoDetailFragment@" + Integer.toHexString(hashCode());
|
||||||
|
|
||||||
private static final String TAG = "VideoItemDetailActivity";
|
|
||||||
private static final String KORE_PACKET = "org.xbmc.kore";
|
private static final String KORE_PACKET = "org.xbmc.kore";
|
||||||
|
private static final String SERVICE_ID_KEY = "service_id_key";
|
||||||
|
private static final String VIDEO_URL_KEY = "video_url_key";
|
||||||
|
private static final String VIDEO_TITLE_KEY = "video_title_key";
|
||||||
|
private static final String STACK_KEY = "stack_key";
|
||||||
|
|
||||||
/**
|
|
||||||
* The fragment argument representing the item ID that this fragment
|
|
||||||
* represents.
|
|
||||||
*/
|
|
||||||
public static final String AUTO_PLAY = "auto_play";
|
public static final String AUTO_PLAY = "auto_play";
|
||||||
|
|
||||||
|
private AppCompatActivity activity;
|
||||||
|
private OnItemSelectedListener onItemSelectedListener;
|
||||||
private ActionBarHandler actionBarHandler;
|
private ActionBarHandler actionBarHandler;
|
||||||
|
|
||||||
private InfoItemBuilder infoItemBuilder = null;
|
private InfoItemBuilder infoItemBuilder = null;
|
||||||
private StreamInfo currentStreamInfo = null;
|
private StreamInfo currentStreamInfo = null;
|
||||||
private StreamExtractorWorker curExtractorThread;
|
private StreamExtractorWorker curExtractorWorker;
|
||||||
|
|
||||||
|
private String videoTitle;
|
||||||
private String videoUrl;
|
private String videoUrl;
|
||||||
private int serviceId = -1;
|
private int serviceId = -1;
|
||||||
|
|
||||||
|
@ -89,9 +104,9 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
private boolean autoPlayEnabled;
|
private boolean autoPlayEnabled;
|
||||||
private boolean showRelatedStreams;
|
private boolean showRelatedStreams;
|
||||||
|
|
||||||
private ImageLoader imageLoader = ImageLoader.getInstance();
|
private static final ImageLoader imageLoader = ImageLoader.getInstance();
|
||||||
private DisplayImageOptions displayImageOptions =
|
private static final DisplayImageOptions displayImageOptions =
|
||||||
new DisplayImageOptions.Builder().displayer(new FadeInBitmapDisplayer(400)).cacheInMemory(true).build();
|
new DisplayImageOptions.Builder().displayer(new FadeInBitmapDisplayer(400)).cacheInMemory(false).build();
|
||||||
private Bitmap streamThumbnail = null;
|
private Bitmap streamThumbnail = null;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -130,55 +145,141 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
private RelativeLayout relatedStreamRootLayout;
|
private RelativeLayout relatedStreamRootLayout;
|
||||||
private LinearLayout relatedStreamsView;
|
private LinearLayout relatedStreamsView;
|
||||||
|
|
||||||
|
/*////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
public static VideoDetailFragment getInstance(int serviceId, String url) {
|
||||||
|
return getInstance(serviceId, url, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VideoDetailFragment getInstance(int serviceId, String videoUrl, String videoTitle) {
|
||||||
|
VideoDetailFragment instance = getInstance();
|
||||||
|
instance.selectVideo(serviceId, videoUrl, videoTitle);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VideoDetailFragment getInstance() {
|
||||||
|
return new VideoDetailFragment();
|
||||||
|
}
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Activity's Lifecycle
|
// Fragment's Lifecycle
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
activity = (AppCompatActivity) context;
|
||||||
|
onItemSelectedListener = (OnItemSelectedListener) context;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
videoTitle = savedInstanceState.getString(VIDEO_TITLE_KEY);
|
||||||
|
videoUrl = savedInstanceState.getString(VIDEO_URL_KEY);
|
||||||
|
serviceId = savedInstanceState.getInt(SERVICE_ID_KEY);
|
||||||
|
Serializable serializable = savedInstanceState.getSerializable(STACK_KEY);
|
||||||
|
if (serializable instanceof Stack) {
|
||||||
|
//noinspection unchecked
|
||||||
|
Stack<StackItem> list = (Stack<StackItem>) serializable;
|
||||||
|
stack.clear();
|
||||||
|
stack.addAll(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
showRelatedStreams = PreferenceManager.getDefaultSharedPreferences(this).getBoolean(getString(R.string.show_next_video_key), true);
|
showRelatedStreams = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(getString(R.string.show_next_video_key), true);
|
||||||
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
|
PreferenceManager.getDefaultSharedPreferences(activity).registerOnSharedPreferenceChangeListener(this);
|
||||||
|
activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||||
ThemeHelper.setTheme(this, true);
|
isLoading.set(false);
|
||||||
setContentView(R.layout.activity_videoitem_detail);
|
setHasOptionsMenu(true);
|
||||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
|
||||||
|
|
||||||
if (getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
else Log.e(TAG, "Could not get SupportActionBar");
|
|
||||||
|
|
||||||
initViews();
|
|
||||||
initListeners();
|
|
||||||
handleIntent(getIntent());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.fragment_video_detail, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View rootView, Bundle savedInstanceState) {
|
||||||
|
initViews(rootView);
|
||||||
|
initListeners();
|
||||||
|
isLoading.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
thumbnailImageView.setImageBitmap(null);
|
||||||
|
relatedStreamsView.removeAllViews();
|
||||||
|
|
||||||
|
loadingProgressBar = null;
|
||||||
|
|
||||||
|
parallaxScrollRootView = null;
|
||||||
|
contentRootLayout = null;
|
||||||
|
|
||||||
|
thumbnailBackgroundButton = null;
|
||||||
|
thumbnailImageView = null;
|
||||||
|
thumbnailPlayButton = null;
|
||||||
|
|
||||||
|
videoTitleRoot = null;
|
||||||
|
videoTitleTextView = null;
|
||||||
|
videoTitleToggleArrow = null;
|
||||||
|
videoCountView = null;
|
||||||
|
|
||||||
|
videoDescriptionRootLayout = null;
|
||||||
|
videoUploadDateView = null;
|
||||||
|
videoDescriptionView = null;
|
||||||
|
|
||||||
|
uploaderButton = null;
|
||||||
|
uploaderTextView = null;
|
||||||
|
uploaderThumb = null;
|
||||||
|
|
||||||
|
thumbsUpTextView = null;
|
||||||
|
thumbsUpImageView = null;
|
||||||
|
thumbsDownTextView = null;
|
||||||
|
thumbsDownImageView = null;
|
||||||
|
thumbsDisabledTextView = null;
|
||||||
|
|
||||||
|
nextStreamTitle = null;
|
||||||
|
relatedStreamRootLayout = null;
|
||||||
|
relatedStreamsView = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
// Currently only used for enable/disable related videos
|
// Currently only used for enable/disable related videos
|
||||||
// but can be extended for other live settings change
|
// but can be extended for other live settings changes
|
||||||
if (needUpdate) {
|
if (needUpdate) {
|
||||||
if (relatedStreamsView != null) initRelatedVideos(currentStreamInfo);
|
if (relatedStreamsView != null) initRelatedVideos(currentStreamInfo);
|
||||||
needUpdate = false;
|
needUpdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if it was loading when the activity was stopped/paused,
|
||||||
|
// because when this happen, the curExtractorWorker is cancelled
|
||||||
|
if (isLoading.get()) selectAndLoadVideo(serviceId, videoUrl, videoTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onNewIntent(Intent intent) {
|
public void onStop() {
|
||||||
super.onNewIntent(intent);
|
super.onStop();
|
||||||
setIntent(intent);
|
if (curExtractorWorker != null && curExtractorWorker.isRunning()) curExtractorWorker.cancel();
|
||||||
handleIntent(intent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onDestroy() {
|
||||||
try {
|
super.onDestroy();
|
||||||
NavStack.getInstance().navBack(this);
|
PreferenceManager.getDefaultSharedPreferences(activity).unregisterOnSharedPreferenceChangeListener(this);
|
||||||
} catch (Exception e) {
|
imageLoader.clearMemoryCache();
|
||||||
ErrorActivity.reportUiError(this, e);
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
outState.putString(VIDEO_URL_KEY, videoUrl);
|
||||||
|
outState.putString(VIDEO_TITLE_KEY, videoTitle);
|
||||||
|
outState.putInt(SERVICE_ID_KEY, serviceId);
|
||||||
|
outState.putSerializable(STACK_KEY, stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -186,9 +287,8 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
case ReCaptchaActivity.RECAPTCHA_REQUEST:
|
case ReCaptchaActivity.RECAPTCHA_REQUEST:
|
||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
String videoUrl = getIntent().getStringExtra(NavStack.URL);
|
NavigationHelper.openVideoDetail(onItemSelectedListener, serviceId, videoUrl, videoTitle);
|
||||||
NavStack.getInstance().openDetailActivity(this, videoUrl, serviceId);
|
|
||||||
} else Log.e(TAG, "ReCaptcha failed");
|
} else Log.e(TAG, "ReCaptcha failed");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -209,48 +309,47 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
// Init
|
// Init
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public void initViews() {
|
private void initViews(View rootView) {
|
||||||
loadingProgressBar = (ProgressBar) findViewById(R.id.detail_loading_progress_bar);
|
loadingProgressBar = (ProgressBar) rootView.findViewById(R.id.detail_loading_progress_bar);
|
||||||
|
|
||||||
parallaxScrollRootView = (ParallaxScrollView) findViewById(R.id.detail_main_content);
|
parallaxScrollRootView = (ParallaxScrollView) rootView.findViewById(R.id.detail_main_content);
|
||||||
|
|
||||||
//thumbnailRootLayout = (RelativeLayout) findViewById(R.id.detail_thumbnail_root_layout);
|
//thumbnailRootLayout = (RelativeLayout) rootView.findViewById(R.id.detail_thumbnail_root_layout);
|
||||||
thumbnailBackgroundButton = (Button) findViewById(R.id.detail_stream_thumbnail_background_button);
|
thumbnailBackgroundButton = (Button) rootView.findViewById(R.id.detail_stream_thumbnail_background_button);
|
||||||
thumbnailImageView = (ImageView) findViewById(R.id.detail_thumbnail_image_view);
|
thumbnailImageView = (ImageView) rootView.findViewById(R.id.detail_thumbnail_image_view);
|
||||||
thumbnailPlayButton = (ImageView) findViewById(R.id.detail_thumbnail_play_button);
|
thumbnailPlayButton = (ImageView) rootView.findViewById(R.id.detail_thumbnail_play_button);
|
||||||
|
|
||||||
contentRootLayout = (RelativeLayout) findViewById(R.id.detail_content_root_layout);
|
contentRootLayout = (RelativeLayout) rootView.findViewById(R.id.detail_content_root_layout);
|
||||||
|
|
||||||
videoTitleRoot = findViewById(R.id.detail_title_root_layout);
|
videoTitleRoot = rootView.findViewById(R.id.detail_title_root_layout);
|
||||||
videoTitleTextView = (TextView) findViewById(R.id.detail_video_title_view);
|
videoTitleTextView = (TextView) rootView.findViewById(R.id.detail_video_title_view);
|
||||||
videoTitleToggleArrow = (ImageView) findViewById(R.id.detail_toggle_description_view);
|
videoTitleToggleArrow = (ImageView) rootView.findViewById(R.id.detail_toggle_description_view);
|
||||||
videoCountView = (TextView) findViewById(R.id.detail_view_count_view);
|
videoCountView = (TextView) rootView.findViewById(R.id.detail_view_count_view);
|
||||||
|
|
||||||
videoDescriptionRootLayout = (RelativeLayout) findViewById(R.id.detail_description_root_layout);
|
videoDescriptionRootLayout = (RelativeLayout) rootView.findViewById(R.id.detail_description_root_layout);
|
||||||
videoUploadDateView = (TextView) findViewById(R.id.detail_upload_date_view);
|
videoUploadDateView = (TextView) rootView.findViewById(R.id.detail_upload_date_view);
|
||||||
videoDescriptionView = (TextView) findViewById(R.id.detail_description_view);
|
videoDescriptionView = (TextView) rootView.findViewById(R.id.detail_description_view);
|
||||||
|
|
||||||
//thumbsRootLayout = (LinearLayout) findViewById(R.id.detail_thumbs_root_layout);
|
//thumbsRootLayout = (LinearLayout) rootView.findViewById(R.id.detail_thumbs_root_layout);
|
||||||
thumbsUpTextView = (TextView) findViewById(R.id.detail_thumbs_up_count_view);
|
thumbsUpTextView = (TextView) rootView.findViewById(R.id.detail_thumbs_up_count_view);
|
||||||
thumbsUpImageView = (ImageView) findViewById(R.id.detail_thumbs_up_img_view);
|
thumbsUpImageView = (ImageView) rootView.findViewById(R.id.detail_thumbs_up_img_view);
|
||||||
thumbsDownTextView = (TextView) findViewById(R.id.detail_thumbs_down_count_view);
|
thumbsDownTextView = (TextView) rootView.findViewById(R.id.detail_thumbs_down_count_view);
|
||||||
thumbsDownImageView = (ImageView) findViewById(R.id.detail_thumbs_down_img_view);
|
thumbsDownImageView = (ImageView) rootView.findViewById(R.id.detail_thumbs_down_img_view);
|
||||||
thumbsDisabledTextView = (TextView) findViewById(R.id.detail_thumbs_disabled_view);
|
thumbsDisabledTextView = (TextView) rootView.findViewById(R.id.detail_thumbs_disabled_view);
|
||||||
|
|
||||||
//uploaderRootLayout = (FrameLayout) findViewById(R.id.detail_uploader_root_layout);
|
//uploaderRootLayout = (FrameLayout) rootView.findViewById(R.id.detail_uploader_root_layout);
|
||||||
uploaderButton = (Button) findViewById(R.id.detail_uploader_button);
|
uploaderButton = (Button) rootView.findViewById(R.id.detail_uploader_button);
|
||||||
uploaderTextView = (TextView) findViewById(R.id.detail_uploader_text_view);
|
uploaderTextView = (TextView) rootView.findViewById(R.id.detail_uploader_text_view);
|
||||||
uploaderThumb = (ImageView) findViewById(R.id.detail_uploader_thumbnail_view);
|
uploaderThumb = (ImageView) rootView.findViewById(R.id.detail_uploader_thumbnail_view);
|
||||||
|
|
||||||
relatedStreamRootLayout = (RelativeLayout) findViewById(R.id.detail_related_streams_root_layout);
|
relatedStreamRootLayout = (RelativeLayout) rootView.findViewById(R.id.detail_related_streams_root_layout);
|
||||||
nextStreamTitle = (TextView) findViewById(R.id.detail_next_stream_title);
|
nextStreamTitle = (TextView) rootView.findViewById(R.id.detail_next_stream_title);
|
||||||
relatedStreamsView = (LinearLayout) findViewById(R.id.detail_related_streams_view);
|
relatedStreamsView = (LinearLayout) rootView.findViewById(R.id.detail_related_streams_view);
|
||||||
|
|
||||||
actionBarHandler = new ActionBarHandler(this);
|
actionBarHandler = new ActionBarHandler(activity);
|
||||||
actionBarHandler.setupNavMenu(this);
|
|
||||||
videoDescriptionView.setMovementMethod(LinkMovementMethod.getInstance());
|
videoDescriptionView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
|
|
||||||
infoItemBuilder = new InfoItemBuilder(this, findViewById(android.R.id.content));
|
infoItemBuilder = new InfoItemBuilder(activity, rootView.findViewById(android.R.id.content));
|
||||||
|
|
||||||
setHeightThumbnail();
|
setHeightThumbnail();
|
||||||
}
|
}
|
||||||
|
@ -279,15 +378,16 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
|
|
||||||
infoItemBuilder.setOnStreamInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
|
infoItemBuilder.setOnStreamInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void selected(String url, int serviceId) {
|
public void selected(int serviceId, String url, String title) {
|
||||||
NavStack.getInstance().openDetailActivity(VideoItemDetailActivity.this, url, serviceId);
|
//NavigationHelper.openVideoDetail(activity, url, serviceId);
|
||||||
|
selectAndLoadVideo(serviceId, url, title);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
uploaderButton.setOnClickListener(new View.OnClickListener() {
|
uploaderButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
NavStack.getInstance().openChannelActivity(VideoItemDetailActivity.this, currentStreamInfo.channel_url, currentStreamInfo.service_id);
|
NavigationHelper.openChannel(onItemSelectedListener, currentStreamInfo.service_id, currentStreamInfo.channel_url, currentStreamInfo.uploader);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -311,14 +411,14 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
|
ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
|
||||||
Intent intent = new Intent(AbstractPlayer.ACTION_UPDATE_THUMB);
|
Intent intent = new Intent(AbstractPlayer.ACTION_UPDATE_THUMB);
|
||||||
intent.putExtra(AbstractPlayer.VIDEO_URL, currentStreamInfo.webpage_url);
|
intent.putExtra(AbstractPlayer.VIDEO_URL, currentStreamInfo.webpage_url);
|
||||||
sendBroadcast(intent);
|
activity.sendBroadcast(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||||
ErrorActivity.reportError(VideoItemDetailActivity.this,
|
ErrorActivity.reportError(activity,
|
||||||
failReason.getCause(), null, findViewById(android.R.id.content),
|
failReason.getCause(), null, activity.findViewById(android.R.id.content),
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE,
|
ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE,
|
||||||
NewPipe.getNameOfService(currentStreamInfo.service_id), imageUri,
|
NewPipe.getNameOfService(currentStreamInfo.service_id), imageUri,
|
||||||
R.string.could_not_load_thumbnails));
|
R.string.could_not_load_thumbnails));
|
||||||
|
@ -330,7 +430,7 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
if (info.uploader_thumbnail_url != null && !info.uploader_thumbnail_url.isEmpty()) {
|
if (info.uploader_thumbnail_url != null && !info.uploader_thumbnail_url.isEmpty()) {
|
||||||
imageLoader.displayImage(info.uploader_thumbnail_url,
|
imageLoader.displayImage(info.uploader_thumbnail_url,
|
||||||
uploaderThumb, displayImageOptions,
|
uploaderThumb, displayImageOptions,
|
||||||
new ImageErrorLoadingListener(this, findViewById(android.R.id.content), info.service_id));
|
new ImageErrorLoadingListener(activity, activity.findViewById(android.R.id.content), info.service_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,15 +441,15 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
nextStreamTitle.setVisibility(View.VISIBLE);
|
nextStreamTitle.setVisibility(View.VISIBLE);
|
||||||
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, info.next_video));
|
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, info.next_video));
|
||||||
relatedStreamsView.addView(getSeparatorView());
|
relatedStreamsView.addView(getSeparatorView());
|
||||||
relatedStreamsView.setVisibility(View.VISIBLE);
|
relatedStreamRootLayout.setVisibility(View.VISIBLE);
|
||||||
} else nextStreamTitle.setVisibility(View.GONE);
|
} else nextStreamTitle.setVisibility(View.GONE);
|
||||||
|
|
||||||
if (info.related_streams != null && !info.related_streams.isEmpty() && showRelatedStreams) {
|
if (info.related_streams != null && !info.related_streams.isEmpty() && showRelatedStreams) {
|
||||||
for (InfoItem item : info.related_streams) {
|
for (InfoItem item : info.related_streams) {
|
||||||
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, item));
|
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, item));
|
||||||
}
|
}
|
||||||
relatedStreamsView.setVisibility(View.VISIBLE);
|
relatedStreamRootLayout.setVisibility(View.VISIBLE);
|
||||||
} else if (info.next_video == null) relatedStreamsView.setVisibility(View.GONE);
|
} else if (info.next_video == null) relatedStreamRootLayout.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -357,21 +457,29 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
actionBarHandler.setupMenu(menu, getMenuInflater());
|
actionBarHandler.setupMenu(menu, inflater);
|
||||||
return super.onCreateOptionsMenu(menu);
|
actionBarHandler.setupNavMenu(activity);
|
||||||
|
ActionBar supportActionBar = activity.getSupportActionBar();
|
||||||
|
if (supportActionBar != null) {
|
||||||
|
supportActionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
supportActionBar.setDisplayShowTitleEnabled(false);
|
||||||
|
//noinspection deprecation
|
||||||
|
supportActionBar.setNavigationMode(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
if (item.getItemId() == android.R.id.home) {
|
|
||||||
NavStack.getInstance().openMainActivity(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return actionBarHandler.onItemSelected(item) || super.onOptionsItemSelected(item);
|
return actionBarHandler.onItemSelected(item) || super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupActionBarHandler(final StreamInfo info) {
|
private void setupActionBarHandler(final StreamInfo info) {
|
||||||
|
if (activity.getSupportActionBar() != null) {
|
||||||
|
//noinspection deprecation
|
||||||
|
activity.getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
|
||||||
|
}
|
||||||
|
|
||||||
actionBarHandler.setupStreamList(info.video_streams);
|
actionBarHandler.setupStreamList(info.video_streams);
|
||||||
actionBarHandler.setOnShareListener(new ActionBarHandler.OnActionListener() {
|
actionBarHandler.setOnShareListener(new ActionBarHandler.OnActionListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -382,7 +490,7 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
intent.setAction(Intent.ACTION_SEND);
|
intent.setAction(Intent.ACTION_SEND);
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, info.webpage_url);
|
intent.putExtra(Intent.EXTRA_TEXT, info.webpage_url);
|
||||||
intent.setType("text/plain");
|
intent.setType("text/plain");
|
||||||
startActivity(Intent.createChooser(intent, VideoItemDetailActivity.this.getString(R.string.share_dialog_title)));
|
startActivity(Intent.createChooser(intent, activity.getString(R.string.share_dialog_title)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -394,7 +502,7 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse(info.webpage_url));
|
intent.setData(Uri.parse(info.webpage_url));
|
||||||
startActivity(Intent.createChooser(intent, VideoItemDetailActivity.this.getString(R.string.choose_browser)));
|
startActivity(Intent.createChooser(intent, activity.getString(R.string.choose_browser)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -403,21 +511,21 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
public void onActionSelected(int selectedStreamId) {
|
public void onActionSelected(int selectedStreamId) {
|
||||||
if (isLoading.get()) return;
|
if (isLoading.get()) return;
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !PermissionHelper.checkSystemAlertWindowPermission(VideoItemDetailActivity.this)) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !PermissionHelper.checkSystemAlertWindowPermission(activity)) {
|
||||||
Toast.makeText(VideoItemDetailActivity.this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (streamThumbnail != null) ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
|
if (streamThumbnail != null) ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
|
||||||
|
|
||||||
Intent i = new Intent(VideoItemDetailActivity.this, PopupVideoPlayer.class);
|
Intent i = new Intent(activity, PopupVideoPlayer.class);
|
||||||
Toast.makeText(VideoItemDetailActivity.this, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
||||||
i.putExtra(AbstractPlayer.VIDEO_TITLE, info.title)
|
i.putExtra(AbstractPlayer.VIDEO_TITLE, info.title)
|
||||||
.putExtra(AbstractPlayer.CHANNEL_NAME, info.uploader)
|
.putExtra(AbstractPlayer.CHANNEL_NAME, info.uploader)
|
||||||
.putExtra(AbstractPlayer.VIDEO_URL, info.webpage_url)
|
.putExtra(AbstractPlayer.VIDEO_URL, info.webpage_url)
|
||||||
.putExtra(AbstractPlayer.INDEX_SEL_VIDEO_STREAM, selectedStreamId)
|
.putExtra(AbstractPlayer.INDEX_SEL_VIDEO_STREAM, selectedStreamId)
|
||||||
.putExtra(AbstractPlayer.VIDEO_STREAMS_LIST, new ArrayList<>(info.video_streams));
|
.putExtra(AbstractPlayer.VIDEO_STREAMS_LIST, new ArrayList<>(info.video_streams));
|
||||||
if (info.start_position > 0) i.putExtra(AbstractPlayer.START_POSITION, info.start_position * 1000);
|
if (info.start_position > 0) i.putExtra(AbstractPlayer.START_POSITION, info.start_position * 1000);
|
||||||
VideoItemDetailActivity.this.startService(i);
|
activity.startService(i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -430,18 +538,18 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.setPackage(KORE_PACKET);
|
intent.setPackage(KORE_PACKET);
|
||||||
intent.setData(Uri.parse(info.webpage_url.replace("https", "http")));
|
intent.setData(Uri.parse(info.webpage_url.replace("https", "http")));
|
||||||
VideoItemDetailActivity.this.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(VideoItemDetailActivity.this);
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||||
builder.setMessage(R.string.kore_not_found)
|
builder.setMessage(R.string.kore_not_found)
|
||||||
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
|
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse(VideoItemDetailActivity.this.getString(R.string.fdroid_kore_url)));
|
intent.setData(Uri.parse(activity.getString(R.string.fdroid_kore_url)));
|
||||||
VideoItemDetailActivity.this.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||||
|
@ -459,7 +567,7 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
@Override
|
@Override
|
||||||
public void onActionSelected(int selectedStreamId) {
|
public void onActionSelected(int selectedStreamId) {
|
||||||
|
|
||||||
if (isLoading.get() || !PermissionHelper.checkStoragePermissions(VideoItemDetailActivity.this)) {
|
if (isLoading.get() || !PermissionHelper.checkStoragePermissions(activity)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,7 +579,7 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
|
|
||||||
if (info.audio_streams != null) {
|
if (info.audio_streams != null) {
|
||||||
AudioStream audioStream =
|
AudioStream audioStream =
|
||||||
info.audio_streams.get(getPreferredAudioStreamId(info));
|
info.audio_streams.get(Utils.getPreferredAudioFormat(activity, info.audio_streams));
|
||||||
|
|
||||||
String audioSuffix = "." + MediaFormat.getSuffixById(audioStream.format);
|
String audioSuffix = "." + MediaFormat.getSuffixById(audioStream.format);
|
||||||
args.putString(DownloadDialog.AUDIO_URL, audioStream.url);
|
args.putString(DownloadDialog.AUDIO_URL, audioStream.url);
|
||||||
|
@ -487,9 +595,9 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
|
|
||||||
args.putString(DownloadDialog.TITLE, info.title);
|
args.putString(DownloadDialog.TITLE, info.title);
|
||||||
DownloadDialog downloadDialog = DownloadDialog.newInstance(args);
|
DownloadDialog downloadDialog = DownloadDialog.newInstance(args);
|
||||||
downloadDialog.show(VideoItemDetailActivity.this.getSupportFragmentManager(), "downloadDialog");
|
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Toast.makeText(VideoItemDetailActivity.this,
|
Toast.makeText(activity,
|
||||||
R.string.could_not_setup_download_menu, Toast.LENGTH_LONG).show();
|
R.string.could_not_setup_download_menu, Toast.LENGTH_LONG).show();
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -504,17 +612,17 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
public void onActionSelected(int selectedStreamId) {
|
public void onActionSelected(int selectedStreamId) {
|
||||||
if (isLoading.get()) return;
|
if (isLoading.get()) return;
|
||||||
|
|
||||||
boolean useExternalAudioPlayer = PreferenceManager.getDefaultSharedPreferences(VideoItemDetailActivity.this)
|
boolean useExternalAudioPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
.getBoolean(VideoItemDetailActivity.this.getString(R.string.use_external_audio_player_key), false);
|
.getBoolean(activity.getString(R.string.use_external_audio_player_key), false);
|
||||||
Intent intent;
|
Intent intent;
|
||||||
AudioStream audioStream =
|
AudioStream audioStream =
|
||||||
info.audio_streams.get(getPreferredAudioStreamId(info));
|
info.audio_streams.get(Utils.getPreferredAudioFormat(activity, info.audio_streams));
|
||||||
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 18) {
|
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 18) {
|
||||||
//internal music player: explicit intent
|
//internal music player: explicit intent
|
||||||
if (!BackgroundPlayer.isRunning && streamThumbnail != null) {
|
if (!BackgroundPlayer.isRunning && streamThumbnail != null) {
|
||||||
ActivityCommunicator.getCommunicator()
|
ActivityCommunicator.getCommunicator()
|
||||||
.backgroundPlayerThumbnail = streamThumbnail;
|
.backgroundPlayerThumbnail = streamThumbnail;
|
||||||
intent = new Intent(VideoItemDetailActivity.this, BackgroundPlayer.class);
|
intent = new Intent(activity, BackgroundPlayer.class);
|
||||||
|
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
intent.setDataAndType(Uri.parse(audioStream.url),
|
intent.setDataAndType(Uri.parse(audioStream.url),
|
||||||
|
@ -523,7 +631,7 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
intent.putExtra(BackgroundPlayer.WEB_URL, info.webpage_url);
|
intent.putExtra(BackgroundPlayer.WEB_URL, info.webpage_url);
|
||||||
intent.putExtra(BackgroundPlayer.SERVICE_ID, serviceId);
|
intent.putExtra(BackgroundPlayer.SERVICE_ID, serviceId);
|
||||||
intent.putExtra(BackgroundPlayer.CHANNEL_NAME, info.uploader);
|
intent.putExtra(BackgroundPlayer.CHANNEL_NAME, info.uploader);
|
||||||
VideoItemDetailActivity.this.startService(intent);
|
activity.startService(intent);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
intent = new Intent();
|
intent = new Intent();
|
||||||
|
@ -534,18 +642,18 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
intent.putExtra(Intent.EXTRA_TITLE, info.title);
|
intent.putExtra(Intent.EXTRA_TITLE, info.title);
|
||||||
intent.putExtra("title", info.title);
|
intent.putExtra("title", info.title);
|
||||||
// HERE !!!
|
// HERE !!!
|
||||||
VideoItemDetailActivity.this.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(VideoItemDetailActivity.this);
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||||
builder.setMessage(R.string.no_player_found)
|
builder.setMessage(R.string.no_player_found)
|
||||||
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
|
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse(VideoItemDetailActivity.this.getString(R.string.fdroid_vlc_url)));
|
intent.setData(Uri.parse(activity.getString(R.string.fdroid_vlc_url)));
|
||||||
VideoItemDetailActivity.this.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||||
|
@ -564,32 +672,109 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// OwnStack
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stack that contains the "navigation history".<br>
|
||||||
|
* The peek is the current video.
|
||||||
|
*/
|
||||||
|
private final Stack<StackItem> stack = new Stack<>();
|
||||||
|
|
||||||
|
public void clearHistory() {
|
||||||
|
stack.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushToStack(String videoUrl, String videoTitle) {
|
||||||
|
|
||||||
|
if (stack.size() > 0 && stack.peek().getUrl().equals(videoUrl)) return;
|
||||||
|
stack.push(new StackItem(videoUrl, videoTitle));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitleToUrl(String videoUrl, String videoTitle) {
|
||||||
|
if (videoTitle != null && !videoTitle.isEmpty()) {
|
||||||
|
for (StackItem stackItem : stack) {
|
||||||
|
if (stackItem.getUrl().equals(videoUrl)) stackItem.setTitle(videoTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onActivityBackPressed() {
|
||||||
|
// That means that we are on the start of the stack,
|
||||||
|
// return false to let the MainActivity handle the onBack
|
||||||
|
if (stack.size() == 1) return false;
|
||||||
|
// Remove top
|
||||||
|
stack.pop();
|
||||||
|
// Get url from the new top
|
||||||
|
StackItem peek = stack.peek();
|
||||||
|
selectAndLoadVideo(0, peek.getUrl(),
|
||||||
|
peek.getTitle() != null && !peek.getTitle().isEmpty() ? peek.getTitle() : ""
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Utils
|
// Utils
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private void handleIntent(Intent intent) {
|
|
||||||
if (intent == null) return;
|
|
||||||
|
|
||||||
serviceId = intent.getIntExtra(NavStack.SERVICE_ID, 0);
|
public void setAutoplay(boolean autoplay) {
|
||||||
videoUrl = intent.getStringExtra(NavStack.URL);
|
this.autoPlayEnabled = autoplay;
|
||||||
autoPlayEnabled = intent.getBooleanExtra(AUTO_PLAY, false);
|
|
||||||
selectVideo(videoUrl, serviceId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectVideo(String url, int serviceId) {
|
public void selectVideo(int serviceId, String videoUrl, String videoTitle) {
|
||||||
if (curExtractorThread != null && curExtractorThread.isRunning()) curExtractorThread.cancel();
|
this.videoUrl = videoUrl;
|
||||||
|
this.videoTitle = videoTitle;
|
||||||
|
this.serviceId = serviceId;
|
||||||
|
}
|
||||||
|
|
||||||
animateView(contentRootLayout, false, 200, null);
|
public void selectAndLoadVideo(int serviceId, String videoUrl, String videoTitle) {
|
||||||
|
selectVideo(serviceId, videoUrl, videoTitle);
|
||||||
|
loadSelectedVideo();
|
||||||
|
}
|
||||||
|
|
||||||
thumbnailPlayButton.setVisibility(View.GONE);
|
public void loadSelectedVideo() {
|
||||||
|
pushToStack(videoUrl, videoTitle);
|
||||||
|
|
||||||
|
if (curExtractorWorker != null && curExtractorWorker.isRunning()) curExtractorWorker.cancel();
|
||||||
|
|
||||||
|
if (activity.getSupportActionBar() != null) {
|
||||||
|
//noinspection deprecation
|
||||||
|
activity.getSupportActionBar().setNavigationMode(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
animateView(contentRootLayout, false, 50, null);
|
||||||
|
|
||||||
|
videoTitleTextView.setMaxLines(1);
|
||||||
|
int scrollY = parallaxScrollRootView.getScrollY();
|
||||||
|
if (scrollY < 30) animateView(videoTitleTextView, false, 200, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
videoTitleTextView.setText(videoTitle != null ? videoTitle : "");
|
||||||
|
animateView(videoTitleTextView, true, 400, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
else videoTitleTextView.setText(videoTitle != null ? videoTitle : "");
|
||||||
|
//videoTitleTextView.setText(videoTitle != null ? videoTitle : "");
|
||||||
|
videoDescriptionRootLayout.setVisibility(View.GONE);
|
||||||
|
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
|
||||||
|
videoTitleToggleArrow.setVisibility(View.GONE);
|
||||||
|
videoTitleRoot.setClickable(false);
|
||||||
|
|
||||||
|
//thumbnailPlayButton.setVisibility(View.GONE);
|
||||||
|
animateView(thumbnailPlayButton, false, 50, null);
|
||||||
loadingProgressBar.setVisibility(View.VISIBLE);
|
loadingProgressBar.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
imageLoader.cancelDisplayTask(thumbnailImageView);
|
imageLoader.cancelDisplayTask(thumbnailImageView);
|
||||||
imageLoader.cancelDisplayTask(uploaderThumb);
|
imageLoader.cancelDisplayTask(uploaderThumb);
|
||||||
thumbnailImageView.setImageDrawable(null);
|
thumbnailImageView.setImageBitmap(null);
|
||||||
|
uploaderThumb.setImageBitmap(null);
|
||||||
|
|
||||||
curExtractorThread = StreamExtractorWorker.startExtractorThread(serviceId, url, this, this);
|
curExtractorWorker = new StreamExtractorWorker(activity, serviceId, videoUrl, this);
|
||||||
|
curExtractorWorker.start();
|
||||||
isLoading.set(true);
|
isLoading.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,7 +782,7 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
// ----------- THE MAGIC MOMENT ---------------
|
// ----------- THE MAGIC MOMENT ---------------
|
||||||
VideoStream selectedVideoStream = info.video_streams.get(actionBarHandler.getSelectedVideoStream());
|
VideoStream selectedVideoStream = info.video_streams.get(actionBarHandler.getSelectedVideoStream());
|
||||||
|
|
||||||
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
|
if (PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
|
||||||
|
|
||||||
// External Player
|
// External Player
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
|
@ -609,7 +794,7 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
this.startActivity(intent);
|
this.startActivity(intent);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||||
builder.setMessage(R.string.no_player_found)
|
builder.setMessage(R.string.no_player_found)
|
||||||
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
|
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -629,14 +814,13 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Intent intent;
|
Intent intent;
|
||||||
boolean useOldPlayer = PreferenceManager
|
boolean useOldPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
.getDefaultSharedPreferences(this)
|
|
||||||
.getBoolean(getString(R.string.use_old_player_key), false)
|
.getBoolean(getString(R.string.use_old_player_key), false)
|
||||||
|| (Build.VERSION.SDK_INT < 16);
|
|| (Build.VERSION.SDK_INT < 16);
|
||||||
if (!useOldPlayer) {
|
if (!useOldPlayer) {
|
||||||
// ExoPlayer
|
// ExoPlayer
|
||||||
if (streamThumbnail != null) ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
|
if (streamThumbnail != null) ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
|
||||||
intent = new Intent(this, ExoPlayerActivity.class)
|
intent = new Intent(activity, ExoPlayerActivity.class)
|
||||||
.putExtra(AbstractPlayer.VIDEO_TITLE, info.title)
|
.putExtra(AbstractPlayer.VIDEO_TITLE, info.title)
|
||||||
.putExtra(AbstractPlayer.VIDEO_URL, info.webpage_url)
|
.putExtra(AbstractPlayer.VIDEO_URL, info.webpage_url)
|
||||||
.putExtra(AbstractPlayer.CHANNEL_NAME, info.uploader)
|
.putExtra(AbstractPlayer.CHANNEL_NAME, info.uploader)
|
||||||
|
@ -645,46 +829,19 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
if (info.start_position > 0) intent.putExtra(AbstractPlayer.START_POSITION, info.start_position * 1000);
|
if (info.start_position > 0) intent.putExtra(AbstractPlayer.START_POSITION, info.start_position * 1000);
|
||||||
} else {
|
} else {
|
||||||
// Internal Player
|
// Internal Player
|
||||||
intent = new Intent(this, PlayVideoActivity.class)
|
intent = new Intent(activity, PlayVideoActivity.class)
|
||||||
.putExtra(PlayVideoActivity.VIDEO_TITLE, info.title)
|
.putExtra(PlayVideoActivity.VIDEO_TITLE, info.title)
|
||||||
.putExtra(PlayVideoActivity.STREAM_URL, selectedVideoStream.url)
|
.putExtra(PlayVideoActivity.STREAM_URL, selectedVideoStream.url)
|
||||||
.putExtra(PlayVideoActivity.VIDEO_URL, info.webpage_url)
|
.putExtra(PlayVideoActivity.VIDEO_URL, info.webpage_url)
|
||||||
.putExtra(PlayVideoActivity.START_POSITION, info.start_position);
|
.putExtra(PlayVideoActivity.START_POSITION, info.start_position);
|
||||||
}
|
}
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
//intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getPreferredAudioStreamId(final StreamInfo info) {
|
|
||||||
String preferredFormatString = PreferenceManager.getDefaultSharedPreferences(this)
|
|
||||||
.getString(getString(R.string.default_audio_format_key), "webm");
|
|
||||||
|
|
||||||
int preferredFormat = MediaFormat.WEBMA.id;
|
|
||||||
switch (preferredFormatString) {
|
|
||||||
case "webm":
|
|
||||||
preferredFormat = MediaFormat.WEBMA.id;
|
|
||||||
break;
|
|
||||||
case "m4a":
|
|
||||||
preferredFormat = MediaFormat.M4A.id;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < info.audio_streams.size(); i++) {
|
|
||||||
if (info.audio_streams.get(i).format == preferredFormat) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo: make this a proper error
|
|
||||||
Log.e(TAG, "FAILED to set audioStream value!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private View getSeparatorView() {
|
private View getSeparatorView() {
|
||||||
View separator = new View(this);
|
View separator = new View(activity);
|
||||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1);
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1);
|
||||||
int m8 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics());
|
int m8 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics());
|
||||||
int m5 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics());
|
int m5 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics());
|
||||||
|
@ -692,7 +849,7 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
separator.setLayoutParams(params);
|
separator.setLayoutParams(params);
|
||||||
|
|
||||||
TypedValue typedValue = new TypedValue();
|
TypedValue typedValue = new TypedValue();
|
||||||
getTheme().resolveAttribute(R.attr.separatorColor, typedValue, true);
|
activity.getTheme().resolveAttribute(R.attr.separatorColor, typedValue, true);
|
||||||
separator.setBackgroundColor(typedValue.data);
|
separator.setBackgroundColor(typedValue.data);
|
||||||
return separator;
|
return separator;
|
||||||
}
|
}
|
||||||
|
@ -762,11 +919,11 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(StreamInfo info) {
|
public void onReceive(StreamInfo info) {
|
||||||
currentStreamInfo = info;
|
if (info == null || isRemoving() || !isVisible()) return;
|
||||||
|
|
||||||
|
currentStreamInfo = info;
|
||||||
loadingProgressBar.setVisibility(View.GONE);
|
loadingProgressBar.setVisibility(View.GONE);
|
||||||
thumbnailPlayButton.setVisibility(View.VISIBLE);
|
animateView(thumbnailPlayButton, true, 200, null);
|
||||||
relatedStreamRootLayout.setVisibility(showRelatedStreams ? View.VISIBLE : View.GONE);
|
|
||||||
parallaxScrollRootView.scrollTo(0, 0);
|
parallaxScrollRootView.scrollTo(0, 0);
|
||||||
|
|
||||||
// Since newpipe is designed to work even if certain information is not available,
|
// Since newpipe is designed to work even if certain information is not available,
|
||||||
|
@ -775,9 +932,9 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
if (!info.uploader.isEmpty()) uploaderTextView.setText(info.uploader);
|
if (!info.uploader.isEmpty()) uploaderTextView.setText(info.uploader);
|
||||||
uploaderTextView.setVisibility(!info.uploader.isEmpty() ? View.VISIBLE : View.GONE);
|
uploaderTextView.setVisibility(!info.uploader.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
uploaderButton.setVisibility(!info.channel_url.isEmpty() ? View.VISIBLE : View.GONE);
|
uploaderButton.setVisibility(!info.channel_url.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
uploaderThumb.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.buddy));
|
uploaderThumb.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.buddy));
|
||||||
|
|
||||||
if (info.view_count >= 0) videoCountView.setText(Localization.localizeViewCount(info.view_count, this));
|
if (info.view_count >= 0) videoCountView.setText(Localization.localizeViewCount(info.view_count, activity));
|
||||||
videoCountView.setVisibility(info.view_count >= 0 ? View.VISIBLE : View.GONE);
|
videoCountView.setVisibility(info.view_count >= 0 ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
if (info.dislike_count == -1 && info.like_count == -1) {
|
if (info.dislike_count == -1 && info.like_count == -1) {
|
||||||
|
@ -790,54 +947,64 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
} else {
|
} else {
|
||||||
thumbsDisabledTextView.setVisibility(View.GONE);
|
thumbsDisabledTextView.setVisibility(View.GONE);
|
||||||
|
|
||||||
if (info.dislike_count >= 0) thumbsDownTextView.setText(Localization.localizeNumber(info.dislike_count, this));
|
if (info.dislike_count >= 0) thumbsDownTextView.setText(Localization.localizeNumber(info.dislike_count, activity));
|
||||||
thumbsDownTextView.setVisibility(info.dislike_count >= 0 ? View.VISIBLE : View.GONE);
|
thumbsDownTextView.setVisibility(info.dislike_count >= 0 ? View.VISIBLE : View.GONE);
|
||||||
thumbsDownImageView.setVisibility(info.dislike_count >= 0 ? View.VISIBLE : View.GONE);
|
thumbsDownImageView.setVisibility(info.dislike_count >= 0 ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
if (info.like_count >= 0) thumbsUpTextView.setText(Localization.localizeNumber(info.like_count, this));
|
if (info.like_count >= 0) thumbsUpTextView.setText(Localization.localizeNumber(info.like_count, activity));
|
||||||
thumbsUpTextView.setVisibility(info.like_count >= 0 ? View.VISIBLE : View.GONE);
|
thumbsUpTextView.setVisibility(info.like_count >= 0 ? View.VISIBLE : View.GONE);
|
||||||
thumbsUpImageView.setVisibility(info.like_count >= 0 ? View.VISIBLE : View.GONE);
|
thumbsUpImageView.setVisibility(info.like_count >= 0 ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!info.upload_date.isEmpty()) videoUploadDateView.setText(Localization.localizeDate(info.upload_date, this));
|
if (!info.upload_date.isEmpty()) videoUploadDateView.setText(Localization.localizeDate(info.upload_date, activity));
|
||||||
videoUploadDateView.setVisibility(!info.upload_date.isEmpty() ? View.VISIBLE : View.GONE);
|
videoUploadDateView.setVisibility(!info.upload_date.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
if (!info.description.isEmpty()) videoDescriptionView.setText(
|
if (!info.description.isEmpty()) { //noinspection deprecation
|
||||||
Build.VERSION.SDK_INT >= 24 ? Html.fromHtml(info.description, 0) : Html.fromHtml(info.description)
|
videoDescriptionView.setText(Build.VERSION.SDK_INT >= 24 ? Html.fromHtml(info.description, 0) : Html.fromHtml(info.description));
|
||||||
);
|
}
|
||||||
videoDescriptionView.setVisibility(!info.description.isEmpty() ? View.VISIBLE : View.GONE);
|
videoDescriptionView.setVisibility(!info.description.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
videoDescriptionRootLayout.setVisibility(View.GONE);
|
videoDescriptionRootLayout.setVisibility(View.GONE);
|
||||||
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
|
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
|
||||||
|
videoTitleToggleArrow.setVisibility(View.VISIBLE);
|
||||||
|
videoTitleRoot.setClickable(true);
|
||||||
|
|
||||||
setupActionBarHandler(info);
|
setupActionBarHandler(info);
|
||||||
initRelatedVideos(info);
|
initRelatedVideos(info);
|
||||||
initThumbnailViews(info);
|
initThumbnailViews(info);
|
||||||
|
|
||||||
|
setTitleToUrl(info.webpage_url, info.title);
|
||||||
|
|
||||||
animateView(contentRootLayout, true, 200, null);
|
animateView(contentRootLayout, true, 200, null);
|
||||||
|
|
||||||
|
if (autoPlayEnabled) {
|
||||||
|
playVideo(info);
|
||||||
|
// Only auto play in the first open
|
||||||
|
autoPlayEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
isLoading.set(false);
|
isLoading.set(false);
|
||||||
if (autoPlayEnabled) playVideo(info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(int messageId) {
|
public void onError(int messageId) {
|
||||||
Toast.makeText(this, messageId, Toast.LENGTH_LONG).show();
|
Toast.makeText(activity, messageId, Toast.LENGTH_LONG).show();
|
||||||
loadingProgressBar.setVisibility(View.GONE);
|
loadingProgressBar.setVisibility(View.GONE);
|
||||||
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.not_available_monkey));
|
videoTitleTextView.setText(getString(messageId));
|
||||||
|
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.not_available_monkey));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReCaptchaException() {
|
public void onReCaptchaException() {
|
||||||
Toast.makeText(this, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
Toast.makeText(activity, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
||||||
// Starting ReCaptcha Challenge Activity
|
// Starting ReCaptcha Challenge Activity
|
||||||
startActivityForResult(new Intent(this, ReCaptchaActivity.class), ReCaptchaActivity.RECAPTCHA_REQUEST);
|
startActivityForResult(new Intent(activity, ReCaptchaActivity.class), ReCaptchaActivity.RECAPTCHA_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBlockedByGemaError() {
|
public void onBlockedByGemaError() {
|
||||||
loadingProgressBar.setVisibility(View.GONE);
|
loadingProgressBar.setVisibility(View.GONE);
|
||||||
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.gruese_die_gema));
|
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.gruese_die_gema));
|
||||||
thumbnailBackgroundButton.setOnClickListener(new View.OnClickListener() {
|
thumbnailBackgroundButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
@ -848,20 +1015,20 @@ public class VideoItemDetailActivity extends AppCompatActivity implements Stream
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Toast.makeText(this, R.string.blocked_by_gema, Toast.LENGTH_LONG).show();
|
Toast.makeText(activity, R.string.blocked_by_gema, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onContentErrorWithMessage(int messageId) {
|
public void onContentErrorWithMessage(int messageId) {
|
||||||
loadingProgressBar.setVisibility(View.GONE);
|
loadingProgressBar.setVisibility(View.GONE);
|
||||||
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.not_available_monkey));
|
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.not_available_monkey));
|
||||||
Toast.makeText(this, messageId, Toast.LENGTH_LONG).show();
|
Toast.makeText(activity, messageId, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onContentError() {
|
public void onContentError() {
|
||||||
loadingProgressBar.setVisibility(View.GONE);
|
loadingProgressBar.setVisibility(View.GONE);
|
||||||
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.not_available_monkey));
|
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.not_available_monkey));
|
||||||
Toast.makeText(this, R.string.content_not_available, Toast.LENGTH_LONG).show();
|
Toast.makeText(activity, R.string.content_not_available, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,10 +1,13 @@
|
||||||
package org.schabi.newpipe.search_fragment;
|
package org.schabi.newpipe.fragments.search;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.support.v7.widget.SearchView;
|
import android.support.v7.widget.SearchView;
|
||||||
|
@ -24,10 +27,11 @@ import org.schabi.newpipe.ReCaptchaActivity;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.search.SearchEngine;
|
import org.schabi.newpipe.extractor.search.SearchEngine;
|
||||||
import org.schabi.newpipe.extractor.search.SearchResult;
|
import org.schabi.newpipe.extractor.search.SearchResult;
|
||||||
|
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
import org.schabi.newpipe.util.NavStack;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
@ -36,120 +40,87 @@ import static org.schabi.newpipe.ReCaptchaActivity.RECAPTCHA_REQUEST;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 02.08.16.
|
* Created by Christian Schabesberger on 02.08.16.
|
||||||
*
|
* <p>
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||||
* SearchInfoItemFragment.java is part of NewPipe.
|
* SearchFragment.java is part of NewPipe.
|
||||||
*
|
* <p>
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
* NewPipe is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
* <p>
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
* NewPipe is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
* <p>
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class SearchInfoItemFragment extends Fragment {
|
public class SearchFragment extends Fragment implements SearchView.OnQueryTextListener, SearchWorker.SearchWorkerResultListener {
|
||||||
|
|
||||||
private static final String TAG = SearchInfoItemFragment.class.toString();
|
private static final String TAG = SearchFragment.class.toString();
|
||||||
|
|
||||||
private EnumSet<SearchEngine.Filter> filter =
|
|
||||||
EnumSet.of(SearchEngine.Filter.CHANNEL, SearchEngine.Filter.STREAM);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener for search queries
|
|
||||||
*/
|
|
||||||
public class SearchQueryListener implements SearchView.OnQueryTextListener {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onQueryTextSubmit(String query) {
|
|
||||||
Activity a = getActivity();
|
|
||||||
try {
|
|
||||||
search(query);
|
|
||||||
|
|
||||||
// hide virtual keyboard
|
|
||||||
InputMethodManager inputManager =
|
|
||||||
(InputMethodManager) a.getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
||||||
try {
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
inputManager.hideSoftInputFromWindow(
|
|
||||||
a.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
ErrorActivity.reportError(a, e, null,
|
|
||||||
a.findViewById(android.R.id.content),
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
|
||||||
NewPipe.getNameOfService(streamingServiceId),
|
|
||||||
"Could not get widget with focus", R.string.general_error));
|
|
||||||
}
|
|
||||||
// clear focus
|
|
||||||
// 1. to not open up the keyboard after switching back to this
|
|
||||||
// 2. It's a workaround to a seeming bug by the Android OS it self, causing
|
|
||||||
// onQueryTextSubmit to trigger twice when focus is not cleared.
|
|
||||||
// See: http://stackoverflow.com/questions/17874951/searchview-onquerytextsubmit-runs-twice-while-i-pressed-once
|
|
||||||
a.getCurrentFocus().clearFocus();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onQueryTextChange(String newText) {
|
|
||||||
if (!newText.isEmpty()) {
|
|
||||||
searchSuggestions(newText);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int streamingServiceId = -1;
|
|
||||||
private String searchQuery = "";
|
|
||||||
private boolean isLoading = false;
|
|
||||||
|
|
||||||
private ProgressBar loadingIndicator = null;
|
|
||||||
private int pageNumber = 0;
|
|
||||||
private SuggestionListAdapter suggestionListAdapter = null;
|
|
||||||
private InfoListAdapter infoListAdapter = null;
|
|
||||||
private LinearLayoutManager streamInfoListLayoutManager = null;
|
|
||||||
|
|
||||||
// savedInstanceBundle arguments
|
// savedInstanceBundle arguments
|
||||||
private static final String QUERY = "query";
|
private static final String QUERY = "query";
|
||||||
private static final String STREAMING_SERVICE = "streaming_service";
|
private static final String STREAMING_SERVICE = "streaming_service";
|
||||||
|
|
||||||
|
private int streamingServiceId = -1;
|
||||||
|
private String searchQuery = "";
|
||||||
|
private boolean isLoading = false;
|
||||||
|
|
||||||
|
@SuppressWarnings("FieldCanBeLocal")
|
||||||
|
private SearchView searchView;
|
||||||
|
private RecyclerView recyclerView;
|
||||||
|
private ProgressBar loadingIndicator;
|
||||||
|
private int pageNumber = 0;
|
||||||
|
private SuggestionListAdapter suggestionListAdapter;
|
||||||
|
private InfoListAdapter infoListAdapter;
|
||||||
|
private LinearLayoutManager streamInfoListLayoutManager;
|
||||||
|
|
||||||
|
private EnumSet<SearchEngine.Filter> filter = EnumSet.of(SearchEngine.Filter.CHANNEL, SearchEngine.Filter.STREAM);
|
||||||
|
private OnItemSelectedListener onItemSelectedListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mandatory empty constructor for the fragment manager to instantiate the
|
* Mandatory empty constructor for the fragment manager to instantiate the
|
||||||
* fragment (e.g. upon screen orientation changes).
|
* fragment (e.g. upon screen orientation changes).
|
||||||
*/
|
*/
|
||||||
public SearchInfoItemFragment() {
|
public SearchFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public static SearchInfoItemFragment newInstance(int streamingServiceId, String searchQuery) {
|
public static SearchFragment newInstance(int streamingServiceId, String searchQuery) {
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putInt(STREAMING_SERVICE, streamingServiceId);
|
args.putInt(STREAMING_SERVICE, streamingServiceId);
|
||||||
args.putString(QUERY, searchQuery);
|
args.putString(QUERY, searchQuery);
|
||||||
SearchInfoItemFragment fragment = new SearchInfoItemFragment();
|
SearchFragment fragment = new SearchFragment();
|
||||||
fragment.setArguments(args);
|
fragment.setArguments(args);
|
||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Fragment's LifeCycle
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
onItemSelectedListener = ((OnItemSelectedListener) context);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
searchQuery = "";
|
searchQuery = "";
|
||||||
|
isLoading = false;
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
searchQuery = savedInstanceState.getString(QUERY);
|
searchQuery = savedInstanceState.getString(QUERY);
|
||||||
streamingServiceId = savedInstanceState.getInt(STREAMING_SERVICE);
|
streamingServiceId = savedInstanceState.getInt(STREAMING_SERVICE);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
Bundle args = getArguments();
|
Bundle args = getArguments();
|
||||||
if(args != null) {
|
if (args != null) {
|
||||||
searchQuery = args.getString(QUERY);
|
searchQuery = args.getString(QUERY);
|
||||||
streamingServiceId = args.getInt(STREAMING_SERVICE);
|
streamingServiceId = args.getInt(STREAMING_SERVICE);
|
||||||
} else {
|
} else {
|
||||||
|
@ -168,50 +139,16 @@ public class SearchInfoItemFragment extends Fragment {
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
SearchWorker sw = SearchWorker.getInstance();
|
SearchWorker sw = SearchWorker.getInstance();
|
||||||
sw.setSearchWorkerResultListener(new SearchWorker.SearchWorkerResultListener() {
|
sw.setSearchWorkerResultListener(this);
|
||||||
@Override
|
|
||||||
public void onResult(SearchResult result) {
|
|
||||||
infoListAdapter.addInfoItemList(result.resultList);
|
|
||||||
setDoneLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNothingFound(int stringResource) {
|
|
||||||
//setListShown(true);
|
|
||||||
Toast.makeText(getActivity(), getString(stringResource),
|
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
setDoneLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(String message) {
|
|
||||||
//setListShown(true);
|
|
||||||
Toast.makeText(getActivity(), message,
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
setDoneLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReCaptchaChallenge() {
|
|
||||||
Toast.makeText(getActivity(), "ReCaptcha Challenge requested",
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
|
|
||||||
// Starting ReCaptcha Challenge Activity
|
|
||||||
startActivityForResult(
|
|
||||||
new Intent(getActivity(), ReCaptchaActivity.class),
|
|
||||||
RECAPTCHA_REQUEST);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
Bundle savedInstanceState) {
|
View view = inflater.inflate(R.layout.fragment_search, container, false);
|
||||||
View view = inflater.inflate(R.layout.fragment_searchinfoitem, container, false);
|
|
||||||
|
|
||||||
Context context = view.getContext();
|
Context context = view.getContext();
|
||||||
loadingIndicator = (ProgressBar) view.findViewById(R.id.progressBar);
|
loadingIndicator = (ProgressBar) view.findViewById(R.id.loading_progress_bar);
|
||||||
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.list);
|
recyclerView = (RecyclerView) view.findViewById(R.id.list);
|
||||||
streamInfoListLayoutManager = new LinearLayoutManager(context);
|
streamInfoListLayoutManager = new LinearLayoutManager(context);
|
||||||
recyclerView.setLayoutManager(streamInfoListLayoutManager);
|
recyclerView.setLayoutManager(streamInfoListLayoutManager);
|
||||||
|
|
||||||
|
@ -219,19 +156,16 @@ public class SearchInfoItemFragment extends Fragment {
|
||||||
getActivity().findViewById(android.R.id.content));
|
getActivity().findViewById(android.R.id.content));
|
||||||
infoListAdapter.setFooter(inflater.inflate(R.layout.pignate_footer, recyclerView, false));
|
infoListAdapter.setFooter(inflater.inflate(R.layout.pignate_footer, recyclerView, false));
|
||||||
infoListAdapter.showFooter(false);
|
infoListAdapter.showFooter(false);
|
||||||
infoListAdapter.setOnStreamInfoItemSelectedListener(
|
infoListAdapter.setOnStreamInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
|
||||||
new InfoItemBuilder.OnInfoItemSelectedListener() {
|
|
||||||
@Override
|
@Override
|
||||||
public void selected(String url, int serviceId) {
|
public void selected(int serviceId, String url, String title) {
|
||||||
NavStack.getInstance()
|
NavigationHelper.openVideoDetail(onItemSelectedListener, serviceId, url, title);
|
||||||
.openDetailActivity(getContext(), url, serviceId);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
infoListAdapter.setOnChannelInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
|
infoListAdapter.setOnChannelInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void selected(String url, int serviceId) {
|
public void selected(int serviceId, String url, String title) {
|
||||||
NavStack.getInstance()
|
NavigationHelper.openChannel(onItemSelectedListener, serviceId, url, title);
|
||||||
.openChannelActivity(getContext(), url, serviceId);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
recyclerView.setAdapter(infoListAdapter);
|
recyclerView.setAdapter(infoListAdapter);
|
||||||
|
@ -249,6 +183,13 @@ public class SearchInfoItemFragment extends Fragment {
|
||||||
|
|
||||||
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount && !isLoading) {
|
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount && !isLoading) {
|
||||||
pageNumber++;
|
pageNumber++;
|
||||||
|
recyclerView.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
infoListAdapter.showFooter(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
search(searchQuery, pageNumber);
|
search(searchQuery, pageNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,13 +200,35 @@ public class SearchInfoItemFragment extends Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onStart();
|
super.onViewCreated(view, savedInstanceState);
|
||||||
if(!searchQuery.isEmpty()) {
|
if (!searchQuery.isEmpty()) {
|
||||||
search(searchQuery);
|
search(searchQuery);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
recyclerView.removeAllViews();
|
||||||
|
infoListAdapter.clearSteamItemList();
|
||||||
|
recyclerView = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
if (isLoading && !searchQuery.isEmpty()) {
|
||||||
|
search(searchQuery);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
SearchWorker.getInstance().terminate();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
|
@ -273,19 +236,45 @@ public class SearchInfoItemFragment extends Fragment {
|
||||||
outState.putInt(STREAMING_SERVICE, streamingServiceId);
|
outState.putInt(STREAMING_SERVICE, streamingServiceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
switch (requestCode) {
|
||||||
|
case RECAPTCHA_REQUEST:
|
||||||
|
if (resultCode == RESULT_OK && searchQuery.length() != 0) {
|
||||||
|
search(searchQuery);
|
||||||
|
} else Log.e(TAG, "ReCaptcha failed");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log.e(TAG, "Request code from activity not supported [" + requestCode + "]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Menu
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
ActionBar supportActionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
|
||||||
|
if (supportActionBar != null) {
|
||||||
|
supportActionBar.setDisplayHomeAsUpEnabled(false);
|
||||||
|
supportActionBar.setDisplayShowTitleEnabled(false);
|
||||||
|
//noinspection deprecation
|
||||||
|
supportActionBar.setNavigationMode(0);
|
||||||
|
}
|
||||||
inflater.inflate(R.menu.search_menu, menu);
|
inflater.inflate(R.menu.search_menu, menu);
|
||||||
|
|
||||||
MenuItem searchItem = menu.findItem(R.id.action_search);
|
MenuItem searchItem = menu.findItem(R.id.action_search);
|
||||||
SearchView searchView = (SearchView) searchItem.getActionView();
|
searchView = (SearchView) searchItem.getActionView();
|
||||||
setupSearchView(searchView);
|
setupSearchView(searchView);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
switch(item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.menu_filter_all:
|
case R.id.menu_filter_all:
|
||||||
changeFilter(item, EnumSet.of(SearchEngine.Filter.STREAM, SearchEngine.Filter.CHANNEL));
|
changeFilter(item, EnumSet.of(SearchEngine.Filter.STREAM, SearchEngine.Filter.CHANNEL));
|
||||||
return true;
|
return true;
|
||||||
|
@ -300,11 +289,15 @@ public class SearchInfoItemFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private void changeFilter(MenuItem item, EnumSet<SearchEngine.Filter> filter) {
|
private void changeFilter(MenuItem item, EnumSet<SearchEngine.Filter> filter) {
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
item.setChecked(true);
|
item.setChecked(true);
|
||||||
if(searchQuery != null && !searchQuery.isEmpty()) {
|
if (searchQuery != null && !searchQuery.isEmpty()) {
|
||||||
Log.d(TAG, "Fuck+ " + searchQuery);
|
Log.e(TAG, "Fuck+ " + searchQuery);
|
||||||
search(searchQuery);
|
search(searchQuery);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,7 +306,7 @@ public class SearchInfoItemFragment extends Fragment {
|
||||||
suggestionListAdapter = new SuggestionListAdapter(getActivity());
|
suggestionListAdapter = new SuggestionListAdapter(getActivity());
|
||||||
searchView.setSuggestionsAdapter(suggestionListAdapter);
|
searchView.setSuggestionsAdapter(suggestionListAdapter);
|
||||||
searchView.setOnSuggestionListener(new SearchSuggestionListener(searchView, suggestionListAdapter));
|
searchView.setOnSuggestionListener(new SearchSuggestionListener(searchView, suggestionListAdapter));
|
||||||
searchView.setOnQueryTextListener(new SearchQueryListener());
|
searchView.setOnQueryTextListener(this);
|
||||||
if (searchQuery != null && !searchQuery.isEmpty()) {
|
if (searchQuery != null && !searchQuery.isEmpty()) {
|
||||||
searchView.setQuery(searchQuery, false);
|
searchView.setQuery(searchQuery, false);
|
||||||
searchView.setIconifiedByDefault(false);
|
searchView.setIconifiedByDefault(false);
|
||||||
|
@ -341,9 +334,9 @@ public class SearchInfoItemFragment extends Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDoneLoading() {
|
private void setDoneLoading() {
|
||||||
this.isLoading = false;
|
isLoading = false;
|
||||||
loadingIndicator.setVisibility(View.GONE);
|
loadingIndicator.setVisibility(View.GONE);
|
||||||
infoListAdapter.showFooter(true);
|
infoListAdapter.showFooter(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -351,7 +344,7 @@ public class SearchInfoItemFragment extends Fragment {
|
||||||
*/
|
*/
|
||||||
private void hideBackground() {
|
private void hideBackground() {
|
||||||
View view = getView();
|
View view = getView();
|
||||||
if(view == null) return;
|
if (view == null) return;
|
||||||
view.findViewById(R.id.mainBG).setVisibility(View.GONE);
|
view.findViewById(R.id.mainBG).setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,22 +355,86 @@ public class SearchInfoItemFragment extends Fragment {
|
||||||
suggestionThread.start();
|
suggestionThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean isMainBgVisible() {
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
return getActivity().findViewById(R.id.mainBG).getVisibility() == View.VISIBLE;
|
||||||
switch (requestCode) {
|
|
||||||
case RECAPTCHA_REQUEST:
|
|
||||||
if (resultCode == RESULT_OK) {
|
|
||||||
if (searchQuery.length() != 0) {
|
|
||||||
search(searchQuery);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "ReCaptcha failed");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
Log.e(TAG, "Request code from activity not supported [" + requestCode + "]");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// OnQueryTextListener
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextSubmit(String query) {
|
||||||
|
Activity a = getActivity();
|
||||||
|
try {
|
||||||
|
search(query);
|
||||||
|
|
||||||
|
// hide virtual keyboard
|
||||||
|
InputMethodManager inputManager =
|
||||||
|
(InputMethodManager) a.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
try {
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
inputManager.hideSoftInputFromWindow(
|
||||||
|
a.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
ErrorActivity.reportError(a, e, null,
|
||||||
|
a.findViewById(android.R.id.content),
|
||||||
|
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
||||||
|
NewPipe.getNameOfService(streamingServiceId),
|
||||||
|
"Could not get widget with focus", R.string.general_error));
|
||||||
|
}
|
||||||
|
// clear focus
|
||||||
|
// 1. to not open up the keyboard after switching back to this
|
||||||
|
// 2. It's a workaround to a seeming bug by the Android OS it self, causing
|
||||||
|
// onQueryTextSubmit to trigger twice when focus is not cleared.
|
||||||
|
// See: http://stackoverflow.com/questions/17874951/searchview-onquerytextsubmit-runs-twice-while-i-pressed-once
|
||||||
|
a.getCurrentFocus().clearFocus();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextChange(String newText) {
|
||||||
|
if (!newText.isEmpty()) {
|
||||||
|
searchSuggestions(newText);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// SearchWorkerResultListener
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResult(SearchResult result) {
|
||||||
|
infoListAdapter.addInfoItemList(result.resultList);
|
||||||
|
setDoneLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingFound(int stringResource) {
|
||||||
|
//setListShown(true);
|
||||||
|
Toast.makeText(getActivity(), getString(stringResource), Toast.LENGTH_SHORT).show();
|
||||||
|
setDoneLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(String message) {
|
||||||
|
//setListShown(true);
|
||||||
|
Toast.makeText(getActivity(), message, Toast.LENGTH_LONG).show();
|
||||||
|
setDoneLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReCaptchaChallenge() {
|
||||||
|
Toast.makeText(getActivity(), "ReCaptcha Challenge requested",
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
|
// Starting ReCaptcha Challenge Activity
|
||||||
|
startActivityForResult(new Intent(getActivity(), ReCaptchaActivity.class), RECAPTCHA_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package org.schabi.newpipe.search_fragment;
|
package org.schabi.newpipe.fragments.search;
|
||||||
|
|
||||||
import android.support.v7.widget.SearchView;
|
import android.support.v7.widget.SearchView;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.schabi.newpipe.search_fragment;
|
package org.schabi.newpipe.fragments.search;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
@ -7,13 +7,13 @@ import android.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||||
import org.schabi.newpipe.extractor.search.SearchEngine;
|
import org.schabi.newpipe.extractor.search.SearchEngine;
|
||||||
import org.schabi.newpipe.extractor.search.SearchResult;
|
import org.schabi.newpipe.extractor.search.SearchResult;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
@ -209,6 +209,7 @@ public class SearchWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void terminate() {
|
public void terminate() {
|
||||||
|
if (runnable == null) return;
|
||||||
requestId++;
|
requestId++;
|
||||||
runnable.terminate();
|
runnable.terminate();
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package org.schabi.newpipe.search_fragment;
|
package org.schabi.newpipe.fragments.search;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.schabi.newpipe.search_fragment;
|
package org.schabi.newpipe.fragments.search;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
@ -6,11 +6,11 @@ import android.os.Handler;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
|
||||||
import org.schabi.newpipe.extractor.SuggestionExtractor;
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.extractor.SuggestionExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
|
@ -1,11 +1,10 @@
|
||||||
package org.schabi.newpipe.info_list;
|
package org.schabi.newpipe.info_list;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.AdapterView;
|
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
@ -39,20 +38,21 @@ import org.schabi.newpipe.extractor.stream_info.StreamInfoItem;
|
||||||
|
|
||||||
public class InfoItemBuilder {
|
public class InfoItemBuilder {
|
||||||
|
|
||||||
final String viewsS;
|
private final String viewsS;
|
||||||
final String videosS;
|
private final String videosS;
|
||||||
final String subsS;
|
private final String subsS;
|
||||||
|
private final String subsPluralS;
|
||||||
|
|
||||||
final String thousand;
|
private final String thousand;
|
||||||
final String million;
|
private final String million;
|
||||||
final String billion;
|
private final String billion;
|
||||||
|
|
||||||
private static final String TAG = InfoItemBuilder.class.toString();
|
private static final String TAG = InfoItemBuilder.class.toString();
|
||||||
public interface OnInfoItemSelectedListener {
|
public interface OnInfoItemSelectedListener {
|
||||||
void selected(String url, int serviceId);
|
void selected(int serviceId, String url, String title);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Activity activity = null;
|
private Context mContext = null;
|
||||||
private View rootView = null;
|
private View rootView = null;
|
||||||
private ImageLoader imageLoader = ImageLoader.getInstance();
|
private ImageLoader imageLoader = ImageLoader.getInstance();
|
||||||
private DisplayImageOptions displayImageOptions =
|
private DisplayImageOptions displayImageOptions =
|
||||||
|
@ -60,15 +60,16 @@ public class InfoItemBuilder {
|
||||||
private OnInfoItemSelectedListener onStreamInfoItemSelectedListener;
|
private OnInfoItemSelectedListener onStreamInfoItemSelectedListener;
|
||||||
private OnInfoItemSelectedListener onChannelInfoItemSelectedListener;
|
private OnInfoItemSelectedListener onChannelInfoItemSelectedListener;
|
||||||
|
|
||||||
public InfoItemBuilder(Activity a, View rootView) {
|
public InfoItemBuilder(Context context, View rootView) {
|
||||||
activity = a;
|
mContext = context;
|
||||||
this.rootView = rootView;
|
this.rootView = rootView;
|
||||||
viewsS = a.getString(R.string.views);
|
viewsS = context.getString(R.string.views);
|
||||||
videosS = a.getString(R.string.videos);
|
videosS = context.getString(R.string.videos);
|
||||||
subsS = a.getString(R.string.subscriber);
|
subsS = context.getString(R.string.subscriber);
|
||||||
thousand = a.getString(R.string.short_thousand);
|
subsPluralS = context.getString(R.string.subscriber_plural);
|
||||||
million = a.getString(R.string.short_million);
|
thousand = context.getString(R.string.short_thousand);
|
||||||
billion = a.getString(R.string.short_billion);
|
million = context.getString(R.string.short_million);
|
||||||
|
billion = context.getString(R.string.short_billion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnStreamInfoItemSelectedListener(
|
public void setOnStreamInfoItemSelectedListener(
|
||||||
|
@ -156,13 +157,13 @@ public class InfoItemBuilder {
|
||||||
imageLoader.displayImage(info.thumbnail_url,
|
imageLoader.displayImage(info.thumbnail_url,
|
||||||
holder.itemThumbnailView,
|
holder.itemThumbnailView,
|
||||||
displayImageOptions,
|
displayImageOptions,
|
||||||
new ImageErrorLoadingListener(activity, rootView, info.service_id));
|
new ImageErrorLoadingListener(mContext, rootView, info.service_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.itemButton.setOnClickListener(new View.OnClickListener() {
|
holder.itemButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
onStreamInfoItemSelectedListener.selected(info.webpage_url, info.service_id);
|
onStreamInfoItemSelectedListener.selected(info.service_id, info.webpage_url, info.getTitle());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -178,13 +179,13 @@ public class InfoItemBuilder {
|
||||||
imageLoader.displayImage(info.thumbnailUrl,
|
imageLoader.displayImage(info.thumbnailUrl,
|
||||||
holder.itemThumbnailView,
|
holder.itemThumbnailView,
|
||||||
displayImageOptions,
|
displayImageOptions,
|
||||||
new ImageErrorLoadingListener(activity, rootView, info.serviceId));
|
new ImageErrorLoadingListener(mContext, rootView, info.serviceId));
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.itemButton.setOnClickListener(new View.OnClickListener() {
|
holder.itemButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
onChannelInfoItemSelectedListener.selected(info.getLink(), info.serviceId);
|
onChannelInfoItemSelectedListener.selected(info.serviceId, info.getLink(), info.channelName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -202,15 +203,17 @@ public class InfoItemBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String shortSubscriber(Long count){
|
public String shortSubscriber(Long count) {
|
||||||
if(count >= 1000000000){
|
String curSubString = count > 1 ? subsPluralS : subsS;
|
||||||
return Long.toString(count/1000000000)+ billion + " " + subsS;
|
|
||||||
}else if(count>=1000000){
|
if (count >= 1000000000) {
|
||||||
return Long.toString(count/1000000)+ million + " " + subsS;
|
return Long.toString(count / 1000000000) + billion + " " + curSubString;
|
||||||
}else if(count>=1000){
|
} else if (count >= 1000000) {
|
||||||
return Long.toString(count/1000)+ thousand + " " + subsS;
|
return Long.toString(count / 1000000) + million + " " + curSubString;
|
||||||
}else {
|
} else if (count >= 1000) {
|
||||||
return Long.toString(count)+ " " + subsS;
|
return Long.toString(count / 1000) + thousand + " " + curSubString;
|
||||||
|
} else {
|
||||||
|
return Long.toString(count) + " " + curSubString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -299,7 +299,6 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
changeState(STATE_LOADING);
|
|
||||||
isPrepared = false;
|
isPrepared = false;
|
||||||
qualityChanged = false;
|
qualityChanged = false;
|
||||||
|
|
||||||
|
@ -312,6 +311,7 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
if (videoStartPos > 0) simpleExoPlayer.seekTo(videoStartPos);
|
if (videoStartPos > 0) simpleExoPlayer.seekTo(videoStartPos);
|
||||||
simpleExoPlayer.prepare(videoSource);
|
simpleExoPlayer.prepare(videoSource);
|
||||||
simpleExoPlayer.setPlayWhenReady(autoPlay);
|
simpleExoPlayer.setPlayWhenReady(autoPlay);
|
||||||
|
changeState(STATE_LOADING);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
|
@ -396,7 +396,6 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) playbackSeekBar.getThumb().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) playbackSeekBar.getThumb().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
|
||||||
|
|
||||||
animateView(endScreen, false, 0, 0);
|
animateView(endScreen, false, 0, 0);
|
||||||
animateView(controlsRoot, false, 0, 0);
|
|
||||||
loadingPanel.setBackgroundColor(Color.BLACK);
|
loadingPanel.setBackgroundColor(Color.BLACK);
|
||||||
animateView(loadingPanel, true, 0, 0);
|
animateView(loadingPanel, true, 0, 0);
|
||||||
animateView(surfaceForeground, true, 100, 0);
|
animateView(surfaceForeground, true, 100, 0);
|
||||||
|
@ -408,7 +407,12 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
if (!isProgressLoopRunning.get()) startProgressLoop();
|
if (!isProgressLoopRunning.get()) startProgressLoop();
|
||||||
showAndAnimateControl(-1, true);
|
showAndAnimateControl(-1, true);
|
||||||
loadingPanel.setVisibility(View.GONE);
|
loadingPanel.setVisibility(View.GONE);
|
||||||
animateView(controlsRoot, false, 500, DEFAULT_CONTROLS_HIDE_TIME, true);
|
animateView(controlsRoot, true, 500, 0, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
animateView(controlsRoot, false, 500, DEFAULT_CONTROLS_HIDE_TIME, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
animateView(currentDisplaySeek, false, 200, 0);
|
animateView(currentDisplaySeek, false, 200, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,7 +421,6 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
if (DEBUG) Log.d(TAG, "onBuffering() called");
|
if (DEBUG) Log.d(TAG, "onBuffering() called");
|
||||||
loadingPanel.setBackgroundColor(Color.TRANSPARENT);
|
loadingPanel.setBackgroundColor(Color.TRANSPARENT);
|
||||||
animateView(loadingPanel, true, 500, 0);
|
animateView(loadingPanel, true, 500, 0);
|
||||||
animateView(controlsRoot, false, 0, 0, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -598,14 +601,12 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
if (DEBUG) Log.d(TAG, "onFastRewind() called");
|
if (DEBUG) Log.d(TAG, "onFastRewind() called");
|
||||||
seekBy(-FAST_FORWARD_REWIND_AMOUNT);
|
seekBy(-FAST_FORWARD_REWIND_AMOUNT);
|
||||||
showAndAnimateControl(R.drawable.ic_action_av_fast_rewind, true);
|
showAndAnimateControl(R.drawable.ic_action_av_fast_rewind, true);
|
||||||
animateView(controlsRoot, false, 100, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onFastForward() {
|
public void onFastForward() {
|
||||||
if (DEBUG) Log.d(TAG, "onFastForward() called");
|
if (DEBUG) Log.d(TAG, "onFastForward() called");
|
||||||
seekBy(FAST_FORWARD_REWIND_AMOUNT);
|
seekBy(FAST_FORWARD_REWIND_AMOUNT);
|
||||||
showAndAnimateControl(R.drawable.ic_action_av_fast_forward, true);
|
showAndAnimateControl(R.drawable.ic_action_av_fast_forward, true);
|
||||||
animateView(controlsRoot, false, 100, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -24,9 +24,10 @@ import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.ActivityCommunicator;
|
import org.schabi.newpipe.ActivityCommunicator;
|
||||||
import org.schabi.newpipe.BuildConfig;
|
import org.schabi.newpipe.BuildConfig;
|
||||||
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.detail.VideoItemDetailActivity;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.util.NavStack;
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -353,10 +354,11 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||||
new Intent(ACTION_REWIND), PendingIntent.FLAG_UPDATE_CURRENT);
|
new Intent(ACTION_REWIND), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
//build intent to return to video, on tapping notification
|
//build intent to return to video, on tapping notification
|
||||||
Intent openDetailViewIntent = new Intent(getApplicationContext(),
|
Intent openDetailViewIntent = new Intent(getApplicationContext(), MainActivity.class);
|
||||||
VideoItemDetailActivity.class);
|
openDetailViewIntent.putExtra(Constants.KEY_SERVICE_ID, serviceId);
|
||||||
openDetailViewIntent.putExtra(NavStack.SERVICE_ID, serviceId);
|
openDetailViewIntent.putExtra(Constants.KEY_URL, webUrl);
|
||||||
openDetailViewIntent.putExtra(NavStack.URL, webUrl);
|
openDetailViewIntent.putExtra(Constants.KEY_TITLE, title);
|
||||||
|
openDetailViewIntent.putExtra(Constants.KEY_LINK_TYPE, StreamingService.LinkType.STREAM);
|
||||||
openDetailViewIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
openDetailViewIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
PendingIntent openDetailView = PendingIntent.getActivity(owner, noteID,
|
PendingIntent openDetailView = PendingIntent.getActivity(owner, noteID,
|
||||||
openDetailViewIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
openDetailViewIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
|
@ -24,7 +24,6 @@ import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.util.NavStack;
|
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
|
@ -70,6 +69,7 @@ public class ExoPlayerActivity extends Activity {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showSystemUi();
|
||||||
setContentView(R.layout.activity_exo_player);
|
setContentView(R.layout.activity_exo_player);
|
||||||
playerImpl = new AbstractPlayerImpl();
|
playerImpl = new AbstractPlayerImpl();
|
||||||
playerImpl.setup(findViewById(android.R.id.content));
|
playerImpl.setup(findViewById(android.R.id.content));
|
||||||
|
@ -88,7 +88,6 @@ public class ExoPlayerActivity extends Activity {
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
if (DEBUG) Log.d(TAG, "onBackPressed() called");
|
if (DEBUG) Log.d(TAG, "onBackPressed() called");
|
||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
if (playerImpl.isStartedFromNewPipe()) NavStack.getInstance().openDetailActivity(this, playerImpl.getVideoUrl(), 0);
|
|
||||||
if (playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(false);
|
if (playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,8 +339,7 @@ public class ExoPlayerActivity extends Activity {
|
||||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||||
super.onStopTrackingTouch(seekBar);
|
super.onStopTrackingTouch(seekBar);
|
||||||
if (playerImpl.wasPlaying()) {
|
if (playerImpl.wasPlaying()) {
|
||||||
hideSystemUi();
|
animateView(playerImpl.getControlsRoot(), false, 100, 0);
|
||||||
playerImpl.getControlsRoot().setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,6 +363,13 @@ public class ExoPlayerActivity extends Activity {
|
||||||
public void onLoading() {
|
public void onLoading() {
|
||||||
super.onLoading();
|
super.onLoading();
|
||||||
playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
||||||
|
animateView(playPauseButton, false, 100, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBuffering() {
|
||||||
|
super.onBuffering();
|
||||||
|
animateView(playPauseButton, false, 100, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -384,6 +389,7 @@ public class ExoPlayerActivity extends Activity {
|
||||||
public void onPlaying() {
|
public void onPlaying() {
|
||||||
super.onPlaying();
|
super.onPlaying();
|
||||||
animateView(playPauseButton, true, 500, 0);
|
animateView(playPauseButton, true, 500, 0);
|
||||||
|
showSystemUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -34,16 +34,17 @@ import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListene
|
||||||
|
|
||||||
import org.schabi.newpipe.ActivityCommunicator;
|
import org.schabi.newpipe.ActivityCommunicator;
|
||||||
import org.schabi.newpipe.BuildConfig;
|
import org.schabi.newpipe.BuildConfig;
|
||||||
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.detail.VideoItemDetailActivity;
|
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
|
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||||
import org.schabi.newpipe.util.NavStack;
|
import org.schabi.newpipe.util.Constants;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
import org.schabi.newpipe.util.Utils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -107,7 +108,7 @@ public class PopupVideoPlayer extends Service {
|
||||||
if (!playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(true);
|
if (!playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(true);
|
||||||
|
|
||||||
if (imageLoader != null) imageLoader.clearMemoryCache();
|
if (imageLoader != null) imageLoader.clearMemoryCache();
|
||||||
if (intent.getStringExtra(NavStack.URL) != null) {
|
if (intent.getStringExtra(Constants.KEY_URL) != null) {
|
||||||
playerImpl.setStartedFromNewPipe(false);
|
playerImpl.setStartedFromNewPipe(false);
|
||||||
Thread fetcher = new Thread(new FetcherRunnable(intent));
|
Thread fetcher = new Thread(new FetcherRunnable(intent));
|
||||||
fetcher.start();
|
fetcher.start();
|
||||||
|
@ -158,7 +159,7 @@ public class PopupVideoPlayer extends Service {
|
||||||
playerImpl.onVideoPlayPause();
|
playerImpl.onVideoPlayPause();
|
||||||
break;
|
break;
|
||||||
case ACTION_OPEN_DETAIL:
|
case ACTION_OPEN_DETAIL:
|
||||||
onOpenDetail(PopupVideoPlayer.this, playerImpl.getVideoUrl());
|
onOpenDetail(PopupVideoPlayer.this, playerImpl.getVideoUrl(), playerImpl.getVideoTitle());
|
||||||
break;
|
break;
|
||||||
case ACTION_REPEAT:
|
case ACTION_REPEAT:
|
||||||
playerImpl.onRepeatClicked();
|
playerImpl.onRepeatClicked();
|
||||||
|
@ -266,12 +267,14 @@ public class PopupVideoPlayer extends Service {
|
||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onOpenDetail(Context context, String videoUrl) {
|
public void onOpenDetail(Context context, String videoUrl, String videoTitle) {
|
||||||
if (DEBUG) Log.d(TAG, "onOpenDetail() called with: context = [" + context + "], videoUrl = [" + videoUrl + "]");
|
if (DEBUG) Log.d(TAG, "onOpenDetail() called with: context = [" + context + "], videoUrl = [" + videoUrl + "]");
|
||||||
Intent i = new Intent(context, VideoItemDetailActivity.class);
|
Intent i = new Intent(context, MainActivity.class);
|
||||||
i.putExtra(NavStack.SERVICE_ID, 0)
|
i.putExtra(Constants.KEY_SERVICE_ID, 0);
|
||||||
.putExtra(NavStack.URL, videoUrl)
|
i.putExtra(Constants.KEY_URL, videoUrl);
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
i.putExtra(Constants.KEY_TITLE, videoTitle);
|
||||||
|
i.putExtra(Constants.KEY_LINK_TYPE, StreamingService.LinkType.STREAM);
|
||||||
|
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
context.startActivity(i);
|
context.startActivity(i);
|
||||||
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
|
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
|
||||||
}
|
}
|
||||||
|
@ -510,8 +513,6 @@ public class PopupVideoPlayer extends Service {
|
||||||
private class FetcherRunnable implements Runnable {
|
private class FetcherRunnable implements Runnable {
|
||||||
private final Intent intent;
|
private final Intent intent;
|
||||||
private final Handler mainHandler;
|
private final Handler mainHandler;
|
||||||
private final boolean printStreams = true;
|
|
||||||
|
|
||||||
|
|
||||||
FetcherRunnable(Intent intent) {
|
FetcherRunnable(Intent intent) {
|
||||||
this.intent = intent;
|
this.intent = intent;
|
||||||
|
@ -524,48 +525,22 @@ public class PopupVideoPlayer extends Service {
|
||||||
try {
|
try {
|
||||||
StreamingService service = NewPipe.getService(0);
|
StreamingService service = NewPipe.getService(0);
|
||||||
if (service == null) return;
|
if (service == null) return;
|
||||||
streamExtractor = service.getExtractorInstance(intent.getStringExtra(NavStack.URL));
|
streamExtractor = service.getExtractorInstance(intent.getStringExtra(Constants.KEY_URL));
|
||||||
StreamInfo info = StreamInfo.getVideoInfo(streamExtractor);
|
StreamInfo info = StreamInfo.getVideoInfo(streamExtractor);
|
||||||
String defaultResolution = playerImpl.getSharedPreferences().getString(
|
|
||||||
getResources().getString(R.string.default_resolution_key),
|
|
||||||
getResources().getString(R.string.default_resolution_value));
|
|
||||||
|
|
||||||
VideoStream chosen = null, secondary = null, fallback = null;
|
|
||||||
playerImpl.setVideoStreamsList(info.video_streams instanceof ArrayList
|
playerImpl.setVideoStreamsList(info.video_streams instanceof ArrayList
|
||||||
? (ArrayList<VideoStream>) info.video_streams
|
? (ArrayList<VideoStream>) info.video_streams
|
||||||
: new ArrayList<>(info.video_streams));
|
: new ArrayList<>(info.video_streams));
|
||||||
|
|
||||||
for (VideoStream item : info.video_streams) {
|
int defaultResolution = Utils.getPreferredResolution(PopupVideoPlayer.this, info.video_streams);
|
||||||
if (DEBUG && printStreams) {
|
playerImpl.setSelectedIndexStream(defaultResolution);
|
||||||
Log.d(TAG, "FetcherRunnable.StreamExtractor: current Item"
|
|
||||||
+ ", item.resolution = " + item.resolution
|
if (DEBUG) {
|
||||||
+ ", item.format = " + item.format
|
Log.d(TAG, "FetcherRunnable.StreamExtractor: chosen = "
|
||||||
+ ", item.url = " + item.url);
|
+ MediaFormat.getNameById(info.video_streams.get(defaultResolution).format) + " "
|
||||||
}
|
+ info.video_streams.get(defaultResolution).resolution + " > "
|
||||||
if (defaultResolution.equals(item.resolution)) {
|
+ info.video_streams.get(defaultResolution).url);
|
||||||
if (item.format == MediaFormat.MPEG_4.id) {
|
|
||||||
chosen = item;
|
|
||||||
if (DEBUG) Log.d(TAG, "FetcherRunnable.StreamExtractor: CHOSEN item, item.resolution = " + item.resolution + ", item.format = " + item.format + ", item.url = " + item.url);
|
|
||||||
} else if (item.format == 2) secondary = item;
|
|
||||||
else fallback = item;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int selectedIndexStream;
|
|
||||||
|
|
||||||
if (chosen != null) selectedIndexStream = info.video_streams.indexOf(chosen);
|
|
||||||
else if (secondary != null) selectedIndexStream = info.video_streams.indexOf(secondary);
|
|
||||||
else if (fallback != null) selectedIndexStream = info.video_streams.indexOf(fallback);
|
|
||||||
else selectedIndexStream = 0;
|
|
||||||
|
|
||||||
playerImpl.setSelectedIndexStream(selectedIndexStream);
|
|
||||||
|
|
||||||
if (DEBUG && printStreams) Log.d(TAG, "FetcherRunnable.StreamExtractor: chosen = " + chosen
|
|
||||||
+ "\n, secondary = " + secondary
|
|
||||||
+ "\n, fallback = " + fallback
|
|
||||||
+ "\n, info.video_streams.get(0).url = " + info.video_streams.get(0).url);
|
|
||||||
|
|
||||||
|
|
||||||
playerImpl.setVideoUrl(info.webpage_url);
|
playerImpl.setVideoUrl(info.webpage_url);
|
||||||
playerImpl.setVideoTitle(info.title);
|
playerImpl.setVideoTitle(info.title);
|
||||||
playerImpl.setChannelName(info.uploader);
|
playerImpl.setChannelName(info.uploader);
|
||||||
|
@ -578,6 +553,8 @@ public class PopupVideoPlayer extends Service {
|
||||||
playerImpl.playVideo(playerImpl.getSelectedStreamUri(), true);
|
playerImpl.playVideo(playerImpl.getSelectedStreamUri(), true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
imageLoader.resume();
|
||||||
imageLoader.loadImage(info.thumbnail_url, displayImageOptions, new SimpleImageLoadingListener() {
|
imageLoader.loadImage(info.thumbnail_url, displayImageOptions, new SimpleImageLoadingListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onLoadingComplete(String imageUri, View view, final Bitmap loadedImage) {
|
public void onLoadingComplete(String imageUri, View view, final Bitmap loadedImage) {
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.schabi.newpipe.util;
|
||||||
|
|
||||||
|
public class Constants {
|
||||||
|
public static final String KEY_SERVICE_ID = "key_service_id";
|
||||||
|
public static final String KEY_URL = "key_url";
|
||||||
|
public static final String KEY_TITLE = "key_title";
|
||||||
|
public static final String KEY_LINK_TYPE = "key_link_type";
|
||||||
|
}
|
|
@ -1,148 +0,0 @@
|
||||||
package org.schabi.newpipe.util;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.app.NavUtils;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.ChannelActivity;
|
|
||||||
import org.schabi.newpipe.MainActivity;
|
|
||||||
import org.schabi.newpipe.detail.VideoItemDetailActivity;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 16.02.17.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* NavStack.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* class helps to navigate within the app
|
|
||||||
* IMPORTAND: the top of the stack is the current activity !!!
|
|
||||||
*/
|
|
||||||
public class NavStack {
|
|
||||||
private static final String TAG = NavStack.class.toString();
|
|
||||||
public static final String SERVICE_ID = "service_id";
|
|
||||||
public static final String URL = "url";
|
|
||||||
|
|
||||||
private static final String NAV_STACK="nav_stack";
|
|
||||||
|
|
||||||
private enum ActivityId {
|
|
||||||
CHANNEL,
|
|
||||||
DETAIL
|
|
||||||
}
|
|
||||||
|
|
||||||
private class NavEntry {
|
|
||||||
public NavEntry(String url, int serviceId) {
|
|
||||||
this.url = url;
|
|
||||||
this.serviceId = serviceId;
|
|
||||||
}
|
|
||||||
public String url;
|
|
||||||
public int serviceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static NavStack instance = new NavStack();
|
|
||||||
private Stack<NavEntry> stack = new Stack<NavEntry>();
|
|
||||||
|
|
||||||
private NavStack() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static NavStack getInstance() {
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void navBack(Activity activity) throws Exception {
|
|
||||||
if(stack.size() == 0) { // if stack is already empty here, activity was probably called
|
|
||||||
// from another app
|
|
||||||
activity.finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
stack.pop(); // remove curent activty, since we dont want to return to itself
|
|
||||||
if (stack.size() == 0) {
|
|
||||||
openMainActivity(activity); // if no more page is on the stack this means we are home
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
NavEntry entry = stack.pop(); // this element will reapear, since by calling the old page
|
|
||||||
// this element will be pushed on top again
|
|
||||||
try {
|
|
||||||
StreamingService service = NewPipe.getService(entry.serviceId);
|
|
||||||
switch (service.getLinkTypeByUrl(entry.url)) {
|
|
||||||
case STREAM:
|
|
||||||
openDetailActivity(activity, entry.url, entry.serviceId);
|
|
||||||
break;
|
|
||||||
case CHANNEL:
|
|
||||||
openChannelActivity(activity, entry.url, entry.serviceId);
|
|
||||||
break;
|
|
||||||
case NONE:
|
|
||||||
throw new Exception("Url not known to service. service="
|
|
||||||
+ Integer.toString(entry.serviceId) + " url=" + entry.url);
|
|
||||||
default:
|
|
||||||
openMainActivity(activity);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void openChannelActivity(Context context, String url, int serviceId) {
|
|
||||||
openActivity(context, url, serviceId, ChannelActivity.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void openDetailActivity(Context context, String url, int serviceId) {
|
|
||||||
openActivity(context, url, serviceId, VideoItemDetailActivity.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openActivity(Context context, String url, int serviceId, Class acitivtyClass) {
|
|
||||||
//if last element has the same url do not push to stack again
|
|
||||||
if(stack.isEmpty() || !stack.peek().url.equals(url)) {
|
|
||||||
stack.push(new NavEntry(url, serviceId));
|
|
||||||
}
|
|
||||||
Intent i = new Intent(context, acitivtyClass);
|
|
||||||
i.putExtra(SERVICE_ID, serviceId);
|
|
||||||
i.putExtra(URL, url);
|
|
||||||
context.startActivity(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void openMainActivity(Activity a) {
|
|
||||||
stack.clear();
|
|
||||||
Intent i = new Intent(a, MainActivity.class);
|
|
||||||
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
||||||
NavUtils.navigateUpTo(a, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onSaveInstanceState(Bundle state) {
|
|
||||||
ArrayList<String> sa = new ArrayList<>();
|
|
||||||
for(NavEntry entry : stack) {
|
|
||||||
sa.add(entry.url);
|
|
||||||
}
|
|
||||||
state.putStringArrayList(NAV_STACK, sa);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void restoreSavedInstanceState(Bundle state) {
|
|
||||||
ArrayList<String> sa = state.getStringArrayList(NAV_STACK);
|
|
||||||
stack.clear();
|
|
||||||
for(String url : sa) {
|
|
||||||
stack.push(new NavEntry(url, NewPipe.getServiceByUrl(url).getServiceId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package org.schabi.newpipe.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.MainActivity;
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
||||||
|
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||||
|
|
||||||
|
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||||
|
public class NavigationHelper {
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Through Interface (faster)
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
public static void openChannel(OnItemSelectedListener listener, int serviceId, String url) {
|
||||||
|
openChannel(listener, serviceId, url, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openChannel(OnItemSelectedListener listener, int serviceId, String url, String name) {
|
||||||
|
listener.onItemSelected(StreamingService.LinkType.CHANNEL, serviceId, url, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openVideoDetail(OnItemSelectedListener listener, int serviceId, String url) {
|
||||||
|
openVideoDetail(listener, serviceId, url, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openVideoDetail(OnItemSelectedListener listener, int serviceId, String url, String title) {
|
||||||
|
listener.onItemSelected(StreamingService.LinkType.STREAM, serviceId, url, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Through Intents
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
public static void openByLink(Context context, String url) throws Exception {
|
||||||
|
context.startActivity(getIntentByLink(context, url));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openChannel(Context context, int serviceId, String url) {
|
||||||
|
openChannel(context, serviceId, url, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openChannel(Context context, int serviceId, String url, String name) {
|
||||||
|
Intent openIntent = getOpenIntent(context, url, serviceId, StreamingService.LinkType.CHANNEL);
|
||||||
|
if (name != null && !name.isEmpty()) openIntent.putExtra(Constants.KEY_TITLE, name);
|
||||||
|
context.startActivity(openIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openVideoDetail(Context context, int serviceId, String url) {
|
||||||
|
openVideoDetail(context, serviceId, url, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openVideoDetail(Context context, int serviceId, String url, String title) {
|
||||||
|
Intent openIntent = getOpenIntent(context, url, serviceId, StreamingService.LinkType.STREAM);
|
||||||
|
if (title != null && !title.isEmpty()) openIntent.putExtra(Constants.KEY_TITLE, title);
|
||||||
|
context.startActivity(openIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openMainActivity(Context context) {
|
||||||
|
Intent mIntent = new Intent(context, MainActivity.class);
|
||||||
|
context.startActivity(mIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Intent getOpenIntent(Context context, String url, int serviceId, StreamingService.LinkType type) {
|
||||||
|
Intent mIntent = new Intent(context, MainActivity.class);
|
||||||
|
mIntent.putExtra(Constants.KEY_SERVICE_ID, serviceId);
|
||||||
|
mIntent.putExtra(Constants.KEY_URL, url);
|
||||||
|
mIntent.putExtra(Constants.KEY_LINK_TYPE, type);
|
||||||
|
return mIntent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Intent getIntentByLink(Context context, String url) throws Exception {
|
||||||
|
StreamingService service = NewPipe.getServiceByUrl(url);
|
||||||
|
if (service == null) throw new Exception("NewPipe.getServiceByUrl returned null for url > \"" + url + "\"");
|
||||||
|
int serviceId = service.getServiceId();
|
||||||
|
switch (service.getLinkTypeByUrl(url)) {
|
||||||
|
case STREAM:
|
||||||
|
Intent sIntent = getOpenIntent(context, url, serviceId, StreamingService.LinkType.STREAM);
|
||||||
|
sIntent.putExtra(VideoDetailFragment.AUTO_PLAY, PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
.getBoolean(context.getString(R.string.autoplay_through_intent_key), false));
|
||||||
|
return sIntent;
|
||||||
|
case CHANNEL:
|
||||||
|
return getOpenIntent(context, url, serviceId, StreamingService.LinkType.CHANNEL);
|
||||||
|
case NONE:
|
||||||
|
throw new Exception("Url not known to service. service="
|
||||||
|
+ Integer.toString(serviceId) + " url=" + url);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package org.schabi.newpipe.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.AudioStream;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Utils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the index of the default stream in the list, based on the
|
||||||
|
* preferred resolution and format chosen in the settings
|
||||||
|
*
|
||||||
|
* @param videoStreams the list that will be extracted the index
|
||||||
|
* @return index of the preferred resolution&format
|
||||||
|
*/
|
||||||
|
public static int getPreferredResolution(Context context, List<VideoStream> videoStreams) {
|
||||||
|
SharedPreferences defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
if (defaultPreferences == null) return 0;
|
||||||
|
|
||||||
|
String defaultResolution = defaultPreferences
|
||||||
|
.getString(context.getString(R.string.default_resolution_key),
|
||||||
|
context.getString(R.string.default_resolution_value));
|
||||||
|
|
||||||
|
String preferredFormat = defaultPreferences
|
||||||
|
.getString(context.getString(R.string.preferred_video_format_key),
|
||||||
|
context.getString(R.string.preferred_video_format_default));
|
||||||
|
|
||||||
|
// first try to find the one with the right resolution
|
||||||
|
int selectedFormat = 0;
|
||||||
|
for (int i = 0; i < videoStreams.size(); i++) {
|
||||||
|
VideoStream item = videoStreams.get(i);
|
||||||
|
if (defaultResolution.equals(item.resolution)) {
|
||||||
|
selectedFormat = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// than try to find the one with the right resolution and format
|
||||||
|
for (int i = 0; i < videoStreams.size(); i++) {
|
||||||
|
VideoStream item = videoStreams.get(i);
|
||||||
|
if (defaultResolution.equals(item.resolution)
|
||||||
|
&& preferredFormat.equals(MediaFormat.getNameById(item.format))) {
|
||||||
|
selectedFormat = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is actually an error,
|
||||||
|
// but maybe there is really no stream fitting to the default value.
|
||||||
|
return selectedFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the index of the default stream in the list, based on the
|
||||||
|
* preferred audio format chosen in the settings
|
||||||
|
*
|
||||||
|
* @param audioStreams the list that will be extracted the index
|
||||||
|
* @return index of the preferred format
|
||||||
|
*/
|
||||||
|
public static int getPreferredAudioFormat(Context context, List<AudioStream> audioStreams) {
|
||||||
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
if (sharedPreferences == null) return 0;
|
||||||
|
|
||||||
|
String preferredFormatString = sharedPreferences.getString(context.getString(R.string.default_audio_format_key), "webm");
|
||||||
|
|
||||||
|
int preferredFormat = MediaFormat.WEBMA.id;
|
||||||
|
switch (preferredFormatString) {
|
||||||
|
case "webm":
|
||||||
|
preferredFormat = MediaFormat.WEBMA.id;
|
||||||
|
break;
|
||||||
|
case "m4a":
|
||||||
|
preferredFormat = MediaFormat.M4A.id;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < audioStreams.size(); i++) {
|
||||||
|
if (audioStreams.get(i).format == preferredFormat) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
package org.schabi.newpipe.workers;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.MainActivity;
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract {@link ChannelInfo} with {@link ChannelExtractor} from the given url of the given service
|
||||||
|
*
|
||||||
|
* @author mauriciocolli
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public class ChannelExtractorWorker extends ExtractorWorker {
|
||||||
|
//private static final String TAG = "ChannelExtractorWorker";
|
||||||
|
|
||||||
|
private int pageNumber;
|
||||||
|
private boolean onlyVideos;
|
||||||
|
|
||||||
|
private ChannelInfo channelInfo = null;
|
||||||
|
private OnChannelInfoReceive callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface which will be called for result and errors
|
||||||
|
*/
|
||||||
|
public interface OnChannelInfoReceive {
|
||||||
|
void onReceive(ChannelInfo info);
|
||||||
|
void onError(int messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param context context for error reporting purposes
|
||||||
|
* @param serviceId id of the request service
|
||||||
|
* @param channelUrl channelUrl of the service (e.g. https://www.youtube.com/channel/UC_aEa8K-EOJ3D6gOs7HcyNg)
|
||||||
|
* @param callback listener that will be called-back when events occur (check {@link ChannelExtractorWorker.OnChannelInfoReceive})
|
||||||
|
*/
|
||||||
|
public ChannelExtractorWorker(Context context, int serviceId, String channelUrl, int pageNumber, OnChannelInfoReceive callback) {
|
||||||
|
super(context, channelUrl, serviceId);
|
||||||
|
this.pageNumber = pageNumber;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
this.callback = null;
|
||||||
|
this.channelInfo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doWork(int serviceId, String url) throws Exception {
|
||||||
|
ChannelExtractor extractor = getService().getChannelExtractorInstance(url, pageNumber);
|
||||||
|
channelInfo = ChannelInfo.getInfo(extractor);
|
||||||
|
|
||||||
|
if (!channelInfo.errors.isEmpty()) handleErrorsDuringExtraction(channelInfo.errors, ErrorActivity.REQUESTED_CHANNEL);
|
||||||
|
|
||||||
|
if (callback != null && channelInfo != null && !isInterrupted()) getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isInterrupted() || callback == null) return;
|
||||||
|
|
||||||
|
callback.onReceive(channelInfo);
|
||||||
|
onDestroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleException(Exception exception, int serviceId, String url) {
|
||||||
|
if (exception instanceof IOException) {
|
||||||
|
if (callback != null) getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onError(R.string.network_error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (exception instanceof ParsingException || exception instanceof ExtractionException) {
|
||||||
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL, getServiceName(), url, R.string.parsing_error));
|
||||||
|
finishIfActivity();
|
||||||
|
} else {
|
||||||
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL, getServiceName(), url, R.string.general_error));
|
||||||
|
finishIfActivity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOnlyVideos() {
|
||||||
|
return onlyVideos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnlyVideos(boolean onlyVideos) {
|
||||||
|
this.onlyVideos = onlyVideos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
package org.schabi.newpipe.workers;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common properties of ExtractorWorkers
|
||||||
|
*
|
||||||
|
* @author mauriciocolli
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public abstract class ExtractorWorker extends Thread {
|
||||||
|
|
||||||
|
private final AtomicBoolean isRunning = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
private final String url;
|
||||||
|
private final int serviceId;
|
||||||
|
private Context context;
|
||||||
|
private Handler handler;
|
||||||
|
private StreamingService service;
|
||||||
|
|
||||||
|
public ExtractorWorker(Context context, String url, int serviceId) {
|
||||||
|
this.context = context;
|
||||||
|
this.url = url;
|
||||||
|
this.serviceId = serviceId;
|
||||||
|
this.handler = new Handler(context.getMainLooper());
|
||||||
|
if (url.length() >= 40) setName("Thread-" + url.substring(url.length() - 11, url.length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
isRunning.set(true);
|
||||||
|
service = NewPipe.getService(serviceId);
|
||||||
|
doWork(serviceId, url);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Handle the exception only if thread is not interrupted
|
||||||
|
if (!isInterrupted() && !(e instanceof InterruptedIOException) && !(e.getCause() instanceof InterruptedIOException)) {
|
||||||
|
handleException(e, serviceId, url);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
isRunning.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Here is the place that the heavy work is realized
|
||||||
|
*
|
||||||
|
* @param serviceId serviceId that was passed when created this object
|
||||||
|
* @param url url that was passed when created this object
|
||||||
|
*
|
||||||
|
* @throws Exception these exceptions are handled by the {@link #handleException(Exception, int, String)}
|
||||||
|
*/
|
||||||
|
protected abstract void doWork(int serviceId, String url) throws Exception;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that handle the exception thrown by the {@link #doWork(int, String)}.
|
||||||
|
*
|
||||||
|
* @param exception {@link Exception} that was thrown by {@link #doWork(int, String)}
|
||||||
|
*/
|
||||||
|
protected abstract void handleException(Exception exception, int serviceId, String url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the errors <b>during</b> extraction and shows a Report button to the user.<br/>
|
||||||
|
* Subclasses <b>maybe</b> call this method.
|
||||||
|
*
|
||||||
|
* @param errorsList list of exceptions that happened during extraction
|
||||||
|
* @param errorUserAction what action was the user performing during the error.
|
||||||
|
* (One of the {@link ErrorActivity}.REQUEST_* error (message) ids)
|
||||||
|
*/
|
||||||
|
protected void handleErrorsDuringExtraction(List<Throwable> errorsList, int errorUserAction){
|
||||||
|
String errorString = "<error id>";
|
||||||
|
switch (errorUserAction) {
|
||||||
|
case ErrorActivity.REQUESTED_STREAM:
|
||||||
|
errorString= ErrorActivity.REQUESTED_STREAM_STRING;
|
||||||
|
break;
|
||||||
|
case ErrorActivity.REQUESTED_CHANNEL:
|
||||||
|
errorString= ErrorActivity.REQUESTED_CHANNEL_STRING;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.e(errorString, "OCCURRED ERRORS DURING EXTRACTION:");
|
||||||
|
for (Throwable e : errorsList) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e(errorString, "------");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getContext() instanceof Activity) {
|
||||||
|
View rootView = getContext() != null ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
||||||
|
ErrorActivity.reportError(getHandler(), getContext(), errorsList, null, rootView, ErrorActivity.ErrorInfo.make(errorUserAction, getServiceName(), url, 0 /* no message for the user */));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the extraction is not completed yet
|
||||||
|
*
|
||||||
|
* @return the value of the AtomicBoolean {@link #isRunning}
|
||||||
|
*/
|
||||||
|
public boolean isRunning() {
|
||||||
|
return isRunning.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel this ExtractorWorker, calling {@link #onDestroy()} and interrupting this thread.
|
||||||
|
* <p>
|
||||||
|
* <b>Note:</b> Any I/O that is active in the moment that this method is called will be canceled and a Exception will be thrown, because of the {@link #interrupt()}.<br>
|
||||||
|
* This is useful when you don't want the resulting {@link StreamInfo} anymore, but don't want to waste bandwidth, otherwise it'd run till it receives the StreamInfo.
|
||||||
|
*/
|
||||||
|
public void cancel() {
|
||||||
|
onDestroy();
|
||||||
|
this.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that discards everything that doesn't need anymore.<br>
|
||||||
|
* Subclasses can override this method to destroy their garbage.
|
||||||
|
*/
|
||||||
|
protected void onDestroy() {
|
||||||
|
this.isRunning.set(false);
|
||||||
|
this.context = null;
|
||||||
|
this.handler = null;
|
||||||
|
this.service = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the context passed in the constructor is an {@link Activity}, finish it.
|
||||||
|
*/
|
||||||
|
protected void finishIfActivity() {
|
||||||
|
if (getContext() instanceof Activity) ((Activity) getContext()).finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Handler getHandler() {
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamingService getService() {
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getServiceId() {
|
||||||
|
return serviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServiceName() {
|
||||||
|
return service == null ? "none" : service.getServiceInfo().name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
package org.schabi.newpipe.workers;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.MainActivity;
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||||
|
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract {@link StreamInfo} with {@link StreamExtractor} from the given url of the given service
|
||||||
|
*
|
||||||
|
* @author mauriciocolli
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public class StreamExtractorWorker extends ExtractorWorker {
|
||||||
|
//private static final String TAG = "StreamExtractorWorker";
|
||||||
|
|
||||||
|
private StreamInfo streamInfo = null;
|
||||||
|
private OnStreamInfoReceivedListener callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface which will be called for result and errors
|
||||||
|
*/
|
||||||
|
public interface OnStreamInfoReceivedListener {
|
||||||
|
void onReceive(StreamInfo info);
|
||||||
|
void onError(int messageId);
|
||||||
|
void onReCaptchaException();
|
||||||
|
void onBlockedByGemaError();
|
||||||
|
void onContentErrorWithMessage(int messageId);
|
||||||
|
void onContentError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param context context for error reporting purposes
|
||||||
|
* @param serviceId id of the request service
|
||||||
|
* @param videoUrl videoUrl of the service (e.g. https://www.youtube.com/watch?v=HyHNuVaZJ-k)
|
||||||
|
* @param callback listener that will be called-back when events occur (check {@link StreamExtractorWorker.OnStreamInfoReceivedListener})
|
||||||
|
*/
|
||||||
|
public StreamExtractorWorker(Context context, int serviceId, String videoUrl, OnStreamInfoReceivedListener callback) {
|
||||||
|
super(context, videoUrl, serviceId);
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
this.callback = null;
|
||||||
|
this.streamInfo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doWork(int serviceId, String url) throws Exception {
|
||||||
|
StreamExtractor streamExtractor = getService().getExtractorInstance(url);
|
||||||
|
streamInfo = StreamInfo.getVideoInfo(streamExtractor);
|
||||||
|
|
||||||
|
if (streamInfo != null && !streamInfo.errors.isEmpty()) handleErrorsDuringExtraction(streamInfo.errors, ErrorActivity.REQUESTED_STREAM);
|
||||||
|
|
||||||
|
if (callback != null && streamInfo != null && !isInterrupted()) getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isInterrupted() || callback == null) return;
|
||||||
|
|
||||||
|
callback.onReceive(streamInfo);
|
||||||
|
onDestroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleException(final Exception exception, int serviceId, String url) {
|
||||||
|
if (exception instanceof ReCaptchaException) {
|
||||||
|
if (callback != null) getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onReCaptchaException();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (exception instanceof IOException) {
|
||||||
|
if (callback != null) getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onError(R.string.network_error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (exception instanceof YoutubeStreamExtractor.GemaException) {
|
||||||
|
if (callback != null) getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onBlockedByGemaError();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (exception instanceof YoutubeStreamExtractor.LiveStreamException) {
|
||||||
|
if (callback != null) getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onContentErrorWithMessage(R.string.live_streams_not_supported);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (exception instanceof StreamExtractor.ContentNotAvailableException) {
|
||||||
|
if (callback != null) getHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.onContentError();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (exception instanceof YoutubeStreamExtractor.DecryptException) {
|
||||||
|
// custom service related exceptions
|
||||||
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.youtube_signature_decryption_error));
|
||||||
|
finishIfActivity();
|
||||||
|
} else if (exception instanceof StreamInfo.StreamExctractException) {
|
||||||
|
if (!streamInfo.errors.isEmpty()) {
|
||||||
|
// !!! if this case ever kicks in someone gets kicked out !!!
|
||||||
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.could_not_get_stream));
|
||||||
|
} else {
|
||||||
|
ErrorActivity.reportError(getHandler(), getContext(), streamInfo.errors, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.could_not_get_stream));
|
||||||
|
}
|
||||||
|
finishIfActivity();
|
||||||
|
} else if (exception instanceof ParsingException) {
|
||||||
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.parsing_error));
|
||||||
|
finishIfActivity();
|
||||||
|
} else {
|
||||||
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.general_error));
|
||||||
|
finishIfActivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,15 +1,16 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context="org.schabi.newpipe.MainActivity"
|
android:orientation="vertical"
|
||||||
android:orientation="vertical">
|
tools:context="org.schabi.newpipe.MainActivity">
|
||||||
|
|
||||||
<fragment
|
<FrameLayout
|
||||||
android:id="@+id/search_fragment"
|
android:id="@+id/fragment_holder"
|
||||||
android:name="org.schabi.newpipe.search_fragment.SearchInfoItemFragment"
|
android:name="org.schabi.newpipe.search_fragment.SearchInfoItemFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
|
@ -3,24 +3,22 @@
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:title="Channel">
|
android:title="@string/channel">
|
||||||
|
|
||||||
<android.support.v7.widget.RecyclerView
|
<android.support.v7.widget.RecyclerView
|
||||||
android:id="@+id/channel_streams_view"
|
android:id="@+id/channel_streams_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?android:windowBackground"
|
android:background="?android:windowBackground"
|
||||||
android:scrollbars="vertical"/>
|
android:scrollbars="vertical"
|
||||||
|
tools:listitem="@layout/stream_item"/>
|
||||||
|
|
||||||
<RelativeLayout
|
<ProgressBar
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/loading_progress_bar"
|
||||||
android:layout_height="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:id="@+id/channel_loading">
|
android:layout_height="wrap_content"
|
||||||
<ProgressBar android:id="@+id/progressBar"
|
android:layout_centerInParent="true"
|
||||||
android:layout_width="wrap_content"
|
android:indeterminate="true"/>
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerInParent="true"
|
|
||||||
android:indeterminate="true"/>
|
|
||||||
</RelativeLayout>
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
|
@ -5,7 +5,7 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:name="org.schabi.newpipe.SearchInfoItemFragment"
|
android:name="org.schabi.newpipe.SearchInfoItemFragment"
|
||||||
tools:context=".search_fragment.SearchInfoItemFragment">
|
tools:context=".fragments.search.SearchFragment">
|
||||||
|
|
||||||
<include layout="@layout/main_bg" />
|
<include layout="@layout/main_bg" />
|
||||||
|
|
||||||
|
@ -17,10 +17,11 @@
|
||||||
tools:listitem="@layout/stream_item"
|
tools:listitem="@layout/stream_item"
|
||||||
android:scrollbars="vertical"/>
|
android:scrollbars="vertical"/>
|
||||||
|
|
||||||
<ProgressBar android:id="@+id/progressBar"
|
<ProgressBar android:id="@+id/loading_progress_bar"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:indeterminate="true"
|
android:indeterminate="true"
|
||||||
android:visibility="gone"/>
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"/>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
|
@ -17,6 +17,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<!-- THUMBNAIL -->
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/detail_thumbnail_root_layout"
|
android:id="@+id/detail_thumbnail_root_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -57,14 +58,13 @@
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<!-- TITLE -->
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/detail_content_root_layout"
|
android:id="@+id/detail_title_background"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/detail_thumbnail_root_layout"
|
android:layout_below="@+id/detail_thumbnail_root_layout"
|
||||||
android:background="?android:windowBackground"
|
android:background="?android:windowBackground">
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/detail_title_root_layout"
|
android:id="@+id/detail_title_root_layout"
|
||||||
|
@ -106,13 +106,25 @@
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<!-- CONTENT -->
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/detail_content_root_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_below="@+id/detail_title_background"
|
||||||
|
android:background="?android:windowBackground"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/detail_view_count_view"
|
android:id="@+id/detail_view_count_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_below="@id/detail_title_root_layout"
|
|
||||||
android:layout_marginLeft="12dp"
|
android:layout_marginLeft="12dp"
|
||||||
android:layout_marginRight="12dp"
|
android:layout_marginRight="12dp"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
|
@ -309,14 +321,15 @@
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<!-- LOADING BAR -->
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/detail_loading_progress_bar"
|
android:id="@+id/detail_loading_progress_bar"
|
||||||
style="@style/Widget.AppCompat.ProgressBar"
|
style="@style/Widget.AppCompat.ProgressBar"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@+id/detail_thumbnail_root_layout"
|
android:layout_below="@+id/detail_title_background"
|
||||||
android:layout_centerHorizontal="true"
|
android:layout_centerHorizontal="true"
|
||||||
android:layout_marginTop="30dp"
|
android:layout_marginTop="20dp"
|
||||||
android:indeterminate="true"/>
|
android:indeterminate="true"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
|
@ -4,7 +4,7 @@
|
||||||
android:orientation="vertical" android:layout_width="match_parent"
|
android:orientation="vertical" android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:id="@+id/mainBG"
|
android:id="@+id/mainBG"
|
||||||
tools:context=".detail.VideoItemDetailActivity">
|
tools:context=".fragments.detail.VideoDetailFragment">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item android:id="@+id/action_show_downloads"
|
<item android:id="@+id/action_show_downloads"
|
||||||
app:showAsAction="never"
|
android:orderInCategory="980"
|
||||||
android:title="@string/downloads" />
|
android:title="@string/downloads"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
|
||||||
<item android:id="@+id/action_settings"
|
<item android:id="@+id/action_settings"
|
||||||
app:showAsAction="never"
|
android:orderInCategory="990"
|
||||||
android:title="@string/settings"/>
|
android:title="@string/settings"
|
||||||
|
app:showAsAction="never"/>
|
||||||
|
|
||||||
</menu>
|
</menu>
|
|
@ -1,7 +1,7 @@
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context="org.schabi.newpipe.ChannelActivity">
|
tools:context="org.schabi.newpipe.fragments.channel.ChannelFragment">
|
||||||
|
|
||||||
<item android:id="@+id/menu_item_openInBrowser"
|
<item android:id="@+id/menu_item_openInBrowser"
|
||||||
app:showAsAction="never"
|
app:showAsAction="never"
|
||||||
|
@ -11,10 +11,4 @@
|
||||||
android:title="@string/share"
|
android:title="@string/share"
|
||||||
app:showAsAction="ifRoom"
|
app:showAsAction="ifRoom"
|
||||||
android:icon="?attr/share"/>
|
android:icon="?attr/share"/>
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_settings"
|
|
||||||
android:orderInCategory="100"
|
|
||||||
android:title="@string/action_settings"
|
|
||||||
app:showAsAction="never" />
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
app:actionViewClass="android.support.v7.widget.SearchView" />
|
app:actionViewClass="android.support.v7.widget.SearchView" />
|
||||||
|
|
||||||
<group android:id="@+id/search_filter_group"
|
<group android:id="@+id/search_filter_group"
|
||||||
android:checkableBehavior="single">
|
android:checkableBehavior="single"
|
||||||
|
android:orderInCategory="999">
|
||||||
<item android:id="@+id/menu_filter_all"
|
<item android:id="@+id/menu_filter_all"
|
||||||
android:title = "@string/all"
|
android:title = "@string/all"
|
||||||
android:checked = "true"/>
|
android:checked = "true"/>
|
||||||
|
|
|
@ -29,14 +29,4 @@
|
||||||
<item android:id="@+id/menu_item_openInBrowser"
|
<item android:id="@+id/menu_item_openInBrowser"
|
||||||
app:showAsAction="never"
|
app:showAsAction="never"
|
||||||
android:title="@string/open_in_browser" />
|
android:title="@string/open_in_browser" />
|
||||||
|
|
||||||
<item android:id="@+id/menu_item_downloads"
|
|
||||||
app:showAsAction="never"
|
|
||||||
android:title="@string/downloads" />
|
|
||||||
|
|
||||||
<item android:id="@+id/action_settings"
|
|
||||||
app:showAsAction="never"
|
|
||||||
android:title="@string/settings"/>
|
|
||||||
|
|
||||||
|
|
||||||
</menu>
|
</menu>
|
|
@ -158,6 +158,7 @@
|
||||||
<string name="use_old_player_summary">Old build in Mediaframework player.</string>
|
<string name="use_old_player_summary">Old build in Mediaframework player.</string>
|
||||||
<string name="videos">videos</string>
|
<string name="videos">videos</string>
|
||||||
<string name="subscriber">subscriber</string>
|
<string name="subscriber">subscriber</string>
|
||||||
|
<string name="subscriber_plural">subscribers</string>
|
||||||
<string name="subscribe">Subscribe</string>
|
<string name="subscribe">Subscribe</string>
|
||||||
<string name="views">views</string>
|
<string name="views">views</string>
|
||||||
<string name="short_thousand">K</string>
|
<string name="short_thousand">K</string>
|
||||||
|
|
Loading…
Reference in New Issue