1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2026-02-08 19:20:16 +00:00

Compare commits

...

15 Commits

Author SHA1 Message Date
Aayush Gupta
83748c2d8e FocusOverlayView: Avoid accessing restricted API
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
Aayush Gupta
e0e86b76ff ic_smart_display: Fix invalid vector path on older devices
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
Aayush Gupta
a7a372406b lint: Supress more translation related errors
They should be fixed by translators or weblate in general.

 ../../src/main/res/values-lt/strings.xml:87: For locale "lt" (Lithuanian) the following quantity should also be defined: many (e.g. "1.1 obuolio")
 ../../src/main/res/values-is/strings.xml:318: The quantity 'one' matches more than one specific number in this locale (1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …), but the message did not include a formatting argument (such as %d). This is usually an internationalization error. See full issue explanation for more.

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
Aayush Gupta
9a3069b86f DownloadDialog: Avoid using restricted API for menuitem
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
Aayush Gupta
c3f89b758c activity_player_queue_control: Switch to app:tint instead of android:tint
../../src/main/res/layout/activity_player_queue_control.xml:208: Must use app:tint instead of android:tint

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
Aayush Gupta
4f676f8646 DownloadRunnableFallback: Fix error with log tag being too long
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
Aayush Gupta
a0453e1299 SearchFragment: Fix hint translation
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
Aayush Gupta
d1d92d42c0 Use correct constant for hiding keyboard
../../src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt:509: Must be one or more of: InputMethodManager.HIDE_IMPLICIT_ONLY, InputMethodManager.HIDE_NOT_ALWAYS

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
Aayush Gupta
b494dd557d Use requireContext() instead of asserting non-null context
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
Aayush Gupta
e0768b7307 FinishedMIssionStore: Throw exception if column is missing
../../src/main/java/us/shandian/giga/get/sqlite/FinishedMissionStore.java:105: Value must be ≥ 0 but getColumnIndex can be -1

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
Aayush Gupta
8ba26f567b ReCaptchaActivity: Supress lint error for missing super call
saveCookiesAndFinish method handles back navigation

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
Aayush Gupta
b79c6fecce Add missing permission checks for notifications
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
Aayush Gupta
edb0ce67b7 Introduce lint configuration and enable checks
Supress missing translation errors as they are done by volunteers

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
Aayush Gupta
dd71e479ac Enable resources shrinking
AGP 9.0.0 has introduced additional resource shrinking tasks. Its better
to enable and fix this.

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
Aayush Gupta
df16dc4b21 Address non-final resource IDs warnings
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-02-08 16:54:54 +08:00
30 changed files with 492 additions and 460 deletions

View File

@@ -79,7 +79,7 @@ configure<ApplicationExtension> {
resValue("string", "app_name", "NewPipe $suffix") resValue("string", "app_name", "NewPipe $suffix")
} }
isMinifyEnabled = true isMinifyEnabled = true
isShrinkResources = false // disabled to fix F-Droid"s reproducible build isShrinkResources = true
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro" "proguard-rules.pro"
@@ -88,13 +88,7 @@ configure<ApplicationExtension> {
} }
lint { lint {
checkReleaseBuilds = false lintConfig = file("lint.xml")
// Or, if you prefer, you can continue to check for errors in release builds,
// but continue the build even when errors are found:
abortOnError = false
// suppress false warning ("Resource IDs will be non-final in Android Gradle Plugin version
// 5.0, avoid using them in switch case statements"), which affects only library projects
disable += "NonConstantResourceId"
} }
compileOptions { compileOptions {

10
app/lint.xml Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
~ SPDX-License-Identifier: GPL-3.0-or-later
-->
<lint>
<issue id="MissingTranslation" severity="ignore" />
<issue id="MissingQuantity" severity="ignore" />
<issue id="ImpliedQuantity" severity="ignore" />
</lint>

View File

@@ -309,25 +309,21 @@ public class MainActivity extends AppCompatActivity {
} }
private boolean drawerItemSelected(final MenuItem item) { private boolean drawerItemSelected(final MenuItem item) {
switch (item.getGroupId()) { final int groupId = item.getGroupId();
case R.id.menu_services_group: if (groupId == R.id.menu_services_group) {
changeService(item); changeService(item);
break; } else if (groupId == R.id.menu_tabs_group) {
case R.id.menu_tabs_group: tabSelected(item);
tabSelected(item); } else if (groupId == R.id.menu_kiosks_group) {
break; try {
case R.id.menu_kiosks_group: kioskSelected(item);
try { } catch (final Exception e) {
kioskSelected(item); ErrorUtil.showUiErrorSnackbar(this, "Selecting drawer kiosk", e);
} catch (final Exception e) { }
ErrorUtil.showUiErrorSnackbar(this, "Selecting drawer kiosk", e); } else if (groupId == R.id.menu_options_about_group) {
} optionsAboutSelected(item);
break; } else {
case R.id.menu_options_about_group: return false;
optionsAboutSelected(item);
break;
default:
return false;
} }
mainBinding.getRoot().closeDrawers(); mainBinding.getRoot().closeDrawers();

View File

@@ -82,7 +82,9 @@ class NewVersionWorker(
) )
val notificationManager = NotificationManagerCompat.from(applicationContext) val notificationManager = NotificationManagerCompat.from(applicationContext)
notificationManager.notify(2000, notificationBuilder.build()) if (notificationManager.areNotificationsEnabled()) {
notificationManager.notify(2000, notificationBuilder.build())
}
} }
@Throws(IOException::class, ReCaptchaException::class) @Throws(IOException::class, ReCaptchaException::class)

View File

@@ -41,50 +41,50 @@ public final class QueueItemMenuUtil {
} }
popupMenu.setOnMenuItemClickListener(menuItem -> { popupMenu.setOnMenuItemClickListener(menuItem -> {
switch (menuItem.getItemId()) { final int itemId = menuItem.getItemId();
case R.id.menu_item_remove: if (itemId == R.id.menu_item_remove) {
final int index = playQueue.indexOf(item); final int index = playQueue.indexOf(item);
playQueue.remove(index); playQueue.remove(index);
return true; return true;
case R.id.menu_item_details: } else if (itemId == R.id.menu_item_details) {
// playQueue is null since we don't want any queue change // playQueue is null since we don't want any queue change
NavigationHelper.openVideoDetail(context, item.getServiceId(), NavigationHelper.openVideoDetail(context, item.getServiceId(),
item.getUrl(), item.getTitle(), null, item.getUrl(), item.getTitle(), null,
false); false);
return true; return true;
case R.id.menu_item_append_playlist: } else if (itemId == R.id.menu_item_append_playlist) {
PlaylistDialog.createCorrespondingDialog( PlaylistDialog.createCorrespondingDialog(
context, context,
List.of(new StreamEntity(item)), List.of(new StreamEntity(item)),
dialog -> dialog.show( dialog -> dialog.show(
fragmentManager, fragmentManager,
"QueueItemMenuUtil@append_playlist" "QueueItemMenuUtil@append_playlist"
) )
); );
return true; return true;
case R.id.menu_item_channel_details: } else if (itemId == R.id.menu_item_channel_details) {
SparseItemUtil.fetchUploaderUrlIfSparse(context, item.getServiceId(), SparseItemUtil.fetchUploaderUrlIfSparse(context, item.getServiceId(),
item.getUrl(), item.getUploaderUrl(), item.getUrl(), item.getUploaderUrl(),
// An intent must be used here. // An intent must be used here.
// Opening with FragmentManager transactions is not working, // Opening with FragmentManager transactions is not working,
// as PlayQueueActivity doesn't use fragments. // as PlayQueueActivity doesn't use fragments.
uploaderUrl -> NavigationHelper.openChannelFragmentUsingIntent( uploaderUrl -> NavigationHelper.openChannelFragmentUsingIntent(
context, item.getServiceId(), uploaderUrl, item.getUploader() context, item.getServiceId(), uploaderUrl, item.getUploader()
)); ));
return true; return true;
case R.id.menu_item_share: } else if (itemId == R.id.menu_item_share) {
shareText(context, item.getTitle(), item.getUrl(), shareText(context, item.getTitle(), item.getUrl(),
item.getThumbnails()); item.getThumbnails());
return true; return true;
case R.id.menu_item_download: } else if (itemId == R.id.menu_item_download) {
fetchStreamInfoAndSaveToDatabase(context, item.getServiceId(), item.getUrl(), fetchStreamInfoAndSaveToDatabase(context, item.getServiceId(), item.getUrl(),
info -> { info -> {
final DownloadDialog downloadDialog = new DownloadDialog(context, final DownloadDialog downloadDialog = new DownloadDialog(context,
info); info);
downloadDialog.show(fragmentManager, "downloadDialog"); downloadDialog.show(fragmentManager, "downloadDialog");
}); });
return true; return true;
} }
return false; return false;
}); });

View File

@@ -16,6 +16,7 @@ import android.os.IBinder;
import android.provider.Settings; import android.provider.Settings;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
@@ -31,7 +32,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.view.menu.ActionMenuItemView;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.collection.SparseArrayCompat; import androidx.collection.SparseArrayCompat;
import androidx.documentfile.provider.DocumentFile; import androidx.documentfile.provider.DocumentFile;
@@ -113,7 +113,7 @@ public class DownloadDialog extends DialogFragment
private StoredDirectoryHelper mainStorageAudio = null; private StoredDirectoryHelper mainStorageAudio = null;
private StoredDirectoryHelper mainStorageVideo = null; private StoredDirectoryHelper mainStorageVideo = null;
private DownloadManager downloadManager = null; private DownloadManager downloadManager = null;
private ActionMenuItemView okButton = null; private MenuItem okButton = null;
private Context context = null; private Context context = null;
private boolean askForSavePath; private boolean askForSavePath;
@@ -558,17 +558,13 @@ public class DownloadDialog extends DialogFragment
} }
boolean flag = true; boolean flag = true;
switch (checkedId) { if (checkedId == R.id.audio_button) {
case R.id.audio_button: setupAudioSpinner();
setupAudioSpinner(); } else if (checkedId == R.id.video_button) {
break; setupVideoSpinner();
case R.id.video_button: } else if (checkedId == R.id.subtitle_button) {
setupVideoSpinner(); setupSubtitleSpinner();
break; flag = false;
case R.id.subtitle_button:
setupSubtitleSpinner();
flag = false;
break;
} }
dialogBinding.threads.setEnabled(flag); dialogBinding.threads.setEnabled(flag);
@@ -585,29 +581,26 @@ public class DownloadDialog extends DialogFragment
+ "position = [" + position + "], id = [" + id + "]"); + "position = [" + position + "], id = [" + id + "]");
} }
switch (parent.getId()) { final int parentId = parent.getId();
case R.id.quality_spinner: if (parentId == R.id.quality_spinner) {
switch (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()) { final int checkedRadioButtonId = dialogBinding.videoAudioGroup
case R.id.video_button: .getCheckedRadioButtonId();
selectedVideoIndex = position; if (checkedRadioButtonId == R.id.video_button) {
onVideoStreamSelected(); selectedVideoIndex = position;
break; onVideoStreamSelected();
case R.id.subtitle_button: } else if (checkedRadioButtonId == R.id.subtitle_button) {
selectedSubtitleIndex = position; selectedSubtitleIndex = position;
break; }
} onItemSelectedSetFileName();
onItemSelectedSetFileName(); } else if (parentId == R.id.audio_track_spinner) {
break; final boolean trackChanged = selectedAudioTrackIndex != position;
case R.id.audio_track_spinner: selectedAudioTrackIndex = position;
final boolean trackChanged = selectedAudioTrackIndex != position; if (trackChanged) {
selectedAudioTrackIndex = position; updateSecondaryStreams();
if (trackChanged) { fetchStreamsSize();
updateSecondaryStreams(); }
fetchStreamsSize(); } else if (parentId == R.id.audio_stream_spinner) {
} selectedAudioIndex = position;
break;
case R.id.audio_stream_spinner:
selectedAudioIndex = position;
} }
} }
@@ -622,23 +615,20 @@ public class DownloadDialog extends DialogFragment
|| prevFileName.startsWith(getString(R.string.caption_file_name, fileName, ""))) { || prevFileName.startsWith(getString(R.string.caption_file_name, fileName, ""))) {
// only update the file name field if it was not edited by the user // only update the file name field if it was not edited by the user
switch (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()) { final int radioButtonId = dialogBinding.videoAudioGroup
case R.id.audio_button: .getCheckedRadioButtonId();
case R.id.video_button: if (radioButtonId == R.id.audio_button || radioButtonId == R.id.video_button) {
if (!prevFileName.equals(fileName)) { if (!prevFileName.equals(fileName)) {
// since the user might have switched between audio and video, the correct // since the user might have switched between audio and video, the correct
// text might already be in place, so avoid resetting the cursor position // text might already be in place, so avoid resetting the cursor position
dialogBinding.fileName.setText(fileName); dialogBinding.fileName.setText(fileName);
} }
break; } else if (radioButtonId == R.id.subtitle_button) {
final String setSubtitleLanguageCode = subtitleStreamsAdapter
case R.id.subtitle_button: .getItem(selectedSubtitleIndex).getLanguageTag();
final String setSubtitleLanguageCode = subtitleStreamsAdapter // this will reset the cursor position, which is bad UX, but it can't be avoided
.getItem(selectedSubtitleIndex).getLanguageTag(); dialogBinding.fileName.setText(getString(
// this will reset the cursor position, which is bad UX, but it can't be avoided R.string.caption_file_name, fileName, setSubtitleLanguageCode));
dialogBinding.fileName.setText(getString(
R.string.caption_file_name, fileName, setSubtitleLanguageCode));
break;
} }
} }
} }
@@ -770,47 +760,44 @@ public class DownloadDialog extends DialogFragment
filenameTmp = getNameEditText().concat("."); filenameTmp = getNameEditText().concat(".");
switch (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()) { final int checkedRadioButtonId = dialogBinding.videoAudioGroup.getCheckedRadioButtonId();
case R.id.audio_button: if (checkedRadioButtonId == R.id.audio_button) {
selectedMediaType = getString(R.string.last_download_type_audio_key); selectedMediaType = getString(R.string.last_download_type_audio_key);
mainStorage = mainStorageAudio; mainStorage = mainStorageAudio;
format = audioStreamsAdapter.getItem(selectedAudioIndex).getFormat(); format = audioStreamsAdapter.getItem(selectedAudioIndex).getFormat();
size = getWrappedAudioStreams().getSizeInBytes(selectedAudioIndex); size = getWrappedAudioStreams().getSizeInBytes(selectedAudioIndex);
if (format == MediaFormat.WEBMA_OPUS) { if (format == MediaFormat.WEBMA_OPUS) {
mimeTmp = "audio/ogg"; mimeTmp = "audio/ogg";
filenameTmp += "opus"; filenameTmp += "opus";
} else if (format != null) { } else if (format != null) {
mimeTmp = format.mimeType; mimeTmp = format.mimeType;
filenameTmp += format.getSuffix(); filenameTmp += format.getSuffix();
} }
break; } else if (checkedRadioButtonId == R.id.video_button) {
case R.id.video_button: selectedMediaType = getString(R.string.last_download_type_video_key);
selectedMediaType = getString(R.string.last_download_type_video_key); mainStorage = mainStorageVideo;
mainStorage = mainStorageVideo; format = videoStreamsAdapter.getItem(selectedVideoIndex).getFormat();
format = videoStreamsAdapter.getItem(selectedVideoIndex).getFormat(); size = wrappedVideoStreams.getSizeInBytes(selectedVideoIndex);
size = wrappedVideoStreams.getSizeInBytes(selectedVideoIndex); if (format != null) {
if (format != null) { mimeTmp = format.mimeType;
mimeTmp = format.mimeType; filenameTmp += format.getSuffix();
filenameTmp += format.getSuffix(); }
} } else if (checkedRadioButtonId == R.id.subtitle_button) {
break; selectedMediaType = getString(R.string.last_download_type_subtitle_key);
case R.id.subtitle_button: mainStorage = mainStorageVideo; // subtitle & video files go together
selectedMediaType = getString(R.string.last_download_type_subtitle_key); format = subtitleStreamsAdapter.getItem(selectedSubtitleIndex).getFormat();
mainStorage = mainStorageVideo; // subtitle & video files go together size = wrappedSubtitleStreams.getSizeInBytes(selectedSubtitleIndex);
format = subtitleStreamsAdapter.getItem(selectedSubtitleIndex).getFormat(); if (format != null) {
size = wrappedSubtitleStreams.getSizeInBytes(selectedSubtitleIndex); mimeTmp = format.mimeType;
if (format != null) { }
mimeTmp = format.mimeType;
}
if (format == MediaFormat.TTML) { if (format == MediaFormat.TTML) {
filenameTmp += MediaFormat.SRT.getSuffix(); filenameTmp += MediaFormat.SRT.getSuffix();
} else if (format != null) { } else if (format != null) {
filenameTmp += format.getSuffix(); filenameTmp += format.getSuffix();
} }
break; } else {
default: throw new RuntimeException("No stream selected");
throw new RuntimeException("No stream selected");
} }
if (!askForSavePath && (mainStorage == null if (!askForSavePath && (mainStorage == null
@@ -1057,59 +1044,56 @@ public class DownloadDialog extends DialogFragment
long nearLength = 0; long nearLength = 0;
// more download logic: select muxer, subtitle converter, etc. // more download logic: select muxer, subtitle converter, etc.
switch (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()) { final int checkedRadioButtonId = dialogBinding.videoAudioGroup.getCheckedRadioButtonId();
case R.id.audio_button: if (checkedRadioButtonId == R.id.audio_button) {
kind = 'a'; kind = 'a';
selectedStream = audioStreamsAdapter.getItem(selectedAudioIndex); selectedStream = audioStreamsAdapter.getItem(selectedAudioIndex);
if (selectedStream.getFormat() == MediaFormat.M4A) { if (selectedStream.getFormat() == MediaFormat.M4A) {
psName = Postprocessing.ALGORITHM_M4A_NO_DASH; psName = Postprocessing.ALGORITHM_M4A_NO_DASH;
} else if (selectedStream.getFormat() == MediaFormat.WEBMA_OPUS) { } else if (selectedStream.getFormat() == MediaFormat.WEBMA_OPUS) {
psName = Postprocessing.ALGORITHM_OGG_FROM_WEBM_DEMUXER; psName = Postprocessing.ALGORITHM_OGG_FROM_WEBM_DEMUXER;
}
} else if (checkedRadioButtonId == R.id.video_button) {
kind = 'v';
selectedStream = videoStreamsAdapter.getItem(selectedVideoIndex);
final SecondaryStreamHelper<AudioStream> secondary = videoStreamsAdapter
.getAllSecondary()
.get(wrappedVideoStreams.getStreamsList().indexOf(selectedStream));
if (secondary != null) {
secondaryStream = secondary.getStream();
if (selectedStream.getFormat() == MediaFormat.MPEG_4) {
psName = Postprocessing.ALGORITHM_MP4_FROM_DASH_MUXER;
} else {
psName = Postprocessing.ALGORITHM_WEBM_MUXER;
} }
break;
case R.id.video_button:
kind = 'v';
selectedStream = videoStreamsAdapter.getItem(selectedVideoIndex);
final SecondaryStreamHelper<AudioStream> secondary = videoStreamsAdapter final long videoSize = wrappedVideoStreams.getSizeInBytes(
.getAllSecondary() (VideoStream) selectedStream);
.get(wrappedVideoStreams.getStreamsList().indexOf(selectedStream));
if (secondary != null) { // set nearLength, only, if both sizes are fetched or known. This probably
secondaryStream = secondary.getStream(); // does not work on slow networks but is later updated in the downloader
if (secondary.getSizeInBytes() > 0 && videoSize > 0) {
if (selectedStream.getFormat() == MediaFormat.MPEG_4) { nearLength = secondary.getSizeInBytes() + videoSize;
psName = Postprocessing.ALGORITHM_MP4_FROM_DASH_MUXER;
} else {
psName = Postprocessing.ALGORITHM_WEBM_MUXER;
}
final long videoSize = wrappedVideoStreams.getSizeInBytes(
(VideoStream) selectedStream);
// set nearLength, only, if both sizes are fetched or known. This probably
// does not work on slow networks but is later updated in the downloader
if (secondary.getSizeInBytes() > 0 && videoSize > 0) {
nearLength = secondary.getSizeInBytes() + videoSize;
}
} }
break; }
case R.id.subtitle_button: } else if (checkedRadioButtonId == R.id.subtitle_button) {
threads = 1; // use unique thread for subtitles due small file size threads = 1; // use unique thread for subtitles due small file size
kind = 's'; kind = 's';
selectedStream = subtitleStreamsAdapter.getItem(selectedSubtitleIndex); selectedStream = subtitleStreamsAdapter.getItem(selectedSubtitleIndex);
if (selectedStream.getFormat() == MediaFormat.TTML) { if (selectedStream.getFormat() == MediaFormat.TTML) {
psName = Postprocessing.ALGORITHM_TTML_CONVERTER; psName = Postprocessing.ALGORITHM_TTML_CONVERTER;
psArgs = new String[] { psArgs = new String[]{
selectedStream.getFormat().getSuffix(), selectedStream.getFormat().getSuffix(),
"false" // ignore empty frames "false" // ignore empty frames
}; };
} }
break; } else {
default: return;
return;
} }
if (secondaryStream == null) { if (secondaryStream == null) {

View File

@@ -133,17 +133,16 @@ public class ErrorActivity extends AppCompatActivity {
@Override @Override
public boolean onOptionsItemSelected(final MenuItem item) { public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) { final int itemId = item.getItemId();
case android.R.id.home: if (itemId == android.R.id.home) {
onBackPressed(); onBackPressed();
return true; return true;
case R.id.menu_item_share_error: } else if (itemId == R.id.menu_item_share_error) {
ShareUtils.shareText(getApplicationContext(), ShareUtils.shareText(getApplicationContext(),
getString(R.string.error_report_title), buildJson()); getString(R.string.error_report_title), buildJson());
return true; return true;
default:
return false;
} }
return false;
} }
private void openPrivacyPolicyDialog(final Context context, final String action) { private void openPrivacyPolicyDialog(final Context context, final String action) {

View File

@@ -134,8 +134,11 @@ class ErrorUtil {
) )
) )
NotificationManagerCompat.from(context) val notificationManager = NotificationManagerCompat.from(context)
.notify(ERROR_REPORT_NOTIFICATION_ID, notificationBuilder.build()) if (notificationManager.areNotificationsEnabled()) {
notificationManager
.notify(ERROR_REPORT_NOTIFICATION_ID, notificationBuilder.build())
}
ContextCompat.getMainExecutor(context).execute { ContextCompat.getMainExecutor(context).execute {
// since the notification is silent, also show a toast, otherwise the user is confused // since the notification is silent, also show a toast, otherwise the user is confused

View File

@@ -126,6 +126,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
} }
@Override @Override
@SuppressLint("MissingSuperCall")
public void onBackPressed() { public void onBackPressed() {
saveCookiesAndFinish(); saveCookiesAndFinish();
} }

View File

@@ -160,34 +160,29 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
@Override @Override
public boolean onMenuItemSelected(@NonNull final MenuItem item) { public boolean onMenuItemSelected(@NonNull final MenuItem item) {
switch (item.getItemId()) { final int itemId = item.getItemId();
case R.id.menu_item_notify: if (itemId == R.id.menu_item_notify) {
final boolean value = !item.isChecked(); final boolean value = !item.isChecked();
item.setEnabled(false); item.setEnabled(false);
setNotify(value); setNotify(value);
break; } else if (itemId == R.id.action_settings) {
case R.id.action_settings: NavigationHelper.openSettings(requireContext());
NavigationHelper.openSettings(requireContext()); } else if (itemId == R.id.menu_item_rss) {
break; if (currentInfo != null) {
case R.id.menu_item_rss: ShareUtils.openUrlInApp(requireContext(), currentInfo.getFeedUrl());
if (currentInfo != null) { }
ShareUtils.openUrlInApp(requireContext(), currentInfo.getFeedUrl()); } else if (itemId == R.id.menu_item_openInBrowser) {
} if (currentInfo != null) {
break; ShareUtils.openUrlInBrowser(requireContext(),
case R.id.menu_item_openInBrowser: currentInfo.getOriginalUrl());
if (currentInfo != null) { }
ShareUtils.openUrlInBrowser(requireContext(), } else if (itemId == R.id.menu_item_share) {
currentInfo.getOriginalUrl()); if (currentInfo != null) {
} ShareUtils.shareText(requireContext(), name,
break; currentInfo.getOriginalUrl(), currentInfo.getAvatars());
case R.id.menu_item_share: }
if (currentInfo != null) { } else {
ShareUtils.shareText(requireContext(), name, return false;
currentInfo.getOriginalUrl(), currentInfo.getAvatars());
}
break;
default:
return false;
} }
return true; return true;
} }

View File

@@ -232,35 +232,30 @@ public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, Playl
@Override @Override
public boolean onOptionsItemSelected(final MenuItem item) { public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) { final int itemId = item.getItemId();
case R.id.action_settings: if (itemId == R.id.action_settings) {
NavigationHelper.openSettings(requireContext()); NavigationHelper.openSettings(requireContext());
break; } else if (itemId == R.id.menu_item_openInBrowser) {
case R.id.menu_item_openInBrowser: ShareUtils.openUrlInBrowser(requireContext(), url);
ShareUtils.openUrlInBrowser(requireContext(), url); } else if (itemId == R.id.menu_item_share) {
break; ShareUtils.shareText(requireContext(), name, url,
case R.id.menu_item_share: currentInfo == null ? List.of() : currentInfo.getThumbnails());
ShareUtils.shareText(requireContext(), name, url, } else if (itemId == R.id.menu_item_bookmark) {
currentInfo == null ? List.of() : currentInfo.getThumbnails()); onBookmarkClicked();
break; } else if (itemId == R.id.menu_item_append_playlist) {
case R.id.menu_item_bookmark: if (currentInfo != null) {
onBookmarkClicked(); disposables.add(PlaylistDialog.createCorrespondingDialog(
break; getContext(),
case R.id.menu_item_append_playlist: getPlayQueue()
if (currentInfo != null) { .getStreams()
disposables.add(PlaylistDialog.createCorrespondingDialog( .stream()
getContext(), .map(StreamEntity::new)
getPlayQueue() .collect(Collectors.toList()),
.getStreams() dialog -> dialog.show(getFM(), TAG)
.stream() ));
.map(StreamEntity::new) }
.collect(Collectors.toList()), } else {
dialog -> dialog.show(getFM(), TAG) return super.onOptionsItemSelected(item);
));
}
break;
default:
return super.onOptionsItemSelected(item);
} }
return true; return true;
} }

View File

@@ -255,7 +255,7 @@ class FeedFragment : BaseStateFragment<FeedState>() {
viewModel.getShowFutureItemsFromPreferences() viewModel.getShowFutureItemsFromPreferences()
) )
AlertDialog.Builder(context!!) AlertDialog.Builder(requireContext())
.setTitle(R.string.feed_hide_streams_title) .setTitle(R.string.feed_hide_streams_title)
.setMultiChoiceItems(dialogItems, checkedDialogItems) { _, which, isChecked -> .setMultiChoiceItems(dialogItems, checkedDialogItems) { _, which, isChecked ->
checkedDialogItems[which] = isChecked checkedDialogItems[which] = isChecked

View File

@@ -92,8 +92,10 @@ class NotificationHelper(val context: Context) {
// Show individual stream notifications, set channel icon only if there is actually // Show individual stream notifications, set channel icon only if there is actually
// one // one
showStreamNotifications(newStreams, data.serviceId, data.url, bitmap) showStreamNotifications(newStreams, data.serviceId, data.url, bitmap)
// Show summary notification // Show summary notification if enabled
manager.notify(data.pseudoId, summaryBuilder.build()) if (manager.areNotificationsEnabled()) {
manager.notify(data.pseudoId, summaryBuilder.build())
}
iconLoadingTargets.remove(this) // allow it to be garbage-collected iconLoadingTargets.remove(this) // allow it to be garbage-collected
} }
@@ -101,8 +103,10 @@ class NotificationHelper(val context: Context) {
override fun onBitmapFailed(e: Exception, errorDrawable: Drawable) { override fun onBitmapFailed(e: Exception, errorDrawable: Drawable) {
// Show individual stream notifications // Show individual stream notifications
showStreamNotifications(newStreams, data.serviceId, data.url, null) showStreamNotifications(newStreams, data.serviceId, data.url, null)
// Show summary notification // Show summary notification if enabled
manager.notify(data.pseudoId, summaryBuilder.build()) if (manager.areNotificationsEnabled()) {
manager.notify(data.pseudoId, summaryBuilder.build())
}
iconLoadingTargets.remove(this) // allow it to be garbage-collected iconLoadingTargets.remove(this) // allow it to be garbage-collected
} }
@@ -126,7 +130,9 @@ class NotificationHelper(val context: Context) {
) { ) {
for (stream in newStreams) { for (stream in newStreams) {
val notification = createStreamNotification(stream, serviceId, channelUrl, channelIcon) val notification = createStreamNotification(stream, serviceId, channelUrl, channelIcon)
manager.notify(stream.url.hashCode(), notification) if (manager.areNotificationsEnabled()) {
manager.notify(stream.url.hashCode(), notification)
}
} }
} }

View File

@@ -185,7 +185,9 @@ class FeedLoadService : Service() {
} }
} }
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()) if (notificationManager.areNotificationsEnabled()) {
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
}
} }
// ///////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////

View File

@@ -506,7 +506,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
private fun hideKeyboardSearch() { private fun hideKeyboardSearch() {
inputMethodManager.hideSoftInputFromWindow( inputMethodManager.hideSoftInputFromWindow(
searchLayoutBinding.toolbarSearchEditText.windowToken, searchLayoutBinding.toolbarSearchEditText.windowToken,
InputMethodManager.RESULT_UNCHANGED_SHOWN InputMethodManager.HIDE_NOT_ALWAYS
) )
searchLayoutBinding.toolbarSearchEditText.clearFocus() searchLayoutBinding.toolbarSearchEditText.clearFocus()
} }
@@ -523,7 +523,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
private fun hideKeyboard() { private fun hideKeyboard() {
inputMethodManager.hideSoftInputFromWindow( inputMethodManager.hideSoftInputFromWindow(
feedGroupCreateBinding.groupNameInput.windowToken, feedGroupCreateBinding.groupNameInput.windowToken,
InputMethodManager.RESULT_UNCHANGED_SHOWN InputMethodManager.HIDE_NOT_ALWAYS
) )
feedGroupCreateBinding.groupNameInput.clearFocus() feedGroupCreateBinding.groupNameInput.clearFocus()
} }

View File

@@ -144,7 +144,9 @@ public abstract class BaseImportExportService extends Service {
notificationBuilder.setContentText(text); notificationBuilder.setContentText(text);
} }
notificationManager.notify(getNotificationId(), notificationBuilder.build()); if (notificationManager.areNotificationsEnabled()) {
notificationManager.notify(getNotificationId(), notificationBuilder.build());
}
} }
protected void stopService() { protected void stopService() {
@@ -174,7 +176,10 @@ public abstract class BaseImportExportService extends Service {
.setContentTitle(title) .setContentTitle(title)
.setStyle(new NotificationCompat.BigTextStyle().bigText(textOrEmpty)) .setStyle(new NotificationCompat.BigTextStyle().bigText(textOrEmpty))
.setContentText(textOrEmpty); .setContentText(textOrEmpty);
notificationManager.notify(getNotificationId(), notificationBuilder.build());
if (notificationManager.areNotificationsEnabled()) {
notificationManager.notify(getNotificationId(), notificationBuilder.build());
}
} }
protected NotificationCompat.Builder createNotification() { protected NotificationCompat.Builder createNotification() {

View File

@@ -127,39 +127,39 @@ public final class PlayQueueActivity extends AppCompatActivity
@Override @Override
public boolean onOptionsItemSelected(final MenuItem item) { public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) { final int itemId = item.getItemId();
case android.R.id.home: if (itemId == android.R.id.home) {
finish(); finish();
return true; return true;
case R.id.action_settings: } else if (itemId == R.id.action_settings) {
NavigationHelper.openSettings(this); NavigationHelper.openSettings(this);
return true; return true;
case R.id.action_append_playlist: } else if (itemId == R.id.action_append_playlist) {
PlaylistDialog.showForPlayQueue(player, getSupportFragmentManager()); PlaylistDialog.showForPlayQueue(player, getSupportFragmentManager());
return true; return true;
case R.id.action_playback_speed: } else if (itemId == R.id.action_playback_speed) {
openPlaybackParameterDialog(); openPlaybackParameterDialog();
return true; return true;
case R.id.action_mute: } else if (itemId == R.id.action_mute) {
player.toggleMute(); player.toggleMute();
return true; return true;
case R.id.action_system_audio: } else if (itemId == R.id.action_system_audio) {
startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS)); startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS));
return true; return true;
case R.id.action_switch_main: } else if (itemId == R.id.action_switch_main) {
this.player.setRecovery();
NavigationHelper.playOnMainPlayer(this, player.getPlayQueue(), true);
return true;
} else if (itemId == R.id.action_switch_popup) {
if (PermissionHelper.isPopupEnabledElseAsk(this)) {
this.player.setRecovery(); this.player.setRecovery();
NavigationHelper.playOnMainPlayer(this, player.getPlayQueue(), true); NavigationHelper.playOnPopupPlayer(this, player.getPlayQueue(), true);
return true; }
case R.id.action_switch_popup: return true;
if (PermissionHelper.isPopupEnabledElseAsk(this)) { } else if (itemId == R.id.action_switch_background) {
this.player.setRecovery(); this.player.setRecovery();
NavigationHelper.playOnPopupPlayer(this, player.getPlayQueue(), true); NavigationHelper.playOnBackgroundPlayer(this, player.getPlayQueue(), true);
} return true;
return true;
case R.id.action_switch_background:
this.player.setRecovery();
NavigationHelper.playOnBackgroundPlayer(this, player.getPlayQueue(), true);
return true;
} }
if (item.getGroupId() == MENU_ID_AUDIO_TRACK) { if (item.getGroupId() == MENU_ID_AUDIO_TRACK) {

View File

@@ -72,7 +72,9 @@ public final class NotificationUtil {
notificationBuilder = createNotification(); notificationBuilder = createNotification();
} }
updateNotification(); updateNotification();
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); if (notificationManager.areNotificationsEnabled()) {
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
}
} }
public synchronized void updateThumbnail() { public synchronized void updateThumbnail() {
@@ -84,7 +86,9 @@ public final class NotificationUtil {
} }
setLargeIcon(notificationBuilder); setLargeIcon(notificationBuilder);
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); if (notificationManager.areNotificationsEnabled()) {
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
}
} }
} }

View File

@@ -48,7 +48,7 @@ public final class KeyboardUtil {
final InputMethodManager imm = ContextCompat.getSystemService(activity, final InputMethodManager imm = ContextCompat.getSystemService(activity,
InputMethodManager.class); InputMethodManager.class);
imm.hideSoftInputFromWindow(editText.getWindowToken(), imm.hideSoftInputFromWindow(editText.getWindowToken(),
InputMethodManager.RESULT_UNCHANGED_SHOWN); InputMethodManager.HIDE_NOT_ALWAYS);
editText.clearFocus(); editText.clearFocus();
} }

View File

@@ -40,7 +40,6 @@ import android.view.Window;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.appcompat.view.WindowCallbackWrapper;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
@@ -232,7 +231,7 @@ public final class FocusOverlayView extends Drawable implements
// Unfortunately many such forms of "scrolling" do not count as scrolling for purpose // Unfortunately many such forms of "scrolling" do not count as scrolling for purpose
// of dispatching ViewTreeObserver callbacks, so we have to intercept them by directly // of dispatching ViewTreeObserver callbacks, so we have to intercept them by directly
// receiving keys from Window. // receiving keys from Window.
window.setCallback(new WindowCallbackWrapper(window.getCallback()) { window.setCallback(new SimpleWindowCallback(window.getCallback()) {
@Override @Override
public boolean dispatchKeyEvent(final KeyEvent event) { public boolean dispatchKeyEvent(final KeyEvent event) {
final boolean res = super.dispatchKeyEvent(event); final boolean res = super.dispatchKeyEvent(event);

View File

@@ -0,0 +1,35 @@
/*
* SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package org.schabi.newpipe.views
import android.os.Build
import android.view.KeyEvent
import android.view.KeyboardShortcutGroup
import android.view.Menu
import android.view.Window
import androidx.annotation.RequiresApi
open class SimpleWindowCallback(private val baseCallback: Window.Callback) :
Window.Callback by baseCallback {
override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
return baseCallback.dispatchKeyEvent(event)
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onPointerCaptureChanged(hasCapture: Boolean) {
baseCallback.onPointerCaptureChanged(hasCapture)
}
@RequiresApi(Build.VERSION_CODES.N)
override fun onProvideKeyboardShortcuts(
data: List<KeyboardShortcutGroup?>?,
menu: Menu?,
deviceId: Int
) {
baseCallback.onProvideKeyboardShortcuts(data, menu, deviceId)
}
}

View File

@@ -21,7 +21,7 @@ import static us.shandian.giga.get.DownloadMission.ERROR_HTTP_FORBIDDEN;
* Single-threaded fallback mode * Single-threaded fallback mode
*/ */
public class DownloadRunnableFallback extends Thread { public class DownloadRunnableFallback extends Thread {
private static final String TAG = "DownloadRunnableFallback"; private static final String TAG = DownloadRunnableFallback.class.getSimpleName();
private final DownloadMission mMission; private final DownloadMission mMission;

View File

@@ -102,14 +102,23 @@ public class FinishedMissionStore extends SQLiteOpenHelper {
db.beginTransaction(); db.beginTransaction();
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(KEY_SOURCE, cursor.getString(cursor.getColumnIndex(KEY_SOURCE))); values.put(
values.put(KEY_DONE, cursor.getString(cursor.getColumnIndex(KEY_DONE))); KEY_SOURCE,
values.put(KEY_TIMESTAMP, cursor.getLong(cursor.getColumnIndex(KEY_TIMESTAMP))); cursor.getString(cursor.getColumnIndexOrThrow(KEY_SOURCE))
values.put(KEY_KIND, cursor.getString(cursor.getColumnIndex(KEY_KIND))); );
values.put(
KEY_DONE,
cursor.getString(cursor.getColumnIndexOrThrow(KEY_DONE))
);
values.put(
KEY_TIMESTAMP,
cursor.getLong(cursor.getColumnIndexOrThrow(KEY_TIMESTAMP))
);
values.put(KEY_KIND, cursor.getString(cursor.getColumnIndexOrThrow(KEY_KIND)));
values.put(KEY_PATH, Uri.fromFile( values.put(KEY_PATH, Uri.fromFile(
new File( new File(
cursor.getString(cursor.getColumnIndex(KEY_LOCATION)), cursor.getString(cursor.getColumnIndexOrThrow(KEY_LOCATION)),
cursor.getString(cursor.getColumnIndex(KEY_NAME)) cursor.getString(cursor.getColumnIndexOrThrow(KEY_NAME))
) )
).toString()); ).toString());
@@ -141,7 +150,8 @@ public class FinishedMissionStore extends SQLiteOpenHelper {
} }
private FinishedMission getMissionFromCursor(Cursor cursor) { private FinishedMission getMissionFromCursor(Cursor cursor) {
String kind = Objects.requireNonNull(cursor).getString(cursor.getColumnIndex(KEY_KIND)); String kind = Objects.requireNonNull(cursor)
.getString(cursor.getColumnIndexOrThrow(KEY_KIND));
if (kind == null || kind.isEmpty()) kind = "?"; if (kind == null || kind.isEmpty()) kind = "?";
String path = cursor.getString(cursor.getColumnIndexOrThrow(KEY_PATH)); String path = cursor.getString(cursor.getColumnIndexOrThrow(KEY_PATH));

View File

@@ -632,103 +632,95 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
DownloadMission mission = h.item.mission instanceof DownloadMission ? (DownloadMission) h.item.mission : null; DownloadMission mission = h.item.mission instanceof DownloadMission ? (DownloadMission) h.item.mission : null;
if (mission != null) { if (mission != null) {
switch (id) { if (id == R.id.start) {
case R.id.start: h.status.setText(UNDEFINED_PROGRESS);
h.status.setText(UNDEFINED_PROGRESS); mDownloadManager.resumeMission(mission);
mDownloadManager.resumeMission(mission); return true;
return true; } else if (id == R.id.pause) {
case R.id.pause: mDownloadManager.pauseMission(mission);
mDownloadManager.pauseMission(mission); return true;
return true; } else if (id == R.id.error_message_view) {
case R.id.error_message_view: showError(mission);
showError(mission); return true;
return true; } else if (id == R.id.queue) {
case R.id.queue: boolean flag = !h.queue.isChecked();
boolean flag = !h.queue.isChecked(); h.queue.setChecked(flag);
h.queue.setChecked(flag); mission.setEnqueued(flag);
mission.setEnqueued(flag); updateProgress(h);
updateProgress(h); return true;
return true; } else if (id == R.id.retry) {
case R.id.retry: if (mission.isPsRunning()) {
if (mission.isPsRunning()) { mission.psContinue(true);
mission.psContinue(true); } else {
} else { mDownloadManager.tryRecover(mission);
mDownloadManager.tryRecover(mission); if (mission.storage.isInvalid())
if (mission.storage.isInvalid()) mRecover.tryRecover(mission);
mRecover.tryRecover(mission); else
else recoverMission(mission);
recoverMission(mission); }
} return true;
return true; } else if (id == R.id.cancel) {
case R.id.cancel: mission.psContinue(false);
mission.psContinue(false); return false;
return false;
} }
} }
switch (id) { if (id == R.id.menu_item_share) {
case R.id.menu_item_share: shareFile(h.item.mission);
shareFile(h.item.mission); return true;
return true; } else if (id == R.id.delete) {// delete the entry and the file
case R.id.delete: mDeleter.append(h.item.mission, true);
// delete the entry and the file applyChanges();
checkMasterButtonsVisibility();
return true;
} else if (id == R.id.delete_entry) {// just delete the entry
mDeleter.append(h.item.mission, false);
applyChanges();
checkMasterButtonsVisibility();
return true;
} else if (id == R.id.md5 || id == R.id.sha1) {
final StoredFileHelper storage = h.item.mission.storage;
if (!storage.existsAsFile()) {
Toast.makeText(mContext, R.string.missing_file, Toast.LENGTH_SHORT).show();
mDeleter.append(h.item.mission, true); mDeleter.append(h.item.mission, true);
applyChanges(); applyChanges();
checkMasterButtonsVisibility();
return true; return true;
case R.id.delete_entry: }
// just delete the entry final NotificationManager notificationManager
mDeleter.append(h.item.mission, false); = ContextCompat.getSystemService(mContext, NotificationManager.class);
applyChanges(); final NotificationCompat.Builder progressNotificationBuilder
checkMasterButtonsVisibility(); = new NotificationCompat.Builder(mContext,
return true; mContext.getString(R.string.hash_channel_id))
case R.id.md5: .setPriority(NotificationCompat.PRIORITY_HIGH)
case R.id.sha1: .setSmallIcon(R.drawable.ic_newpipe_triangle_white)
final StoredFileHelper storage = h.item.mission.storage; .setContentTitle(mContext.getString(R.string.msg_calculating_hash))
if (!storage.existsAsFile()) { .setContentText(mContext.getString(R.string.msg_wait))
Toast.makeText(mContext, R.string.missing_file, Toast.LENGTH_SHORT).show(); .setProgress(0, 0, true)
mDeleter.append(h.item.mission, true); .setOngoing(true);
applyChanges();
return true;
}
final NotificationManager notificationManager
= ContextCompat.getSystemService(mContext, NotificationManager.class);
final NotificationCompat.Builder progressNotificationBuilder
= new NotificationCompat.Builder(mContext,
mContext.getString(R.string.hash_channel_id))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
.setContentTitle(mContext.getString(R.string.msg_calculating_hash))
.setContentText(mContext.getString(R.string.msg_wait))
.setProgress(0, 0, true)
.setOngoing(true);
notificationManager.notify(HASH_NOTIFICATION_ID, progressNotificationBuilder notificationManager.notify(HASH_NOTIFICATION_ID, progressNotificationBuilder
.build()); .build());
compositeDisposable.add( compositeDisposable.add(
Observable.fromCallable(() -> Utility.checksum(storage, id)) Observable.fromCallable(() -> Utility.checksum(storage, id))
.subscribeOn(Schedulers.computation()) .subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> { .subscribe(result -> {
ShareUtils.copyToClipboard(mContext, result); ShareUtils.copyToClipboard(mContext, result);
notificationManager.cancel(HASH_NOTIFICATION_ID); notificationManager.cancel(HASH_NOTIFICATION_ID);
}) })
); );
return true; return true;
case R.id.source: } else if (id == R.id.source) {
/*Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(h.item.mission.source)); try {
mContext.startActivity(intent);*/ Intent intent = NavigationHelper.getIntentByLink(mContext, h.item.mission.source);
try { intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
Intent intent = NavigationHelper.getIntentByLink(mContext, h.item.mission.source); mContext.startActivity(intent);
intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); } catch (Exception e) {
mContext.startActivity(intent); Log.w(TAG, "Selected item has a invalid source", e);
} catch (Exception e) { }
Log.w(TAG, "Selected item has a invalid source", e); return true;
}
return true;
default:
return false;
} }
return false;
} }
public void applyChanges() { public void applyChanges() {

View File

@@ -186,23 +186,24 @@ public class MissionsFragment extends Fragment {
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { int itemId = item.getItemId();
case R.id.switch_mode: if (itemId == R.id.switch_mode) {
mLinear = !mLinear; mLinear = !mLinear;
updateList(); updateList();
return true; return true;
case R.id.clear_list: } else if (itemId == R.id.clear_list) {
showClearDownloadHistoryPrompt(); showClearDownloadHistoryPrompt();
return true; return true;
case R.id.start_downloads: } else if (itemId == R.id.start_downloads) {
mBinder.getDownloadManager().startAllMissions(); mBinder.getDownloadManager().startAllMissions();
return true; return true;
case R.id.pause_downloads: } else if (itemId == R.id.pause_downloads) {
mBinder.getDownloadManager().pauseAllMissions(false); mBinder.getDownloadManager().pauseAllMissions(false);
mAdapter.refreshMissionItems();// update items view mAdapter.refreshMissionItems();// update items view
default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
return super.onOptionsItemSelected(item);
} }
public void showClearDownloadHistoryPrompt() { public void showClearDownloadHistoryPrompt() {

View File

@@ -5,6 +5,6 @@
android:viewportWidth="24" android:viewportWidth="24"
android:tint="@color/defaultIconTint"> android:tint="@color/defaultIconTint">
<path <path
android:pathData="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM9.5 16.5v-9l7 4.5-7 4.5z" android:pathData="M20 4H4c-1.1 0-2 0.9-2 2v12c0 1.1 0.9 2 2 2h16c1.1 0 2-0.9 2-2V6c0-1.1-0.9-2-2-2zM9.5 16.5v-9l7 4.5-7 4.5z"
android:fillColor="#FF000000"/> android:fillColor="#FF000000"/>
</vector> </vector>

View File

@@ -122,8 +122,8 @@
android:focusable="true" android:focusable="true"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:src="@drawable/exo_controls_rewind" android:src="@drawable/exo_controls_rewind"
android:tint="?attr/colorAccent" android:contentDescription="@string/rewind"
android:contentDescription="@string/rewind" /> app:tint="?attr/colorAccent" />
<ImageButton <ImageButton
android:id="@+id/control_play_pause" android:id="@+id/control_play_pause"
@@ -139,8 +139,8 @@
android:focusable="true" android:focusable="true"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:src="@drawable/ic_pause" android:src="@drawable/ic_pause"
android:tint="?attr/colorAccent" android:contentDescription="@string/pause"
android:contentDescription="@string/pause" /> app:tint="?attr/colorAccent" />
<ProgressBar <ProgressBar
android:id="@+id/control_progress_bar" android:id="@+id/control_progress_bar"
@@ -172,8 +172,8 @@
android:focusable="true" android:focusable="true"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:src="@drawable/exo_controls_fastforward" android:src="@drawable/exo_controls_fastforward"
android:tint="?attr/colorAccent" android:contentDescription="@string/forward"
android:contentDescription="@string/forward" /> app:tint="?attr/colorAccent" />
</RelativeLayout> </RelativeLayout>
<RelativeLayout <RelativeLayout
@@ -215,8 +215,8 @@
android:focusable="true" android:focusable="true"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_repeat" android:src="@drawable/ic_repeat"
android:tint="?attr/colorAccent" android:contentDescription="@string/notification_action_repeat"
android:contentDescription="@string/notification_action_repeat" /> app:tint="?attr/colorAccent" />
<View <View
android:id="@+id/anchor" android:id="@+id/anchor"
@@ -236,8 +236,8 @@
android:focusable="true" android:focusable="true"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_shuffle" android:src="@drawable/ic_shuffle"
android:tint="?attr/colorAccent" android:contentDescription="@string/notification_action_shuffle"
android:contentDescription="@string/notification_action_shuffle" /> app:tint="?attr/colorAccent" />
<androidx.appcompat.widget.AppCompatImageButton <androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/control_forward" android:id="@+id/control_forward"

View File

@@ -175,7 +175,7 @@
android:focusable="true" android:focusable="true"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_repeat" android:src="@drawable/ic_repeat"
android:tint="?attr/colorAccent" app:tint="?attr/colorAccent"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<androidx.appcompat.widget.AppCompatImageButton <androidx.appcompat.widget.AppCompatImageButton
@@ -205,7 +205,7 @@
android:focusable="true" android:focusable="true"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:src="@drawable/exo_controls_rewind" android:src="@drawable/exo_controls_rewind"
android:tint="?attr/colorAccent" /> app:tint="?attr/colorAccent" />
<ImageButton <ImageButton
android:id="@+id/control_play_pause" android:id="@+id/control_play_pause"
@@ -220,7 +220,7 @@
android:focusable="true" android:focusable="true"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:src="@drawable/ic_pause" android:src="@drawable/ic_pause"
android:tint="?attr/colorAccent" app:tint="?attr/colorAccent"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<ProgressBar <ProgressBar
@@ -255,7 +255,7 @@
android:focusable="true" android:focusable="true"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:src="@drawable/exo_controls_fastforward" android:src="@drawable/exo_controls_fastforward"
android:tint="?attr/colorAccent" /> app:tint="?attr/colorAccent" />
<androidx.appcompat.widget.AppCompatImageButton <androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/control_forward" android:id="@+id/control_forward"
@@ -285,7 +285,7 @@
android:focusable="true" android:focusable="true"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_shuffle" android:src="@drawable/ic_shuffle"
android:tint="?attr/colorAccent" app:tint="?attr/colorAccent"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
</RelativeLayout> </RelativeLayout>

View File

@@ -309,6 +309,6 @@
<string name="notification_action_nothing">কিছু না</string> <string name="notification_action_nothing">কিছু না</string>
<string name="yes">হ্যাঁ</string> <string name="yes">হ্যাঁ</string>
<string name="no">না</string> <string name="no">না</string>
<string name="search_with_service_name">সার্চ</string> <string name="search_with_service_name">সার্চ %1$s</string>
<string name="search_with_service_name_and_filter">খুঁজুন</string> <string name="search_with_service_name_and_filter">খুঁজুন %1$s (%2$s)</string>
</resources> </resources>

View File

@@ -1,4 +1,3 @@
android.nonFinalResIds=false
android.useAndroidX=true android.useAndroidX=true
org.gradle.jvmargs=-Xmx2048M --add-opens jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED org.gradle.jvmargs=-Xmx2048M --add-opens jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
systemProp.file.encoding=utf-8 systemProp.file.encoding=utf-8