Finished moving strings to strings.xml for activities and fragments.

This commit is contained in:
Koen 2023-10-25 14:53:50 +02:00
parent 35f9173980
commit 206c3884e9
33 changed files with 393 additions and 213 deletions

View file

@ -96,8 +96,8 @@ class AddSourceActivity : AppCompatActivity() {
var url = intent?.dataString;
if(url == null)
UIDialogs.showDialog(this, R.drawable.ic_error, "No valid URL provided..", null, null,
0, UIDialogs.Action("Ok", { finish() }, UIDialogs.ActionStyle.PRIMARY));
UIDialogs.showDialog(this, R.drawable.ic_error, getString(R.string.no_valid_url_provided), null, null,
0, UIDialogs.Action(getString(R.string.ok), { finish() }, UIDialogs.ActionStyle.PRIMARY));
else {
if(url.startsWith("vfuto://"))
url = "https://" + url.substring("vfuto://".length);
@ -129,14 +129,14 @@ class AddSourceActivity : AppCompatActivity() {
Logger.e(TAG, "Failed decode config", ex);
withContext(Dispatchers.Main) {
UIDialogs.showDialog(this@AddSourceActivity, R.drawable.ic_error,
"Invalid Config Format", null, null,
getString(R.string.invalid_config_format), null, null,
0, UIDialogs.Action("Ok", { finish() }, UIDialogs.ActionStyle.PRIMARY));
};
return@launch;
} catch(ex: Exception) {
Logger.e(TAG, "Failed fetch config", ex);
withContext(Dispatchers.Main) {
UIDialogs.showGeneralErrorDialog(this@AddSourceActivity, "Failed to fetch configuration", ex);
UIDialogs.showGeneralErrorDialog(this@AddSourceActivity, getString(R.string.failed_to_fetch_configuration), ex);
};
return@launch;
}
@ -152,7 +152,7 @@ class AddSourceActivity : AppCompatActivity() {
} catch (ex: Exception) {
Logger.e(TAG, "Failed fetch script", ex);
withContext(Dispatchers.Main) {
UIDialogs.showGeneralErrorDialog(this@AddSourceActivity, "Failed to fetch script", ex);
UIDialogs.showGeneralErrorDialog(this@AddSourceActivity, getString(R.string.failed_to_fetch_script), ex);
};
return@launch;
}
@ -175,8 +175,8 @@ class AddSourceActivity : AppCompatActivity() {
_sourcePermissions.addView(
SourceInfoView(this,
R.drawable.ic_language,
"URL Access",
"The plugin will have access to the following domains",
getString(R.string.url_access),
getString(R.string.the_plugin_will_have_access_to_the_following_domains),
config.allowUrls, true)
)
@ -184,8 +184,8 @@ class AddSourceActivity : AppCompatActivity() {
_sourcePermissions.addView(
SourceInfoView(this,
R.drawable.ic_code,
"Eval Access",
"The plugin will have access to eval capability (remote injection)",
getString(R.string.eval_access),
getString(R.string.the_plugin_will_have_access_to_eval_capability_remote_injection),
config.allowUrls, true)
)

View file

@ -22,7 +22,7 @@ class AddSourceOptionsActivity : AppCompatActivity() {
scanResult?.let {
val content = it.contents
if (content == null) {
UIDialogs.toast(this, "Failed to scan QR code")
UIDialogs.toast(this, getString(R.string.failed_to_scan_qr_code))
return@let
}
@ -31,7 +31,7 @@ class AddSourceOptionsActivity : AppCompatActivity() {
} else if (content.startsWith("grayjay://plugin/")) {
content.substring("grayjay://plugin/".length)
} else {
UIDialogs.toast(this, "Not a plugin URL")
UIDialogs.toast(this, getString(R.string.not_a_plugin_url))
return@let;
}
@ -59,7 +59,7 @@ class AddSourceOptionsActivity : AppCompatActivity() {
_buttonQR.onClick.subscribe {
val integrator = IntentIntegrator(this);
integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE)
integrator.setPrompt("Scan a QR Code")
integrator.setPrompt(getString(R.string.scan_a_qr_code))
integrator.setOrientationLocked(true);
integrator.setCameraId(0)
integrator.setBeepEnabled(false)
@ -69,7 +69,7 @@ class AddSourceOptionsActivity : AppCompatActivity() {
}
_buttonURL.onClick.subscribe {
UIDialogs.toast(this, "Not implemented yet..");
UIDialogs.toast(this, getString(R.string.not_implemented_yet));
}
}

View file

@ -38,8 +38,8 @@ class ExceptionActivity : AppCompatActivity() {
_buttonRestart = findViewById(R.id.button_restart);
_buttonClose = findViewById(R.id.button_close);
val context = intent.getStringExtra(EXTRA_CONTEXT) ?: "Unknown Context";
val stack = intent.getStringExtra(EXTRA_STACK) ?: "Something went wrong... missing stack trace?";
val context = intent.getStringExtra(EXTRA_CONTEXT) ?: getString(R.string.unknown_context);
val stack = intent.getStringExtra(EXTRA_STACK) ?: getString(R.string.something_went_wrong_missing_stack_trace);
val exceptionString = "Version information (version_name = ${BuildConfig.VERSION_NAME}, version_code = ${BuildConfig.VERSION_CODE}, flavor = ${BuildConfig.FLAVOR}, build_type = ${BuildConfig.BUILD_TYPE})\n" +
"Device information (brand= ${Build.BRAND}, manufacturer = ${Build.MANUFACTURER}, device = ${Build.DEVICE}, version-sdk = ${Build.VERSION.SDK_INT}, version-os = ${Build.VERSION.BASE_OS})\n\n" +
@ -79,13 +79,13 @@ class ExceptionActivity : AppCompatActivity() {
private fun submitFile() {
if (_submitted) {
Toast.makeText(this, "Logs already submitted.", Toast.LENGTH_LONG).show();
Toast.makeText(this, getString(R.string.logs_already_submitted), Toast.LENGTH_LONG).show();
return;
}
val file = _file;
if (file == null) {
Toast.makeText(this, "No logs found.", Toast.LENGTH_LONG).show();
Toast.makeText(this, getString(R.string.no_logs_found), Toast.LENGTH_LONG).show();
return;
}
@ -101,14 +101,14 @@ class ExceptionActivity : AppCompatActivity() {
withContext(Dispatchers.Main) {
if (id == null) {
try {
Toast.makeText(this@ExceptionActivity, "Failed automated share, share manually?", Toast.LENGTH_LONG).show();
Toast.makeText(this@ExceptionActivity, getString(R.string.failed_automated_share_share_manually), Toast.LENGTH_LONG).show();
} catch (e: Throwable) {
//Ignored
}
} else {
_submitted = true;
file.delete();
Toast.makeText(this@ExceptionActivity, "Shared $id", Toast.LENGTH_LONG).show();
Toast.makeText(this@ExceptionActivity, getString(R.string.shared_id).replace("{id}", id), Toast.LENGTH_LONG).show();
}
}
}
@ -119,10 +119,10 @@ class ExceptionActivity : AppCompatActivity() {
val i = Intent(Intent.ACTION_SEND);
i.type = "text/plain";
i.putExtra(Intent.EXTRA_EMAIL, arrayOf("grayjay@futo.org"));
i.putExtra(Intent.EXTRA_SUBJECT, "Unhandled exception in VS");
i.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.unhandled_exception_in_vs));
i.putExtra(Intent.EXTRA_TEXT, exceptionString);
startActivity(Intent.createChooser(i, "Send exception to developers..."));
startActivity(Intent.createChooser(i, getString(R.string.send_exception_to_developers)));
} catch (e: Throwable) {
//Ignored

View file

@ -478,13 +478,13 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
if(targetData.startsWith("grayjay://license/")) {
if(StatePayment.instance.setPaymentLicenseUrl(targetData))
{
UIDialogs.showDialogOk(this, R.drawable.ic_check, "Your license key has been set!\nAn app restart might be required.");
UIDialogs.showDialogOk(this, R.drawable.ic_check, getString(R.string.your_license_key_has_been_set_an_app_restart_might_be_required));
if(fragCurrent is BuyFragment)
closeSegment(fragCurrent);
}
else
UIDialogs.toast("Invalid license format");
UIDialogs.toast(getString(R.string.invalid_license_format));
}
else if(targetData.startsWith("grayjay://plugin/")) {
@ -499,7 +499,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
UIDialogs.showSingleButtonDialog(
this,
R.drawable.ic_play,
"Unknown content format [${targetData}]",
getString(R.string.unknown_content_format) + " [${targetData}]",
"Ok",
{ });
}
@ -509,7 +509,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
UIDialogs.showSingleButtonDialog(
this,
R.drawable.ic_play,
"Unknown file format [${targetData}]",
getString(R.string.unknown_file_format) + " [${targetData}]",
"Ok",
{ });
}
@ -519,7 +519,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
UIDialogs.showSingleButtonDialog(
this,
R.drawable.ic_play,
"Unknown Polycentric format [${targetData}]",
getString(R.string.unknown_polycentric_format) + " [${targetData}]",
"Ok",
{ });
}
@ -529,7 +529,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
UIDialogs.showSingleButtonDialog(
this,
R.drawable.ic_play,
"Unknown url format [${targetData}]",
getString(R.string.unknown_url_format) + " [${targetData}]",
"Ok",
{ });
}
@ -538,7 +538,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
}
}
catch(ex: Throwable) {
UIDialogs.showGeneralErrorDialog(this, "Failed to handle file", ex);
UIDialogs.showGeneralErrorDialog(this, getString(R.string.failed_to_handle_file), ex);
}
}
@ -603,7 +603,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
val store: ManagedStore<*> = when(type) {
"Playlist" -> StatePlaylists.instance.playlistStore
else -> {
UIDialogs.toast("Unknown reconstruction type ${type}", false);
UIDialogs.toast(getString(R.string.unknown_reconstruction_type) + " ${type}", false);
return;
};
};
@ -646,7 +646,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
}
catch(ex: Exception) {
Logger.e(TAG, ex.message, ex);
UIDialogs.showGeneralErrorDialog(context, "Failed to parse NewPipe Subscriptions", ex);
UIDialogs.showGeneralErrorDialog(context, getString(R.string.failed_to_parse_newpipe_subscriptions), ex);
}
/*

View file

@ -53,7 +53,7 @@ class PolycentricBackupActivity : AppCompatActivity() {
val qrCodeBitmap = generateQRCode(_exportBundle, dimension, dimension);
_imageQR.setImageBitmap(qrCodeBitmap);
} catch (e: Exception) {
Logger.e(TAG, "Failed to generate QR code", e);
Logger.e(TAG, getString(R.string.failed_to_generate_qr_code), e);
_imageQR.visibility = View.INVISIBLE;
_textQR.visibility = View.INVISIBLE;
}
@ -63,12 +63,12 @@ class PolycentricBackupActivity : AppCompatActivity() {
type = "text/plain";
putExtra(Intent.EXTRA_TEXT, _exportBundle);
}
startActivity(Intent.createChooser(shareIntent, "Share Text"));
startActivity(Intent.createChooser(shareIntent, getString(R.string.share_text)));
};
_buttonCopy.onClick.subscribe {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager;
val clip = ClipData.newPlainText("Copied Text", _exportBundle);
val clip = ClipData.newPlainText(getString(R.string.copied_text), _exportBundle);
clipboard.setPrimaryClip(clip);
};
}

View file

@ -54,7 +54,7 @@ class PolycentricCreateProfileActivity : AppCompatActivity() {
try {
val username = _profileName.text.toString();
if (username.length < 3) {
UIDialogs.toast(this@PolycentricCreateProfileActivity, "Must be at least 3 characters long.");
UIDialogs.toast(this@PolycentricCreateProfileActivity, getString(R.string.must_be_at_least_3_characters_long));
return@setOnClickListener;
}
@ -68,7 +68,7 @@ class PolycentricCreateProfileActivity : AppCompatActivity() {
processHandle.setUsername(username);
StatePolycentric.instance.setProcessHandle(processHandle);
} catch (e: Throwable) {
Logger.e(TAG, "Failed to create profile .", e);
Logger.e(TAG, getString(R.string.failed_to_create_profile), e);
return@launch;
} finally {
_creating = false;
@ -77,7 +77,7 @@ class PolycentricCreateProfileActivity : AppCompatActivity() {
try {
processHandle.fullyBackfillServers();
} catch (e: Throwable) {
Logger.e(TAG, "Failed to fully backfill servers.");
Logger.e(TAG, getString(R.string.failed_to_fully_backfill_servers));
}
withContext(Dispatchers.Main) {

View file

@ -47,7 +47,7 @@ class PolycentricHomeActivity : AppCompatActivity() {
this.setMargins(0, 0, 0, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, resources.displayMetrics).toInt());
};
profileButton.withPrimaryText(systemState.username);
profileButton.withSecondaryText("Sign in to this identity");
profileButton.withSecondaryText(getString(R.string.sign_in_to_this_identity));
profileButton.onClick.subscribe {
StatePolycentric.instance.setProcessHandle(processHandle);
startActivity(Intent(this@PolycentricHomeActivity, PolycentricProfileActivity::class.java));

View file

@ -59,7 +59,7 @@ class PolycentricImportProfileActivity : AppCompatActivity() {
_buttonScanProfile.setOnClickListener {
val integrator = IntentIntegrator(this)
integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE)
integrator.setPrompt("Scan a QR code")
integrator.setPrompt(getString(R.string.scan_a_qr_code))
integrator.setOrientationLocked(true);
integrator.setCameraId(0)
integrator.setBeepEnabled(false)
@ -70,7 +70,7 @@ class PolycentricImportProfileActivity : AppCompatActivity() {
_buttonImportProfile.setOnClickListener {
if (_editProfile.text.isEmpty()) {
UIDialogs.toast(this, "Text field does not contain any data");
UIDialogs.toast(this, getString(R.string.text_field_does_not_contain_any_data));
return@setOnClickListener;
}
@ -85,7 +85,7 @@ class PolycentricImportProfileActivity : AppCompatActivity() {
private fun import(url: String) {
if (!url.startsWith("polycentric://")) {
UIDialogs.toast(this, "Not a valid URL");
UIDialogs.toast(this, getString(R.string.not_a_valid_url));
return;
}
@ -101,7 +101,7 @@ class PolycentricImportProfileActivity : AppCompatActivity() {
val existingProcessSecret = Store.instance.getProcessSecret(keyPair.publicKey);
if (existingProcessSecret != null) {
UIDialogs.toast(this, "This profile is already imported");
UIDialogs.toast(this, getString(R.string.this_profile_is_already_imported));
return;
}
@ -124,7 +124,7 @@ class PolycentricImportProfileActivity : AppCompatActivity() {
finish();
} catch (e: Throwable) {
Logger.w(TAG, "Failed to import profile", e);
UIDialogs.toast(this, "Failed to import profile: '${e.message}'");
UIDialogs.toast(this, getString(R.string.failed_to_import_profile) + " '${e.message}'");
}
}

View file

@ -72,7 +72,7 @@ class PolycentricProfileActivity : AppCompatActivity() {
}
} catch (e: Throwable) {
withContext(Dispatchers.Main) {
UIDialogs.toast(this@PolycentricProfileActivity, "Failed to backfill client");
UIDialogs.toast(this@PolycentricProfileActivity, getString(R.string.failed_to_backfill_client));
}
}
}
@ -101,10 +101,10 @@ class PolycentricProfileActivity : AppCompatActivity() {
}
_buttonDelete.onClick.subscribe {
UIDialogs.showConfirmationDialog(this, "Are you sure you want to remove this profile?", {
UIDialogs.showConfirmationDialog(this, getString(R.string.are_you_sure_you_want_to_remove_this_profile), {
val processHandle = StatePolycentric.instance.processHandle;
if (processHandle == null) {
UIDialogs.toast(this, "No process handle set");
UIDialogs.toast(this, getString(R.string.no_process_handle_set));
return@showConfirmationDialog;
}
@ -122,13 +122,13 @@ class PolycentricProfileActivity : AppCompatActivity() {
var hasChanges = false;
val username = _editName.text.toString();
if (username.length < 3) {
UIDialogs.toast(this@PolycentricProfileActivity, "Name must be at least 3 characters long");
UIDialogs.toast(this@PolycentricProfileActivity, getString(R.string.name_must_be_at_least_3_characters_long));
return@launch;
}
val processHandle = StatePolycentric.instance.processHandle;
if (processHandle == null) {
UIDialogs.toast(this@PolycentricProfileActivity, "Process handle unset");
UIDialogs.toast(this@PolycentricProfileActivity, getString(R.string.process_handle_unset));
return@launch;
}
@ -143,7 +143,7 @@ class PolycentricProfileActivity : AppCompatActivity() {
val bytes = readBytesFromUri(applicationContext.contentResolver, avatarUri);
if (bytes == null) {
withContext(Dispatchers.Main) {
UIDialogs.toast(this@PolycentricProfileActivity, "Failed to read image");
UIDialogs.toast(this@PolycentricProfileActivity, getString(R.string.failed_to_read_image));
}
return@launch;
@ -188,12 +188,12 @@ class PolycentricProfileActivity : AppCompatActivity() {
try {
processHandle.fullyBackfillServers();
withContext(Dispatchers.Main) {
UIDialogs.toast(this@PolycentricProfileActivity, "Changes have been saved");
UIDialogs.toast(this@PolycentricProfileActivity, getString(R.string.changes_have_been_saved));
}
} catch (e: Throwable) {
Logger.w(TAG, "Failed to synchronize changes", e);
withContext(Dispatchers.Main) {
UIDialogs.toast(this@PolycentricProfileActivity, "Failed to synchronize changes");
UIDialogs.toast(this@PolycentricProfileActivity, getString(R.string.failed_to_synchronize_changes));
}
}
}
@ -235,7 +235,7 @@ class PolycentricProfileActivity : AppCompatActivity() {
} else if (resultCode == ImagePicker.RESULT_ERROR) {
UIDialogs.toast(this, ImagePicker.getError(data));
} else {
UIDialogs.toast(this, "Image picker cancelled");
UIDialogs.toast(this, getString(R.string.image_picker_cancelled));
}
}

View file

@ -70,7 +70,7 @@ class SettingsActivity : AppCompatActivity(), IWithResultLauncher {
SettingsDev.instance.developerMode = true;
SettingsDev.instance.save();
updateDevMode();
UIDialogs.toast(this, "You are now in developer mode");
UIDialogs.toast(this, getString(R.string.you_are_now_in_developer_mode));
}
};
};

View file

@ -77,7 +77,7 @@ class ChannelAboutFragment : Fragment, IChannelTabFragment {
};
_textName?.text = channel.name;
val metadata = if(channel.subscribers > 0) "${channel.subscribers.toHumanNumber()} subscribers" else "";
val metadata = if(channel.subscribers > 0) "${channel.subscribers.toHumanNumber()} " + getString(R.string.subscribers).lowercase() else "";
_textMetadata?.text = metadata;
_lastChannel = channel;
setLinks(channel.links, channel.name);
@ -91,7 +91,7 @@ class ChannelAboutFragment : Fragment, IChannelTabFragment {
l.removeAllViews();
if (links.isNotEmpty()) {
_textFindOn?.text = "Find $name on";
_textFindOn?.text = getString(R.string.find_name_on).replace("{name}", name);
_textFindOn?.visibility = View.VISIBLE;
for (pair in links) {

View file

@ -56,7 +56,7 @@ class ChannelListFragment : Fragment, IChannelTabFragment {
}.exception<ScriptCaptchaRequiredException> { }
.exceptionWithParameter<Throwable> { ex, para ->
Logger.w(ChannelFragment.TAG, "Failed to load results.", ex);
UIDialogs.toast(requireContext(), "Failed to fetch\n${para}", false)
UIDialogs.toast(requireContext(), getString(R.string.failed_to_fetch) + "\n " + para, false)
loadNext();
};

View file

@ -60,12 +60,12 @@ class BuyFragment : MainFragment() {
_paymentManager = PaymentManager(StatePayment.instance, fragment, _overlayPaying) { success, purchaseId, exception ->
if(success) {
UIDialogs.showDialog(context, R.drawable.ic_check, "Payment succeeded", "Thanks for your purchase, a key will be sent to your email after your payment has been received!", null, 0,
UIDialogs.showDialog(context, R.drawable.ic_check, context.getString(R.string.payment_succeeded), context.getString(R.string.thanks_for_your_purchase_a_key_will_be_sent_to_your_email_after_your_payment_has_been_received), null, 0,
UIDialogs.Action("Ok", {}, UIDialogs.ActionStyle.PRIMARY));
_fragment.close(true);
}
else {
UIDialogs.showGeneralErrorDialog(context, "Payment failed", exception);
UIDialogs.showGeneralErrorDialog(context, context.getString(R.string.payment_failed), exception);
}
}
@ -107,12 +107,12 @@ class BuyFragment : MainFragment() {
}
private fun paid() {
val licenseInput = SlideUpMenuTextInput(context, "License");
val productLicenseDialog = SlideUpMenuOverlay(context, findViewById<FrameLayout>(R.id.overlay_paid), "Enter license key", "Ok", true, licenseInput);
val licenseInput = SlideUpMenuTextInput(context, context.getString(R.string.license));
val productLicenseDialog = SlideUpMenuOverlay(context, findViewById<FrameLayout>(R.id.overlay_paid), context.getString(R.string.enter_license_key), context.getString(R.string.ok), true, licenseInput);
productLicenseDialog.onOK.subscribe {
val licenseText = licenseInput.text;
if (licenseText.isNullOrEmpty()) {
UIDialogs.showDialogOk(context, R.drawable.ic_error_pred, "Invalid license key");
UIDialogs.showDialogOk(context, R.drawable.ic_error_pred, context.getString(R.string.invalid_license_key));
return@subscribe;
}
@ -127,19 +127,19 @@ class BuyFragment : MainFragment() {
licenseInput.clear();
productLicenseDialog.hide(true);
UIDialogs.showDialogOk(context, R.drawable.ic_check, "Your license key has been set!\nAn app restart might be required.");
UIDialogs.showDialogOk(context, R.drawable.ic_check, context.getString(R.string.your_license_key_has_been_set_an_app_restart_might_be_required));
_fragment.close(true);
}
else
{
UIDialogs.showDialogOk(context, R.drawable.ic_error_pred, "Invalid license key");
UIDialogs.showDialogOk(context, R.drawable.ic_error_pred, context.getString(R.string.invalid_license_key));
}
}
}
catch(ex: Throwable) {
Logger.e("BuyFragment", "Failed to activate key", ex);
withContext(Dispatchers.Main) {
UIDialogs.showGeneralErrorDialog(context, "Failed to activate key", ex);
UIDialogs.showGeneralErrorDialog(context, context.getString(R.string.failed_to_activate_key), ex);
}
}
}

View file

@ -141,7 +141,7 @@ class ChannelFragment : MainFragment() {
UIDialogs.showDialog(context,
R.drawable.ic_sources,
"No source enabled to support this channel\n(${_url})", null, null,
context.getString(R.string.no_source_enabled_to_support_this_channel) + "\n(${_url})", null, null,
0,
UIDialogs.Action("Back", {
fragment.close(true);
@ -344,19 +344,19 @@ class ChannelFragment : MainFragment() {
_fragment.topBar?.onShown(channel);
val buttons = arrayListOf(Pair(R.drawable.ic_playlist_add) {
UIDialogs.showConfirmationDialog(context, "Do you want to convert channel ${channel.name} to a playlist?", {
UIDialogs.showConfirmationDialog(context, context.getString(R.string.do_you_want_to_convert_channel_channelname_to_a_playlist).replace("{channelName}", channel.name), {
UIDialogs.showDialogProgress(context) {
_fragment.lifecycleScope.launch(Dispatchers.IO) {
try {
StatePlaylists.instance.createPlaylistFromChannel(channel) { page ->
_fragment.lifecycleScope.launch(Dispatchers.Main) {
it.setText("${channel.name}\nPage ${page}");
it.setText("${channel.name}\n" + context.getString(R.string.page) + " $page");
}
};
}
catch(ex: Exception) {
Logger.e(TAG, "Error", ex);
UIDialogs.showGeneralErrorDialog(context, "Failed to convert channel", ex);
UIDialogs.showGeneralErrorDialog(context, context.getString(R.string.failed_to_convert_channel), ex);
}
withContext(Dispatchers.Main) {
@ -377,7 +377,7 @@ class ChannelFragment : MainFragment() {
_fragment.topBar?.assume<NavigationTopBarFragment>()?.setMenuItems(buttons);
_buttonSubscribe.setSubscribeChannel(channel);
_textChannelSub.text = if(channel.subscribers > 0) "${channel.subscribers.toHumanNumber()} subscribers" else "";
_textChannelSub.text = if(channel.subscribers > 0) "${channel.subscribers.toHumanNumber()} " + context.getString(R.string.subscribers).lowercase() else "";
//TODO: Find a better way to access the adapter fragments..

View file

@ -72,7 +72,7 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
//TODO: Reconstruct search video from detail if search is null
_overlayContainer.let {
if(content is IPlatformVideo)
UISlideOverlays.showVideoOptionsOverlay(content, it, SlideUpMenuItem(context, R.drawable.ic_visibility_off, "Hide", "Hide from Home", "hide",
UISlideOverlays.showVideoOptionsOverlay(content, it, SlideUpMenuItem(context, R.drawable.ic_visibility_off, context.getString(R.string.hide), context.getString(R.string.hide_from_home), "hide",
{ StateMeta.instance.addHiddenVideo(content.url);
if (fragment is HomeFragment) {
val removeIndex = recyclerData.results.indexOf(content);
@ -82,7 +82,7 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
}
}
}),
SlideUpMenuItem(context, R.drawable.ic_playlist, "Play Feed as Queue", "Play entire feed", "playFeed",
SlideUpMenuItem(context, R.drawable.ic_playlist, context.getString(R.string.play_feed_as_queue), context.getString(R.string.play_entire_feed), "playFeed",
{
val newQueue = listOf(content) + recyclerData.results
.filterIsInstance<IPlatformVideo>()
@ -96,7 +96,7 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
if(it is IPlatformVideo) {
StatePlayer.instance.addToQueue(it);
val name = if (it.name.length > 20) (it.name.subSequence(0, 20).toString() + "...") else it.name;
UIDialogs.toast(context, "Queued [$name]", false);
UIDialogs.toast(context, context.getString(R.string.queued) + " [$name]", false);
}
};
}

View file

@ -89,7 +89,7 @@ class ContentSearchResultsFragment : MainFragment() {
})
.success { loadedResult(it); }.exception<ScriptCaptchaRequiredException> { }
.exception<Throwable> {
Logger.w(ChannelFragment.TAG, "Failed to load results.", it);
Logger.w(TAG, "Failed to load results.", it);
UIDialogs.showGeneralRetryErrorDialog(context, it.message ?: "", it, { loadResults() });
}
}

View file

@ -136,8 +136,8 @@ class DownloadsFragment : MainFragment() {
fun reloadUI() {
val usage = StateDownloads.instance.getTotalUsage(true);
_usageUsed.text = "${usage.usage.toHumanBytesSize()} Used";
_usageAvailable.text = "${usage.available.toHumanBytesSize()} Available";
_usageUsed.text = "${usage.usage.toHumanBytesSize()} " + context.getString(R.string.used);
_usageAvailable.text = "${usage.available.toHumanBytesSize()} " + context.getString(R.string.available);
_usageProgress.progress = usage.percentage.toFloat();
@ -161,7 +161,7 @@ class DownloadsFragment : MainFragment() {
_listPlaylistsContainer.visibility = GONE;
else {
_listPlaylistsContainer.visibility = VISIBLE;
_listPlaylistsMeta.text = "(${playlists.size} playlists, ${playlists.sumOf { it.playlist.videos.size }} videos)";
_listPlaylistsMeta.text = "(${playlists.size} ${context.getString(R.string.playlists).lowercase()}, ${playlists.sumOf { it.playlist.videos.size }} ${context.getString(R.string.videos).lowercase()})";
_listPlaylists.removeAllViews();
for(view in playlists.map { PlaylistDownloadItem(context, it) }) {
@ -176,7 +176,7 @@ class DownloadsFragment : MainFragment() {
_listDownloadedHeader.visibility = GONE;
} else {
_listDownloadedHeader.visibility = VISIBLE;
_listDownloadedMeta.text = "(${downloaded.size} videos)";
_listDownloadedMeta.text = "(${downloaded.size} ${context.getString(R.string.videos).lowercase()})";
}
_listDownloaded.setData(downloaded);

View file

@ -144,7 +144,7 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
recyclerData.adapter.notifyItemRangeInserted(recyclerData.adapter.childToParentPosition(posBefore), filteredResults.size);
}.exception<Throwable> {
Logger.w(TAG, "Failed to load next page.", it);
UIDialogs.showGeneralRetryErrorDialog(context, "Failed to load next page", it, {
UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_next_page), it, {
loadNextPage();
});
//UIDialogs.showDataRetryDialog(layoutInflater, it.message, { loadNextPage() });
@ -256,7 +256,7 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
fragment.lifecycleScope.launch(Dispatchers.Main) {
try {
if(jsVideoPager != null)
UIDialogs.toast(it, "Plugin ${jsVideoPager.getPluginConfig().name} failed:\n${kv.value.message}", false);
UIDialogs.toast(it, context.getString(R.string.plugin_pluginname_failed_message).replace("{pluginName}", jsVideoPager.getPluginConfig().name).replace("{message}", kv.value.message ?: ""), false);
else
UIDialogs.toast(it, kv.value.message ?: "", false);
} catch (e: Throwable) {
@ -333,11 +333,11 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
parentPager.onPagerError.subscribe(this) {
Logger.e(TAG, "Search pager failed: ${it.message}", it);
when (it) {
is PluginException -> UIDialogs.toast("Plugin [${it.config.name}] failed due to:\n${it.message}")
is PluginException -> UIDialogs.toast("Plugin [{pluginName}] failed due to:\n{exceptionMessage}".replace("{pluginName}", it.config.name).replace("{exceptionMessage}", it.message ?: ""))
is CancellationException -> {
//Hide cancelled toast
}
else -> UIDialogs.toast("Plugin failed due to:\n${it.message}")
else -> UIDialogs.toast(context.getString(R.string.plugin_failed_due_to) + "\n${it.message}")
};
};
}

View file

@ -101,21 +101,21 @@ class HomeFragment : MainFragment() {
.exception<ScriptCaptchaRequiredException> { }
.exception<ScriptExecutionException> {
Logger.w(ChannelFragment.TAG, "Plugin failure.", it);
UIDialogs.showDialog(context, R.drawable.ic_error_pred, "Failed to get Home\nPlugin [${it.config.name}]", it.message, null, 0,
UIDialogs.Action("Ignore", {}),
UIDialogs.Action("Sources", { fragment.navigate<SourcesFragment>() }, UIDialogs.ActionStyle.PRIMARY)
UIDialogs.showDialog(context, R.drawable.ic_error_pred, context.getString(R.string.failed_to_get_home_plugin) + " [${it.config.name}]", it.message, null, 0,
UIDialogs.Action(context.getString(R.string.ignore), {}),
UIDialogs.Action(context.getString(R.string.sources), { fragment.navigate<SourcesFragment>() }, UIDialogs.ActionStyle.PRIMARY)
);
}
.exception<ScriptImplementationException> {
Logger.w(TAG, "Plugin failure.", it);
UIDialogs.showDialog(context, R.drawable.ic_error_pred, "Failed to get Home\nPlugin [${it.config.name}]", it.message, null, 0,
UIDialogs.Action("Ignore", {}),
UIDialogs.Action("Sources", { fragment.navigate<SourcesFragment>() }, UIDialogs.ActionStyle.PRIMARY)
UIDialogs.showDialog(context, R.drawable.ic_error_pred, context.getString(R.string.failed_to_get_home_plugin) + " [${it.config.name}]", it.message, null, 0,
UIDialogs.Action(context.getString(R.string.ignore), {}),
UIDialogs.Action(context.getString(R.string.sources), { fragment.navigate<SourcesFragment>() }, UIDialogs.ActionStyle.PRIMARY)
);
}
.exception<Throwable> {
Logger.w(TAG, "Failed to load channel.", it);
UIDialogs.showGeneralRetryErrorDialog(context, "Failed to get Home", it, {
UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_get_home), it, {
loadResults()
}) {
finishRefreshLayoutLoader();
@ -159,7 +159,7 @@ class HomeFragment : MainFragment() {
}
private fun loadedResult(pager : IPager<IPlatformContent>) {
if (pager is EmptyPager<IPlatformContent>) {
StateAnnouncement.instance.registerAnnouncement(UUID.randomUUID().toString(), "No home available", "No home page is available, please check if you are connected to the internet and refresh.", AnnouncementType.SESSION);
StateAnnouncement.instance.registerAnnouncement(UUID.randomUUID().toString(), context.getString(R.string.no_home_available), context.getString(R.string.no_home_page_is_available_please_check_if_you_are_connected_to_the_internet_and_refresh), AnnouncementType.SESSION);
}
Logger.i(TAG, "Got new home pager ${pager}");

View file

@ -113,7 +113,7 @@ class ImportPlaylistsFragment : MainFragment() {
}.exceptionWithParameter<Throwable> { ex, para ->
//setLoading(false);
Logger.w(ChannelFragment.TAG, "Failed to load results.", ex);
UIDialogs.toast(context, "Failed to fetch\n${para}", false)
UIDialogs.toast(context, context.getString(R.string.failed_to_fetch) + "\n${para}", false)
//UIDialogs.showDataRetryDialog(layoutInflater, { load(); });
loadNext();
};
@ -144,14 +144,14 @@ class ImportPlaylistsFragment : MainFragment() {
val tb = _fragment.topBar as ImportTopBarFragment?;
tb?.let {
it.title = "Import Playlists";
it.title = context.getString(R.string.import_playlists);
it.onImport.subscribe(this) {
val playlistsToImport = _items.filter { i -> i.selected }.toList();
for (playlistToImport in playlistsToImport) {
StatePlaylists.instance.createOrUpdatePlaylist(playlistToImport.playlist);
}
UIDialogs.toast("${playlistsToImport.size} playlists imported.");
UIDialogs.toast("${playlistsToImport.size} " + context.getString(R.string.playlists_imported));
_fragment.closeSegment();
};
}
@ -175,7 +175,7 @@ class ImportPlaylistsFragment : MainFragment() {
val itemsSelected = _items.count { i -> i.selected };
if (itemsSelected > 0) {
_textSelectDeselectAll.text = context.getString(R.string.deselect_all);
_textCounter.text = "$itemsSelected out of ${_items.size} selected";
_textCounter.text = context.getString(R.string.index_out_of_size_selected).replace("{index}", itemsSelected.toString()).replace("{size}", _items.size.toString());
(_fragment.topBar as ImportTopBarFragment?)?.setImportEnabled(true);
} else {
_textSelectDeselectAll.text = context.getString(R.string.select_all);

View file

@ -116,7 +116,7 @@ class ImportSubscriptionsFragment : MainFragment() {
}.exceptionWithParameter<Throwable> { ex, para ->
//setLoading(false);
Logger.w(ChannelFragment.TAG, "Failed to load results.", ex);
UIDialogs.toast(context, "Failed to fetch\n${para}", false)
UIDialogs.toast(context, context.getString(R.string.failed_to_fetch) + "\n${para}", false)
//UIDialogs.showDataRetryDialog(layoutInflater, { load(); });
loadNext();
};
@ -147,14 +147,14 @@ class ImportSubscriptionsFragment : MainFragment() {
val tb = _fragment.topBar as ImportTopBarFragment?;
tb?.let {
it.title = "Import Subscriptions";
it.title = context.getString(R.string.import_subscriptions);
it.onImport.subscribe(this) {
val subscriptionsToImport = _items.filter { i -> i.selected }.toList();
for (subscriptionToImport in subscriptionsToImport) {
StateSubscriptions.instance.addSubscription(subscriptionToImport.channel);
}
UIDialogs.toast("${subscriptionsToImport.size} subscriptions imported.");
UIDialogs.toast("${subscriptionsToImport.size} " + context.getString(R.string.subscriptions_imported));
_fragment.closeSegment();
};
}
@ -165,7 +165,7 @@ class ImportSubscriptionsFragment : MainFragment() {
if (_counter >= MAXIMUM_BATCH_SIZE) {
if (!_limitToastShown) {
_limitToastShown = true;
UIDialogs.toast(context, "Stopped after $MAXIMUM_BATCH_SIZE to avoid rate limit, re-enter to import rest");
UIDialogs.toast(context, "Stopped after {requestCount} to avoid rate limit, re-enter to import rest".replace("{requestCount}", MAXIMUM_BATCH_SIZE.toString()));
}
setLoading(false);
@ -187,7 +187,7 @@ class ImportSubscriptionsFragment : MainFragment() {
val itemsSelected = _items.count { i -> i.selected };
if (itemsSelected > 0) {
_textSelectDeselectAll.text = context.getString(R.string.deselect_all);
_textCounter.text = "$itemsSelected out of ${_items.size} selected";
_textCounter.text = context.getString(R.string.index_out_of_size_selected).replace("{index}", itemsSelected.toString()).replace("{size}", _items.size.toString());
(_fragment.topBar as ImportTopBarFragment?)?.setImportEnabled(true);
} else {
_textSelectDeselectAll.text = context.getString(R.string.select_all);

View file

@ -80,8 +80,8 @@ class PlaylistFragment : MainFragment() {
constructor(fragment: PlaylistFragment, inflater: LayoutInflater) : super(inflater) {
_fragment = fragment;
val nameInput = SlideUpMenuTextInput(context, "Name");
val editPlaylistOverlay = SlideUpMenuOverlay(context, overlayContainer, "Edit playlist", "Ok", false, nameInput);
val nameInput = SlideUpMenuTextInput(context, context.getString(R.string.name));
val editPlaylistOverlay = SlideUpMenuOverlay(context, overlayContainer, context.getString(R.string.edit_playlist), context.getString(R.string.ok), false, nameInput);
_buttonDownload.visibility = View.VISIBLE;
editPlaylistOverlay.onOK.subscribe {
@ -113,14 +113,14 @@ class PlaylistFragment : MainFragment() {
val playlist = _playlist ?: return@setOnShare;
val reconstruction = StatePlaylists.instance.playlistStore.getReconstructionString(playlist);
UISlideOverlays.showOverlay(overlayContainer, "Playlist [${playlist.name}]", null, {},
SlideUpMenuItem(context, R.drawable.ic_list, "Share as Text", "Share as a list of video urls", 1, {
UISlideOverlays.showOverlay(overlayContainer, context.getString(R.string.playlist) + " [${playlist.name}]", null, {},
SlideUpMenuItem(context, R.drawable.ic_list, context.getString(R.string.share_as_text), context.getString(R.string.share_as_a_list_of_video_urls), 1, {
_fragment.startActivity(ShareCompat.IntentBuilder(context)
.setType("text/plain")
.setText(reconstruction)
.intent);
}),
SlideUpMenuItem(context, R.drawable.ic_move_up, "Share as Import", "Share as a import file for Grayjay", 2, {
SlideUpMenuItem(context, R.drawable.ic_move_up, context.getString(R.string.share_as_import), context.getString(R.string.share_as_a_import_file_for_grayjay), 2, {
val shareUri = StatePlaylists.instance.createPlaylistShareJsonUri(context, playlist);
_fragment.startActivity(ShareCompat.IntentBuilder(context)
.setType("application/json")
@ -146,7 +146,7 @@ class PlaylistFragment : MainFragment() {
.exception<Throwable> {
Logger.w(TAG, "Failed to load playlist.", it);
val c = context ?: return@exception;
UIDialogs.showGeneralRetryErrorDialog(c, "Failed to load playlist", it, ::fetchPlaylist);
UIDialogs.showGeneralRetryErrorDialog(c, context.getString(R.string.failed_to_load_playlist), it, ::fetchPlaylist);
};
}
@ -234,7 +234,7 @@ class PlaylistFragment : MainFragment() {
_fragment.topBar?.assume<NavigationTopBarFragment>()?.setMenuItems(arrayListOf(Pair(R.drawable.ic_copy) {
val remotePlaylist = _remotePlaylist;
if (remotePlaylist == null) {
UIDialogs.toast("Please wait for playlist to finish loading");
UIDialogs.toast(context.getString(R.string.please_wait_for_playlist_to_finish_loading));
return@Pair;
}
@ -245,7 +245,7 @@ class PlaylistFragment : MainFragment() {
withContext(Dispatchers.Main) {
setLoading(false);
UIDialogs.toast("Playlist copied as local playlist");
UIDialogs.toast(context.getString(R.string.playlist_copied_as_local_playlist));
}
} catch (e: Throwable) {
withContext(Dispatchers.Main) {
@ -284,7 +284,7 @@ class PlaylistFragment : MainFragment() {
_buttonDownload.setImageResource(R.drawable.ic_loader_animated);
_buttonDownload.drawable.assume<Animatable, Unit> { it.start() };
_buttonDownload.setOnClickListener {
UIDialogs.showConfirmationDialog(context, "Are you sure you want to delete the downloaded videos?", {
UIDialogs.showConfirmationDialog(context, context.getString(R.string.are_you_sure_you_want_to_delete_the_downloaded_videos), {
StateDownloads.instance.deleteCachedPlaylist(playlist.id);
});
}
@ -292,7 +292,7 @@ class PlaylistFragment : MainFragment() {
else if(isDownloaded) {
_buttonDownload.setImageResource(R.drawable.ic_download_off);
_buttonDownload.setOnClickListener {
UIDialogs.showConfirmationDialog(context, "Are you sure you want to delete the downloaded videos?", {
UIDialogs.showConfirmationDialog(context, context.getString(R.string.are_you_sure_you_want_to_delete_the_downloaded_videos), {
StateDownloads.instance.deleteCachedPlaylist(playlist.id);
});
}

View file

@ -92,8 +92,8 @@ class PlaylistsFragment : MainFragment() {
recyclerPlaylists.adapter = _adapterPlaylist;
recyclerPlaylists.layoutManager = LinearLayoutManager(context);
val nameInput = SlideUpMenuTextInput(context, "Name");
val addPlaylistOverlay = SlideUpMenuOverlay(context, findViewById<FrameLayout>(R.id.overlay_create_playlist), "Create new playlist", "Ok", false, nameInput);
val nameInput = SlideUpMenuTextInput(context, context.getString(R.string.name));
val addPlaylistOverlay = SlideUpMenuOverlay(context, findViewById<FrameLayout>(R.id.overlay_create_playlist), context.getString(R.string.create_new_playlist), context.getString(R.string.ok), false, nameInput);
_adapterPlaylist.onClick.subscribe { p -> _fragment.navigate<PlaylistFragment>(p); };
_adapterPlaylist.onPlay.subscribe { p ->
@ -130,7 +130,7 @@ class PlaylistsFragment : MainFragment() {
_appBar = findViewById(R.id.app_bar);
_layoutWatchlist = findViewById(R.id.layout_watchlist);
findViewById<TextView>(R.id.text_view_all).setOnClickListener { _fragment.navigate<WatchLaterFragment>("Watch Later"); };
findViewById<TextView>(R.id.text_view_all).setOnClickListener { _fragment.navigate<WatchLaterFragment>(context.getString(R.string.watch_later)); };
StatePlaylists.instance.onWatchLaterChanged.subscribe(this) {
updateWatchLater();
};

View file

@ -154,13 +154,13 @@ class PostDetailFragment : MainFragment {
{
val result = StatePlatform.instance.getContentDetails(it).await();
if(result !is IPlatformPostDetails)
throw IllegalStateException("Expected media content, found ${result.contentType}");
throw IllegalStateException(context.getString(R.string.expected_media_content_found) + " ${result.contentType}");
return@TaskHandler result;
})
.success { setPostDetails(it) }
.exception<Throwable> {
Logger.w(ChannelFragment.TAG, "Failed to load post.", it);
UIDialogs.showGeneralRetryErrorDialog(context, "Failed to load post", it, ::fetchPost);
Logger.w(ChannelFragment.TAG, context.getString(R.string.failed_to_load_post), it);
UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_post), it, ::fetchPost);
} else TaskHandler(IPlatformPostDetails::class.java) { _fragment.lifecycleScope };
private val _taskLoadPolycentricProfile = TaskHandler<PlatformID, PolycentricCache.CachedPolycentricProfile?>(StateApp.instance.scopeGetter, { PolycentricCache.instance.getProfileAsync(it) })
@ -222,7 +222,7 @@ class PostDetailFragment : MainFragment {
val replyCount = c.replyCount ?: 0;
var metadata = "";
if (replyCount > 0) {
metadata += "$replyCount replies";
metadata += "$replyCount " + context.getString(R.string.replies);
}
if (c is PolycentricPlatformComment) {
@ -601,7 +601,7 @@ class PostDetailFragment : MainFragment {
val subscribers = value?.author?.subscribers;
if(subscribers != null && subscribers > 0) {
_channelMeta.visibility = View.VISIBLE;
_channelMeta.text = if((value.author?.subscribers ?: 0) > 0) value.author.subscribers!!.toHumanNumber() + " subscribers" else "";
_channelMeta.text = if((value.author.subscribers ?: 0) > 0) value.author.subscribers!!.toHumanNumber() + " " + context.getString(R.string.subscribers) else "";
} else {
_channelMeta.visibility = View.GONE;
_channelMeta.text = "";

View file

@ -107,7 +107,7 @@ class SourceDetailFragment : MainFragment() {
StatePlugins.instance.setPluginSettings(id, _settings!!);
reloadSource(id);
UIDialogs.toast("Plugin settings saved", false);
UIDialogs.toast(context.getString(R.string.plugin_settings_saved), false);
}
if(_settingsAppChanged) {
_settingsAppForm.setObjectValues();
@ -144,8 +144,8 @@ class SourceDetailFragment : MainFragment() {
try {
_settings = settingValues;
_settingsForm.fromPluginSettings(
settings, settingValues, "Plugin settings",
"These settings are defined by the plugin"
settings, settingValues, context.getString(R.string.plugin_settings),
context.getString(R.string.these_settings_are_defined_by_the_plugin)
);
_settingsForm.onChanged.clear();
_settingsForm.onChanged.subscribe { field, value ->
@ -158,7 +158,7 @@ class SourceDetailFragment : MainFragment() {
}
catch(ex: Throwable) {
Logger.e(TAG, "Failed to load source", ex);
UIDialogs.toast("Failed to loast source");
UIDialogs.toast(context.getString(R.string.failed_to_load_source));
}
}
}
@ -204,8 +204,8 @@ class SourceDetailFragment : MainFragment() {
val isEnabled = StatePlatform.instance.isClientEnabled(source);
groups.add(
BigButtonGroup(c, "Update",
BigButton(c, "Check for updates", "Checks for new versions of the source", R.drawable.ic_update) {
BigButtonGroup(c, context.getString(R.string.update),
BigButton(c, context.getString(R.string.check_for_updates), context.getString(R.string.checks_for_new_versions_of_the_source), R.drawable.ic_update) {
checkForUpdatesSource();
}
)
@ -213,8 +213,8 @@ class SourceDetailFragment : MainFragment() {
if (source.isLoggedIn) {
groups.add(
BigButtonGroup(c, "Authentication",
BigButton(c, "Logout", "Sign out of the platform", R.drawable.ic_logout) {
BigButtonGroup(c, context.getString(R.string.authentication),
BigButton(c, context.getString(R.string.logout), context.getString(R.string.sign_out_of_the_platform), R.drawable.ic_logout) {
logoutSource();
}
)
@ -223,7 +223,7 @@ class SourceDetailFragment : MainFragment() {
val migrationButtons = mutableListOf<BigButton>();
if (isEnabled && source.capabilities.hasGetUserSubscriptions) {
migrationButtons.add(
BigButton(c, "Import Subscriptions", "Import your subscriptions from this source", R.drawable.ic_subscriptions) {
BigButton(c, context.getString(R.string.import_subscriptions), context.getString(R.string.import_your_subscriptions_from_this_source), R.drawable.ic_subscriptions) {
Logger.i(TAG, "Import subscriptions clicked.");
importSubscriptionsSource();
}
@ -231,7 +231,7 @@ class SourceDetailFragment : MainFragment() {
}
if (isEnabled && source.capabilities.hasGetUserPlaylists && source.capabilities.hasGetPlaylist) {
val bigButton = BigButton(c, "Import Playlists", "Import your playlists from this source", R.drawable.ic_playlist) {
val bigButton = BigButton(c, context.getString(R.string.import_playlists), context.getString(R.string.import_your_playlists_from_this_source), R.drawable.ic_playlist) {
Logger.i(TAG, "Import playlists clicked.");
importPlaylistsSource();
};
@ -244,13 +244,13 @@ class SourceDetailFragment : MainFragment() {
}
if (migrationButtons.size > 0) {
groups.add(BigButtonGroup(c, "Migration", *migrationButtons.toTypedArray()));
groups.add(BigButtonGroup(c, context.getString(R.string.migration), *migrationButtons.toTypedArray()));
}
} else {
if(config.authentication != null) {
groups.add(
BigButtonGroup(c, "Authentication",
BigButton(c, "Login", "Sign into the platform of this source", R.drawable.ic_login) {
BigButtonGroup(c, context.getString(R.string.authentication),
BigButton(c, context.getString(R.string.login), context.getString(R.string.sign_into_the_platform_of_this_source), R.drawable.ic_login) {
loginSource();
}
)
@ -260,8 +260,8 @@ class SourceDetailFragment : MainFragment() {
val clientIfExists = StatePlugins.instance.getPlugin(config.id);
groups.add(
BigButtonGroup(c, "Management",
BigButton(c, "Uninstall", "Removes the plugin from the app", R.drawable.ic_block) {
BigButtonGroup(c, context.getString(R.string.management),
BigButton(c, context.getString(R.string.uninstall), context.getString(R.string.removes_the_plugin_from_the_app), R.drawable.ic_block) {
uninstallSource();
}.withBackground(R.drawable.background_big_button_red).apply {
this.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
@ -269,8 +269,8 @@ class SourceDetailFragment : MainFragment() {
};
},
if(clientIfExists?.captchaEncrypted != null)
BigButton(c, "Delete Captcha", "Deletes stored captcha answer for this plugin", R.drawable.ic_block) {
clientIfExists?.updateCaptcha(null);
BigButton(c, context.getString(R.string.delete_captcha), context.getString(R.string.deletes_stored_captcha_answer_for_this_plugin), R.drawable.ic_block) {
clientIfExists.updateCaptcha(null);
}.apply {
this.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
setMargins(0, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics).toInt(), 0, 0);
@ -329,7 +329,7 @@ class SourceDetailFragment : MainFragment() {
}
} catch (e: Throwable) {
withContext(Dispatchers.Main) {
context?.let { UIDialogs.showGeneralErrorDialog(it, "Failed to retrieve playlists.", e) }
context?.let { UIDialogs.showGeneralErrorDialog(it, it.getString(R.string.failed_to_retrieve_playlists), e) }
}
} finally {
setLoading(false);
@ -354,14 +354,14 @@ class SourceDetailFragment : MainFragment() {
fragment.lifecycleScope.launch(Dispatchers.IO) {
try {
val subscriptions = source.getUserSubscriptions().distinct();
Logger.i(TAG, "${subscriptions.size} user subscriptions retrieved.");
Logger.i(TAG, context.getString(R.string.subscriptioncount_user_subscriptions_retrieved).replace("{subscriptionCount}", subscriptions.size.toString()));
withContext(Dispatchers.Main) {
fragment.navigate<ImportSubscriptionsFragment>(subscriptions);
}
} catch(e: Throwable) {
withContext(Dispatchers.Main) {
context?.let { UIDialogs.showGeneralErrorDialog(it, "Failed to retrieve subscriptions.", e) }
context?.let { UIDialogs.showGeneralErrorDialog(it, context.getString(R.string.failed_to_retrieve_subscriptions), e) }
}
} finally {
setLoading(false);
@ -375,7 +375,7 @@ class SourceDetailFragment : MainFragment() {
val config = _config ?: return;
val source = StatePlatform.instance.getClient(config.id);
UIDialogs.showConfirmationDialog(context, "Are you sure you want to uninstall ${source.name}", {
UIDialogs.showConfirmationDialog(context, context.getString(R.string.are_you_sure_you_want_to_uninstall) + " ${source.name}", {
StatePlugins.instance.deletePlugin(source.id);
fragment.lifecycleScope.launch(Dispatchers.IO) {
@ -386,7 +386,7 @@ class SourceDetailFragment : MainFragment() {
}
withContext(Dispatchers.Main) {
UIDialogs.toast(context, "Uninstalled ${source.name}");
UIDialogs.toast(context, context.getString(R.string.uninstalled) + " ${source.name}");
fragment.closeSegment();
}
}
@ -405,7 +405,7 @@ class SourceDetailFragment : MainFragment() {
if (!response.isOk || response.body == null) {
Logger.w(TAG, "Failed to check for updates (sourceUrl=${sourceUrl}, response.isOk=${response.isOk}, response.body=${response.body}).");
withContext(Dispatchers.Main) { UIDialogs.toast("Failed to check for updates"); };
withContext(Dispatchers.Main) { UIDialogs.toast(context.getString(R.string.failed_to_check_for_updates)); };
return@launch;
}
@ -415,7 +415,7 @@ class SourceDetailFragment : MainFragment() {
val config = SourcePluginConfig.fromJson(configJson);
if (config.version <= c.version) {
Logger.i(TAG, "Plugin is up to date.");
withContext(Dispatchers.Main) { UIDialogs.toast("Plugin is fully up to date"); };
withContext(Dispatchers.Main) { UIDialogs.toast(context.getString(R.string.plugin_is_fully_up_to_date)); };
return@launch;
}
@ -430,7 +430,7 @@ class SourceDetailFragment : MainFragment() {
Logger.i(TAG, "Started add source activity.");
} catch (e: Throwable) {
Logger.e(TAG, "Failed to check for updates.", e);
withContext(Dispatchers.Main) { UIDialogs.toast("Failed to check for updates"); };
withContext(Dispatchers.Main) { UIDialogs.toast(context.getString(R.string.failed_to_check_for_updates)); };
}
}
}

View file

@ -195,9 +195,8 @@ class SubscriptionsFeedFragment : MainFragment() {
withContext(Dispatchers.Main) {
UIDialogs.showDialog(context, R.drawable.ic_security_pred,
"Rate Limit Warning", "This is a temporary measure to prevent people from hitting rate limit until we have better support for lots of subscriptions." +
"\n\nYou have too many subscriptions for the following plugins:\n",
subsByLimited.map { "${it.first.config.name}: ${it.second.size} Subscriptions" } .joinToString("\n"), 0, UIDialogs.Action("Refresh Anyway", {
context.getString(R.string.rate_limit_warning), context.getString(R.string.this_is_a_temporary_measure_to_prevent_people_from_hitting_rate_limit_until_we_have_better_support_for_lots_of_subscriptions) + context.getString(R.string.you_have_too_many_subscriptions_for_the_following_plugins),
subsByLimited.map { it.first.config.name + ": " + it.second.size + " " + context.getString(R.string.subscriptions) } .joinToString("\n"), 0, UIDialogs.Action("Refresh Anyway", {
_bypassRateLimit = true;
loadResults();
}, UIDialogs.ActionStyle.DANGEROUS_TEXT),
@ -226,10 +225,10 @@ class SubscriptionsFeedFragment : MainFragment() {
synchronized(_filterLock) {
_subscriptionBar?.setToggles(
SubscriptionBar.Toggle("Videos", _filterSettings.allowContentTypes.contains(ContentType.MEDIA)) { toggleFilterContentTypes(listOf(ContentType.MEDIA, ContentType.NESTED_VIDEO), it); },
SubscriptionBar.Toggle("Posts", _filterSettings.allowContentTypes.contains(ContentType.POST)) { toggleFilterContentType(ContentType.POST, it); },
SubscriptionBar.Toggle("Live", _filterSettings.allowLive) { _filterSettings.allowLive = it; _filterSettings.save(); loadResults(false); },
SubscriptionBar.Toggle("Planned", _filterSettings.allowPlanned) { _filterSettings.allowPlanned = it; _filterSettings.save(); loadResults(false); }
SubscriptionBar.Toggle(context.getString(R.string.videos), _filterSettings.allowContentTypes.contains(ContentType.MEDIA)) { toggleFilterContentTypes(listOf(ContentType.MEDIA, ContentType.NESTED_VIDEO), it); },
SubscriptionBar.Toggle(context.getString(R.string.posts), _filterSettings.allowContentTypes.contains(ContentType.POST)) { toggleFilterContentType(ContentType.POST, it); },
SubscriptionBar.Toggle(context.getString(R.string.live), _filterSettings.allowLive) { _filterSettings.allowLive = it; _filterSettings.save(); loadResults(false); },
SubscriptionBar.Toggle(context.getString(R.string.planned), _filterSettings.allowPlanned) { _filterSettings.allowPlanned = it; _filterSettings.save(); loadResults(false); }
);
}
@ -283,7 +282,7 @@ class SubscriptionsFeedFragment : MainFragment() {
val cachePager = ChannelContentCache.instance.getSubscriptionCachePager();
val results = cachePager.getResults();
Logger.i(TAG, "Subscription show cache (${results.size})");
setTextCentered(if (results.isEmpty()) "No results found\nSwipe down to refresh" else null);
setTextCentered(if (results.isEmpty()) context.getString(R.string.no_results_found_swipe_down_to_refresh) else null);
setPager(cachePager);
} else {
setTextCentered(null);
@ -299,7 +298,7 @@ class SubscriptionsFeedFragment : MainFragment() {
finishRefreshLayoutLoader();
setLoading(false);
setPager(pager);
setTextCentered(if (pager.getResults().isEmpty()) "No results found\nSwipe down to refresh" else null);
setTextCentered(if (pager.getResults().isEmpty()) context.getString(R.string.no_results_found_swipe_down_to_refresh) else null);
} catch (e: Throwable) {
Logger.e(TAG, "Failed to finish loading", e)
}
@ -326,7 +325,7 @@ class SubscriptionsFeedFragment : MainFragment() {
if (toShow is PluginException)
UIDialogs.toast(
it,
"Plugin [${toShow.config.name}] (${channel}) failed:\n${toShow.message}"
context.getString(R.string.plugin_pluginname_failed_message).replace("{pluginName}", toShow.config.name).replace("{message}", toShow.message ?: "")
);
else
UIDialogs.toast(it, ex.message ?: "");
@ -340,7 +339,7 @@ class SubscriptionsFeedFragment : MainFragment() {
.map { it!! }
.toList();
for(distinctPluginFail in failedPlugins)
UIDialogs.toast(it, "Plugin [${distinctPluginFail.config.name}] failed:\n${distinctPluginFail.message}");
UIDialogs.toast(it, context.getString(R.string.plugin_pluginname_failed_message).replace("{pluginName}", distinctPluginFail.config.name).replace("{message}", distinctPluginFail.message ?: ""));
}
} catch (e: Throwable) {
Logger.e(TAG, "Failed to handle exceptions", e)

View file

@ -541,7 +541,7 @@ class VideoDetailView : ConstraintLayout {
val replyCount = c.replyCount ?: 0;
var metadata = "";
if (replyCount > 0) {
metadata += "$replyCount replies";
metadata += "$replyCount " + context.getString(R.string.replies);
}
if (c is PolycentricPlatformComment) {
@ -579,13 +579,13 @@ class VideoDetailView : ConstraintLayout {
}
fun updateMoreButtons() {
val buttons = listOf(RoundButton(context, R.drawable.ic_add, "Add", TAG_ADD) {
val buttons = listOf(RoundButton(context, R.drawable.ic_add, context.getString(R.string.add), TAG_ADD) {
(video ?: _searchVideo)?.let {
_slideUpOverlay = UISlideOverlays.showAddToOverlay(it, _overlayContainer);
}
},
if(video?.isLive ?: false)
RoundButton(context, R.drawable.ic_chat, "Live Chat", TAG_LIVECHAT) {
RoundButton(context, R.drawable.ic_chat, context.getString(R.string.live_chat), TAG_LIVECHAT) {
video?.let {
try {
loadLiveChat(it);
@ -595,7 +595,7 @@ class VideoDetailView : ConstraintLayout {
}
}
} else null,
RoundButton(context, R.drawable.ic_screen_share, "Background", TAG_BACKGROUND) {
RoundButton(context, R.drawable.ic_screen_share, context.getString(R.string.background), TAG_BACKGROUND) {
if(!allowBackground) {
_player.switchToAudioMode();
allowBackground = true;
@ -607,31 +607,31 @@ class VideoDetailView : ConstraintLayout {
it.text.text = resources.getString(R.string.background);
}
},
RoundButton(context, R.drawable.ic_download, "Download", TAG_DOWNLOAD) {
RoundButton(context, R.drawable.ic_download, context.getString(R.string.download), TAG_DOWNLOAD) {
video?.let {
_slideUpOverlay = UISlideOverlays.showDownloadVideoOverlay(it, _overlayContainer, context.contentResolver);
};
},
RoundButton(context, R.drawable.ic_share, "Share", TAG_SHARE) {
RoundButton(context, R.drawable.ic_share, context.getString(R.string.share), TAG_SHARE) {
video?.let {
Logger.i(TAG, "Share preventPictureInPicture = true");
preventPictureInPicture = true;
shareVideo();
};
},
RoundButton(context, R.drawable.ic_screen_share, "Overlay", TAG_OVERLAY) {
RoundButton(context, R.drawable.ic_screen_share, context.getString(R.string.overlay), TAG_OVERLAY) {
this.startPictureInPicture();
fragment.forcePictureInPicture();
//PiPActivity.startPiP(context);
},
RoundButton(context, R.drawable.ic_export, "Page", TAG_OPEN) {
RoundButton(context, R.drawable.ic_export, context.getString(R.string.page), TAG_OPEN) {
video?.let {
val url = video?.shareUrl ?: _searchVideo?.shareUrl ?: _url;
fragment.navigate<BrowserFragment>(url);
fragment.minimizeVideoDetail();
};
},
RoundButton(context, R.drawable.ic_refresh, "Reload", "Reload") {
RoundButton(context, R.drawable.ic_refresh, context.getString(R.string.reload), "Reload") {
reloadVideo();
}).filterNotNull();
if(!_buttonPinStore.getAllValues().any())
@ -857,7 +857,7 @@ class VideoDetailView : ConstraintLayout {
val subTitleSegments : ArrayList<String> = ArrayList();
if(video.viewCount > 0)
subTitleSegments.add("${video.viewCount.toHumanNumber()} ${if(video.isLive) "watching now" else "views"}");
subTitleSegments.add("${video.viewCount.toHumanNumber()} ${if(video.isLive) context.getString(R.string.watching_now) else context.getString(R.string.views)}");
if(video.datetime != null) {
val diff = video.datetime?.getNowDiffSeconds() ?: 0;
val ago = video.datetime?.toHumanNowDiffString(true)
@ -873,7 +873,7 @@ class VideoDetailView : ConstraintLayout {
_subTitle.text = subTitleSegments.joinToString("");
_playWhenReady = true;
if(video.author.subscribers != null) {
_channelMeta.text = if((video.author.subscribers ?: 0) > 0) video.author.subscribers!!.toHumanNumber() + " subscribers" else "";
_channelMeta.text = if((video.author.subscribers ?: 0) > 0) video.author.subscribers!!.toHumanNumber() + " " + context.getString(R.string.subscribers)else "";
(_channelName.layoutParams as MarginLayoutParams).setMargins(0, (DP_5 * -1).toInt(), 0, 0);
} else {
_channelMeta.text = "";
@ -933,7 +933,7 @@ class VideoDetailView : ConstraintLayout {
}
if(videoDetail.datetime != null && videoDetail.datetime!! > OffsetDateTime.now())
UIDialogs.toast(context, "Planned in ${videoDetail.datetime?.toHumanNowDiffString(true)}")
UIDialogs.toast(context, context.getString(R.string.planned_in) + " ${videoDetail.datetime?.toHumanNowDiffString(true)}")
if (!videoDetail.isLive) {
_player.setPlaybackRate(Settings.instance.playback.getDefaultPlaybackSpeed());
@ -965,7 +965,7 @@ class VideoDetailView : ConstraintLayout {
}
catch(ex: Throwable) {
withContext(Dispatchers.Main) {
UIDialogs.showGeneralErrorDialog(context, "Failed to get Playback Tracker", ex);
UIDialogs.showGeneralErrorDialog(context, context.getString(R.string.failed_to_get_playback_tracker), ex);
};
}
};
@ -983,7 +983,7 @@ class VideoDetailView : ConstraintLayout {
_title.text = video.name;
_channelName.text = video.author.name;
if(video.author.subscribers != null) {
_channelMeta.text = if((video.author.subscribers ?: 0) > 0) video.author.subscribers!!.toHumanNumber() + " subscribers" else "";
_channelMeta.text = if((video.author.subscribers ?: 0) > 0) video.author.subscribers!!.toHumanNumber() + " " + context.getString(R.string.subscribers) else "";
(_channelName.layoutParams as MarginLayoutParams).setMargins(0, (DP_5 * -1).toInt(), 0, 0);
} else {
_channelMeta.text = "";
@ -1008,7 +1008,7 @@ class VideoDetailView : ConstraintLayout {
_platform.setPlatformFromClientID(video.id.pluginId);
val subTitleSegments : ArrayList<String> = ArrayList();
if(video.viewCount > 0)
subTitleSegments.add("${video.viewCount.toHumanNumber()} ${if(video.isLive) "watching now" else "views"}");
subTitleSegments.add("${video.viewCount.toHumanNumber()} ${if(video.isLive) context.getString(R.string.watching_now) else context.getString(R.string.views)}");
if(video.datetime != null) {
val diff = video.datetime?.getNowDiffSeconds() ?: 0;
val ago = video.datetime?.toHumanNowDiffString(true)
@ -1167,7 +1167,7 @@ class VideoDetailView : ConstraintLayout {
livePager = StatePlatform.instance.getLiveEvents(video.url);
} catch (ex: Throwable) {
livePager = null;
UIDialogs.toast("Exception retrieving live events:\n" + ex.message);
UIDialogs.toast(context.getString(R.string.exception_retrieving_live_events) + "\n" + ex.message);
Logger.e(TAG, "Failed to retrieve live chat events", ex);
}
try {
@ -1178,7 +1178,7 @@ class VideoDetailView : ConstraintLayout {
}
catch(ex: Throwable) {
liveChatWindow = null;
UIDialogs.toast("Exception retrieving live chat window:\n" + ex.message);
UIDialogs.toast(context.getString(R.string.exception_retrieving_live_chat_window) + "\n" + ex.message);
Logger.e(TAG, "Failed to retrieve live chat window", ex);
}
val liveChat = livePager?.let {
@ -1200,7 +1200,7 @@ class VideoDetailView : ConstraintLayout {
catch(ex: Throwable) {
Logger.e(TAG, "Failed to load live chat", ex);
UIDialogs.toast("Live chat failed to load\n" + ex.message);
UIDialogs.toast(context.getString(R.string.live_chat_failed_to_load) + "\n" + ex.message);
//_liveChat?.handleEvents(listOf(LiveEventComment("SYSTEM", null, "Failed to load live chat:\n" + ex.message, "#FF0000")))
/*
fragment.lifecycleScope.launch(Dispatchers.Main) {
@ -1257,11 +1257,11 @@ class VideoDetailView : ConstraintLayout {
}
catch(ex: UnsupportedCastException) {
Logger.e(TAG, "Failed to load cast media", ex);
UIDialogs.showGeneralErrorDialog(context, "Unsupported Cast format", ex);
UIDialogs.showGeneralErrorDialog(context, context.getString(R.string.unsupported_cast_format), ex);
}
catch(ex: Throwable) {
Logger.e(TAG, "Failed to load media", ex);
UIDialogs.showGeneralErrorDialog(context, "Failed to load media", ex);
UIDialogs.showGeneralErrorDialog(context, context.getString(R.string.failed_to_load_media), ex);
}
}
private fun loadCurrentVideoCast(video: IPlatformVideoDetails, videoSource: IVideoSource?, audioSource: IAudioSource?, subtitleSource: ISubtitleSource?, resumePositionMs: Long) {
@ -1279,7 +1279,7 @@ class VideoDetailView : ConstraintLayout {
Logger.i(TAG, "onSourceChanged(videoSource=$videoSource, audioSource=$audioSource, resume=$resume)")
if((videoSource == null || videoSource is LocalVideoSource) && (audioSource == null || audioSource is LocalAudioSource))
UIDialogs.toast(context, "Offline Playback", false);
UIDialogs.toast(context, context.getString(R.string.offline_playback), false);
//If LiveStream, set to end
if(videoSource is IDashManifestSource || videoSource is IHLSManifestSource) {
if (video?.isLive == true) {
@ -1320,12 +1320,12 @@ class VideoDetailView : ConstraintLayout {
_didTriggerDatasourceError = true;
UIDialogs.showDialog(context, R.drawable.ic_error_pred,
"Media Error",
"The media source encountered an unauthorized error.\nThis might be solved by a plugin reload.\nWould you like to reload?\n(Experimental)",
context.getString(R.string.media_error),
context.getString(R.string.the_media_source_encountered_an_unauthorized_error_this_might_be_solved_by_a_plugin_reload_would_you_like_to_reload_experimental),
null,
0,
UIDialogs.Action("No", { _didTriggerDatasourceError = false }),
UIDialogs.Action("Yes", {
UIDialogs.Action(context.getString(R.string.no), { _didTriggerDatasourceError = false }),
UIDialogs.Action(context.getString(R.string.yes), {
fragment.lifecycleScope.launch(Dispatchers.IO) {
try {
StatePlatform.instance.reloadClient(context, config.id);
@ -1423,8 +1423,9 @@ class VideoDetailView : ConstraintLayout {
?.filter { it.container == bestAudioContainer }
?.toList() ?: listOf();
_overlay_quality_selector = SlideUpMenuOverlay(this.context, _overlay_quality_container, "Quality", null, true,
if (!_isCasting) SlideUpMenuTitle(this.context).apply { setTitle("Playback Rate") } else null,
_overlay_quality_selector = SlideUpMenuOverlay(this.context, _overlay_quality_container, context.getString(
R.string.quality), null, true,
if (!_isCasting) SlideUpMenuTitle(this.context).apply { setTitle(context.getString(R.string.playback_rate)) } else null,
if (!_isCasting) SlideUpMenuButtonList(this.context).apply {
setButtons(listOf("0.25", "0.5", "0.75", "1.0", "1.25", "1.5", "1.75", "2.0", "2.25"), _player.getPlaybackRate().toString());
onClick.subscribe { v ->
@ -1438,7 +1439,7 @@ class VideoDetailView : ConstraintLayout {
} else null,
if(localVideoSources?.isNotEmpty() == true)
SlideUpMenuGroup(this.context, "Offline Video", "video",
SlideUpMenuGroup(this.context, context.getString(R.string.offline_video), "video",
*localVideoSources.stream()
.map {
SlideUpMenuItem(this.context, R.drawable.ic_movie, it!!.name, "${it.width}x${it.height}", it,
@ -1446,7 +1447,7 @@ class VideoDetailView : ConstraintLayout {
}.toList().toTypedArray())
else null,
if(localAudioSource?.isNotEmpty() == true)
SlideUpMenuGroup(this.context, "Offline Audio", "audio",
SlideUpMenuGroup(this.context, context.getString(R.string.offline_audio), "audio",
*localAudioSource.stream()
.map {
SlideUpMenuItem(this.context, R.drawable.ic_music, it.name, it.bitrate.toHumanBitrate(), it,
@ -1454,7 +1455,7 @@ class VideoDetailView : ConstraintLayout {
}.toList().toTypedArray())
else null,
if(localSubtitleSources?.isNotEmpty() == true)
SlideUpMenuGroup(this.context, "Offline Subtitles", "subtitles",
SlideUpMenuGroup(this.context, context.getString(R.string.offline_subtitles), "subtitles",
*localSubtitleSources
.map {
SlideUpMenuItem(this.context, R.drawable.ic_edit, it.name, "", it,
@ -1462,7 +1463,7 @@ class VideoDetailView : ConstraintLayout {
}.toList().toTypedArray())
else null,
if(liveStreamVideoFormats?.isEmpty() == false)
SlideUpMenuGroup(this.context, "Stream Video", "video",
SlideUpMenuGroup(this.context, context.getString(R.string.stream_video), "video",
*liveStreamVideoFormats.stream()
.map {
SlideUpMenuItem(this.context, R.drawable.ic_movie, it?.label ?: it.containerMimeType ?: it.bitrate.toString(), "${it.width}x${it.height}", it,
@ -1470,7 +1471,7 @@ class VideoDetailView : ConstraintLayout {
}.toList().toTypedArray())
else null,
if(liveStreamAudioFormats?.isEmpty() == false)
SlideUpMenuGroup(this.context, "Stream Audio", "audio",
SlideUpMenuGroup(this.context, context.getString(R.string.stream_audio), "audio",
*liveStreamAudioFormats.stream()
.map {
SlideUpMenuItem(this.context, R.drawable.ic_music, "${it?.label ?: it.containerMimeType} ${it.bitrate}", "", it,
@ -1479,7 +1480,7 @@ class VideoDetailView : ConstraintLayout {
else null,
if(bestVideoSources.isNotEmpty())
SlideUpMenuGroup(this.context, "Video", "video",
SlideUpMenuGroup(this.context, context.getString(R.string.video), "video",
*bestVideoSources.stream()
.map {
SlideUpMenuItem(this.context, R.drawable.ic_movie, it!!.name, if (it.width > 0 && it.height > 0) "${it.width}x${it.height}" else "", it,
@ -1487,7 +1488,7 @@ class VideoDetailView : ConstraintLayout {
}.toList().toTypedArray())
else null,
if(bestAudioSources.isNotEmpty())
SlideUpMenuGroup(this.context, "Audio", "audio",
SlideUpMenuGroup(this.context, context.getString(R.string.audio), "audio",
*bestAudioSources.stream()
.map {
SlideUpMenuItem(this.context, R.drawable.ic_music, it.name, it.bitrate.toHumanBitrate(), it,
@ -1495,7 +1496,7 @@ class VideoDetailView : ConstraintLayout {
}.toList().toTypedArray())
else null,
if(video?.subtitles?.isNotEmpty() ?: false && video != null)
SlideUpMenuGroup(this.context, "Subtitles", "subtitles",
SlideUpMenuGroup(this.context, context.getString(R.string.subtitles), "subtitles",
*video.subtitles
.map {
SlideUpMenuItem(this.context, R.drawable.ic_edit, it.name, "", it,
@ -1618,12 +1619,13 @@ class VideoDetailView : ConstraintLayout {
private fun handleUnavailableVideo(msg: String? = null) {
if (!nextVideo()) {
if(video?.datetime == null || video?.datetime!! < OffsetDateTime.now().minusHours(1))
UIDialogs.showDialog(context, R.drawable.ic_lock, "Unavailable video", msg ?: "This video is unavailable.", null, 0,
UIDialogs.Action("Back", {
UIDialogs.showDialog(context, R.drawable.ic_lock, context.getString(R.string.unavailable_video), msg ?: context.getString(R.string.this_video_is_unavailable), null, 0,
UIDialogs.Action(context.getString(R.string.back), {
this@VideoDetailView.onClose.emit();
}, UIDialogs.ActionStyle.PRIMARY));
}, UIDialogs.ActionStyle.PRIMARY)
);
} else {
StateAnnouncement.instance.registerAnnouncement(video?.id?.value + "_Q_UNAVAILABLE", "Unavailable video", "There was an unavailable video in your queue [${video?.name}] by [${video?.author?.name}].", AnnouncementType.SESSION)
StateAnnouncement.instance.registerAnnouncement(video?.id?.value + "_Q_UNAVAILABLE", context.getString(R.string.unavailable_video), context.getString(R.string.there_was_an_unavailable_video_in_your_queue_videoname_by_authorname).replace("{videoName}", video?.name ?: "").replace("{authorName}", video?.author?.name ?: ""), AnnouncementType.SESSION)
}
video?.let { StatePlatform.instance.clearContentDetailCache(it.url) };
@ -1875,9 +1877,9 @@ class VideoDetailView : ConstraintLayout {
_player.getGlobalVisibleRect(r);
r.right = r.right - _player.paddingEnd;
val playpauseAction = if(_player.playing)
RemoteAction(Icon.createWithResource(context, R.drawable.ic_pause_notif), "Pause", "Pauses the video", MediaControlReceiver.getPauseIntent(context, 5));
RemoteAction(Icon.createWithResource(context, R.drawable.ic_pause_notif), context.getString(R.string.pause), context.getString(R.string.pauses_the_video), MediaControlReceiver.getPauseIntent(context, 5));
else
RemoteAction(Icon.createWithResource(context, R.drawable.ic_play_notif), "Play", "Resumes the video", MediaControlReceiver.getPlayIntent(context, 6));
RemoteAction(Icon.createWithResource(context, R.drawable.ic_play_notif), context.getString(R.string.play), context.getString(R.string.resumes_the_video), MediaControlReceiver.getPlayIntent(context, 6));
return PictureInPictureParams.Builder()
.setAspectRatio(Rational(videoSourceWidth, videoSourceHeight))
@ -2066,7 +2068,7 @@ class VideoDetailView : ConstraintLayout {
}, UIDialogs.ActionStyle.PRIMARY)
);
} else {
StateAnnouncement.instance.registerAnnouncement(video?.id?.value + "_Q_NOSOURCES", "Video without source", "There was a in your queue [${video?.name}] by [${video?.author?.name}] without the required source being enabled, playback was skipped.", AnnouncementType.SESSION)
StateAnnouncement.instance.registerAnnouncement(video?.id?.value + "_Q_NOSOURCES", context.getString(R.string.video_without_source), context.getString(R.string.there_was_a_in_your_queue_videoname_by_authorname_without_the_required_source_being_enabled_playback_was_skipped).replace("{videoName}", video?.name ?: "").replace("{authorName}", video?.author?.name ?: ""), AnnouncementType.SESSION)
}
}
.exception<ContentNotAvailableYetException> {
@ -2085,9 +2087,10 @@ class VideoDetailView : ConstraintLayout {
Logger.w(TAG, "exception<ScriptImplementationException>", it)
if (!nextVideo()) {
UIDialogs.showGeneralRetryErrorDialog(context, "Failed to load video (ScriptImplementationException)", it, ::fetchVideo);
UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_video_scriptimplementationexception), it, ::fetchVideo);
} else {
StateAnnouncement.instance.registerAnnouncement(video?.id?.value + "_Q_INVALIDVIDEO", "Invalid video", "There was an invalid video in your queue [${video?.name}] by [${video?.author?.name}], playback was skipped.", AnnouncementType.SESSION)
StateAnnouncement.instance.registerAnnouncement(video?.id?.value + "_Q_INVALIDVIDEO", context.getString(R.string.invalid_video), context.getString(
R.string.there_was_an_invalid_video_in_your_queue_videoname_by_authorname_playback_was_skipped).replace("{videoName}", video?.name ?: "").replace("{authorName}", video?.author?.name ?: ""), AnnouncementType.SESSION)
}
}
.exception<ScriptAgeException> {
@ -2102,7 +2105,9 @@ class VideoDetailView : ConstraintLayout {
this@VideoDetailView.onClose.emit();
}, UIDialogs.ActionStyle.PRIMARY));
} else {
StateAnnouncement.instance.registerAnnouncement(video?.id?.value + "_Q_AGERESTRICT", "Age restricted video", "There was an age restricted video in your queue [${video?.name}] by [${video?.author?.name}], this video was not accessible and playback was skipped.", AnnouncementType.SESSION)
StateAnnouncement.instance.registerAnnouncement(video?.id?.value + "_Q_AGERESTRICT", context.getString(R.string.age_restricted_video),
context.getString(R.string.there_was_an_age_restricted_video_in_your_queue_videoname_by_authorname_this_video_was_not_accessible_and_playback_was_skipped).replace("{videoName}", video?.name ?: "").replace("{authorName}", video?.author?.name ?: ""),
AnnouncementType.SESSION)
}
}
.exception<ScriptUnavailableException> {
@ -2118,7 +2123,7 @@ class VideoDetailView : ConstraintLayout {
_retryJob = null;
_liveTryJob?.cancel();
_liveTryJob = null;
UIDialogs.showGeneralRetryErrorDialog(context, "Failed to load video (ScriptException)", it, ::fetchVideo);
UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_video_scriptexception), it, ::fetchVideo);
}
}
.exception<Throwable> {
@ -2130,7 +2135,7 @@ class VideoDetailView : ConstraintLayout {
_retryJob = null;
_liveTryJob?.cancel();
_liveTryJob = null;
UIDialogs.showGeneralRetryErrorDialog(context, "Failed to load video", it, ::fetchVideo);
UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_video), it, ::fetchVideo);
}
} else TaskHandler(IPlatformVideoDetails::class.java, {fragment.lifecycleScope});
@ -2171,7 +2176,7 @@ class VideoDetailView : ConstraintLayout {
val toWait = _liveStreamCheckInterval.toList().sortedBy { abs(diffSeconds - it.first) }.firstOrNull()?.second?.toLong() ?: return;
fragment.lifecycleScope.launch(Dispatchers.Main){
UIDialogs.toast(context, "Not yet available, retrying in ${toWait}s");
UIDialogs.toast(context, context.getString(R.string.not_yet_available_retrying_in_time_s).replace("{time}", toWait.toString()));
}
_liveTryJob?.cancel();
@ -2185,7 +2190,7 @@ class VideoDetailView : ConstraintLayout {
if(videoDetail.datetime != null && videoDetail.live == null && !videoDetail.video.videoSources.any()) {
if(videoDetail.datetime!! > OffsetDateTime.now())
withContext(Dispatchers.Main) {
UIDialogs.toast(context, "Planned in ${videoDetail.datetime?.toHumanNowDiffString(true)}");
UIDialogs.toast(context, context.getString(R.string.planned_in) + " ${videoDetail.datetime?.toHumanNowDiffString(true)}");
}
startLiveTry(liveTryVideo);
}
@ -2198,7 +2203,7 @@ class VideoDetailView : ConstraintLayout {
catch(ex: Throwable) {
Logger.e(TAG, "Failed to live try fetch video.", ex);
withContext(Dispatchers.Main) {
UIDialogs.toast(context, "Failed to retry for live stream");
UIDialogs.toast(context, context.getString(R.string.failed_to_retry_for_live_stream));
}
}
}

View file

@ -91,7 +91,7 @@ abstract class VideoListEditorView : LinearLayout {
}
protected fun setVideoCount(videoCount: Int = -1) {
_textMetadata.text = if (videoCount == -1) "" else "${videoCount} videos";
_textMetadata.text = if (videoCount == -1) "" else "${videoCount} " + context.getString(R.string.videos);
}
protected fun setVideos(videos: List<IPlatformVideo>?, canEdit: Boolean) {
@ -105,7 +105,7 @@ abstract class VideoListEditorView : LinearLayout {
.into(it);
};
} else {
_textMetadata.text = "0 videos";
_textMetadata.text = "0 " + context.getString(R.string.videos);
if(_imagePlaylistThumbnail != null) {
Glide.with(_imagePlaylistThumbnail)
.load(R.drawable.placeholder_video_thumbnail)

View file

@ -35,7 +35,7 @@ class GeneralTopBarFragment : TopFragment() {
val view = inflater.inflate(R.layout.fragment_overview_top_bar, container, false);
view.findViewById<ImageView>(R.id.app_icon).setOnClickListener {
UIDialogs.toast("This app is in development. Please submit bug reports and understand that many features are incomplete.", true);
UIDialogs.toast(getString(R.string.this_app_is_in_development_please_submit_bug_reports_and_understand_that_many_features_are_incomplete), true);
};
val buttonSearch: ImageButton = view.findViewById(R.id.button_search);

View file

@ -194,7 +194,7 @@ class SearchTopBarFragment : TopFragment() {
if (editSearch != null) {
val text = editSearch.text.toString();
if (text.length < 3) {
UIDialogs.toast("Please use at least 3 characters");
UIDialogs.toast(getString(R.string.please_use_at_least_3_characters));
return;
}

View file

@ -161,7 +161,8 @@
<string name="incompatible">The operation failed because it is fundamentally incompatible with this device</string>
<string name="invalid">The operation failed because one or more of the APKs was invalid</string>
<string name="not_enough_storage">The operation failed because of storage issues</string>
<string name="live">LIVE</string>
<string name="live_capitalized">LIVE</string>
<string name="live">Live</string>
<string name="click_to_read_more">Click to read more</string>
<string name="click_to_hide">Click to hide</string>
<string name="version">Version</string>
@ -184,6 +185,7 @@
<string name="create">Create</string>
<string name="ok">OK</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="confirm">Confirm</string>
<string name="confirm_delete_playlist">Are you sure you want to delete this playlist?</string>
<string name="confirm_delete_subscription">Are you sure you want to delete this subscription?</string>
@ -512,6 +514,176 @@
<string name="enable_where_this_plugins_content_are_visible">Enable where this plugin\'s content are visible</string>
<string name="show_content_in_home_tab">Show content in home tab</string>
<string name="show_content_in_search_results">Show content in search results</string>
<string name="no_valid_url_provided">No valid URL provided..</string>
<string name="invalid_config_format">Invalid Config Format</string>
<string name="failed_to_fetch_configuration">Failed to fetch configuration</string>
<string name="failed_to_fetch_script">Failed to fetch script</string>
<string name="url_access">URL Access</string>
<string name="the_plugin_will_have_access_to_the_following_domains">The plugin will have access to the following domains</string>
<string name="eval_access">Eval Access</string>
<string name="the_plugin_will_have_access_to_eval_capability_remote_injection">The plugin will have access to eval capability (remote injection)</string>
<string name="failed_to_scan_qr_code">Failed to scan QR code</string>
<string name="not_a_plugin_url">Not a plugin URL</string>
<string name="scan_a_qr_code">Scan a QR Code</string>
<string name="not_implemented_yet">Not implemented yet..</string>
<string name="unknown_context">Unknown Context</string>
<string name="something_went_wrong_missing_stack_trace">Something went wrong... missing stack trace?</string>
<string name="logs_already_submitted">Logs already submitted.</string>
<string name="no_logs_found">No logs found.</string>
<string name="failed_automated_share_share_manually">Failed automated share, share manually?</string>
<string name="shared_id">Shared {id}</string>
<string name="unhandled_exception_in_vs">Unhandled exception in VS</string>
<string name="send_exception_to_developers">Send exception to developers...</string>
<string name="your_license_key_has_been_set_an_app_restart_might_be_required">Your license key has been set!\nAn app restart might be required.</string>
<string name="invalid_license_format">Invalid license format</string>
<string name="unknown_content_format">Unknown content format</string>
<string name="unknown_file_format">Unknown file format</string>
<string name="unknown_polycentric_format">Unknown Polycentric format</string>
<string name="unknown_url_format">Unknown url format</string>
<string name="failed_to_handle_file">Failed to handle file</string>
<string name="unknown_reconstruction_type">Unknown reconstruction type</string>
<string name="failed_to_parse_newpipe_subscriptions">Failed to parse NewPipe Subscriptions</string>
<string name="failed_to_generate_qr_code">Failed to generate QR code</string>
<string name="share_text">Share Text</string>
<string name="copied_text">Copied Text</string>
<string name="must_be_at_least_3_characters_long">Must be at least 3 characters long.</string>
<string name="failed_to_create_profile">Failed to create profile.</string>
<string name="failed_to_fully_backfill_servers">Failed to fully backfill servers.</string>
<string name="sign_in_to_this_identity">Sign in to this identity</string>
<string name="text_field_does_not_contain_any_data">Text field does not contain any data</string>
<string name="not_a_valid_url">Not a valid URL</string>
<string name="this_profile_is_already_imported">This profile is already imported</string>
<string name="failed_to_import_profile">Failed to import profile:</string>
<string name="failed_to_backfill_client">Failed to backfill client</string>
<string name="are_you_sure_you_want_to_remove_this_profile">Are you sure you want to remove this profile?</string>
<string name="no_process_handle_set">No process handle set</string>
<string name="name_must_be_at_least_3_characters_long">Name must be at least 3 characters long</string>
<string name="process_handle_unset">Process handle unset</string>
<string name="failed_to_read_image">Failed to read image</string>
<string name="changes_have_been_saved">Changes have been saved</string>
<string name="failed_to_synchronize_changes">Failed to synchronize changes</string>
<string name="image_picker_cancelled">Image picker cancelled</string>
<string name="you_are_now_in_developer_mode">You are now in developer mode</string>
<string name="subscribers">Subscribers</string>
<string name="find_name_on">Find {name} on</string>
<string name="failed_to_fetch">Failed to fetch</string>
<string name="thanks_for_your_purchase_a_key_will_be_sent_to_your_email_after_your_payment_has_been_received">Thanks for your purchase, a key will be sent to your email after your payment has been received!</string>
<string name="payment_failed">Payment failed</string>
<string name="payment_succeeded">Payment succeeded</string>
<string name="enter_license_key">Enter license key</string>
<string name="invalid_license_key">Invalid license key</string>
<string name="license">License</string>
<string name="failed_to_activate_key">Failed to activate key</string>
<string name="no_source_enabled_to_support_this_channel">No source enabled to support this channel</string>
<string name="do_you_want_to_convert_channel_channelname_to_a_playlist">Do you want to convert channel {channelName} to a playlist?</string>
<string name="failed_to_convert_channel">Failed to convert channel</string>
<string name="page">Page</string>
<string name="hide">Hide</string>
<string name="hide_from_home">Hide from Home</string>
<string name="play_feed_as_queue">Play Feed as Queue</string>
<string name="play_entire_feed">Play entire feed</string>
<string name="queued">Queued</string>
<string name="used">Used</string>
<string name="available">Available</string>
<string name="failed_to_load_next_page">Failed to load next page</string>
<string name="plugin_pluginname_failed_message">Plugin {pluginName} failed:\n{message}</string>
<string name="plugin_failed_due_to">Plugin failed due to:</string>
<string name="failed_to_get_home_plugin">Failed to get Home\nPlugin</string>
<string name="failed_to_get_home">Failed to get Home</string>
<string name="no_home_available">No home available</string>
<string name="no_home_page_is_available_please_check_if_you_are_connected_to_the_internet_and_refresh">No home page is available, please check if you are connected to the internet and refresh.</string>
<string name="import_playlists">Import Playlists</string>
<string name="playlists_imported">playlists imported.</string>
<string name="index_out_of_size_selected">{index} out of {size} selected</string>
<string name="import_subscriptions">Import Subscriptions</string>
<string name="subscriptions_imported">subscriptions imported.</string>
<string name="edit_playlist">Edit playlist</string>
<string name="share_as_text">Share as Text</string>
<string name="share_as_a_list_of_video_urls">Share as a list of video urls</string>
<string name="share_as_import">Share as Import</string>
<string name="share_as_a_import_file_for_grayjay">Share as a import file for Grayjay</string>
<string name="failed_to_load_playlist">Failed to load playlist</string>
<string name="please_wait_for_playlist_to_finish_loading">Please wait for playlist to finish loading</string>
<string name="playlist_copied_as_local_playlist">Playlist copied as local playlist</string>
<string name="are_you_sure_you_want_to_delete_the_downloaded_videos">Are you sure you want to delete the downloaded videos?</string>
<string name="create_new_playlist">Create new playlist</string>
<string name="expected_media_content_found">Expected media content, found</string>
<string name="failed_to_load_post">Failed to load post.</string>
<string name="replies">replies</string>
<string name="plugin_settings_saved">Plugin settings saved</string>
<string name="plugin_settings">Plugin settings</string>
<string name="these_settings_are_defined_by_the_plugin">These settings are defined by the plugin</string>
<string name="failed_to_load_source">Failed to load source</string>
<string name="check_for_updates">Check for updates</string>
<string name="checks_for_new_versions_of_the_source">Checks for new versions of the source</string>
<string name="authentication">Authentication</string>
<string name="sign_out_of_the_platform">Sign out of the platform</string>
<string name="import_your_subscriptions_from_this_source">Import your subscriptions from this source</string>
<string name="import_your_playlists_from_this_source">Import your playlists from this source</string>
<string name="login">Login</string>
<string name="sign_into_the_platform_of_this_source">Sign into the platform of this source</string>
<string name="management">Management</string>
<string name="uninstall">Uninstall</string>
<string name="removes_the_plugin_from_the_app">Removes the plugin from the app</string>
<string name="delete_captcha">Delete Captcha</string>
<string name="deletes_stored_captcha_answer_for_this_plugin">Deletes stored captcha answer for this plugin</string>
<string name="failed_to_retrieve_playlists">Failed to retrieve playlists.</string>
<string name="subscriptioncount_user_subscriptions_retrieved">{subscriptionCount} user subscriptions retrieved.</string>
<string name="failed_to_retrieve_subscriptions">Failed to retrieve subscriptions.</string>
<string name="are_you_sure_you_want_to_uninstall">Are you sure you want to uninstall</string>
<string name="uninstalled">Uninstalled </string>
<string name="failed_to_check_for_updates">Failed to check for updates</string>
<string name="plugin_is_fully_up_to_date">Plugin is fully up to date</string>
<string name="rate_limit_warning">Rate Limit Warning</string>
<string name="this_is_a_temporary_measure_to_prevent_people_from_hitting_rate_limit_until_we_have_better_support_for_lots_of_subscriptions">This is a temporary measure to prevent people from hitting rate limit until we have better support for lots of subscriptions.</string>
<string name="you_have_too_many_subscriptions_for_the_following_plugins">\n\nYou have too many subscriptions for the following plugins:\n</string>
<string name="posts">Posts</string>
<string name="planned">Planned</string>
<string name="no_results_found_swipe_down_to_refresh">No results found\nSwipe down to refresh</string>
<string name="overlay">Overlay</string>
<string name="reload">Reload</string>
<string name="watching_now">watching now</string>
<string name="views">views</string>
<string name="planned_in">Planned in</string>
<string name="failed_to_get_playback_tracker">Failed to get Playback Tracker</string>
<string name="exception_retrieving_live_events">Exception retrieving live events:</string>
<string name="exception_retrieving_live_chat_window">Exception retrieving live chat window:</string>
<string name="live_chat_failed_to_load">Live chat failed to load</string>
<string name="unsupported_cast_format">Unsupported Cast format</string>
<string name="failed_to_load_media">Failed to load media</string>
<string name="offline_playback">Offline Playback</string>
<string name="media_error">Media Error</string>
<string name="the_media_source_encountered_an_unauthorized_error_this_might_be_solved_by_a_plugin_reload_would_you_like_to_reload_experimental">The media source encountered an unauthorized error.\nThis might be solved by a plugin reload.\nWould you like to reload?\n(Experimental)</string>
<string name="playback_rate">Playback Rate</string>
<string name="quality">Quality</string>
<string name="offline_video">Offline Video</string>
<string name="offline_audio">Offline Audio</string>
<string name="offline_subtitles">Offline Subtitles</string>
<string name="stream_video">Stream Video</string>
<string name="stream_audio">Stream Audio</string>
<string name="audio">Audio</string>
<string name="subtitles">Subtitles</string>
<string name="unavailable_video">Unavailable video</string>
<string name="this_video_is_unavailable">This video is unavailable.</string>
<string name="there_was_an_unavailable_video_in_your_queue_videoname_by_authorname">There was an unavailable video in your queue [{videoName}] by [{authorName}].</string>
<string name="back">Back</string>
<string name="pause">Pause</string>
<string name="play">Play</string>
<string name="pauses_the_video">Pauses the video</string>
<string name="resumes_the_video">Resumes the video</string>
<string name="video_without_source">Video without source</string>
<string name="there_was_a_in_your_queue_videoname_by_authorname_without_the_required_source_being_enabled_playback_was_skipped">There was a in your queue [{videoName}] by [{authorName}] without the required source being enabled, playback was skipped.</string>
<string name="failed_to_load_video_scriptimplementationexception">Failed to load video (ScriptImplementationException)</string>
<string name="invalid_video">Invalid video</string>
<string name="there_was_an_invalid_video_in_your_queue_videoname_by_authorname_playback_was_skipped">There was an invalid video in your queue [{videoName}] by [{authorName}], playback was skipped.</string>
<string name="age_restricted_video">Age restricted video</string>
<string name="there_was_an_age_restricted_video_in_your_queue_videoname_by_authorname_this_video_was_not_accessible_and_playback_was_skipped">There was an age restricted video in your queue [{videoName}] by [{authorName}], this video was not accessible and playback was skipped.</string>
<string name="failed_to_load_video_scriptexception">Failed to load video (ScriptException)</string>
<string name="failed_to_load_video">Failed to load video</string>
<string name="not_yet_available_retrying_in_time_s">Not yet available, retrying in {time}s</string>
<string name="failed_to_retry_for_live_stream">Failed to retry for live stream</string>
<string name="this_app_is_in_development_please_submit_bug_reports_and_understand_that_many_features_are_incomplete">This app is in development. Please submit bug reports and understand that many features are incomplete.</string>
<string name="please_use_at_least_3_characters">Please use at least 3 characters</string>
<string-array name="casting_device_type_array">
<item>FastCast</item>
<item>ChromeCast</item>

@ -1 +1 @@
Subproject commit 3090ca3af03ce276fc167d47f49daa76a5b67dd7
Subproject commit dfecd64655d1434d5d9345acadade6388942dc7b

4
lint.xml Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<issue id="SpUsage" severity="ignore" />
</lint>