mirror of
https://github.com/TeamNewPipe/NewPipe.git
synced 2025-12-11 01:40:14 +00:00
Compare commits
279 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a45a13f73 | ||
|
|
067b15c300 | ||
|
|
8a1c283542 | ||
|
|
93d1e8b2ff | ||
|
|
c60d5b54fa | ||
|
|
ef180f082e | ||
|
|
f52741cc37 | ||
|
|
2a2661f066 | ||
|
|
b521903138 | ||
|
|
acbd699d95 | ||
|
|
6b8928becb | ||
|
|
e393bdb1e5 | ||
|
|
bba6b96765 | ||
|
|
740116356c | ||
|
|
2f6e4fa4a3 | ||
|
|
fb3f6721b2 | ||
|
|
4e7bd21e5c | ||
|
|
219c2030b9 | ||
|
|
b75fdb4566 | ||
|
|
4584b14a31 | ||
|
|
814ddb5932 | ||
|
|
6ea0f6290a | ||
|
|
c796fe1fe6 | ||
|
|
a452a164e6 | ||
|
|
0d9dd69b19 | ||
|
|
d7b73c18f1 | ||
|
|
07f66c0e45 | ||
|
|
d449acbf86 | ||
|
|
fce416ba76 | ||
|
|
cb6bfe8556 | ||
|
|
d7b31e1d25 | ||
|
|
85057376d6 | ||
|
|
c43ac7c869 | ||
|
|
b2657315f1 | ||
|
|
de5ed9717c | ||
|
|
e17a6cbb9f | ||
|
|
8e783b774b | ||
|
|
733663f40d | ||
|
|
4b2a792a62 | ||
|
|
f7aa171d01 | ||
|
|
5eafefb683 | ||
|
|
5d1b02a856 | ||
|
|
4acda3d9ae | ||
|
|
643e10ace2 | ||
|
|
7b64a232de | ||
|
|
d7472d837d | ||
|
|
94b473ab4b | ||
|
|
4a05bbb6c8 | ||
|
|
0343659b35 | ||
|
|
f38eadbe30 | ||
|
|
eb29a53ac5 | ||
|
|
bc71e260e2 | ||
|
|
ddf23a3443 | ||
|
|
d41b248d1c | ||
|
|
a025b25933 | ||
|
|
c9a52a6088 | ||
|
|
0276dca406 | ||
|
|
3bb95ad44c | ||
|
|
0a6572c282 | ||
|
|
3937067be1 | ||
|
|
48e4eb44f2 | ||
|
|
c78cc6f2fd | ||
|
|
73a71e0f5c | ||
|
|
93605774f0 | ||
|
|
2834e5d78f | ||
|
|
dd467b4d63 | ||
|
|
1fa541776b | ||
|
|
f6f67c7b0a | ||
|
|
9b3f19c19b | ||
|
|
f4a9ec15e8 | ||
|
|
006c4ecb02 | ||
|
|
1c752b0e18 | ||
|
|
f84aff63e3 | ||
|
|
ae8121b680 | ||
|
|
882fbf9275 | ||
|
|
0a1743251e | ||
|
|
3403a127c1 | ||
|
|
9c5ca9f09d | ||
|
|
73eea5608a | ||
|
|
f48aeb91f4 | ||
|
|
0bda964ece | ||
|
|
14f5d54b50 | ||
|
|
105ac2f6ff | ||
|
|
160560f1fd | ||
|
|
deeb667d6f | ||
|
|
78123ff6f5 | ||
|
|
9b1fdff22f | ||
|
|
0275502796 | ||
|
|
8af475e319 | ||
|
|
2202c8f09e | ||
|
|
adf309d3a8 | ||
|
|
3071314586 | ||
|
|
88e1df840d | ||
|
|
23c1b66f6c | ||
|
|
b0318a1cce | ||
|
|
c130a66e4d | ||
|
|
3386ba6d1b | ||
|
|
9275569fa6 | ||
|
|
2b23dfd0a6 | ||
|
|
2a13d9990e | ||
|
|
c9669b51c6 | ||
|
|
486c180b3c | ||
|
|
9eb5bf9b87 | ||
|
|
953a89f3a1 | ||
|
|
d638fa1434 | ||
|
|
e6d700288c | ||
|
|
371f14cdc9 | ||
|
|
0733ae2404 | ||
|
|
b1731ebd49 | ||
|
|
342b3191ac | ||
|
|
cd39445245 | ||
|
|
92eac67367 | ||
|
|
0e31a0c704 | ||
|
|
d60c117a70 | ||
|
|
69ccad5998 | ||
|
|
b6d22320e6 | ||
|
|
d3bf948dba | ||
|
|
5de3d96b31 | ||
|
|
d30dd64322 | ||
|
|
386df10a5a | ||
|
|
af147de547 | ||
|
|
23cd0e5a5e | ||
|
|
c5a566657c | ||
|
|
ce2018c864 | ||
|
|
472ab46af2 | ||
|
|
7f87d45bb5 | ||
|
|
e9f7ab18bb | ||
|
|
fba83a8afe | ||
|
|
05089abddc | ||
|
|
ccd70aac51 | ||
|
|
5df8445d04 | ||
|
|
8c43674fa4 | ||
|
|
f162316a6b | ||
|
|
670596ed88 | ||
|
|
6b3eb716c4 | ||
|
|
6a5180d94c | ||
|
|
83faaedfcc | ||
|
|
d98d790a7a | ||
|
|
36b5833a3a | ||
|
|
5e86781a79 | ||
|
|
6a780504b4 | ||
|
|
a0d8212136 | ||
|
|
0e1e6a9d62 | ||
|
|
812282a332 | ||
|
|
a8a4c9e97f | ||
|
|
24c293e335 | ||
|
|
0a596df497 | ||
|
|
3d66c6572b | ||
|
|
f45769cbb2 | ||
|
|
ef51f93c6f | ||
|
|
35af68f148 | ||
|
|
ff21430b43 | ||
|
|
fcb67f5119 | ||
|
|
0a41fbd185 | ||
|
|
d32aaf488f | ||
|
|
1254798013 | ||
|
|
c72d2a2308 | ||
|
|
703181655b | ||
|
|
da4a1c5bf0 | ||
|
|
1130bd502e | ||
|
|
27ea4ee679 | ||
|
|
56e3b66d06 | ||
|
|
7d4768e151 | ||
|
|
7ec1011610 | ||
|
|
e1bbd2055c | ||
|
|
8a19547d9f | ||
|
|
6e6922dab8 | ||
|
|
e009ade922 | ||
|
|
90e15bcab9 | ||
|
|
2a040cea4b | ||
|
|
72f2a7f8db | ||
|
|
8a8022afe6 | ||
|
|
b692bec310 | ||
|
|
536c01c70d | ||
|
|
ec8e14e977 | ||
|
|
85b34f8809 | ||
|
|
22951a56a5 | ||
|
|
46ad84b101 | ||
|
|
5efb77e520 | ||
|
|
76903102b8 | ||
|
|
c8e26b429c | ||
|
|
55c1310f74 | ||
|
|
b8278d91e0 | ||
|
|
b032502148 | ||
|
|
bec1a4dd1a | ||
|
|
4dfb9e7977 | ||
|
|
2bfa165cdc | ||
|
|
658666276d | ||
|
|
62f91b9084 | ||
|
|
719140ab78 | ||
|
|
0471fd8145 | ||
|
|
a079a0c901 | ||
|
|
ac2fa74c8f | ||
|
|
4c10ef65f5 | ||
|
|
cfa697fab2 | ||
|
|
a09b9d3e4d | ||
|
|
c470909f19 | ||
|
|
5e59cfcf9d | ||
|
|
a099fe35d2 | ||
|
|
bcfd8a2450 | ||
|
|
8ed9d71e14 | ||
|
|
004c2fa55a | ||
|
|
3dd63d03cb | ||
|
|
cceedab864 | ||
|
|
b494b2ea39 | ||
|
|
0b29cf086b | ||
|
|
11d33097f7 | ||
|
|
3ae61645de | ||
|
|
4711befffa | ||
|
|
5673d53a20 | ||
|
|
90ca4a5e92 | ||
|
|
ad252956ab | ||
|
|
1b2c091c39 | ||
|
|
9031bc0c7b | ||
|
|
1d85e0ea63 | ||
|
|
458774aadb | ||
|
|
ae89f7bea3 | ||
|
|
fd77b8552b | ||
|
|
bae9f5e844 | ||
|
|
e3f3d90b68 | ||
|
|
646fa877ba | ||
|
|
7f3bd8aec2 | ||
|
|
4501203a7a | ||
|
|
06292bceb2 | ||
|
|
f94f14ab65 | ||
|
|
7145c68e03 | ||
|
|
d1b0cd74be | ||
|
|
bac3825c87 | ||
|
|
fc1d283414 | ||
|
|
c0652daa97 | ||
|
|
553903bd9d | ||
|
|
a43ec25b7e | ||
|
|
bb2a66fd02 | ||
|
|
71ac830bfa | ||
|
|
82bce80c62 | ||
|
|
67ddf78e18 | ||
|
|
8285df0f3f | ||
|
|
bdb45295b9 | ||
|
|
f330ee8f8d | ||
|
|
7331e4a7f2 | ||
|
|
51252d3b61 | ||
|
|
dcdb2c323e | ||
|
|
c4ac901c67 | ||
|
|
a7ce072ca2 | ||
|
|
a9b427b877 | ||
|
|
67a9f3a4ad | ||
|
|
1a4905f36a | ||
|
|
cbb1fde7b0 | ||
|
|
b86bd019a7 | ||
|
|
6a0bada9d2 | ||
|
|
a708278cf0 | ||
|
|
119462cbc9 | ||
|
|
0324a4928c | ||
|
|
895a2a56b5 | ||
|
|
d9e616beee | ||
|
|
aa5d5d2b6d | ||
|
|
85dc555358 | ||
|
|
15b4a7d055 | ||
|
|
696c94050d | ||
|
|
b222614c4a | ||
|
|
edff694bf3 | ||
|
|
be430a6ac0 | ||
|
|
937d40c5f7 | ||
|
|
d3979676ab | ||
|
|
6716262a28 | ||
|
|
06d8bafce6 | ||
|
|
f814755908 | ||
|
|
f355fd2551 | ||
|
|
ea84c62d76 | ||
|
|
acda71cebb | ||
|
|
67dcd2e5c6 | ||
|
|
c996644613 | ||
|
|
171c3e492d | ||
|
|
8834195cc6 | ||
|
|
ed57e72fa1 | ||
|
|
7e84c3e167 | ||
|
|
4adc33471b | ||
|
|
31d07cc1e2 | ||
|
|
a349a66d5a |
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@@ -28,7 +28,7 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras
|
||||
* Stick to NewPipe's style conventions (well, just look the other code and then do it the same way :))
|
||||
* Do not bring non-free software (e.g., binary blobs) into the project. Also, make sure you do not introduce Google libraries.
|
||||
* Stick to [F-Droid contribution guidelines](https://f-droid.org/wiki/page/Inclusion_Policy)
|
||||
* Make changes on a separate branch, not on the master branch. This is commonly known as *feature branch workflow*. You may then send your changes as a pull request on GitHub. Patches to the email address mentioned in this document might not be considered, GitHub is the primary platform.
|
||||
* Make changes on a separate branch, not on the master branch. This is commonly known as *feature branch workflow*. You may then send your changes as a pull request on GitHub. Patches to the email address mentioned in this document might not be considered, GitHub is the primary platform. (This only affects you if you are a member of TeamNewPipe)
|
||||
* When submitting changes, you confirm that your code is licensed under the terms of the [GNU General Public License v3](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
* 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.
|
||||
|
||||
@@ -8,8 +8,8 @@ android {
|
||||
applicationId "org.schabi.newpipe"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 27
|
||||
versionCode 61
|
||||
versionName "0.13.2"
|
||||
versionCode 64
|
||||
versionName "0.13.5"
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
@@ -41,7 +41,7 @@ android {
|
||||
}
|
||||
|
||||
ext {
|
||||
supportLibVersion = '27.1.0'
|
||||
supportLibVersion = '27.1.1'
|
||||
exoPlayerLibVersion = '2.7.3'
|
||||
roomDbLibVersion = '1.0.0'
|
||||
leakCanaryLibVersion = '1.5.4'
|
||||
@@ -54,8 +54,7 @@ dependencies {
|
||||
exclude module: 'support-annotations'
|
||||
}
|
||||
|
||||
implementation 'com.github.TeamNewPipe:NewPipeExtractor:77a74b8'
|
||||
|
||||
implementation 'com.github.TeamNewPipe:NewPipeExtractor:bf1c771'
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'org.mockito:mockito-core:1.10.19'
|
||||
@@ -66,7 +65,6 @@ dependencies {
|
||||
implementation "com.android.support:recyclerview-v7:$supportLibVersion"
|
||||
implementation "com.android.support:preference-v14:$supportLibVersion"
|
||||
|
||||
implementation 'com.google.code.gson:gson:2.8.2'
|
||||
implementation 'ch.acra:acra:4.9.2'
|
||||
|
||||
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
||||
|
||||
6
app/proguard-rules.pro
vendored
6
app/proguard-rules.pro
vendored
@@ -42,3 +42,9 @@
|
||||
-dontwarn javax.annotation.**
|
||||
# A resource is loaded with a relative path so the package of this class must be preserved.
|
||||
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
|
||||
-keepclassmembers class * implements java.io.Serializable {
|
||||
static final long serialVersionUID;
|
||||
!static !transient <fields>;
|
||||
private void writeObject(java.io.ObjectOutputStream);
|
||||
private void readObject(java.io.ObjectInputStream);
|
||||
}
|
||||
|
||||
@@ -80,8 +80,8 @@
|
||||
android:name=".history.HistoryActivity"
|
||||
android:label="@string/title_activity_history"/>
|
||||
|
||||
<service android:name=".subscription.services.SubscriptionsImportService"/>
|
||||
<service android:name=".subscription.services.SubscriptionsExportService"/>
|
||||
<service android:name=".local.subscription.services.SubscriptionsImportService"/>
|
||||
<service android:name=".local.subscription.services.SubscriptionsExportService"/>
|
||||
|
||||
<activity
|
||||
android:name=".PanicResponderActivity"
|
||||
|
||||
@@ -151,7 +151,8 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
settings.setOnClickListener(view -> NavigationHelper.openSettings(this));
|
||||
downloads.setOnClickListener(view ->NavigationHelper.openDownloads(this));
|
||||
history.setOnClickListener(view -> NavigationHelper.openHistory(this));
|
||||
history.setOnClickListener(view ->
|
||||
NavigationHelper.openStatisticFragment(getSupportFragmentManager()));
|
||||
}
|
||||
|
||||
private void setupDrawerHeader() {
|
||||
@@ -327,16 +328,16 @@ public class MainActivity extends AppCompatActivity {
|
||||
case android.R.id.home:
|
||||
onHomeButtonPressed();
|
||||
return true;
|
||||
case R.id.action_settings:
|
||||
NavigationHelper.openSettings(this);
|
||||
return true;
|
||||
case R.id.action_show_downloads:
|
||||
return NavigationHelper.openDownloads(this);
|
||||
case R.id.action_history:
|
||||
NavigationHelper.openStatisticFragment(getSupportFragmentManager());
|
||||
return true;
|
||||
case R.id.action_about:
|
||||
NavigationHelper.openAbout(this);
|
||||
return true;
|
||||
case R.id.action_history:
|
||||
NavigationHelper.openHistory(this);
|
||||
case R.id.action_settings:
|
||||
NavigationHelper.openSettings(this);
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
|
||||
@@ -32,10 +32,10 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||
import org.schabi.newpipe.playlist.ChannelPlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlaylistPlayQueue;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
@@ -59,7 +59,8 @@ import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.*;
|
||||
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
|
||||
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO;
|
||||
import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr;
|
||||
|
||||
/**
|
||||
@@ -67,13 +68,10 @@ import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr;
|
||||
*/
|
||||
public class RouterActivity extends AppCompatActivity {
|
||||
|
||||
@State
|
||||
protected int currentServiceId = -1;
|
||||
@State protected int currentServiceId = -1;
|
||||
private StreamingService currentService;
|
||||
@State
|
||||
protected LinkType currentLinkType;
|
||||
@State
|
||||
protected int selectedRadioPosition = -1;
|
||||
@State protected LinkType currentLinkType;
|
||||
@State protected int selectedRadioPosition = -1;
|
||||
protected int selectedPreviously = -1;
|
||||
|
||||
protected String currentUrl;
|
||||
@@ -94,8 +92,7 @@ public class RouterActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
setTheme(ThemeHelper.isLightThemeSelected(this)
|
||||
? R.style.RouterActivityThemeLight
|
||||
: R.style.RouterActivityThemeDark);
|
||||
? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,49 +159,65 @@ public class RouterActivity extends AppCompatActivity {
|
||||
|
||||
protected void onSuccess() {
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
|
||||
boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
|
||||
|
||||
if ((isExtAudioEnabled || isExtVideoEnabled) && currentLinkType != LinkType.STREAM) {
|
||||
Toast.makeText(this, R.string.external_player_unsupported_link_type, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
final String playerChoiceKey = preferences.getString(getString(R.string.preferred_open_action_key), getString(R.string.preferred_open_action_default));
|
||||
final String selectedChoiceKey = preferences.getString(getString(R.string.preferred_open_action_key), getString(R.string.preferred_open_action_default));
|
||||
|
||||
final String showInfoKey = getString(R.string.show_info_key);
|
||||
final String videoPlayerKey = getString(R.string.video_player_key);
|
||||
final String backgroundPlayerKey = getString(R.string.background_player_key);
|
||||
final String popupPlayerKey = getString(R.string.popup_player_key);
|
||||
final String alwaysAskKey = getString(R.string.always_ask_open_action_key);
|
||||
|
||||
final List<StreamingService.ServiceInfo.MediaCapability> capabilities = currentService.getServiceInfo().getMediaCapabilities();
|
||||
if (selectedChoiceKey.equals(alwaysAskKey)) {
|
||||
final List<AdapterChoiceItem> choices = getChoicesForService(currentService, currentLinkType);
|
||||
|
||||
boolean serviceSupportsPlayer = false;
|
||||
if (playerChoiceKey.equals(videoPlayerKey) || playerChoiceKey.equals(popupPlayerKey)) {
|
||||
serviceSupportsPlayer = capabilities.contains(VIDEO);
|
||||
} else if (playerChoiceKey.equals(backgroundPlayerKey)) {
|
||||
serviceSupportsPlayer = capabilities.contains(AUDIO);
|
||||
}
|
||||
|
||||
if (playerChoiceKey.equals(alwaysAskKey) || !serviceSupportsPlayer) {
|
||||
showDialog();
|
||||
if (choices.size() == 1) {
|
||||
handleChoice(choices.get(0).key);
|
||||
} else if (choices.size() == 0) {
|
||||
handleChoice(showInfoKey);
|
||||
} else {
|
||||
showDialog(choices);
|
||||
}
|
||||
} else if (selectedChoiceKey.equals(showInfoKey)) {
|
||||
handleChoice(showInfoKey);
|
||||
} else {
|
||||
handleChoice(playerChoiceKey);
|
||||
final boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
|
||||
final boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
|
||||
final boolean isVideoPlayerSelected = selectedChoiceKey.equals(videoPlayerKey) || selectedChoiceKey.equals(popupPlayerKey);
|
||||
final boolean isAudioPlayerSelected = selectedChoiceKey.equals(backgroundPlayerKey);
|
||||
|
||||
if (currentLinkType != LinkType.STREAM) {
|
||||
if (isExtAudioEnabled && isAudioPlayerSelected || isExtVideoEnabled && isVideoPlayerSelected) {
|
||||
Toast.makeText(this, R.string.external_player_unsupported_link_type, Toast.LENGTH_LONG).show();
|
||||
handleChoice(showInfoKey);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final List<StreamingService.ServiceInfo.MediaCapability> capabilities = currentService.getServiceInfo().getMediaCapabilities();
|
||||
|
||||
boolean serviceSupportsChoice = false;
|
||||
if (isVideoPlayerSelected) {
|
||||
serviceSupportsChoice = capabilities.contains(VIDEO);
|
||||
} else if (selectedChoiceKey.equals(backgroundPlayerKey)) {
|
||||
serviceSupportsChoice = capabilities.contains(AUDIO);
|
||||
}
|
||||
|
||||
if (serviceSupportsChoice) {
|
||||
handleChoice(selectedChoiceKey);
|
||||
} else {
|
||||
handleChoice(showInfoKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showDialog() {
|
||||
private void showDialog(final List<AdapterChoiceItem> choices) {
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
final ContextThemeWrapper themeWrapperContext = new ContextThemeWrapper(this,
|
||||
ThemeHelper.isLightThemeSelected(this) ? R.style.LightTheme : R.style.DarkTheme);
|
||||
final Context themeWrapperContext = getThemeWrapperContext();
|
||||
|
||||
final LayoutInflater inflater = LayoutInflater.from(themeWrapperContext);
|
||||
final LinearLayout rootLayout = (LinearLayout) inflater.inflate(R.layout.preferred_player_dialog_view, null, false);
|
||||
final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list);
|
||||
|
||||
final List<AdapterChoiceItem> choices = getChoicesForService(themeWrapperContext, currentService);
|
||||
|
||||
final DialogInterface.OnClickListener dialogButtonsClickListener = (dialog, which) -> {
|
||||
final int indexOfChild = radioGroup.indexOfChild(
|
||||
radioGroup.findViewById(radioGroup.getCheckedRadioButtonId()));
|
||||
@@ -278,21 +291,27 @@ public class RouterActivity extends AppCompatActivity {
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
private List<AdapterChoiceItem> getChoicesForService(Context context, StreamingService service) {
|
||||
private List<AdapterChoiceItem> getChoicesForService(StreamingService service, LinkType linkType) {
|
||||
final Context context = getThemeWrapperContext();
|
||||
|
||||
final List<AdapterChoiceItem> returnList = new ArrayList<>();
|
||||
final List<StreamingService.ServiceInfo.MediaCapability> capabilities = service.getServiceInfo().getMediaCapabilities();
|
||||
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
|
||||
boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
|
||||
|
||||
returnList.add(new AdapterChoiceItem(getString(R.string.show_info_key), getString(R.string.show_info),
|
||||
resolveResourceIdFromAttr(context, R.attr.info)));
|
||||
|
||||
if (capabilities.contains(VIDEO)) {
|
||||
if (capabilities.contains(VIDEO) && !(isExtVideoEnabled && linkType != LinkType.STREAM)) {
|
||||
returnList.add(new AdapterChoiceItem(getString(R.string.video_player_key), getString(R.string.video_player),
|
||||
resolveResourceIdFromAttr(context, R.attr.play)));
|
||||
returnList.add(new AdapterChoiceItem(getString(R.string.popup_player_key), getString(R.string.popup_player),
|
||||
resolveResourceIdFromAttr(context, R.attr.popup)));
|
||||
}
|
||||
|
||||
if (capabilities.contains(AUDIO)) {
|
||||
if (capabilities.contains(AUDIO) && !(isExtAudioEnabled && linkType != LinkType.STREAM)) {
|
||||
returnList.add(new AdapterChoiceItem(getString(R.string.background_player_key), getString(R.string.background_player),
|
||||
resolveResourceIdFromAttr(context, R.attr.audio)));
|
||||
}
|
||||
@@ -300,6 +319,11 @@ public class RouterActivity extends AppCompatActivity {
|
||||
return returnList;
|
||||
}
|
||||
|
||||
private Context getThemeWrapperContext() {
|
||||
return new ContextThemeWrapper(this,
|
||||
ThemeHelper.isLightThemeSelected(this) ? R.style.LightTheme : R.style.DarkTheme);
|
||||
}
|
||||
|
||||
private void setDialogButtonsState(AlertDialog dialog, boolean state) {
|
||||
final Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
|
||||
final Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
|
||||
@@ -309,15 +333,15 @@ public class RouterActivity extends AppCompatActivity {
|
||||
positiveButton.setEnabled(state);
|
||||
}
|
||||
|
||||
private void handleChoice(final String playerChoiceKey) {
|
||||
private void handleChoice(final String selectedChoiceKey) {
|
||||
final List<String> validChoicesList = Arrays.asList(getResources().getStringArray(R.array.preferred_open_action_values_list));
|
||||
if (validChoicesList.contains(playerChoiceKey)) {
|
||||
if (validChoicesList.contains(selectedChoiceKey)) {
|
||||
PreferenceManager.getDefaultSharedPreferences(this).edit()
|
||||
.putString(getString(R.string.preferred_open_action_last_selected_key), playerChoiceKey)
|
||||
.putString(getString(R.string.preferred_open_action_last_selected_key), selectedChoiceKey)
|
||||
.apply();
|
||||
}
|
||||
|
||||
if (playerChoiceKey.equals(getString(R.string.popup_player_key)) && !PermissionHelper.isPopupEnabled(this)) {
|
||||
if (selectedChoiceKey.equals(getString(R.string.popup_player_key)) && !PermissionHelper.isPopupEnabled(this)) {
|
||||
PermissionHelper.showPopupEnablementToast(this);
|
||||
finish();
|
||||
return;
|
||||
@@ -325,7 +349,7 @@ public class RouterActivity extends AppCompatActivity {
|
||||
|
||||
// stop and bypass FetcherService if InfoScreen was selected since
|
||||
// StreamDetailFragment can fetch data itself
|
||||
if (playerChoiceKey.equals(getString(R.string.show_info_key))) {
|
||||
if (selectedChoiceKey.equals(getString(R.string.show_info_key))) {
|
||||
disposables.add(Observable
|
||||
.fromCallable(() -> NavigationHelper.getIntentByLink(this, currentUrl))
|
||||
.subscribeOn(Schedulers.io())
|
||||
@@ -342,7 +366,7 @@ public class RouterActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
final Intent intent = new Intent(this, FetcherService.class);
|
||||
final Choice choice = new Choice(currentService.getServiceId(), currentLinkType, currentUrl, playerChoiceKey);
|
||||
final Choice choice = new Choice(currentService.getServiceId(), currentLinkType, currentUrl, selectedChoiceKey);
|
||||
intent.putExtra(FetcherService.KEY_CHOICE, choice);
|
||||
startService(intent);
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ public class AboutActivity extends AppCompatActivity {
|
||||
new SoftwareComponent("Giga Get", "2014", "Peter Cai", "https://github.com/PaperAirplane-Dev-Team/GigaGet", StandardLicenses.GPL2),
|
||||
new SoftwareComponent("NewPipe Extractor", "2017", "Christian Schabesberger", "https://github.com/TeamNewPipe/NewPipeExtractor", StandardLicenses.GPL3),
|
||||
new SoftwareComponent("Jsoup", "2017", "Jonathan Hedley", "https://github.com/jhy/jsoup", StandardLicenses.MIT),
|
||||
new SoftwareComponent("Google Gson", "2008", "Google Inc", "https://github.com/google/gson", StandardLicenses.APACHE2),
|
||||
new SoftwareComponent("Rhino", "2015", "Mozilla", "https://www.mozilla.org/rhino/", StandardLicenses.MPL2),
|
||||
new SoftwareComponent("ACRA", "2013", "Kevin Gaudin", "http://www.acra.ch", StandardLicenses.APACHE2),
|
||||
new SoftwareComponent("Universal Image Loader", "2011 - 2015", "Sergey Tarasevich", "https://github.com/nostra13/Android-Universal-Image-Loader", StandardLicenses.APACHE2),
|
||||
@@ -129,47 +128,31 @@ public class AboutActivity extends AppCompatActivity {
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_about, container, false);
|
||||
Context context = this.getContext();
|
||||
|
||||
TextView version = rootView.findViewById(R.id.app_version);
|
||||
version.setText(BuildConfig.VERSION_NAME);
|
||||
|
||||
View githubLink = rootView.findViewById(R.id.github_link);
|
||||
githubLink.setOnClickListener(new OnGithubLinkClickListener());
|
||||
githubLink.setOnClickListener(nv -> openWebsite(context.getString(R.string.github_url), context));
|
||||
|
||||
View donationLink = rootView.findViewById(R.id.donation_link);
|
||||
donationLink.setOnClickListener(new OnDonationLinkClickListener());
|
||||
donationLink.setOnClickListener(v -> openWebsite(context.getString(R.string.donation_url), context));
|
||||
|
||||
View websiteLink = rootView.findViewById(R.id.website_link);
|
||||
websiteLink.setOnClickListener(new OnWebsiteLinkClickListener());
|
||||
websiteLink.setOnClickListener(nv -> openWebsite(context.getString(R.string.website_url), context));
|
||||
|
||||
View privacyPolicyLink = rootView.findViewById(R.id.privacy_policy_link);
|
||||
privacyPolicyLink.setOnClickListener(v -> openWebsite(context.getString(R.string.privacy_policy_url), context));
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
private static class OnGithubLinkClickListener 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.github_url)));
|
||||
context.startActivity(intent);
|
||||
}
|
||||
private void openWebsite(String url, Context context) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
private static class OnDonationLinkClickListener 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.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.schabi.newpipe.about;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
@@ -39,7 +40,7 @@ public class LicenseFragment extends Fragment {
|
||||
* @param license the license to show
|
||||
*/
|
||||
public static void showLicense(Context context, License license) {
|
||||
new LicenseFragmentHelper().execute(context, license);
|
||||
new LicenseFragmentHelper((Activity) context).execute(license);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package org.schabi.newpipe.about;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.webkit.WebView;
|
||||
import org.schabi.newpipe.R;
|
||||
@@ -10,26 +13,46 @@ import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class LicenseFragmentHelper extends AsyncTask<Object, Void, Integer> {
|
||||
|
||||
private Context context;
|
||||
WeakReference<Activity> weakReference;
|
||||
private License license;
|
||||
|
||||
public LicenseFragmentHelper(@Nullable Activity activity) {
|
||||
weakReference = new WeakReference<>(activity);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Activity getActivity() {
|
||||
Activity activity = weakReference.get();
|
||||
|
||||
if (activity != null && activity.isFinishing()) {
|
||||
return null;
|
||||
} else {
|
||||
return activity;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer doInBackground(Object... objects) {
|
||||
context = (Context) objects[0];
|
||||
license = (License) objects[1];
|
||||
license = (License) objects[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Integer result){
|
||||
String webViewData = getFormattedLicense(context, license);
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(context);
|
||||
protected void onPostExecute(Integer result) {
|
||||
Activity activity = getActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String webViewData = getFormattedLicense(activity, license);
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
|
||||
alert.setTitle(license.getName());
|
||||
|
||||
WebView wv = new WebView(context);
|
||||
WebView wv = new WebView(activity);
|
||||
wv.loadData(webViewData, "text/html; charset=UTF-8", null);
|
||||
|
||||
alert.setView(wv);
|
||||
|
||||
@@ -9,7 +9,7 @@ import android.arch.persistence.room.PrimaryKey;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@@ -239,7 +239,8 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
||||
if (rootView == null && getView() != null) rootView = getView();
|
||||
if (rootView == null) return;
|
||||
|
||||
ErrorActivity.reportError(getContext(), exception, MainActivity.class, rootView, ErrorActivity.ErrorInfo.make(userAction, serviceName, request, errorId));
|
||||
ErrorActivity.reportError(getContext(), exception, MainActivity.class, rootView,
|
||||
ErrorActivity.ErrorInfo.make(userAction, serviceName, request, errorId));
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -27,10 +27,10 @@ import org.schabi.newpipe.extractor.ServiceList;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.kiosk.KioskList;
|
||||
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
|
||||
import org.schabi.newpipe.fragments.list.feed.FeedFragment;
|
||||
import org.schabi.newpipe.local.feed.FeedFragment;
|
||||
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
|
||||
import org.schabi.newpipe.fragments.local.bookmark.BookmarkFragment;
|
||||
import org.schabi.newpipe.fragments.subscription.SubscriptionFragment;
|
||||
import org.schabi.newpipe.local.bookmark.BookmarkFragment;
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionFragment;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.KioskTranslator;
|
||||
|
||||
@@ -64,15 +64,15 @@ import org.schabi.newpipe.fragments.BackPressable;
|
||||
import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||
import org.schabi.newpipe.util.StreamItemAdapter;
|
||||
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
|
||||
import org.schabi.newpipe.fragments.local.dialog.PlaylistAppendDialog;
|
||||
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
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;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
@@ -20,10 +20,10 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
||||
import org.schabi.newpipe.fragments.local.dialog.PlaylistAppendDialog;
|
||||
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.OnClickGesture;
|
||||
import org.schabi.newpipe.util.StateSaver;
|
||||
@@ -140,9 +140,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||
infoListAdapter.setOnStreamSelectedListener(new OnClickGesture<StreamInfoItem>() {
|
||||
@Override
|
||||
public void selected(StreamInfoItem selectedItem) {
|
||||
onItemSelected(selectedItem);
|
||||
NavigationHelper.openVideoDetailFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(),
|
||||
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
|
||||
onStreamSelected(selectedItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -178,6 +176,12 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||
});
|
||||
}
|
||||
|
||||
private void onStreamSelected(StreamInfoItem selectedItem) {
|
||||
onItemSelected(selectedItem);
|
||||
NavigationHelper.openVideoDetailFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(),
|
||||
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
|
||||
}
|
||||
|
||||
protected void onScrollToBottom() {
|
||||
if (hasMoreItems() && !isLoading.get()) {
|
||||
loadMoreItems();
|
||||
@@ -216,6 +220,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||
|
||||
new InfoItemDialog(getActivity(), item, commands, actions).show();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Menu
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@@ -32,17 +32,15 @@ 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.Stream;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||
import org.schabi.newpipe.fragments.local.dialog.PlaylistAppendDialog;
|
||||
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.local.dialog.PlaylistAppendDialog;
|
||||
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.subscription.SubscriptionService;
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionService;
|
||||
import org.schabi.newpipe.util.AnimationUtils;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
|
||||
@@ -77,8 +77,7 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
||||
UrlIdHandler kioskTypeUrlIdHandler = service.getKioskList()
|
||||
.getUrlIdHandlerByType(kioskId);
|
||||
instance.setInitialData(serviceId,
|
||||
kioskTypeUrlIdHandler.getUrl(kioskId),
|
||||
kioskId);
|
||||
kioskTypeUrlIdHandler.getUrl(kioskId), kioskId);
|
||||
instance.kioskId = kioskId;
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -27,14 +27,13 @@ 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.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||
import org.schabi.newpipe.fragments.local.RemotePlaylistManager;
|
||||
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
|
||||
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.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
|
||||
@@ -41,7 +41,7 @@ import org.schabi.newpipe.extractor.search.SearchEngine;
|
||||
import org.schabi.newpipe.extractor.search.SearchResult;
|
||||
import org.schabi.newpipe.fragments.BackPressable;
|
||||
import org.schabi.newpipe.fragments.list.BaseListFragment;
|
||||
import org.schabi.newpipe.history.HistoryRecordManager;
|
||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.AnimationUtils;
|
||||
@@ -544,7 +544,7 @@ public class SearchFragment
|
||||
howManyDeleted -> suggestionPublisher
|
||||
.onNext(searchEditText.getText().toString()),
|
||||
throwable -> showSnackBarError(throwable,
|
||||
UserAction.SOMETHING_ELSE, "none",
|
||||
UserAction.DELETE_FROM_HISTORY, "none",
|
||||
"Deleting item failed", R.string.general_error)
|
||||
);
|
||||
disposables.add(onDelete);
|
||||
|
||||
@@ -63,24 +63,15 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
||||
public void onBindViewHolder(SuggestionItemHolder holder, int position) {
|
||||
final SuggestionItem currentItem = getItem(position);
|
||||
holder.updateFrom(currentItem);
|
||||
holder.queryView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (listener != null) listener.onSuggestionItemSelected(currentItem);
|
||||
}
|
||||
holder.queryView.setOnClickListener(v -> {
|
||||
if (listener != null) listener.onSuggestionItemSelected(currentItem);
|
||||
});
|
||||
holder.queryView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
holder.queryView.setOnLongClickListener(v -> {
|
||||
if (listener != null) listener.onSuggestionItemLongClick(currentItem);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
holder.insertView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (listener != null) listener.onSuggestionItemInserted(currentItem);
|
||||
}
|
||||
holder.insertView.setOnClickListener(v -> {
|
||||
if (listener != null) listener.onSuggestionItemInserted(currentItem);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.schabi.newpipe.fragments.local.bookmark;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class LastPlayedFragment extends StatisticsPlaylistFragment {
|
||||
@Override
|
||||
protected String getName() {
|
||||
return getString(R.string.title_last_played);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<StreamStatisticsEntry> processResult(List<StreamStatisticsEntry> results) {
|
||||
Collections.sort(results, (left, right) ->
|
||||
right.latestAccessDate.compareTo(left.latestAccessDate));
|
||||
return results;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package org.schabi.newpipe.fragments.local.bookmark;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class MostPlayedFragment extends StatisticsPlaylistFragment {
|
||||
@Override
|
||||
protected String getName() {
|
||||
return getString(R.string.title_most_played);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<StreamStatisticsEntry> processResult(List<StreamStatisticsEntry> results) {
|
||||
Collections.sort(results, (left, right) ->
|
||||
((Long) right.watchCount).compareTo(left.watchCount));
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
package org.schabi.newpipe.history;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.design.widget.TabLayout;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentPagerAdapter;
|
||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.jakewharton.rxbinding2.view.RxView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.settings.SettingsActivity;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
|
||||
public class HistoryActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = "HistoryActivity";
|
||||
/**
|
||||
* The {@link android.support.v4.view.PagerAdapter} that will provide
|
||||
* fragments for each of the sections. We use a
|
||||
* {@link FragmentPagerAdapter} derivative, which will keep every
|
||||
* loaded fragment in memory. If this becomes too memory intensive, it
|
||||
* may be best to switch to a
|
||||
* {@link android.support.v4.app.FragmentStatePagerAdapter}.
|
||||
*/
|
||||
private SectionsPagerAdapter mSectionsPagerAdapter;
|
||||
|
||||
/**
|
||||
* The {@link ViewPager} that will host the section contents.
|
||||
*/
|
||||
private ViewPager mViewPager;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
ThemeHelper.setTheme(this);
|
||||
setContentView(R.layout.activity_history);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setTitle(R.string.title_activity_history);
|
||||
}
|
||||
// Create the adapter that will return a fragment for each of the three
|
||||
// primary sections of the activity.
|
||||
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
|
||||
|
||||
// Set up the ViewPager with the sections adapter.
|
||||
mViewPager = findViewById(R.id.container);
|
||||
mViewPager.setAdapter(mSectionsPagerAdapter);
|
||||
|
||||
TabLayout tabLayout = findViewById(R.id.tabs);
|
||||
tabLayout.setupWithViewPager(mViewPager);
|
||||
|
||||
final FloatingActionButton fab = findViewById(R.id.fab);
|
||||
RxView.clicks(fab)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> {
|
||||
int currentItem = mViewPager.getCurrentItem();
|
||||
HistoryFragment fragment = (HistoryFragment) mSectionsPagerAdapter
|
||||
.instantiateItem(mViewPager, currentItem);
|
||||
fragment.onHistoryCleared();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
getMenuInflater().inflate(R.menu.menu_history, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
case R.id.action_settings:
|
||||
Intent intent = new Intent(this, SettingsActivity.class);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to
|
||||
* one of the sections/tabs/pages.
|
||||
*/
|
||||
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
public SectionsPagerAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
Fragment fragment;
|
||||
switch (position) {
|
||||
case 0:
|
||||
fragment = SearchHistoryFragment.newInstance();
|
||||
break;
|
||||
case 1:
|
||||
fragment = WatchHistoryFragment.newInstance();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("position: " + position);
|
||||
}
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return getString(R.string.title_history_search);
|
||||
case 1:
|
||||
return getString(R.string.title_history_view);
|
||||
}
|
||||
throw new IllegalArgumentException("position: " + position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
// Show 3 total pages.
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
package org.schabi.newpipe.history;
|
||||
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.CallSuper;
|
||||
import android.support.annotation.MainThread;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import org.schabi.newpipe.BaseFragment;
|
||||
import org.schabi.newpipe.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import icepick.State;
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
public abstract class HistoryFragment<E> extends BaseFragment
|
||||
implements HistoryEntryAdapter.OnHistoryItemClickListener<E> {
|
||||
|
||||
private SharedPreferences mSharedPreferences;
|
||||
private String mHistoryIsEnabledKey;
|
||||
private boolean mHistoryIsEnabled;
|
||||
private HistoryIsEnabledChangeListener mHistoryIsEnabledChangeListener;
|
||||
|
||||
private View mDisabledView;
|
||||
private View mEmptyHistoryView;
|
||||
|
||||
@State
|
||||
Parcelable mRecyclerViewState;
|
||||
private RecyclerView mRecyclerView;
|
||||
private HistoryEntryAdapter<E, ? extends RecyclerView.ViewHolder> mHistoryAdapter;
|
||||
|
||||
private Subscription historySubscription;
|
||||
|
||||
protected HistoryRecordManager historyRecordManager;
|
||||
protected CompositeDisposable disposables;
|
||||
|
||||
@StringRes
|
||||
abstract int getEnabledConfigKey();
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mHistoryIsEnabledKey = getString(getEnabledConfigKey());
|
||||
|
||||
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
// Read history enabled from preferences
|
||||
mHistoryIsEnabled = isHistoryEnabled();
|
||||
// Register history enabled listener
|
||||
mSharedPreferences.registerOnSharedPreferenceChangeListener(mHistoryIsEnabledChangeListener);
|
||||
|
||||
historyRecordManager = new HistoryRecordManager(getContext());
|
||||
disposables = new CompositeDisposable();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected abstract HistoryEntryAdapter<E, ? extends RecyclerView.ViewHolder> createAdapter();
|
||||
|
||||
protected abstract Single<List<Long>> insert(final Collection<E> entries);
|
||||
|
||||
protected abstract Single<Integer> delete(final Collection<E> entries);
|
||||
|
||||
@NonNull
|
||||
protected abstract Flowable<List<E>> getAll();
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
getAll().observeOn(AndroidSchedulers.mainThread()).subscribe(getHistorySubscriber());
|
||||
|
||||
final boolean newEnabled = isHistoryEnabled();
|
||||
if (newEnabled != mHistoryIsEnabled) {
|
||||
onHistoryIsEnabledChanged(newEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Subscriber<List<E>> getHistorySubscriber() {
|
||||
return new Subscriber<List<E>>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscription s) {
|
||||
if (historySubscription != null) historySubscription.cancel();
|
||||
|
||||
historySubscription = s;
|
||||
historySubscription.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(List<E> entries) {
|
||||
if (!entries.isEmpty()) {
|
||||
mHistoryAdapter.setEntries(entries);
|
||||
animateView(mEmptyHistoryView, false, 200);
|
||||
|
||||
if (mRecyclerViewState != null) {
|
||||
mRecyclerView.getLayoutManager().onRestoreInstanceState(mRecyclerViewState);
|
||||
mRecyclerViewState = null;
|
||||
}
|
||||
} else {
|
||||
mHistoryAdapter.clear();
|
||||
showEmptyHistory();
|
||||
}
|
||||
|
||||
if (historySubscription != null) historySubscription.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isHistoryEnabled() {
|
||||
return mSharedPreferences.getBoolean(mHistoryIsEnabledKey, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the history is cleared to update the views
|
||||
*/
|
||||
@MainThread
|
||||
public void onHistoryCleared() {
|
||||
if (getContext() == null) return;
|
||||
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setTitle(R.string.delete_all)
|
||||
.setMessage(R.string.delete_all_history_prompt)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.delete_all, (dialog, i) -> clearHistory())
|
||||
.show();
|
||||
}
|
||||
|
||||
protected void makeSnackbar(@StringRes final int text) {
|
||||
if (getActivity() == null) return;
|
||||
|
||||
View view = getActivity().findViewById(R.id.main_content);
|
||||
if (view == null) view = mRecyclerView.getRootView();
|
||||
Snackbar.make(view, text, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private void clearHistory() {
|
||||
final Collection<E> itemsToDelete = new ArrayList<>(mHistoryAdapter.getItems());
|
||||
|
||||
final Disposable deletion = delete(itemsToDelete)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
ignored -> Log.d(TAG, "Clear history deleted [" +
|
||||
itemsToDelete.size() + "] items."),
|
||||
error -> Log.e(TAG, "Clear history delete step failed", error)
|
||||
);
|
||||
|
||||
final Disposable cleanUp = historyRecordManager.removeOrphanedRecords()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
ignored -> Log.d(TAG, "Clear history deleted orphaned stream records"),
|
||||
error -> Log.e(TAG, "Clear history remove orphaned records failed", error)
|
||||
);
|
||||
|
||||
disposables.addAll(deletion, cleanUp);
|
||||
|
||||
makeSnackbar(R.string.history_cleared);
|
||||
mHistoryAdapter.clear();
|
||||
showEmptyHistory();
|
||||
}
|
||||
|
||||
private void showEmptyHistory() {
|
||||
if (mHistoryIsEnabled) {
|
||||
animateView(mEmptyHistoryView, true, 200);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@CallSuper
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_history, container, false);
|
||||
mRecyclerView = rootView.findViewById(R.id.history_view);
|
||||
|
||||
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext(),
|
||||
LinearLayoutManager.VERTICAL, false);
|
||||
mRecyclerView.setLayoutManager(layoutManager);
|
||||
|
||||
mHistoryAdapter = createAdapter();
|
||||
mHistoryAdapter.setOnHistoryItemClickListener(this);
|
||||
mRecyclerView.setAdapter(mHistoryAdapter);
|
||||
mDisabledView = rootView.findViewById(R.id.history_disabled_view);
|
||||
mEmptyHistoryView = rootView.findViewById(R.id.history_empty);
|
||||
|
||||
if (mHistoryIsEnabled) {
|
||||
mRecyclerView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mRecyclerView.setVisibility(View.GONE);
|
||||
mDisabledView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (disposables != null) disposables.dispose();
|
||||
if (historySubscription != null) historySubscription.cancel();
|
||||
|
||||
mSharedPreferences.unregisterOnSharedPreferenceChangeListener(mHistoryIsEnabledChangeListener);
|
||||
mSharedPreferences = null;
|
||||
mHistoryIsEnabledChangeListener = null;
|
||||
mHistoryIsEnabledKey = null;
|
||||
historySubscription = null;
|
||||
disposables = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mRecyclerViewState = mRecyclerView.getLayoutManager().onSaveInstanceState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when history enabled flag is changed.
|
||||
*
|
||||
* @param historyIsEnabled the new value
|
||||
*/
|
||||
@CallSuper
|
||||
public void onHistoryIsEnabledChanged(boolean historyIsEnabled) {
|
||||
mHistoryIsEnabled = historyIsEnabled;
|
||||
if (historyIsEnabled) {
|
||||
animateView(mRecyclerView, true, 300);
|
||||
animateView(mDisabledView, false, 300);
|
||||
if (mHistoryAdapter.isEmpty()) {
|
||||
animateView(mEmptyHistoryView, true, 300);
|
||||
}
|
||||
} else {
|
||||
animateView(mRecyclerView, false, 300);
|
||||
animateView(mDisabledView, true, 300);
|
||||
animateView(mEmptyHistoryView, false, 300);
|
||||
}
|
||||
}
|
||||
|
||||
private class HistoryIsEnabledChangeListener
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (key.equals(mHistoryIsEnabledKey)) {
|
||||
boolean enabled = sharedPreferences.getBoolean(key, false);
|
||||
if (mHistoryIsEnabled != enabled) {
|
||||
onHistoryIsEnabledChanged(enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
package org.schabi.newpipe.history;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
||||
public class SearchHistoryFragment extends HistoryFragment<SearchHistoryEntry> {
|
||||
|
||||
@NonNull
|
||||
public static SearchHistoryFragment newInstance() {
|
||||
return new SearchHistoryFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected SearchHistoryAdapter createAdapter() {
|
||||
return new SearchHistoryAdapter(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Single<List<Long>> insert(Collection<SearchHistoryEntry> entries) {
|
||||
return historyRecordManager.insertSearches(entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Single<Integer> delete(Collection<SearchHistoryEntry> entries) {
|
||||
return historyRecordManager.deleteSearches(entries);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Flowable<List<SearchHistoryEntry>> getAll() {
|
||||
return historyRecordManager.getSearchHistory();
|
||||
}
|
||||
|
||||
@StringRes
|
||||
@Override
|
||||
int getEnabledConfigKey() {
|
||||
return R.string.enable_search_history_key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHistoryItemClick(final SearchHistoryEntry historyItem) {
|
||||
NavigationHelper.openSearch(getContext(), historyItem.getServiceId(),
|
||||
historyItem.getSearch());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHistoryItemLongClick(final SearchHistoryEntry item) {
|
||||
if (activity == null) return;
|
||||
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(item.getSearch())
|
||||
.setMessage(R.string.delete_item_search_history)
|
||||
.setCancelable(true)
|
||||
.setNeutralButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.delete_one, (dialog, i) -> {
|
||||
final Disposable onDelete = historyRecordManager
|
||||
.deleteSearches(Collections.singleton(item))
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
ignored -> {/*successful*/},
|
||||
error -> Log.e(TAG, "Search history Delete One failed:", error)
|
||||
);
|
||||
disposables.add(onDelete);
|
||||
makeSnackbar(R.string.item_deleted);
|
||||
})
|
||||
.setNegativeButton(R.string.delete_all, (dialog, i) -> {
|
||||
final Disposable onDeleteAll = historyRecordManager
|
||||
.deleteSearchHistory(item.getSearch())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
ignored -> {/*successful*/},
|
||||
error -> Log.e(TAG, "Search history Delete All failed:", error)
|
||||
);
|
||||
disposables.add(onDeleteAll);
|
||||
makeSnackbar(R.string.item_deleted);
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private final TextView search;
|
||||
private final TextView info;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
search = itemView.findViewById(R.id.search);
|
||||
info = itemView.findViewById(R.id.info);
|
||||
}
|
||||
}
|
||||
|
||||
protected class SearchHistoryAdapter extends HistoryEntryAdapter<SearchHistoryEntry, ViewHolder> {
|
||||
|
||||
SearchHistoryAdapter(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
View rootView = inflater.inflate(R.layout.item_search_history, parent, false);
|
||||
return new ViewHolder(rootView);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onBindViewHolder(ViewHolder holder, SearchHistoryEntry entry, int position) {
|
||||
holder.search.setText(entry.getSearch());
|
||||
|
||||
final String info = Localization.concatenateStrings(
|
||||
getFormattedDate(entry.getCreationDate()),
|
||||
NewPipe.getNameOfService(entry.getServiceId()));
|
||||
holder.info.setText(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
package org.schabi.newpipe.history;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
|
||||
import org.schabi.newpipe.info_list.holder.StreamInfoItemHolder;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
||||
|
||||
public class WatchHistoryFragment extends HistoryFragment<StreamHistoryEntry> {
|
||||
|
||||
@NonNull
|
||||
public static WatchHistoryFragment newInstance() {
|
||||
return new WatchHistoryFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@StringRes
|
||||
@Override
|
||||
int getEnabledConfigKey() {
|
||||
return R.string.enable_watch_history_key;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected StreamHistoryAdapter createAdapter() {
|
||||
return new StreamHistoryAdapter(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Single<List<Long>> insert(Collection<StreamHistoryEntry> entries) {
|
||||
return historyRecordManager.insertStreamHistory(entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Single<Integer> delete(Collection<StreamHistoryEntry> entries) {
|
||||
return historyRecordManager.deleteStreamHistory(entries);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Flowable<List<StreamHistoryEntry>> getAll() {
|
||||
return historyRecordManager.getStreamHistory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHistoryItemClick(StreamHistoryEntry historyItem) {
|
||||
NavigationHelper.openVideoDetail(getContext(), historyItem.serviceId, historyItem.url,
|
||||
historyItem.title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHistoryItemLongClick(StreamHistoryEntry item) {
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(item.title)
|
||||
.setMessage(R.string.delete_stream_history_prompt)
|
||||
.setCancelable(true)
|
||||
.setNeutralButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.delete_one, (dialog, i) -> {
|
||||
final Disposable onDelete = historyRecordManager
|
||||
.deleteStreamHistory(Collections.singleton(item))
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
ignored -> {/*successful*/},
|
||||
error -> Log.e(TAG, "Watch history Delete One failed:", error)
|
||||
);
|
||||
disposables.add(onDelete);
|
||||
makeSnackbar(R.string.item_deleted);
|
||||
})
|
||||
.setNegativeButton(R.string.delete_all, (dialog, i) -> {
|
||||
final Disposable onDeleteAll = historyRecordManager
|
||||
.deleteStreamHistory(item.streamId)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
ignored -> {/*successful*/},
|
||||
error -> Log.e(TAG, "Watch history Delete All failed:", error)
|
||||
);
|
||||
disposables.add(onDeleteAll);
|
||||
makeSnackbar(R.string.item_deleted);
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private static class StreamHistoryAdapter extends HistoryEntryAdapter<StreamHistoryEntry, ViewHolder> {
|
||||
|
||||
StreamHistoryAdapter(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
View itemView = inflater.inflate(R.layout.list_stream_item, parent, false);
|
||||
return new ViewHolder(itemView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewRecycled(ViewHolder holder) {
|
||||
holder.itemView.setOnClickListener(null);
|
||||
ImageLoader.getInstance()
|
||||
.cancelDisplayTask(holder.thumbnailView);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onBindViewHolder(ViewHolder holder, StreamHistoryEntry entry, int position) {
|
||||
final String formattedDate = getFormattedDate(entry.accessDate);
|
||||
final String info;
|
||||
if (entry.repeatCount > 1) {
|
||||
info = Localization.concatenateStrings(formattedDate,
|
||||
getFormattedViewString(entry.repeatCount));
|
||||
} else {
|
||||
info = formattedDate;
|
||||
}
|
||||
|
||||
holder.info.setText(info);
|
||||
holder.streamTitle.setText(entry.title);
|
||||
holder.uploader.setText(entry.uploader);
|
||||
holder.duration.setText(Localization.getDurationString(entry.duration));
|
||||
ImageLoader.getInstance().displayImage(entry.thumbnailUrl, holder.thumbnailView,
|
||||
ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private final TextView info;
|
||||
private final TextView streamTitle;
|
||||
private final ImageView thumbnailView;
|
||||
private final TextView uploader;
|
||||
private final TextView duration;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
thumbnailView = itemView.findViewById(R.id.itemThumbnailView);
|
||||
info = itemView.findViewById(R.id.itemAdditionalDetails);
|
||||
streamTitle = itemView.findViewById(R.id.itemVideoTitleView);
|
||||
uploader = itemView.findViewById(R.id.itemUploaderView);
|
||||
duration = itemView.findViewById(R.id.itemDurationView);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.PlaylistMiniInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.StreamInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.StreamMiniInfoItemHolder;
|
||||
import org.schabi.newpipe.util.FallbackViewHolder;
|
||||
import org.schabi.newpipe.util.OnClickGesture;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -238,7 +239,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||
return new PlaylistInfoItemHolder(infoItemBuilder, parent);
|
||||
default:
|
||||
Log.e(TAG, "Trollolo");
|
||||
return null;
|
||||
return new FallbackViewHolder(new View(parent.getContext()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local.bookmark;
|
||||
package org.schabi.newpipe.local;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
@@ -13,7 +13,6 @@ import android.view.View;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||
import org.schabi.newpipe.fragments.list.ListViewContract;
|
||||
import org.schabi.newpipe.fragments.local.LocalItemListAdapter;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local;
|
||||
package org.schabi.newpipe.local;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local;
|
||||
package org.schabi.newpipe.local;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.ImageView;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local;
|
||||
package org.schabi.newpipe.local;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
@@ -7,11 +7,14 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.fragments.local.holder.LocalItemHolder;
|
||||
import org.schabi.newpipe.fragments.local.holder.LocalPlaylistItemHolder;
|
||||
import org.schabi.newpipe.fragments.local.holder.LocalPlaylistStreamItemHolder;
|
||||
import org.schabi.newpipe.fragments.local.holder.LocalStatisticStreamItemHolder;
|
||||
import org.schabi.newpipe.fragments.local.holder.RemotePlaylistItemHolder;
|
||||
import org.schabi.newpipe.local.HeaderFooterHolder;
|
||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.local.holder.LocalItemHolder;
|
||||
import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder;
|
||||
import org.schabi.newpipe.local.holder.LocalPlaylistStreamItemHolder;
|
||||
import org.schabi.newpipe.local.holder.LocalStatisticStreamItemHolder;
|
||||
import org.schabi.newpipe.local.holder.RemotePlaylistItemHolder;
|
||||
import org.schabi.newpipe.util.FallbackViewHolder;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.util.OnClickGesture;
|
||||
|
||||
@@ -223,7 +226,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||
return new LocalStatisticStreamItemHolder(localItemBuilder, parent);
|
||||
default:
|
||||
Log.e(TAG, "No view type has been considered for holder: [" + type + "]");
|
||||
return null;
|
||||
return new FallbackViewHolder(new View(parent.getContext()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local.bookmark;
|
||||
package org.schabi.newpipe.local.bookmark;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.os.Bundle;
|
||||
@@ -19,8 +19,9 @@ import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.playlist.PlaylistLocalItem;
|
||||
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
||||
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||
import org.schabi.newpipe.fragments.local.LocalPlaylistManager;
|
||||
import org.schabi.newpipe.fragments.local.RemotePlaylistManager;
|
||||
import org.schabi.newpipe.local.BaseLocalListFragment;
|
||||
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
|
||||
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.OnClickGesture;
|
||||
@@ -38,9 +39,6 @@ import io.reactivex.disposables.CompositeDisposable;
|
||||
public final class BookmarkFragment
|
||||
extends BaseLocalListFragment<List<PlaylistLocalItem>, Void> {
|
||||
|
||||
private View lastPlayedButton;
|
||||
private View mostPlayedButton;
|
||||
|
||||
@State
|
||||
protected Parcelable itemsListState;
|
||||
|
||||
@@ -94,15 +92,6 @@ public final class BookmarkFragment
|
||||
super.initViews(rootView, savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getListHeader() {
|
||||
final View headerRootLayout = activity.getLayoutInflater()
|
||||
.inflate(R.layout.bookmark_header, itemsList, false);
|
||||
lastPlayedButton = headerRootLayout.findViewById(R.id.lastPlayed);
|
||||
mostPlayedButton = headerRootLayout.findViewById(R.id.mostPlayed);
|
||||
return headerRootLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initListeners() {
|
||||
super.initListeners();
|
||||
@@ -136,18 +125,6 @@ public final class BookmarkFragment
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
lastPlayedButton.setOnClickListener(view -> {
|
||||
if (getParentFragment() != null) {
|
||||
NavigationHelper.openLastPlayedFragment(getParentFragment().getFragmentManager());
|
||||
}
|
||||
});
|
||||
|
||||
mostPlayedButton.setOnClickListener(view -> {
|
||||
if (getParentFragment() != null) {
|
||||
NavigationHelper.openMostPlayedFragment(getParentFragment().getFragmentManager());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@@ -180,8 +157,6 @@ public final class BookmarkFragment
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
if (mostPlayedButton != null) mostPlayedButton.setOnClickListener(null);
|
||||
if (lastPlayedButton != null) lastPlayedButton.setOnClickListener(null);
|
||||
|
||||
if (disposables != null) disposables.clear();
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local.dialog;
|
||||
package org.schabi.newpipe.local.dialog;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Bundle;
|
||||
@@ -18,9 +18,9 @@ import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.fragments.local.LocalItemListAdapter;
|
||||
import org.schabi.newpipe.fragments.local.LocalPlaylistManager;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.local.LocalItemListAdapter;
|
||||
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
import org.schabi.newpipe.util.OnClickGesture;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local.dialog;
|
||||
package org.schabi.newpipe.local.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
@@ -12,7 +12,7 @@ import android.widget.Toast;
|
||||
import org.schabi.newpipe.NewPipeDatabase;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||
import org.schabi.newpipe.fragments.local.LocalPlaylistManager;
|
||||
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local.dialog;
|
||||
package org.schabi.newpipe.local.dialog;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.list.feed;
|
||||
package org.schabi.newpipe.local.feed;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@@ -22,7 +22,7 @@ import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.fragments.list.BaseListFragment;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.subscription.SubscriptionService;
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionService;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.history;
|
||||
package org.schabi.newpipe.local.history;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.history;
|
||||
package org.schabi.newpipe.local.history;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
package org.schabi.newpipe.history;
|
||||
package org.schabi.newpipe.local.history;
|
||||
|
||||
/*
|
||||
* Copyright (C) Mauricio Colli 2018
|
||||
* HistoryRecordManager.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
@@ -27,6 +45,7 @@ import java.util.List;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
@@ -80,6 +99,11 @@ public class HistoryRecordManager {
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public Single<Integer> deleteWholeStreamHistory() {
|
||||
return Single.fromCallable(() -> streamHistoryTable.deleteAll())
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public Flowable<List<StreamHistoryEntry>> getStreamHistory() {
|
||||
return streamHistoryTable.getHistory().subscribeOn(Schedulers.io());
|
||||
}
|
||||
@@ -114,20 +138,6 @@ public class HistoryRecordManager {
|
||||
// Search History
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
public Single<List<Long>> insertSearches(final Collection<SearchHistoryEntry> entries) {
|
||||
return Single.fromCallable(() -> searchHistoryTable.insertAll(entries))
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public Single<Integer> deleteSearches(final Collection<SearchHistoryEntry> entries) {
|
||||
return Single.fromCallable(() -> searchHistoryTable.delete(entries))
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public Flowable<List<SearchHistoryEntry>> getSearchHistory() {
|
||||
return searchHistoryTable.getAll();
|
||||
}
|
||||
|
||||
public Maybe<Long> onSearched(final int serviceId, final String search) {
|
||||
if (!isSearchHistoryEnabled()) return Maybe.empty();
|
||||
|
||||
@@ -150,6 +160,11 @@ public class HistoryRecordManager {
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public Single<Integer> deleteWholeSearchHistory() {
|
||||
return Single.fromCallable(() -> searchHistoryTable.deleteAll())
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public Flowable<List<SearchHistoryEntry>> getRelatedSearches(final String query,
|
||||
final int similarQueryLimit,
|
||||
final int uniqueQueryLimit) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local.bookmark;
|
||||
package org.schabi.newpipe.local.history;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
@@ -7,9 +7,13 @@ import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
@@ -17,13 +21,14 @@ import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.history.HistoryRecordManager;
|
||||
import org.schabi.newpipe.local.BaseLocalListFragment;
|
||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.OnClickGesture;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -31,13 +36,19 @@ import java.util.List;
|
||||
|
||||
import icepick.State;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
||||
public abstract class StatisticsPlaylistFragment
|
||||
public class StatisticsPlaylistFragment
|
||||
extends BaseLocalListFragment<List<StreamStatisticsEntry>, Void> {
|
||||
|
||||
private View headerPlayAllButton;
|
||||
private View headerPopupButton;
|
||||
private View headerBackgroundButton;
|
||||
private View playlistCtrl;
|
||||
private View sortButton;
|
||||
private ImageView sortButtonIcon;
|
||||
private TextView sortButtonText;
|
||||
|
||||
@State
|
||||
protected Parcelable itemsListState;
|
||||
@@ -45,14 +56,28 @@ public abstract class StatisticsPlaylistFragment
|
||||
/* Used for independent events */
|
||||
private Subscription databaseSubscription;
|
||||
private HistoryRecordManager recordManager;
|
||||
private CompositeDisposable disposables = new CompositeDisposable();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Abstracts
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
private enum StatisticSortMode {
|
||||
LAST_PLAYED,
|
||||
MOST_PLAYED,
|
||||
}
|
||||
|
||||
protected abstract String getName();
|
||||
StatisticSortMode sortMode = StatisticSortMode.LAST_PLAYED;
|
||||
|
||||
protected abstract List<StreamStatisticsEntry> processResult(final List<StreamStatisticsEntry> results);
|
||||
protected List<StreamStatisticsEntry> processResult(final List<StreamStatisticsEntry> results) {
|
||||
switch (sortMode) {
|
||||
case LAST_PLAYED:
|
||||
Collections.sort(results, (left, right) ->
|
||||
right.latestAccessDate.compareTo(left.latestAccessDate));
|
||||
return results;
|
||||
case MOST_PLAYED:
|
||||
Collections.sort(results, (left, right) ->
|
||||
((Long) right.watchCount).compareTo(left.watchCount));
|
||||
return results;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment LifeCycle - Creation
|
||||
@@ -78,16 +103,20 @@ public abstract class StatisticsPlaylistFragment
|
||||
@Override
|
||||
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||
super.initViews(rootView, savedInstanceState);
|
||||
setTitle(getName());
|
||||
setTitle(getString(R.string.title_last_played));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getListHeader() {
|
||||
final View headerRootLayout = activity.getLayoutInflater().inflate(R.layout.playlist_control,
|
||||
final View headerRootLayout = activity.getLayoutInflater().inflate(R.layout.statistic_playlist_control,
|
||||
itemsList, false);
|
||||
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);
|
||||
sortButton = headerRootLayout.findViewById(R.id.sortButton);
|
||||
sortButtonIcon = headerRootLayout.findViewById(R.id.sortButtonIcon);
|
||||
sortButtonText = headerRootLayout.findViewById(R.id.sortButtonText);
|
||||
return headerRootLayout;
|
||||
}
|
||||
|
||||
@@ -193,6 +222,8 @@ public abstract class StatisticsPlaylistFragment
|
||||
super.handleResult(result);
|
||||
if (itemListAdapter == null) return;
|
||||
|
||||
playlistCtrl.setVisibility(View.VISIBLE);
|
||||
|
||||
itemListAdapter.clearStreamItemList();
|
||||
|
||||
if (result.isEmpty()) {
|
||||
@@ -212,6 +243,7 @@ public abstract class StatisticsPlaylistFragment
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue()));
|
||||
headerBackgroundButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue()));
|
||||
sortButton.setOnClickListener(view -> toggleSortMode());
|
||||
|
||||
hideLoading();
|
||||
}
|
||||
@@ -238,6 +270,21 @@ public abstract class StatisticsPlaylistFragment
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void toggleSortMode() {
|
||||
if(sortMode == StatisticSortMode.LAST_PLAYED) {
|
||||
sortMode = StatisticSortMode.MOST_PLAYED;
|
||||
setTitle(getString(R.string.title_most_played));
|
||||
sortButtonIcon.setImageResource(ThemeHelper.getIconByAttr(R.attr.history, getContext()));
|
||||
sortButtonText.setText(R.string.title_last_played);
|
||||
} else {
|
||||
sortMode = StatisticSortMode.LAST_PLAYED;
|
||||
setTitle(getString(R.string.title_last_played));
|
||||
sortButtonIcon.setImageResource(ThemeHelper.getIconByAttr(R.attr.filter, getContext()));
|
||||
sortButtonText.setText(R.string.title_most_played);
|
||||
}
|
||||
startLoading(true);
|
||||
}
|
||||
|
||||
private void showStreamDialog(final StreamStatisticsEntry item) {
|
||||
final Context context = getContext();
|
||||
final Activity activity = getActivity();
|
||||
@@ -250,6 +297,7 @@ public abstract class StatisticsPlaylistFragment
|
||||
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),
|
||||
context.getResources().getString(R.string.delete),
|
||||
};
|
||||
|
||||
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
|
||||
@@ -270,6 +318,9 @@ public abstract class StatisticsPlaylistFragment
|
||||
case 4:
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
|
||||
break;
|
||||
case 5:
|
||||
deleteEntry(index);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -278,6 +329,32 @@ public abstract class StatisticsPlaylistFragment
|
||||
new InfoItemDialog(getActivity(), infoItem, commands, actions).show();
|
||||
}
|
||||
|
||||
private void deleteEntry(final int index) {
|
||||
final LocalItem infoItem = itemListAdapter.getItemsList()
|
||||
.get(index);
|
||||
if(infoItem instanceof StreamStatisticsEntry) {
|
||||
final StreamStatisticsEntry entry = (StreamStatisticsEntry) infoItem;
|
||||
final Disposable onDelete = recordManager.deleteStreamHistory(entry.streamId)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
howManyDelted -> {
|
||||
if(getView() != null) {
|
||||
Snackbar.make(getView(), R.string.one_item_deleted,
|
||||
Snackbar.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(getContext(),
|
||||
R.string.one_item_deleted,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
},
|
||||
throwable -> showSnackBarError(throwable,
|
||||
UserAction.DELETE_FROM_HISTORY, "none",
|
||||
"Deleting item failed", R.string.general_error));
|
||||
|
||||
disposables.add(onDelete);
|
||||
}
|
||||
}
|
||||
|
||||
private PlayQueue getPlayQueue() {
|
||||
return getPlayQueue(0);
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.schabi.newpipe.fragments.local.holder;
|
||||
package org.schabi.newpipe.local.holder;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.fragments.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||
|
||||
import java.text.DateFormat;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.schabi.newpipe.fragments.local.holder;
|
||||
package org.schabi.newpipe.local.holder;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
||||
import org.schabi.newpipe.fragments.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
|
||||
import java.text.DateFormat;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local.holder;
|
||||
package org.schabi.newpipe.local.holder;
|
||||
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.MotionEvent;
|
||||
@@ -11,7 +11,7 @@ import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.fragments.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local.holder;
|
||||
package org.schabi.newpipe.local.holder;
|
||||
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.View;
|
||||
@@ -10,7 +10,7 @@ import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.fragments.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local.holder;
|
||||
package org.schabi.newpipe.local.holder;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
@@ -6,7 +6,7 @@ import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.fragments.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||
|
||||
import java.text.DateFormat;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.schabi.newpipe.fragments.local.holder;
|
||||
package org.schabi.newpipe.local.holder;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.fragments.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local.bookmark;
|
||||
package org.schabi.newpipe.local.playlist;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
@@ -26,10 +26,10 @@ import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.fragments.local.LocalPlaylistManager;
|
||||
import org.schabi.newpipe.local.BaseLocalListFragment;
|
||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
@@ -173,7 +173,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||
@Override
|
||||
public void held(LocalItem selectedItem) {
|
||||
if (selectedItem instanceof PlaylistStreamEntry) {
|
||||
showStreamDialog((PlaylistStreamEntry) selectedItem);
|
||||
showStreamItemDialog((PlaylistStreamEntry) selectedItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,7 +506,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected void showStreamDialog(final PlaylistStreamEntry item) {
|
||||
protected void showStreamItemDialog(final PlaylistStreamEntry item) {
|
||||
final Context context = getContext();
|
||||
final Activity activity = getActivity();
|
||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local;
|
||||
package org.schabi.newpipe.local.playlist;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.local;
|
||||
package org.schabi.newpipe.local.playlist;
|
||||
|
||||
import org.schabi.newpipe.database.AppDatabase;
|
||||
import org.schabi.newpipe.database.playlist.dao.PlaylistRemoteDAO;
|
||||
@@ -13,11 +13,9 @@ import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public class RemotePlaylistManager {
|
||||
|
||||
private final AppDatabase database;
|
||||
private final PlaylistRemoteDAO playlistRemoteTable;
|
||||
|
||||
public RemotePlaylistManager(final AppDatabase db) {
|
||||
database = db;
|
||||
playlistRemoteTable = db.playlistRemoteDAO();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.subscription;
|
||||
package org.schabi.newpipe.local.subscription;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.subscription;
|
||||
package org.schabi.newpipe.local.subscription;
|
||||
|
||||
public interface ImportExportEventListener {
|
||||
/**
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.schabi.newpipe.subscription;
|
||||
package org.schabi.newpipe.local.subscription;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.subscription;
|
||||
package org.schabi.newpipe.local.subscription;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
@@ -39,9 +39,8 @@ import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
|
||||
import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.subscription.SubscriptionService;
|
||||
import org.schabi.newpipe.subscription.services.SubscriptionsExportService;
|
||||
import org.schabi.newpipe.subscription.services.SubscriptionsImportService;
|
||||
import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService;
|
||||
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService;
|
||||
import org.schabi.newpipe.util.FilePickerActivityHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.OnClickGesture;
|
||||
@@ -53,7 +52,6 @@ import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -65,9 +63,9 @@ import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.KEY_MODE;
|
||||
import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.KEY_VALUE;
|
||||
import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.PREVIOUS_EXPORT_MODE;
|
||||
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE;
|
||||
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_VALUE;
|
||||
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.PREVIOUS_EXPORT_MODE;
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateRotation;
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.subscription;
|
||||
package org.schabi.newpipe.local.subscription;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.fragments.subscription;
|
||||
package org.schabi.newpipe.local.subscription;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
@@ -26,7 +26,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.subscription.services.SubscriptionsImportService;
|
||||
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.FilePickerActivityHelper;
|
||||
import org.schabi.newpipe.util.ServiceHelper;
|
||||
@@ -37,10 +37,10 @@ import java.util.List;
|
||||
import icepick.State;
|
||||
|
||||
import static org.schabi.newpipe.extractor.subscription.SubscriptionExtractor.ContentSource.CHANNEL_URL;
|
||||
import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.CHANNEL_URL_MODE;
|
||||
import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.INPUT_STREAM_MODE;
|
||||
import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.KEY_MODE;
|
||||
import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.KEY_VALUE;
|
||||
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.CHANNEL_URL_MODE;
|
||||
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.INPUT_STREAM_MODE;
|
||||
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE;
|
||||
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_VALUE;
|
||||
|
||||
public class SubscriptionsImportFragment extends BaseFragment {
|
||||
private static final int REQUEST_IMPORT_FILE_CODE = 666;
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.schabi.newpipe.subscription.services;
|
||||
package org.schabi.newpipe.local.subscription.services;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
@@ -36,8 +36,8 @@ import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.subscription.ImportExportEventListener;
|
||||
import org.schabi.newpipe.subscription.SubscriptionService;
|
||||
import org.schabi.newpipe.local.subscription.ImportExportEventListener;
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionService;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.schabi.newpipe.subscription.services;
|
||||
package org.schabi.newpipe.local.subscription.services;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
@@ -29,7 +29,7 @@ import org.reactivestreams.Subscription;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
|
||||
import org.schabi.newpipe.extractor.subscription.SubscriptionItem;
|
||||
import org.schabi.newpipe.subscription.ImportExportJsonHelper;
|
||||
import org.schabi.newpipe.local.subscription.ImportExportJsonHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -49,7 +49,7 @@ public class SubscriptionsExportService extends BaseImportExportService {
|
||||
/**
|
||||
* A {@link LocalBroadcastManager local broadcast} will be made with this action when the export is successfully completed.
|
||||
*/
|
||||
public static final String EXPORT_COMPLETE_ACTION = "org.schabi.newpipe.subscription.services.SubscriptionsExportService.EXPORT_COMPLETE";
|
||||
public static final String EXPORT_COMPLETE_ACTION = "org.schabi.newpipe.local.subscription.services.SubscriptionsExportService.EXPORT_COMPLETE";
|
||||
|
||||
private Subscription subscription;
|
||||
private File outFile;
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.schabi.newpipe.subscription.services;
|
||||
package org.schabi.newpipe.local.subscription.services;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.NonNull;
|
||||
@@ -33,7 +33,7 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.subscription.SubscriptionItem;
|
||||
import org.schabi.newpipe.subscription.ImportExportJsonHelper;
|
||||
import org.schabi.newpipe.local.subscription.ImportExportJsonHelper;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
@@ -64,7 +64,7 @@ public class SubscriptionsImportService extends BaseImportExportService {
|
||||
/**
|
||||
* A {@link LocalBroadcastManager local broadcast} will be made with this action when the import is successfully completed.
|
||||
*/
|
||||
public static final String IMPORT_COMPLETE_ACTION = "org.schabi.newpipe.subscription.services.SubscriptionsImportService.IMPORT_COMPLETE";
|
||||
public static final String IMPORT_COMPLETE_ACTION = "org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.IMPORT_COMPLETE";
|
||||
|
||||
private Subscription subscription;
|
||||
private int currentMode;
|
||||
@@ -48,7 +48,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||
import org.schabi.newpipe.player.helper.LockManager;
|
||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
import org.schabi.newpipe.util.ListHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
@@ -58,7 +58,7 @@ import org.schabi.newpipe.Downloader;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
import org.schabi.newpipe.history.HistoryRecordManager;
|
||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||
import org.schabi.newpipe.player.helper.AudioReactor;
|
||||
import org.schabi.newpipe.player.helper.LoadController;
|
||||
import org.schabi.newpipe.player.helper.MediaSessionManager;
|
||||
@@ -69,9 +69,9 @@ import org.schabi.newpipe.player.playback.BasePlayerMediaSession;
|
||||
import org.schabi.newpipe.player.playback.CustomTrackSelector;
|
||||
import org.schabi.newpipe.player.playback.MediaSourceManager;
|
||||
import org.schabi.newpipe.player.playback.PlaybackListener;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueueAdapter;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueAdapter;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
import org.schabi.newpipe.util.SerializedCache;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -271,6 +271,7 @@ public abstract class BasePlayer implements
|
||||
if (audioReactor != null) audioReactor.dispose();
|
||||
if (playbackManager != null) playbackManager.dispose();
|
||||
if (databaseUpdateReactor != null) databaseUpdateReactor.dispose();
|
||||
if (mediaSessionManager != null) mediaSessionManager.dispose();
|
||||
|
||||
if (playQueueAdapter != null) {
|
||||
playQueueAdapter.unsetSelectedListener();
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -62,10 +63,10 @@ import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
||||
import org.schabi.newpipe.player.helper.PlaybackParameterDialog;
|
||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItemBuilder;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItemHolder;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItemTouchCallback;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItemBuilder;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback;
|
||||
import org.schabi.newpipe.util.AnimationUtils;
|
||||
import org.schabi.newpipe.util.ListHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
@@ -114,9 +115,14 @@ public final class MainVideoPlayer extends AppCompatActivity
|
||||
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
ThemeHelper.setTheme(this);
|
||||
getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) getWindow().setStatusBarColor(Color.BLACK);
|
||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||
|
||||
WindowManager.LayoutParams lp = getWindow().getAttributes();
|
||||
lp.screenBrightness = PlayerHelper.getScreenBrightness(getApplicationContext());
|
||||
getWindow().setAttributes(lp);
|
||||
|
||||
hideSystemUi();
|
||||
setContentView(R.layout.activity_main_player);
|
||||
playerImpl = new VideoPlayerImpl(this);
|
||||
@@ -203,6 +209,9 @@ public final class MainVideoPlayer extends AppCompatActivity
|
||||
if (DEBUG) Log.d(TAG, "onStop() called");
|
||||
super.onStop();
|
||||
playerImpl.destroy();
|
||||
|
||||
PlayerHelper.setScreenBrightness(getApplicationContext(),
|
||||
getWindow().getAttributes().screenBrightness);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
@@ -645,7 +654,7 @@ public final class MainVideoPlayer extends AppCompatActivity
|
||||
@Override
|
||||
protected int getOverrideResolutionIndex(final List<VideoStream> sortedVideos,
|
||||
final String playbackQuality) {
|
||||
return ListHelper.getDefaultResolutionIndex(context, sortedVideos, playbackQuality);
|
||||
return ListHelper.getResolutionIndex(context, sortedVideos, playbackQuality);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
@@ -847,10 +856,12 @@ public final class MainVideoPlayer extends AppCompatActivity
|
||||
if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
|
||||
if (!playerImpl.isPlaying()) return false;
|
||||
|
||||
if (e.getX() > playerImpl.getRootView().getWidth() / 2) {
|
||||
if (e.getX() > playerImpl.getRootView().getWidth() * 2 / 3) {
|
||||
playerImpl.onFastForward();
|
||||
} else {
|
||||
} else if (e.getX() < playerImpl.getRootView().getWidth() / 3) {
|
||||
playerImpl.onFastRewind();
|
||||
} else {
|
||||
playerImpl.getPlayPauseButton().performClick();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -880,7 +891,9 @@ public final class MainVideoPlayer extends AppCompatActivity
|
||||
private final boolean isPlayerGestureEnabled = PlayerHelper.isPlayerGestureEnabled(getApplicationContext());
|
||||
|
||||
private final float stepsBrightness = 15, stepBrightness = (1f / stepsBrightness), minBrightness = .01f;
|
||||
private float currentBrightness = .5f;
|
||||
private float currentBrightness = getWindow().getAttributes().screenBrightness > 0
|
||||
? getWindow().getAttributes().screenBrightness
|
||||
: 0.5f;
|
||||
|
||||
private int currentVolume, maxVolume = playerImpl.getAudioReactor().getMaxVolume();
|
||||
private final float stepsVolume = 15, stepVolume = (float) Math.ceil(maxVolume / stepsVolume), minVolume = 0;
|
||||
|
||||
@@ -2,17 +2,12 @@ package org.schabi.newpipe.player;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class PlayerState implements Serializable {
|
||||
private final static String TAG = "PlayerState";
|
||||
|
||||
@NonNull private final PlayQueue playQueue;
|
||||
private final int repeatMode;
|
||||
@@ -41,21 +36,6 @@ public class PlayerState implements Serializable {
|
||||
// Serdes
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Nullable
|
||||
public static PlayerState fromJson(@NonNull final String json) {
|
||||
try {
|
||||
return new Gson().fromJson(json, PlayerState.class);
|
||||
} catch (JsonSyntaxException error) {
|
||||
Log.e(TAG, "Failed to deserialize PlayerState from json=[" + json + "]", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String toJson() {
|
||||
return new Gson().toJson(this);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@@ -44,6 +44,7 @@ import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.SeekBar;
|
||||
@@ -64,7 +65,7 @@ import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||
import org.schabi.newpipe.player.helper.LockManager;
|
||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||
import org.schabi.newpipe.player.old.PlayVideoActivity;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
import org.schabi.newpipe.util.ListHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
@@ -370,6 +371,7 @@ public final class PopupVideoPlayer extends Service {
|
||||
protected class VideoPlayerImpl extends VideoPlayer implements View.OnLayoutChangeListener {
|
||||
private TextView resizingIndicator;
|
||||
private ImageButton fullScreenButton;
|
||||
private ImageView videoPlayPause;
|
||||
|
||||
private View extraOptionsView;
|
||||
|
||||
@@ -391,6 +393,8 @@ public final class PopupVideoPlayer extends Service {
|
||||
resizingIndicator = rootView.findViewById(R.id.resizing_indicator);
|
||||
fullScreenButton = rootView.findViewById(R.id.fullScreenButton);
|
||||
fullScreenButton.setOnClickListener(v -> onFullScreenButtonClicked());
|
||||
videoPlayPause = rootView.findViewById(R.id.videoPlayPause);
|
||||
videoPlayPause.setOnClickListener(this::onPlayPauseButtonPressed);
|
||||
|
||||
extraOptionsView = rootView.findViewById(R.id.extraOptionsView);
|
||||
rootView.addOnLayoutChangeListener(this);
|
||||
@@ -406,6 +410,10 @@ public final class PopupVideoPlayer extends Service {
|
||||
view.setStyle(captionStyle);
|
||||
}
|
||||
|
||||
private void onPlayPauseButtonPressed(View ib) {
|
||||
onPlayPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayoutChange(final View view, int left, int top, int right, int bottom,
|
||||
int oldLeft, int oldTop, int oldRight, int oldBottom) {
|
||||
@@ -509,7 +517,7 @@ public final class PopupVideoPlayer extends Service {
|
||||
@Override
|
||||
protected int getOverrideResolutionIndex(final List<VideoStream> sortedVideos,
|
||||
final String playbackQuality) {
|
||||
return ListHelper.getPopupDefaultResolutionIndex(context, sortedVideos, playbackQuality);
|
||||
return ListHelper.getPopupResolutionIndex(context, sortedVideos, playbackQuality);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
@@ -651,6 +659,7 @@ public final class PopupVideoPlayer extends Service {
|
||||
public void onPlaying() {
|
||||
super.onPlaying();
|
||||
updateNotification(R.drawable.ic_pause_white);
|
||||
videoPlayPause.setBackgroundResource(R.drawable.ic_pause_white);
|
||||
lockManager.acquireWifiAndCpu();
|
||||
|
||||
hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
|
||||
@@ -666,13 +675,14 @@ public final class PopupVideoPlayer extends Service {
|
||||
public void onPaused() {
|
||||
super.onPaused();
|
||||
updateNotification(R.drawable.ic_play_arrow_white);
|
||||
showAndAnimateControl(R.drawable.ic_play_arrow_white, false);
|
||||
videoPlayPause.setBackgroundResource(R.drawable.ic_play_arrow_white);
|
||||
lockManager.releaseWifiAndCpu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPausedSeek() {
|
||||
super.onPausedSeek();
|
||||
videoPlayPause.setBackgroundResource(R.drawable.ic_pause_white);
|
||||
updateNotification(R.drawable.ic_play_arrow_white);
|
||||
}
|
||||
|
||||
@@ -680,10 +690,27 @@ public final class PopupVideoPlayer extends Service {
|
||||
public void onCompleted() {
|
||||
super.onCompleted();
|
||||
updateNotification(R.drawable.ic_replay_white);
|
||||
showAndAnimateControl(R.drawable.ic_replay_white, false);
|
||||
videoPlayPause.setBackgroundResource(R.drawable.ic_replay_white);
|
||||
lockManager.releaseWifiAndCpu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showControlsThenHide() {
|
||||
videoPlayPause.setVisibility(View.VISIBLE);
|
||||
super.showControlsThenHide();
|
||||
}
|
||||
|
||||
public void showControls(long duration) {
|
||||
videoPlayPause.setVisibility(View.VISIBLE);
|
||||
super.showControls(duration);
|
||||
}
|
||||
|
||||
public void hideControls(final long duration, long delay) {
|
||||
super.hideControlsAndButton(duration, delay, videoPlayPause);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@@ -717,6 +744,8 @@ public final class PopupVideoPlayer extends Service {
|
||||
Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
|
||||
if (playerImpl == null || !playerImpl.isPlaying()) return false;
|
||||
|
||||
playerImpl.hideControls(0, 0);
|
||||
|
||||
if (e.getX() > popupWidth / 2) {
|
||||
playerImpl.onFastForward();
|
||||
} else {
|
||||
@@ -730,7 +759,12 @@ public final class PopupVideoPlayer extends Service {
|
||||
public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||
if (DEBUG) Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]");
|
||||
if (playerImpl == null || playerImpl.getPlayer() == null) return false;
|
||||
playerImpl.onPlayPause();
|
||||
if (playerImpl.isControlsVisible()) {
|
||||
playerImpl.hideControls(100, 100);
|
||||
} else {
|
||||
playerImpl.showControlsThenHide();
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,14 +29,14 @@ import com.google.android.exoplayer2.Player;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
||||
import org.schabi.newpipe.fragments.local.dialog.PlaylistAppendDialog;
|
||||
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
||||
import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||
import org.schabi.newpipe.player.helper.PlaybackParameterDialog;
|
||||
import org.schabi.newpipe.playlist.PlayQueueAdapter;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItemBuilder;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItemHolder;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItemTouchCallback;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueAdapter;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItemBuilder;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
@@ -68,7 +68,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
import org.schabi.newpipe.util.AnimationUtils;
|
||||
import org.schabi.newpipe.util.ListHelper;
|
||||
|
||||
@@ -887,6 +887,19 @@ public abstract class VideoPlayer extends BasePlayer
|
||||
() -> animateView(controlsRoot, false, duration), delay);
|
||||
}
|
||||
|
||||
public void hideControlsAndButton(final long duration, long delay, View button) {
|
||||
if (DEBUG) Log.d(TAG, "hideControls() called with: delay = [" + delay + "]");
|
||||
controlsVisibilityHandler.removeCallbacksAndMessages(null);
|
||||
controlsVisibilityHandler.postDelayed(hideControlsAndButtonHandler(duration, button), delay);
|
||||
}
|
||||
|
||||
private Runnable hideControlsAndButtonHandler(long duration, View videoPlayPause)
|
||||
{
|
||||
return () -> {
|
||||
videoPlayPause.setVisibility(View.INVISIBLE);
|
||||
animateView(controlsRoot, false,duration);
|
||||
};
|
||||
}
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Getters and Setters
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@@ -11,7 +11,6 @@ import android.view.KeyEvent;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
|
||||
|
||||
import org.schabi.newpipe.player.mediasession.DummyPlaybackPreparer;
|
||||
import org.schabi.newpipe.player.mediasession.MediaSessionCallback;
|
||||
import org.schabi.newpipe.player.mediasession.PlayQueueNavigator;
|
||||
import org.schabi.newpipe.player.mediasession.PlayQueuePlaybackController;
|
||||
@@ -26,10 +25,12 @@ public class MediaSessionManager {
|
||||
@NonNull final Player player,
|
||||
@NonNull final MediaSessionCallback callback) {
|
||||
this.mediaSession = new MediaSessionCompat(context, TAG);
|
||||
this.mediaSession.setActive(true);
|
||||
|
||||
this.sessionConnector = new MediaSessionConnector(mediaSession,
|
||||
new PlayQueuePlaybackController(callback));
|
||||
this.sessionConnector.setQueueNavigator(new PlayQueueNavigator(mediaSession, callback));
|
||||
this.sessionConnector.setPlayer(player, new DummyPlaybackPreparer());
|
||||
this.sessionConnector.setPlayer(player, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -37,4 +38,11 @@ public class MediaSessionManager {
|
||||
public KeyEvent handleMediaButtonIntent(final Intent intent) {
|
||||
return MediaButtonReceiver.handleIntent(mediaSession, intent);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
this.sessionConnector.setPlayer(null, null);
|
||||
this.sessionConnector.setQueueNavigator(null);
|
||||
this.mediaSession.setActive(false);
|
||||
this.mediaSession.release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import android.support.annotation.Nullable;
|
||||
import android.view.accessibility.CaptioningManager;
|
||||
|
||||
import com.google.android.exoplayer2.SeekParameters;
|
||||
import com.google.android.exoplayer2.text.CaptionStyleCompat;
|
||||
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.text.CaptionStyleCompat;
|
||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||
import com.google.android.exoplayer2.upstream.BandwidthMeter;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
@@ -24,9 +24,9 @@ import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.SubtitlesFormat;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
@@ -37,6 +37,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FILL;
|
||||
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT;
|
||||
@@ -260,6 +261,16 @@ public class PlayerHelper {
|
||||
|
||||
return captioningManager.getFontScale();
|
||||
}
|
||||
|
||||
public static float getScreenBrightness(@NonNull final Context context) {
|
||||
//a value of less than 0, the default, means to use the preferred screen brightness
|
||||
return getScreenBrightness(context, -1);
|
||||
}
|
||||
|
||||
public static void setScreenBrightness(@NonNull final Context context, final float setScreenBrightness) {
|
||||
setScreenBrightness(context, setScreenBrightness, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Private helpers
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
@@ -292,4 +303,23 @@ public class PlayerHelper {
|
||||
private static boolean isAutoQueueEnabled(@NonNull final Context context, final boolean b) {
|
||||
return getPreferences(context).getBoolean(context.getString(R.string.auto_queue_key), b);
|
||||
}
|
||||
|
||||
private static void setScreenBrightness(@NonNull final Context context, final float screenBrightness, final long timestamp) {
|
||||
SharedPreferences.Editor editor = getPreferences(context).edit();
|
||||
editor.putFloat(context.getString(R.string.screen_brightness_key), screenBrightness);
|
||||
editor.putLong(context.getString(R.string.screen_brightness_timestamp_key), timestamp);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private static float getScreenBrightness(@NonNull final Context context, final float screenBrightness) {
|
||||
SharedPreferences sp = getPreferences(context);
|
||||
long timestamp = sp.getLong(context.getString(R.string.screen_brightness_timestamp_key), 0);
|
||||
// hypothesis: 4h covers a viewing block, eg evening. External lightning conditions will change in the next
|
||||
// viewing block so we fall back to the default brightness
|
||||
if ((System.currentTimeMillis() - timestamp) > TimeUnit.HOURS.toMillis(4)) {
|
||||
return screenBrightness;
|
||||
} else {
|
||||
return sp.getFloat(context.getString(R.string.screen_brightness_key), screenBrightness);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package org.schabi.newpipe.player.mediasession;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.ResultReceiver;
|
||||
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
|
||||
|
||||
public class DummyPlaybackPreparer implements MediaSessionConnector.PlaybackPreparer {
|
||||
@Override
|
||||
public long getSupportedPrepareActions() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepare() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareFromMediaId(String mediaId, Bundle extras) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareFromSearch(String query, Bundle extras) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareFromUri(Uri uri, Bundle extras) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getCommands() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommand(Player player, String command, Bundle extras, ResultReceiver cb) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -13,5 +13,4 @@ public interface MediaSessionCallback {
|
||||
|
||||
void onPlay();
|
||||
void onPause();
|
||||
void onSetShuffle(final boolean isShuffled);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package org.schabi.newpipe.player.mediasession;
|
||||
|
||||
import android.support.v4.media.session.PlaybackStateCompat;
|
||||
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.ext.mediasession.DefaultPlaybackController;
|
||||
|
||||
@@ -22,10 +20,4 @@ public class PlayQueuePlaybackController extends DefaultPlaybackController {
|
||||
public void onPause(Player player) {
|
||||
callback.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetShuffleMode(Player player, int shuffleMode) {
|
||||
callback.onSetShuffle(shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_ALL
|
||||
|| shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_GROUP);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import android.support.annotation.NonNull;
|
||||
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
|
||||
public interface ManagedMediaSource extends MediaSource {
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import android.support.v4.media.MediaDescriptionCompat;
|
||||
|
||||
import org.schabi.newpipe.player.BasePlayer;
|
||||
import org.schabi.newpipe.player.mediasession.MediaSessionCallback;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
|
||||
public class BasePlayerMediaSession implements MediaSessionCallback {
|
||||
private BasePlayer player;
|
||||
@@ -69,9 +69,4 @@ public class BasePlayerMediaSession implements MediaSessionCallback {
|
||||
public void onPause() {
|
||||
player.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetShuffle(boolean isShuffled) {
|
||||
player.onShuffleModeEnabledChanged(isShuffled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,12 @@ import org.schabi.newpipe.player.mediasource.LoadedMediaSource;
|
||||
import org.schabi.newpipe.player.mediasource.ManagedMediaSource;
|
||||
import org.schabi.newpipe.player.mediasource.ManagedMediaSourcePlaylist;
|
||||
import org.schabi.newpipe.player.mediasource.PlaceholderMediaSource;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.playlist.events.MoveEvent;
|
||||
import org.schabi.newpipe.playlist.events.PlayQueueEvent;
|
||||
import org.schabi.newpipe.playlist.events.RemoveEvent;
|
||||
import org.schabi.newpipe.playlist.events.ReorderEvent;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.events.MoveEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.PlayQueueEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.RemoveEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.ReorderEvent;
|
||||
import org.schabi.newpipe.util.ServiceHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -45,7 +45,7 @@ import io.reactivex.subjects.PublishSubject;
|
||||
|
||||
import static org.schabi.newpipe.player.mediasource.FailedMediaSource.MediaSourceResolutionException;
|
||||
import static org.schabi.newpipe.player.mediasource.FailedMediaSource.StreamInfoLoadException;
|
||||
import static org.schabi.newpipe.playlist.PlayQueue.DEBUG;
|
||||
import static org.schabi.newpipe.player.playqueue.PlayQueue.DEBUG;
|
||||
|
||||
public class MediaSourceManager {
|
||||
@NonNull private final String TAG = "MediaSourceManager@" + hashCode();
|
||||
|
||||
@@ -6,7 +6,7 @@ import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
package org.schabi.newpipe.player.playqueue;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
package org.schabi.newpipe.player.playqueue;
|
||||
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
package org.schabi.newpipe.player.playqueue;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@@ -6,15 +6,15 @@ import android.util.Log;
|
||||
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import org.schabi.newpipe.playlist.events.AppendEvent;
|
||||
import org.schabi.newpipe.playlist.events.ErrorEvent;
|
||||
import org.schabi.newpipe.playlist.events.InitEvent;
|
||||
import org.schabi.newpipe.playlist.events.MoveEvent;
|
||||
import org.schabi.newpipe.playlist.events.PlayQueueEvent;
|
||||
import org.schabi.newpipe.playlist.events.RecoveryEvent;
|
||||
import org.schabi.newpipe.playlist.events.RemoveEvent;
|
||||
import org.schabi.newpipe.playlist.events.ReorderEvent;
|
||||
import org.schabi.newpipe.playlist.events.SelectEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.AppendEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.ErrorEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.InitEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.MoveEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.PlayQueueEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.RecoveryEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.RemoveEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.ReorderEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.SelectEvent;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
package org.schabi.newpipe.player.playqueue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
@@ -8,12 +8,13 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.playlist.events.AppendEvent;
|
||||
import org.schabi.newpipe.playlist.events.ErrorEvent;
|
||||
import org.schabi.newpipe.playlist.events.MoveEvent;
|
||||
import org.schabi.newpipe.playlist.events.PlayQueueEvent;
|
||||
import org.schabi.newpipe.playlist.events.RemoveEvent;
|
||||
import org.schabi.newpipe.playlist.events.SelectEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.AppendEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.ErrorEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.MoveEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.PlayQueueEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.RemoveEvent;
|
||||
import org.schabi.newpipe.player.playqueue.events.SelectEvent;
|
||||
import org.schabi.newpipe.util.FallbackViewHolder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -188,7 +189,7 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
||||
return new PlayQueueItemHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.play_queue_item, parent, false));
|
||||
default:
|
||||
Log.e(TAG, "Attempting to create view holder with undefined type: " + type);
|
||||
return null;
|
||||
return new FallbackViewHolder(new View(parent.getContext()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
package org.schabi.newpipe.player.playqueue;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
package org.schabi.newpipe.player.playqueue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
package org.schabi.newpipe.player.playqueue;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
package org.schabi.newpipe.player.playqueue;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
package org.schabi.newpipe.player.playqueue;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
package org.schabi.newpipe.player.playqueue;
|
||||
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist.events;
|
||||
package org.schabi.newpipe.player.playqueue.events;
|
||||
|
||||
|
||||
public class AppendEvent implements PlayQueueEvent {
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist.events;
|
||||
package org.schabi.newpipe.player.playqueue.events;
|
||||
|
||||
|
||||
public class ErrorEvent implements PlayQueueEvent {
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist.events;
|
||||
package org.schabi.newpipe.player.playqueue.events;
|
||||
|
||||
public class InitEvent implements PlayQueueEvent {
|
||||
@Override
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist.events;
|
||||
package org.schabi.newpipe.player.playqueue.events;
|
||||
|
||||
public class MoveEvent implements PlayQueueEvent {
|
||||
final private int fromIndex;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist.events;
|
||||
package org.schabi.newpipe.player.playqueue.events;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist.events;
|
||||
package org.schabi.newpipe.player.playqueue.events;
|
||||
|
||||
public enum PlayQueueEventType {
|
||||
INIT,
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist.events;
|
||||
package org.schabi.newpipe.player.playqueue.events;
|
||||
|
||||
|
||||
public class RecoveryEvent implements PlayQueueEvent {
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist.events;
|
||||
package org.schabi.newpipe.player.playqueue.events;
|
||||
|
||||
|
||||
public class RemoveEvent implements PlayQueueEvent {
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist.events;
|
||||
package org.schabi.newpipe.player.playqueue.events;
|
||||
|
||||
public class ReorderEvent implements PlayQueueEvent {
|
||||
private final int fromSelectedIndex;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.playlist.events;
|
||||
package org.schabi.newpipe.player.playqueue.events;
|
||||
|
||||
|
||||
public class SelectEvent implements PlayQueueEvent {
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.schabi.newpipe.report;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
@@ -33,10 +35,8 @@ import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.schabi.newpipe.ActivityCommunicator;
|
||||
import org.schabi.newpipe.BuildConfig;
|
||||
import org.schabi.newpipe.Downloader;
|
||||
import org.schabi.newpipe.MainActivity;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.utils.Parser;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -44,9 +44,9 @@ import java.io.StringWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/*
|
||||
* Created by Christian Schabesberger on 24.10.15.
|
||||
@@ -96,7 +96,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
public static void reportError(final Context context, final List<Throwable> el,
|
||||
final Class returnActivity, View rootView, final ErrorInfo errorInfo) {
|
||||
if (rootView != null) {
|
||||
Snackbar.make(rootView, R.string.error_snackbar_message, 15 * 1000)
|
||||
Snackbar.make(rootView, R.string.error_snackbar_message, 3 * 1000)
|
||||
.setActionTextColor(Color.YELLOW)
|
||||
.setAction(R.string.error_snackbar_action, v ->
|
||||
startErrorActivity(returnActivity, context, errorInfo, el)).show();
|
||||
@@ -210,12 +210,31 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
currentTimeStamp = getCurrentTimeStamp();
|
||||
|
||||
reportButton.setOnClickListener((View v) -> {
|
||||
Intent i = new Intent(Intent.ACTION_SENDTO);
|
||||
i.setData(Uri.parse("mailto:" + ERROR_EMAIL_ADDRESS))
|
||||
.putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT)
|
||||
.putExtra(Intent.EXTRA_TEXT, buildJson());
|
||||
Context context = this;
|
||||
new AlertDialog.Builder(context)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setTitle(R.string.privacy_policy_title)
|
||||
.setMessage(R.string.start_accept_privacy_policy)
|
||||
.setCancelable(false)
|
||||
.setNeutralButton(R.string.read_privacy_policy, (dialog, which) -> {
|
||||
Intent webIntent = new Intent(Intent.ACTION_VIEW,
|
||||
Uri.parse(context.getString(R.string.privacy_policy_url))
|
||||
);
|
||||
context.startActivity(webIntent);
|
||||
})
|
||||
.setPositiveButton(R.string.accept, (dialog, which) -> {
|
||||
Intent i = new Intent(Intent.ACTION_SENDTO);
|
||||
i.setData(Uri.parse("mailto:" + ERROR_EMAIL_ADDRESS))
|
||||
.putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT)
|
||||
.putExtra(Intent.EXTRA_TEXT, buildJson());
|
||||
|
||||
startActivity(Intent.createChooser(i, "Send Email"));
|
||||
})
|
||||
.setNegativeButton(R.string.decline, (dialog, which) -> {
|
||||
// do nothing
|
||||
})
|
||||
.show();
|
||||
|
||||
startActivity(Intent.createChooser(i, "Send Email"));
|
||||
});
|
||||
|
||||
// normal bugreport
|
||||
|
||||
@@ -14,7 +14,8 @@ public enum UserAction {
|
||||
REQUESTED_STREAM("requested stream"),
|
||||
REQUESTED_CHANNEL("requested channel"),
|
||||
REQUESTED_PLAYLIST("requested playlist"),
|
||||
REQUESTED_KIOSK("requested kiosk");
|
||||
REQUESTED_KIOSK("requested kiosk"),
|
||||
DELETE_FROM_HISTORY("delete from history");
|
||||
|
||||
|
||||
private final String message;
|
||||
|
||||
@@ -4,7 +4,9 @@ import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.preference.ListPreference;
|
||||
@@ -30,15 +32,21 @@ import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
|
||||
public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||
|
||||
private static final int REQUEST_IMPORT_PATH = 8945;
|
||||
@@ -48,6 +56,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||
private File databasesDir;
|
||||
private File newpipe_db;
|
||||
private File newpipe_db_journal;
|
||||
private File newpipe_settings;
|
||||
|
||||
private String thumbnailLoadToggleKey;
|
||||
|
||||
@@ -79,6 +88,8 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||
databasesDir = new File(homeDir + "/databases");
|
||||
newpipe_db = new File(homeDir + "/databases/newpipe.db");
|
||||
newpipe_db_journal = new File(homeDir + "/databases/newpipe.db-journal");
|
||||
newpipe_settings = new File(homeDir + "/databases/newpipe.settings");
|
||||
newpipe_settings.delete();
|
||||
|
||||
addPreferencesFromResource(R.xml.content_settings);
|
||||
|
||||
@@ -174,19 +185,19 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||
|
||||
if ((requestCode == REQUEST_IMPORT_PATH || requestCode == REQUEST_EXPORT_PATH)
|
||||
&& resultCode == Activity.RESULT_OK && data.getData() != null) {
|
||||
String path = Utils.getFileForUri(data.getData()).getAbsolutePath();
|
||||
if (requestCode == REQUEST_EXPORT_PATH) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
|
||||
exportDatabase(path + "/NewPipeData-" + sdf.format(new Date()) + ".zip");
|
||||
} else {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setMessage(R.string.override_current_data)
|
||||
.setPositiveButton(android.R.string.ok,
|
||||
(DialogInterface d, int id) -> importDatabase(path))
|
||||
.setNegativeButton(android.R.string.cancel,
|
||||
(DialogInterface d, int id) -> d.cancel());
|
||||
builder.create().show();
|
||||
}
|
||||
String path = Utils.getFileForUri(data.getData()).getAbsolutePath();
|
||||
if (requestCode == REQUEST_EXPORT_PATH) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
|
||||
exportDatabase(path + "/NewPipeData-" + sdf.format(new Date()) + ".zip");
|
||||
} else {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setMessage(R.string.override_current_data)
|
||||
.setPositiveButton(android.R.string.ok,
|
||||
(DialogInterface d, int id) -> importDatabase(path))
|
||||
.setNegativeButton(android.R.string.cancel,
|
||||
(DialogInterface d, int id) -> d.cancel());
|
||||
builder.create().show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,6 +208,8 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||
new FileOutputStream(path)));
|
||||
ZipHelper.addFileToZip(outZip, newpipe_db.getPath(), "newpipe.db");
|
||||
ZipHelper.addFileToZip(outZip, newpipe_db_journal.getPath(), "newpipe.db-journal");
|
||||
saveSharedPreferencesToFile(newpipe_settings);
|
||||
ZipHelper.addFileToZip(outZip, newpipe_settings.getPath(), "newpipe.settings");
|
||||
|
||||
outZip.close();
|
||||
|
||||
@@ -207,6 +220,29 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void saveSharedPreferencesToFile(File dst) {
|
||||
ObjectOutputStream output = null;
|
||||
try {
|
||||
output = new ObjectOutputStream(new FileOutputStream(dst));
|
||||
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
output.writeObject(pref.getAll());
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
try {
|
||||
if (output != null) {
|
||||
output.flush();
|
||||
output.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void importDatabase(String filePath) {
|
||||
// check if file is supported
|
||||
ZipFile zipFile = null;
|
||||
@@ -223,30 +259,83 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||
}
|
||||
|
||||
try {
|
||||
ZipInputStream zipIn = new ZipInputStream(
|
||||
new BufferedInputStream(
|
||||
new FileInputStream(filePath)));
|
||||
|
||||
if (!databasesDir.exists() && !databasesDir.mkdir()) {
|
||||
throw new Exception("Could not create databases dir");
|
||||
}
|
||||
|
||||
if(!(ZipHelper.extractFileFromZip(zipIn, newpipe_db.getPath(), "newpipe.db")
|
||||
&& ZipHelper.extractFileFromZip(zipIn, newpipe_db_journal.getPath(), "newpipe.db-journal"))) {
|
||||
Toast.makeText(getContext(), R.string.could_not_import_all_files, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
if(!(ZipHelper.extractFileFromZip(filePath, newpipe_db.getPath(), "newpipe.db")
|
||||
&& ZipHelper.extractFileFromZip(filePath, newpipe_db_journal.getPath(), "newpipe.db-journal"))) {
|
||||
Toast.makeText(getContext(), R.string.could_not_import_all_files, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
|
||||
zipIn.close();
|
||||
//If settings file exist, ask if it should be imported.
|
||||
if(ZipHelper.extractFileFromZip(filePath, newpipe_settings.getPath(), "newpipe.settings")) {
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
|
||||
alert.setTitle(R.string.import_settings);
|
||||
|
||||
alert.setNegativeButton(android.R.string.no, (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
// restart app to properly load db
|
||||
System.exit(0);
|
||||
});
|
||||
alert.setPositiveButton(android.R.string.yes, (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
loadSharedPreferences(newpipe_settings);
|
||||
// restart app to properly load db
|
||||
System.exit(0);
|
||||
});
|
||||
alert.show();
|
||||
} else {
|
||||
// restart app to properly load db
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// restart app to properly load db
|
||||
//App.restart(getContext());
|
||||
System.exit(0);
|
||||
} catch (Exception e) {
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadSharedPreferences(File src) {
|
||||
ObjectInputStream input = null;
|
||||
try {
|
||||
input = new ObjectInputStream(new FileInputStream(src));
|
||||
SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(getContext()).edit();
|
||||
prefEdit.clear();
|
||||
Map<String, ?> entries = (Map<String, ?>) input.readObject();
|
||||
for (Map.Entry<String, ?> entry : entries.entrySet()) {
|
||||
Object v = entry.getValue();
|
||||
String key = entry.getKey();
|
||||
|
||||
if (v instanceof Boolean)
|
||||
prefEdit.putBoolean(key, ((Boolean) v).booleanValue());
|
||||
else if (v instanceof Float)
|
||||
prefEdit.putFloat(key, ((Float) v).floatValue());
|
||||
else if (v instanceof Integer)
|
||||
prefEdit.putInt(key, ((Integer) v).intValue());
|
||||
else if (v instanceof Long)
|
||||
prefEdit.putLong(key, ((Long) v).longValue());
|
||||
else if (v instanceof String)
|
||||
prefEdit.putString(key, ((String) v));
|
||||
}
|
||||
prefEdit.commit();
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
try {
|
||||
if (input != null) {
|
||||
input.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
@@ -1,20 +1,45 @@
|
||||
package org.schabi.newpipe.settings;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.MainActivity;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.InfoCache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.disposables.Disposables;
|
||||
|
||||
public class HistorySettingsFragment extends BasePreferenceFragment {
|
||||
private String cacheWipeKey;
|
||||
private String viewsHistroyClearKey;
|
||||
private String searchHistoryClearKey;
|
||||
private HistoryRecordManager recordManager;
|
||||
private CompositeDisposable disposables;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
cacheWipeKey = getString(R.string.metadata_cache_wipe_key);
|
||||
viewsHistroyClearKey = getString(R.string.clear_views_history_key);
|
||||
searchHistoryClearKey = getString(R.string.clear_search_history_key);
|
||||
recordManager = new HistoryRecordManager(getActivity());
|
||||
disposables = new CompositeDisposable();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -30,6 +55,70 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
if (preference.getKey().equals(viewsHistroyClearKey)) {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.delete_view_history_alert)
|
||||
.setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss()))
|
||||
.setPositiveButton(R.string.delete, ((dialog, which) -> {
|
||||
final Disposable onDelete = recordManager.deleteWholeStreamHistory()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
howManyDeleted -> Toast.makeText(getActivity(),
|
||||
R.string.view_history_deleted,
|
||||
Toast.LENGTH_SHORT).show(),
|
||||
throwable -> ErrorActivity.reportError(getContext(),
|
||||
throwable,
|
||||
SettingsActivity.class, null,
|
||||
ErrorActivity.ErrorInfo.make(
|
||||
UserAction.DELETE_FROM_HISTORY,
|
||||
"none",
|
||||
"Delete view history",
|
||||
R.string.general_error)));
|
||||
|
||||
final Disposable onClearOrphans = recordManager.removeOrphanedRecords()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
howManyDeleted -> {},
|
||||
throwable -> ErrorActivity.reportError(getContext(),
|
||||
throwable,
|
||||
SettingsActivity.class, null,
|
||||
ErrorActivity.ErrorInfo.make(
|
||||
UserAction.DELETE_FROM_HISTORY,
|
||||
"none",
|
||||
"Delete search history",
|
||||
R.string.general_error)));
|
||||
disposables.add(onClearOrphans);
|
||||
disposables.add(onDelete);
|
||||
}))
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
if (preference.getKey().equals(searchHistoryClearKey)) {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.delete_search_history_alert)
|
||||
.setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss()))
|
||||
.setPositiveButton(R.string.delete, ((dialog, which) -> {
|
||||
final Disposable onDelete = recordManager.deleteWholeSearchHistory()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
howManyDeleted -> Toast.makeText(getActivity(),
|
||||
R.string.search_history_deleted,
|
||||
Toast.LENGTH_SHORT).show(),
|
||||
throwable -> ErrorActivity.reportError(getContext(),
|
||||
throwable,
|
||||
SettingsActivity.class, null,
|
||||
ErrorActivity.ErrorInfo.make(
|
||||
UserAction.DELETE_FROM_HISTORY,
|
||||
"none",
|
||||
"Delete search history",
|
||||
R.string.general_error)));
|
||||
disposables.add(onDelete);
|
||||
}))
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
return super.onPreferenceTreeClick(preference);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user