mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2026-02-08 19:20:16 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83748c2d8e | ||
|
|
e0e86b76ff | ||
|
|
a7a372406b | ||
|
|
9a3069b86f | ||
|
|
c3f89b758c | ||
|
|
4f676f8646 | ||
|
|
a0453e1299 | ||
|
|
d1d92d42c0 | ||
|
|
b494dd557d | ||
|
|
e0768b7307 | ||
|
|
8ba26f567b | ||
|
|
b79c6fecce | ||
|
|
edb0ce67b7 | ||
|
|
dd71e479ac | ||
|
|
df16dc4b21 |
@@ -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
10
app/lint.xml
Normal 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>
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressLint("MissingSuperCall")
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
saveCookiesAndFinish();
|
saveCookiesAndFinish();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -185,7 +185,9 @@ class FeedLoadService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
|
if (notificationManager.areNotificationsEnabled()) {
|
||||||
|
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user