feat(launcher): full launcher Apps page (#1686)

* feat(launcher): apps page with search, sort, filter, folders, widgets and page swipes

* feat(launcher): app shortcuts, pinned shortcuts, hidden apps and LauncherApps listing

* fix(ui): let page content scroll to the bottom edge

* feat(launcher): accept widgets other apps ask to pin

* i18n(launcher): translate apps page strings

* fix(launcher): keep disabled pins, serialize widget adds, add many plurals

* fix(launcher): drop in-flight pin refreshes after unpinning

* fix(launcher): skip unpin when pinned shortcuts can't be read

* fix(launcher): drop in-flight app lists when home role is lost

* fix(launcher): only migrate folder entries with one launcher activity
This commit is contained in:
Kurt Himebauch authored and GitHub committed 2026-10-27 14:50:11 -04:00
1 parent ef9866e423
commit e906d2f077
52 files changed
+3160 -56

No files matched your search

+13
View File
@@ -30,6 +30,7 @@
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.CAMERA" />
@@ -157,6 +158,18 @@
</activity>
<activity
android:name=".PinRequestActivity"
android:exported="true"
android:excludeFromRecents="true"
android:taskAffinity="${applicationId}.pin"
android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
<intent-filter>
<action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
<action android:name="android.content.pm.action.CONFIRM_PIN_APPWIDGET" />
</intent-filter>
</activity>
<activity
android:name=".input.ControllerEditorActivity"
android:exported="false"
@@ -495,6 +495,21 @@ class FrontEndActivity : ComponentActivity() {
private var homeScreenEnabled by mutableStateOf(false)
private var defaultHomeLabel by mutableStateOf<String?>(null)
private var androidApps by mutableStateOf<List<HomeApp.LaunchableApp>>(emptyList())
private var androidAppsListing = 0
private val packageCallback = object : android.content.pm.LauncherApps.Callback() {
private fun changed() {
refreshAndroidApps()
if (HomeWidgets.providers != null) HomeWidgets.refreshProviders(this@FrontEndActivity)
}
override fun onPackageRemoved(packageName: String, user: android.os.UserHandle) = changed()
override fun onPackageAdded(packageName: String, user: android.os.UserHandle) = changed()
override fun onPackageChanged(packageName: String, user: android.os.UserHandle) = changed()
override fun onPackagesAvailable(packageNames: Array<out String>, user: android.os.UserHandle, replacing: Boolean) = changed()
override fun onPackagesUnavailable(packageNames: Array<out String>, user: android.os.UserHandle, replacing: Boolean) = changed()
override fun onShortcutsChanged(packageName: String, shortcuts: MutableList<android.content.pm.ShortcutInfo>, user: android.os.UserHandle) {
if (homeAppSelected) HomePins.refresh(this@FrontEndActivity)
}
}
private var secondScreenDisplays by mutableStateOf<List<SecondScreenDisplay>>(emptyList())
private lateinit var displayManager: DisplayManager
private val secondScreenDisplayListener = object : DisplayManager.DisplayListener {
@@ -1568,6 +1583,15 @@ class FrontEndActivity : ComponentActivity() {
})
displayManager.registerDisplayListener(secondScreenDisplayListener, ui)
refreshSecondScreenDisplays()
getSystemService(android.content.pm.LauncherApps::class.java).registerCallback(packageCallback, ui)
HomeWidgets.startListening(this)
}
@Suppress("OVERRIDE_DEPRECATION")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (HomeWidgets.onActivityResult(this, requestCode, resultCode)) return
@Suppress("DEPRECATION")
super.onActivityResult(requestCode, resultCode, data)
}
override fun onSaveInstanceState(outState: Bundle) {
@@ -1593,6 +1617,8 @@ class FrontEndActivity : ComponentActivity() {
override fun onStop() {
unregisterReceiver(wifiLocationReceiver)
unregisterReceiver(mediaReceiver)
getSystemService(android.content.pm.LauncherApps::class.java).unregisterCallback(packageCallback)
HomeWidgets.stopListening(this)
ui.removeCallbacks(mediaRefresh)
com.droiddeck.launcher.stores.StoresState.libraryListener = null
com.droiddeck.launcher.stores.battlenet.BattleNetClient.opener = null
@@ -1608,10 +1634,24 @@ class FrontEndActivity : ComponentActivity() {
homeScreenEnabled = HomeApp.isHomeScreenEnabled(this)
homeAppSelected = homeScreenEnabled && HomeApp.isDefault(this)
defaultHomeLabel = HomeApp.defaultLabel(this)
androidApps = if (homeAppSelected) HomeApp.launchableApps(this) else emptyList()
refreshAndroidApps()
integrationState = readIntegration()
}
private fun refreshAndroidApps() {
if (!homeAppSelected) {
++androidAppsListing
androidApps = emptyList()
return
}
val seq = ++androidAppsListing
HomePins.refresh(this)
Thread({
val apps = runCatching { HomeApp.launchableApps(this) }.getOrDefault(emptyList())
ui.post { if (seq == androidAppsListing) androidApps = apps }
}, "home-apps").start()
}
private fun refreshSecondScreenDisplays() {
if (!::displayManager.isInitialized) return
// The launcher can itself be shown on a non-default display (DeX, an external monitor); that one is not a "second screen".
@@ -6,7 +6,15 @@ import android.graphics.Bitmap
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.LauncherApps
import android.content.pm.ShortcutInfo
import android.graphics.Rect
import android.os.Process
import android.os.UserHandle
import android.os.UserManager
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.provider.Settings
import androidx.core.graphics.drawable.toBitmap
@@ -22,7 +30,17 @@ object HomeApp {
val packageName: String,
val className: String,
val icon: Bitmap?,
)
val system: Boolean = false,
val game: Boolean = false,
val installedAt: Long = 0L,
val updatedAt: Long = 0L,
val user: UserHandle? = null,
val userSerial: Long = 0L,
) {
val component get() = ComponentName(packageName, className)
val key get() = "$packageName/$className" + if (userSerial != 0L) "#$userSerial" else ""
val work get() = userSerial != 0L
}
fun isHomeScreenEnabled(context: Context): Boolean =
context.packageManager.getComponentEnabledSetting(ComponentName(context, HomeActivity::class.java)) ==
@@ -72,34 +90,79 @@ object HomeApp {
fun settingsIntent(): Intent = Intent(Settings.ACTION_HOME_SETTINGS)
fun launchableApps(context: Context): List<LaunchableApp> {
val launcher = context.getSystemService(LauncherApps::class.java)
val users = context.getSystemService(UserManager::class.java)
val pm = context.packageManager
val launch = Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER)
val own = Process.myUserHandle()
val iconPx = (ICON_DP * context.resources.displayMetrics.density).roundToInt()
val apps = pm.queryIntentActivities(launch, 0)
.asSequence()
.mapNotNull { result ->
val activity = result.activityInfo ?: return@mapNotNull null
if (!activity.exported || activity.packageName == context.packageName) return@mapNotNull null
val label = result.loadLabel(pm)?.toString()?.trim().orEmpty().ifEmpty { activity.packageName }
val icon = runCatching { result.loadIcon(pm).toBitmap(iconPx, iconPx) }.getOrNull()
LaunchableApp(label, activity.packageName, activity.name, icon)
val apps = users.userProfiles.flatMap { user ->
val serial = if (user == own) 0L else users.getSerialNumberForUser(user)
runCatching { launcher.getActivityList(null, user) }.getOrDefault(emptyList()).mapNotNull { info ->
val component = info.componentName
if (component.packageName == context.packageName && user == own) return@mapNotNull null
val label = info.label?.toString()?.trim().orEmpty().ifEmpty { component.packageName }
val icon = runCatching { info.getBadgedIcon(0).toBitmap(iconPx, iconPx) }.getOrNull()
val app = info.applicationInfo
val updated = if (user == own) runCatching { pm.getPackageInfo(component.packageName, 0).lastUpdateTime }.getOrNull() else null
@Suppress("DEPRECATION")
val game = app.category == ApplicationInfo.CATEGORY_GAME || app.flags and ApplicationInfo.FLAG_IS_GAME != 0
LaunchableApp(
label, component.packageName, component.className, icon,
system = app.flags and (ApplicationInfo.FLAG_SYSTEM or ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0,
game = game,
installedAt = info.firstInstallTime,
updatedAt = updated ?: info.firstInstallTime,
user = user,
userSerial = serial,
)
}
.distinctBy { it.packageName }
.toList()
return apps.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.label })
}
return apps.distinctBy { it.key }.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.label })
}
fun launch(context: Context, app: LaunchableApp, displayId: Int? = null) {
val intent = Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setComponent(ComponentName(app.packageName, app.className))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
if (displayId == null) {
context.startActivity(intent)
return
}
val options = ActivityOptions.makeBasic().setLaunchDisplayId(displayId)
context.startActivity(intent, options.toBundle())
fun launch(context: Context, app: LaunchableApp, displayId: Int? = null, bounds: Rect? = null) {
val options = displayId?.let { ActivityOptions.makeBasic().setLaunchDisplayId(it).toBundle() }
val launcher = context.getSystemService(LauncherApps::class.java)
launcher.startMainActivity(app.component, app.user ?: Process.myUserHandle(), bounds, options)
}
fun canUseShortcuts(context: Context): Boolean =
runCatching { context.getSystemService(LauncherApps::class.java).hasShortcutHostPermission() }.getOrDefault(false)
fun shortcuts(context: Context, app: LaunchableApp): List<ShortcutInfo> {
if (!canUseShortcuts(context)) return emptyList()
val query = LauncherApps.ShortcutQuery()
.setPackage(app.packageName)
.setActivity(app.component)
.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC or LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST)
return runCatching { context.getSystemService(LauncherApps::class.java).getShortcuts(query, app.user ?: Process.myUserHandle()) }
.getOrNull().orEmpty()
.filter { it.isEnabled }
.sortedWith(compareBy<ShortcutInfo> { if (it.isDeclaredInManifest) 0 else 1 }.thenBy { it.rank })
.take(5)
}
fun shortcutIcon(context: Context, info: ShortcutInfo, px: Int = (ICON_DP * context.resources.displayMetrics.density).roundToInt()): Bitmap? = runCatching {
context.getSystemService(LauncherApps::class.java)
.getShortcutIconDrawable(info, context.resources.displayMetrics.densityDpi)?.toBitmap(px, px)
}.getOrNull()
fun startShortcut(context: Context, info: ShortcutInfo) {
context.getSystemService(LauncherApps::class.java).startShortcut(info, null, null)
}
fun openAppInfo(context: Context, app: LaunchableApp) {
context.getSystemService(LauncherApps::class.java)
.startAppDetailsActivity(app.component, app.user ?: Process.myUserHandle(), null, null)
}
fun canUninstall(app: LaunchableApp): Boolean = !app.system && !app.work
fun uninstall(context: Context, app: LaunchableApp) {
context.startActivity(
Intent(Intent.ACTION_DELETE, Uri.fromParts("package", app.packageName, null))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
)
}
fun isActiveSteamSession(): Boolean =
@@ -0,0 +1,65 @@
package com.droiddeck.launcher
import android.content.Context
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import org.json.JSONArray
import org.json.JSONObject
import java.util.UUID
object HomeFolders {
data class Folder(val id: String, val name: String, val apps: List<String>)
private const val PREFS = "home_launcher"
private const val KEY = "folders"
var folders by mutableStateOf<List<Folder>>(emptyList())
private set
private var loaded = false
fun load(context: Context) {
if (loaded) return
loaded = true
val raw = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).getString(KEY, null) ?: return
folders = runCatching {
val array = JSONArray(raw)
(0 until array.length()).map { i ->
val o = array.getJSONObject(i)
val apps = o.getJSONArray("apps")
Folder(o.getString("id"), o.getString("name"), (0 until apps.length()).map { apps.getString(it) })
}
}.getOrDefault(emptyList())
}
fun create(context: Context, name: String, appKey: String): Folder {
val folder = Folder(UUID.randomUUID().toString(), name, listOf(appKey))
update(context, without(appKey) + folder)
return folder
}
fun add(context: Context, folderId: String, appKey: String) =
update(context, without(appKey).map { if (it.id == folderId) it.copy(apps = it.apps + appKey) else it })
fun remove(context: Context, appKey: String) = update(context, without(appKey))
fun rename(context: Context, folderId: String, name: String) =
update(context, folders.map { if (it.id == folderId) it.copy(name = name) else it })
fun delete(context: Context, folderId: String) = update(context, folders.filterNot { it.id == folderId })
fun migrateMembers(context: Context, keyFor: (String) -> String?) {
if (folders.none { f -> f.apps.any { '/' !in it } }) return
update(context, folders.map { f -> f.copy(apps = f.apps.map { if ('/' in it) it else keyFor(it) ?: it }.distinct()) })
}
private fun without(appKey: String): List<Folder> =
folders.map { it.copy(apps = it.apps - appKey) }.filter { it.apps.isNotEmpty() }
private fun update(context: Context, next: List<Folder>) {
folders = next
val array = JSONArray()
for (f in next) array.put(JSONObject().put("id", f.id).put("name", f.name).put("apps", JSONArray(f.apps)))
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit().putString(KEY, array.toString()).apply()
}
}
@@ -0,0 +1,26 @@
package com.droiddeck.launcher
import android.content.Context
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
object HomeHidden {
private const val PREFS = "home_launcher"
private const val KEY = "hidden"
var keys by mutableStateOf<Set<String>>(emptySet())
private set
private var loaded = false
fun load(context: Context) {
if (loaded) return
loaded = true
keys = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).getStringSet(KEY, emptySet()).orEmpty().toSet()
}
fun set(context: Context, key: String, hidden: Boolean) {
keys = if (hidden) keys + key else keys - key
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit().putStringSet(KEY, keys).apply()
}
}
@@ -0,0 +1,75 @@
package com.droiddeck.launcher
import android.content.Context
import android.content.pm.LauncherApps
import android.content.pm.ShortcutInfo
import android.graphics.Bitmap
import android.os.Handler
import android.os.Looper
import android.os.Process
import android.os.UserManager
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
/** Shortcuts other apps asked to put on the home screen. The system keeps the pinned set per launcher. */
object HomePins {
class Pinned(val info: ShortcutInfo, val icon: Bitmap?, val appLabel: String, val userSerial: Long) {
val key get() = "pin:${info.`package`}/${info.id}" + if (userSerial != 0L) "#$userSerial" else ""
val label get() = info.shortLabel?.toString()?.trim().orEmpty().ifEmpty { appLabel }
val work get() = userSerial != 0L
}
var pinned by mutableStateOf<List<Pinned>>(emptyList())
private set
private val main = Handler(Looper.getMainLooper())
private var listing = 0
fun refresh(context: Context) {
val app = context.applicationContext
val seq = ++listing
Thread({
val list = runCatching { load(app) }.getOrDefault(emptyList())
main.post { if (seq == listing) pinned = list }
}, "home-pins").start()
}
private fun load(context: Context): List<Pinned> {
if (!HomeApp.canUseShortcuts(context)) return emptyList()
val launcher = context.getSystemService(LauncherApps::class.java)
val users = context.getSystemService(UserManager::class.java)
val pm = context.packageManager
val own = Process.myUserHandle()
val query = LauncherApps.ShortcutQuery().setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED)
return users.userProfiles.flatMap { user ->
val serial = if (user == own) 0L else users.getSerialNumberForUser(user)
runCatching { launcher.getShortcuts(query, user) }.getOrNull().orEmpty()
.filter { it.isEnabled && it.isPinned }
.map { info ->
val appLabel = runCatching { pm.getApplicationLabel(pm.getApplicationInfo(info.`package`, 0)).toString() }
.getOrDefault(info.`package`)
Pinned(info, HomeApp.shortcutIcon(context, info), appLabel, serial)
}
}
}
fun accept(context: Context, request: LauncherApps.PinItemRequest): Boolean {
val ok = runCatching { request.isValid && request.accept() }.getOrDefault(false)
if (ok) refresh(context)
return ok
}
fun unpin(context: Context, pinned: Pinned) {
val info = pinned.info
val launcher = context.getSystemService(LauncherApps::class.java)
val query = LauncherApps.ShortcutQuery().setPackage(info.`package`).setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED)
val unpinned = runCatching {
val current = checkNotNull(launcher.getShortcuts(query, info.userHandle))
val keep = current.map { it.id } - info.id
launcher.pinShortcuts(info.`package`, keep, info.userHandle)
}.isSuccess
if (!unpinned) return
listing++
this.pinned = this.pinned.filterNot { it.key == pinned.key }
}
}
@@ -0,0 +1,317 @@
package com.droiddeck.launcher
import android.app.Activity
import android.appwidget.AppWidgetHost
import android.appwidget.AppWidgetHostView
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProviderInfo
import android.content.Context
import android.content.Intent
import android.content.pm.LauncherApps
import android.graphics.Bitmap
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.SizeF
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.ViewConfiguration
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.graphics.drawable.toBitmap
import org.json.JSONArray
import org.json.JSONObject
import kotlin.math.hypot
import kotlin.math.roundToInt
object HomeWidgets {
data class Placed(val id: Int, val widthDp: Int, val heightDp: Int)
data class Provider(
val info: AppWidgetProviderInfo,
val label: String,
val appLabel: String,
val preview: Bitmap?,
val widthDp: Int,
val heightDp: Int,
)
private const val HOST_ID = 0x0DDE
private const val PREFS = "home_launcher"
private const val KEY = "widgets"
private const val REQUEST_BIND = 0x0DD1
private const val REQUEST_CONFIGURE = 0x0DD2
private const val REQUEST_RECONFIGURE = 0x0DD3
const val MIN_WIDTH_DP = 96
const val MIN_HEIGHT_DP = 56
var widgets by mutableStateOf<List<Placed>>(emptyList())
private set
var providers by mutableStateOf<List<Provider>?>(null)
private set
private var loaded = false
private var host: Host? = null
private var pending: AppWidgetProviderInfo? = null
private var pendingId = AppWidgetManager.INVALID_APPWIDGET_ID
private var pinRequest: LauncherApps.PinItemRequest? = null
private val main = Handler(Looper.getMainLooper())
private class Host(context: Context) : AppWidgetHost(context, HOST_ID) {
override fun onCreateView(context: Context, appWidgetId: Int, appWidget: AppWidgetProviderInfo?): AppWidgetHostView =
HomeWidgetView(context)
}
class HomeWidgetView(context: Context) : AppWidgetHostView(context) {
var onLongPress: (() -> Unit)? = null
private var fired = false
private var downX = 0f
private var downY = 0f
private val slop = ViewConfiguration.get(context).scaledTouchSlop
private val longPress = Runnable {
fired = true
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
onLongPress?.invoke()
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
when (ev.actionMasked) {
MotionEvent.ACTION_DOWN -> {
fired = false
downX = ev.x
downY = ev.y
postDelayed(longPress, ViewConfiguration.getLongPressTimeout().toLong())
}
MotionEvent.ACTION_MOVE -> if (hypot(ev.x - downX, ev.y - downY) > slop) removeCallbacks(longPress)
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_POINTER_DOWN -> removeCallbacks(longPress)
}
return fired
}
override fun onTouchEvent(event: MotionEvent): Boolean {
if (fired) {
if (event.actionMasked == MotionEvent.ACTION_UP || event.actionMasked == MotionEvent.ACTION_CANCEL) fired = false
return true
}
return super.onTouchEvent(event)
}
}
private fun host(context: Context): Host = host ?: Host(context.applicationContext).also { host = it }
fun load(context: Context) {
if (loaded) return
loaded = true
val raw = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).getString(KEY, null) ?: return
val manager = AppWidgetManager.getInstance(context)
val parsed = runCatching {
val array = JSONArray(raw)
(0 until array.length()).map { i ->
val o = array.getJSONObject(i)
Placed(o.getInt("id"), o.getInt("w"), o.getInt("h"))
}
}.getOrDefault(emptyList())
val live = parsed.filter { manager.getAppWidgetInfo(it.id) != null }
parsed.filterNot { it in live }.forEach { host(context).deleteAppWidgetId(it.id) }
widgets = live
if (live.size != parsed.size) save(context)
}
fun startListening(context: Context) {
load(context)
runCatching { host(context).startListening() }
}
fun stopListening(context: Context) {
runCatching { host(context).stopListening() }
}
fun info(context: Context, id: Int): AppWidgetProviderInfo? = AppWidgetManager.getInstance(context).getAppWidgetInfo(id)
fun createView(context: Context, id: Int): HomeWidgetView? {
val info = info(context, id) ?: return null
return host(context).createView(context, id, info) as? HomeWidgetView
}
fun applySize(view: AppWidgetHostView, widthDp: Int, heightDp: Int) {
if (Build.VERSION.SDK_INT >= 31) {
view.updateAppWidgetSize(Bundle(), listOf(SizeF(widthDp.toFloat(), heightDp.toFloat())))
} else {
@Suppress("DEPRECATION")
view.updateAppWidgetSize(null, widthDp, heightDp, widthDp, heightDp)
}
}
fun loadProviders(context: Context) {
if (providers != null) return
val app = context.applicationContext
Thread({
val pm = app.packageManager
val manager = AppWidgetManager.getInstance(app)
val dpi = app.resources.displayMetrics.densityDpi
val list = manager.installedProviders.mapNotNull { info ->
runCatching {
val appLabel = runCatching { pm.getApplicationLabel(pm.getApplicationInfo(info.provider.packageName, 0)).toString() }
.getOrDefault(info.provider.packageName)
val drawable = info.loadPreviewImage(app, dpi) ?: info.loadIcon(app, dpi)
val preview = drawable?.let { d ->
val w = d.intrinsicWidth.takeIf { it > 0 } ?: 192
val h = d.intrinsicHeight.takeIf { it > 0 } ?: 192
val scale = minOf(1f, 360f / maxOf(w, h))
d.toBitmap((w * scale).roundToInt().coerceAtLeast(1), (h * scale).roundToInt().coerceAtLeast(1))
}
val (w, h) = defaultSize(app, info)
Provider(info, info.loadLabel(pm)?.toString()?.trim().orEmpty().ifEmpty { appLabel }, appLabel, preview, w, h)
}.getOrNull()
}.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, Provider::appLabel).thenBy(String.CASE_INSENSITIVE_ORDER, Provider::label))
main.post { providers = list }
}, "widget-providers").start()
}
fun refreshProviders(context: Context) {
providers = null
loadProviders(context)
}
private fun defaultSize(context: Context, info: AppWidgetProviderInfo): Pair<Int, Int> {
val density = context.resources.displayMetrics.density
var w = (info.minWidth / density).roundToInt()
var h = (info.minHeight / density).roundToInt()
if (Build.VERSION.SDK_INT >= 31) {
if (info.targetCellWidth > 0) w = maxOf(w, info.targetCellWidth * 80)
if (info.targetCellHeight > 0) h = maxOf(h, info.targetCellHeight * 80)
}
return w.coerceIn(160, 560) to h.coerceIn(MIN_HEIGHT_DP, 360)
}
fun minSize(context: Context, id: Int): Pair<Int, Int> {
val info = info(context, id) ?: return MIN_WIDTH_DP to MIN_HEIGHT_DP
val density = context.resources.displayMetrics.density
val w = (if (info.minResizeWidth > 0) info.minResizeWidth else info.minWidth) / density
val h = (if (info.minResizeHeight > 0) info.minResizeHeight else info.minHeight) / density
return w.roundToInt().coerceAtLeast(MIN_WIDTH_DP) to h.roundToInt().coerceAtLeast(MIN_HEIGHT_DP)
}
fun add(activity: Activity, provider: Provider) {
if (pendingId == AppWidgetManager.INVALID_APPWIDGET_ID) add(activity, provider.info, null)
}
/** Places a widget another app asked for; its own app has already configured it. */
fun addPinned(activity: Activity, request: LauncherApps.PinItemRequest): Boolean {
if (pendingId != AppWidgetManager.INVALID_APPWIDGET_ID) return false
val info = request.getAppWidgetProviderInfo(activity) ?: return false
load(activity)
add(activity, info, request)
return pending != null
}
private fun add(activity: Activity, info: AppWidgetProviderInfo, request: LauncherApps.PinItemRequest?) {
val h = host(activity)
val id = h.allocateAppWidgetId()
pending = info
pendingId = id
pinRequest = request
val manager = AppWidgetManager.getInstance(activity)
val bound = runCatching { manager.bindAppWidgetIdIfAllowed(id, info.profile, info.provider, null) }.getOrDefault(false)
if (bound) {
bound(activity, id, info)
return
}
val bind = Intent(AppWidgetManager.ACTION_APPWIDGET_BIND)
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id)
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.provider)
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE, info.profile)
@Suppress("DEPRECATION")
runCatching { activity.startActivityForResult(bind, REQUEST_BIND) }.onFailure { abandon(activity) }
}
private fun bound(activity: Activity, id: Int, info: AppWidgetProviderInfo) {
val request = pinRequest
if (request == null) {
configure(activity, id, info)
return
}
val accepted = runCatching { request.isValid && request.accept(Bundle().apply { putInt(AppWidgetManager.EXTRA_APPWIDGET_ID, id) }) }
.getOrDefault(false)
if (accepted) place(activity, id, info) else abandon(activity)
}
private fun configure(activity: Activity, id: Int, info: AppWidgetProviderInfo) {
val optional = Build.VERSION.SDK_INT >= 31 &&
info.widgetFeatures and AppWidgetProviderInfo.WIDGET_FEATURE_CONFIGURATION_OPTIONAL != 0
if (info.configure == null || optional) {
place(activity, id, info)
return
}
runCatching { host(activity).startAppWidgetConfigureActivityForResult(activity, id, 0, REQUEST_CONFIGURE, null) }
.onFailure { place(activity, id, info) }
}
fun canReconfigure(context: Context, id: Int): Boolean {
val info = info(context, id) ?: return false
if (info.configure == null) return false
return Build.VERSION.SDK_INT < 28 || info.widgetFeatures and AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE != 0
}
fun reconfigure(activity: Activity, id: Int) {
runCatching { host(activity).startAppWidgetConfigureActivityForResult(activity, id, 0, REQUEST_RECONFIGURE, null) }
}
fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int): Boolean {
when (requestCode) {
REQUEST_BIND -> {
val info = pending
if (resultCode == Activity.RESULT_OK && info != null) bound(activity, pendingId, info) else abandon(activity)
}
REQUEST_CONFIGURE -> {
val info = pending
if (resultCode == Activity.RESULT_OK && info != null) place(activity, pendingId, info) else abandon(activity)
}
REQUEST_RECONFIGURE -> Unit
else -> return false
}
return true
}
private fun abandon(context: Context) {
if (pendingId != AppWidgetManager.INVALID_APPWIDGET_ID) host(context).deleteAppWidgetId(pendingId)
pending = null
pinRequest = null
pendingId = AppWidgetManager.INVALID_APPWIDGET_ID
}
private fun place(context: Context, id: Int, info: AppWidgetProviderInfo) {
val (w, h) = defaultSize(context, info)
pending = null
pinRequest = null
pendingId = AppWidgetManager.INVALID_APPWIDGET_ID
widgets = widgets + Placed(id, w, h)
save(context)
}
fun resize(context: Context, id: Int, widthDp: Int, heightDp: Int) {
widgets = widgets.map { if (it.id == id) it.copy(widthDp = widthDp, heightDp = heightDp) else it }
save(context)
}
fun move(context: Context, id: Int, by: Int) {
val i = widgets.indexOfFirst { it.id == id }
val j = i + by
if (i < 0 || j !in widgets.indices) return
widgets = widgets.toMutableList().apply { add(j, removeAt(i)) }
save(context)
}
fun remove(context: Context, id: Int) {
host(context).deleteAppWidgetId(id)
widgets = widgets.filterNot { it.id == id }
save(context)
}
private fun save(context: Context) {
val array = JSONArray()
for (w in widgets) array.put(JSONObject().put("id", w.id).put("w", w.widthDp).put("h", w.heightDp))
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit().putString(KEY, array.toString()).apply()
}
}
@@ -0,0 +1,82 @@
package com.droiddeck.launcher
import android.content.Intent
import android.content.pm.LauncherApps
import android.graphics.Bitmap
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.core.graphics.drawable.toBitmap
import com.droiddeck.launcher.core.AppUiPrefs
import com.droiddeck.launcher.ui.DroidDeckTheme
import com.droiddeck.launcher.ui.PinRequestPrompt
class PinRequestActivity : ComponentActivity() {
override fun attachBaseContext(newBase: android.content.Context) {
super.attachBaseContext(newBase)
com.droiddeck.launcher.core.AppLanguage.applyTo(this, newBase)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val request = runCatching { getSystemService(LauncherApps::class.java).getPinItemRequest(intent) }.getOrNull()
if (request == null || !request.isValid) {
finish()
return
}
val prompt = when (request.requestType) {
LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT -> request.shortcutInfo?.let { info ->
val appLabel = appLabel(info.`package`)
Triple(info.shortLabel?.toString().orEmpty().ifEmpty { appLabel }, appLabel, HomeApp.shortcutIcon(this, info))
}
LauncherApps.PinItemRequest.REQUEST_TYPE_APPWIDGET -> request.getAppWidgetProviderInfo(this)?.let { info ->
val appLabel = appLabel(info.provider.packageName)
val dpi = resources.displayMetrics.densityDpi
val icon = runCatching { (info.loadPreviewImage(this, dpi) ?: info.loadIcon(this, dpi))?.let(::scaled) }.getOrNull()
Triple(info.loadLabel(packageManager)?.toString().orEmpty().ifEmpty { appLabel }, appLabel, icon)
}
else -> null
}
if (prompt == null) {
finish()
return
}
val (label, appLabel, icon) = prompt
val widget = request.requestType == LauncherApps.PinItemRequest.REQUEST_TYPE_APPWIDGET
setContent {
DroidDeckTheme(appScale = AppUiPrefs.scale(this)) {
PinRequestPrompt(
label = label, appLabel = appLabel, icon = icon, wide = widget,
onAdd = {
if (!widget) {
HomePins.accept(this, request)
finish()
} else if (!HomeWidgets.addPinned(this, request)) finish()
},
onCancel = { finish() },
)
}
}
}
@Suppress("OVERRIDE_DEPRECATION")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (HomeWidgets.onActivityResult(this, requestCode, resultCode)) {
finish()
return
}
@Suppress("DEPRECATION")
super.onActivityResult(requestCode, resultCode, data)
}
private fun appLabel(packageName: String): String =
runCatching { packageManager.getApplicationLabel(packageManager.getApplicationInfo(packageName, 0)).toString() }
.getOrDefault(packageName)
private fun scaled(d: android.graphics.drawable.Drawable): Bitmap {
val w = d.intrinsicWidth.takeIf { it > 0 } ?: 192
val h = d.intrinsicHeight.takeIf { it > 0 } ?: 192
val k = minOf(1f, 480f / maxOf(w, h))
return d.toBitmap((w * k).toInt().coerceAtLeast(1), (h * k).toInt().coerceAtLeast(1))
}
}
@@ -0,0 +1,959 @@
package com.droiddeck.launcher.ui
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.view.KeyEvent
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.hoverable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.FilterAlt
import androidx.compose.material.icons.filled.Sort
import androidx.compose.material.icons.filled.Widgets
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.boundsInRoot
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.zIndex
import com.droiddeck.launcher.HomeApp
import com.droiddeck.launcher.HomeFolders
import com.droiddeck.launcher.HomeHidden
import com.droiddeck.launcher.HomePins
import android.content.pm.ShortcutInfo
import android.graphics.Bitmap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import com.droiddeck.launcher.HomeWidgets
import com.droiddeck.launcher.R
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
private sealed interface AppEntry : AppEntryKey {
val key: String
class App(val app: HomeApp.LaunchableApp) : AppEntry {
override val label get() = app.label
override val key get() = "app:" + app.key
override val installedAt get() = app.installedAt
override val updatedAt get() = app.updatedAt
}
class Folder(val folder: HomeFolders.Folder, val apps: List<HomeApp.LaunchableApp>) : AppEntry {
override val label get() = folder.name
override val key get() = "folder:" + folder.id
override val installedAt get() = apps.maxOf { it.installedAt }
override val updatedAt get() = apps.maxOf { it.updatedAt }
}
class Shortcut(val pinned: HomePins.Pinned) : AppEntry {
override val label get() = pinned.label
override val key get() = pinned.key
override val installedAt get() = 0L
override val updatedAt get() = 0L
}
}
private fun indexLetter(label: String): String {
val c = label.trim().firstOrNull()?.uppercaseChar() ?: return "#"
return if (c in 'A'..'Z') c.toString() else if (c.isLetter()) c.toString() else "#"
}
private fun searchRank(label: String, q: String): Int? {
if (label.startsWith(q, ignoreCase = true)) return 0
if (label.split(' ', '-', '_', '.').any { it.startsWith(q, ignoreCase = true) }) return 1
if (label.contains(q, ignoreCase = true)) return 2
return null
}
internal fun Context.findActivity(): Activity? {
var c: Context? = this
while (c is ContextWrapper) {
if (c is Activity) return c
c = c.baseContext
}
return null
}
private fun Modifier.menuKey(onMenu: () -> Unit): Modifier = onPreviewKeyEvent { event ->
val k = event.nativeKeyEvent
if (k.keyCode != KeyEvent.KEYCODE_BUTTON_Y && k.keyCode != KeyEvent.KEYCODE_MENU) false
else {
if (k.action == KeyEvent.ACTION_DOWN && k.repeatCount == 0) onMenu()
true
}
}
internal object AppsView {
const val ALL = "all"
const val GAMES = "games"
const val DOWNLOADED = "downloaded"
const val SYSTEM = "system"
const val HIDDEN = "hidden"
val FILTERS = listOf(ALL, GAMES, DOWNLOADED, SYSTEM, HIDDEN)
const val NAME_ASC = "nameAsc"
const val NAME_DESC = "nameDesc"
const val INSTALLED = "installed"
const val UPDATED = "updated"
val SORTS = listOf(NAME_ASC, NAME_DESC, INSTALLED, UPDATED)
var filter by mutableStateOf(ALL)
var sort by mutableStateOf(NAME_ASC)
private var loaded = false
private fun prefs(context: Context) = context.getSharedPreferences("home_launcher", Context.MODE_PRIVATE)
fun load(context: Context) {
if (loaded) return
loaded = true
val p = prefs(context)
filter = p.getString("filter", ALL)?.takeIf { it in FILTERS } ?: ALL
sort = p.getString("sort", NAME_ASC)?.takeIf { it in SORTS } ?: NAME_ASC
}
fun setFilter(context: Context, value: String) {
filter = value
prefs(context).edit().putString("filter", value).apply()
}
fun setSort(context: Context, value: String) {
sort = value
prefs(context).edit().putString("sort", value).apply()
}
fun keeps(app: HomeApp.LaunchableApp): Boolean {
val hidden = app.key in HomeHidden.keys
return when (filter) {
HIDDEN -> hidden
GAMES -> !hidden && app.game
DOWNLOADED -> !hidden && !app.system
SYSTEM -> !hidden && app.system
else -> !hidden
}
}
fun comparator(): Comparator<AppEntryKey> = when (sort) {
NAME_DESC -> compareBy<AppEntryKey, String>(String.CASE_INSENSITIVE_ORDER) { it.label }.reversed()
INSTALLED -> compareByDescending<AppEntryKey> { it.installedAt }.thenBy(String.CASE_INSENSITIVE_ORDER) { it.label }
UPDATED -> compareByDescending<AppEntryKey> { it.updatedAt }.thenBy(String.CASE_INSENSITIVE_ORDER) { it.label }
else -> compareBy(String.CASE_INSENSITIVE_ORDER) { it.label }
}
}
internal interface AppEntryKey {
val label: String
val installedAt: Long
val updatedAt: Long
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
internal fun AndroidAppsPage(s: FrontEndState, modifier: Modifier, onLaunch: (HomeApp.LaunchableApp) -> Unit) {
val ctx = LocalContext.current
remember { HomeFolders.load(ctx); HomeWidgets.load(ctx); HomeHidden.load(ctx); AppsView.load(ctx); true }
val colors = MaterialTheme.colorScheme
val narrow = LocalNarrowPane.current
val padH = if (narrow) 16.dp else 22.dp
var query by rememberSaveable { mutableStateOf("") }
var openFolder by rememberSaveable { mutableStateOf<String?>(null) }
var renameOnOpen by rememberSaveable { mutableStateOf(false) }
var picking by rememberSaveable { mutableStateOf(false) }
val folders = HomeFolders.folders
val byKey = remember(s.androidApps) { s.androidApps.associateBy { it.key } }
val byPkg = remember(s.androidApps) { s.androidApps.filterNot { it.work }.groupBy { it.packageName }.mapNotNull { (pkg, apps) -> apps.singleOrNull()?.let { pkg to it } }.toMap() }
LaunchedEffect(byPkg) { if (byPkg.isNotEmpty()) HomeFolders.migrateMembers(ctx) { byPkg[it]?.key } }
val member: (String) -> HomeApp.LaunchableApp? = { byKey[it] ?: byPkg[it] }
val pins = HomePins.pinned
val hidden = HomeHidden.keys
val q = query.trim()
val filter = AppsView.filter
val sort = AppsView.sort
val entries: List<AppEntry> = remember(s.androidApps, folders, q, filter, sort, pins, hidden) {
val kept = s.androidApps.filter { AppsView.keeps(it) }
val shortcuts = if (filter == AppsView.ALL) pins.map { AppEntry.Shortcut(it) } else emptyList()
if (q.isNotEmpty()) {
(kept.map { AppEntry.App(it) } + shortcuts).mapNotNull { e -> searchRank(e.label, q)?.let { it to e } }
.sortedWith(compareBy<Pair<Int, AppEntry>> { it.first }.thenBy(String.CASE_INSENSITIVE_ORDER) { it.second.label })
.map { it.second }
} else if (filter != AppsView.ALL) {
kept.map { AppEntry.App(it) }.sortedWith(AppsView.comparator())
} else {
val folderEntries = folders.mapNotNull { f ->
val apps = f.apps.mapNotNull(member).filter { it.key !in hidden }.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.label })
if (apps.isEmpty()) null else AppEntry.Folder(f, apps)
}
val grouped = folderEntries.flatMap { f -> f.apps.map { it.key } }.toSet()
(folderEntries + kept.filter { it.key !in grouped }.map { AppEntry.App(it) } + shortcuts).sortedWith(AppsView.comparator())
}
}
val grid = rememberLazyGridState()
LaunchedEffect(q, filter, sort) { grid.scrollToItem(0) }
val widgetsShown = q.isEmpty() && filter == AppsView.ALL && HomeWidgets.widgets.isNotEmpty()
val leadItems = if (widgetsShown) 1 else 0
val byName = sort == AppsView.NAME_ASC || sort == AppsView.NAME_DESC
val newFolder: (HomeApp.LaunchableApp) -> Unit = { app ->
val f = HomeFolders.create(ctx, ctx.getString(R.string.apps_folder_default), app.key)
renameOnOpen = true
openFolder = f.id
}
Box(modifier) {
Column(Modifier.fillMaxSize().imePadding()) {
Box(Modifier.fillMaxWidth().padding(start = padH, end = padH, top = if (narrow) 8.dp else 12.dp)) {
ModeHeader(
stringResource(R.string.content_android_apps), query, { query = it },
placeholder = stringResource(R.string.apps_search_hint),
clearSearchFocus = openFolder != null || picking,
lead = {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.padding(start = 2.dp, end = 4.dp)) {
Text(stringResource(R.string.content_android_apps), fontSize = if (narrow) 20.sp else 24.sp, fontWeight = FontWeight.Bold, color = colors.onBackground, maxLines = 1)
val shownCount = s.androidApps.count { AppsView.keeps(it) } + if (filter == AppsView.ALL) pins.size else 0
Text(androidx.compose.ui.res.pluralStringResource(R.plurals.apps_count, shownCount, shownCount), fontSize = 13.sp, color = colors.onSurfaceVariant, maxLines = 1)
}
},
) {
AppsFilterButton()
AppsSortButton()
HeaderIconButton(stringResource(R.string.apps_add_widget), Icons.Filled.Widgets, "add-widget") {
HomeWidgets.loadProviders(ctx)
picking = true
}
}
}
Box(Modifier.weight(1f).fillMaxWidth()) {
val scroller = q.isEmpty() && byName && entries.size > 16
LazyVerticalGrid(
columns = GridCells.Adaptive(if (narrow) 84.dp else 96.dp),
state = grid,
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
contentPadding = PaddingValues(start = padH, end = if (scroller) padH + 22.dp else padH, top = 12.dp, bottom = 16.dp),
modifier = Modifier.fillMaxSize(),
) {
if (widgetsShown) item(key = "widgets", span = { GridItemSpan(maxLineSpan) }) {
BoxWithConstraints(Modifier.fillMaxWidth()) {
val maxW = maxWidth
FlowRow(
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
modifier = Modifier.fillMaxWidth().padding(bottom = 6.dp),
) {
for (w in HomeWidgets.widgets) key(w.id) { WidgetCard(w, maxW.value.toInt()) }
}
}
}
if (entries.isEmpty()) item(key = "empty", span = { GridItemSpan(maxLineSpan) }) {
Note(
when {
q.isNotEmpty() -> stringResource(R.string.apps_no_match, q)
filter == AppsView.HIDDEN -> stringResource(R.string.apps_hidden_none)
else -> stringResource(R.string.content_no_android_apps)
},
)
}
items(entries, key = { it.key }) { entry ->
when (entry) {
is AppEntry.App -> AppTile(
entry.app, folderId = null, onLaunch = onLaunch, onNewFolder = newFolder,
first = entry === entries.first(),
)
is AppEntry.Shortcut -> PinnedTile(entry.pinned, first = entry === entries.first())
is AppEntry.Folder -> FolderTile(entry, first = entry === entries.first(), onOpen = { renameOnOpen = false; openFolder = entry.folder.id }) {
renameOnOpen = true
openFolder = entry.folder.id
}
}
}
}
if (scroller) AlphabetScroller(entries, grid, leadItems, Modifier.align(Alignment.CenterEnd).fillMaxHeight().padding(end = 4.dp, top = 8.dp, bottom = 8.dp))
}
}
val folder = folders.firstOrNull { it.id == openFolder }
LaunchedEffect(folder == null, openFolder) { if (folder == null && openFolder != null) openFolder = null }
AnimatedVisibility(
folder != null, enter = fadeIn(Motion.tw(180)), exit = fadeOut(Motion.tw(160)),
modifier = Modifier.fillMaxSize().zIndex(2f),
) {
var last by remember { mutableStateOf(folder) }
if (folder != null) last = folder
last?.let { f ->
FolderSheet(f, f.apps.mapNotNull(member).filter { it.key !in hidden }.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.label }), renameOnOpen, onLaunch, newFolder) {
openFolder = null
renameOnOpen = false
}
}
}
AnimatedVisibility(
picking, enter = fadeIn(Motion.tw(180)), exit = fadeOut(Motion.tw(160)),
modifier = Modifier.fillMaxSize().zIndex(3f),
) { WidgetPicker(padH) { picking = false } }
}
}
@Composable
private fun SwipeLock() {
DisposableEffect(Unit) {
PageSwipe.locks++
onDispose { PageSwipe.locks-- }
}
}
@Composable
private fun appsFilterLabel(filter: String): String = stringResource(
when (filter) {
AppsView.GAMES -> R.string.apps_filter_games
AppsView.DOWNLOADED -> R.string.apps_filter_downloaded
AppsView.SYSTEM -> R.string.apps_filter_system
AppsView.HIDDEN -> R.string.apps_filter_hidden
else -> R.string.apps_filter_all
},
)
@Composable
private fun appsSortLabel(sort: String): String = stringResource(
when (sort) {
AppsView.NAME_DESC -> R.string.apps_sort_name_desc
AppsView.INSTALLED -> R.string.apps_sort_installed
AppsView.UPDATED -> R.string.apps_sort_updated
else -> R.string.apps_sort_name_asc
},
)
@Composable
private fun AppsFilterButton() = HeaderChoiceButton(
stringResource(R.string.apps_filter), Icons.Filled.FilterAlt, "apps-filter",
AppsView.FILTERS, AppsView.filter, { appsFilterLabel(it) }, AppsView::setFilter,
)
@Composable
private fun AppsSortButton() = HeaderChoiceButton(
stringResource(R.string.stores_sort), Icons.Filled.Sort, "apps-sort",
AppsView.SORTS, AppsView.sort, { appsSortLabel(it) }, AppsView::setSort,
)
@Composable
private fun HeaderChoiceButton(
title: String, icon: androidx.compose.ui.graphics.vector.ImageVector, key: String,
options: List<String>, current: String, label: @Composable (String) -> String, onPick: (Context, String) -> Unit,
) {
val pal = LocalPalette.current
val ctx = LocalContext.current
var at by remember { mutableStateOf<androidx.compose.ui.geometry.Rect?>(null) }
Box(Modifier.onGloballyPositioned { at = it.boundsInRoot() }) {
HeaderIconButton("$title · ${label(current)}", icon, key) {
val from = at ?: return@HeaderIconButton
Steps.ask(StepAsk(
anchor = from, side = StepSide.Left, accent = pal.signal, pillCorner = 8.dp, items = options.size,
handle = { Icon(icon, null, tint = pal.signal, modifier = Modifier.size(18.dp)) },
) {
StepTitle(title)
StepList {
options.forEachIndexed { i, option ->
StepChoice(
label(option), selected = option == current, enabled = open,
modifier = Modifier.fillMaxWidth().stepItem(i).then(if (option == current) Modifier.focusRequester(first) else Modifier),
) {
onPick(ctx, option)
Steps.fold()
}
}
}
})
}
}
}
@Composable
private fun AlphabetScroller(entries: List<AppEntry>, grid: LazyGridState, leadItems: Int, modifier: Modifier) {
val colors = MaterialTheme.colorScheme
val pal = LocalPalette.current
val scope = rememberCoroutineScope()
val firstAt = remember(entries) {
val map = LinkedHashMap<String, Int>()
entries.forEachIndexed { i, e -> map.putIfAbsent(indexLetter(e.label), i) }
map
}
val letters = remember(firstAt) { firstAt.keys.toList() }
var dragging by remember { mutableStateOf<String?>(null) }
val current by remember(entries, leadItems) {
derivedStateOf {
val i = (grid.firstVisibleItemIndex - leadItems).coerceIn(0, (entries.size - 1).coerceAtLeast(0))
entries.getOrNull(i)?.let { indexLetter(it.label) }
}
}
var height by remember { mutableIntStateOf(1) }
val jump: (Float) -> Unit = { y ->
val i = ((y / height) * letters.size).toInt().coerceIn(0, letters.size - 1)
val letter = letters[i]
if (letter != dragging) {
dragging = letter
val at = firstAt[letter] ?: 0
scope.launch { grid.scrollToItem(if (at == 0) 0 else leadItems + at) }
}
}
Box(modifier.width(22.dp)) {
Column(
verticalArrangement = Arrangement.SpaceEvenly, horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
.clip(RoundedCornerShape(11.dp))
.background(if (dragging != null) colors.surface.copy(alpha = 0.85f) else Color.Transparent)
.onSizeChanged { height = it.height.coerceAtLeast(1) }
.pointerInput(letters) {
awaitEachGesture {
val down = awaitFirstDown()
down.consume()
jump(down.position.y)
while (true) {
val event = awaitPointerEvent()
val c = event.changes.firstOrNull { it.id == down.id } ?: break
if (!c.pressed) break
c.consume()
jump(c.position.y)
}
dragging = null
}
},
) {
for (l in letters) {
val on = l == (dragging ?: current)
Text(
l, fontSize = 10.sp, lineHeight = 11.sp, fontWeight = if (on) FontWeight.Bold else FontWeight.Medium,
color = if (on) pal.signal else colors.onSurfaceVariant, textAlign = TextAlign.Center,
)
}
}
dragging?.let { l ->
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.align(Alignment.Center).offset { IntOffset(-64.dp.roundToPx(), 0) }.size(56.dp)
.clip(CircleShape).background(pal.signal),
) { Text(l, fontSize = 26.sp, fontWeight = FontWeight.Bold, color = pal.onPrimary) }
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun TileFrame(
id: String, first: Boolean, onClick: () -> Unit, onMenu: () -> Unit,
content: @Composable (hot: Boolean) -> Unit,
) {
val colors = MaterialTheme.colorScheme
val pal = LocalPalette.current
val src = remember { MutableInteractionSource() }
val hot = rememberHot(src)
val pressed by src.collectIsPressedAsState()
val scale by animateFloatAsState(if (pressed) 0.95f else if (hot) 1.04f else 1f, Motion.sp(0.55f, Spring.StiffnessMedium), label = "appScale")
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
.paneItem("tile:$id").then(if (first) Modifier.firstTile().paneEntry() else Modifier)
.graphicsLayer { scaleX = scale; scaleY = scale }
.clip(Shape12)
.background(if (hot) colors.surface else colors.surface.copy(alpha = 0.55f))
.glideBorder(hot, Shape12, pal.signal, hotWidth = 2.5.dp)
.hoverable(src)
.combinedClickable(interactionSource = src, indication = LocalIndication.current, role = Role.Button, onLongClick = onMenu, onClick = onClick)
.controllerConfirm(onClick = onClick)
.menuKey(onMenu)
.padding(horizontal = 6.dp, vertical = 10.dp),
) { content(hot) }
}
@Composable
private fun TileLabel(text: String) = Text(
text, fontSize = 12.sp, lineHeight = 15.sp, fontWeight = FontWeight.SemiBold, color = MaterialTheme.colorScheme.onBackground,
maxLines = 1, overflow = TextOverflow.Ellipsis, textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth().padding(top = 8.dp),
)
@Composable
private fun AppIcon(app: HomeApp.LaunchableApp, size: androidx.compose.ui.unit.Dp) {
val icon = app.icon
if (icon != null) Image(icon.asImageBitmap(), null, contentScale = ContentScale.Fit, modifier = Modifier.size(size))
else Box(Modifier.size(size).clip(CircleShape).background(artBrush(hueOf(app.label))))
}
@Composable
private fun AppTile(
app: HomeApp.LaunchableApp, folderId: String?, first: Boolean,
onLaunch: (HomeApp.LaunchableApp) -> Unit, onNewFolder: (HomeApp.LaunchableApp) -> Unit,
) {
var menu by remember { mutableStateOf(false) }
Box {
TileFrame(app.key, first, onClick = { onLaunch(app) }, onMenu = { menu = true }) {
AppIcon(app, 52.dp)
TileLabel(app.label)
}
AppMenu(app, folderId, menu, onDismiss = { menu = false }, onNewFolder = onNewFolder)
}
}
@Composable
private fun AppMenu(
app: HomeApp.LaunchableApp, folderId: String?, open: Boolean,
onDismiss: () -> Unit, onNewFolder: (HomeApp.LaunchableApp) -> Unit,
) {
val ctx = LocalContext.current
var choosing by remember { mutableStateOf(false) }
var shortcuts by remember { mutableStateOf<List<Pair<ShortcutInfo, Bitmap?>>>(emptyList()) }
LaunchedEffect(open) {
if (!open) {
choosing = false
return@LaunchedEffect
}
shortcuts = withContext(Dispatchers.IO) {
HomeApp.shortcuts(ctx, app).map { it to HomeApp.shortcutIcon(ctx, it, 64) }
}
}
val isHidden = app.key in HomeHidden.keys
AnchoredMenu(open, onDismiss = onDismiss, title = app.label, compact = true, minWidth = 200.dp) { first ->
if (choosing) {
val others = HomeFolders.folders.filter { it.id != folderId }
var firstUsed = false
for (f in others) key(f.id) {
MenuItem(f.name, checked = false, focusRequester = if (!firstUsed) first.also { firstUsed = true } else null) {
HomeFolders.add(ctx, f.id, app.key)
onDismiss()
}
}
MenuItem(stringResource(R.string.apps_new_folder), checked = false, focusRequester = if (others.isEmpty()) first else null) {
onDismiss()
onNewFolder(app)
}
} else {
shortcuts.forEachIndexed { i, (info, icon) ->
key(info.id) {
MenuItem(
info.shortLabel?.toString().orEmpty(), checked = false,
focusRequester = if (i == 0) first else null,
leading = { MenuIcon(icon) },
) {
onDismiss()
runCatching { HomeApp.startShortcut(ctx, info) }
}
}
}
if (shortcuts.isNotEmpty()) Box(Modifier.fillMaxWidth().padding(vertical = 4.dp).height(1.dp).background(LocalPalette.current.line))
if (!isHidden) MenuItem(stringResource(R.string.apps_add_to_folder), checked = false, focusRequester = if (shortcuts.isEmpty()) first else null) { choosing = true }
if (folderId != null) MenuItem(stringResource(R.string.apps_remove_from_folder), checked = false) {
HomeFolders.remove(ctx, app.key)
onDismiss()
}
MenuItem(stringResource(if (isHidden) R.string.apps_unhide else R.string.apps_hide), checked = false,
focusRequester = if (isHidden && shortcuts.isEmpty()) first else null) {
onDismiss()
HomeHidden.set(ctx, app.key, !isHidden)
}
MenuItem(stringResource(R.string.apps_app_info), checked = false) {
onDismiss()
runCatching { HomeApp.openAppInfo(ctx, app) }
}
if (HomeApp.canUninstall(app)) MenuItem(stringResource(R.string.apps_uninstall), checked = false) {
onDismiss()
runCatching { HomeApp.uninstall(ctx, app) }
}
}
}
}
@Composable
private fun MenuIcon(icon: Bitmap?) {
if (icon != null) Image(icon.asImageBitmap(), null, modifier = Modifier.size(20.dp))
else Spacer(Modifier.size(20.dp))
}
@Composable
private fun PinnedTile(pinned: HomePins.Pinned, first: Boolean) {
val ctx = LocalContext.current
var menu by remember { mutableStateOf(false) }
Box {
TileFrame(pinned.key, first, onClick = { runCatching { HomeApp.startShortcut(ctx, pinned.info) } }, onMenu = { menu = true }) {
Box {
val icon = pinned.icon
if (icon != null) Image(icon.asImageBitmap(), null, contentScale = ContentScale.Fit, modifier = Modifier.size(52.dp))
else Box(Modifier.size(52.dp).clip(CircleShape).background(artBrush(hueOf(pinned.label))))
}
TileLabel(pinned.label)
}
AnchoredMenu(menu, onDismiss = { menu = false }, title = pinned.label, compact = true, minWidth = 180.dp) { firstItem ->
MenuItem(stringResource(R.string.apps_shortcut_remove), checked = false, detail = pinned.appLabel, focusRequester = firstItem) {
menu = false
HomePins.unpin(ctx, pinned)
}
}
}
}
@Composable
private fun FolderIcon(apps: List<HomeApp.LaunchableApp>, size: androidx.compose.ui.unit.Dp) {
val colors = MaterialTheme.colorScheme
val pal = LocalPalette.current
val cell = (size - 14.dp) / 2
Box(
Modifier.size(size).clip(RoundedCornerShape(size / 3.5f)).background(colors.surfaceVariant)
.glideBorder(false, RoundedCornerShape(size / 3.5f), pal.signal, pal.line2)
.padding(5.dp),
) {
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
for (row in apps.take(4).chunked(2)) Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
for (a in row) AppIcon(a, cell)
}
}
}
}
@Composable
private fun FolderTile(entry: AppEntry.Folder, first: Boolean, onOpen: () -> Unit, onRename: () -> Unit) {
val ctx = LocalContext.current
var menu by remember { mutableStateOf(false) }
Box {
TileFrame(entry.key, first, onClick = onOpen, onMenu = { menu = true }) {
FolderIcon(entry.apps, 52.dp)
TileLabel(entry.folder.name)
}
AnchoredMenu(menu, onDismiss = { menu = false }, title = entry.folder.name, compact = true, minWidth = 160.dp) { firstItem ->
MenuItem(stringResource(R.string.apps_folder_rename), checked = false, focusRequester = firstItem) {
menu = false
onRename()
}
MenuItem(stringResource(R.string.apps_folder_ungroup), checked = false) {
menu = false
HomeFolders.delete(ctx, entry.folder.id)
}
}
}
}
@Composable
private fun FolderSheet(
folder: HomeFolders.Folder, apps: List<HomeApp.LaunchableApp>, renameFirst: Boolean,
onLaunch: (HomeApp.LaunchableApp) -> Unit, onNewFolder: (HomeApp.LaunchableApp) -> Unit, onClose: () -> Unit,
) {
SwipeLock()
val ctx = LocalContext.current
val colors = MaterialTheme.colorScheme
val pal = LocalPalette.current
var renaming by remember(folder.id) { mutableStateOf(renameFirst) }
var name by remember(folder.id) { mutableStateOf(if (renameFirst) "" else folder.name) }
val commit = {
val n = name.trim()
if (n.isNotEmpty() && n != folder.name) HomeFolders.rename(ctx, folder.id, n)
renaming = false
}
val close = { if (renaming) commit(); onClose() }
BackHandler { close() }
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxSize().background(Color.Black.copy(alpha = 0.55f))
.pointerInput(Unit) { awaitEachGesture { val d = awaitFirstDown(); d.consume(); close() } },
) {
val shown = remember { androidx.compose.animation.core.MutableTransitionState(false) }.apply { targetState = true }
AnimatedVisibility(shown, enter = scaleIn(Motion.sp(0.75f), initialScale = 0.92f) + fadeIn(Motion.tw(160)), exit = scaleOut(Motion.tw(140)) + fadeOut(Motion.tw(140))) {
Column(
modifier = Modifier.widthIn(max = 620.dp).fillMaxWidth(0.9f).heightIn(max = 520.dp)
.clip(Shape16).background(pal.surfaceVariant.copy(alpha = 0.97f))
.glideBorder(false, Shape16, pal.signal, pal.signal.copy(alpha = 0.22f))
.pointerInput(Unit) { awaitEachGesture { awaitFirstDown(requireUnconsumed = false).consume() } }
.controllerBack(close)
.padding(16.dp),
) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth().padding(bottom = 10.dp)) {
if (renaming) Box(Modifier.weight(1f)) {
AdbTextField(
value = name, onValueChange = { name = it.take(40) }, label = stringResource(R.string.apps_folder_name),
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done, compact = true, showLabel = false,
placeholder = folder.name, focusOnShow = true, onDone = { commit() },
)
} else {
Text(folder.name, fontSize = 20.sp, fontWeight = FontWeight.Bold, color = colors.onBackground, maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier.weight(1f, fill = false))
SmallIconButton(Icons.Filled.Edit, stringResource(R.string.apps_folder_rename), tint = colors.onSurfaceVariant) { renaming = true }
Spacer(Modifier.weight(1f))
}
SmallIconButton(Icons.Filled.Close, stringResource(R.string.common_close), tint = colors.onSurfaceVariant) { close() }
}
LazyVerticalGrid(
columns = GridCells.Adaptive(92.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
items(apps, key = { it.key }) { app ->
AppTile(app, folderId = folder.id, first = false, onLaunch = { onClose(); onLaunch(it) }, onNewFolder = onNewFolder)
}
}
}
}
}
}
@Composable
private fun WidgetCard(w: HomeWidgets.Placed, maxWidthDp: Int) {
val ctx = LocalContext.current
val colors = MaterialTheme.colorScheme
val pal = LocalPalette.current
var menu by remember { mutableStateOf(false) }
val src = remember { MutableInteractionSource() }
val hot = rememberHot(src)
val width = w.widthDp.coerceAtMost(maxWidthDp)
val activity = remember(ctx) { ctx.findActivity() }
Box {
Box(
Modifier.size(width.dp, w.heightDp.dp)
.clip(Shape16)
.background(colors.surface.copy(alpha = 0.35f))
.glideBorder(hot || menu, Shape16, pal.signal)
.hoverable(src).focusable(interactionSource = src)
.controllerConfirm { menu = true }
.menuKey { menu = true },
) {
AndroidView(
factory = { c ->
(HomeWidgets.createView(c, w.id) ?: android.view.View(c)).apply {
val radius = 16 * c.resources.displayMetrics.density
outlineProvider = object : android.view.ViewOutlineProvider() {
override fun getOutline(view: android.view.View, outline: android.graphics.Outline) =
outline.setRoundRect(0, 0, view.width, view.height, radius)
}
clipToOutline = true
}
},
update = { v ->
if (v is HomeWidgets.HomeWidgetView) {
v.onLongPress = { menu = true }
HomeWidgets.applySize(v, width, w.heightDp)
}
},
modifier = Modifier.fillMaxSize(),
)
}
val (minW, minH) = remember(w.id) { HomeWidgets.minSize(ctx, w.id) }
val index = HomeWidgets.widgets.indexOfFirst { it.id == w.id }
val label = remember(w.id) { HomeWidgets.info(ctx, w.id)?.loadLabel(ctx.packageManager) ?: "" }
AnchoredMenu(menu, onDismiss = { menu = false }, title = label, compact = true, minWidth = 180.dp) { first ->
val step = 48
MenuItem(stringResource(R.string.apps_widget_wider), checked = false, enabled = width < maxWidthDp, focusRequester = first) {
HomeWidgets.resize(ctx, w.id, (width + step).coerceAtMost(maxWidthDp), w.heightDp)
}
MenuItem(stringResource(R.string.apps_widget_narrower), checked = false, enabled = width > minW) {
HomeWidgets.resize(ctx, w.id, (width - step).coerceAtLeast(minW), w.heightDp)
}
MenuItem(stringResource(R.string.apps_widget_taller), checked = false, enabled = w.heightDp < 480) {
HomeWidgets.resize(ctx, w.id, width, (w.heightDp + step).coerceAtMost(480))
}
MenuItem(stringResource(R.string.apps_widget_shorter), checked = false, enabled = w.heightDp > minH) {
HomeWidgets.resize(ctx, w.id, width, (w.heightDp - step).coerceAtLeast(minH))
}
if (index > 0) MenuItem(stringResource(R.string.apps_widget_move_earlier), checked = false) { HomeWidgets.move(ctx, w.id, -1) }
if (index in 0 until HomeWidgets.widgets.size - 1) MenuItem(stringResource(R.string.apps_widget_move_later), checked = false) { HomeWidgets.move(ctx, w.id, 1) }
if (activity != null && HomeWidgets.canReconfigure(ctx, w.id)) MenuItem(stringResource(R.string.apps_widget_configure), checked = false) {
menu = false
HomeWidgets.reconfigure(activity, w.id)
}
MenuItem(stringResource(R.string.apps_widget_remove), checked = false) {
menu = false
HomeWidgets.remove(ctx, w.id)
}
}
}
}
@Composable
private fun WidgetPicker(padH: androidx.compose.ui.unit.Dp, onClose: () -> Unit) {
SwipeLock()
val ctx = LocalContext.current
val colors = MaterialTheme.colorScheme
val pal = LocalPalette.current
val activity = remember(ctx) { ctx.findActivity() }
BackHandler { onClose() }
val providers = HomeWidgets.providers
Column(Modifier.fillMaxSize().background(colors.background).pointerInput(Unit) { awaitEachGesture { awaitFirstDown(requireUnconsumed = false) } }) {
LazyVerticalGrid(
columns = GridCells.Adaptive(150.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
contentPadding = PaddingValues(horizontal = padH, vertical = 14.dp),
modifier = Modifier.fillMaxSize(),
) {
item(key = "back", span = { GridItemSpan(maxLineSpan) }) {
Column {
Box(Modifier.padding(bottom = 10.dp)) { BackLink(stringResource(R.string.rail_apps), compact = true, onClick = onClose) }
PageHeader(stringResource(R.string.apps_add_widget)) {
if (providers != null) Chip(providers.size.toString(), ok = false)
}
}
}
when {
providers == null -> item(key = "loading", span = { GridItemSpan(maxLineSpan) }) { Note(stringResource(R.string.apps_widgets_loading)) }
providers.isEmpty() -> item(key = "none", span = { GridItemSpan(maxLineSpan) }) { Note(stringResource(R.string.apps_widgets_none)) }
else -> items(providers, key = { it.info.provider.flattenToString() }) { p ->
ProviderCard(p, first = p === providers.first()) {
onClose()
if (activity != null) HomeWidgets.add(activity, p)
}
}
}
}
}
}
@Composable
private fun ProviderCard(p: HomeWidgets.Provider, first: Boolean, onClick: () -> Unit) {
val colors = MaterialTheme.colorScheme
val pal = LocalPalette.current
val src = remember { MutableInteractionSource() }
val hot = rememberHot(src)
Column(
modifier = Modifier.fillMaxWidth().paneItem("widget:" + p.info.provider.flattenToString()).then(if (first) Modifier.firstTile() else Modifier)
.clip(Shape12).background(colors.surface)
.glideBorder(hot, Shape12, pal.signal, hotWidth = 2.5.dp)
.hoverable(src)
.clickable(interactionSource = src, indication = LocalIndication.current, role = Role.Button, onClick = onClick)
.controllerConfirm(onClick = onClick),
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxWidth().aspectRatio(1.6f).background(colors.surfaceVariant).padding(10.dp),
) {
val preview = p.preview
if (preview != null) Image(preview.asImageBitmap(), null, contentScale = ContentScale.Fit, modifier = Modifier.fillMaxSize())
}
Column(Modifier.padding(horizontal = 10.dp, vertical = 8.dp)) {
Text(p.label, fontSize = 13.sp, fontWeight = FontWeight.SemiBold, color = colors.onBackground, maxLines = 1, overflow = TextOverflow.Ellipsis)
val cells = "${(p.widthDp / 80f).roundToInt().coerceAtLeast(1)} × ${(p.heightDp / 80f).roundToInt().coerceAtLeast(1)}"
Text(if (p.appLabel == p.label) cells else "${p.appLabel} · $cells", fontSize = 12.sp, color = colors.onSurfaceVariant, maxLines = 1, overflow = TextOverflow.Ellipsis)
}
}
}
@Composable
fun PinRequestPrompt(label: String, appLabel: String, icon: Bitmap?, wide: Boolean, onAdd: () -> Unit, onCancel: () -> Unit) {
val colors = MaterialTheme.colorScheme
val pal = LocalPalette.current
BackHandler { onCancel() }
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxSize().background(Color.Black.copy(alpha = 0.55f))
.pointerInput(Unit) { awaitEachGesture { awaitFirstDown().consume(); onCancel() } },
) {
val shown = remember { androidx.compose.animation.core.MutableTransitionState(false) }.apply { targetState = true }
AnimatedVisibility(shown, enter = scaleIn(Motion.sp(0.75f), initialScale = 0.92f) + fadeIn(Motion.tw(160))) {
Column(
verticalArrangement = Arrangement.spacedBy(14.dp),
modifier = Modifier.widthIn(min = 280.dp, max = 420.dp)
.clip(Shape16).background(pal.surfaceVariant.copy(alpha = 0.97f))
.glideBorder(false, Shape16, pal.signal, pal.signal.copy(alpha = 0.22f))
.pointerInput(Unit) { awaitEachGesture { awaitFirstDown(requireUnconsumed = false).consume() } }
.controllerBack(onCancel)
.padding(18.dp),
) {
Text(
stringResource(R.string.apps_pin_title).uppercase(), fontSize = 12.sp, fontWeight = FontWeight.SemiBold,
letterSpacing = 1.5.sp, color = colors.onSurfaceVariant,
)
if (wide && icon != null) Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxWidth().heightIn(max = 180.dp).clip(Shape12).background(MaterialTheme.colorScheme.surface).padding(10.dp),
) { Image(icon.asImageBitmap(), null, contentScale = ContentScale.Fit, modifier = Modifier.fillMaxWidth()) }
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(14.dp)) {
if (!wide) {
if (icon != null) Image(icon.asImageBitmap(), null, contentScale = ContentScale.Fit, modifier = Modifier.size(52.dp))
else Box(Modifier.size(52.dp).clip(CircleShape).background(artBrush(hueOf(label))))
}
Column(Modifier.weight(1f)) {
Text(label, fontSize = 17.sp, fontWeight = FontWeight.Bold, color = colors.onBackground, maxLines = 2, overflow = TextOverflow.Ellipsis)
Text(appLabel, fontSize = 13.sp, color = colors.onSurfaceVariant, maxLines = 1, overflow = TextOverflow.Ellipsis)
}
}
Row(horizontalArrangement = Arrangement.spacedBy(10.dp, Alignment.End), modifier = Modifier.fillMaxWidth()) {
SecondaryButton(stringResource(R.string.common_cancel), onClick = onCancel)
PrimaryButton(stringResource(R.string.apps_pin_add), onClick = onAdd)
}
}
}
}
}
@@ -699,7 +699,7 @@ fun ComponentsPage(
Box(Modifier.fillMaxSize().onGloballyPositioned { pageAt = it.positionInRoot() }.onFocusChanged { pageFocused = it.hasFocus }) {
Column(
Modifier.fillMaxSize().padding(horizontal = if (narrow) 16.dp else 22.dp, vertical = if (narrow) 12.dp else 18.dp)
Modifier.fillMaxSize().padding(horizontal = if (narrow) 16.dp else 22.dp).padding(top = if (narrow) 12.dp else 18.dp)
.then(if (selectedGame == null) Modifier.bumpers(
onPrevious = { pick(layerStep(layer, -1)) },
onNext = { pick(layerStep(layer, 1)) },
@@ -972,7 +972,7 @@ private fun Stack(
onFocus = { onFocus(l) }, onClick = { onOpen(l) },
)
}
Column(Modifier.fillMaxWidth().verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(3.dp)) {
Column(Modifier.fillMaxWidth().verticalScroll(rememberScrollState()).padding(bottom = 16.dp), verticalArrangement = Arrangement.spacedBy(3.dp)) {
StackHeading(stringResource(R.string.comp_this_device))
card(GPU_TAB)
Spacer(Modifier.height(6.dp))
@@ -10,6 +10,8 @@ import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
import androidx.compose.animation.scaleIn
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.scaleOut
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.rememberUpdatedState
@@ -138,12 +140,19 @@ internal fun Pane(
before[0] = target
val targetNow by rememberUpdatedState(target)
val leave = if (Hops.linking) PageLeave.Link else PageLeave.Ordinary
LaunchedEffect(target) { Hops.linking = false }
LaunchedEffect(target) {
Hops.linking = false
PageSwipe.direction = 0
}
AnimatedContent(
targetState = target,
modifier = Modifier.onGloballyPositioned { paneAt[0] = it.positionInRoot() },
transitionSpec = {
val swipe = PageSwipe.direction
when {
swipe != 0 -> (fadeIn(Motion.tw(260, 40)) + slideInHorizontally(Motion.tw(360)) { it * swipe / 8 })
.togetherWith(fadeOut(Motion.tw(160)) + slideOutHorizontally(Motion.tw(220)) { -it * swipe / 8 })
.apply { targetContentZIndex = 1f }
targetState in origins -> (fadeIn(Motion.tw(120)))
.togetherWith(fadeOut(Motion.tw(320, 80)) + scaleOut(Motion.tw(480), targetScale = 0.95f))
.apply { targetContentZIndex = 1f }
@@ -232,13 +241,13 @@ private fun Content(
val padH = if (narrow) 16.dp else 22.dp
val padV = if (narrow) 12.dp else 18.dp
if (selected == "setup") {
Column(modifier = modifier.padding(horizontal = padH, vertical = padV)) {
Column(modifier = modifier.padding(horizontal = padH).padding(top = padV)) {
SetupPanel(s, a, onOpenDeveloperOptions, onRequestWirelessAdb, onPairWirelessDebugging)
}
return
}
if (selected == "updates") {
UpdatesPage(s, a, modifier.padding(horizontal = padH, vertical = padV))
UpdatesPage(s, a, modifier.padding(horizontal = padH).padding(top = padV))
return
}
if (selected == "steam") {
@@ -261,24 +270,12 @@ private fun Content(
DesktopPage(s, a, modifier, onSelect)
return
}
if (selected == "android-apps" && s.isHomeApp) {
AndroidAppsPage(s, modifier, onAndroidAppClick)
return
}
Column(modifier = modifier.verticalScroll(rememberScrollState()).padding(horizontal = padH, vertical = padV)) {
when {
selected == "android-apps" && s.isHomeApp -> {
Rise(0) { PageHeader(stringResource(R.string.content_android_apps)) { Chip(stringResource(R.string.content_apps_count, s.androidApps.size), ok = false) } }
if (s.androidApps.isEmpty()) Rise(3) { Note(stringResource(R.string.content_no_android_apps)) }
else Rise(3, Modifier.fillMaxWidth()) {
ArtGrid(s.androidApps.map { app ->
Tile(
app.label,
null,
null,
"android:${app.packageName}",
onClick = { onAndroidAppClick(app) },
iconBitmap = app.icon,
)
})
}
}
selected.startsWith("user:") -> {
val app = UserAppsState.items.firstOrNull { "user:${it.key}" == selected }
if (app == null) Note(stringResource(R.string.user_apps_gone)) else UserAppPage(app, s, a, onSelect)
@@ -118,7 +118,7 @@ internal fun BottomBar(
}
}
private fun navigationKeys(s: FrontEndState) = buildList {
internal fun navigationKeys(s: FrontEndState) = buildList {
add("steam"); add("games")
if (s.gameStoresEnabled) add("stores")
add("desktop"); add("components")
@@ -507,6 +507,7 @@ private fun FrontEndScreenBody(s: FrontEndState, a: FrontEndActions, page: (@Com
selected.startsWith("emu:") || selected.startsWith("rom:") || selected.startsWith("user:") || selected == "store" -> "desktop"
else -> s.pageKey ?: selected
}
val railSelectionNow = androidx.compose.runtime.rememberUpdatedState(railSelection)
var railPicks by remember { mutableStateOf(0) }
val showRailPage: (String, Boolean) -> Unit = { key, focusContent ->
if (key == "components") a.onComponents(focusContent)
@@ -609,7 +610,16 @@ private fun FrontEndScreenBody(s: FrontEndState, a: FrontEndActions, page: (@Com
},
) {
Pane(
s, selected, a, page, Modifier.fillMaxSize().then(paneFocus), { selected = it },
s, selected, a, page,
Modifier.fillMaxSize().then(paneFocus).pageSwipe({ !processSettingsPageVisible && appToChooseDisplay == null }) { dir ->
val keys = navigationKeys(s)
val at = keys.indexOf(railSelectionNow.value)
if (at >= 0) keys.getOrNull(at + dir)?.let { key ->
PageSwipe.direction = dir
showRailPage(key, false)
}
},
{ selected = it },
onAndroidAppClick = { app ->
if (s.secondScreenDisplays.isEmpty()) a.onAndroidApp(app, null)
else appToChooseDisplay = app
@@ -304,7 +304,7 @@ internal fun LibraryPage(s: FrontEndState, a: FrontEndActions, selected: String,
open, detailScroll,
gridScrolled = { grid.firstVisibleItemIndex > 0 || grid.firstVisibleItemScrollOffset > 0 },
onDetailFocus = { detailFocused = it },
modifier = modifier.padding(horizontal = if (narrow) 14.dp else 22.dp, vertical = if (narrow) 10.dp else 14.dp),
modifier = modifier.padding(horizontal = if (narrow) 14.dp else 22.dp).padding(top = if (narrow) 10.dp else 14.dp),
wrap = { key, content -> StoreTheme(GameRef.parse(key).let { r -> r.store ?: r.game(s.steamGames)?.source?.let { Store.byId(it) } }) { content() } },
grid = {
LibraryGrid(
@@ -638,7 +638,7 @@ private fun LibraryRow(e: LibraryEntry, first: Boolean, navigation: Modifier, ch
}
@Composable
private fun HeaderIconButton(description: String, icon: androidx.compose.ui.graphics.vector.ImageVector, key: String, onClick: () -> Unit) {
internal fun HeaderIconButton(description: String, icon: androidx.compose.ui.graphics.vector.ImageVector, key: String, onClick: () -> Unit) {
val colors = MaterialTheme.colorScheme
val pal = LocalPalette.current
val src = remember { MutableInteractionSource() }
@@ -655,7 +655,7 @@ private fun HeaderIconButton(description: String, icon: androidx.compose.ui.grap
/** Choosing Library in the rail lands on the first game, as Play Steam does on the Steam page. */
@Composable
private fun Modifier.paneEntry(): Modifier {
internal fun Modifier.paneEntry(): Modifier {
val ff = LocalFrontFocus.current ?: return this
androidx.compose.runtime.DisposableEffect(ff) {
ff.primaryAttached++
@@ -0,0 +1,60 @@
package com.droiddeck.launcher.ui
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.util.VelocityTracker
import androidx.compose.ui.input.pointer.util.addPointerInputChange
import androidx.compose.ui.unit.dp
import kotlin.math.abs
internal object PageSwipe {
var locks by mutableIntStateOf(0)
var direction by mutableIntStateOf(0)
}
// Runs on the Final pass so carousels, sliders, scrollers and widgets that consumed the drag keep it.
@Composable
internal fun Modifier.pageSwipe(enabled: () -> Boolean, onSwipe: (Int) -> Unit): Modifier {
val enabledNow by rememberUpdatedState(enabled)
val onSwipeNow by rememberUpdatedState(onSwipe)
return pointerInput(Unit) {
val distance = 72.dp.toPx()
val flingDistance = 32.dp.toPx()
val flingVelocity = 900.dp.toPx()
val slop = viewConfiguration.touchSlop
awaitEachGesture {
val down = awaitFirstDown(requireUnconsumed = false, pass = PointerEventPass.Final)
if (!enabledNow() || PageSwipe.locks > 0) return@awaitEachGesture
val tracker = VelocityTracker()
tracker.addPointerInputChange(down)
var total = Offset.Zero
var mine = false
while (true) {
val event = awaitPointerEvent(PointerEventPass.Final)
if (event.changes.size > 1) return@awaitEachGesture
val change = event.changes.firstOrNull { it.id == down.id } ?: return@awaitEachGesture
if (!change.pressed) break
if (!mine && change.isConsumed) return@awaitEachGesture
total += change.position - change.previousPosition
tracker.addPointerInputChange(change)
if (!mine && abs(total.x) > slop && abs(total.x) > abs(total.y) * 2f) mine = true
if (mine) change.consume()
}
if (!mine || PageSwipe.locks > 0) return@awaitEachGesture
val vx = tracker.calculateVelocity().x
val horizontal = abs(total.x) > abs(total.y) * 1.5f
val far = abs(total.x) > distance
val flung = abs(total.x) > flingDistance && abs(vx) > flingVelocity && (vx > 0) == (total.x > 0)
if (horizontal && (far || flung)) onSwipeNow(if (total.x < 0) 1 else -1)
}
}
}
@@ -157,7 +157,7 @@ private fun FlathubHero(s: FrontEndState, ring: Int) {
StoreState.sections.values.flatten().mapNotNull { it.icon }.distinct().take(48).takeIf { it.size >= 8 }.orEmpty()
}
val busy = StoreState.busy == "setup"
BoxWithConstraints(Modifier.fillMaxSize().padding(horizontal = if (narrow) 14.dp else 22.dp, vertical = if (narrow) 10.dp else 14.dp)) {
BoxWithConstraints(Modifier.fillMaxSize().padding(horizontal = if (narrow) 14.dp else 22.dp).padding(top = if (narrow) 10.dp else 14.dp)) {
val shape = RoundedCornerShape(20.dp)
Box(Modifier.fillMaxWidth().height(maxHeight.coerceAtLeast(320.dp)).clip(shape).background(colors.background).border(1.dp, pal.line, shape)) {
androidx.compose.animation.Crossfade(icons, animationSpec = Motion.tw(600), label = "flathubWall") { tiles ->
@@ -316,7 +316,7 @@ internal fun Storefront(s: FrontEndState, a: FrontEndActions) {
}
Column(
Modifier.fillMaxSize().padding(horizontal = padH, vertical = if (narrow) 10.dp else 14.dp)
Modifier.fillMaxSize().padding(horizontal = padH).padding(top = if (narrow) 10.dp else 14.dp)
.bumpers(
onPrevious = { if (openApp == null) mode = modes[(modes.indexOf(mode) + modes.size - 1) % modes.size] },
onNext = { if (openApp == null) mode = modes[(modes.indexOf(mode) + 1) % modes.size] },
@@ -311,7 +311,7 @@ internal fun StoresPage(s: FrontEndState, a: FrontEndActions, modifier: Modifier
val backLabel = stringResource(R.string.common_back)
Box(modifier.onGloballyPositioned { hostAt = it.positionInRoot() }) {
Column(
modifier = Modifier.fillMaxSize().padding(horizontal = padH, vertical = if (narrow) 10.dp else 14.dp)
modifier = Modifier.fillMaxSize().padding(horizontal = padH).padding(top = if (narrow) 10.dp else 14.dp)
.then(if (openGame != null && !arranging) Modifier else Modifier.bumpers(
onPrevious = {
val picked = Store.byId(focusedChip)
@@ -115,7 +115,7 @@ internal fun UpdatesPage(s: FrontEndState, a: FrontEndActions, modifier: Modifie
}
BoxWithConstraints(Modifier.weight(1f).fillMaxWidth()) {
val stacked = LocalNarrowPane.current || maxWidth < 600.dp
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(bottom = 8.dp)) {
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(bottom = 16.dp)) {
if (stacked) {
StatusPanel(s, u, ua, me)
Box(Modifier.height(18.dp))
@@ -360,6 +360,7 @@ internal fun AdbTextField(
onNext: (() -> Unit)? = null,
onDone: (() -> Unit)? = null,
focusOnShow: Boolean = false,
showLabel: Boolean = true,
) {
val context = LocalContext.current
val focusAsked = remember { booleanArrayOf(false) }
@@ -374,7 +375,7 @@ internal fun AdbTextField(
val colors = MaterialTheme.colorScheme
Column(modifier = modifier.fillMaxWidth()) {
Text(
if (showLabel) Text(
label,
style = MaterialTheme.typography.labelSmall,
fontWeight = FontWeight.Medium,
+46
View File
@@ -3221,4 +3221,50 @@
<string name="itch_collections">المجموعات</string>
<string name="xbox_filter_cloud">سحابية</string>
<string name="stores_xbox_cloud_settings">إعدادات السحابة</string>
<string name="apps_search_hint">البحث في التطبيقات</string>
<string name="apps_no_match">لا توجد تطبيقات تطابق ”%1$s“.</string>
<string name="apps_add_widget">إضافة أداة</string>
<string name="apps_widgets_loading">جارٍ تحميل الأدوات…</string>
<string name="apps_widgets_none">لا توجد تطبيقات على هذا الجهاز توفّر أدوات.</string>
<string name="apps_widget_wider">أعرض</string>
<string name="apps_widget_narrower">أضيق</string>
<string name="apps_widget_taller">أطول</string>
<string name="apps_widget_shorter">أقصر</string>
<string name="apps_widget_move_earlier">نقل إلى الأمام</string>
<string name="apps_widget_move_later">نقل إلى الخلف</string>
<string name="apps_widget_configure">إعدادات الأداة</string>
<string name="apps_widget_remove">إزالة الأداة</string>
<string name="apps_add_to_folder">إضافة إلى مجلد</string>
<string name="apps_new_folder">مجلد جديد</string>
<string name="apps_remove_from_folder">إزالة من المجلد</string>
<string name="apps_app_info">معلومات التطبيق</string>
<string name="apps_uninstall">إلغاء التثبيت</string>
<string name="apps_folder_rename">إعادة التسمية</string>
<string name="apps_folder_ungroup">فك التجميع</string>
<string name="apps_folder_default">مجلد</string>
<string name="apps_folder_name">اسم المجلد</string>
<string name="apps_filter">عرض</string>
<string name="apps_filter_all">كل التطبيقات</string>
<string name="apps_filter_games">الألعاب</string>
<string name="apps_filter_downloaded">المنزّلة</string>
<string name="apps_filter_system">النظام</string>
<string name="apps_filter_hidden">المخفية</string>
<string name="apps_hide">إخفاء</string>
<string name="apps_unhide">إظهار في التطبيقات</string>
<string name="apps_shortcut_remove">إزالة الاختصار</string>
<string name="apps_pin_title">إضافة إلى التطبيقات</string>
<string name="apps_pin_add">إضافة</string>
<string name="apps_hidden_none">لا توجد تطبيقات مخفية.</string>
<string name="apps_sort_name_asc">الاسم أ–ي</string>
<string name="apps_sort_name_desc">الاسم ي–أ</string>
<string name="apps_sort_installed">المثبّتة مؤخرًا</string>
<string name="apps_sort_updated">المحدّثة مؤخرًا</string>
<plurals name="apps_count">
<item quantity="zero">%d تطبيق</item>
<item quantity="one">تطبيق واحد</item>
<item quantity="two">تطبيقان</item>
<item quantity="few">%d تطبيقات</item>
<item quantity="many">%d تطبيقًا</item>
<item quantity="other">%d تطبيق</item>
</plurals>
</resources>
@@ -3071,4 +3071,47 @@
<string name="itch_collections">Colecciones</string>
<string name="xbox_filter_cloud">Nube</string>
<string name="stores_xbox_cloud_settings">Configuración de nube</string>
<string name="apps_search_hint">Buscar apps</string>
<string name="apps_no_match">Ninguna app coincide con «%1$s».</string>
<string name="apps_add_widget">Agregar widget</string>
<string name="apps_widgets_loading">Cargando widgets…</string>
<string name="apps_widgets_none">Ninguna app de este dispositivo ofrece widgets.</string>
<string name="apps_widget_wider">Más ancho</string>
<string name="apps_widget_narrower">Más angosto</string>
<string name="apps_widget_taller">Más alto</string>
<string name="apps_widget_shorter">Más bajo</string>
<string name="apps_widget_move_earlier">Mover antes</string>
<string name="apps_widget_move_later">Mover después</string>
<string name="apps_widget_configure">Configuración del widget</string>
<string name="apps_widget_remove">Quitar widget</string>
<string name="apps_add_to_folder">Agregar a carpeta</string>
<string name="apps_new_folder">Carpeta nueva</string>
<string name="apps_remove_from_folder">Quitar de la carpeta</string>
<string name="apps_app_info">Información de la app</string>
<string name="apps_uninstall">Desinstalar</string>
<string name="apps_folder_rename">Cambiar nombre</string>
<string name="apps_folder_ungroup">Desagrupar</string>
<string name="apps_folder_default">Carpeta</string>
<string name="apps_folder_name">Nombre de la carpeta</string>
<string name="apps_filter">Mostrar</string>
<string name="apps_filter_all">Todas las apps</string>
<string name="apps_filter_games">Juegos</string>
<string name="apps_filter_downloaded">Descargadas</string>
<string name="apps_filter_system">Sistema</string>
<string name="apps_filter_hidden">Ocultas</string>
<string name="apps_hide">Ocultar</string>
<string name="apps_unhide">Mostrar en apps</string>
<string name="apps_shortcut_remove">Quitar acceso directo</string>
<string name="apps_pin_title">Agregar a Apps</string>
<string name="apps_pin_add">Agregar</string>
<string name="apps_hidden_none">No hay apps ocultas.</string>
<string name="apps_sort_name_asc">Nombre A–Z</string>
<string name="apps_sort_name_desc">Nombre Z–A</string>
<string name="apps_sort_installed">Instaladas recientemente</string>
<string name="apps_sort_updated">Actualizadas recientemente</string>
<plurals name="apps_count">
<item quantity="one">%d app</item>
<item quantity="many">%d apps</item>
<item quantity="other">%d apps</item>
</plurals>
</resources>
+42
View File
@@ -3085,4 +3085,46 @@
<string name="itch_collections">Колекции</string>
<string name="xbox_filter_cloud">Облак</string>
<string name="stores_xbox_cloud_settings">Настройки на облака</string>
<string name="apps_search_hint">Търсене на приложения</string>
<string name="apps_no_match">Няма приложения, отговарящи на „%1$s“.</string>
<string name="apps_add_widget">Добавяне на приспособление</string>
<string name="apps_widgets_loading">Приспособленията се зареждат…</string>
<string name="apps_widgets_none">Няма приложения с приспособления на това устройство.</string>
<string name="apps_widget_wider">По-широко</string>
<string name="apps_widget_narrower">По-тясно</string>
<string name="apps_widget_taller">По-високо</string>
<string name="apps_widget_shorter">По-ниско</string>
<string name="apps_widget_move_earlier">Преместване по-рано</string>
<string name="apps_widget_move_later">Преместване по-късно</string>
<string name="apps_widget_configure">Настройки на приспособлението</string>
<string name="apps_widget_remove">Премахване на приспособлението</string>
<string name="apps_add_to_folder">Добавяне в папка</string>
<string name="apps_new_folder">Нова папка</string>
<string name="apps_remove_from_folder">Премахване от папката</string>
<string name="apps_app_info">Информация за приложението</string>
<string name="apps_uninstall">Деинсталиране</string>
<string name="apps_folder_rename">Преименуване</string>
<string name="apps_folder_ungroup">Разгрупиране</string>
<string name="apps_folder_default">Папка</string>
<string name="apps_folder_name">Име на папката</string>
<string name="apps_filter">Показване</string>
<string name="apps_filter_all">Всички приложения</string>
<string name="apps_filter_games">Игри</string>
<string name="apps_filter_downloaded">Изтеглени</string>
<string name="apps_filter_system">Системни</string>
<string name="apps_filter_hidden">Скрити</string>
<string name="apps_hide">Скриване</string>
<string name="apps_unhide">Показване в приложенията</string>
<string name="apps_shortcut_remove">Премахване на прекия път</string>
<string name="apps_pin_title">Добавяне към приложенията</string>
<string name="apps_pin_add">Добавяне</string>
<string name="apps_hidden_none">Няма скрити приложения.</string>
<string name="apps_sort_name_asc">Име А–Я</string>
<string name="apps_sort_name_desc">Име Я–А</string>
<string name="apps_sort_installed">Наскоро инсталирани</string>
<string name="apps_sort_updated">Наскоро актуализирани</string>
<plurals name="apps_count">
<item quantity="one">%d приложение</item>
<item quantity="other">%d приложения</item>
</plurals>
</resources>
+44
View File
@@ -3153,4 +3153,48 @@
<string name="itch_collections">Kolekce</string>
<string name="xbox_filter_cloud">Cloud</string>
<string name="stores_xbox_cloud_settings">Nastavení cloudu</string>
<string name="apps_search_hint">Hledat aplikace</string>
<string name="apps_no_match">Výrazu „%1$s“ neodpovídá žádná aplikace.</string>
<string name="apps_add_widget">Přidat widget</string>
<string name="apps_widgets_loading">Načítání widgetů…</string>
<string name="apps_widgets_none">Žádná aplikace v zařízení nenabízí widgety.</string>
<string name="apps_widget_wider">Širší</string>
<string name="apps_widget_narrower">Užší</string>
<string name="apps_widget_taller">Vyšší</string>
<string name="apps_widget_shorter">Nižší</string>
<string name="apps_widget_move_earlier">Posunout dříve</string>
<string name="apps_widget_move_later">Posunout později</string>
<string name="apps_widget_configure">Nastavení widgetu</string>
<string name="apps_widget_remove">Odebrat widget</string>
<string name="apps_add_to_folder">Přidat do složky</string>
<string name="apps_new_folder">Nová složka</string>
<string name="apps_remove_from_folder">Odebrat ze složky</string>
<string name="apps_app_info">Informace o aplikaci</string>
<string name="apps_uninstall">Odinstalovat</string>
<string name="apps_folder_rename">Přejmenovat</string>
<string name="apps_folder_ungroup">Zrušit složku</string>
<string name="apps_folder_default">Složka</string>
<string name="apps_folder_name">Název složky</string>
<string name="apps_filter">Zobrazit</string>
<string name="apps_filter_all">Všechny aplikace</string>
<string name="apps_filter_games">Hry</string>
<string name="apps_filter_downloaded">Stažené</string>
<string name="apps_filter_system">Systémové</string>
<string name="apps_filter_hidden">Skryté</string>
<string name="apps_hide">Skrýt</string>
<string name="apps_unhide">Zobrazit v aplikacích</string>
<string name="apps_shortcut_remove">Odebrat zkratku</string>
<string name="apps_pin_title">Přidat do aplikací</string>
<string name="apps_pin_add">Přidat</string>
<string name="apps_hidden_none">Žádné skryté aplikace.</string>
<string name="apps_sort_name_asc">Název A–Z</string>
<string name="apps_sort_name_desc">Název Z–A</string>
<string name="apps_sort_installed">Nedávno nainstalované</string>
<string name="apps_sort_updated">Nedávno aktualizované</string>
<plurals name="apps_count">
<item quantity="one">%d aplikace</item>
<item quantity="few">%d aplikace</item>
<item quantity="many">%d aplikace</item>
<item quantity="other">%d aplikací</item>
</plurals>
</resources>
+42
View File
@@ -3085,4 +3085,46 @@
<string name="itch_collections">Samlinger</string>
<string name="xbox_filter_cloud">Sky</string>
<string name="stores_xbox_cloud_settings">Skyindstillinger</string>
<string name="apps_search_hint">Søg i apps</string>
<string name="apps_no_match">Ingen apps matcher “%1$s”.</string>
<string name="apps_add_widget">Tilføj widget</string>
<string name="apps_widgets_loading">Indlæser widgets…</string>
<string name="apps_widgets_none">Ingen apps på enheden tilbyder widgets.</string>
<string name="apps_widget_wider">Bredere</string>
<string name="apps_widget_narrower">Smallere</string>
<string name="apps_widget_taller">Højere</string>
<string name="apps_widget_shorter">Lavere</string>
<string name="apps_widget_move_earlier">Flyt tidligere</string>
<string name="apps_widget_move_later">Flyt senere</string>
<string name="apps_widget_configure">Widgetindstillinger</string>
<string name="apps_widget_remove">Fjern widget</string>
<string name="apps_add_to_folder">Føj til mappe</string>
<string name="apps_new_folder">Ny mappe</string>
<string name="apps_remove_from_folder">Fjern fra mappe</string>
<string name="apps_app_info">Appinfo</string>
<string name="apps_uninstall">Afinstaller</string>
<string name="apps_folder_rename">Omdøb</string>
<string name="apps_folder_ungroup">Opløs</string>
<string name="apps_folder_default">Mappe</string>
<string name="apps_folder_name">Mappenavn</string>
<string name="apps_filter">Vis</string>
<string name="apps_filter_all">Alle apps</string>
<string name="apps_filter_games">Spil</string>
<string name="apps_filter_downloaded">Downloadet</string>
<string name="apps_filter_system">System</string>
<string name="apps_filter_hidden">Skjulte</string>
<string name="apps_hide">Skjul</string>
<string name="apps_unhide">Vis i apps</string>
<string name="apps_shortcut_remove">Fjern genvej</string>
<string name="apps_pin_title">Føj til Apps</string>
<string name="apps_pin_add">Tilføj</string>
<string name="apps_hidden_none">Ingen skjulte apps.</string>
<string name="apps_sort_name_asc">Navn A–Å</string>
<string name="apps_sort_name_desc">Navn Å–A</string>
<string name="apps_sort_installed">Senest installeret</string>
<string name="apps_sort_updated">Senest opdateret</string>
<plurals name="apps_count">
<item quantity="one">%d app</item>
<item quantity="other">%d apps</item>
</plurals>
</resources>
+42
View File
@@ -3085,4 +3085,46 @@
<string name="itch_collections">Sammlungen</string>
<string name="xbox_filter_cloud">Cloud</string>
<string name="stores_xbox_cloud_settings">Cloud-Einstellungen</string>
<string name="apps_search_hint">Apps durchsuchen</string>
<string name="apps_no_match">Keine Apps passen zu „%1$s“.</string>
<string name="apps_add_widget">Widget hinzufügen</string>
<string name="apps_widgets_loading">Widgets werden geladen…</string>
<string name="apps_widgets_none">Keine App auf diesem Gerät bietet Widgets an.</string>
<string name="apps_widget_wider">Breiter</string>
<string name="apps_widget_narrower">Schmaler</string>
<string name="apps_widget_taller">Höher</string>
<string name="apps_widget_shorter">Niedriger</string>
<string name="apps_widget_move_earlier">Nach vorne</string>
<string name="apps_widget_move_later">Nach hinten</string>
<string name="apps_widget_configure">Widget-Einstellungen</string>
<string name="apps_widget_remove">Widget entfernen</string>
<string name="apps_add_to_folder">Zu Ordner hinzufügen</string>
<string name="apps_new_folder">Neuer Ordner</string>
<string name="apps_remove_from_folder">Aus Ordner entfernen</string>
<string name="apps_app_info">App-Info</string>
<string name="apps_uninstall">Deinstallieren</string>
<string name="apps_folder_rename">Umbenennen</string>
<string name="apps_folder_ungroup">Auflösen</string>
<string name="apps_folder_default">Ordner</string>
<string name="apps_folder_name">Ordnername</string>
<string name="apps_filter">Anzeigen</string>
<string name="apps_filter_all">Alle Apps</string>
<string name="apps_filter_games">Spiele</string>
<string name="apps_filter_downloaded">Heruntergeladen</string>
<string name="apps_filter_system">System</string>
<string name="apps_filter_hidden">Ausgeblendet</string>
<string name="apps_hide">Ausblenden</string>
<string name="apps_unhide">In Apps anzeigen</string>
<string name="apps_shortcut_remove">Verknüpfung entfernen</string>
<string name="apps_pin_title">Zu Apps hinzufügen</string>
<string name="apps_pin_add">Hinzufügen</string>
<string name="apps_hidden_none">Keine ausgeblendeten Apps.</string>
<string name="apps_sort_name_asc">Name A–Z</string>
<string name="apps_sort_name_desc">Name Z–A</string>
<string name="apps_sort_installed">Zuletzt installiert</string>
<string name="apps_sort_updated">Zuletzt aktualisiert</string>
<plurals name="apps_count">
<item quantity="one">%d App</item>
<item quantity="other">%d Apps</item>
</plurals>
</resources>
+42
View File
@@ -3085,4 +3085,46 @@
<string name="itch_collections">Συλλογές</string>
<string name="xbox_filter_cloud">Cloud</string>
<string name="stores_xbox_cloud_settings">Ρυθμίσεις cloud</string>
<string name="apps_search_hint">Αναζήτηση εφαρμογών</string>
<string name="apps_no_match">Καμία εφαρμογή δεν ταιριάζει με «%1$s».</string>
<string name="apps_add_widget">Προσθήκη γραφικού στοιχείου</string>
<string name="apps_widgets_loading">Φόρτωση γραφικών στοιχείων…</string>
<string name="apps_widgets_none">Καμία εφαρμογή στη συσκευή δεν προσφέρει γραφικά στοιχεία.</string>
<string name="apps_widget_wider">Πλατύτερο</string>
<string name="apps_widget_narrower">Στενότερο</string>
<string name="apps_widget_taller">Ψηλότερο</string>
<string name="apps_widget_shorter">Χαμηλότερο</string>
<string name="apps_widget_move_earlier">Μετακίνηση νωρίτερα</string>
<string name="apps_widget_move_later">Μετακίνηση αργότερα</string>
<string name="apps_widget_configure">Ρυθμίσεις γραφικού στοιχείου</string>
<string name="apps_widget_remove">Αφαίρεση γραφικού στοιχείου</string>
<string name="apps_add_to_folder">Προσθήκη σε φάκελο</string>
<string name="apps_new_folder">Νέος φάκελος</string>
<string name="apps_remove_from_folder">Αφαίρεση από φάκελο</string>
<string name="apps_app_info">Πληροφορίες εφαρμογής</string>
<string name="apps_uninstall">Απεγκατάσταση</string>
<string name="apps_folder_rename">Μετονομασία</string>
<string name="apps_folder_ungroup">Κατάργηση ομάδας</string>
<string name="apps_folder_default">Φάκελος</string>
<string name="apps_folder_name">Όνομα φακέλου</string>
<string name="apps_filter">Εμφάνιση</string>
<string name="apps_filter_all">Όλες οι εφαρμογές</string>
<string name="apps_filter_games">Παιχνίδια</string>
<string name="apps_filter_downloaded">Ληφθείσες</string>
<string name="apps_filter_system">Συστήματος</string>
<string name="apps_filter_hidden">Κρυφές</string>
<string name="apps_hide">Απόκρυψη</string>
<string name="apps_unhide">Εμφάνιση στις εφαρμογές</string>
<string name="apps_shortcut_remove">Αφαίρεση συντόμευσης</string>
<string name="apps_pin_title">Προσθήκη στις Εφαρμογές</string>
<string name="apps_pin_add">Προσθήκη</string>
<string name="apps_hidden_none">Δεν υπάρχουν κρυφές εφαρμογές.</string>
<string name="apps_sort_name_asc">Όνομα Α–Ω</string>
<string name="apps_sort_name_desc">Όνομα Ω–Α</string>
<string name="apps_sort_installed">Πρόσφατα εγκατεστημένες</string>
<string name="apps_sort_updated">Πρόσφατα ενημερωμένες</string>
<plurals name="apps_count">
<item quantity="one">%d εφαρμογή</item>
<item quantity="other">%d εφαρμογές</item>
</plurals>
</resources>
+43
View File
@@ -3121,4 +3121,47 @@
<string name="itch_collections">Colecciones</string>
<string name="xbox_filter_cloud">Nube</string>
<string name="stores_xbox_cloud_settings">Ajustes de nube</string>
<string name="apps_search_hint">Buscar aplicaciones</string>
<string name="apps_no_match">Ninguna aplicación coincide con «%1$s».</string>
<string name="apps_add_widget">Añadir widget</string>
<string name="apps_widgets_loading">Cargando widgets…</string>
<string name="apps_widgets_none">Ninguna aplicación de este dispositivo ofrece widgets.</string>
<string name="apps_widget_wider">Más ancho</string>
<string name="apps_widget_narrower">Más estrecho</string>
<string name="apps_widget_taller">Más alto</string>
<string name="apps_widget_shorter">Más bajo</string>
<string name="apps_widget_move_earlier">Mover antes</string>
<string name="apps_widget_move_later">Mover después</string>
<string name="apps_widget_configure">Ajustes del widget</string>
<string name="apps_widget_remove">Quitar widget</string>
<string name="apps_add_to_folder">Añadir a carpeta</string>
<string name="apps_new_folder">Carpeta nueva</string>
<string name="apps_remove_from_folder">Quitar de la carpeta</string>
<string name="apps_app_info">Información de la aplicación</string>
<string name="apps_uninstall">Desinstalar</string>
<string name="apps_folder_rename">Cambiar nombre</string>
<string name="apps_folder_ungroup">Desagrupar</string>
<string name="apps_folder_default">Carpeta</string>
<string name="apps_folder_name">Nombre de la carpeta</string>
<string name="apps_filter">Mostrar</string>
<string name="apps_filter_all">Todas las aplicaciones</string>
<string name="apps_filter_games">Juegos</string>
<string name="apps_filter_downloaded">Descargadas</string>
<string name="apps_filter_system">Sistema</string>
<string name="apps_filter_hidden">Ocultas</string>
<string name="apps_hide">Ocultar</string>
<string name="apps_unhide">Mostrar en aplicaciones</string>
<string name="apps_shortcut_remove">Quitar acceso directo</string>
<string name="apps_pin_title">Añadir a Aplicaciones</string>
<string name="apps_pin_add">Añadir</string>
<string name="apps_hidden_none">No hay aplicaciones ocultas.</string>
<string name="apps_sort_name_asc">Nombre A–Z</string>
<string name="apps_sort_name_desc">Nombre Z–A</string>
<string name="apps_sort_installed">Instaladas recientemente</string>
<string name="apps_sort_updated">Actualizadas recientemente</string>
<plurals name="apps_count">
<item quantity="one">%d aplicación</item>
<item quantity="many">%d aplicaciones</item>
<item quantity="other">%d aplicaciones</item>
</plurals>
</resources>
+42
View File
@@ -3085,4 +3085,46 @@
<string name="itch_collections">Kokoelmat</string>
<string name="xbox_filter_cloud">Pilvi</string>
<string name="stores_xbox_cloud_settings">Pilviasetukset</string>
<string name="apps_search_hint">Hae sovelluksia</string>
<string name="apps_no_match">Mikään sovellus ei vastaa hakua ”%1$s”.</string>
<string name="apps_add_widget">Lisää widget</string>
<string name="apps_widgets_loading">Ladataan widgettejä…</string>
<string name="apps_widgets_none">Mikään tämän laitteen sovellus ei tarjoa widgettejä.</string>
<string name="apps_widget_wider">Leveämpi</string>
<string name="apps_widget_narrower">Kapeampi</string>
<string name="apps_widget_taller">Korkeampi</string>
<string name="apps_widget_shorter">Matalampi</string>
<string name="apps_widget_move_earlier">Siirrä aiemmaksi</string>
<string name="apps_widget_move_later">Siirrä myöhemmäksi</string>
<string name="apps_widget_configure">Widgetin asetukset</string>
<string name="apps_widget_remove">Poista widget</string>
<string name="apps_add_to_folder">Lisää kansioon</string>
<string name="apps_new_folder">Uusi kansio</string>
<string name="apps_remove_from_folder">Poista kansiosta</string>
<string name="apps_app_info">Sovelluksen tiedot</string>
<string name="apps_uninstall">Poista asennus</string>
<string name="apps_folder_rename">Nimeä uudelleen</string>
<string name="apps_folder_ungroup">Pura kansio</string>
<string name="apps_folder_default">Kansio</string>
<string name="apps_folder_name">Kansion nimi</string>
<string name="apps_filter">Näytä</string>
<string name="apps_filter_all">Kaikki sovellukset</string>
<string name="apps_filter_games">Pelit</string>
<string name="apps_filter_downloaded">Ladatut</string>
<string name="apps_filter_system">Järjestelmä</string>
<string name="apps_filter_hidden">Piilotetut</string>
<string name="apps_hide">Piilota</string>
<string name="apps_unhide">Näytä sovelluksissa</string>
<string name="apps_shortcut_remove">Poista pikakuvake</string>
<string name="apps_pin_title">Lisää sovelluksiin</string>
<string name="apps_pin_add">Lisää</string>
<string name="apps_hidden_none">Ei piilotettuja sovelluksia.</string>
<string name="apps_sort_name_asc">Nimi A–Ö</string>
<string name="apps_sort_name_desc">Nimi Ö–A</string>
<string name="apps_sort_installed">Äskettäin asennetut</string>
<string name="apps_sort_updated">Äskettäin päivitetyt</string>
<plurals name="apps_count">
<item quantity="one">%d sovellus</item>
<item quantity="other">%d sovellusta</item>
</plurals>
</resources>
+43
View File
@@ -3121,4 +3121,47 @@
<string name="itch_collections">Collections</string>
<string name="xbox_filter_cloud">Cloud</string>
<string name="stores_xbox_cloud_settings">Paramètres cloud</string>
<string name="apps_search_hint">Rechercher des applications</string>
<string name="apps_no_match">Aucune application ne correspond à « %1$s ».</string>
<string name="apps_add_widget">Ajouter un widget</string>
<string name="apps_widgets_loading">Chargement des widgets…</string>
<string name="apps_widgets_none">Aucune application de cet appareil ne propose de widgets.</string>
<string name="apps_widget_wider">Plus large</string>
<string name="apps_widget_narrower">Plus étroit</string>
<string name="apps_widget_taller">Plus haut</string>
<string name="apps_widget_shorter">Moins haut</string>
<string name="apps_widget_move_earlier">Avancer</string>
<string name="apps_widget_move_later">Reculer</string>
<string name="apps_widget_configure">Paramètres du widget</string>
<string name="apps_widget_remove">Retirer le widget</string>
<string name="apps_add_to_folder">Ajouter à un dossier</string>
<string name="apps_new_folder">Nouveau dossier</string>
<string name="apps_remove_from_folder">Retirer du dossier</string>
<string name="apps_app_info">Infos sur l’application</string>
<string name="apps_uninstall">Désinstaller</string>
<string name="apps_folder_rename">Renommer</string>
<string name="apps_folder_ungroup">Dégrouper</string>
<string name="apps_folder_default">Dossier</string>
<string name="apps_folder_name">Nom du dossier</string>
<string name="apps_filter">Afficher</string>
<string name="apps_filter_all">Toutes les applications</string>
<string name="apps_filter_games">Jeux</string>
<string name="apps_filter_downloaded">Téléchargées</string>
<string name="apps_filter_system">Système</string>
<string name="apps_filter_hidden">Masquées</string>
<string name="apps_hide">Masquer</string>
<string name="apps_unhide">Afficher dans les applications</string>
<string name="apps_shortcut_remove">Retirer le raccourci</string>
<string name="apps_pin_title">Ajouter aux applications</string>
<string name="apps_pin_add">Ajouter</string>
<string name="apps_hidden_none">Aucune application masquée.</string>
<string name="apps_sort_name_asc">Nom A–Z</string>
<string name="apps_sort_name_desc">Nom Z–A</string>
<string name="apps_sort_installed">Installées récemment</string>
<string name="apps_sort_updated">Mises à jour récemment</string>
<plurals name="apps_count">
<item quantity="one">%d application</item>
<item quantity="many">%d applications</item>
<item quantity="other">%d applications</item>
</plurals>
</resources>
+42
View File
@@ -3085,4 +3085,46 @@
<string name="itch_collections">संग्रह</string>
<string name="xbox_filter_cloud">क्लाउड</string>
<string name="stores_xbox_cloud_settings">क्लाउड सेटिंग्स</string>
<string name="apps_search_hint">ऐप खोजें</string>
<string name="apps_no_match">“%1$s” से कोई ऐप मेल नहीं खाता।</string>
<string name="apps_add_widget">विजेट जोड़ें</string>
<string name="apps_widgets_loading">विजेट लोड हो रहे हैं…</string>
<string name="apps_widgets_none">इस डिवाइस पर कोई ऐप विजेट नहीं देता।</string>
<string name="apps_widget_wider">चौड़ा</string>
<string name="apps_widget_narrower">पतला</string>
<string name="apps_widget_taller">लंबा</string>
<string name="apps_widget_shorter">छोटा</string>
<string name="apps_widget_move_earlier">पहले ले जाएँ</string>
<string name="apps_widget_move_later">बाद में ले जाएँ</string>
<string name="apps_widget_configure">विजेट सेटिंग</string>
<string name="apps_widget_remove">विजेट हटाएँ</string>
<string name="apps_add_to_folder">फ़ोल्डर में जोड़ें</string>
<string name="apps_new_folder">नया फ़ोल्डर</string>
<string name="apps_remove_from_folder">फ़ोल्डर से हटाएँ</string>
<string name="apps_app_info">ऐप की जानकारी</string>
<string name="apps_uninstall">अनइंस्टॉल करें</string>
<string name="apps_folder_rename">नाम बदलें</string>
<string name="apps_folder_ungroup">समूह तोड़ें</string>
<string name="apps_folder_default">फ़ोल्डर</string>
<string name="apps_folder_name">फ़ोल्डर का नाम</string>
<string name="apps_filter">दिखाएँ</string>
<string name="apps_filter_all">सभी ऐप</string>
<string name="apps_filter_games">गेम</string>
<string name="apps_filter_downloaded">डाउनलोड किए गए</string>
<string name="apps_filter_system">सिस्टम</string>
<string name="apps_filter_hidden">छिपे हुए</string>
<string name="apps_hide">छिपाएँ</string>
<string name="apps_unhide">ऐप में दिखाएँ</string>
<string name="apps_shortcut_remove">शॉर्टकट हटाएँ</string>
<string name="apps_pin_title">ऐप में जोड़ें</string>
<string name="apps_pin_add">जोड़ें</string>
<string name="apps_hidden_none">कोई छिपा हुआ ऐप नहीं।</string>
<string name="apps_sort_name_asc">नाम A–Z</string>
<string name="apps_sort_name_desc">नाम Z–A</string>
<string name="apps_sort_installed">हाल ही में इंस्टॉल किए गए</string>
<string name="apps_sort_updated">हाल ही में अपडेट किए गए</string>
<plurals name="apps_count">
<item quantity="one">%d ऐप</item>
<item quantity="other">%d ऐप</item>
</plurals>
</resources>
+42
View File
@@ -3085,4 +3085,46 @@
<string name="itch_collections">Gyűjtemények</string>
<string name="xbox_filter_cloud">Felhő</string>
<string name="stores_xbox_cloud_settings">Felhőbeállítások</string>
<string name="apps_search_hint">Alkalmazások keresése</string>
<string name="apps_no_match">Egy alkalmazás sem felel meg ennek: „%1$s”.</string>
<string name="apps_add_widget">Modul hozzáadása</string>
<string name="apps_widgets_loading">Modulok betöltése…</string>
<string name="apps_widgets_none">Az eszközön egyik alkalmazás sem kínál modult.</string>
<string name="apps_widget_wider">Szélesebb</string>
<string name="apps_widget_narrower">Keskenyebb</string>
<string name="apps_widget_taller">Magasabb</string>
<string name="apps_widget_shorter">Alacsonyabb</string>
<string name="apps_widget_move_earlier">Előrébb</string>
<string name="apps_widget_move_later">Hátrébb</string>
<string name="apps_widget_configure">Modulbeállítások</string>
<string name="apps_widget_remove">Modul eltávolítása</string>
<string name="apps_add_to_folder">Hozzáadás mappához</string>
<string name="apps_new_folder">Új mappa</string>
<string name="apps_remove_from_folder">Eltávolítás a mappából</string>
<string name="apps_app_info">Alkalmazásinformáció</string>
<string name="apps_uninstall">Eltávolítás</string>
<string name="apps_folder_rename">Átnevezés</string>
<string name="apps_folder_ungroup">Szétbontás</string>
<string name="apps_folder_default">Mappa</string>
<string name="apps_folder_name">Mappa neve</string>
<string name="apps_filter">Megjelenítés</string>
<string name="apps_filter_all">Összes alkalmazás</string>
<string name="apps_filter_games">Játékok</string>
<string name="apps_filter_downloaded">Letöltött</string>
<string name="apps_filter_system">Rendszer</string>
<string name="apps_filter_hidden">Rejtett</string>
<string name="apps_hide">Elrejtés</string>
<string name="apps_unhide">Megjelenítés az alkalmazások között</string>
<string name="apps_shortcut_remove">Parancsikon eltávolítása</string>
<string name="apps_pin_title">Hozzáadás az alkalmazásokhoz</string>
<string name="apps_pin_add">Hozzáadás</string>
<string name="apps_hidden_none">Nincsenek rejtett alkalmazások.</string>
<string name="apps_sort_name_asc">Név A–Z</string>
<string name="apps_sort_name_desc">Név Z–A</string>
<string name="apps_sort_installed">Nemrég telepített</string>
<string name="apps_sort_updated">Nemrég frissített</string>
<plurals name="apps_count">
<item quantity="one">%d alkalmazás</item>
<item quantity="other">%d alkalmazás</item>
</plurals>
</resources>
+41
View File
@@ -3075,4 +3075,45 @@
<string name="itch_collections">Koleksi</string>
<string name="xbox_filter_cloud">Cloud</string>
<string name="stores_xbox_cloud_settings">Pengaturan cloud</string>
<string name="apps_search_hint">Cari aplikasi</string>
<string name="apps_no_match">Tidak ada aplikasi yang cocok dengan “%1$s”.</string>
<string name="apps_add_widget">Tambahkan widget</string>
<string name="apps_widgets_loading">Memuat widget…</string>
<string name="apps_widgets_none">Tidak ada aplikasi di perangkat ini yang menyediakan widget.</string>
<string name="apps_widget_wider">Lebih lebar</string>
<string name="apps_widget_narrower">Lebih sempit</string>
<string name="apps_widget_taller">Lebih tinggi</string>
<string name="apps_widget_shorter">Lebih pendek</string>
<string name="apps_widget_move_earlier">Pindahkan ke depan</string>
<string name="apps_widget_move_later">Pindahkan ke belakang</string>
<string name="apps_widget_configure">Setelan widget</string>
<string name="apps_widget_remove">Hapus widget</string>
<string name="apps_add_to_folder">Tambahkan ke folder</string>
<string name="apps_new_folder">Folder baru</string>
<string name="apps_remove_from_folder">Keluarkan dari folder</string>
<string name="apps_app_info">Info aplikasi</string>
<string name="apps_uninstall">Uninstal</string>
<string name="apps_folder_rename">Ganti nama</string>
<string name="apps_folder_ungroup">Pisahkan</string>
<string name="apps_folder_default">Folder</string>
<string name="apps_folder_name">Nama folder</string>
<string name="apps_filter">Tampilkan</string>
<string name="apps_filter_all">Semua aplikasi</string>
<string name="apps_filter_games">Game</string>
<string name="apps_filter_downloaded">Didownload</string>
<string name="apps_filter_system">Sistem</string>
<string name="apps_filter_hidden">Tersembunyi</string>
<string name="apps_hide">Sembunyikan</string>
<string name="apps_unhide">Tampilkan di aplikasi</string>
<string name="apps_shortcut_remove">Hapus pintasan</string>
<string name="apps_pin_title">Tambahkan ke Aplikasi</string>
<string name="apps_pin_add">Tambahkan</string>
<string name="apps_hidden_none">Tidak ada aplikasi tersembunyi.</string>
<string name="apps_sort_name_asc">Nama A–Z</string>
<string name="apps_sort_name_desc">Nama Z–A</string>
<string name="apps_sort_installed">Baru diinstal</string>
<string name="apps_sort_updated">Baru diperbarui</string>
<plurals name="apps_count">
<item quantity="other">%d aplikasi</item>
</plurals>
</resources>
+43
View File
@@ -3119,4 +3119,47 @@
<string name="itch_collections">Raccolte</string>
<string name="xbox_filter_cloud">Cloud</string>
<string name="stores_xbox_cloud_settings">Impostazioni cloud</string>
<string name="apps_search_hint">Cerca app</string>
<string name="apps_no_match">Nessuna app corrisponde a «%1$s».</string>
<string name="apps_add_widget">Aggiungi widget</string>
<string name="apps_widgets_loading">Caricamento widget…</string>
<string name="apps_widgets_none">Nessuna app su questo dispositivo offre widget.</string>
<string name="apps_widget_wider">Più largo</string>
<string name="apps_widget_narrower">Più stretto</string>
<string name="apps_widget_taller">Più alto</string>
<string name="apps_widget_shorter">Più basso</string>
<string name="apps_widget_move_earlier">Sposta prima</string>
<string name="apps_widget_move_later">Sposta dopo</string>
<string name="apps_widget_configure">Impostazioni widget</string>
<string name="apps_widget_remove">Rimuovi widget</string>
<string name="apps_add_to_folder">Aggiungi a cartella</string>
<string name="apps_new_folder">Nuova cartella</string>
<string name="apps_remove_from_folder">Rimuovi dalla cartella</string>
<string name="apps_app_info">Info app</string>
<string name="apps_uninstall">Disinstalla</string>
<string name="apps_folder_rename">Rinomina</string>
<string name="apps_folder_ungroup">Separa</string>
<string name="apps_folder_default">Cartella</string>
<string name="apps_folder_name">Nome cartella</string>
<string name="apps_filter">Mostra</string>
<string name="apps_filter_all">Tutte le app</string>
<string name="apps_filter_games">Giochi</string>
<string name="apps_filter_downloaded">Scaricate</string>
<string name="apps_filter_system">Sistema</string>
<string name="apps_filter_hidden">Nascoste</string>
<string name="apps_hide">Nascondi</string>
<string name="apps_unhide">Mostra nelle app</string>
<string name="apps_shortcut_remove">Rimuovi scorciatoia</string>
<string name="apps_pin_title">Aggiungi ad App</string>
<string name="apps_pin_add">Aggiungi</string>
<string name="apps_hidden_none">Nessuna app nascosta.</string>
<string name="apps_sort_name_asc">Nome A–Z</string>
<string name="apps_sort_name_desc">Nome Z–A</string>
<string name="apps_sort_installed">Installate di recente</string>
<string name="apps_sort_updated">Aggiornate di recente</string>
<plurals name="apps_count">
<item quantity="one">%d app</item>
<item quantity="many">%d app</item>
<item quantity="other">%d app</item>
</plurals>
</resources>
+41
View File
@@ -3062,4 +3062,45 @@
<string name="itch_collections">コレクション</string>
<string name="xbox_filter_cloud">クラウド</string>
<string name="stores_xbox_cloud_settings">クラウド設定</string>
<string name="apps_search_hint">アプリを検索</string>
<string name="apps_no_match">「%1$s」に一致するアプリはありません。</string>
<string name="apps_add_widget">ウィジェットを追加</string>
<string name="apps_widgets_loading">ウィジェットを読み込み中…</string>
<string name="apps_widgets_none">このデバイスにはウィジェットを提供するアプリがありません。</string>
<string name="apps_widget_wider">幅を広く</string>
<string name="apps_widget_narrower">幅を狭く</string>
<string name="apps_widget_taller">高く</string>
<string name="apps_widget_shorter">低く</string>
<string name="apps_widget_move_earlier">前へ移動</string>
<string name="apps_widget_move_later">後ろへ移動</string>
<string name="apps_widget_configure">ウィジェットの設定</string>
<string name="apps_widget_remove">ウィジェットを削除</string>
<string name="apps_add_to_folder">フォルダに追加</string>
<string name="apps_new_folder">新しいフォルダ</string>
<string name="apps_remove_from_folder">フォルダから削除</string>
<string name="apps_app_info">アプリ情報</string>
<string name="apps_uninstall">アンインストール</string>
<string name="apps_folder_rename">名前を変更</string>
<string name="apps_folder_ungroup">グループを解除</string>
<string name="apps_folder_default">フォルダ</string>
<string name="apps_folder_name">フォルダ名</string>
<string name="apps_filter">表示</string>
<string name="apps_filter_all">すべてのアプリ</string>
<string name="apps_filter_games">ゲーム</string>
<string name="apps_filter_downloaded">ダウンロード済み</string>
<string name="apps_filter_system">システム</string>
<string name="apps_filter_hidden">非表示</string>
<string name="apps_hide">非表示にする</string>
<string name="apps_unhide">アプリに表示</string>
<string name="apps_shortcut_remove">ショートカットを削除</string>
<string name="apps_pin_title">アプリに追加</string>
<string name="apps_pin_add">追加</string>
<string name="apps_hidden_none">非表示のアプリはありません。</string>
<string name="apps_sort_name_asc">名前 A–Z</string>
<string name="apps_sort_name_desc">名前 Z–A</string>
<string name="apps_sort_installed">最近インストール</string>
<string name="apps_sort_updated">最近更新</string>
<plurals name="apps_count">
<item quantity="other">%d 個のアプリ</item>
</plurals>
</resources>
+41
View File
@@ -3062,4 +3062,45 @@
<string name="itch_collections">컬렉션</string>
<string name="xbox_filter_cloud">클라우드</string>
<string name="stores_xbox_cloud_settings">클라우드 설정</string>
<string name="apps_search_hint">앱 검색</string>
<string name="apps_no_match">‘%1$s’와(과) 일치하는 앱이 없습니다.</string>
<string name="apps_add_widget">위젯 추가</string>
<string name="apps_widgets_loading">위젯 불러오는 중…</string>
<string name="apps_widgets_none">이 기기에 위젯을 제공하는 앱이 없습니다.</string>
<string name="apps_widget_wider">더 넓게</string>
<string name="apps_widget_narrower">더 좁게</string>
<string name="apps_widget_taller">더 높게</string>
<string name="apps_widget_shorter">더 낮게</string>
<string name="apps_widget_move_earlier">앞으로 이동</string>
<string name="apps_widget_move_later">뒤로 이동</string>
<string name="apps_widget_configure">위젯 설정</string>
<string name="apps_widget_remove">위젯 삭제</string>
<string name="apps_add_to_folder">폴더에 추가</string>
<string name="apps_new_folder">새 폴더</string>
<string name="apps_remove_from_folder">폴더에서 제거</string>
<string name="apps_app_info">앱 정보</string>
<string name="apps_uninstall">제거</string>
<string name="apps_folder_rename">이름 변경</string>
<string name="apps_folder_ungroup">그룹 해제</string>
<string name="apps_folder_default">폴더</string>
<string name="apps_folder_name">폴더 이름</string>
<string name="apps_filter">표시</string>
<string name="apps_filter_all">모든 앱</string>
<string name="apps_filter_games">게임</string>
<string name="apps_filter_downloaded">다운로드됨</string>
<string name="apps_filter_system">시스템</string>
<string name="apps_filter_hidden">숨김</string>
<string name="apps_hide">숨기기</string>
<string name="apps_unhide">앱에 표시</string>
<string name="apps_shortcut_remove">바로가기 삭제</string>
<string name="apps_pin_title">앱에 추가</string>
<string name="apps_pin_add">추가</string>
<string name="apps_hidden_none">숨긴 앱이 없습니다.</string>
<string name="apps_sort_name_asc">이름 A–Z</string>
<string name="apps_sort_name_desc">이름 Z–A</string>
<string name="apps_sort_installed">최근 설치</string>
<string name="apps_sort_updated">최근 업데이트</string>
<plurals name="apps_count">
<item quantity="other">앱 %d개</item>
</plurals>
</resources>
+41
View File
@@ -3060,4 +3060,45 @@
<string name="itch_collections">Koleksi</string>
<string name="xbox_filter_cloud">Awan</string>
<string name="stores_xbox_cloud_settings">Tetapan awan</string>
<string name="apps_search_hint">Cari apl</string>
<string name="apps_no_match">Tiada apl sepadan dengan “%1$s”.</string>
<string name="apps_add_widget">Tambah widget</string>
<string name="apps_widgets_loading">Memuatkan widget…</string>
<string name="apps_widgets_none">Tiada apl pada peranti ini yang menawarkan widget.</string>
<string name="apps_widget_wider">Lebih lebar</string>
<string name="apps_widget_narrower">Lebih sempit</string>
<string name="apps_widget_taller">Lebih tinggi</string>
<string name="apps_widget_shorter">Lebih rendah</string>
<string name="apps_widget_move_earlier">Alih ke depan</string>
<string name="apps_widget_move_later">Alih ke belakang</string>
<string name="apps_widget_configure">Tetapan widget</string>
<string name="apps_widget_remove">Alih keluar widget</string>
<string name="apps_add_to_folder">Tambah pada folder</string>
<string name="apps_new_folder">Folder baharu</string>
<string name="apps_remove_from_folder">Alih keluar daripada folder</string>
<string name="apps_app_info">Maklumat apl</string>
<string name="apps_uninstall">Nyahpasang</string>
<string name="apps_folder_rename">Namakan semula</string>
<string name="apps_folder_ungroup">Leraikan</string>
<string name="apps_folder_default">Folder</string>
<string name="apps_folder_name">Nama folder</string>
<string name="apps_filter">Tunjukkan</string>
<string name="apps_filter_all">Semua apl</string>
<string name="apps_filter_games">Permainan</string>
<string name="apps_filter_downloaded">Dimuat turun</string>
<string name="apps_filter_system">Sistem</string>
<string name="apps_filter_hidden">Tersembunyi</string>
<string name="apps_hide">Sembunyikan</string>
<string name="apps_unhide">Tunjukkan dalam apl</string>
<string name="apps_shortcut_remove">Alih keluar pintasan</string>
<string name="apps_pin_title">Tambah pada Apl</string>
<string name="apps_pin_add">Tambah</string>
<string name="apps_hidden_none">Tiada apl tersembunyi.</string>
<string name="apps_sort_name_asc">Nama A–Z</string>
<string name="apps_sort_name_desc">Nama Z–A</string>
<string name="apps_sort_installed">Dipasang baru-baru ini</string>
<string name="apps_sort_updated">Dikemas kini baru-baru ini</string>
<plurals name="apps_count">
<item quantity="other">%d apl</item>
</plurals>
</resources>
+42
View File
@@ -3085,4 +3085,46 @@
<string name="itch_collections">Samlinger</string>
<string name="xbox_filter_cloud">Sky</string>
<string name="stores_xbox_cloud_settings">Skyinnstillinger</string>
<string name="apps_search_hint">Søk i apper</string>
<string name="apps_no_match">Ingen apper samsvarer med «%1$s».</string>
<string name="apps_add_widget">Legg til modul</string>
<string name="apps_widgets_loading">Laster inn moduler…</string>
<string name="apps_widgets_none">Ingen apper på enheten tilbyr moduler.</string>
<string name="apps_widget_wider">Bredere</string>
<string name="apps_widget_narrower">Smalere</string>
<string name="apps_widget_taller">Høyere</string>
<string name="apps_widget_shorter">Lavere</string>
<string name="apps_widget_move_earlier">Flytt tidligere</string>
<string name="apps_widget_move_later">Flytt senere</string>
<string name="apps_widget_configure">Modulinnstillinger</string>
<string name="apps_widget_remove">Fjern modul</string>
<string name="apps_add_to_folder">Legg til i mappe</string>
<string name="apps_new_folder">Ny mappe</string>
<string name="apps_remove_from_folder">Fjern fra mappe</string>
<string name="apps_app_info">Appinfo</string>
<string name="apps_uninstall">Avinstaller</string>
<string name="apps_folder_rename">Gi nytt navn</string>
<string name="apps_folder_ungroup">Løs opp</string>
<string name="apps_folder_default">Mappe</string>
<string name="apps_folder_name">Mappenavn</string>
<string name="apps_filter">Vis</string>
<string name="apps_filter_all">Alle apper</string>
<string name="apps_filter_games">Spill</string>
<string name="apps_filter_downloaded">Nedlastet</string>
<string name="apps_filter_system">System</string>
<string name="apps_filter_hidden">Skjult</string>
<string name="apps_hide">Skjul</string>
<string name="apps_unhide">Vis i apper</string>
<string name="apps_shortcut_remove">Fjern snarvei</string>
<string name="apps_pin_title">Legg til i Apper</string>
<string name="apps_pin_add">Legg til</string>
<string name="apps_hidden_none">Ingen skjulte apper.</string>
<string name="apps_sort_name_asc">Navn A–Å</string>
<string name="apps_sort_name_desc">Navn Å–A</string>
<string name="apps_sort_installed">Nylig installert</string>
<string name="apps_sort_updated">Nylig oppdatert</string>
<plurals name="apps_count">
<item quantity="one">%d app</item>
<item quantity="other">%d apper</item>
</plurals>
</resources>
+42
View File
@@ -3085,4 +3085,46 @@
<string name="itch_collections">Collecties</string>
<string name="xbox_filter_cloud">Cloud</string>
<string name="stores_xbox_cloud_settings">Cloudinstellingen</string>
<string name="apps_search_hint">Apps zoeken</string>
<string name="apps_no_match">Geen apps gevonden voor ‘%1$s’.</string>
<string name="apps_add_widget">Widget toevoegen</string>
<string name="apps_widgets_loading">Widgets laden…</string>
<string name="apps_widgets_none">Geen enkele app op dit apparaat biedt widgets.</string>
<string name="apps_widget_wider">Breder</string>
<string name="apps_widget_narrower">Smaller</string>
<string name="apps_widget_taller">Hoger</string>
<string name="apps_widget_shorter">Lager</string>
<string name="apps_widget_move_earlier">Naar voren</string>
<string name="apps_widget_move_later">Naar achteren</string>
<string name="apps_widget_configure">Widgetinstellingen</string>
<string name="apps_widget_remove">Widget verwijderen</string>
<string name="apps_add_to_folder">Toevoegen aan map</string>
<string name="apps_new_folder">Nieuwe map</string>
<string name="apps_remove_from_folder">Uit map verwijderen</string>
<string name="apps_app_info">App-info</string>
<string name="apps_uninstall">Verwijderen</string>
<string name="apps_folder_rename">Naam wijzigen</string>
<string name="apps_folder_ungroup">Opheffen</string>
<string name="apps_folder_default">Map</string>
<string name="apps_folder_name">Mapnaam</string>
<string name="apps_filter">Tonen</string>
<string name="apps_filter_all">Alle apps</string>
<string name="apps_filter_games">Games</string>
<string name="apps_filter_downloaded">Gedownload</string>
<string name="apps_filter_system">Systeem</string>
<string name="apps_filter_hidden">Verborgen</string>
<string name="apps_hide">Verbergen</string>
<string name="apps_unhide">Tonen in apps</string>
<string name="apps_shortcut_remove">Snelkoppeling verwijderen</string>
<string name="apps_pin_title">Toevoegen aan Apps</string>
<string name="apps_pin_add">Toevoegen</string>
<string name="apps_hidden_none">Geen verborgen apps.</string>
<string name="apps_sort_name_asc">Naam A–Z</string>
<string name="apps_sort_name_desc">Naam Z–A</string>
<string name="apps_sort_installed">Recent geïnstalleerd</string>
<string name="apps_sort_updated">Recent bijgewerkt</string>
<plurals name="apps_count">
<item quantity="one">%d app</item>
<item quantity="other">%d apps</item>
</plurals>
</resources>
+44
View File
@@ -3156,4 +3156,48 @@
<string name="itch_collections">Kolekcje</string>
<string name="xbox_filter_cloud">Chmura</string>
<string name="stores_xbox_cloud_settings">Ustawienia chmury</string>
<string name="apps_search_hint">Szukaj aplikacji</string>
<string name="apps_no_match">Żadna aplikacja nie pasuje do „%1$s”.</string>
<string name="apps_add_widget">Dodaj widżet</string>
<string name="apps_widgets_loading">Wczytywanie widżetów…</string>
<string name="apps_widgets_none">Żadna aplikacja na tym urządzeniu nie oferuje widżetów.</string>
<string name="apps_widget_wider">Szerszy</string>
<string name="apps_widget_narrower">Węższy</string>
<string name="apps_widget_taller">Wyższy</string>
<string name="apps_widget_shorter">Niższy</string>
<string name="apps_widget_move_earlier">Przesuń wcześniej</string>
<string name="apps_widget_move_later">Przesuń później</string>
<string name="apps_widget_configure">Ustawienia widżetu</string>
<string name="apps_widget_remove">Usuń widżet</string>
<string name="apps_add_to_folder">Dodaj do folderu</string>
<string name="apps_new_folder">Nowy folder</string>
<string name="apps_remove_from_folder">Usuń z folderu</string>
<string name="apps_app_info">Informacje o aplikacji</string>
<string name="apps_uninstall">Odinstaluj</string>
<string name="apps_folder_rename">Zmień nazwę</string>
<string name="apps_folder_ungroup">Rozgrupuj</string>
<string name="apps_folder_default">Folder</string>
<string name="apps_folder_name">Nazwa folderu</string>
<string name="apps_filter">Pokaż</string>
<string name="apps_filter_all">Wszystkie aplikacje</string>
<string name="apps_filter_games">Gry</string>
<string name="apps_filter_downloaded">Pobrane</string>
<string name="apps_filter_system">Systemowe</string>
<string name="apps_filter_hidden">Ukryte</string>
<string name="apps_hide">Ukryj</string>
<string name="apps_unhide">Pokaż w aplikacjach</string>
<string name="apps_shortcut_remove">Usuń skrót</string>
<string name="apps_pin_title">Dodaj do aplikacji</string>
<string name="apps_pin_add">Dodaj</string>
<string name="apps_hidden_none">Brak ukrytych aplikacji.</string>
<string name="apps_sort_name_asc">Nazwa A–Z</string>
<string name="apps_sort_name_desc">Nazwa Z–A</string>
<string name="apps_sort_installed">Ostatnio zainstalowane</string>
<string name="apps_sort_updated">Ostatnio zaktualizowane</string>
<plurals name="apps_count">
<item quantity="one">%d aplikacja</item>
<item quantity="few">%d aplikacje</item>
<item quantity="many">%d aplikacji</item>
<item quantity="other">%d aplikacji</item>
</plurals>
</resources>
@@ -3119,4 +3119,47 @@
<string name="itch_collections">Coleções</string>
<string name="xbox_filter_cloud">Nuvem</string>
<string name="stores_xbox_cloud_settings">Definições da nuvem</string>
<string name="apps_search_hint">Pesquisar aplicações</string>
<string name="apps_no_match">Nenhuma aplicação corresponde a “%1$s”.</string>
<string name="apps_add_widget">Adicionar widget</string>
<string name="apps_widgets_loading">A carregar widgets…</string>
<string name="apps_widgets_none">Nenhuma aplicação neste dispositivo oferece widgets.</string>
<string name="apps_widget_wider">Mais largo</string>
<string name="apps_widget_narrower">Mais estreito</string>
<string name="apps_widget_taller">Mais alto</string>
<string name="apps_widget_shorter">Mais baixo</string>
<string name="apps_widget_move_earlier">Mover para antes</string>
<string name="apps_widget_move_later">Mover para depois</string>
<string name="apps_widget_configure">Definições do widget</string>
<string name="apps_widget_remove">Remover widget</string>
<string name="apps_add_to_folder">Adicionar a pasta</string>
<string name="apps_new_folder">Nova pasta</string>
<string name="apps_remove_from_folder">Remover da pasta</string>
<string name="apps_app_info">Informações da aplicação</string>
<string name="apps_uninstall">Desinstalar</string>
<string name="apps_folder_rename">Mudar o nome</string>
<string name="apps_folder_ungroup">Desagrupar</string>
<string name="apps_folder_default">Pasta</string>
<string name="apps_folder_name">Nome da pasta</string>
<string name="apps_filter">Mostrar</string>
<string name="apps_filter_all">Todas as aplicações</string>
<string name="apps_filter_games">Jogos</string>
<string name="apps_filter_downloaded">Transferidas</string>
<string name="apps_filter_system">Sistema</string>
<string name="apps_filter_hidden">Ocultas</string>
<string name="apps_hide">Ocultar</string>
<string name="apps_unhide">Mostrar nas aplicações</string>
<string name="apps_shortcut_remove">Remover atalho</string>
<string name="apps_pin_title">Adicionar às Aplicações</string>
<string name="apps_pin_add">Adicionar</string>
<string name="apps_hidden_none">Nenhuma aplicação oculta.</string>
<string name="apps_sort_name_asc">Nome A–Z</string>
<string name="apps_sort_name_desc">Nome Z–A</string>
<string name="apps_sort_installed">Instaladas recentemente</string>
<string name="apps_sort_updated">Atualizadas recentemente</string>
<plurals name="apps_count">
<item quantity="one">%d aplicação</item>
<item quantity="many">%d aplicações</item>
<item quantity="other">%d aplicações</item>
</plurals>
</resources>
+43
View File
@@ -3119,4 +3119,47 @@
<string name="itch_collections">Coleções</string>
<string name="xbox_filter_cloud">Nuvem</string>
<string name="stores_xbox_cloud_settings">Configurações da nuvem</string>
<string name="apps_search_hint">Pesquisar apps</string>
<string name="apps_no_match">Nenhum app corresponde a “%1$s”.</string>
<string name="apps_add_widget">Adicionar widget</string>
<string name="apps_widgets_loading">Carregando widgets…</string>
<string name="apps_widgets_none">Nenhum app neste dispositivo oferece widgets.</string>
<string name="apps_widget_wider">Mais largo</string>
<string name="apps_widget_narrower">Mais estreito</string>
<string name="apps_widget_taller">Mais alto</string>
<string name="apps_widget_shorter">Mais baixo</string>
<string name="apps_widget_move_earlier">Mover para antes</string>
<string name="apps_widget_move_later">Mover para depois</string>
<string name="apps_widget_configure">Configurações do widget</string>
<string name="apps_widget_remove">Remover widget</string>
<string name="apps_add_to_folder">Adicionar à pasta</string>
<string name="apps_new_folder">Nova pasta</string>
<string name="apps_remove_from_folder">Remover da pasta</string>
<string name="apps_app_info">Informações do app</string>
<string name="apps_uninstall">Desinstalar</string>
<string name="apps_folder_rename">Renomear</string>
<string name="apps_folder_ungroup">Desagrupar</string>
<string name="apps_folder_default">Pasta</string>
<string name="apps_folder_name">Nome da pasta</string>
<string name="apps_filter">Mostrar</string>
<string name="apps_filter_all">Todos os apps</string>
<string name="apps_filter_games">Jogos</string>
<string name="apps_filter_downloaded">Baixados</string>
<string name="apps_filter_system">Sistema</string>
<string name="apps_filter_hidden">Ocultos</string>
<string name="apps_hide">Ocultar</string>
<string name="apps_unhide">Mostrar nos apps</string>
<string name="apps_shortcut_remove">Remover atalho</string>
<string name="apps_pin_title">Adicionar aos Apps</string>
<string name="apps_pin_add">Adicionar</string>
<string name="apps_hidden_none">Nenhum app oculto.</string>
<string name="apps_sort_name_asc">Nome A–Z</string>
<string name="apps_sort_name_desc">Nome Z–A</string>
<string name="apps_sort_installed">Instalados recentemente</string>
<string name="apps_sort_updated">Atualizados recentemente</string>
<plurals name="apps_count">
<item quantity="one">%d app</item>
<item quantity="many">%d apps</item>
<item quantity="other">%d apps</item>
</plurals>
</resources>
+43
View File
@@ -3119,4 +3119,47 @@
<string name="itch_collections">Colecții</string>
<string name="xbox_filter_cloud">Cloud</string>
<string name="stores_xbox_cloud_settings">Setări cloud</string>
<string name="apps_search_hint">Caută aplicații</string>
<string name="apps_no_match">Nicio aplicație nu se potrivește cu „%1$s”.</string>
<string name="apps_add_widget">Adaugă widget</string>
<string name="apps_widgets_loading">Se încarcă widgeturile…</string>
<string name="apps_widgets_none">Nicio aplicație de pe acest dispozitiv nu oferă widgeturi.</string>
<string name="apps_widget_wider">Mai lat</string>
<string name="apps_widget_narrower">Mai îngust</string>
<string name="apps_widget_taller">Mai înalt</string>
<string name="apps_widget_shorter">Mai scund</string>
<string name="apps_widget_move_earlier">Mută mai devreme</string>
<string name="apps_widget_move_later">Mută mai târziu</string>
<string name="apps_widget_configure">Setări widget</string>
<string name="apps_widget_remove">Elimină widgetul</string>
<string name="apps_add_to_folder">Adaugă în dosar</string>
<string name="apps_new_folder">Dosar nou</string>
<string name="apps_remove_from_folder">Elimină din dosar</string>
<string name="apps_app_info">Informații despre aplicație</string>
<string name="apps_uninstall">Dezinstalează</string>
<string name="apps_folder_rename">Redenumește</string>
<string name="apps_folder_ungroup">Degrupează</string>
<string name="apps_folder_default">Dosar</string>
<string name="apps_folder_name">Numele dosarului</string>
<string name="apps_filter">Afișează</string>
<string name="apps_filter_all">Toate aplicațiile</string>
<string name="apps_filter_games">Jocuri</string>
<string name="apps_filter_downloaded">Descărcate</string>
<string name="apps_filter_system">Sistem</string>
<string name="apps_filter_hidden">Ascunse</string>
<string name="apps_hide">Ascunde</string>
<string name="apps_unhide">Afișează în aplicații</string>
<string name="apps_shortcut_remove">Elimină comanda rapidă</string>
<string name="apps_pin_title">Adaugă în Aplicații</string>
<string name="apps_pin_add">Adaugă</string>
<string name="apps_hidden_none">Nicio aplicație ascunsă.</string>
<string name="apps_sort_name_asc">Nume A–Z</string>
<string name="apps_sort_name_desc">Nume Z–A</string>
<string name="apps_sort_installed">Instalate recent</string>
<string name="apps_sort_updated">Actualizate recent</string>
<plurals name="apps_count">
<item quantity="one">%d aplicație</item>
<item quantity="few">%d aplicații</item>
<item quantity="other">%d de aplicații</item>
</plurals>
</resources>
+44
View File
@@ -3155,4 +3155,48 @@
<string name="itch_collections">Коллекции</string>
<string name="xbox_filter_cloud">Облако</string>
<string name="stores_xbox_cloud_settings">Настройки облака</string>
<string name="apps_search_hint">Поиск приложений</string>
<string name="apps_no_match">Нет приложений по запросу «%1$s».</string>
<string name="apps_add_widget">Добавить виджет</string>
<string name="apps_widgets_loading">Загрузка виджетов…</string>
<string name="apps_widgets_none">На этом устройстве нет приложений с виджетами.</string>
<string name="apps_widget_wider">Шире</string>
<string name="apps_widget_narrower">Уже</string>
<string name="apps_widget_taller">Выше</string>
<string name="apps_widget_shorter">Ниже</string>
<string name="apps_widget_move_earlier">Переместить раньше</string>
<string name="apps_widget_move_later">Переместить позже</string>
<string name="apps_widget_configure">Настройки виджета</string>
<string name="apps_widget_remove">Удалить виджет</string>
<string name="apps_add_to_folder">Добавить в папку</string>
<string name="apps_new_folder">Новая папка</string>
<string name="apps_remove_from_folder">Убрать из папки</string>
<string name="apps_app_info">О приложении</string>
<string name="apps_uninstall">Удалить</string>
<string name="apps_folder_rename">Переименовать</string>
<string name="apps_folder_ungroup">Разгруппировать</string>
<string name="apps_folder_default">Папка</string>
<string name="apps_folder_name">Название папки</string>
<string name="apps_filter">Показать</string>
<string name="apps_filter_all">Все приложения</string>
<string name="apps_filter_games">Игры</string>
<string name="apps_filter_downloaded">Загруженные</string>
<string name="apps_filter_system">Системные</string>
<string name="apps_filter_hidden">Скрытые</string>
<string name="apps_hide">Скрыть</string>
<string name="apps_unhide">Показывать в приложениях</string>
<string name="apps_shortcut_remove">Удалить ярлык</string>
<string name="apps_pin_title">Добавить в приложения</string>
<string name="apps_pin_add">Добавить</string>
<string name="apps_hidden_none">Скрытых приложений нет.</string>
<string name="apps_sort_name_asc">Название А–Я</string>
<string name="apps_sort_name_desc">Название Я–А</string>
<string name="apps_sort_installed">Недавно установленные</string>
<string name="apps_sort_updated">Недавно обновлённые</string>
<plurals name="apps_count">
<item quantity="one">%d приложение</item>
<item quantity="few">%d приложения</item>
<item quantity="many">%d приложений</item>
<item quantity="other">%d приложения</item>
</plurals>
</resources>
+42
View File
@@ -3085,4 +3085,46 @@
<string name="itch_collections">Samlingar</string>
<string name="xbox_filter_cloud">Moln</string>
<string name="stores_xbox_cloud_settings">Molninställningar</string>
<string name="apps_search_hint">Sök appar</string>
<string name="apps_no_match">Inga appar matchar ”%1$s”.</string>
<string name="apps_add_widget">Lägg till widget</string>
<string name="apps_widgets_loading">Läser in widgetar…</string>
<string name="apps_widgets_none">Inga appar på enheten erbjuder widgetar.</string>
<string name="apps_widget_wider">Bredare</string>
<string name="apps_widget_narrower">Smalare</string>
<string name="apps_widget_taller">Högre</string>
<string name="apps_widget_shorter">Lägre</string>
<string name="apps_widget_move_earlier">Flytta tidigare</string>
<string name="apps_widget_move_later">Flytta senare</string>
<string name="apps_widget_configure">Widgetinställningar</string>
<string name="apps_widget_remove">Ta bort widget</string>
<string name="apps_add_to_folder">Lägg till i mapp</string>
<string name="apps_new_folder">Ny mapp</string>
<string name="apps_remove_from_folder">Ta bort från mapp</string>
<string name="apps_app_info">Appinfo</string>
<string name="apps_uninstall">Avinstallera</string>
<string name="apps_folder_rename">Byt namn</string>
<string name="apps_folder_ungroup">Dela upp</string>
<string name="apps_folder_default">Mapp</string>
<string name="apps_folder_name">Mappnamn</string>
<string name="apps_filter">Visa</string>
<string name="apps_filter_all">Alla appar</string>
<string name="apps_filter_games">Spel</string>
<string name="apps_filter_downloaded">Nedladdade</string>
<string name="apps_filter_system">System</string>
<string name="apps_filter_hidden">Dolda</string>
<string name="apps_hide">Dölj</string>
<string name="apps_unhide">Visa i appar</string>
<string name="apps_shortcut_remove">Ta bort genväg</string>
<string name="apps_pin_title">Lägg till i Appar</string>
<string name="apps_pin_add">Lägg till</string>
<string name="apps_hidden_none">Inga dolda appar.</string>
<string name="apps_sort_name_asc">Namn A–Ö</string>
<string name="apps_sort_name_desc">Namn Ö–A</string>
<string name="apps_sort_installed">Senast installerade</string>
<string name="apps_sort_updated">Senast uppdaterade</string>
<plurals name="apps_count">
<item quantity="one">%d app</item>
<item quantity="other">%d appar</item>
</plurals>
</resources>
+41
View File
@@ -3060,4 +3060,45 @@
<string name="itch_collections">คอลเลกชัน</string>
<string name="xbox_filter_cloud">คลาวด์</string>
<string name="stores_xbox_cloud_settings">การตั้งค่าคลาวด์</string>
<string name="apps_search_hint">ค้นหาแอป</string>
<string name="apps_no_match">ไม่มีแอปที่ตรงกับ “%1$s”</string>
<string name="apps_add_widget">เพิ่มวิดเจ็ต</string>
<string name="apps_widgets_loading">กำลังโหลดวิดเจ็ต…</string>
<string name="apps_widgets_none">ไม่มีแอปในอุปกรณ์นี้ที่มีวิดเจ็ต</string>
<string name="apps_widget_wider">กว้างขึ้น</string>
<string name="apps_widget_narrower">แคบลง</string>
<string name="apps_widget_taller">สูงขึ้น</string>
<string name="apps_widget_shorter">เตี้ยลง</string>
<string name="apps_widget_move_earlier">ย้ายไปก่อน</string>
<string name="apps_widget_move_later">ย้ายไปหลัง</string>
<string name="apps_widget_configure">การตั้งค่าวิดเจ็ต</string>
<string name="apps_widget_remove">นำวิดเจ็ตออก</string>
<string name="apps_add_to_folder">เพิ่มลงในโฟลเดอร์</string>
<string name="apps_new_folder">โฟลเดอร์ใหม่</string>
<string name="apps_remove_from_folder">นำออกจากโฟลเดอร์</string>
<string name="apps_app_info">ข้อมูลแอป</string>
<string name="apps_uninstall">ถอนการติดตั้ง</string>
<string name="apps_folder_rename">เปลี่ยนชื่อ</string>
<string name="apps_folder_ungroup">ยกเลิกกลุ่ม</string>
<string name="apps_folder_default">โฟลเดอร์</string>
<string name="apps_folder_name">ชื่อโฟลเดอร์</string>
<string name="apps_filter">แสดง</string>
<string name="apps_filter_all">แอปทั้งหมด</string>
<string name="apps_filter_games">เกม</string>
<string name="apps_filter_downloaded">ที่ดาวน์โหลด</string>
<string name="apps_filter_system">ระบบ</string>
<string name="apps_filter_hidden">ที่ซ่อนไว้</string>
<string name="apps_hide">ซ่อน</string>
<string name="apps_unhide">แสดงในแอป</string>
<string name="apps_shortcut_remove">นำทางลัดออก</string>
<string name="apps_pin_title">เพิ่มลงในแอป</string>
<string name="apps_pin_add">เพิ่ม</string>
<string name="apps_hidden_none">ไม่มีแอปที่ซ่อนไว้</string>
<string name="apps_sort_name_asc">ชื่อ A–Z</string>
<string name="apps_sort_name_desc">ชื่อ Z–A</string>
<string name="apps_sort_installed">ติดตั้งล่าสุด</string>
<string name="apps_sort_updated">อัปเดตล่าสุด</string>
<plurals name="apps_count">
<item quantity="other">%d แอป</item>
</plurals>
</resources>
+42
View File
@@ -3087,4 +3087,46 @@
<string name="itch_collections">Koleksiyonlar</string>
<string name="xbox_filter_cloud">Bulut</string>
<string name="stores_xbox_cloud_settings">Bulut ayarları</string>
<string name="apps_search_hint">Uygulama ara</string>
<string name="apps_no_match">“%1$s” ile eşleşen uygulama yok.</string>
<string name="apps_add_widget">Widget ekle</string>
<string name="apps_widgets_loading">Widget’lar yükleniyor…</string>
<string name="apps_widgets_none">Bu cihazdaki hiçbir uygulama widget sunmuyor.</string>
<string name="apps_widget_wider">Daha geniş</string>
<string name="apps_widget_narrower">Daha dar</string>
<string name="apps_widget_taller">Daha uzun</string>
<string name="apps_widget_shorter">Daha kısa</string>
<string name="apps_widget_move_earlier">Öne taşı</string>
<string name="apps_widget_move_later">Arkaya taşı</string>
<string name="apps_widget_configure">Widget ayarları</string>
<string name="apps_widget_remove">Widget’ı kaldır</string>
<string name="apps_add_to_folder">Klasöre ekle</string>
<string name="apps_new_folder">Yeni klasör</string>
<string name="apps_remove_from_folder">Klasörden çıkar</string>
<string name="apps_app_info">Uygulama bilgileri</string>
<string name="apps_uninstall">Kaldır</string>
<string name="apps_folder_rename">Yeniden adlandır</string>
<string name="apps_folder_ungroup">Grubu çöz</string>
<string name="apps_folder_default">Klasör</string>
<string name="apps_folder_name">Klasör adı</string>
<string name="apps_filter">Göster</string>
<string name="apps_filter_all">Tüm uygulamalar</string>
<string name="apps_filter_games">Oyunlar</string>
<string name="apps_filter_downloaded">İndirilenler</string>
<string name="apps_filter_system">Sistem</string>
<string name="apps_filter_hidden">Gizli</string>
<string name="apps_hide">Gizle</string>
<string name="apps_unhide">Uygulamalarda göster</string>
<string name="apps_shortcut_remove">Kısayolu kaldır</string>
<string name="apps_pin_title">Uygulamalara ekle</string>
<string name="apps_pin_add">Ekle</string>
<string name="apps_hidden_none">Gizli uygulama yok.</string>
<string name="apps_sort_name_asc">Ad A–Z</string>
<string name="apps_sort_name_desc">Ad Z–A</string>
<string name="apps_sort_installed">Son yüklenenler</string>
<string name="apps_sort_updated">Son güncellenenler</string>
<plurals name="apps_count">
<item quantity="one">%d uygulama</item>
<item quantity="other">%d uygulama</item>
</plurals>
</resources>
+44
View File
@@ -3153,4 +3153,48 @@
<string name="itch_collections">Колекції</string>
<string name="xbox_filter_cloud">Хмара</string>
<string name="stores_xbox_cloud_settings">Налаштування хмари</string>
<string name="apps_search_hint">Пошук застосунків</string>
<string name="apps_no_match">Немає застосунків за запитом «%1$s».</string>
<string name="apps_add_widget">Додати віджет</string>
<string name="apps_widgets_loading">Завантаження віджетів…</string>
<string name="apps_widgets_none">На цьому пристрої немає застосунків із віджетами.</string>
<string name="apps_widget_wider">Ширше</string>
<string name="apps_widget_narrower">Вужче</string>
<string name="apps_widget_taller">Вище</string>
<string name="apps_widget_shorter">Нижче</string>
<string name="apps_widget_move_earlier">Перемістити раніше</string>
<string name="apps_widget_move_later">Перемістити пізніше</string>
<string name="apps_widget_configure">Налаштування віджета</string>
<string name="apps_widget_remove">Вилучити віджет</string>
<string name="apps_add_to_folder">Додати до папки</string>
<string name="apps_new_folder">Нова папка</string>
<string name="apps_remove_from_folder">Вилучити з папки</string>
<string name="apps_app_info">Про застосунок</string>
<string name="apps_uninstall">Видалити</string>
<string name="apps_folder_rename">Перейменувати</string>
<string name="apps_folder_ungroup">Розгрупувати</string>
<string name="apps_folder_default">Папка</string>
<string name="apps_folder_name">Назва папки</string>
<string name="apps_filter">Показати</string>
<string name="apps_filter_all">Усі застосунки</string>
<string name="apps_filter_games">Ігри</string>
<string name="apps_filter_downloaded">Завантажені</string>
<string name="apps_filter_system">Системні</string>
<string name="apps_filter_hidden">Приховані</string>
<string name="apps_hide">Приховати</string>
<string name="apps_unhide">Показувати в застосунках</string>
<string name="apps_shortcut_remove">Вилучити ярлик</string>
<string name="apps_pin_title">Додати до застосунків</string>
<string name="apps_pin_add">Додати</string>
<string name="apps_hidden_none">Прихованих застосунків немає.</string>
<string name="apps_sort_name_asc">Назва А–Я</string>
<string name="apps_sort_name_desc">Назва Я–А</string>
<string name="apps_sort_installed">Нещодавно встановлені</string>
<string name="apps_sort_updated">Нещодавно оновлені</string>
<plurals name="apps_count">
<item quantity="one">%d застосунок</item>
<item quantity="few">%d застосунки</item>
<item quantity="many">%d застосунків</item>
<item quantity="other">%d застосунку</item>
</plurals>
</resources>
+41
View File
@@ -3060,4 +3060,45 @@
<string name="itch_collections">Bộ sưu tập</string>
<string name="xbox_filter_cloud">Đám mây</string>
<string name="stores_xbox_cloud_settings">Cài đặt đám mây</string>
<string name="apps_search_hint">Tìm ứng dụng</string>
<string name="apps_no_match">Không có ứng dụng nào khớp với “%1$s”.</string>
<string name="apps_add_widget">Thêm tiện ích</string>
<string name="apps_widgets_loading">Đang tải tiện ích…</string>
<string name="apps_widgets_none">Không có ứng dụng nào trên thiết bị này cung cấp tiện ích.</string>
<string name="apps_widget_wider">Rộng hơn</string>
<string name="apps_widget_narrower">Hẹp hơn</string>
<string name="apps_widget_taller">Cao hơn</string>
<string name="apps_widget_shorter">Thấp hơn</string>
<string name="apps_widget_move_earlier">Chuyển lên trước</string>
<string name="apps_widget_move_later">Chuyển ra sau</string>
<string name="apps_widget_configure">Cài đặt tiện ích</string>
<string name="apps_widget_remove">Xóa tiện ích</string>
<string name="apps_add_to_folder">Thêm vào thư mục</string>
<string name="apps_new_folder">Thư mục mới</string>
<string name="apps_remove_from_folder">Xóa khỏi thư mục</string>
<string name="apps_app_info">Thông tin ứng dụng</string>
<string name="apps_uninstall">Gỡ cài đặt</string>
<string name="apps_folder_rename">Đổi tên</string>
<string name="apps_folder_ungroup">Bỏ nhóm</string>
<string name="apps_folder_default">Thư mục</string>
<string name="apps_folder_name">Tên thư mục</string>
<string name="apps_filter">Hiển thị</string>
<string name="apps_filter_all">Tất cả ứng dụng</string>
<string name="apps_filter_games">Trò chơi</string>
<string name="apps_filter_downloaded">Đã tải xuống</string>
<string name="apps_filter_system">Hệ thống</string>
<string name="apps_filter_hidden">Đã ẩn</string>
<string name="apps_hide">Ẩn</string>
<string name="apps_unhide">Hiện trong ứng dụng</string>
<string name="apps_shortcut_remove">Xóa lối tắt</string>
<string name="apps_pin_title">Thêm vào Ứng dụng</string>
<string name="apps_pin_add">Thêm</string>
<string name="apps_hidden_none">Không có ứng dụng nào bị ẩn.</string>
<string name="apps_sort_name_asc">Tên A–Z</string>
<string name="apps_sort_name_desc">Tên Z–A</string>
<string name="apps_sort_installed">Mới cài đặt</string>
<string name="apps_sort_updated">Mới cập nhật</string>
<plurals name="apps_count">
<item quantity="other">%d ứng dụng</item>
</plurals>
</resources>
@@ -3064,4 +3064,45 @@
<string name="itch_collections">收藏集</string>
<string name="xbox_filter_cloud">云</string>
<string name="stores_xbox_cloud_settings">云设置</string>
<string name="apps_search_hint">搜索应用</string>
<string name="apps_no_match">没有与“%1$s”匹配的应用。</string>
<string name="apps_add_widget">添加微件</string>
<string name="apps_widgets_loading">正在加载微件…</string>
<string name="apps_widgets_none">此设备上没有提供微件的应用。</string>
<string name="apps_widget_wider">加宽</string>
<string name="apps_widget_narrower">缩窄</string>
<string name="apps_widget_taller">加高</string>
<string name="apps_widget_shorter">降低</string>
<string name="apps_widget_move_earlier">前移</string>
<string name="apps_widget_move_later">后移</string>
<string name="apps_widget_configure">微件设置</string>
<string name="apps_widget_remove">移除微件</string>
<string name="apps_add_to_folder">添加到文件夹</string>
<string name="apps_new_folder">新建文件夹</string>
<string name="apps_remove_from_folder">从文件夹中移除</string>
<string name="apps_app_info">应用信息</string>
<string name="apps_uninstall">卸载</string>
<string name="apps_folder_rename">重命名</string>
<string name="apps_folder_ungroup">取消分组</string>
<string name="apps_folder_default">文件夹</string>
<string name="apps_folder_name">文件夹名称</string>
<string name="apps_filter">显示</string>
<string name="apps_filter_all">所有应用</string>
<string name="apps_filter_games">游戏</string>
<string name="apps_filter_downloaded">已下载</string>
<string name="apps_filter_system">系统</string>
<string name="apps_filter_hidden">已隐藏</string>
<string name="apps_hide">隐藏</string>
<string name="apps_unhide">在应用中显示</string>
<string name="apps_shortcut_remove">移除快捷方式</string>
<string name="apps_pin_title">添加到应用</string>
<string name="apps_pin_add">添加</string>
<string name="apps_hidden_none">没有隐藏的应用。</string>
<string name="apps_sort_name_asc">名称 A–Z</string>
<string name="apps_sort_name_desc">名称 Z–A</string>
<string name="apps_sort_installed">最近安装</string>
<string name="apps_sort_updated">最近更新</string>
<plurals name="apps_count">
<item quantity="other">%d 个应用</item>
</plurals>
</resources>
@@ -3063,4 +3063,45 @@
<string name="itch_collections">收藏集</string>
<string name="xbox_filter_cloud">雲端</string>
<string name="stores_xbox_cloud_settings">雲端設定</string>
<string name="apps_search_hint">搜尋應用程式</string>
<string name="apps_no_match">沒有符合「%1$s」的應用程式。</string>
<string name="apps_add_widget">新增小工具</string>
<string name="apps_widgets_loading">正在載入小工具…</string>
<string name="apps_widgets_none">此裝置上沒有提供小工具的應用程式。</string>
<string name="apps_widget_wider">加闊</string>
<string name="apps_widget_narrower">收窄</string>
<string name="apps_widget_taller">加高</string>
<string name="apps_widget_shorter">降低</string>
<string name="apps_widget_move_earlier">向前移</string>
<string name="apps_widget_move_later">向後移</string>
<string name="apps_widget_configure">小工具設定</string>
<string name="apps_widget_remove">移除小工具</string>
<string name="apps_add_to_folder">加入資料夾</string>
<string name="apps_new_folder">新資料夾</string>
<string name="apps_remove_from_folder">從資料夾移除</string>
<string name="apps_app_info">應用程式資料</string>
<string name="apps_uninstall">解除安裝</string>
<string name="apps_folder_rename">重新命名</string>
<string name="apps_folder_ungroup">取消組合</string>
<string name="apps_folder_default">資料夾</string>
<string name="apps_folder_name">資料夾名稱</string>
<string name="apps_filter">顯示</string>
<string name="apps_filter_all">所有應用程式</string>
<string name="apps_filter_games">遊戲</string>
<string name="apps_filter_downloaded">已下載</string>
<string name="apps_filter_system">系統</string>
<string name="apps_filter_hidden">已隱藏</string>
<string name="apps_hide">隱藏</string>
<string name="apps_unhide">在應用程式中顯示</string>
<string name="apps_shortcut_remove">移除捷徑</string>
<string name="apps_pin_title">加入應用程式</string>
<string name="apps_pin_add">新增</string>
<string name="apps_hidden_none">沒有隱藏的應用程式。</string>
<string name="apps_sort_name_asc">名稱 A–Z</string>
<string name="apps_sort_name_desc">名稱 Z–A</string>
<string name="apps_sort_installed">最近安裝</string>
<string name="apps_sort_updated">最近更新</string>
<plurals name="apps_count">
<item quantity="other">%d 個應用程式</item>
</plurals>
</resources>
@@ -3063,4 +3063,45 @@
<string name="itch_collections">收藏集</string>
<string name="xbox_filter_cloud">雲端</string>
<string name="stores_xbox_cloud_settings">雲端設定</string>
<string name="apps_search_hint">搜尋應用程式</string>
<string name="apps_no_match">沒有符合「%1$s」的應用程式。</string>
<string name="apps_add_widget">新增小工具</string>
<string name="apps_widgets_loading">正在載入小工具…</string>
<string name="apps_widgets_none">這部裝置上沒有提供小工具的應用程式。</string>
<string name="apps_widget_wider">加寬</string>
<string name="apps_widget_narrower">縮窄</string>
<string name="apps_widget_taller">加高</string>
<string name="apps_widget_shorter">降低</string>
<string name="apps_widget_move_earlier">往前移</string>
<string name="apps_widget_move_later">往後移</string>
<string name="apps_widget_configure">小工具設定</string>
<string name="apps_widget_remove">移除小工具</string>
<string name="apps_add_to_folder">新增至資料夾</string>
<string name="apps_new_folder">新資料夾</string>
<string name="apps_remove_from_folder">從資料夾移除</string>
<string name="apps_app_info">應用程式資訊</string>
<string name="apps_uninstall">解除安裝</string>
<string name="apps_folder_rename">重新命名</string>
<string name="apps_folder_ungroup">取消群組</string>
<string name="apps_folder_default">資料夾</string>
<string name="apps_folder_name">資料夾名稱</string>
<string name="apps_filter">顯示</string>
<string name="apps_filter_all">所有應用程式</string>
<string name="apps_filter_games">遊戲</string>
<string name="apps_filter_downloaded">已下載</string>
<string name="apps_filter_system">系統</string>
<string name="apps_filter_hidden">已隱藏</string>
<string name="apps_hide">隱藏</string>
<string name="apps_unhide">在應用程式中顯示</string>
<string name="apps_shortcut_remove">移除捷徑</string>
<string name="apps_pin_title">新增至應用程式</string>
<string name="apps_pin_add">新增</string>
<string name="apps_hidden_none">沒有隱藏的應用程式。</string>
<string name="apps_sort_name_asc">名稱 A–Z</string>
<string name="apps_sort_name_desc">名稱 Z–A</string>
<string name="apps_sort_installed">最近安裝</string>
<string name="apps_sort_updated">最近更新</string>
<plurals name="apps_count">
<item quantity="other">%d 個應用程式</item>
</plurals>
</resources>
+42
View File
@@ -1081,6 +1081,48 @@
<string name="content_android_apps">Android apps</string>
<string name="content_apps_count">%1$d apps</string>
<string name="content_no_android_apps">No launchable Android apps found.</string>
<string name="apps_search_hint">Search apps</string>
<string name="apps_no_match">No apps match “%1$s”.</string>
<string name="apps_add_widget">Add widget</string>
<string name="apps_widgets_loading">Loading widgets…</string>
<string name="apps_widgets_none">No apps on this device offer widgets.</string>
<string name="apps_widget_wider">Wider</string>
<string name="apps_widget_narrower">Narrower</string>
<string name="apps_widget_taller">Taller</string>
<string name="apps_widget_shorter">Shorter</string>
<string name="apps_widget_move_earlier">Move earlier</string>
<string name="apps_widget_move_later">Move later</string>
<string name="apps_widget_configure">Widget settings</string>
<string name="apps_widget_remove">Remove widget</string>
<string name="apps_add_to_folder">Add to folder</string>
<string name="apps_new_folder">New folder</string>
<string name="apps_remove_from_folder">Remove from folder</string>
<string name="apps_app_info">App info</string>
<string name="apps_uninstall">Uninstall</string>
<string name="apps_folder_rename">Rename</string>
<string name="apps_folder_ungroup">Ungroup</string>
<string name="apps_folder_default">Folder</string>
<string name="apps_folder_name">Folder name</string>
<string name="apps_filter">Show</string>
<string name="apps_filter_all">All apps</string>
<string name="apps_filter_games">Games</string>
<string name="apps_filter_downloaded">Downloaded</string>
<string name="apps_filter_system">System</string>
<string name="apps_filter_hidden">Hidden</string>
<string name="apps_hide">Hide</string>
<string name="apps_unhide">Show in apps</string>
<string name="apps_shortcut_remove">Remove shortcut</string>
<string name="apps_pin_title">Add to Apps</string>
<string name="apps_pin_add">Add</string>
<string name="apps_hidden_none">No hidden apps.</string>
<plurals name="apps_count">
<item quantity="one">%d app</item>
<item quantity="other">%d apps</item>
</plurals>
<string name="apps_sort_name_asc">Name A–Z</string>
<string name="apps_sort_name_desc">Name Z–A</string>
<string name="apps_sort_installed">Recently installed</string>
<string name="apps_sort_updated">Recently updated</string>
<string name="content_desktop_installed">● Desktop installed</string>
<string name="content_desktop_first_open">Installs on first open</string>
<string name="content_available">Available to install</string>