diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 4a3cc8254..ae6ea696c 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -32,7 +32,7 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras
* Please test (compile and run) your code before you submit changes! Ideally, provide test feedback in the PR description. Untested code will **not** be merged!
* Try to figure out yourself why builds on our CI fail.
* Make sure your PR is up-to-date with the rest of the code. Often, a simple click on "Update branch" will do the job, but if not, you are asked to merge the master branch manually and resolve the problems on your own. That will make the maintainers' jobs way easier.
-* Please show intention to maintain your features and code after you contributed it. Unmaintained code is a hassle for the core developers, and just adds work. If you do not intend to maintain features you contributed, please think again about sumission, or clearly state that in the description of your PR.
+* Please show intention to maintain your features and code after you contributed it. Unmaintained code is a hassle for the core developers, and just adds work. If you do not intend to maintain features you contributed, please think again about submission, or clearly state that in the description of your PR.
* Respond yourselves if someone requests changes or otherwise raises issues about your PRs.
* Check if your contributions align with the [fdroid inclusion guidelines](https://f-droid.org/en/docs/Inclusion_Policy/).
* Check if your submission can be build with the current fdroid build server setup.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 0f588e9dd..d0e58680a 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1 +1 @@
-- [ ] I carefully reed the [contribution guidelines](https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md) and agree to them.
+- [ ] I carefully read the [contribution guidelines](https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md) and agree to them.
diff --git a/.travis.yml b/.travis.yml
index e0fcfb82b..fcdfeb9b2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,11 +5,13 @@ android:
components:
# The BuildTools version used by NewPipe
- tools
- - build-tools-26.0.1
+ - build-tools-27.0.1
# The SDK version used to compile NewPipe
- - android-26
+ - android-27
+before_install:
+ - yes | sdkmanager "platforms;android-27"
script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest
licenses:
diff --git a/README.md b/README.md
index 374c56d00..030963a89 100644
--- a/README.md
+++ b/README.md
@@ -13,21 +13,22 @@
WARNING: PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR TERMS OF CONDITIONS.
## Screenshots
-[](screenshots/shot_1.png)
-[](screenshots/shot_2.png)
-[](screenshots/shot_3.png)
-[](screenshots/shot_4.png)
-[](screenshots/shot_5.png)
-[](screenshots/shot_6.png)
-[](screenshots/shot_7.png)
-[](screenshots/shot_8.png)
-[](screenshots/shot_9.png)
-[](screenshots/shot_10.png)
+[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_1.png)
+[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_2.png)
+[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_3.png)
+[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_4.png)
+[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_5.png)
+[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_6.png)
+[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_7.png)
+[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_8.png)
+[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.png)
+[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png)
## Description
diff --git a/app/build.gradle b/app/build.gradle
index f7af72968..46e63a082 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,15 +1,15 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 26
- buildToolsVersion '26.0.1'
+ compileSdkVersion 27
+ buildToolsVersion '27.0.1'
defaultConfig {
applicationId "org.schabi.newpipe"
minSdkVersion 15
- targetSdkVersion 26
- versionCode 41
- versionName "0.11.0"
+ targetSdkVersion 27
+ versionCode 43
+ versionName "0.11.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@@ -27,6 +27,10 @@ android {
applicationIdSuffix ".debug"
}
beta {
+ minifyEnabled true
+ shrinkResources true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
applicationIdSuffix ".beta"
}
}
@@ -43,43 +47,46 @@ android {
}
}
+ext {
+ supportLibVersion = '27.0.2'
+}
dependencies {
- androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2') {
+ androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2') {
exclude module: 'support-annotations'
}
- compile 'com.github.TeamNewPipe:NewPipeExtractor:b9d0941'
+ implementation 'com.github.TeamNewPipe:NewPipeExtractor:2d191c4ca'
- testCompile 'junit:junit:4.12'
- testCompile 'org.mockito:mockito-core:1.10.19'
+ testImplementation 'junit:junit:4.12'
+ testImplementation 'org.mockito:mockito-core:1.10.19'
- compile 'com.android.support:appcompat-v7:26.0.1'
- compile 'com.android.support:support-v4:26.0.1'
- compile 'com.android.support:design:26.0.1'
- compile 'com.android.support:recyclerview-v7:26.0.1'
- compile 'com.android.support:preference-v14:26.0.1'
+ implementation "com.android.support:appcompat-v7:$supportLibVersion"
+ implementation "com.android.support:support-v4:$supportLibVersion"
+ implementation "com.android.support:design:$supportLibVersion"
+ implementation "com.android.support:recyclerview-v7:$supportLibVersion"
+ implementation "com.android.support:preference-v14:$supportLibVersion"
- compile 'com.google.code.gson:gson:2.7'
- compile 'ch.acra:acra:4.9.0'
+ implementation 'com.google.code.gson:gson:2.8.2'
+ implementation 'ch.acra:acra:4.9.2'
- compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
- compile 'de.hdodenhof:circleimageview:2.1.0'
- compile 'com.github.nirhart:parallaxscroll:1.0'
- compile 'com.nononsenseapps:filepicker:3.0.1'
- compile 'com.google.android.exoplayer:exoplayer:r2.5.4'
+ implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
+ implementation 'de.hdodenhof:circleimageview:2.2.0'
+ implementation 'com.github.nirhart:ParallaxScroll:dd53d1f9d1'
+ implementation 'com.nononsenseapps:filepicker:3.0.1'
+ implementation 'com.google.android.exoplayer:exoplayer:r2.5.4'
- debugCompile 'com.facebook.stetho:stetho:1.5.0'
- debugCompile 'com.facebook.stetho:stetho-urlconnection:1.5.0'
- debugCompile 'com.android.support:multidex:1.0.1'
+ debugImplementation 'com.facebook.stetho:stetho:1.5.0'
+ debugImplementation 'com.facebook.stetho:stetho-urlconnection:1.5.0'
+ debugImplementation 'com.android.support:multidex:1.0.2'
- compile 'io.reactivex.rxjava2:rxjava:2.1.2'
- compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
- compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
+ implementation 'io.reactivex.rxjava2:rxjava:2.1.7'
+ implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
+ implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
- compile 'android.arch.persistence.room:runtime:1.0.0-alpha8'
- compile 'android.arch.persistence.room:rxjava2:1.0.0-alpha8'
- annotationProcessor 'android.arch.persistence.room:compiler:1.0.0-alpha8'
+ implementation 'android.arch.persistence.room:runtime:1.0.0'
+ implementation 'android.arch.persistence.room:rxjava2:1.0.0'
+ annotationProcessor 'android.arch.persistence.room:compiler:1.0.0'
- compile 'frankiesardo:icepick:3.2.0'
- provided 'frankiesardo:icepick-processor:3.2.0'
+ implementation 'frankiesardo:icepick:3.2.0'
+ annotationProcessor 'frankiesardo:icepick-processor:3.2.0'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4f97a7201..dab6fb2ec 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -185,7 +185,7 @@
android:name=".RouterPopupActivity"
android:label="@string/popup_mode_share_menu_title"
android:taskAffinity=""
- android:theme="@android:style/Theme.NoDisplay">
+ android:theme="@style/PopupPermissionsTheme">
diff --git a/app/src/main/java/org/schabi/newpipe/RouterPopupActivity.java b/app/src/main/java/org/schabi/newpipe/RouterPopupActivity.java
index 1cff0ca76..2e7089300 100644
--- a/app/src/main/java/org/schabi/newpipe/RouterPopupActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/RouterPopupActivity.java
@@ -21,6 +21,7 @@ public class RouterPopupActivity extends RouterActivity {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !PermissionHelper.checkSystemAlertWindowPermission(this)) {
Toast.makeText(this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
+ finish();
return;
}
StreamingService service;
diff --git a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java
index a2fe35894..a64ed7ff4 100644
--- a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java
@@ -135,8 +135,12 @@ public class AboutActivity extends AppCompatActivity {
View githubLink = rootView.findViewById(R.id.github_link);
githubLink.setOnClickListener(new OnGithubLinkClickListener());
- View licenseLink = rootView.findViewById(R.id.app_read_license);
- licenseLink.setOnClickListener(new OnReadFullLicenseClickListener());
+ View donationLink = rootView.findViewById(R.id.donation_link);
+ donationLink.setOnClickListener(new OnDonationLinkClickListener());
+
+ View websiteLink = rootView.findViewById(R.id.website_link);
+ websiteLink.setOnClickListener(new OnWebsiteLinkClickListener());
+
return rootView;
}
@@ -149,10 +153,21 @@ public class AboutActivity extends AppCompatActivity {
}
}
- private static class OnReadFullLicenseClickListener implements View.OnClickListener {
+ private static class OnDonationLinkClickListener implements View.OnClickListener {
@Override
- public void onClick(View v) {
- LicenseFragment.showLicense(v.getContext(), StandardLicenses.GPL3);
+ public void onClick(final View view) {
+ final Context context = view.getContext();
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.donation_url)));
+ context.startActivity(intent);
+ }
+ }
+
+ private static class OnWebsiteLinkClickListener implements View.OnClickListener {
+ @Override
+ public void onClick(final View view) {
+ final Context context = view.getContext();
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.website_url)));
+ context.startActivity(intent);
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java
index 4400cac53..272e27240 100644
--- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java
@@ -62,6 +62,9 @@ public class LicenseFragment extends Fragment {
View rootView = inflater.inflate(R.layout.fragment_licenses, container, false);
ViewGroup softwareComponentsView = rootView.findViewById(R.id.software_components);
+ View licenseLink = rootView.findViewById(R.id.app_read_license);
+ licenseLink.setOnClickListener(new OnReadFullLicenseClickListener());
+
for (final SoftwareComponent component : softwareComponents) {
View componentView = inflater.inflate(R.layout.item_software_component, container, false);
TextView softwareName = componentView.findViewById(R.id.name);
@@ -119,4 +122,11 @@ public class LicenseFragment extends Fragment {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(componentLink));
startActivity(browserIntent);
}
+
+ private static class OnReadFullLicenseClickListener implements View.OnClickListener {
+ @Override
+ public void onClick(View v) {
+ LicenseFragment.showLicense(v.getContext(), StandardLicenses.GPL3);
+ }
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipe/database/history/model/WatchHistoryEntry.java b/app/src/main/java/org/schabi/newpipe/database/history/model/WatchHistoryEntry.java
index 203b3fb7a..bfd84d377 100644
--- a/app/src/main/java/org/schabi/newpipe/database/history/model/WatchHistoryEntry.java
+++ b/app/src/main/java/org/schabi/newpipe/database/history/model/WatchHistoryEntry.java
@@ -48,7 +48,7 @@ public class WatchHistoryEntry extends HistoryEntry {
}
public WatchHistoryEntry(StreamInfo streamInfo) {
- this(new Date(), streamInfo.service_id, streamInfo.name, streamInfo.url,
+ this(new Date(), streamInfo.getServiceId(), streamInfo.getName(), streamInfo.getUrl(),
streamInfo.id, streamInfo.thumbnail_url, streamInfo.uploader_name, streamInfo.duration);
}
diff --git a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java
index 12d1764cc..e71088ac9 100644
--- a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java
+++ b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java
@@ -116,10 +116,7 @@ public class SubscriptionEntity {
@Ignore
public ChannelInfoItem toChannelInfoItem() {
- ChannelInfoItem item = new ChannelInfoItem();
- item.url = getUrl();
- item.service_id = getServiceId();
- item.name = getName();
+ ChannelInfoItem item = new ChannelInfoItem(getServiceId(), getUrl(), getName());
item.thumbnail_url = getAvatarUrl();
item.subscriber_count = getSubscriberCount();
item.description = getDescription();
diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java
index b93f66d26..b217b91b3 100644
--- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java
+++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java
@@ -108,8 +108,8 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
nameEditText = view.findViewById(R.id.file_name);
- nameEditText.setText(FilenameUtils.createFilename(getContext(), currentInfo.name));
- selectedAudioIndex = ListHelper.getDefaultAudioFormat(getContext(), currentInfo.audio_streams);
+ nameEditText.setText(FilenameUtils.createFilename(getContext(), currentInfo.getName()));
+ selectedAudioIndex = ListHelper.getDefaultAudioFormat(getContext(), currentInfo.getAudioStreams());
streamsSpinner = view.findViewById(R.id.quality_spinner);
streamsSpinner.setOnItemSelectedListener(this);
@@ -183,7 +183,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
String[] items = new String[audioStreams.size()];
for (int i = 0; i < audioStreams.size(); i++) {
AudioStream audioStream = audioStreams.get(i);
- items[i] = MediaFormat.getNameById(audioStream.format) + " " + audioStream.average_bitrate + "kbps";
+ items[i] = audioStream.getFormat().getName() + " " + audioStream.getAverageBitrate() + "kbps";
}
ArrayAdapter itemAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, items);
@@ -242,7 +242,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
RadioButton audioButton = view.findViewById(R.id.audio_button);
RadioButton videoButton = view.findViewById(R.id.video_button);
- if (currentInfo.audio_streams == null || currentInfo.audio_streams.size() == 0) {
+ if (currentInfo.getAudioStreams() == null || currentInfo.getAudioStreams().size() == 0) {
audioButton.setVisibility(View.GONE);
videoButton.setChecked(true);
} else if (sortedStreamVideosList == null || sortedStreamVideosList.size() == 0) {
@@ -256,14 +256,18 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
String url, location;
String fileName = nameEditText.getText().toString().trim();
- if (fileName.isEmpty()) fileName = FilenameUtils.createFilename(getContext(), currentInfo.name);
+ if (fileName.isEmpty()) fileName = FilenameUtils.createFilename(getContext(), currentInfo.getName());
boolean isAudio = radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.audio_button;
- url = isAudio ? currentInfo.audio_streams.get(selectedAudioIndex).url : sortedStreamVideosList.get(selectedVideoIndex).url;
- location = isAudio ? NewPipeSettings.getAudioDownloadPath(getContext()) : NewPipeSettings.getVideoDownloadPath(getContext());
-
- if (isAudio) fileName += "." + MediaFormat.getSuffixById(currentInfo.audio_streams.get(selectedAudioIndex).format);
- else fileName += "." + MediaFormat.getSuffixById(sortedStreamVideosList.get(selectedVideoIndex).format);
+ if (isAudio) {
+ url = currentInfo.getAudioStreams().get(selectedAudioIndex).getUrl();
+ location = NewPipeSettings.getAudioDownloadPath(getContext());
+ fileName += "." + currentInfo.getAudioStreams().get(selectedAudioIndex).getFormat().getSuffix();
+ } else {
+ url = sortedStreamVideosList.get(selectedVideoIndex).getUrl();
+ location = NewPipeSettings.getVideoDownloadPath(getContext());
+ fileName += "." + sortedStreamVideosList.get(selectedVideoIndex).getFormat().getSuffix();
+ }
DownloadManagerService.startMission(getContext(), url, location, fileName, isAudio, threadsSeekBar.getProgress() + 1);
getDialog().dismiss();
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java
index 80f05585b..a6c8f5fcc 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java
@@ -50,6 +50,7 @@ public abstract class BaseStateFragment extends BaseFragment implements ViewC
protected Button errorButtonRetry;
protected TextView errorTextView;
+ @State
protected boolean useAsFrontPage = false;
@Override
@@ -218,6 +219,7 @@ public abstract class BaseStateFragment extends BaseFragment implements ViewC
if (serviceName == null) serviceName = "none";
if (request == null) request = "none";
+
ErrorActivity.reportError(getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(userAction, serviceName, request, errorId));
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java
index 7992f88f0..e220654a4 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java
@@ -2,6 +2,7 @@ package org.schabi.newpipe.fragments;
import android.content.SharedPreferences;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
@@ -30,30 +31,28 @@ import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
import org.schabi.newpipe.fragments.subscription.SubscriptionFragment;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
-import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ThemeHelper;
-import java.util.concurrent.ExecutionException;
-
public class MainFragment extends BaseFragment implements TabLayout.OnTabSelectedListener {
private ViewPager viewPager;
private boolean showBlankTab = false;
- private static final int FALLBACK_SERVICE_ID = 0; // Youtbe
+ public int currentServiceId = -1;
+
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Constants
+ //////////////////////////////////////////////////////////////////////////*/
+
+
+ private static final int FALLBACK_SERVICE_ID = 0; // Youtube
private static final String FALLBACK_CHANNEL_URL =
"https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ";
private static final String FALLBACK_CHANNEL_NAME = "Music";
private static final String FALLBACK_KIOSK_ID = "Trending";
-
- public int currentServiceId = -1;
-
- /*//////////////////////////////////////////////////////////////////////////
- // Konst
- //////////////////////////////////////////////////////////////////////////*/
-
- private static final int KIOSK_MENU_OFFSETT = 2000;
+ private static final int KIOSK_MENU_OFFSET = 2000;
/*//////////////////////////////////////////////////////////////////////////
// Fragment's LifeCycle
@@ -66,7 +65,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
}
@Override
- public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
currentServiceId = Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(getActivity())
.getString(getString(R.string.current_service_key), "0"));
return inflater.inflate(R.layout.fragment_main, container, false);
@@ -86,27 +85,27 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
tabLayout.setupWithViewPager(viewPager);
- if(ThemeHelper.isLightThemeSelected(getActivity())) {
+ int channelIcon;
+ int whatsHotIcon;
+
+ if (ThemeHelper.isLightThemeSelected(getActivity())) {
tabLayout.setBackgroundColor(getResources().getColor(R.color.light_youtube_primary_color));
+ channelIcon = R.drawable.ic_channel_black_24dp;
+ whatsHotIcon = R.drawable.ic_whatshot_black_24dp;
+ } else {
+ channelIcon = R.drawable.ic_channel_white_24dp;
+ whatsHotIcon = R.drawable.ic_whatshot_white_24dp;
}
- if(PreferenceManager.getDefaultSharedPreferences(getActivity())
+
+ if (PreferenceManager.getDefaultSharedPreferences(getActivity())
.getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key))
.equals(getString(R.string.subscription_page_key))) {
- if(ThemeHelper.isLightThemeSelected(getActivity())) {
- tabLayout.getTabAt(0).setIcon(R.drawable.ic_channel_black_24dp);
- } else{
- tabLayout.getTabAt(0).setIcon(R.drawable.ic_channel_white_24dp);
- }
- } else {
- if(ThemeHelper.isLightThemeSelected(getActivity())) {
- tabLayout.getTabAt(0).setIcon(R.drawable.ic_whatshot_black_24dp);
- tabLayout.getTabAt(1).setIcon(R.drawable.ic_channel_black_24dp);
- } else {
- tabLayout.getTabAt(0).setIcon(R.drawable.ic_whatshot_white_24dp);
- tabLayout.getTabAt(1).setIcon(R.drawable.ic_channel_white_24dp);
- }
- }
+ tabLayout.getTabAt(0).setIcon(channelIcon);
+ } else {
+ tabLayout.getTabAt(0).setIcon(whatsHotIcon);
+ tabLayout.getTabAt(1).setIcon(channelIcon);
+ }
}
/*//////////////////////////////////////////////////////////////////////////
@@ -182,7 +181,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
.equals(getString(R.string.subscription_page_key))) {
return new SubscriptionFragment();
} else {
- return getMainPageFramgent();
+ return getMainPageFragment();
}
case 1:
return new SubscriptionFragment();
@@ -213,7 +212,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
// Main page content
//////////////////////////////////////////////////////////////////////////*/
- private Fragment getMainPageFramgent() {
+ private Fragment getMainPageFragment() {
try {
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(getActivity());
@@ -268,7 +267,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
KioskList kl = service.getKioskList();
int i = 0;
for(final String ks : kl.getAvailableKiosks()) {
- menu.add(0, KIOSK_MENU_OFFSETT + i, Menu.NONE,
+ menu.add(0, KIOSK_MENU_OFFSET + i, Menu.NONE,
KioskTranslator.getTranslatedKioskName(ks, getContext()))
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/SpinnerToolbarAdapter.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/SpinnerToolbarAdapter.java
index 94fe2cf5b..33f87be70 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/SpinnerToolbarAdapter.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/SpinnerToolbarAdapter.java
@@ -60,7 +60,7 @@ public class SpinnerToolbarAdapter extends BaseAdapter {
ImageView woSoundIcon = convertView.findViewById(R.id.wo_sound_icon);
TextView text = convertView.findViewById(android.R.id.text1);
VideoStream item = (VideoStream) getItem(position);
- text.setText(MediaFormat.getNameById(item.format) + " " + item.resolution);
+ text.setText(item.getFormat().getName() + " " + item.getResolution());
int visibility = !showIconNoAudio ? View.GONE
: item.isVideoOnly ? View.VISIBLE
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
index f50f805c2..b8957f33c 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java
@@ -4,7 +4,8 @@ import java.io.Serializable;
class StackItem implements Serializable {
private int serviceId;
- private String title, url;
+ private String title;
+ private String url;
StackItem(int serviceId, String url, String title) {
this.serviceId = serviceId;
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
index 63b7e9ace..5b445e813 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
@@ -13,6 +13,7 @@ import android.support.annotation.DrawableRes;
import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
+import android.support.v4.text.TextUtilsCompat;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
@@ -36,6 +37,7 @@ import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.PopupMenu;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
@@ -62,9 +64,10 @@ import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.history.HistoryListener;
import org.schabi.newpipe.info_list.InfoItemBuilder;
-import org.schabi.newpipe.player.BackgroundPlayer;
+import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.player.MainVideoPlayer;
import org.schabi.newpipe.player.PopupVideoPlayer;
+import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.old.PlayVideoActivity;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.SinglePlayQueue;
@@ -281,7 +284,7 @@ public class VideoDetailFragment extends BaseStateFragment implement
// Check if the next video label and video is visible,
// if it is, include the two elements in the next check
- int nextCount = currentInfo != null && currentInfo.next_video != null ? 2 : 0;
+ int nextCount = currentInfo != null && currentInfo.getNextVideo() != null ? 2 : 0;
if (relatedStreamsView != null && relatedStreamsView.getChildCount() > INITIAL_RELATED_VIDEOS + nextCount) {
outState.putSerializable(WAS_RELATED_EXPANDED_KEY, true);
}
@@ -328,10 +331,14 @@ public class VideoDetailFragment extends BaseStateFragment implement
openPopupPlayer(false);
break;
case R.id.detail_uploader_root_layout:
- if (currentInfo.uploader_url == null || currentInfo.uploader_url.isEmpty()) {
+ if (TextUtils.isEmpty(currentInfo.getUploaderUrl())) {
Log.w(TAG, "Can't open channel because we got no channel URL");
} else {
- NavigationHelper.openChannelFragment(getFragmentManager(), currentInfo.service_id, currentInfo.uploader_url, currentInfo.uploader_name);
+ NavigationHelper.openChannelFragment(
+ getFragmentManager(),
+ currentInfo.getServiceId(),
+ currentInfo.getUploaderUrl(),
+ currentInfo.getUploaderName());
}
break;
case R.id.detail_thumbnail_root_layout:
@@ -378,7 +385,7 @@ public class VideoDetailFragment extends BaseStateFragment implement
if (DEBUG) Log.d(TAG, "toggleExpandRelatedVideos() called with: info = [" + info + "]");
if (!showRelatedStreams) return;
- int nextCount = info.next_video != null ? 2 : 0;
+ int nextCount = info.getNextVideo() != null ? 2 : 0;
int initialCount = INITIAL_RELATED_VIDEOS + nextCount;
if (relatedStreamsView.getChildCount() > initialCount) {
@@ -388,8 +395,8 @@ public class VideoDetailFragment extends BaseStateFragment implement
}
//Log.d(TAG, "toggleExpandRelatedVideos() called with: info = [" + info + "], from = [" + INITIAL_RELATED_VIDEOS + "]");
- for (int i = INITIAL_RELATED_VIDEOS; i < info.related_streams.size(); i++) {
- InfoItem item = info.related_streams.get(i);
+ for (int i = INITIAL_RELATED_VIDEOS; i < info.getRelatedStreams().size(); i++) {
+ InfoItem item = info.getRelatedStreams().get(i);
//Log.d(TAG, "i = " + i);
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, item));
}
@@ -457,7 +464,12 @@ public class VideoDetailFragment extends BaseStateFragment implement
infoItemBuilder.setOnStreamSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
@Override
public void selected(StreamInfoItem selectedItem) {
- selectAndLoadVideo(selectedItem.service_id, selectedItem.url, selectedItem.name);
+ selectAndLoadVideo(selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
+ }
+
+ @Override
+ public void held(StreamInfoItem selectedItem) {
+ showStreamDialog(selectedItem);
}
});
@@ -476,6 +488,34 @@ public class VideoDetailFragment extends BaseStateFragment implement
detailControlsPopup.setOnTouchListener(getOnControlsTouchListener());
}
+ private void showStreamDialog(final StreamInfoItem item) {
+ final Context context = getContext();
+ if (context == null || context.getResources() == null || getActivity() == null) return;
+
+ final String[] commands = new String[]{
+ context.getResources().getString(R.string.enqueue_on_background),
+ context.getResources().getString(R.string.enqueue_on_popup)
+ };
+
+ final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ switch (i) {
+ case 0:
+ NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
+ break;
+ case 1:
+ NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item));
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ new InfoItemDialog(getActivity(), item, commands, actions).show();
+ }
+
private View.OnTouchListener getOnControlsTouchListener() {
return new View.OnTouchListener() {
@Override
@@ -497,35 +537,35 @@ public class VideoDetailFragment extends BaseStateFragment implement
private void initThumbnailViews(StreamInfo info) {
thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
- if (info.thumbnail_url != null && !info.thumbnail_url.isEmpty()) {
- imageLoader.displayImage(info.thumbnail_url, thumbnailImageView, DISPLAY_THUMBNAIL_OPTIONS, new SimpleImageLoadingListener() {
+ if (!TextUtils.isEmpty(info.getThumbnailUrl())) {
+ imageLoader.displayImage(info.getThumbnailUrl(), thumbnailImageView, DISPLAY_THUMBNAIL_OPTIONS, new SimpleImageLoadingListener() {
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
- ErrorActivity.reportError(activity, failReason.getCause(), null, activity.findViewById(android.R.id.content), ErrorActivity.ErrorInfo.make(UserAction.LOAD_IMAGE, NewPipe.getNameOfService(currentInfo.service_id), imageUri, R.string.could_not_load_thumbnails));
+ ErrorActivity.reportError(activity, failReason.getCause(), null, activity.findViewById(android.R.id.content), ErrorActivity.ErrorInfo.make(UserAction.LOAD_IMAGE, NewPipe.getNameOfService(currentInfo.getServiceId()), imageUri, R.string.could_not_load_thumbnails));
}
});
}
-
- if (info.uploader_avatar_url != null && !info.uploader_avatar_url.isEmpty()) {
- imageLoader.displayImage(info.uploader_avatar_url, uploaderThumb, DISPLAY_AVATAR_OPTIONS);
+
+ if (!TextUtils.isEmpty(info.getUploaderAvatarUrl())) {
+ imageLoader.displayImage(info.getUploaderAvatarUrl(), uploaderThumb, DISPLAY_AVATAR_OPTIONS);
}
}
private void initRelatedVideos(StreamInfo info) {
if (relatedStreamsView.getChildCount() > 0) relatedStreamsView.removeAllViews();
- if (info.next_video != null && showRelatedStreams) {
+ if (info.getNextVideo() != null && showRelatedStreams) {
nextStreamTitle.setVisibility(View.VISIBLE);
- relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, info.next_video));
+ relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, info.getNextVideo()));
relatedStreamsView.addView(getSeparatorView());
relatedStreamRootLayout.setVisibility(View.VISIBLE);
} else nextStreamTitle.setVisibility(View.GONE);
if (info.related_streams != null && !info.related_streams.isEmpty() && showRelatedStreams) {
//long first = System.nanoTime(), each;
- int to = info.related_streams.size() >= INITIAL_RELATED_VIDEOS ? INITIAL_RELATED_VIDEOS : info.related_streams.size();
+ int to = info.getRelatedStreams().size() >= INITIAL_RELATED_VIDEOS ? INITIAL_RELATED_VIDEOS : info.getRelatedStreams().size();
for (int i = 0; i < to; i++) {
- InfoItem item = info.related_streams.get(i);
+ InfoItem item = info.getRelatedStreams().get(i);
//each = System.nanoTime();
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, item));
//if (DEBUG) Log.d(TAG, "each took " + ((System.nanoTime() - each) / 1000000L) + "ms");
@@ -537,7 +577,7 @@ public class VideoDetailFragment extends BaseStateFragment implement
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, resolveResourceIdFromAttr(R.attr.expand)));
} else {
- if (info.next_video == null) relatedStreamRootLayout.setVisibility(View.GONE);
+ if (info.getNextVideo() == null) relatedStreamRootLayout.setVisibility(View.GONE);
relatedStreamExpandButton.setVisibility(View.GONE);
}
}
@@ -581,14 +621,14 @@ public class VideoDetailFragment extends BaseStateFragment implement
private void setupActionBarHandler(final StreamInfo info) {
if (DEBUG) Log.d(TAG, "setupActionBarHandler() called with: info = [" + info + "]");
- sortedStreamVideosList = new ArrayList<>(ListHelper.getSortedStreamVideosList(activity, info.video_streams, info.video_only_streams, false));
+ sortedStreamVideosList = new ArrayList<>(ListHelper.getSortedStreamVideosList(activity, info.getVideoStreams(), info.getVideoOnlyStreams(), false));
actionBarHandler.setupStreamList(sortedStreamVideosList, spinnerToolbar);
actionBarHandler.setOnShareListener(new ActionBarHandler.OnActionListener() {
@Override
public void onActionSelected(int selectedStreamId) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
- intent.putExtra(Intent.EXTRA_TEXT, info.url);
+ intent.putExtra(Intent.EXTRA_TEXT, info.getUrl());
intent.setType("text/plain");
startActivity(Intent.createChooser(intent, activity.getString(R.string.share_dialog_title)));
}
@@ -599,7 +639,7 @@ public class VideoDetailFragment extends BaseStateFragment implement
public void onActionSelected(int selectedStreamId) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
- intent.setData(Uri.parse(info.url));
+ intent.setData(Uri.parse(info.getUrl()));
startActivity(Intent.createChooser(intent, activity.getString(R.string.choose_browser)));
}
});
@@ -608,7 +648,7 @@ public class VideoDetailFragment extends BaseStateFragment implement
@Override
public void onActionSelected(int selectedStreamId) {
try {
- NavigationHelper.playWithKore(activity, Uri.parse(info.url.replace("https", "http")));
+ NavigationHelper.playWithKore(activity, Uri.parse(info.getUrl().replace("https", "http")));
if(activity instanceof HistoryListener) {
((HistoryListener) activity).onVideoPlayed(info, null);
}
@@ -707,7 +747,7 @@ public class VideoDetailFragment extends BaseStateFragment implement
public void prepareAndHandleInfo(final StreamInfo info, boolean scrollToTop) {
if (DEBUG) Log.d(TAG, "prepareAndHandleInfo() called with: info = [" + info + "], scrollToTop = [" + scrollToTop + "]");
- setInitialData(info.service_id, info.url, info.name);
+ setInitialData(info.getServiceId(), info.getUrl(), info.getName());
pushToStack(serviceId, url, name);
showLoading();
@@ -763,7 +803,7 @@ public class VideoDetailFragment extends BaseStateFragment implement
//////////////////////////////////////////////////////////////////////////*/
private void openBackgroundPlayer(final boolean append) {
- AudioStream audioStream = currentInfo.audio_streams.get(ListHelper.getDefaultAudioFormat(activity, currentInfo.audio_streams));
+ AudioStream audioStream = currentInfo.getAudioStreams().get(ListHelper.getDefaultAudioFormat(activity, currentInfo.getAudioStreams()));
if (activity instanceof HistoryListener) {
((HistoryListener) activity).onAudioPlayed(currentInfo, audioStream);
@@ -792,16 +832,16 @@ public class VideoDetailFragment extends BaseStateFragment implement
((HistoryListener) activity).onVideoPlayed(currentInfo, getSelectedVideoStream());
}
- final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
- final Intent intent;
+ final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
if (append) {
- Toast.makeText(activity, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
- intent = NavigationHelper.getPlayerEnqueueIntent(activity, PopupVideoPlayer.class, playQueue);
+ NavigationHelper.enqueueOnPopupPlayer(activity, itemQueue);
} else {
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
- intent = NavigationHelper.getPlayerIntent(activity, PopupVideoPlayer.class, playQueue, getSelectedVideoStream().resolution);
+ final Intent intent = NavigationHelper.getPlayerIntent(
+ activity, PopupVideoPlayer.class, itemQueue, getSelectedVideoStream().resolution
+ );
+ activity.startService(intent);
}
- activity.startService(intent);
}
private void openVideoPlayer() {
@@ -820,13 +860,11 @@ public class VideoDetailFragment extends BaseStateFragment implement
private void openNormalBackgroundPlayer(final boolean append) {
- final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
+ final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
if (append) {
- activity.startService(NavigationHelper.getPlayerEnqueueIntent(activity, BackgroundPlayer.class, playQueue));
- Toast.makeText(activity, R.string.background_player_append, Toast.LENGTH_SHORT).show();
+ NavigationHelper.enqueueOnBackgroundPlayer(activity, itemQueue);
} else {
- activity.startService(NavigationHelper.getPlayerIntent(activity, BackgroundPlayer.class, playQueue));
- Toast.makeText(activity, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show();
+ NavigationHelper.playOnBackgroundPlayer(activity, itemQueue);
}
}
@@ -835,9 +873,9 @@ public class VideoDetailFragment extends BaseStateFragment implement
intent = new Intent();
try {
intent.setAction(Intent.ACTION_VIEW);
- intent.setDataAndType(Uri.parse(audioStream.url), MediaFormat.getMimeById(audioStream.format));
- intent.putExtra(Intent.EXTRA_TITLE, currentInfo.name);
- intent.putExtra("title", currentInfo.name);
+ intent.setDataAndType(Uri.parse(audioStream.getUrl()), audioStream.getFormat().getMimeType());
+ intent.putExtra(Intent.EXTRA_TITLE, currentInfo.getName());
+ intent.putExtra("title", currentInfo.getName());
activity.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
@@ -866,19 +904,18 @@ public class VideoDetailFragment extends BaseStateFragment implement
private void openNormalPlayer(VideoStream selectedVideoStream) {
Intent mIntent;
- boolean useOldPlayer = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(getString(R.string.use_old_player_key), false)
- || (Build.VERSION.SDK_INT < 16);
+ boolean useOldPlayer = PlayerHelper.isUsingOldPlayer(activity) || (Build.VERSION.SDK_INT < 16);
if (!useOldPlayer) {
// ExoPlayer
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
- mIntent = NavigationHelper.getPlayerIntent(activity, MainVideoPlayer.class, playQueue, getSelectedVideoStream().resolution);
+ mIntent = NavigationHelper.getPlayerIntent(activity, MainVideoPlayer.class, playQueue, getSelectedVideoStream().getResolution());
} else {
// Internal Player
mIntent = new Intent(activity, PlayVideoActivity.class)
- .putExtra(PlayVideoActivity.VIDEO_TITLE, currentInfo.name)
- .putExtra(PlayVideoActivity.STREAM_URL, selectedVideoStream.url)
- .putExtra(PlayVideoActivity.VIDEO_URL, currentInfo.url)
- .putExtra(PlayVideoActivity.START_POSITION, currentInfo.start_position);
+ .putExtra(PlayVideoActivity.VIDEO_TITLE, currentInfo.getName())
+ .putExtra(PlayVideoActivity.STREAM_URL, selectedVideoStream.getUrl())
+ .putExtra(PlayVideoActivity.VIDEO_URL, currentInfo.getUrl())
+ .putExtra(PlayVideoActivity.START_POSITION, currentInfo.getStartPosition());
}
startActivity(mIntent);
}
@@ -888,9 +925,9 @@ public class VideoDetailFragment extends BaseStateFragment implement
Intent intent = new Intent();
try {
intent.setAction(Intent.ACTION_VIEW)
- .setDataAndType(Uri.parse(selectedVideoStream.url), MediaFormat.getMimeById(selectedVideoStream.format))
- .putExtra(Intent.EXTRA_TITLE, currentInfo.name)
- .putExtra("title", currentInfo.name);
+ .setDataAndType(Uri.parse(selectedVideoStream.getUrl()), selectedVideoStream.getFormat().getMimeType())
+ .putExtra(Intent.EXTRA_TITLE, currentInfo.getName())
+ .putExtra("title", currentInfo.getName());
this.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
@@ -1063,20 +1100,28 @@ public class VideoDetailFragment extends BaseStateFragment implement
public void handleResult(@NonNull StreamInfo info) {
super.handleResult(info);
- setInitialData(info.service_id, info.url, info.name);
+ setInitialData(info.getServiceId(), info.getUrl(), info.getName());
pushToStack(serviceId, url, name);
animateView(thumbnailPlayButton, true, 200);
videoTitleTextView.setText(name);
- if (!TextUtils.isEmpty(info.uploader_name)) uploaderTextView.setText(info.uploader_name);
- uploaderTextView.setVisibility(!TextUtils.isEmpty(info.uploader_name) ? View.VISIBLE : View.GONE);
+ if (!TextUtils.isEmpty(info.getUploaderName())) {
+ uploaderTextView.setText(info.getUploaderName());
+ uploaderTextView.setVisibility(View.VISIBLE);
+ } else {
+ uploaderTextView.setVisibility(View.GONE);
+ }
uploaderThumb.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.buddy));
- if (info.view_count >= 0) videoCountView.setText(Localization.localizeViewCount(activity, info.view_count));
- videoCountView.setVisibility(info.view_count >= 0 ? View.VISIBLE : View.GONE);
+ if (info.getViewCount() >= 0) {
+ videoCountView.setText(Localization.localizeViewCount(activity, info.getViewCount()));
+ videoCountView.setVisibility(View.VISIBLE);
+ } else {
+ videoCountView.setVisibility(View.GONE);
+ }
- if (info.dislike_count == -1 && info.like_count == -1) {
+ if (info.getDislikeCount() == -1 && info.getLikeCount() == -1) {
thumbsDownImageView.setVisibility(View.VISIBLE);
thumbsUpImageView.setVisibility(View.VISIBLE);
thumbsUpTextView.setVisibility(View.GONE);
@@ -1084,14 +1129,23 @@ public class VideoDetailFragment extends BaseStateFragment implement
thumbsDisabledTextView.setVisibility(View.VISIBLE);
} else {
- if (info.dislike_count >= 0) thumbsDownTextView.setText(Localization.shortCount(activity, info.dislike_count));
- thumbsDownTextView.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.shortCount(activity, info.like_count));
- thumbsUpTextView.setVisibility(info.like_count >= 0 ? View.VISIBLE : View.GONE);
- thumbsUpImageView.setVisibility(info.like_count >= 0 ? View.VISIBLE : View.GONE);
+ if (info.getDislikeCount() >= 0) {
+ thumbsDownTextView.setText(Localization.shortCount(activity, info.getDislikeCount()));
+ thumbsDownTextView.setVisibility(View.VISIBLE);
+ thumbsDownImageView.setVisibility(View.VISIBLE);
+ } else {
+ thumbsDownTextView.setVisibility(View.GONE);
+ thumbsDownImageView.setVisibility(View.GONE);
+ }
+ if (info.getLikeCount() >= 0) {
+ thumbsUpTextView.setText(Localization.shortCount(activity, info.getLikeCount()));
+ thumbsUpTextView.setVisibility(View.VISIBLE);
+ thumbsUpImageView.setVisibility(View.VISIBLE);
+ } else {
+ thumbsUpTextView.setVisibility(View.GONE);
+ thumbsUpImageView.setVisibility(View.GONE);
+ }
thumbsDisabledTextView.setVisibility(View.GONE);
}
@@ -1100,10 +1154,10 @@ public class VideoDetailFragment extends BaseStateFragment implement
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
videoDescriptionView.setVisibility(View.GONE);
videoDescriptionRootLayout.setVisibility(View.GONE);
- if (!TextUtils.isEmpty(info.upload_date)) {
- videoUploadDateView.setText(Localization.localizeDate(activity, info.upload_date));
+ if (!TextUtils.isEmpty(info.getUploadDate())) {
+ videoUploadDateView.setText(Localization.localizeDate(activity, info.getUploadDate()));
}
- prepareDescription(info.description);
+ prepareDescription(info.getDescription());
animateView(spinnerToolbar, true, 500);
setupActionBarHandler(info);
@@ -1113,10 +1167,10 @@ public class VideoDetailFragment extends BaseStateFragment implement
toggleExpandRelatedVideos(currentInfo);
wasRelatedStreamsExpanded = false;
}
- setTitleToUrl(info.service_id, info.url, info.name);
+ setTitleToUrl(info.getServiceId(), info.getUrl(), info.getName());
- if (!info.errors.isEmpty()) {
- showSnackBarError(info.errors, UserAction.REQUESTED_STREAM, NewPipe.getNameOfService(info.service_id), info.url, 0);
+ if (!info.getErrors().isEmpty()) {
+ showSnackBarError(info.getErrors(), UserAction.REQUESTED_STREAM, NewPipe.getNameOfService(info.getServiceId()), info.getUrl(), 0);
}
if (autoPlayEnabled) {
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java
index 35f6a08d3..8b20f0122 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java
@@ -1,6 +1,7 @@
package org.schabi.newpipe.fragments.list;
import android.content.Context;
+import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.ActionBar;
@@ -19,7 +20,9 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
import org.schabi.newpipe.info_list.InfoItemBuilder;
+import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.info_list.InfoListAdapter;
+import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.StateSaver;
@@ -137,7 +140,12 @@ public abstract class BaseListFragment extends BaseStateFragment implem
onItemSelected(selectedItem);
NavigationHelper.openVideoDetailFragment(
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
- selectedItem.service_id, selectedItem.url, selectedItem.name);
+ selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
+ }
+
+ @Override
+ public void held(StreamInfoItem selectedItem) {
+ showStreamDialog(selectedItem);
}
});
@@ -147,8 +155,11 @@ public abstract class BaseListFragment extends BaseStateFragment implem
onItemSelected(selectedItem);
NavigationHelper.openChannelFragment(
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
- selectedItem.service_id, selectedItem.url, selectedItem.name);
+ selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
}
+
+ @Override
+ public void held(ChannelInfoItem selectedItem) {}
});
infoListAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
@@ -157,8 +168,11 @@ public abstract class BaseListFragment extends BaseStateFragment implem
onItemSelected(selectedItem);
NavigationHelper.openPlaylistFragment(
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
- selectedItem.service_id, selectedItem.url, selectedItem.name);
+ selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
}
+
+ @Override
+ public void held(PlaylistInfoItem selectedItem) {}
});
itemsList.clearOnScrollListeners();
@@ -176,6 +190,33 @@ public abstract class BaseListFragment extends BaseStateFragment implem
}
}
+ protected void showStreamDialog(final StreamInfoItem item) {
+ final Context context = getContext();
+ if (context == null || context.getResources() == null || getActivity() == null) return;
+
+ final String[] commands = new String[]{
+ context.getResources().getString(R.string.enqueue_on_background),
+ context.getResources().getString(R.string.enqueue_on_popup)
+ };
+
+ final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ switch (i) {
+ case 0:
+ NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
+ break;
+ case 1:
+ NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item));
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ new InfoItemDialog(getActivity(), item, commands, actions).show();
+ }
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java
index 4baf323ff..41561af66 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java
@@ -190,8 +190,8 @@ public abstract class BaseListInfoFragment extends BaseListF
public void handleResult(@NonNull I result) {
super.handleResult(result);
- url = result.url;
- name = result.name;
+ url = result.getUrl();
+ name = result.getName();
setTitle(name);
if (infoListAdapter.getItemsList().size() == 0) {
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
index 64875b17f..7a2c65898 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
@@ -1,8 +1,10 @@
package org.schabi.newpipe.fragments.list.channel;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -10,6 +12,7 @@ import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.text.TextUtils;
import android.util.Log;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -18,7 +21,9 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.TextView;
+import android.widget.Toast;
import com.jakewharton.rxbinding2.view.RxView;
@@ -28,12 +33,19 @@ import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
+import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.fragments.subscription.SubscriptionService;
+import org.schabi.newpipe.info_list.InfoItemDialog;
+import org.schabi.newpipe.playlist.ChannelPlayQueue;
+import org.schabi.newpipe.playlist.PlayQueue;
+import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.Localization;
+import org.schabi.newpipe.util.NavigationHelper;
+import org.schabi.newpipe.util.PermissionHelper;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -68,6 +80,11 @@ public class ChannelFragment extends BaseListInfoFragment {
private TextView headerTitleView;
private TextView headerSubscribersTextView;
private Button headerSubscribeButton;
+ private View playlistCtrl;
+
+ private LinearLayout headerPlayAllButton;
+ private LinearLayout headerPopupButton;
+ private LinearLayout headerBackgroundButton;
private MenuItem menuRssButton;
@@ -88,7 +105,7 @@ public class ChannelFragment extends BaseListInfoFragment {
&& useAsFrontPage
&& isVisibleToUser) {
try {
- activity.getSupportActionBar().setTitle(currentInfo.name);
+ activity.getSupportActionBar().setTitle(currentInfo.getName());
} catch (Exception e) {
onError(e);
}
@@ -124,10 +141,57 @@ public class ChannelFragment extends BaseListInfoFragment {
headerTitleView = headerRootLayout.findViewById(R.id.channel_title_view);
headerSubscribersTextView = headerRootLayout.findViewById(R.id.channel_subscriber_view);
headerSubscribeButton = headerRootLayout.findViewById(R.id.channel_subscribe_button);
+ playlistCtrl = headerRootLayout.findViewById(R.id.playlist_control);
+
+
+ headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_all_button);
+ headerPopupButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_popup_button);
+ headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_bg_button);
return headerRootLayout;
}
+ @Override
+ protected void showStreamDialog(final StreamInfoItem item) {
+ final Context context = getContext();
+ if (context == null || context.getResources() == null || getActivity() == null) return;
+
+ final String[] commands = new String[]{
+ context.getResources().getString(R.string.enqueue_on_background),
+ context.getResources().getString(R.string.enqueue_on_popup),
+ context.getResources().getString(R.string.start_here_on_main),
+ context.getResources().getString(R.string.start_here_on_background),
+ context.getResources().getString(R.string.start_here_on_popup),
+ };
+
+ final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
+ switch (i) {
+ case 0:
+ NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
+ break;
+ case 1:
+ NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item));
+ break;
+ case 2:
+ NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
+ break;
+ case 3:
+ NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
+ break;
+ case 4:
+ NavigationHelper.playOnPopupPlayer(context, getPlayQueue(index));
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ new InfoItemDialog(getActivity(), item, commands, actions).show();
+ }
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
@@ -144,7 +208,7 @@ public class ChannelFragment extends BaseListInfoFragment {
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]");
menuRssButton = menu.findItem(R.id.menu_item_rss);
if (currentInfo != null) {
- menuRssButton.setVisible(!TextUtils.isEmpty(currentInfo.feed_url));
+ menuRssButton.setVisible(!TextUtils.isEmpty(currentInfo.getFeedUrl()));
}
}
@@ -153,7 +217,7 @@ public class ChannelFragment extends BaseListInfoFragment {
private void openRssFeed() {
final ChannelInfo info = currentInfo;
if(info != null) {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(info.feed_url));
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(info.getFeedUrl()));
startActivity(intent);
}
}
@@ -200,12 +264,12 @@ public class ChannelFragment extends BaseListInfoFragment {
@Override
public void accept(Throwable throwable) throws Exception {
animateView(headerSubscribeButton, false, 100);
- showSnackBarError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.service_id), "Get subscription status", 0);
+ showSnackBarError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.getServiceId()), "Get subscription status", 0);
}
};
final Observable> observable = subscriptionService.subscriptionTable()
- .getSubscription(info.service_id, info.url)
+ .getSubscription(info.getServiceId(), info.getUrl())
.toObservable();
disposables.add(observable
@@ -251,14 +315,14 @@ public class ChannelFragment extends BaseListInfoFragment {
final Action onComplete = new Action() {
@Override
public void run() throws Exception {
- if (DEBUG) Log.d(TAG, "Updated subscription: " + info.url);
+ if (DEBUG) Log.d(TAG, "Updated subscription: " + info.getUrl());
}
};
final Consumer onError = new Consumer() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
- onUnrecoverableError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(info.service_id), "Updating Subscription for " + info.url, R.string.subscription_update_failed);
+ onUnrecoverableError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(info.getServiceId()), "Updating Subscription for " + info.getUrl(), R.string.subscription_update_failed);
}
};
@@ -279,7 +343,7 @@ public class ChannelFragment extends BaseListInfoFragment {
final Consumer onError = new Consumer() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
- onUnrecoverableError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.service_id), "Subscription Change", R.string.subscription_change_failed);
+ onUnrecoverableError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.getServiceId()), "Subscription Change", R.string.subscription_change_failed);
}
};
@@ -303,9 +367,9 @@ public class ChannelFragment extends BaseListInfoFragment {
if (subscriptionEntities.isEmpty()) {
if (DEBUG) Log.d(TAG, "No subscription to this channel!");
SubscriptionEntity channel = new SubscriptionEntity();
- channel.setServiceId(info.service_id);
- channel.setUrl(info.url);
- channel.setData(info.name, info.avatar_url, info.description, info.subscriber_count);
+ channel.setServiceId(info.getServiceId());
+ channel.setUrl(info.getUrl());
+ channel.setData(info.getName(), info.getAvatarUrl(), info.getDescription(), info.getSubscriberCount());
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnSubscribe(channel));
} else {
if (DEBUG) Log.d(TAG, "Found subscription to this channel!");
@@ -376,29 +440,70 @@ public class ChannelFragment extends BaseListInfoFragment {
imageLoader.displayImage(result.banner_url, headerChannelBanner, DISPLAY_BANNER_OPTIONS);
imageLoader.displayImage(result.avatar_url, headerAvatarView, DISPLAY_AVATAR_OPTIONS);
- if (result.subscriber_count != -1) {
- headerSubscribersTextView.setText(Localization.localizeSubscribersCount(activity, result.subscriber_count));
+ if (result.getSubscriberCount() != -1) {
+ headerSubscribersTextView.setText(Localization.localizeSubscribersCount(activity, result.getSubscriberCount()));
headerSubscribersTextView.setVisibility(View.VISIBLE);
} else headerSubscribersTextView.setVisibility(View.GONE);
- if (menuRssButton != null) menuRssButton.setVisible(!TextUtils.isEmpty(result.feed_url));
+ if (menuRssButton != null) menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl()));
+ playlistCtrl.setVisibility(View.VISIBLE);
if (!result.errors.isEmpty()) {
- showSnackBarError(result.errors, UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(result.service_id), result.url, 0);
+ showSnackBarError(result.errors, UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
}
if (disposables != null) disposables.clear();
if (subscribeButtonMonitor != null) subscribeButtonMonitor.dispose();
updateSubscription(result);
monitorSubscription(result);
+
+ headerPlayAllButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ NavigationHelper.playOnMainPlayer(activity, getPlayQueue());
+ }
+ });
+ headerPopupButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !PermissionHelper.checkSystemAlertWindowPermission(activity)) {
+ Toast toast = Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG);
+ TextView messageView = toast.getView().findViewById(android.R.id.message);
+ if (messageView != null) messageView.setGravity(Gravity.CENTER);
+ toast.show();
+ return;
+ }
+ NavigationHelper.playOnPopupPlayer(activity, getPlayQueue());
+ }
+ });
+ headerBackgroundButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue());
+ }
+ });
+ }
+
+ private PlayQueue getPlayQueue() {
+ return getPlayQueue(0);
+ }
+
+ private PlayQueue getPlayQueue(final int index) {
+ return new ChannelPlayQueue(
+ currentInfo.getServiceId(),
+ currentInfo.getUrl(),
+ currentInfo.getNextStreamsUrl(),
+ infoListAdapter.getItemsList(),
+ index
+ );
}
@Override
public void handleNextItems(ListExtractor.NextItemsResult result) {
super.handleNextItems(result);
- if (!result.errors.isEmpty()) {
- showSnackBarError(result.errors, UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(serviceId),
+ if (!result.getErrors().isEmpty()) {
+ showSnackBarError(result.getErrors(), UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(serviceId),
"Get next page of: " + url, R.string.general_error);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java
index 835647eec..a62593047 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java
@@ -297,12 +297,12 @@ public class FeedFragment extends BaseListFragment, Voi
// Called only when response is non-empty
@Override
public void onSuccess(final ChannelInfo channelInfo) {
- if (infoListAdapter == null || channelInfo.related_streams.isEmpty()) {
+ if (infoListAdapter == null || channelInfo.getRelatedStreams().isEmpty()) {
onDone();
return;
}
- final InfoItem item = channelInfo.related_streams.get(0);
+ final InfoItem item = channelInfo.getRelatedStreams().get(0);
// Keep requesting new items if the current one already exists
boolean itemExists = doesItemExist(infoListAdapter.getItemsList(), item);
if (!itemExists) {
@@ -412,9 +412,9 @@ public class FeedFragment extends BaseListFragment, Voi
private boolean doesItemExist(final List items, final InfoItem item) {
for (final InfoItem existingItem : items) {
if (existingItem.info_type == item.info_type &&
- existingItem.service_id == item.service_id &&
- existingItem.name.equals(item.name) &&
- existingItem.url.equals(item.url)) return true;
+ existingItem.getServiceId() == item.getServiceId() &&
+ existingItem.getName().equals(item.getName()) &&
+ existingItem.getUrl().equals(item.getUrl())) return true;
}
return false;
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java
index a9d1cda76..c50a8a66a 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java
@@ -27,6 +27,7 @@ import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.NavigationHelper;
+import icepick.State;
import io.reactivex.Single;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
@@ -53,7 +54,8 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView;
public class KioskFragment extends BaseListInfoFragment {
- private String kioskId = "";
+ @State
+ protected String kioskId = "";
/*//////////////////////////////////////////////////////////////////////////
@@ -87,21 +89,40 @@ public class KioskFragment extends BaseListInfoFragment {
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/
+ @Override
+ public void onActivityCreated(Bundle savedState) {
+ super.onActivityCreated(savedState);
+ try {
+ activity.getSupportActionBar()
+ .setTitle(KioskTranslator.getTranslatedKioskName(kioskId, getActivity()));
+ } catch (Exception e) {
+ onUnrecoverableError(e, UserAction.UI_ERROR,
+ "none",
+ "none", R.string.app_ui_crash);
+ }
+ }
+
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
- if(useAsFrontPage && isVisibleToUser) {
+ if(useAsFrontPage && isVisibleToUser && activity != null) {
try {
- activity.getSupportActionBar().setTitle(KioskTranslator.getTranslatedKioskName(kioskId, getActivity()));
+ activity.getSupportActionBar()
+ .setTitle(KioskTranslator.getTranslatedKioskName(kioskId, getActivity()));
} catch (Exception e) {
- onError(e);
+ onUnrecoverableError(e, UserAction.UI_ERROR,
+ "none",
+ "none", R.string.app_ui_crash);
}
}
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fragment_kiosk, container, false);
+ View view = inflater.inflate(R.layout.fragment_kiosk, container, false);
+ activity.getSupportActionBar()
+ .setTitle(KioskTranslator.getTranslatedKioskName(kioskId, getActivity()));
+ return view;
}
/*//////////////////////////////////////////////////////////////////////////
@@ -153,10 +174,10 @@ public class KioskFragment extends BaseListInfoFragment {
ActionBar supportActionBar = activity.getSupportActionBar();
supportActionBar.setTitle(title);
- if (!result.errors.isEmpty()) {
- showSnackBarError(result.errors,
+ if (!result.getErrors().isEmpty()) {
+ showSnackBarError(result.getErrors(),
UserAction.REQUESTED_PLAYLIST,
- NewPipe.getNameOfService(result.service_id), result.url, 0);
+ NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
}
}
@@ -164,8 +185,8 @@ public class KioskFragment extends BaseListInfoFragment {
public void handleNextItems(ListExtractor.NextItemsResult result) {
super.handleNextItems(result);
- if (!result.errors.isEmpty()) {
- showSnackBarError(result.errors,
+ if (!result.getErrors().isEmpty()) {
+ showSnackBarError(result.getErrors(),
UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId)
, "Get next page of: " + url, 0);
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java
index b88d54524..4f87228a5 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java
@@ -1,6 +1,7 @@
package org.schabi.newpipe.fragments.list.playlist;
-import android.content.Intent;
+import android.content.Context;
+import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
@@ -13,7 +14,6 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@@ -23,12 +23,12 @@ import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
+import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
-import org.schabi.newpipe.player.BackgroundPlayer;
-import org.schabi.newpipe.player.MainVideoPlayer;
-import org.schabi.newpipe.player.PopupVideoPlayer;
-import org.schabi.newpipe.playlist.ExternalPlayQueue;
+import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.playlist.PlayQueue;
+import org.schabi.newpipe.playlist.PlaylistPlayQueue;
+import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.NavigationHelper;
@@ -50,10 +50,11 @@ public class PlaylistFragment extends BaseListInfoFragment {
private TextView headerUploaderName;
private ImageView headerUploaderAvatar;
private TextView headerStreamCount;
+ private View playlistCtrl;
- private Button headerPlayAllButton;
- private Button headerPopupButton;
- private Button headerBackgroundButton;
+ private View headerPlayAllButton;
+ private View headerPopupButton;
+ private View headerBackgroundButton;
public static PlaylistFragment getInstance(int serviceId, String url, String name) {
PlaylistFragment instance = new PlaylistFragment();
@@ -81,10 +82,11 @@ public class PlaylistFragment extends BaseListInfoFragment {
headerUploaderName = headerRootLayout.findViewById(R.id.uploader_name);
headerUploaderAvatar = headerRootLayout.findViewById(R.id.uploader_avatar_view);
headerStreamCount = headerRootLayout.findViewById(R.id.playlist_stream_count);
+ playlistCtrl = headerRootLayout.findViewById(R.id.playlist_control);
- headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_play_all_button);
- headerPopupButton = headerRootLayout.findViewById(R.id.playlist_play_popup_button);
- headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_play_bg_button);
+ headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_all_button);
+ headerPopupButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_popup_button);
+ headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_bg_button);
return headerRootLayout;
}
@@ -103,6 +105,47 @@ public class PlaylistFragment extends BaseListInfoFragment {
inflater.inflate(R.menu.menu_playlist, menu);
}
+ @Override
+ protected void showStreamDialog(final StreamInfoItem item) {
+ final Context context = getContext();
+ if (context == null || context.getResources() == null || getActivity() == null) return;
+
+ final String[] commands = new String[]{
+ context.getResources().getString(R.string.enqueue_on_background),
+ context.getResources().getString(R.string.enqueue_on_popup),
+ context.getResources().getString(R.string.start_here_on_main),
+ context.getResources().getString(R.string.start_here_on_background),
+ context.getResources().getString(R.string.start_here_on_popup),
+ };
+
+ final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
+ switch (i) {
+ case 0:
+ NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
+ break;
+ case 1:
+ NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item));
+ break;
+ case 2:
+ NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
+ break;
+ case 3:
+ NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
+ break;
+ case 4:
+ NavigationHelper.playOnPopupPlayer(context, getPlayQueue(index));
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ new InfoItemDialog(getActivity(), item, commands, actions).show();
+ }
/*//////////////////////////////////////////////////////////////////////////
// Load and handle
//////////////////////////////////////////////////////////////////////////*/
@@ -138,29 +181,31 @@ public class PlaylistFragment extends BaseListInfoFragment {
animateView(headerRootLayout, true, 100);
animateView(headerUploaderLayout, true, 300);
headerUploaderLayout.setOnClickListener(null);
- if (!TextUtils.isEmpty(result.uploader_name)) {
- headerUploaderName.setText(result.uploader_name);
- if (!TextUtils.isEmpty(result.uploader_url)) {
+ if (!TextUtils.isEmpty(result.getUploaderName())) {
+ headerUploaderName.setText(result.getUploaderName());
+ if (!TextUtils.isEmpty(result.getUploaderUrl())) {
headerUploaderLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- NavigationHelper.openChannelFragment(getFragmentManager(), result.service_id, result.uploader_url, result.uploader_name);
+ NavigationHelper.openChannelFragment(getFragmentManager(), result.getServiceId(), result.getUploaderUrl(), result.getUploaderName());
}
});
}
}
- imageLoader.displayImage(result.uploader_avatar_url, headerUploaderAvatar, DISPLAY_AVATAR_OPTIONS);
+ playlistCtrl.setVisibility(View.VISIBLE);
+
+ imageLoader.displayImage(result.getUploaderAvatarUrl(), headerUploaderAvatar, DISPLAY_AVATAR_OPTIONS);
headerStreamCount.setText(getResources().getQuantityString(R.plurals.videos, (int) result.stream_count, (int) result.stream_count));
- if (!result.errors.isEmpty()) {
- showSnackBarError(result.errors, UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(result.service_id), result.url, 0);
+ if (!result.getErrors().isEmpty()) {
+ showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
}
headerPlayAllButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- startActivity(buildPlaylistIntent(MainVideoPlayer.class));
+ NavigationHelper.playOnMainPlayer(activity, getPlayQueue());
}
});
headerPopupButton.setOnClickListener(new View.OnClickListener() {
@@ -173,34 +218,37 @@ public class PlaylistFragment extends BaseListInfoFragment {
toast.show();
return;
}
- activity.startService(buildPlaylistIntent(PopupVideoPlayer.class));
+ NavigationHelper.playOnPopupPlayer(activity, getPlayQueue());
}
});
headerBackgroundButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- activity.startService(buildPlaylistIntent(BackgroundPlayer.class));
+ NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue());
}
});
}
- private Intent buildPlaylistIntent(final Class targetClazz) {
- final PlayQueue playQueue = new ExternalPlayQueue(
- currentInfo.service_id,
- currentInfo.url,
- currentInfo.next_streams_url,
+ private PlayQueue getPlayQueue() {
+ return getPlayQueue(0);
+ }
+
+ private PlayQueue getPlayQueue(final int index) {
+ return new PlaylistPlayQueue(
+ currentInfo.getServiceId(),
+ currentInfo.getUrl(),
+ currentInfo.getNextStreamsUrl(),
infoListAdapter.getItemsList(),
- 0
+ index
);
- return NavigationHelper.getPlayerIntent(activity, targetClazz, playQueue);
}
@Override
public void handleNextItems(ListExtractor.NextItemsResult result) {
super.handleNextItems(result);
- if (!result.errors.isEmpty()) {
- showSnackBarError(result.errors, UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId)
+ if (!result.getErrors().isEmpty()) {
+ showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId)
, "Get next page of: " + url, 0);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
index fae97bb7b..b30a73455 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
@@ -165,7 +165,7 @@ public class SearchFragment extends BaseListFragment 0) {
- infoListAdapter.addInfoItemList(result.resultList);
+ if (!result.getResults().isEmpty()) {
+ infoListAdapter.addInfoItemList(result.getResults());
} else {
infoListAdapter.clearStreamItemList();
showEmptyState();
@@ -870,11 +876,11 @@ public class SearchFragment extends BaseListFragment items = new ArrayList<>();
private final Context context;
private OnSuggestionItemSelected listener;
- private boolean showSugestinHistory = true;
+ private boolean showSuggestionHistory = true;
public interface OnSuggestionItemSelected {
void onSuggestionItemSelected(SuggestionItem item);
+ void onSuggestionItemInserted(SuggestionItem item);
void onSuggestionItemLongClick(SuggestionItem item);
}
@@ -32,7 +33,7 @@ public class SuggestionListAdapter extends RecyclerView.Adapter items) {
this.items.clear();
- if (showSugestinHistory) {
+ if (showSuggestionHistory) {
this.items.addAll(items);
} else {
// remove history items if history is disabled
@@ -49,8 +50,8 @@ public class SuggestionListAdapter extends RecyclerView.Adapter {
void selected(T selectedItem);
+ void held(T selectedItem);
}
private final Context context;
diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java
new file mode 100644
index 000000000..cdb2191e5
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java
@@ -0,0 +1,55 @@
+package org.schabi.newpipe.info_list;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import org.schabi.newpipe.R;
+import org.schabi.newpipe.extractor.stream.StreamInfoItem;
+
+public class InfoItemDialog {
+ private final AlertDialog dialog;
+
+ public InfoItemDialog(@NonNull final Activity activity,
+ @NonNull final StreamInfoItem info,
+ @NonNull final String[] commands,
+ @NonNull final DialogInterface.OnClickListener actions) {
+ this(activity, commands, actions, info.getName(), info.uploader_name);
+ }
+
+ public InfoItemDialog(@NonNull final Activity activity,
+ @NonNull final String[] commands,
+ @NonNull final DialogInterface.OnClickListener actions,
+ @NonNull final String title,
+ @Nullable final String additionalDetail) {
+
+ final LayoutInflater inflater = activity.getLayoutInflater();
+ final View bannerView = inflater.inflate(R.layout.dialog_title, null);
+ bannerView.setSelected(true);
+
+ TextView titleView = bannerView.findViewById(R.id.itemTitleView);
+ titleView.setText(title);
+
+ TextView detailsView = bannerView.findViewById(R.id.itemAdditionalDetails);
+ if (additionalDetail != null) {
+ detailsView.setText(additionalDetail);
+ detailsView.setVisibility(View.VISIBLE);
+ } else {
+ detailsView.setVisibility(View.GONE);
+ }
+
+ dialog = new AlertDialog.Builder(activity)
+ .setCustomTitle(bannerView)
+ .setItems(commands, actions)
+ .create();
+ }
+
+ public void show() {
+ dialog.show();
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java
index 9aef6dbd2..48fb18517 100644
--- a/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java
+++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java
@@ -36,7 +36,7 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
if (!(infoItem instanceof ChannelInfoItem)) return;
final ChannelInfoItem item = (ChannelInfoItem) infoItem;
- itemTitleView.setText(item.name);
+ itemTitleView.setText(item.getName());
itemAdditionalDetailView.setText(getDetailLine(item));
itemBuilder.getImageLoader()
diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistInfoItemHolder.java
index 3c29a4b76..9c12c5e4d 100644
--- a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistInfoItemHolder.java
+++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistInfoItemHolder.java
@@ -32,7 +32,7 @@ public class PlaylistInfoItemHolder extends InfoItemHolder {
if (!(infoItem instanceof PlaylistInfoItem)) return;
final PlaylistInfoItem item = (PlaylistInfoItem) infoItem;
- itemTitleView.setText(item.name);
+ itemTitleView.setText(item.getName());
itemStreamCountView.setText(item.stream_count + "");
itemUploaderView.setText(item.uploader_name);
diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java
index 138503d39..48dc470d0 100644
--- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java
+++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java
@@ -40,7 +40,7 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
if (!(infoItem instanceof StreamInfoItem)) return;
final StreamInfoItem item = (StreamInfoItem) infoItem;
- itemVideoTitleView.setText(item.name);
+ itemVideoTitleView.setText(item.getName());
itemUploaderView.setText(item.uploader_name);
if (item.duration > 0) {
@@ -67,6 +67,38 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
}
}
});
+
+ switch (item.stream_type) {
+ case AUDIO_STREAM:
+ case VIDEO_STREAM:
+ case FILE:
+ enableLongClick(item);
+ break;
+ case LIVE_STREAM:
+ case AUDIO_LIVE_STREAM:
+ case NONE:
+ default:
+ disableLongClick();
+ break;
+ }
+ }
+
+ private void enableLongClick(final StreamInfoItem item) {
+ itemView.setLongClickable(true);
+ itemView.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View view) {
+ if (itemBuilder.getOnStreamSelectedListener() != null) {
+ itemBuilder.getOnStreamSelectedListener().held(item);
+ }
+ return true;
+ }
+ });
+ }
+
+ private void disableLongClick() {
+ itemView.setLongClickable(false);
+ itemView.setOnLongClickListener(null);
}
/**
diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
index 863eaf3e8..482f8f803 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
@@ -48,6 +48,7 @@ import org.schabi.newpipe.player.event.PlayerEventListener;
import org.schabi.newpipe.player.helper.LockManager;
import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.util.ListHelper;
+import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ThemeHelper;
import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
@@ -68,6 +69,10 @@ public final class BackgroundPlayer extends Service {
public static final String ACTION_REPEAT = "org.schabi.newpipe.player.BackgroundPlayer.REPEAT";
public static final String ACTION_PLAY_NEXT = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_PLAY_NEXT";
public static final String ACTION_PLAY_PREVIOUS = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_PLAY_PREVIOUS";
+ public static final String ACTION_FAST_REWIND = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_REWIND";
+ public static final String ACTION_FAST_FORWARD = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_FORWARD";
+
+ public static final String SET_IMAGE_RESOURCE_METHOD = "setImageResource";
private BasePlayerImpl basePlayerImpl;
private LockManager lockManager;
@@ -130,16 +135,6 @@ public final class BackgroundPlayer extends Service {
/*//////////////////////////////////////////////////////////////////////////
// Actions
//////////////////////////////////////////////////////////////////////////*/
-
- public void openControl(final Context context) {
- Intent intent = new Intent(context, BackgroundPlayerActivity.class);
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- }
- context.startActivity(intent);
- context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
- }
-
private void onClose() {
if (DEBUG) Log.d(TAG, "onClose() called");
@@ -182,7 +177,7 @@ public final class BackgroundPlayer extends Service {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
.setOngoing(true)
- .setSmallIcon(R.drawable.ic_play_circle_filled_white_24dp)
+ .setSmallIcon(R.drawable.ic_newpipe_triangle_white)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setCustomContentView(notRemoteView)
.setCustomBigContentView(bigNotRemoteView);
@@ -191,6 +186,8 @@ public final class BackgroundPlayer extends Service {
}
private void setupNotification(RemoteViews remoteViews) {
+ if (basePlayerImpl == null) return;
+
remoteViews.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle());
remoteViews.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName());
@@ -203,10 +200,21 @@ public final class BackgroundPlayer extends Service {
remoteViews.setOnClickPendingIntent(R.id.notificationRepeat,
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_REPEAT), PendingIntent.FLAG_UPDATE_CURRENT));
- remoteViews.setOnClickPendingIntent(R.id.notificationFRewind,
- PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PREVIOUS), PendingIntent.FLAG_UPDATE_CURRENT));
- remoteViews.setOnClickPendingIntent(R.id.notificationFForward,
- PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_NEXT), PendingIntent.FLAG_UPDATE_CURRENT));
+ if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) {
+ remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_previous);
+ remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_next);
+ remoteViews.setOnClickPendingIntent(R.id.notificationFRewind,
+ PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PREVIOUS), PendingIntent.FLAG_UPDATE_CURRENT));
+ remoteViews.setOnClickPendingIntent(R.id.notificationFForward,
+ PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_NEXT), PendingIntent.FLAG_UPDATE_CURRENT));
+ } else {
+ remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_rewind);
+ remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_fastforward);
+ remoteViews.setOnClickPendingIntent(R.id.notificationFRewind,
+ PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_FAST_REWIND), PendingIntent.FLAG_UPDATE_CURRENT));
+ remoteViews.setOnClickPendingIntent(R.id.notificationFForward,
+ PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_FAST_FORWARD), PendingIntent.FLAG_UPDATE_CURRENT));
+ }
setRepeatModeIcon(remoteViews, basePlayerImpl.getRepeatMode());
}
@@ -241,17 +249,15 @@ public final class BackgroundPlayer extends Service {
//////////////////////////////////////////////////////////////////////////*/
private void setRepeatModeIcon(final RemoteViews remoteViews, final int repeatMode) {
- final String methodName = "setImageResource";
-
switch (repeatMode) {
case Player.REPEAT_MODE_OFF:
- remoteViews.setInt(R.id.notificationRepeat, methodName, R.drawable.exo_controls_repeat_off);
+ remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_repeat_off);
break;
case Player.REPEAT_MODE_ONE:
- remoteViews.setInt(R.id.notificationRepeat, methodName, R.drawable.exo_controls_repeat_one);
+ remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_repeat_one);
break;
case Player.REPEAT_MODE_ALL:
- remoteViews.setInt(R.id.notificationRepeat, methodName, R.drawable.exo_controls_repeat_all);
+ remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_repeat_all);
break;
}
}
@@ -372,6 +378,7 @@ public final class BackgroundPlayer extends Service {
@Override
public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) {
+ if (currentItem == item && currentInfo == info) return;
super.sync(item, info);
resetNotification();
@@ -380,12 +387,13 @@ public final class BackgroundPlayer extends Service {
}
@Override
+ @Nullable
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
final int index = ListHelper.getDefaultAudioFormat(context, info.audio_streams);
- if (index < 0) return null;
+ if (index < 0 || index >= info.audio_streams.size()) return null;
final AudioStream audio = info.audio_streams.get(index);
- return buildMediaSource(audio.url, MediaFormat.getSuffixById(audio.format));
+ return buildMediaSource(audio.getUrl(), MediaFormat.getSuffixById(audio.format));
}
@Override
@@ -449,6 +457,8 @@ public final class BackgroundPlayer extends Service {
intentFilter.addAction(ACTION_REPEAT);
intentFilter.addAction(ACTION_PLAY_PREVIOUS);
intentFilter.addAction(ACTION_PLAY_NEXT);
+ intentFilter.addAction(ACTION_FAST_REWIND);
+ intentFilter.addAction(ACTION_FAST_FORWARD);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -469,7 +479,7 @@ public final class BackgroundPlayer extends Service {
onVideoPlayPause();
break;
case ACTION_OPEN_CONTROLS:
- openControl(getApplicationContext());
+ NavigationHelper.openBackgroundPlayerControl(getApplicationContext());
break;
case ACTION_REPEAT:
onRepeatClicked();
@@ -480,6 +490,12 @@ public final class BackgroundPlayer extends Service {
case ACTION_PLAY_PREVIOUS:
onPlayPrevious();
break;
+ case ACTION_FAST_FORWARD:
+ onFastForward();
+ break;
+ case ACTION_FAST_REWIND:
+ onFastRewind();
+ break;
case Intent.ACTION_SCREEN_ON:
onScreenOnOff(true);
break;
diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
index 8508bb237..427c97741 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
@@ -26,6 +26,7 @@ import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.net.Uri;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
@@ -76,7 +77,6 @@ import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Predicate;
@@ -134,6 +134,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
protected final static int FAST_FORWARD_REWIND_AMOUNT = 10000; // 10 Seconds
protected final static int PLAY_PREV_ACTIVATION_LIMIT = 5000; // 5 seconds
protected final static int PROGRESS_LOOP_INTERVAL = 500;
+ protected final static int RECOVERY_SKIP_THRESHOLD = 3000; // 3 seconds
protected SimpleExoPlayer simpleExoPlayer;
protected AudioReactor audioReactor;
@@ -193,7 +194,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
.observeOn(AndroidSchedulers.mainThread())
.filter(new Predicate() {
@Override
- public boolean test(@NonNull Long aLong) throws Exception {
+ public boolean test(Long aLong) throws Exception {
return isProgressLoopRunning();
}
})
@@ -235,7 +236,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
initPlayback(queue);
}
- protected void initPlayback(@NonNull final PlayQueue queue) {
+ protected void initPlayback(final PlayQueue queue) {
playQueue = queue;
playQueue.init();
playbackManager = new MediaSourceManager(this, playQueue);
@@ -453,16 +454,20 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
final PlayQueueItem currentSourceItem = playQueue.getItem();
// Check if already playing correct window
- final boolean isCurrentWindowCorrect = simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex;
+ final boolean isCurrentWindowCorrect =
+ simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex;
// Check if recovering
- if (isCurrentWindowCorrect && currentSourceItem != null &&
- currentSourceItem.getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
+ if (isCurrentWindowCorrect && currentSourceItem != null) {
/* Recovering with sub-second position may cause a long buffer delay in ExoPlayer,
* rounding this position to the nearest second will help alleviate this.*/
final long position = currentSourceItem.getRecoveryPosition();
- if (DEBUG) Log.d(TAG, "Rewinding to recovery window: " + currentSourceIndex + " at: " + getTimeString((int)position));
+ /* Skip recovering if the recovery position is not set.*/
+ if (position == PlayQueueItem.RECOVERY_UNSET) return;
+
+ if (DEBUG) Log.d(TAG, "Rewinding to recovery window: " + currentSourceIndex +
+ " at: " + getTimeString((int)position));
simpleExoPlayer.seekTo(currentSourceItem.getRecoveryPosition());
playQueue.unsetRecovery(currentSourceIndex);
}
@@ -515,7 +520,6 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
break;
case Player.STATE_READY: //3
recover();
-
if (!isPrepared) {
isPrepared = true;
onPrepared(playWhenReady);
@@ -545,14 +549,18 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
* an error to the play queue based on if the current error can be skipped.
*
* This is done because ExoPlayer reports the source exceptions before window is
- * transitioned on seamless playback.
+ * transitioned on seamless playback. Because player error causes ExoPlayer to go
+ * back to {@link Player#STATE_IDLE STATE_IDLE}, we reset and prepare the media source
+ * again to resume playback.
*
- * Because player error causes ExoPlayer to go back to {@link Player#STATE_IDLE STATE_IDLE},
- * we reset and prepare the media source again to resume playback.
+ * In the event that this error is produced during a valid stream playback, we save the
+ * current position so the playback may be recovered and resumed manually by the user. This
+ * happens only if the playback is {@link #RECOVERY_SKIP_THRESHOLD} milliseconds until complete.
+ *
* If a runtime error occurred, then we can try to recover it by restarting the playback
- * after setting the timestamp recovery.
+ * after setting the timestamp recovery.