mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-09-04 01:03:25 +00:00
ENH - overhaul wallpaper customization
This commit is contained in:
@@ -645,21 +645,367 @@ button {
|
||||
}
|
||||
.wallpaper--midnight {
|
||||
background:
|
||||
radial-gradient(circle at 70% 18%, #5a397eaa, transparent 35%),
|
||||
radial-gradient(circle at 20% 72%, #164e7daa, transparent 37%),
|
||||
linear-gradient(155deg, #080714, #111126 50%, #02040b);
|
||||
radial-gradient(circle at 76% 17%, #ffffff 0 0.7px, transparent 1.2px),
|
||||
radial-gradient(circle at 28% 8%, #9fceff 0 0.6px, transparent 1px),
|
||||
radial-gradient(circle at 82% 48%, #d8c8ff 0 0.8px, transparent 1.3px),
|
||||
radial-gradient(
|
||||
circle at 69% 19%,
|
||||
#faf7ff 0 3%,
|
||||
#a57be74d 8%,
|
||||
transparent 23%
|
||||
),
|
||||
conic-gradient(
|
||||
from 214deg at 63% 35%,
|
||||
transparent 0 29%,
|
||||
#7352a51f 36%,
|
||||
transparent 44%
|
||||
),
|
||||
radial-gradient(ellipse at 10% 84%, #1767999e 0, transparent 45%),
|
||||
linear-gradient(158deg, #050511 0%, #11132d 42%, #120a25 68%, #02040c 100%);
|
||||
background-size:
|
||||
43px 47px,
|
||||
67px 71px,
|
||||
89px 83px,
|
||||
auto,
|
||||
auto,
|
||||
auto,
|
||||
auto;
|
||||
}
|
||||
.wallpaper--aurora {
|
||||
background:
|
||||
radial-gradient(circle at 72% 20%, #31e2b288, transparent 33%),
|
||||
radial-gradient(circle at 15% 68%, #246fe099, transparent 42%),
|
||||
linear-gradient(145deg, #07191b, #132147 55%, #081019);
|
||||
radial-gradient(circle at 17% 14%, #dfffff 0 0.6px, transparent 1.1px),
|
||||
radial-gradient(circle at 78% 9%, #c6e7ff 0 0.7px, transparent 1.2px),
|
||||
conic-gradient(
|
||||
from 218deg at 84% 8%,
|
||||
transparent 0 18%,
|
||||
#4effbf70 27%,
|
||||
#6bf5e220 35%,
|
||||
transparent 43%
|
||||
),
|
||||
conic-gradient(
|
||||
from 224deg at 62% 26%,
|
||||
transparent 0 20%,
|
||||
#27cfd46b 28%,
|
||||
#695eff42 38%,
|
||||
transparent 47%
|
||||
),
|
||||
radial-gradient(ellipse at 12% 80%, #1860bb9c 0%, transparent 48%),
|
||||
radial-gradient(ellipse at 76% 20%, #2adfbb6e 0%, transparent 38%),
|
||||
linear-gradient(155deg, #031715 0%, #08293a 43%, #151643 68%, #040914 100%);
|
||||
background-size:
|
||||
59px 61px,
|
||||
83px 79px,
|
||||
auto,
|
||||
auto,
|
||||
auto,
|
||||
auto,
|
||||
auto;
|
||||
}
|
||||
.wallpaper--ember {
|
||||
background:
|
||||
radial-gradient(circle at 70% 22%, #ff764f99, transparent 32%),
|
||||
radial-gradient(circle at 15% 65%, #9a306f88, transparent 44%),
|
||||
linear-gradient(150deg, #21090b, #29142e 58%, #080810);
|
||||
radial-gradient(
|
||||
circle at 80% 18%,
|
||||
#fff4ca 0 1.7%,
|
||||
#ffbd67 5%,
|
||||
#ff62335c 17%,
|
||||
transparent 31%
|
||||
),
|
||||
repeating-radial-gradient(
|
||||
ellipse at 78% 21%,
|
||||
transparent 0 16px,
|
||||
#ffb15e12 18px 20px
|
||||
),
|
||||
conic-gradient(
|
||||
from 196deg at 12% 78%,
|
||||
transparent 0 18%,
|
||||
#ff3d4c66 28%,
|
||||
#f3943a29 38%,
|
||||
transparent 48%
|
||||
),
|
||||
conic-gradient(
|
||||
from 22deg at 78% 90%,
|
||||
transparent 0 22%,
|
||||
#b52a755e 31%,
|
||||
transparent 44%
|
||||
),
|
||||
radial-gradient(ellipse at 8% 71%, #c8205c91 0, transparent 46%),
|
||||
linear-gradient(152deg, #20080a 0%, #4b171d 37%, #3c1537 66%, #090710 100%);
|
||||
}
|
||||
.wallpaper--ocean {
|
||||
background:
|
||||
linear-gradient(116deg, transparent 43%, #c7fbff1c 47%, transparent 51%),
|
||||
linear-gradient(64deg, transparent 44%, #8defff17 48%, transparent 52%),
|
||||
radial-gradient(
|
||||
ellipse at 18% 12%,
|
||||
#d7ffff 0 1%,
|
||||
#70e7ff85 12%,
|
||||
transparent 34%
|
||||
),
|
||||
repeating-radial-gradient(
|
||||
ellipse at 48% 107%,
|
||||
transparent 0 24px,
|
||||
#73e8ff2e 27px 30px,
|
||||
transparent 33px 49px
|
||||
),
|
||||
radial-gradient(ellipse at 87% 70%, #056bd1ce 0, transparent 48%),
|
||||
linear-gradient(
|
||||
172deg,
|
||||
#b5f4f8 0%,
|
||||
#148eb0 22%,
|
||||
#075b91 54%,
|
||||
#03284d 78%,
|
||||
#020c20 100%
|
||||
);
|
||||
background-size:
|
||||
82px 82px,
|
||||
82px 82px,
|
||||
auto,
|
||||
auto,
|
||||
auto,
|
||||
auto;
|
||||
}
|
||||
.wallpaper--sunrise {
|
||||
background:
|
||||
linear-gradient(18deg, #281c46 0 13%, transparent 13.3%),
|
||||
linear-gradient(-22deg, #51304a 0 18%, transparent 18.3%),
|
||||
radial-gradient(
|
||||
circle at 58% 69%,
|
||||
#fffbd7 0 5.5%,
|
||||
#ffd28d 6%,
|
||||
#ff9f666e 18%,
|
||||
transparent 35%
|
||||
),
|
||||
repeating-linear-gradient(0deg, transparent 0 16px, #ffe2ae0d 17px 18px),
|
||||
radial-gradient(ellipse at 20% 44%, #f888a360 0, transparent 42%),
|
||||
linear-gradient(
|
||||
178deg,
|
||||
#342b78 0%,
|
||||
#8a477f 35%,
|
||||
#ed706f 57%,
|
||||
#ffc277 72%,
|
||||
#672e50 100%
|
||||
);
|
||||
}
|
||||
.wallpaper--violet {
|
||||
background:
|
||||
radial-gradient(
|
||||
circle at 78% 14%,
|
||||
transparent 0 9%,
|
||||
#e0b6ff35 9.5% 10.3%,
|
||||
transparent 11%
|
||||
),
|
||||
radial-gradient(
|
||||
circle at 18% 74%,
|
||||
transparent 0 16%,
|
||||
#a1a2ff29 16.5% 17.2%,
|
||||
transparent 18%
|
||||
),
|
||||
conic-gradient(
|
||||
from 202deg at 83% 25%,
|
||||
transparent 0 17%,
|
||||
#e48dff91 25%,
|
||||
#8d63ff32 35%,
|
||||
transparent 45%
|
||||
),
|
||||
linear-gradient(122deg, transparent 36%, #f0c9ff13 39%, transparent 42%),
|
||||
radial-gradient(circle at 13% 81%, #5555e5c4 0, transparent 46%),
|
||||
radial-gradient(circle at 72% 28%, #a53dcc70 0, transparent 41%),
|
||||
linear-gradient(148deg, #11072b 0%, #391164 48%, #20103f 72%, #080519 100%);
|
||||
}
|
||||
.wallpaper--forest {
|
||||
background:
|
||||
radial-gradient(
|
||||
ellipse at 82% 12%,
|
||||
transparent 0 7%,
|
||||
#c8f49c2c 7.5% 8.2%,
|
||||
transparent 9%
|
||||
),
|
||||
repeating-radial-gradient(
|
||||
ellipse at -8% 108%,
|
||||
transparent 0 34px,
|
||||
#c1e6a218 36px 39px
|
||||
),
|
||||
linear-gradient(
|
||||
135deg,
|
||||
transparent 42%,
|
||||
#bfe8a31c 45% 47%,
|
||||
transparent 50%
|
||||
),
|
||||
conic-gradient(
|
||||
from 55deg at 85% 24%,
|
||||
transparent 0 24%,
|
||||
#8dcf7361 31%,
|
||||
transparent 39%
|
||||
),
|
||||
radial-gradient(ellipse at 76% 16%, #b7df8675 0, transparent 36%),
|
||||
radial-gradient(ellipse at 10% 78%, #0b8b70b8 0, transparent 49%),
|
||||
linear-gradient(162deg, #061713 0%, #144833 45%, #123326 68%, #040d0b 100%);
|
||||
background-size:
|
||||
auto,
|
||||
auto,
|
||||
auto,
|
||||
auto,
|
||||
auto,
|
||||
auto,
|
||||
100% 100%;
|
||||
}
|
||||
.wallpaper--cobalt {
|
||||
background:
|
||||
linear-gradient(
|
||||
145deg,
|
||||
transparent 0 47%,
|
||||
#c4ddff18 48% 49%,
|
||||
transparent 50%
|
||||
),
|
||||
linear-gradient(
|
||||
35deg,
|
||||
transparent 0 47%,
|
||||
#70a5ff12 48% 49%,
|
||||
transparent 50%
|
||||
),
|
||||
radial-gradient(
|
||||
circle at 76% 25%,
|
||||
transparent 0 12%,
|
||||
#62b8ff45 12.5% 13.2%,
|
||||
transparent 14%
|
||||
),
|
||||
conic-gradient(
|
||||
from 215deg at 80% 28%,
|
||||
transparent 0 22%,
|
||||
#52b5ff85 30%,
|
||||
#5457d62e 41%,
|
||||
transparent 48%
|
||||
),
|
||||
radial-gradient(circle at 14% 78%, #134ed1a3 0, transparent 43%),
|
||||
linear-gradient(152deg, #041438 0%, #0b3182 43%, #172d82 64%, #030a25 100%);
|
||||
background-size:
|
||||
58px 58px,
|
||||
58px 58px,
|
||||
auto,
|
||||
auto,
|
||||
auto,
|
||||
auto;
|
||||
}
|
||||
.wallpaper--rose {
|
||||
background:
|
||||
radial-gradient(
|
||||
circle at 16% 18%,
|
||||
#fff6f3 0 1.5%,
|
||||
#ffd9df9e 8%,
|
||||
transparent 27%
|
||||
),
|
||||
repeating-radial-gradient(
|
||||
ellipse at 108% 8%,
|
||||
transparent 0 24px,
|
||||
#ffd5e121 26px 28px
|
||||
),
|
||||
conic-gradient(
|
||||
from 214deg at 86% 72%,
|
||||
transparent 0 18%,
|
||||
#f078aa8c 27%,
|
||||
#8d3d7b38 39%,
|
||||
transparent 48%
|
||||
),
|
||||
linear-gradient(138deg, transparent 38%, #fff0f217 43%, transparent 48%),
|
||||
radial-gradient(circle at 87% 77%, #e84e92a8 0, transparent 43%),
|
||||
linear-gradient(148deg, #40132f 0%, #8e3159 39%, #b64b70 59%, #351128 100%);
|
||||
}
|
||||
.wallpaper--sand {
|
||||
background:
|
||||
radial-gradient(
|
||||
circle at 76% 16%,
|
||||
#fffce7 0 2.5%,
|
||||
#ffe5a5a8 9%,
|
||||
transparent 25%
|
||||
),
|
||||
repeating-radial-gradient(
|
||||
ellipse at -2% 110%,
|
||||
transparent 0 28px,
|
||||
#fff3d12b 31px 34px,
|
||||
transparent 37px 52px
|
||||
),
|
||||
conic-gradient(
|
||||
from 82deg at 110% 76%,
|
||||
transparent 0 24%,
|
||||
#ecc37e52 32%,
|
||||
transparent 43%
|
||||
),
|
||||
linear-gradient(115deg, transparent 41%, #fff6d21a 44%, transparent 47%),
|
||||
radial-gradient(ellipse at 15% 78%, #9f654b9e 0, transparent 48%),
|
||||
linear-gradient(163deg, #8b5c45 0%, #d4a262 43%, #b87950 68%, #4f302b 100%);
|
||||
}
|
||||
.wallpaper--graphite {
|
||||
background:
|
||||
radial-gradient(circle at 77% 17%, #ffffff42 0 0.8%, transparent 12%),
|
||||
linear-gradient(
|
||||
125deg,
|
||||
transparent 39%,
|
||||
#ffffff12 40% 42%,
|
||||
transparent 43%
|
||||
),
|
||||
linear-gradient(35deg, transparent 39%, #ffffff0a 40% 42%, transparent 43%),
|
||||
repeating-linear-gradient(
|
||||
92deg,
|
||||
transparent 0 3px,
|
||||
#ffffff09 4px,
|
||||
transparent 5px 9px
|
||||
),
|
||||
conic-gradient(
|
||||
from 215deg at 79% 24%,
|
||||
transparent 0 25%,
|
||||
#b7c1d045 32%,
|
||||
transparent 43%
|
||||
),
|
||||
radial-gradient(circle at 17% 78%, #51596c73 0, transparent 44%),
|
||||
linear-gradient(157deg, #06070a 0%, #242832 43%, #171920 67%, #030405 100%);
|
||||
background-size:
|
||||
auto,
|
||||
54px 54px,
|
||||
54px 54px,
|
||||
100% 100%,
|
||||
auto,
|
||||
auto,
|
||||
auto;
|
||||
}
|
||||
.wallpaper--prism {
|
||||
background:
|
||||
linear-gradient(
|
||||
132deg,
|
||||
transparent 0 32%,
|
||||
#ffffff36 32.5% 33.2%,
|
||||
transparent 34%
|
||||
),
|
||||
linear-gradient(
|
||||
42deg,
|
||||
transparent 0 56%,
|
||||
#bffcff25 56.5% 57.2%,
|
||||
transparent 58%
|
||||
),
|
||||
conic-gradient(
|
||||
from 218deg at 70% 29%,
|
||||
#ff527ca1,
|
||||
#ffc75f91,
|
||||
#4cead2a6,
|
||||
#4f72ffae,
|
||||
#c64deaa3,
|
||||
#ff527ca1
|
||||
),
|
||||
conic-gradient(
|
||||
from 38deg at 16% 78%,
|
||||
transparent 0 19%,
|
||||
#4ae3d8a1 28%,
|
||||
#596cff48 39%,
|
||||
transparent 48%
|
||||
),
|
||||
radial-gradient(circle at 16% 76%, #35d8d399 0, transparent 38%),
|
||||
linear-gradient(152deg, #120d35 0%, #47205f 46%, #10205a 70%, #041321 100%);
|
||||
background-blend-mode: screen, screen, screen, screen, screen, normal;
|
||||
}
|
||||
.wallpaper--custom {
|
||||
background-color: #090a0d;
|
||||
background-image: var(--phone-wallpaper-image);
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
||||
.phone-screen--app .springboard {
|
||||
transform: scale(0.91);
|
||||
|
||||
@@ -73,9 +73,15 @@ const time = computed(() =>
|
||||
.join('')
|
||||
.trim(),
|
||||
)
|
||||
const dragStyle = computed(() => ({
|
||||
'--lock-drag': `${dragOffset.value}px`,
|
||||
}))
|
||||
const dragStyle = computed<Record<string, string>>(() => {
|
||||
const imageUrl = preferences.value.settings.wallpaperImageUrl
|
||||
return {
|
||||
'--lock-drag': `${dragOffset.value}px`,
|
||||
...(preferences.value.settings.wallpaper === 'custom' && imageUrl
|
||||
? { '--phone-wallpaper-image': `url(${JSON.stringify(imageUrl)})` }
|
||||
: {}),
|
||||
}
|
||||
})
|
||||
const flashlightShortcutColors = computed(() =>
|
||||
flashlightActive.value
|
||||
? {
|
||||
|
||||
@@ -3938,7 +3938,13 @@ const defaultLocales: LocaleTree = {
|
||||
default: 'The account request failed.',
|
||||
},
|
||||
back: 'Settings',
|
||||
wallpaperPicker: 'Built-in Wallpapers',
|
||||
wallpaperPicker: 'Sky Wallpapers',
|
||||
wallpaperFromPhotos: 'Choose from Photos',
|
||||
wallpaperFromPhotosDescription: 'Use one of your photos',
|
||||
wallpaperFromCamera: 'Take Photo',
|
||||
wallpaperFromCameraDescription: 'Create a new wallpaper',
|
||||
wallpaperHistory: 'Recent',
|
||||
wallpaperCustom: 'Photo wallpaper',
|
||||
toggle: {
|
||||
airplaneMode: 'Toggle Airplane Mode',
|
||||
streamerMode: 'Toggle Streamer Mode',
|
||||
@@ -3977,9 +3983,18 @@ const defaultLocales: LocaleTree = {
|
||||
soft: 'Soft',
|
||||
},
|
||||
wallpapers: {
|
||||
midnight: 'Midnight wallpaper',
|
||||
aurora: 'Aurora wallpaper',
|
||||
ember: 'Ember wallpaper',
|
||||
midnight: 'Midnight',
|
||||
aurora: 'Aurora',
|
||||
ember: 'Ember',
|
||||
ocean: 'Ocean',
|
||||
sunrise: 'Sunrise',
|
||||
violet: 'Violet',
|
||||
forest: 'Forest',
|
||||
cobalt: 'Cobalt',
|
||||
rose: 'Rose',
|
||||
sand: 'Sand',
|
||||
graphite: 'Graphite',
|
||||
prism: 'Prism',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -4361,8 +4376,24 @@ export const usePhoneStore = defineStore('phone', {
|
||||
setSystemDarkMode(value: boolean): void {
|
||||
this.systemDarkMode = value
|
||||
},
|
||||
setWallpaper(wallpaper: WallpaperId): void {
|
||||
setWallpaper(wallpaper: WallpaperId, imageUrl: string | null = null): void {
|
||||
const normalizedImageUrl =
|
||||
wallpaper === 'custom' ? imageUrl?.trim() : null
|
||||
if (wallpaper === 'custom' && !normalizedImageUrl) {
|
||||
throw new Error('Custom wallpapers require a photo URL.')
|
||||
}
|
||||
|
||||
this.preferences.settings.wallpaper = wallpaper
|
||||
this.preferences.settings.wallpaperImageUrl = normalizedImageUrl ?? null
|
||||
this.preferences.settings.wallpaperHistory = [
|
||||
{ imageUrl: normalizedImageUrl ?? null, wallpaper },
|
||||
...this.preferences.settings.wallpaperHistory.filter((entry) =>
|
||||
wallpaper === 'custom'
|
||||
? entry.wallpaper !== 'custom' ||
|
||||
entry.imageUrl !== normalizedImageUrl
|
||||
: entry.wallpaper !== wallpaper,
|
||||
),
|
||||
].slice(0, 4)
|
||||
this.saveDeviceNamespace('settings', this.preferences)
|
||||
},
|
||||
async unlockWithPasscode(
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { LaunchablePhoneAppId } from '@/types/apps'
|
||||
import { DEFAULT_PHONE_PREFERENCES, parsePhonePreferences } from './preferences'
|
||||
import {
|
||||
DEFAULT_PHONE_PREFERENCES,
|
||||
parsePhonePreferences,
|
||||
WALLPAPER_IDS,
|
||||
} from './preferences'
|
||||
describe('preferences', () => {
|
||||
it('falls back for malformed and obsolete records', () => {
|
||||
expect(parsePhonePreferences('{')).toEqual(DEFAULT_PHONE_PREFERENCES)
|
||||
@@ -132,4 +136,52 @@ describe('preferences', () => {
|
||||
wifiEnabled: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('provides twelve built-in wallpapers', () => {
|
||||
expect(WALLPAPER_IDS).toHaveLength(12)
|
||||
expect(new Set(WALLPAPER_IDS).size).toBe(12)
|
||||
})
|
||||
|
||||
it('restores custom photo wallpapers and only the latest four history entries', () => {
|
||||
const value = parsePhonePreferences(
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
settings: {
|
||||
wallpaper: 'custom',
|
||||
wallpaperImageUrl: 'https://media.example/current.jpg',
|
||||
wallpaperHistory: [
|
||||
{
|
||||
wallpaper: 'custom',
|
||||
imageUrl: 'https://media.example/current.jpg',
|
||||
},
|
||||
{ wallpaper: 'prism', imageUrl: null },
|
||||
{ wallpaper: 'ocean', imageUrl: null },
|
||||
{ wallpaper: 'rose', imageUrl: null },
|
||||
{ wallpaper: 'sand', imageUrl: null },
|
||||
],
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
expect(value.settings.wallpaper).toBe('custom')
|
||||
expect(value.settings.wallpaperImageUrl).toBe(
|
||||
'https://media.example/current.jpg',
|
||||
)
|
||||
expect(value.settings.wallpaperHistory).toHaveLength(4)
|
||||
expect(
|
||||
value.settings.wallpaperHistory.map((entry) => entry.wallpaper),
|
||||
).toEqual(['custom', 'prism', 'ocean', 'rose'])
|
||||
})
|
||||
|
||||
it('rejects a custom wallpaper without a photo URL', () => {
|
||||
const value = parsePhonePreferences(
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
settings: { wallpaper: 'custom', wallpaperImageUrl: '' },
|
||||
}),
|
||||
)
|
||||
|
||||
expect(value.settings.wallpaper).toBe('midnight')
|
||||
expect(value.settings.wallpaperImageUrl).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -22,7 +22,20 @@ export const PHONE_FRAME_IDS = [
|
||||
] as const
|
||||
export const RINGTONE_IDS = ['skyline', 'horizon', 'pulse'] as const
|
||||
export const NOTIFICATION_SOUND_IDS = ['chime', 'signal', 'soft'] as const
|
||||
export const WALLPAPER_IDS = ['midnight', 'aurora', 'ember'] as const
|
||||
export const WALLPAPER_IDS = [
|
||||
'midnight',
|
||||
'aurora',
|
||||
'ember',
|
||||
'ocean',
|
||||
'sunrise',
|
||||
'violet',
|
||||
'forest',
|
||||
'cobalt',
|
||||
'rose',
|
||||
'sand',
|
||||
'graphite',
|
||||
'prism',
|
||||
] as const
|
||||
export const PHONE_SCALE_MIN = 75
|
||||
export const PHONE_SCALE_MAX = 150
|
||||
export const PHONE_SCALE_STEP = 5
|
||||
@@ -32,7 +45,12 @@ export type GraphicsMode = (typeof GRAPHICS_MODE_IDS)[number]
|
||||
export type PhoneFrameId = (typeof PHONE_FRAME_IDS)[number]
|
||||
export type RingtoneId = (typeof RINGTONE_IDS)[number]
|
||||
export type NotificationSoundId = (typeof NOTIFICATION_SOUND_IDS)[number]
|
||||
export type WallpaperId = (typeof WALLPAPER_IDS)[number]
|
||||
export type BuiltInWallpaperId = (typeof WALLPAPER_IDS)[number]
|
||||
export type WallpaperId = BuiltInWallpaperId | 'custom'
|
||||
export type WallpaperHistoryEntry = {
|
||||
imageUrl: string | null
|
||||
wallpaper: WallpaperId
|
||||
}
|
||||
export type AppNotificationPreferences = {
|
||||
enabled: boolean
|
||||
sounds: boolean
|
||||
@@ -63,6 +81,8 @@ export type PhonePreferencesV1 = {
|
||||
screenBrightness: number
|
||||
streamerMode: boolean
|
||||
wallpaper: WallpaperId
|
||||
wallpaperHistory: WallpaperHistoryEntry[]
|
||||
wallpaperImageUrl: string | null
|
||||
wifiEnabled: boolean
|
||||
}
|
||||
version: 1
|
||||
@@ -131,6 +151,8 @@ export const DEFAULT_PHONE_PREFERENCES: PhonePreferencesV1 = {
|
||||
screenBrightness: 100,
|
||||
streamerMode: false,
|
||||
wallpaper: 'midnight',
|
||||
wallpaperHistory: [{ imageUrl: null, wallpaper: 'midnight' }],
|
||||
wallpaperImageUrl: null,
|
||||
wifiEnabled: true,
|
||||
},
|
||||
version: 1,
|
||||
@@ -161,6 +183,39 @@ function readChoice<T extends string>(
|
||||
: fallback
|
||||
}
|
||||
|
||||
function readWallpaperImageUrl(value: unknown): string | null {
|
||||
if (typeof value !== 'string') return null
|
||||
const imageUrl = value.trim()
|
||||
return imageUrl.length > 0 && imageUrl.length <= 2_000_000 ? imageUrl : null
|
||||
}
|
||||
|
||||
function readWallpaperHistory(value: unknown): WallpaperHistoryEntry[] {
|
||||
if (!Array.isArray(value)) return []
|
||||
|
||||
const history: WallpaperHistoryEntry[] = []
|
||||
for (const rawEntry of value.slice(0, 4)) {
|
||||
if (!rawEntry || typeof rawEntry !== 'object' || Array.isArray(rawEntry)) {
|
||||
continue
|
||||
}
|
||||
const entry = rawEntry as Partial<WallpaperHistoryEntry>
|
||||
const imageUrl = readWallpaperImageUrl(entry.imageUrl)
|
||||
if (entry.wallpaper === 'custom' && imageUrl) {
|
||||
history.push({ imageUrl, wallpaper: 'custom' })
|
||||
continue
|
||||
}
|
||||
if (
|
||||
typeof entry.wallpaper === 'string' &&
|
||||
WALLPAPER_IDS.includes(entry.wallpaper as BuiltInWallpaperId)
|
||||
) {
|
||||
history.push({
|
||||
imageUrl: null,
|
||||
wallpaper: entry.wallpaper as BuiltInWallpaperId,
|
||||
})
|
||||
}
|
||||
}
|
||||
return history
|
||||
}
|
||||
|
||||
function readNotifications(
|
||||
value: unknown,
|
||||
): Record<LaunchablePhoneAppId, AppNotificationPreferences> {
|
||||
@@ -214,6 +269,13 @@ export function parsePhonePreferences(raw: string | null): PhonePreferencesV1 {
|
||||
}
|
||||
|
||||
const defaults = DEFAULT_PHONE_PREFERENCES.settings
|
||||
const wallpaperImageUrl = readWallpaperImageUrl(settings.wallpaperImageUrl)
|
||||
const wallpaper =
|
||||
settings.wallpaper === 'custom' && wallpaperImageUrl
|
||||
? 'custom'
|
||||
: readChoice(settings.wallpaper, WALLPAPER_IDS, defaults.wallpaper)
|
||||
const wallpaperHistory = readWallpaperHistory(settings.wallpaperHistory)
|
||||
|
||||
return {
|
||||
settings: {
|
||||
airplaneMode: readBoolean(settings.airplaneMode, defaults.airplaneMode),
|
||||
@@ -281,11 +343,17 @@ export function parsePhonePreferences(raw: string | null): PhonePreferencesV1 {
|
||||
100,
|
||||
),
|
||||
streamerMode: readBoolean(settings.streamerMode, defaults.streamerMode),
|
||||
wallpaper: readChoice(
|
||||
settings.wallpaper,
|
||||
WALLPAPER_IDS,
|
||||
defaults.wallpaper,
|
||||
),
|
||||
wallpaper,
|
||||
wallpaperHistory:
|
||||
wallpaperHistory.length > 0
|
||||
? wallpaperHistory
|
||||
: [
|
||||
{
|
||||
imageUrl: wallpaper === 'custom' ? wallpaperImageUrl : null,
|
||||
wallpaper,
|
||||
},
|
||||
],
|
||||
wallpaperImageUrl: wallpaper === 'custom' ? wallpaperImageUrl : null,
|
||||
wifiEnabled: readBoolean(settings.wifiEnabled, defaults.wifiEnabled),
|
||||
},
|
||||
version: 1,
|
||||
|
||||
@@ -57,6 +57,14 @@ const HOME_FOLDER_HOVER_DURATION = 900
|
||||
const phone = usePhoneStore()
|
||||
const appStore = useAppStoreStore()
|
||||
const widgets = useWidgetsStore()
|
||||
const wallpaperStyle = computed<Record<string, string>>(() => {
|
||||
const style: Record<string, string> = {}
|
||||
const imageUrl = phone.preferences.settings.wallpaperImageUrl
|
||||
if (phone.preferences.settings.wallpaper === 'custom' && imageUrl) {
|
||||
style['--phone-wallpaper-image'] = `url(${JSON.stringify(imageUrl)})`
|
||||
}
|
||||
return style
|
||||
})
|
||||
const searchQuery = ref('')
|
||||
const searchFocused = ref(false)
|
||||
const showAllApps = ref(false)
|
||||
@@ -1296,6 +1304,7 @@ onBeforeUnmount(() => {
|
||||
'springboard--widget-dragging': draggingWidgetId !== null,
|
||||
},
|
||||
]"
|
||||
:style="wallpaperStyle"
|
||||
>
|
||||
<div
|
||||
class="springboard-track"
|
||||
|
||||
@@ -35,4 +35,15 @@ describe('SettingsApp Sky UI contract', () => {
|
||||
/\.settings-content--subpage\s*\{[^}]*padding-top:\s*0/,
|
||||
)
|
||||
})
|
||||
|
||||
it('offers photo, camera, recent, and two-column built-in wallpaper choices', () => {
|
||||
expect(source).toContain("openWallpaperMedia('photos')")
|
||||
expect(source).toContain("openWallpaperMedia('camera')")
|
||||
expect(source).toContain("mediaPicker.consume('settings:wallpaper')")
|
||||
expect(source).toContain('class="settings-wallpaper-history"')
|
||||
expect(source).toContain('class="settings-wallpaper-grid"')
|
||||
expect(source).toMatch(
|
||||
/\.settings-wallpaper-grid\s*\{[^}]*grid-template-columns:\s*repeat\(2,/,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
import {
|
||||
BellRing,
|
||||
Bluetooth,
|
||||
Camera,
|
||||
Check,
|
||||
EyeOff,
|
||||
KeyRound,
|
||||
Images,
|
||||
Monitor,
|
||||
PanelsTopLeft,
|
||||
Moon,
|
||||
@@ -22,10 +24,11 @@ import {
|
||||
computed,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
type ComponentPublicInstance,
|
||||
} from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
import { PHONE_FRAME_COLORS } from '@/config/appearance'
|
||||
import {
|
||||
@@ -34,6 +37,7 @@ import {
|
||||
PHONE_APPS,
|
||||
} from '@/config/apps'
|
||||
import { usePhoneStore } from '@/stores/phone'
|
||||
import { useMessageMediaStore } from '@/stores/messageMedia'
|
||||
import PhonePasscode from '@/components/PhonePasscode.vue'
|
||||
import { useAccountStore } from '@/stores/account'
|
||||
import { useAppAuthStore } from '@/stores/app-auth'
|
||||
@@ -83,6 +87,7 @@ import {
|
||||
type NotificationSoundId,
|
||||
type PhoneFrameId,
|
||||
type RingtoneId,
|
||||
type WallpaperHistoryEntry,
|
||||
} from '@/utils/preferences'
|
||||
|
||||
type SettingsView =
|
||||
@@ -117,6 +122,8 @@ type PasscodeFlow =
|
||||
const FACTORY_RESET_DURATION_MS = 60_000
|
||||
|
||||
const phone = usePhoneStore()
|
||||
const mediaPicker = useMessageMediaStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const isDevelopment = import.meta.env.DEV
|
||||
const account = useAccountStore()
|
||||
@@ -151,6 +158,10 @@ const selectedFrameColor = computed(
|
||||
)
|
||||
let factoryResetAnimationFrame: number | undefined
|
||||
|
||||
const wallpaperHistory = computed(
|
||||
() => phone.preferences.settings.wallpaperHistory,
|
||||
)
|
||||
|
||||
const toggleRows = [
|
||||
{
|
||||
key: 'airplaneMode' as const,
|
||||
@@ -221,6 +232,30 @@ function openSkyUiKitchenSink(): void {
|
||||
void router.push({ name: 'development-sky-ui' })
|
||||
}
|
||||
|
||||
function openWallpaperMedia(app: 'photos' | 'camera'): void {
|
||||
mediaPicker.begin(
|
||||
'settings:wallpaper',
|
||||
'photo',
|
||||
'/apps/settings?wallpaper=1',
|
||||
1,
|
||||
)
|
||||
void router.push({
|
||||
path: `/apps/${app}`,
|
||||
query: { mediaAttachment: 'photo' },
|
||||
})
|
||||
}
|
||||
|
||||
function wallpaperPreviewStyle(
|
||||
entry: WallpaperHistoryEntry,
|
||||
): Record<string, string> | undefined {
|
||||
if (entry.wallpaper !== 'custom' || !entry.imageUrl) return undefined
|
||||
return { '--phone-wallpaper-image': `url(${JSON.stringify(entry.imageUrl)})` }
|
||||
}
|
||||
|
||||
function selectWallpaperHistory(entry: WallpaperHistoryEntry): void {
|
||||
phone.setWallpaper(entry.wallpaper, entry.imageUrl)
|
||||
}
|
||||
|
||||
const connectivityRows = [
|
||||
{
|
||||
key: 'wifi',
|
||||
@@ -591,6 +626,13 @@ async function confirmSimEject(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (route.query.wallpaper === '1') activeView.value = 'wallpaper'
|
||||
|
||||
const selectedPhoto = mediaPicker.consume('settings:wallpaper')
|
||||
if (selectedPhoto) phone.setWallpaper('custom', selectedPhoto.url)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (factoryResetAnimationFrame !== undefined) {
|
||||
cancelAnimationFrame(factoryResetAnimationFrame)
|
||||
@@ -1326,16 +1368,105 @@ onBeforeUnmount(() => {
|
||||
</template>
|
||||
|
||||
<template v-else-if="activeView === 'wallpaper'">
|
||||
<SkySettingsGroup :title="phone.t('Apps.settings.wallpaperPicker')">
|
||||
<SkySettingsRow
|
||||
v-for="wallpaper in WALLPAPER_IDS"
|
||||
:key="wallpaper"
|
||||
kind="choice"
|
||||
:selected="phone.preferences.settings.wallpaper === wallpaper"
|
||||
:title="phone.t('Apps.settings.wallpapers.' + wallpaper)"
|
||||
@activate="phone.setWallpaper(wallpaper)"
|
||||
/>
|
||||
</SkySettingsGroup>
|
||||
<section class="settings-wallpaper-actions">
|
||||
<button type="button" @click="openWallpaperMedia('photos')">
|
||||
<span class="settings-wallpaper-actions__icon" aria-hidden="true">
|
||||
<Images />
|
||||
</span>
|
||||
<span>
|
||||
<strong>{{
|
||||
phone.t('Apps.settings.wallpaperFromPhotos')
|
||||
}}</strong>
|
||||
<small>{{
|
||||
phone.t('Apps.settings.wallpaperFromPhotosDescription')
|
||||
}}</small>
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" @click="openWallpaperMedia('camera')">
|
||||
<span class="settings-wallpaper-actions__icon" aria-hidden="true">
|
||||
<Camera />
|
||||
</span>
|
||||
<span>
|
||||
<strong>{{
|
||||
phone.t('Apps.settings.wallpaperFromCamera')
|
||||
}}</strong>
|
||||
<small>{{
|
||||
phone.t('Apps.settings.wallpaperFromCameraDescription')
|
||||
}}</small>
|
||||
</span>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<section
|
||||
v-if="wallpaperHistory.length"
|
||||
class="settings-wallpaper-section"
|
||||
>
|
||||
<h2>{{ phone.t('Apps.settings.wallpaperHistory') }}</h2>
|
||||
<div class="settings-wallpaper-history">
|
||||
<button
|
||||
v-for="(entry, index) in wallpaperHistory"
|
||||
:key="`${entry.wallpaper}-${entry.imageUrl ?? index}`"
|
||||
type="button"
|
||||
:class="[
|
||||
`wallpaper--${entry.wallpaper}`,
|
||||
{
|
||||
'settings-wallpaper-history__item--selected':
|
||||
phone.preferences.settings.wallpaper === entry.wallpaper &&
|
||||
phone.preferences.settings.wallpaperImageUrl ===
|
||||
entry.imageUrl,
|
||||
},
|
||||
]"
|
||||
:style="wallpaperPreviewStyle(entry)"
|
||||
:aria-label="
|
||||
entry.wallpaper === 'custom'
|
||||
? phone.t('Apps.settings.wallpaperCustom')
|
||||
: phone.t('Apps.settings.wallpapers.' + entry.wallpaper)
|
||||
"
|
||||
@click="selectWallpaperHistory(entry)"
|
||||
>
|
||||
<Check
|
||||
v-if="
|
||||
phone.preferences.settings.wallpaper === entry.wallpaper &&
|
||||
phone.preferences.settings.wallpaperImageUrl ===
|
||||
entry.imageUrl
|
||||
"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="settings-wallpaper-section">
|
||||
<h2>{{ phone.t('Apps.settings.wallpaperPicker') }}</h2>
|
||||
<div class="settings-wallpaper-grid">
|
||||
<button
|
||||
v-for="wallpaper in WALLPAPER_IDS"
|
||||
:key="wallpaper"
|
||||
type="button"
|
||||
class="settings-wallpaper-choice"
|
||||
:class="{
|
||||
'settings-wallpaper-choice--selected':
|
||||
phone.preferences.settings.wallpaper === wallpaper,
|
||||
}"
|
||||
:aria-pressed="phone.preferences.settings.wallpaper === wallpaper"
|
||||
@click="phone.setWallpaper(wallpaper)"
|
||||
>
|
||||
<span
|
||||
class="settings-wallpaper-choice__preview"
|
||||
:class="`wallpaper--${wallpaper}`"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<span class="settings-wallpaper-choice__screen"></span>
|
||||
<Check
|
||||
v-if="phone.preferences.settings.wallpaper === wallpaper"
|
||||
/>
|
||||
</span>
|
||||
<strong>{{
|
||||
phone.t('Apps.settings.wallpapers.' + wallpaper)
|
||||
}}</strong>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
</SkyScrollArea>
|
||||
|
||||
@@ -1546,6 +1677,221 @@ onBeforeUnmount(() => {
|
||||
box-shadow: 0 1px 3px rgb(0 0 0 / 18%);
|
||||
}
|
||||
|
||||
.settings-wallpaper-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: var(--sky-space-2);
|
||||
margin-bottom: var(--sky-space-5);
|
||||
}
|
||||
|
||||
.settings-wallpaper-actions > button {
|
||||
min-width: 0;
|
||||
min-height: 116px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--sky-space-3);
|
||||
padding: var(--sky-space-4);
|
||||
border: 1px solid var(--sky-hairline);
|
||||
border-radius: 20px;
|
||||
background: var(--sky-surface);
|
||||
color: var(--sky-text);
|
||||
text-align: left;
|
||||
box-shadow: 0 8px 20px rgb(0 0 0 / 8%);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
transform 150ms ease,
|
||||
background 150ms ease;
|
||||
}
|
||||
|
||||
.settings-wallpaper-actions > button:hover {
|
||||
background: var(--sky-surface-muted);
|
||||
}
|
||||
|
||||
.settings-wallpaper-actions > button:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
|
||||
.settings-wallpaper-actions > button:focus-visible,
|
||||
.settings-wallpaper-history > button:focus-visible,
|
||||
.settings-wallpaper-choice:focus-visible {
|
||||
outline: 2px solid var(--sky-app-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.settings-wallpaper-actions__icon {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 12px;
|
||||
background: var(--sky-app-accent-soft);
|
||||
color: var(--sky-app-accent);
|
||||
}
|
||||
|
||||
.settings-wallpaper-actions__icon > svg {
|
||||
width: 21px;
|
||||
height: 21px;
|
||||
}
|
||||
|
||||
.settings-wallpaper-actions strong,
|
||||
.settings-wallpaper-actions small {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.settings-wallpaper-actions strong {
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.settings-wallpaper-actions small {
|
||||
margin-top: 3px;
|
||||
color: var(--sky-muted);
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.settings-wallpaper-section {
|
||||
margin-bottom: var(--sky-space-5);
|
||||
}
|
||||
|
||||
.settings-wallpaper-section > h2 {
|
||||
margin: 0 var(--sky-space-1) var(--sky-space-2);
|
||||
color: var(--sky-muted);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.settings-wallpaper-history {
|
||||
display: flex;
|
||||
gap: var(--sky-space-2);
|
||||
overflow-x: auto;
|
||||
padding: 2px var(--sky-space-1) var(--sky-space-2);
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.settings-wallpaper-history::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.settings-wallpaper-history > button {
|
||||
width: 62px;
|
||||
height: 88px;
|
||||
position: relative;
|
||||
flex: 0 0 auto;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
overflow: hidden;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 15px;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 5px 14px rgb(0 0 0 / 18%);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.settings-wallpaper-history > button::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border: 1px solid rgb(255 255 255 / 13%);
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.settings-wallpaper-history > button > svg {
|
||||
width: 21px;
|
||||
height: 21px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 4px;
|
||||
border-radius: 50%;
|
||||
background: var(--sky-app-accent);
|
||||
stroke-width: 3;
|
||||
}
|
||||
|
||||
.settings-wallpaper-history__item--selected {
|
||||
border-color: var(--sky-app-accent) !important;
|
||||
}
|
||||
|
||||
.settings-wallpaper-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: var(--sky-space-3);
|
||||
}
|
||||
|
||||
.settings-wallpaper-choice {
|
||||
min-width: 0;
|
||||
padding: 0 0 var(--sky-space-2);
|
||||
border: 0;
|
||||
border-radius: 21px;
|
||||
background: var(--sky-surface);
|
||||
color: var(--sky-text);
|
||||
text-align: left;
|
||||
box-shadow: 0 8px 20px rgb(0 0 0 / 8%);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
transform 150ms ease,
|
||||
box-shadow 150ms ease;
|
||||
}
|
||||
|
||||
.settings-wallpaper-choice:hover {
|
||||
box-shadow: 0 11px 24px rgb(0 0 0 / 13%);
|
||||
}
|
||||
|
||||
.settings-wallpaper-choice:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.settings-wallpaper-choice__preview {
|
||||
aspect-ratio: 0.78;
|
||||
position: relative;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
overflow: hidden;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 21px 21px 14px 14px;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.settings-wallpaper-choice__screen {
|
||||
width: 28%;
|
||||
height: 7px;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 50%;
|
||||
border-radius: 999px;
|
||||
background: rgb(0 0 0 / 58%);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.settings-wallpaper-choice__preview > svg {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
padding: 5px;
|
||||
border-radius: 50%;
|
||||
background: var(--sky-app-accent);
|
||||
color: #ffffff;
|
||||
stroke-width: 3;
|
||||
}
|
||||
|
||||
.settings-wallpaper-choice > strong {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
padding: var(--sky-space-2) var(--sky-space-3) 0;
|
||||
font-size: 13px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.settings-wallpaper-choice--selected .settings-wallpaper-choice__preview {
|
||||
border-color: var(--sky-app-accent);
|
||||
}
|
||||
|
||||
.settings-frame-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 52px);
|
||||
@@ -1639,7 +1985,9 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.settings-frame-choice {
|
||||
.settings-frame-choice,
|
||||
.settings-wallpaper-actions > button,
|
||||
.settings-wallpaper-choice {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1755,7 +1755,10 @@ Locales["en"] = {
|
||||
skyUiKitchenSinkDescription = "Interactive catalog of first-party Sky UI components",
|
||||
about = "About", deviceName = "Device Name", deviceNameValue = "Sky Phone", softwareVersion = "Software Version",
|
||||
language = "Language", languageValue = "English", localStorage = "Local Storage", localStorageValue = "On Device",
|
||||
back = "Settings", wallpaperPicker = "Built-in Wallpapers", deviceInformation = "Device Information",
|
||||
back = "Settings", wallpaperPicker = "Sky Wallpapers", deviceInformation = "Device Information",
|
||||
wallpaperFromPhotos = "Choose from Photos", wallpaperFromPhotosDescription = "Use one of your photos",
|
||||
wallpaperFromCamera = "Take Photo", wallpaperFromCameraDescription = "Create a new wallpaper",
|
||||
wallpaperHistory = "Recent", wallpaperCustom = "Photo wallpaper",
|
||||
imei = "IMEI", linkedDevices = "Linked Devices", thisDevice = "This Phone", removeDevice = "Remove Device",
|
||||
simCard = "SIM Card", simNumber = "Phone Number", simType = "SIM Type", registeredSim = "Registered",
|
||||
anonymousSim = "Anonymous", noSim = "No SIM", ejectSim = "Eject SIM", ejectSimBody = "Return this SIM card to your inventory?",
|
||||
@@ -1789,7 +1792,11 @@ Locales["en"] = {
|
||||
},
|
||||
ringtones = { skyline = "Skyline", horizon = "Horizon", pulse = "Pulse" },
|
||||
notificationSoundsList = { chime = "Chime", signal = "Signal", soft = "Soft" },
|
||||
wallpapers = { midnight = "Midnight wallpaper", aurora = "Aurora wallpaper", ember = "Ember wallpaper" },
|
||||
wallpapers = {
|
||||
midnight = "Midnight", aurora = "Aurora", ember = "Ember", ocean = "Ocean", sunrise = "Sunrise",
|
||||
violet = "Violet", forest = "Forest", cobalt = "Cobalt", rose = "Rose", sand = "Sand",
|
||||
graphite = "Graphite", prism = "Prism",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user