mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-13 03:51:10 +00:00
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:
1 parent
ef9866e423
commit
e906d2f077
52 files changed
+3160
-56
No files matched your search
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user