Further rework strings

This commit is contained in:
kitsunyan
2020-07-19 02:52:09 +03:00
parent 47f7207242
commit 6a55edcaa7
11 changed files with 171 additions and 173 deletions
+1 -1
View File
@@ -12,7 +12,7 @@
<application <application
android:name=".MainApplication" android:name=".MainApplication"
android:label="@string/app_name" android:label="@string/application_name"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:supportsRtl="true" android:supportsRtl="true"
android:allowBackup="false" android:allowBackup="false"
@@ -315,11 +315,11 @@ class EditRepositoryFragment(): ScreenFragment() {
if (usernameInvalid) { if (usernameInvalid) {
layout.usernameError.setText(R.string.invalid_username_format) layout.usernameError.setText(R.string.invalid_username_format)
} else if (usernameEmpty) { } else if (usernameEmpty) {
layout.usernameError.setText(R.string.username_is_not_specified) layout.usernameError.setText(R.string.username_missing)
} }
layout.username.setError(usernameEmpty) layout.username.setError(usernameEmpty)
if (passwordEmpty) { if (passwordEmpty) {
layout.passwordError.setText(R.string.password_is_not_specified) layout.passwordError.setText(R.string.password_missing)
} }
layout.password.setError(passwordEmpty) layout.password.setError(passwordEmpty)
usernamePasswordError = usernameInvalid || usernameEmpty || passwordEmpty usernamePasswordError = usernameInvalid || usernameEmpty || passwordEmpty
@@ -474,7 +474,7 @@ class EditRepositoryFragment(): ScreenFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): AlertDialog { override fun onCreateDialog(savedInstanceState: Bundle?): AlertDialog {
val mirrors = requireArguments().getStringArrayList(EXTRA_MIRRORS)!! val mirrors = requireArguments().getStringArrayList(EXTRA_MIRRORS)!!
return AlertDialog.Builder(requireContext()) return AlertDialog.Builder(requireContext())
.setTitle(R.string.select_the_mirror) .setTitle(R.string.select_mirror)
.setItems(mirrors.toTypedArray()) { _, position -> (parentFragment as EditRepositoryFragment) .setItems(mirrors.toTypedArray()) { _, position -> (parentFragment as EditRepositoryFragment)
.setMirror(mirrors[position]) } .setMirror(mirrors[position]) }
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
@@ -126,19 +126,19 @@ class MessageDialog(): DialogFragment() {
val dialog = AlertDialog.Builder(requireContext()) val dialog = AlertDialog.Builder(requireContext())
when (val message = requireArguments().getParcelable<Message>(EXTRA_MESSAGE)!!) { when (val message = requireArguments().getParcelable<Message>(EXTRA_MESSAGE)!!) {
is Message.DeleteRepositoryConfirm -> { is Message.DeleteRepositoryConfirm -> {
dialog.setTitle(R.string.confirm_action) dialog.setTitle(R.string.confirmation)
dialog.setMessage(R.string.delete_repository_confirm) dialog.setMessage(R.string.delete_repository_DESC)
dialog.setPositiveButton(R.string.delete) { _, _ -> (parentFragment as RepositoryFragment).onDeleteConfirm() } dialog.setPositiveButton(R.string.delete) { _, _ -> (parentFragment as RepositoryFragment).onDeleteConfirm() }
dialog.setNegativeButton(R.string.cancel, null) dialog.setNegativeButton(R.string.cancel, null)
} }
is Message.CantEditSyncing -> { is Message.CantEditSyncing -> {
dialog.setTitle(R.string.action_failed) dialog.setTitle(R.string.action_failed)
dialog.setMessage(R.string.cant_edit_sync_description) dialog.setMessage(R.string.cant_edit_sync_DESC)
dialog.setPositiveButton(R.string.ok, null) dialog.setPositiveButton(R.string.ok, null)
} }
is Message.Link -> { is Message.Link -> {
dialog.setTitle(R.string.confirm_action) dialog.setTitle(R.string.confirmation)
dialog.setMessage(getString(R.string.open_link_confirm_format, message.uri.toString())) dialog.setMessage(getString(R.string.open_DESC_FORMAT, message.uri.toString()))
dialog.setPositiveButton(R.string.ok) { _, _ -> dialog.setPositiveButton(R.string.ok) { _, _ ->
try { try {
startActivity(Intent(Intent.ACTION_VIEW, message.uri)) startActivity(Intent(Intent.ACTION_VIEW, message.uri))
@@ -177,7 +177,7 @@ class MessageDialog(): DialogFragment() {
if (builder.isNotEmpty()) { if (builder.isNotEmpty()) {
builder.delete(builder.length - 2, builder.length) builder.delete(builder.length - 2, builder.length)
} else { } else {
builder.append(getString(R.string.no_description_available_description)) builder.append(getString(R.string.no_description_available_DESC))
} }
dialog.setTitle(title) dialog.setTitle(title)
dialog.setMessage(builder) dialog.setMessage(builder)
@@ -190,19 +190,19 @@ class MessageDialog(): DialogFragment() {
val maxSdkVersion = if (Release.Incompatibility.MaxSdk in message.incompatibilities) val maxSdkVersion = if (Release.Incompatibility.MaxSdk in message.incompatibilities)
message.maxSdkVersion else null message.maxSdkVersion else null
if (minSdkVersion != null || maxSdkVersion != null) { if (minSdkVersion != null || maxSdkVersion != null) {
val versionMessage = minSdkVersion?.let { getString(R.string.incompatible_sdk_min_description_format, it) } val versionMessage = minSdkVersion?.let { getString(R.string.incompatible_api_min_DESC_FORMAT, it) }
?: maxSdkVersion?.let { getString(R.string.incompatible_sdk_max_description_format, it) } ?: maxSdkVersion?.let { getString(R.string.incompatible_api_max_DESC_FORMAT, it) }
builder.append(getString(R.string.incompatible_sdk_description_format, builder.append(getString(R.string.incompatible_api_DESC_FORMAT,
Android.name, Android.sdk, versionMessage.orEmpty())).append("\n\n") Android.name, Android.sdk, versionMessage.orEmpty())).append("\n\n")
} }
if (Release.Incompatibility.Platform in message.incompatibilities) { if (Release.Incompatibility.Platform in message.incompatibilities) {
builder.append(getString(R.string.incompatible_platforms_description_format, builder.append(getString(R.string.incompatible_platforms_DESC_FORMAT,
Android.primaryPlatform ?: getString(R.string.unknown), Android.primaryPlatform ?: getString(R.string.unknown),
message.platforms.joinToString(separator = ", "))).append("\n\n") message.platforms.joinToString(separator = ", "))).append("\n\n")
} }
val features = message.incompatibilities.mapNotNull { it as? Release.Incompatibility.Feature } val features = message.incompatibilities.mapNotNull { it as? Release.Incompatibility.Feature }
if (features.isNotEmpty()) { if (features.isNotEmpty()) {
builder.append(getString(R.string.incompatible_features_description)) builder.append(getString(R.string.incompatible_features_DESC))
for (feature in features) { for (feature in features) {
builder.append("\n\u2022 ").append(feature.feature) builder.append("\n\u2022 ").append(feature.feature)
} }
@@ -217,12 +217,12 @@ class MessageDialog(): DialogFragment() {
} }
is Message.ReleaseOlder -> { is Message.ReleaseOlder -> {
dialog.setTitle(R.string.incompatible_version) dialog.setTitle(R.string.incompatible_version)
dialog.setMessage(R.string.incompatible_older_description) dialog.setMessage(R.string.incompatible_older_DESC)
dialog.setPositiveButton(R.string.ok, null) dialog.setPositiveButton(R.string.ok, null)
} }
is Message.ReleaseSignatureMismatch -> { is Message.ReleaseSignatureMismatch -> {
dialog.setTitle(R.string.incompatible_version) dialog.setTitle(R.string.incompatible_version)
dialog.setMessage(R.string.incompatible_signature_description) dialog.setMessage(R.string.incompatible_signature_DESC)
dialog.setPositiveButton(R.string.ok, null) dialog.setPositiveButton(R.string.ok, null)
} }
}::class }::class
@@ -54,12 +54,12 @@ class PreferencesFragment: ScreenFragment() {
addEnumeration(Preferences.Key.AutoSync, getString(R.string.sync_repositories_automatically)) { addEnumeration(Preferences.Key.AutoSync, getString(R.string.sync_repositories_automatically)) {
when (it) { when (it) {
Preferences.AutoSync.Never -> getString(R.string.never) Preferences.AutoSync.Never -> getString(R.string.never)
Preferences.AutoSync.Wifi -> getString(R.string.over_wifi) Preferences.AutoSync.Wifi -> getString(R.string.only_on_wifi)
Preferences.AutoSync.Always -> getString(R.string.always) Preferences.AutoSync.Always -> getString(R.string.always)
} }
} }
addSwitch(Preferences.Key.UpdateNotify, getString(R.string.update_notifications), addSwitch(Preferences.Key.UpdateNotify, getString(R.string.notify_about_updates),
getString(R.string.update_notifications_summary)) getString(R.string.notify_about_updates_summary))
addSwitch(Preferences.Key.UpdateUnstable, getString(R.string.unstable_updates), addSwitch(Preferences.Key.UpdateUnstable, getString(R.string.unstable_updates),
getString(R.string.unstable_updates_summary)) getString(R.string.unstable_updates_summary))
} }
@@ -102,24 +102,24 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
private enum class SectionType(val titleResId: Int, val colorAttrResId: Int) { private enum class SectionType(val titleResId: Int, val colorAttrResId: Int) {
ANTI_FEATURES(R.string.anti_features, R.attr.colorError), ANTI_FEATURES(R.string.anti_features, R.attr.colorError),
WHATS_NEW(R.string.whats_new, android.R.attr.colorAccent), CHANGES(R.string.changes, android.R.attr.colorAccent),
LINKS(R.string.links, android.R.attr.colorAccent), LINKS(R.string.links, android.R.attr.colorAccent),
DONATE(R.string.donate, android.R.attr.colorAccent), DONATE(R.string.donate, android.R.attr.colorAccent),
PERMISSIONS(R.string.permissions, android.R.attr.colorAccent), PERMISSIONS(R.string.permissions, android.R.attr.colorAccent),
SCREENSHOTS(R.string.screenshots, android.R.attr.colorAccent), SCREENSHOTS(R.string.screenshots, android.R.attr.colorAccent),
RELEASES(R.string.releases, android.R.attr.colorAccent) VERSIONS(R.string.versions, android.R.attr.colorAccent)
} }
internal enum class ExpandType { NOTHING, DESCRIPTION, WHATS_NEW, internal enum class ExpandType { NOTHING, DESCRIPTION, CHANGES,
LINKS, DONATES, PERMISSIONS, SCREENSHOTS, RELEASES } LINKS, DONATES, PERMISSIONS, SCREENSHOTS, VERSIONS }
private enum class TextType { DESCRIPTION, ANTI_FEATURES, WHATS_NEW } private enum class TextType { DESCRIPTION, ANTI_FEATURES, CHANGES }
private enum class LinkType(val iconResId: Int, val titleResId: Int, private enum class LinkType(val iconResId: Int, val titleResId: Int,
val format: ((Context, String) -> String)? = null) { val format: ((Context, String) -> String)? = null) {
AUTHOR(R.drawable.ic_person, R.string.authors_website), AUTHOR(R.drawable.ic_person, R.string.author_website),
EMAIL(R.drawable.ic_email, R.string.authors_email), EMAIL(R.drawable.ic_email, R.string.author_email),
LICENSE(R.drawable.ic_copyright, R.string.license, LICENSE(R.drawable.ic_copyright, R.string.license,
format = { context, text -> context.getString(R.string.license_format, text) }), format = { context, text -> context.getString(R.string.license_FORMAT, text) }),
SOURCE(R.drawable.ic_code, R.string.source_code), SOURCE(R.drawable.ic_code, R.string.source_code),
TRACKER(R.drawable.ic_bug_report, R.string.bug_tracker), TRACKER(R.drawable.ic_bug_report, R.string.bug_tracker),
CHANGELOG(R.drawable.ic_history, R.string.changelog), CHANGELOG(R.drawable.ic_history, R.string.changelog),
@@ -636,18 +636,18 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
val antiFeatures = productRepository.first.antiFeatures.map { val antiFeatures = productRepository.first.antiFeatures.map {
when (it) { when (it) {
"Ads" -> context.getString(R.string.anti_feature_advertising) "Ads" -> context.getString(R.string.has_advertising)
"ApplicationDebuggable" -> context.getString(R.string.anti_feature_application_debuggable) "ApplicationDebuggable" -> context.getString(R.string.compiled_for_debugging)
"DisabledAlgorithm" -> context.getString(R.string.anti_feature_disabled_algorithm) "DisabledAlgorithm" -> context.getString(R.string.signed_using_unsafe_algorithm)
"KnownVuln" -> context.getString(R.string.anti_feature_known_vulnerabilities) "KnownVuln" -> context.getString(R.string.has_security_vulnerabilities)
"NoSourceSince" -> context.getString(R.string.anti_feature_no_source_since) "NoSourceSince" -> context.getString(R.string.source_code_no_longer_available)
"NonFreeAdd" -> context.getString(R.string.anti_feature_non_free_addons) "NonFreeAdd" -> context.getString(R.string.promotes_non_free_software)
"NonFreeAssets" -> context.getString(R.string.anti_feature_non_free_assets) "NonFreeAssets" -> context.getString(R.string.contains_non_free_media)
"NonFreeDep" -> context.getString(R.string.anti_feature_non_free_dependencies) "NonFreeDep" -> context.getString(R.string.has_non_free_dependencies)
"NonFreeNet" -> context.getString(R.string.anti_feature_non_free_network) "NonFreeNet" -> context.getString(R.string.promotes_non_free_network_services)
"Tracking" -> context.getString(R.string.anti_feature_tracking) "Tracking" -> context.getString(R.string.tracks_or_reports_your_activity)
"UpstreamNonFree" -> context.getString(R.string.anti_feature_upstream_non_free) "UpstreamNonFree" -> context.getString(R.string.upstream_source_code_is_not_free)
else -> context.getString(R.string.unknown_format, it) else -> context.getString(R.string.unknown_FORMAT, it)
} }
}.joinToString(separator = "\n") { "\u2022 $it" } }.joinToString(separator = "\n") { "\u2022 $it" }
if (antiFeatures.isNotEmpty()) { if (antiFeatures.isNotEmpty()) {
@@ -655,14 +655,14 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
items += Item.TextItem(TextType.ANTI_FEATURES, antiFeatures) items += Item.TextItem(TextType.ANTI_FEATURES, antiFeatures)
} }
val whatsNew = formatHtml(productRepository.first.whatsNew) val changes = formatHtml(productRepository.first.whatsNew)
if (whatsNew.isNotEmpty()) { if (changes.isNotEmpty()) {
items += Item.SectionItem(SectionType.WHATS_NEW) items += Item.SectionItem(SectionType.CHANGES)
val cropped = if (ExpandType.WHATS_NEW !in expanded) whatsNew.lineCropped(12, 10) else null val cropped = if (ExpandType.CHANGES !in expanded) changes.lineCropped(12, 10) else null
val item = Item.TextItem(TextType.WHATS_NEW, whatsNew) val item = Item.TextItem(TextType.CHANGES, changes)
if (cropped != null) { if (cropped != null) {
items += listOf(Item.TextItem(TextType.WHATS_NEW, cropped), items += listOf(Item.TextItem(TextType.CHANGES, cropped),
Item.ExpandItem(ExpandType.WHATS_NEW, true, listOf(item))) Item.ExpandItem(ExpandType.CHANGES, true, listOf(item)))
} else { } else {
items += item items += item
} }
@@ -758,11 +758,11 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
.sortedByDescending { it.release.versionCode } .sortedByDescending { it.release.versionCode }
.toList() .toList()
if (releaseItems.isNotEmpty()) { if (releaseItems.isNotEmpty()) {
items += Item.SectionItem(SectionType.RELEASES) items += Item.SectionItem(SectionType.VERSIONS)
val maxReleases = 5 val maxReleases = 5
if (releaseItems.size > maxReleases && ExpandType.RELEASES !in expanded) { if (releaseItems.size > maxReleases && ExpandType.VERSIONS !in expanded) {
items += releaseItems.take(maxReleases) items += releaseItems.take(maxReleases)
items += Item.ExpandItem(ExpandType.RELEASES, false, releaseItems.takeLast(releaseItems.size - maxReleases)) items += Item.ExpandItem(ExpandType.VERSIONS, false, releaseItems.takeLast(releaseItems.size - maxReleases))
} else { } else {
items += releaseItems items += releaseItems
} }
@@ -975,7 +975,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
!ProductPreferences[item.product.packageName].shouldIgnoreUpdate(item.product.versionCode) !ProductPreferences[item.product.packageName].shouldIgnoreUpdate(item.product.versionCode)
val version = (if (canUpdate) item.product.version else installedItem?.version)?.nullIfEmpty() val version = (if (canUpdate) item.product.version else installedItem?.version)?.nullIfEmpty()
?: item.product.version.nullIfEmpty() ?: item.product.version.nullIfEmpty()
holder.version.text = version?.let { context.getString(R.string.version_format, it) } holder.version.text = version?.let { context.getString(R.string.version_FORMAT, it) }
?: context.getString(R.string.unknown) ?: context.getString(R.string.unknown)
holder.packageName.text = item.product.packageName holder.packageName.text = item.product.packageName
val action = action val action = action
@@ -1002,7 +1002,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
holder.progress.isIndeterminate = true holder.progress.isIndeterminate = true
} }
is Status.Downloading -> { is Status.Downloading -> {
holder.status.text = context.getString(R.string.downloading_format, if (status.total == null) holder.status.text = context.getString(R.string.downloading_FORMAT, if (status.total == null)
status.read.formatSize() else "${status.read.formatSize()} / ${status.total.formatSize()}") status.read.formatSize() else "${status.read.formatSize()} / ${status.total.formatSize()}")
holder.progress.isIndeterminate = status.total == null holder.progress.isIndeterminate = status.total == null
if (status.total != null) { if (status.total != null) {
@@ -1051,7 +1051,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
holder as ExpandViewHolder holder as ExpandViewHolder
item as Item.ExpandItem item as Item.ExpandItem
holder.text.setText(when (item.expandType) { holder.text.setText(when (item.expandType) {
ExpandType.RELEASES -> R.string.show_older_releases ExpandType.VERSIONS -> R.string.show_older_versions
else -> R.string.show_more else -> R.string.show_more
}) })
} }
@@ -1154,7 +1154,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
val grayOut = incompatibility != null || olderOrSignature val grayOut = incompatibility != null || olderOrSignature
val primarySecondaryColor = context.getColorFromAttr(if (grayOut) val primarySecondaryColor = context.getColorFromAttr(if (grayOut)
android.R.attr.textColorSecondary else android.R.attr.textColorPrimary) android.R.attr.textColorSecondary else android.R.attr.textColorPrimary)
holder.version.text = context.getString(R.string.version_format, item.release.version) holder.version.text = context.getString(R.string.version_FORMAT, item.release.version)
holder.version.setTextColor(primarySecondaryColor) holder.version.setTextColor(primarySecondaryColor)
holder.setStatusActive(!grayOut) holder.setStatusActive(!grayOut)
holder.status.visibility = if (installed || suggested) View.VISIBLE else View.GONE holder.status.visibility = if (installed || suggested) View.VISIBLE else View.GONE
@@ -1163,7 +1163,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
suggested -> R.string.suggested suggested -> R.string.suggested
else -> R.string.unknown else -> R.string.unknown
}) })
holder.source.text = context.getString(R.string.provided_by_format, item.repository.name) holder.source.text = context.getString(R.string.provided_by_FORMAT, item.repository.name)
holder.added.text = holder.dateFormat.format(item.release.added) holder.added.text = holder.dateFormat.format(item.release.added)
holder.added.setTextColor(primarySecondaryColor) holder.added.setTextColor(primarySecondaryColor)
holder.size.text = item.release.size.formatSize() holder.size.text = item.release.size.formatSize()
@@ -1173,14 +1173,14 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
holder.compatibility.setTextColor(context.getColorFromAttr(R.attr.colorError)) holder.compatibility.setTextColor(context.getColorFromAttr(R.attr.colorError))
holder.compatibility.text = when (incompatibility) { holder.compatibility.text = when (incompatibility) {
is Release.Incompatibility.MinSdk, is Release.Incompatibility.MinSdk,
is Release.Incompatibility.MaxSdk -> context.getString(R.string.incompatible_with_format, Android.name) is Release.Incompatibility.MaxSdk -> context.getString(R.string.incompatible_with_FORMAT, Android.name)
is Release.Incompatibility.Platform -> context.getString(R.string.incompatible_with_format, is Release.Incompatibility.Platform -> context.getString(R.string.incompatible_with_FORMAT,
Android.primaryPlatform ?: context.getString(R.string.unknown)) Android.primaryPlatform ?: context.getString(R.string.unknown))
is Release.Incompatibility.Feature -> context.getString(R.string.requires_format, incompatibility.feature) is Release.Incompatibility.Feature -> context.getString(R.string.requires_FORMAT, incompatibility.feature)
} }
} else if (singlePlatform != null) { } else if (singlePlatform != null) {
holder.compatibility.setTextColor(context.getColorFromAttr(android.R.attr.textColorSecondary)) holder.compatibility.setTextColor(context.getColorFromAttr(android.R.attr.textColorSecondary))
holder.compatibility.text = context.getString(R.string.compatible_with_only_format, singlePlatform) holder.compatibility.text = context.getString(R.string.only_compatible_with_FORMAT, singlePlatform)
} }
val enabled = status == null val enabled = status == null
holder.statefulViews.forEach { it.isEnabled = enabled } holder.statefulViews.forEach { it.isEnabled = enabled }
@@ -131,11 +131,11 @@ class ProductsFragment(): ScreenFragment(), CursorOwner.Callback {
this.cursor = cursor this.cursor = cursor
emptyText = when { emptyText = when {
cursor == null -> "" cursor == null -> ""
searchQuery.isNotEmpty() -> getString(R.string.empty_search_summary) searchQuery.isNotEmpty() -> getString(R.string.no_matching_applications_found)
else -> when (source) { else -> when (source) {
Source.AVAILABLE -> getString(R.string.available_empty_summary) Source.AVAILABLE -> getString(R.string.no_applications_available)
Source.INSTALLED -> getString(R.string.installed_empty_summary) Source.INSTALLED -> getString(R.string.no_applications_installed)
Source.UPDATES -> getString(R.string.updates_empty_summary) Source.UPDATES -> getString(R.string.all_applications_up_to_date)
} }
} }
} }
@@ -128,11 +128,11 @@ class RepositoryFragment(): ScreenFragment() {
Database.ProductAdapter.getCount(repository.id).toString()) Database.ProductAdapter.getCount(repository.id).toString())
} }
} else { } else {
layout.addTitleText(R.string.description, getString(R.string.not_updated_description)) layout.addTitleText(R.string.description, getString(R.string.repository_not_used_DESC))
} }
if (repository.fingerprint.isEmpty()) { if (repository.fingerprint.isEmpty()) {
if (repository.updated > 0L) { if (repository.updated > 0L) {
val builder = SpannableStringBuilder(getString(R.string.unsigned_description)) val builder = SpannableStringBuilder(getString(R.string.repository_unsigned_DESC))
builder.setSpan(ForegroundColorSpan(layout.context.getColorFromAttr(R.attr.colorError).defaultColor), builder.setSpan(ForegroundColorSpan(layout.context.getColorFromAttr(R.attr.colorError).defaultColor),
0, builder.length, SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE) 0, builder.length, SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE)
layout.addTitleText(R.string.fingerprint, builder) layout.addTitleText(R.string.fingerprint, builder)
@@ -115,7 +115,7 @@ class TabsFragment: ScreenFragment() {
val toolbar = view.findViewById<Toolbar>(R.id.toolbar)!! val toolbar = view.findViewById<Toolbar>(R.id.toolbar)!!
screenActivity.onToolbarCreated(toolbar) screenActivity.onToolbarCreated(toolbar)
toolbar.setTitle(R.string.app_name) toolbar.setTitle(R.string.application_name)
// Move focus from SearchView to Toolbar // Move focus from SearchView to Toolbar
toolbar.isFocusableInTouchMode = true toolbar.isFocusableInTouchMode = true
@@ -147,7 +147,7 @@ class TabsFragment: ScreenFragment() {
.setActionView(searchView) .setActionView(searchView)
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS or MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS or MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW)
sortOrderMenu = addSubMenu(0, 0, 0, R.string.sort_order) sortOrderMenu = addSubMenu(0, 0, 0, R.string.sorting_order)
.setIcon(Utils.getToolbarIcon(toolbar.context, R.drawable.ic_sort)) .setIcon(Utils.getToolbarIcon(toolbar.context, R.drawable.ic_sort))
.let { menu -> .let { menu ->
menu.item.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS) menu.item.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS)
@@ -174,7 +174,7 @@ class DownloadService: ConnectionService<DownloadService.Binder>() {
} }
} }
private enum class ValidationError { HASH, STRUCTURE, INTEGRITY, SIGNATURE, PERMISSIONS } private enum class ValidationError { INTEGRITY, FORMAT, METADATA, SIGNATURE, PERMISSIONS }
private sealed class ErrorType { private sealed class ErrorType {
object Network: ErrorType() object Network: ErrorType()
@@ -194,23 +194,22 @@ class DownloadService: ConnectionService<DownloadService.Binder>() {
.apply { .apply {
when (errorType) { when (errorType) {
is ErrorType.Network -> { is ErrorType.Network -> {
setContentTitle(getString(R.string.error_downloading_format, task.name)) setContentTitle(getString(R.string.could_not_download_FORMAT, task.name))
setContentText(getString(R.string.network_error_description)) setContentText(getString(R.string.network_error_DESC))
} }
is ErrorType.Http -> { is ErrorType.Http -> {
setContentTitle(getString(R.string.error_downloading_format, task.name)) setContentTitle(getString(R.string.could_not_download_FORMAT, task.name))
setContentText(getString(R.string.http_error_description)) setContentText(getString(R.string.http_error_DESC))
} }
is ErrorType.Validation -> { is ErrorType.Validation -> {
setContentTitle(getString(R.string.error_validating_format, task.name)) setContentTitle(getString(R.string.could_not_validate_FORMAT, task.name))
val resId = R.string.validation_package_error_description_format setContentText(getString(when (errorType.validateError) {
setContentText(getString(resId, getString(when (errorType.validateError) { ValidationError.INTEGRITY -> R.string.integrity_check_error_DESC
ValidationError.HASH -> R.string.validation_error_hash_lower ValidationError.FORMAT -> R.string.file_format_error_DESC
ValidationError.STRUCTURE -> R.string.validation_error_structure_lower ValidationError.METADATA -> R.string.invalid_metadata_error_DESC
ValidationError.INTEGRITY -> R.string.validation_error_integrity_lower ValidationError.SIGNATURE -> R.string.invalid_signature_error_DESC
ValidationError.SIGNATURE -> R.string.validation_error_signature_lower ValidationError.PERMISSIONS -> R.string.invalid_permissions_error_DESC
ValidationError.PERMISSIONS -> R.string.validation_error_permissions_lower }))
})))
} }
}::class }::class
} }
@@ -227,8 +226,8 @@ class DownloadService: ConnectionService<DownloadService.Binder>() {
.setContentIntent(PendingIntent.getBroadcast(this, 0, Intent(this, Receiver::class.java) .setContentIntent(PendingIntent.getBroadcast(this, 0, Intent(this, Receiver::class.java)
.setAction("$ACTION_INSTALL.${task.packageName}") .setAction("$ACTION_INSTALL.${task.packageName}")
.putExtra(EXTRA_CACHE_FILE_NAME, task.release.cacheFileName), PendingIntent.FLAG_UPDATE_CURRENT)) .putExtra(EXTRA_CACHE_FILE_NAME, task.release.cacheFileName), PendingIntent.FLAG_UPDATE_CURRENT))
.setContentTitle(getString(R.string.finished_downloading_format, task.name)) .setContentTitle(getString(R.string.downloaded_FORMAT, task.name))
.setContentText(getString(R.string.tap_to_install_description)) .setContentText(getString(R.string.tap_to_install_DESC))
.build()) .build())
} }
@@ -253,7 +252,7 @@ class DownloadService: ConnectionService<DownloadService.Binder>() {
"" ""
} }
return if (hash.isEmpty() || hash != task.release.hash) { return if (hash.isEmpty() || hash != task.release.hash) {
ValidationError.HASH ValidationError.INTEGRITY
} else { } else {
val packageInfo = try { val packageInfo = try {
packageManager.getPackageArchiveInfo(file.path, Android.PackageManager.signaturesFlag) packageManager.getPackageArchiveInfo(file.path, Android.PackageManager.signaturesFlag)
@@ -262,10 +261,10 @@ class DownloadService: ConnectionService<DownloadService.Binder>() {
null null
} }
if (packageInfo == null) { if (packageInfo == null) {
ValidationError.STRUCTURE ValidationError.FORMAT
} else if (packageInfo.packageName != task.packageName || } else if (packageInfo.packageName != task.packageName ||
packageInfo.versionCodeCompat != task.release.versionCode) { packageInfo.versionCodeCompat != task.release.versionCode) {
ValidationError.INTEGRITY ValidationError.METADATA
} else { } else {
val signature = packageInfo.singleSignature?.let(Utils::calculateHash).orEmpty() val signature = packageInfo.singleSignature?.let(Utils::calculateHash).orEmpty()
if (signature.isEmpty() || signature != task.release.signature) { if (signature.isEmpty() || signature != task.release.signature) {
@@ -296,12 +295,12 @@ class DownloadService: ConnectionService<DownloadService.Binder>() {
startForeground(Common.NOTIFICATION_ID_SYNCING, stateNotificationBuilder.apply { startForeground(Common.NOTIFICATION_ID_SYNCING, stateNotificationBuilder.apply {
when (state) { when (state) {
is State.Connecting -> { is State.Connecting -> {
setContentTitle(getString(R.string.downloading_format, state.name)) setContentTitle(getString(R.string.downloading_FORMAT, state.name))
setContentText(getString(R.string.connecting)) setContentText(getString(R.string.connecting))
setProgress(1, 0, true) setProgress(1, 0, true)
} }
is State.Downloading -> { is State.Downloading -> {
setContentTitle(getString(R.string.downloading_format, state.name)) setContentTitle(getString(R.string.downloading_FORMAT, state.name))
if (state.total != null) { if (state.total != null) {
setContentText("${state.read.formatSize()} / ${state.total.formatSize()}") setContentText("${state.read.formatSize()} / ${state.total.formatSize()}")
setProgress(100, (100f * state.read / state.total).roundToInt(), false) setProgress(100, (100f * state.read / state.total).roundToInt(), false)
@@ -201,15 +201,15 @@ class SyncService: ConnectionService<SyncService.Binder>() {
.setSmallIcon(android.R.drawable.stat_sys_warning) .setSmallIcon(android.R.drawable.stat_sys_warning)
.setColor(ContextThemeWrapper(this, R.style.Theme_Main_Light) .setColor(ContextThemeWrapper(this, R.style.Theme_Main_Light)
.getColorFromAttr(android.R.attr.colorAccent).defaultColor) .getColorFromAttr(android.R.attr.colorAccent).defaultColor)
.setContentTitle(getString(R.string.error_syncing_format, repository.name)) .setContentTitle(getString(R.string.could_not_sync_FORMAT, repository.name))
.setContentText(getString(when (exception) { .setContentText(getString(when (exception) {
is RepositoryUpdater.UpdateException -> when (exception.errorType) { is RepositoryUpdater.UpdateException -> when (exception.errorType) {
RepositoryUpdater.ErrorType.NETWORK -> R.string.network_error_description RepositoryUpdater.ErrorType.NETWORK -> R.string.network_error_DESC
RepositoryUpdater.ErrorType.HTTP -> R.string.http_error_description RepositoryUpdater.ErrorType.HTTP -> R.string.http_error_DESC
RepositoryUpdater.ErrorType.VALIDATION -> R.string.validation_index_error_description RepositoryUpdater.ErrorType.VALIDATION -> R.string.validation_index_error_DESC
RepositoryUpdater.ErrorType.PARSING -> R.string.parsing_index_error_description RepositoryUpdater.ErrorType.PARSING -> R.string.parsing_index_error_DESC
} }
else -> R.string.unknown_error_description else -> R.string.unknown_error_DESC
})) }))
.build()) .build())
} }
@@ -229,12 +229,12 @@ class SyncService: ConnectionService<SyncService.Binder>() {
startForeground(Common.NOTIFICATION_ID_SYNCING, stateNotificationBuilder.apply { startForeground(Common.NOTIFICATION_ID_SYNCING, stateNotificationBuilder.apply {
when (state) { when (state) {
is State.Connecting -> { is State.Connecting -> {
setContentTitle(getString(R.string.syncing_format, state.name)) setContentTitle(getString(R.string.syncing_FORMAT, state.name))
setContentText(getString(R.string.connecting)) setContentText(getString(R.string.connecting))
setProgress(0, 0, true) setProgress(0, 0, true)
} }
is State.Syncing -> { is State.Syncing -> {
setContentTitle(getString(R.string.syncing_format, state.name)) setContentTitle(getString(R.string.syncing_FORMAT, state.name))
when (state.stage) { when (state.stage) {
RepositoryUpdater.Stage.DOWNLOAD -> { RepositoryUpdater.Stage.DOWNLOAD -> {
if (state.total != null) { if (state.total != null) {
@@ -247,12 +247,12 @@ class SyncService: ConnectionService<SyncService.Binder>() {
} }
RepositoryUpdater.Stage.PROCESS -> { RepositoryUpdater.Stage.PROCESS -> {
val progress = state.total?.let { 100f * state.read / it }?.roundToInt() val progress = state.total?.let { 100f * state.read / it }?.roundToInt()
setContentText(getString(R.string.processing_format, "${progress ?: 0}%")) setContentText(getString(R.string.processing_FORMAT, "${progress ?: 0}%"))
setProgress(100, progress ?: 0, progress == null) setProgress(100, progress ?: 0, progress == null)
} }
RepositoryUpdater.Stage.MERGE -> { RepositoryUpdater.Stage.MERGE -> {
val progress = (100f * state.read / (state.total ?: state.read)).roundToInt() val progress = (100f * state.read / (state.total ?: state.read)).roundToInt()
setContentText(getString(R.string.merging_format, "${state.read} / ${state.total ?: state.read}")) setContentText(getString(R.string.merging_FORMAT, "${state.read} / ${state.total ?: state.read}"))
setProgress(100, progress, false) setProgress(100, progress, false)
} }
RepositoryUpdater.Stage.COMMIT -> { RepositoryUpdater.Stage.COMMIT -> {
@@ -350,7 +350,7 @@ class SyncService: ConnectionService<SyncService.Binder>() {
.Builder(this, Common.NOTIFICATION_CHANNEL_UPDATES) .Builder(this, Common.NOTIFICATION_CHANNEL_UPDATES)
.setSmallIcon(R.drawable.ic_new_releases) .setSmallIcon(R.drawable.ic_new_releases)
.setContentTitle(getString(R.string.new_updates_available)) .setContentTitle(getString(R.string.new_updates_available))
.setContentText(resources.getQuantityString(R.plurals.new_updates_description_format, .setContentText(resources.getQuantityString(R.plurals.new_updates_DESC_FORMAT,
productItems.size, productItems.size)) productItems.size, productItems.size))
.setColor(ContextThemeWrapper(this, R.style.Theme_Main_Light) .setColor(ContextThemeWrapper(this, R.style.Theme_Main_Light)
.getColorFromAttr(android.R.attr.colorAccent).defaultColor) .getColorFromAttr(android.R.attr.colorAccent).defaultColor)
@@ -365,7 +365,7 @@ class SyncService: ConnectionService<SyncService.Binder>() {
addLine(builder) addLine(builder)
} }
if (productItems.size > maxUpdates) { if (productItems.size > maxUpdates) {
val summary = getString(R.string.plus_more_format, productItems.size - maxUpdates) val summary = getString(R.string.plus_more_FORMAT, productItems.size - maxUpdates)
if (Android.sdk(24)) { if (Android.sdk(24)) {
addLine(summary) addLine(summary)
} else { } else {
+72 -73
View File
@@ -5,162 +5,161 @@
<string name="add_repository">Add repository</string> <string name="add_repository">Add repository</string>
<string name="address">Address</string> <string name="address">Address</string>
<string name="all_applications">All applications</string> <string name="all_applications">All applications</string>
<string name="all_applications_up_to_date">All applications up to date</string>
<string name="already_exists">Already exists</string> <string name="already_exists">Already exists</string>
<string name="always">Always</string> <string name="always">Always</string>
<string name="anti_feature_advertising">Has advertising</string>
<string name="anti_feature_application_debuggable">Compiled for debugging</string>
<string name="anti_feature_disabled_algorithm">Signed using an unsafe algorithm</string>
<string name="anti_feature_known_vulnerabilities">Has security vulnerabilities</string>
<string name="anti_feature_no_source_since">Source code no longer available</string>
<string name="anti_feature_non_free_addons">Promotes non-free applications or plugins</string>
<string name="anti_feature_non_free_assets">Contains non-free assets</string>
<string name="anti_feature_non_free_dependencies">Depends on non-free applications</string>
<string name="anti_feature_non_free_network">Promotes non-free network services</string>
<string name="anti_feature_tracking">Tracks or reports your activity</string>
<string name="anti_feature_upstream_non_free">Upstream source code is not free</string>
<string name="anti_features">Anti-features</string> <string name="anti_features">Anti-features</string>
<string name="app_name">Foxy Droid</string>
<string name="application">Application</string> <string name="application">Application</string>
<string name="application_name" translatable="false">Foxy Droid</string>
<string name="application_not_found">Application not found</string> <string name="application_not_found">Application not found</string>
<string name="authors_website">Author website</string> <string name="author_email">Author email</string>
<string name="authors_email">Author email</string> <string name="author_website">Author website</string>
<string name="available">Available</string> <string name="available">Available</string>
<string name="available_empty_summary">No applications available</string>
<string name="bug_tracker">Bug tracker</string> <string name="bug_tracker">Bug tracker</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="cant_edit_sync_description">Cannot edit repository since it\'s syncing right now.</string> <string name="cant_edit_sync_DESC">Cannot edit repository since it is syncing right now.</string>
<string name="changelog">Changelog</string> <string name="changelog">Changelog</string>
<string name="changes">Changes</string>
<string name="checking_repository">Checking repository</string> <string name="checking_repository">Checking repository</string>
<string name="compatible_with_only_format">Only compatible with %s</string> <string name="compiled_for_debugging">Compiled for debugging</string>
<string name="confirm_action">Confirmation</string> <string name="confirmation">Confirmation</string>
<string name="connecting">Connecting</string> <string name="connecting">Connecting</string>
<string name="contains_non_free_media">Contains non-free media</string>
<string name="could_not_download_FORMAT">Could not download %s</string>
<string name="could_not_sync_FORMAT">Could not sync %s</string>
<string name="could_not_validate_FORMAT">Could not validate %s</string>
<string name="dark">Dark</string> <string name="dark">Dark</string>
<string name="date_added">Date added</string> <string name="date_added">Date added</string>
<string name="delete">Delete</string> <string name="delete">Delete</string>
<string name="delete_repository_confirm">Are you sure you want to delete the repository?</string> <string name="delete_repository_DESC">Are you sure you want to delete the repository?</string>
<string name="description">Description</string> <string name="description">Description</string>
<string name="details">Details</string> <string name="details">Details</string>
<string name="donate">Donate</string> <string name="donate">Donate</string>
<string name="downloaded_FORMAT">Downloaded %s</string>
<string name="downloading">Downloading</string> <string name="downloading">Downloading</string>
<string name="downloading_format">Downloading %s</string> <string name="downloading_FORMAT">Downloading %s</string>
<string name="edit_repository">Edit repository</string> <string name="edit_repository">Edit repository</string>
<string name="empty_search_summary">No matching applications found</string> <string name="file_format_error_DESC">Invalid file format.</string>
<string name="error_downloading_format">Could not download %s</string>
<string name="error_syncing_format">Could not sync %s</string>
<string name="error_validating_format">Could not validate %s</string>
<string name="fingerprint">Fingerprint</string> <string name="fingerprint">Fingerprint</string>
<string name="finished_downloading_format">Downloaded %s</string> <string name="has_advertising">Has advertising</string>
<string name="http_error_description">Invalid server response.</string> <string name="has_non_free_dependencies">Has non-free dependencies</string>
<string name="has_security_vulnerabilities">Has security vulnerabilities</string>
<string name="http_error_DESC">Invalid server response.</string>
<string name="http_proxy">HTTP proxy</string> <string name="http_proxy">HTTP proxy</string>
<string name="ignore_all_updates">Ignore all updates</string> <string name="ignore_all_updates">Ignore all updates</string>
<string name="ignore_this_update">Ignore this update</string> <string name="ignore_this_update">Ignore this update</string>
<string name="incompatible_features_description">Missing features:</string> <string name="incompatible_api_DESC_FORMAT">Your %1$s (API version %2$d) is not supported. %3$s</string>
<string name="incompatible_older_description">This version is older than the one installed on your device. <string name="incompatible_api_max_DESC_FORMAT">Maximum API version is %d.</string>
<string name="incompatible_api_min_DESC_FORMAT">Minimum API version is %d.</string>
<string name="incompatible_features_DESC">Missing features.</string>
<string name="incompatible_older_DESC">This version is older than the one installed on your device.
Uninstall that first.</string> Uninstall that first.</string>
<string name="incompatible_platforms_description_format">Your %1$s platform is not supported. <string name="incompatible_platforms_DESC_FORMAT">Your %1$s platform is not supported.
Supported platforms: %2$s.</string> Supported platforms: %2$s.</string>
<string name="incompatible_sdk_description_format">Your %1$s (SDK version %2$d) is not supported. %3$s</string> <string name="incompatible_signature_DESC">This version is signed with a different certificate than the one
<string name="incompatible_sdk_max_description_format">Maximum SDK version is %d.</string>
<string name="incompatible_sdk_min_description_format">Minimum SDK version is %d.</string>
<string name="incompatible_signature_description">This version is signed with a different certificate than the one
installed on your device. Uninstall that first.</string> installed on your device. Uninstall that first.</string>
<string name="incompatible_version">Incompatible version</string> <string name="incompatible_version">Incompatible version</string>
<string name="incompatible_versions">Incompatible versions</string> <string name="incompatible_versions">Incompatible versions</string>
<string name="incompatible_versions_summary">Show application versions incompatible with the device</string> <string name="incompatible_versions_summary">Show application versions incompatible with the device</string>
<string name="incompatible_with_format">Incompatible with %s</string> <string name="incompatible_with_FORMAT">Incompatible with %s</string>
<string name="install">Install</string> <string name="install">Install</string>
<string name="installed">Installed</string> <string name="installed">Installed</string>
<string name="installed_empty_summary">No applications installed</string> <string name="integrity_check_error_DESC">Could not check integrity.</string>
<string name="invalid_address">Invalid address</string> <string name="invalid_address">Invalid address</string>
<string name="invalid_fingerprint_format">Invalid fingerprint format</string> <string name="invalid_fingerprint_format">Invalid fingerprint format</string>
<string name="invalid_metadata_error_DESC">Invalid metadata.</string>
<string name="invalid_permissions_error_DESC">Invalid permissions.</string>
<string name="invalid_signature_error_DESC">Invalid signature.</string>
<string name="invalid_username_format">Invalid username format</string> <string name="invalid_username_format">Invalid username format</string>
<string name="last_update">Last update</string> <string name="last_update">Last update</string>
<string name="launch">Launch</string> <string name="launch">Launch</string>
<string name="license">License</string> <string name="license">License</string>
<string name="license_format">License %s</string> <string name="license_FORMAT">License %s</string>
<string name="light">Light</string> <string name="light">Light</string>
<string name="link_copied_to_clipboard">Link copied to clipboard</string> <string name="link_copied_to_clipboard">Link copied to clipboard</string>
<string name="links">Links</string> <string name="links">Links</string>
<string name="merging_format">Merging %s</string> <string name="merging_FORMAT">Merging %s</string>
<string name="name">Name</string> <string name="name">Name</string>
<string name="network_error_description">Network error.</string> <string name="network_error_DESC">Network error.</string>
<string name="never">Never</string> <string name="never">Never</string>
<string name="new_updates_available">New updates available</string> <string name="new_updates_available">New updates available</string>
<plurals name="new_updates_description_format"> <plurals name="new_updates_DESC_FORMAT">
<item quantity="one">%d new update.</item> <item quantity="one">%d new update.</item>
<item quantity="other">%d new updates.</item> <item quantity="other">%d new updates.</item>
</plurals> </plurals>
<string name="no_description_available_description">No description available.</string> <string name="no_applications_available">No applications available</string>
<string name="no_applications_installed">No applications installed</string>
<string name="no_description_available_DESC">No description available.</string>
<string name="no_matching_applications_found">No matching applications found</string>
<string name="no_proxy">No proxy</string> <string name="no_proxy">No proxy</string>
<string name="not_updated_description">This repository has not been used yet. You need to enable it to view <string name="notify_about_updates">Notify about updates</string>
the applications it provides.</string> <string name="notify_about_updates_summary">Show a notification when updates are available</string>
<string name="number_of_applications">Number of applications</string> <string name="number_of_applications">Number of applications</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="open_link_confirm_format">Open %s?</string> <string name="only_compatible_with_FORMAT">Only compatible with %s</string>
<string name="only_on_wifi">Only on Wi-Fi</string>
<string name="open_DESC_FORMAT">Open %s?</string>
<string name="other">Other</string> <string name="other">Other</string>
<string name="over_wifi">Only on Wi-Fi</string> <string name="parsing_index_error_DESC">Could not parse the index file.</string>
<string name="parsing_index_error_description">Could not parse the index file.</string>
<string name="password">Password</string> <string name="password">Password</string>
<string name="password_is_not_specified">Password missing</string> <string name="password_missing">Password missing</string>
<string name="permissions">Permissions</string> <string name="permissions">Permissions</string>
<string name="plus_more_format">+%d more</string> <string name="plus_more_FORMAT">+%d more</string>
<string name="preferences">Preferences</string> <string name="preferences">Preferences</string>
<string name="processing_format">Processing %1$s</string> <string name="processing_FORMAT">Processing %1$s</string>
<string name="project_website">Project website</string> <string name="project_website">Project website</string>
<string name="provided_by_format">Provided by %s</string> <string name="promotes_non_free_network_services">Promotes non-free network services</string>
<string name="promotes_non_free_software">Promotes non-free software</string>
<string name="provided_by_FORMAT">Provided by %s</string>
<string name="proxy">Proxy</string> <string name="proxy">Proxy</string>
<string name="proxy_host">Proxy host</string> <string name="proxy_host">Proxy host</string>
<string name="proxy_port">Proxy port</string> <string name="proxy_port">Proxy port</string>
<string name="proxy_type">Proxy type</string> <string name="proxy_type">Proxy type</string>
<string name="releases">Releases</string>
<string name="repositories">Repositories</string> <string name="repositories">Repositories</string>
<string name="repository">Repository</string> <string name="repository">Repository</string>
<string name="requires_format">Requires %s</string> <string name="repository_not_used_DESC">This repository has not been used yet. You need to enable it to view
the applications it provides.</string>
<string name="repository_unsigned_DESC">Unsigned. Could not verify the application list. Be careful downloading
applications from unsigned repositories.</string>
<string name="requires_FORMAT">Requires %s</string>
<string name="save">Save</string> <string name="save">Save</string>
<string name="saving_details">Saving details</string> <string name="saving_details">Saving details</string>
<string name="screenshots">Screenshots</string> <string name="screenshots">Screenshots</string>
<string name="search">Search</string> <string name="search">Search</string>
<string name="select_the_mirror">Select a mirror</string> <string name="select_mirror">Select a mirror</string>
<string name="show_more">Show more</string> <string name="show_more">Show more</string>
<string name="show_older_releases">Show older releases</string> <string name="show_older_versions">Show older versions</string>
<string name="signed_using_unsafe_algorithm">Signed using an unsafe algorithm</string>
<string name="skip">Skip</string> <string name="skip">Skip</string>
<string name="socks_proxy">SOCKS proxy</string> <string name="socks_proxy">SOCKS proxy</string>
<string name="sort_order">Sorting order</string> <string name="sorting_order">Sorting order</string>
<string name="source_code">Source code</string> <string name="source_code">Source code</string>
<string name="source_code_no_longer_available">Source code no longer available</string>
<string name="suggested">Suggested</string> <string name="suggested">Suggested</string>
<string name="sync_repositories">Sync repositories</string> <string name="sync_repositories">Sync repositories</string>
<string name="sync_repositories_automatically">Sync repositories automatically</string> <string name="sync_repositories_automatically">Sync repositories automatically</string>
<string name="syncing">Syncing</string> <string name="syncing">Syncing</string>
<string name="syncing_format">Syncing %s</string> <string name="syncing_FORMAT">Syncing %s</string>
<string name="tap_to_install_description">Tap to install.</string> <string name="tap_to_install_DESC">Tap to install.</string>
<string name="theme">Theme</string> <string name="theme">Theme</string>
<string name="tracks_or_reports_your_activity">Tracks or reports your activity</string>
<string name="uninstall">Uninstall</string> <string name="uninstall">Uninstall</string>
<string name="unknown">Unknown</string> <string name="unknown">Unknown</string>
<string name="unknown_error_description">Unknown error.</string> <string name="unknown_error_DESC">Unknown error.</string>
<string name="unknown_format">Unknown: %s</string> <string name="unknown_FORMAT">Unknown: %s</string>
<string name="unsigned">Unsigned</string> <string name="unsigned">Unsigned</string>
<string name="unsigned_description">Unsigned. Could not verify the application list. Be careful downloading
applications from unsigned repositories.</string>
<string name="unstable_updates">Unstable updates</string> <string name="unstable_updates">Unstable updates</string>
<string name="unstable_updates_summary">Suggest installing unstable versions</string> <string name="unstable_updates_summary">Suggest installing unstable versions</string>
<string name="unverified">Unverified</string> <string name="unverified">Unverified</string>
<string name="update">Update</string> <string name="update">Update</string>
<string name="update_notifications">Notify about updates</string>
<string name="update_notifications_summary">Show a notification when updates are available</string>
<string name="updates">Updates</string> <string name="updates">Updates</string>
<string name="updates_empty_summary">All applications up to date</string> <string name="upstream_source_code_is_not_free">Upstream source code is not free</string>
<string name="username">Username</string> <string name="username">Username</string>
<string name="username_is_not_specified">Username missing</string> <string name="username_missing">Username missing</string>
<string name="validation_index_error_description">Index could not be validated.</string> <string name="validation_index_error_DESC">Index could not be validated.</string>
<string name="validation_package_error_description_format">Package could not be validated: %s.</string> <string name="version_FORMAT">Version %s</string>
<string name="validation_error_hash_lower">invalid checksum</string> <string name="versions">Versions</string>
<string name="validation_error_integrity_lower">could not check integrity</string>
<string name="validation_error_permissions_lower">invalid permissions</string>
<string name="validation_error_signature_lower">invalid signature</string>
<string name="validation_error_structure_lower">invalid file format</string>
<string name="version_format">Version %s</string>
<string name="waiting_to_start_download">Waiting to start download</string> <string name="waiting_to_start_download">Waiting to start download</string>
<string name="website">Website</string> <string name="website">Website</string>
<string name="whats_new">What\'s new</string>
</resources> </resources>