Merge branch 'develop_sky' into 'release_sky'

Develop sky

See merge request StarlockTeam/app-starlock!271
This commit is contained in:
李仪 2025-09-03 09:20:35 +00:00
commit c9683b4cba
60 changed files with 43810 additions and 43335 deletions

View File

@ -1160,5 +1160,11 @@
"锁语音包设置": "قفل مجموعة صوت",
"(中国台湾)": "(中国台湾)",
"男声": "ذكر صوت",
"女声": "صوت بنات"
"女声": "صوت بنات",
"您的图像和视频数据仅保留": "يتم الاحتفاظ ببيانات الصور والفيديو فقط",
"后图像和视频数据将会失效,开通": "بعد ذلك ، ستكون بيانات الصورة والفيديو غير صالحة ويتم تنشيطها",
"云存会员": "عضوية التخزين السحابي",
"服务,图像视频信息随心存!": "معلومات الخدمة والصور والفيديو في قلبك!",
"图像": "صورة",
"视频": "فيديو"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Заключване на настройките на гласовия пакет",
"(中国台湾)": "(中国台湾)",
"男声": "Мъжки глас",
"女声": "Женски глас"
"女声": "Женски глас",
"您的图像和视频数据仅保留": "Данните ви за изображения и видеоклипове се запазват само",
"后图像和视频数据将会失效,开通": "След това данните за изображението и видеото ще бъдат невалидни и активирани",
"云存会员": "Членство в Cloud Storage",
"服务,图像视频信息随心存!": "Информацията за обслужване, изображения и видео са във вашето сърце!",
"图像": "изображение",
"视频": "Видео"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "ভয়েস প্যাকেজ সেটিংস লক করুন",
"(中国台湾)": "(中国台湾)",
"男声": "পুরুষের কণ্ঠ",
"女声": "নারী কণ্ঠ"
"女声": "নারী কণ্ঠ",
"您的图像和视频数据仅保留": "আপনার চিত্র এবং ভিডিও ডেটা কেবল ধরে রাখা হয়",
"后图像和视频数据将会失效,开通": "এর পরে, চিত্র এবং ভিডিও ডেটা অবৈধ এবং সক্রিয় হবে",
"云存会员": "ক্লাউড স্টোরেজ সদস্যতা",
"服务,图像视频信息随心存!": "পরিষেবা, চিত্র এবং ভিডিও তথ্য আপনার হৃদয়ে!",
"图像": "প্রতিচ্ছবি",
"视频": "ভিডিও"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Zamknout nastavení hlasového balíčku",
"(中国台湾)": "(中国台湾)",
"男声": "Mužský hlas",
"女声": "Ženský hlas"
"女声": "Ženský hlas",
"您的图像和视频数据仅保留": "Uchovávají se pouze vaše obrazová data a data videí",
"后图像和视频数据将会失效,开通": "Poté budou obrazová a video data neplatná a aktivovaná",
"云存会员": "Členství v cloudovém úložišti",
"服务,图像视频信息随心存!": "Servis, obrazové a video informace jsou na prvním místě!",
"图像": "obraz",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Lås stemmepakkeindstillinger",
"(中国台湾)": "(中国台湾)",
"男声": "Mandlige stemmer",
"女声": "Kvindelige stemmer"
"女声": "Kvindelige stemmer",
"您的图像和视频数据仅保留": "Dine billed- og videodata opbevares kun",
"后图像和视频数据将会失效,开通": "Derefter vil billed- og videodataene være ugyldige og aktiveret",
"云存会员": "Medlemskab af Cloud Storage",
"服务,图像视频信息随心存!": "Service-, billed- og videoinformation er i dit hjerte!",
"图像": "billede",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Sperren von Sprachpaketeinstellungen",
"(中国台湾)": "(中国台湾)",
"男声": "Männliche Stimme",
"女声": "Frauenstimme"
"女声": "Frauenstimme",
"您的图像和视频数据仅保留": "Ihre Bild- und Videodaten werden nur dann aufbewahrt",
"后图像和视频数据将会失效,开通": "Danach sind die Bild- und Videodaten ungültig und aktiviert",
"云存会员": "Cloud-Speicher-Mitgliedschaft",
"服务,图像视频信息随心存!": "Service-, Bild- und Videoinformationen liegen Ihnen am Herzen!",
"图像": "Bild",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Ρυθμίσεις κλειδώματος πακέτου φωνής",
"(中国台湾)": "(中国台湾)",
"男声": "Ανδρική φωνή",
"女声": "Γυναικεία φωνή"
"女声": "Γυναικεία φωνή",
"您的图像和视频数据仅保留": "Τα δεδομένα εικόνας και βίντεο διατηρούνται μόνο",
"后图像和视频数据将会失效,开通": "Μετά από αυτό, τα δεδομένα εικόνας και βίντεο θα είναι άκυρα και θα ενεργοποιηθούν",
"云存会员": "Συνδρομή Cloud Storage",
"服务,图像视频信息随心存!": "Οι πληροφορίες εξυπηρέτησης, εικόνας και βίντεο είναι στην καρδιά σας!",
"图像": "εικόνα",
"视频": "Βίντεο"
}

View File

@ -1167,5 +1167,11 @@
"锁语音包设置": "Lock voice package settings",
"(中国台湾)": "(中国台湾)",
"男声": "male voice",
"女声": "female voice"
"女声": "female voice",
"您的图像和视频数据仅保留": "Your image and video data is only retained",
"后图像和视频数据将会失效,开通": "After that, the image and video data will be invalid and activated",
"云存会员": "Cloud Storage Membership",
"服务,图像视频信息随心存!": "Service, image and video information are at your heart!",
"图像": "image",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Configuración del paquete de voz de bloqueo",
"(中国台湾)": "(中国台湾)",
"男声": "Voz masculina",
"女声": "Voz femenina"
"女声": "Voz femenina",
"您的图像和视频数据仅保留": "Solo se conservan los datos de imagen y vídeo",
"后图像和视频数据将会失效,开通": "Después de eso, los datos de imagen y video no serán válidos y se activarán",
"云存会员": "Membresía de almacenamiento en la nube",
"服务,图像视频信息随心存!": "¡La información de servicio, imagen y video está en su corazón!",
"图像": "imagen",
"视频": "Vídeo"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Lukustage häälepaketi seaded",
"(中国台湾)": "(中国台湾)",
"男声": "Meeste hääl",
"女声": "Naiste hääl"
"女声": "Naiste hääl",
"您的图像和视频数据仅保留": "Teie pildi- ja videoandmeid säilitatakse ainult",
"后图像和视频数据将会失效,开通": "Pärast seda on pildi- ja videoandmed kehtetud ja aktiveeritud",
"云存会员": "Pilvesalvestuse liikmelisus",
"服务,图像视频信息随心存!": "Teenindus-, pildi- ja videoteave on teie südames!",
"图像": "Piltide",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Lukitse äänipaketin asetukset",
"(中国台湾)": "(中国台湾)",
"男声": "Miehen ääni",
"女声": "Naisten ääni"
"女声": "Naisten ääni",
"您的图像和视频数据仅保留": "Kuva- ja videotietosi säilytetään vain",
"后图像和视频数据将会失效,开通": "Sen jälkeen kuva- ja videotiedot ovat virheellisiä ja aktivoituvat",
"云存会员": "Pilvitallennustilan jäsenyys",
"服务,图像视频信息随心存!": "Palvelu-, kuva- ja videotiedot ovat sydämessäsi!",
"图像": "kuva",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Paramètres du pack Lock Voice",
"(中国台湾)": "(中国台湾)",
"男声": "Voix masculine",
"女声": "Voix de femmes"
"女声": "Voix de femmes",
"您的图像和视频数据仅保留": "Vos données dimage et de vidéo ne sont conservées que",
"后图像和视频数据将会失效,开通": "Après cela, les données de limage et de la vidéo seront invalides et activées",
"云存会员": "Adhésion au stockage dans le cloud",
"服务,图像视频信息随心存!": "Le service, limage et les informations vidéo sont au cœur de vos préoccupations !",
"图像": "image",
"视频": "Vidéo"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "הגדרות חבילת קול לנעול",
"(中国台湾)": "(中国台湾)",
"男声": "קול גבר",
"女声": "קול נשי"
"女声": "קול נשי",
"您的图像和视频数据仅保留": "נתוני התמונה והסרטונים נשמרים רק",
"后图像和视频数据将会失效,开通": "לאחר מכן, נתוני התמונה והווידאו לא יהיו חוקיים ויופעלו",
"云存会员": "חברות באחסון בענן",
"服务,图像视频信息随心存!": "מידע על שירות, תמונה ווידאו נמצאים בלב שלך!",
"图像": "תמונה",
"视频": "וידאו"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "आवाज पैकेज सेटिंग्स ताला लगाएँ",
"(中国台湾)": "(中国台湾)",
"男声": "पुरुष आवाज",
"女声": "महिला आवाज"
"女声": "महिला आवाज",
"您的图像和视频数据仅保留": "आपकी छवि और वीडियो डेटा केवल बनाए रखा जाता है",
"后图像和视频数据将会失效,开通": "उसके बाद, छवि और वीडियो डेटा अमान्य और सक्रिय हो जाएगा",
"云存会员": "क्लाउड स्टोरेज सदस्यता",
"服务,图像视频信息随心存!": "सेवा, छवि और वीडियो जानकारी आपके दिल में हैं!",
"图像": "प्रतिबिंब",
"视频": "वीडियो"
}

View File

@ -1162,5 +1162,11 @@
"锁语音包设置": "鎖語音包設定",
"(中国台湾)": "(中国台湾)",
"男声": "男聲",
"女声": "女聲"
"女声": "女聲",
"您的图像和视频数据仅保留": "您的圖像和視頻數據僅保留",
"后图像和视频数据将会失效,开通": "后圖像和視頻數據將會失效,開通",
"云存会员": "雲存會員",
"服务,图像视频信息随心存!": "服務,圖像視頻資訊隨心存!",
"图像": "圖像",
"视频": "視頻"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Postavke zaključavanja glasovnog paketa",
"(中国台湾)": "(中国台湾)",
"男声": "Muški glas",
"女声": "Ženski glas"
"女声": "Ženski glas",
"您的图像和视频数据仅保留": "Vaši podaci o slici i videozapisu zadržavaju se samo",
"后图像和视频数据将会失效,开通": "Nakon toga, slikovni i video podaci bit će nevažeći i aktivirani",
"云存会员": "Članstvo u pohrani u oblaku",
"服务,图像视频信息随心存!": "Informacije o usluzi, slikama i videozapisima su vam u srcu!",
"图像": "slika",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Hangcsomag zárolási beállításai",
"(中国台湾)": "(中国台湾)",
"男声": "Férfi hang",
"女声": "női hang"
"女声": "női hang",
"您的图像和视频数据仅保留": "A kép- és videóadatokat csak a rendszer őrzi meg",
"后图像和视频数据将会失效,开通": "Ezt követően a kép- és videóadatok érvénytelenek lesznek és aktiválódnak",
"云存会员": "Felhőalapú tárolási tagság",
"服务,图像视频信息随心存!": "A szolgáltatás, a képi és videós információk a szívedben vannak!",
"图像": "kép",
"视频": "Video"
}

View File

@ -1167,5 +1167,11 @@
"锁语音包设置": "Փակել ձայնային փաթեթի պարամետրերը",
"(中国台湾)": "(中国台湾)",
"男声": "տղամարդկանց ձայն",
"女声": "կանանց ձայն"
"女声": "կանանց ձայն",
"您的图像和视频数据仅保留": "Ձեր պատկերի եւ վիդեո տվյալները պահպանվում են միայն",
"后图像和视频数据将会失效,开通": "Դրանից հետո պատկերի եւ վիդեո տվյալները կլինեն անվավեր եւ կակտիվացվեն",
"云存会员": "Cloud Storage Membership",
"服务,图像视频信息随心存!": "Ծառայությունը, պատկերը եւ վիդեո տեղեկատվությունը ձեր սրտում են:",
"图像": "Պատկերասրահ",
"视频": "Տեսանյութ"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Mengunci paket suara",
"(中国台湾)": "(中国台湾)",
"男声": "6 tahun sebelumnya",
"女声": "Suara wanita"
"女声": "Suara wanita",
"您的图像和视频数据仅保留": "Data gambar dan video Anda hanya disimpan",
"后图像和视频数据将会失效,开通": "Setelah itu, data gambar dan video akan tidak valid dan diaktifkan",
"云存会员": "Keanggotaan Cloud Storage",
"服务,图像视频信息随心存!": "Informasi layanan, gambar, dan video adalah inti Anda!",
"图像": "citra",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Impostazioni pacchetto vocale blocco",
"(中国台湾)": "(中国台湾)",
"男声": "voce maschile",
"女声": "voce femminile"
"女声": "voce femminile",
"您的图像和视频数据仅保留": "I dati delle immagini e dei video vengono conservati solo",
"后图像和视频数据将会失效,开通": "Successivamente, i dati dell'immagine e del video non saranno più validi e attivati",
"云存会员": "Iscrizione al cloud storage",
"服务,图像视频信息随心存!": "Le informazioni sul servizio, le immagini e i video sono al tuo centro!",
"图像": "immagine",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "ロック音声パケット設定",
"(中国台湾)": "(中国台湾)",
"男声": "男声",
"女声": "女声"
"女声": "女声",
"您的图像和视频数据仅保留": "画像と動画のデータのみが保持されます",
"后图像和视频数据将会失效,开通": "その後、画像とビデオのデータは無効になり、アクティブになります",
"云存会员": "クラウドストレージメンバーシップ",
"服务,图像视频信息随心存!": "サービス、画像、ビデオ情報があなたの中心にあります!",
"图像": "画像",
"视频": "ビデオ"
}

View File

@ -1167,5 +1167,11 @@
"锁语音包设置": "ხმის პაკეტის პარამეტრები",
"(中国台湾)": "(中国台湾)",
"男声": "მამაკაცის ხმა",
"女声": "ქალის ხმა"
"女声": "ქალის ხმა",
"您的图像和视频数据仅保留": "თქვენი სურათი და ვიდეო მონაცემები ინახება მხოლოდ",
"后图像和视频数据将会失效,开通": "ამის შემდეგ, სურათისა და ვიდეო მონაცემები არასწორი და გააქტიურებული იქნება",
"云存会员": "Cloud Storage წევრობა",
"服务,图像视频信息随心存!": "მომსახურება, სურათი და ვიდეო ინფორმაცია თქვენს გულშია!",
"图像": "სურათი",
"视频": "ვიდეო"
}

View File

@ -1172,5 +1172,11 @@
"语音包设置": "语音包设置",
"(中国台湾)": "(中国台湾)",
"男声": "男声",
"女声": "女声"
"女声": "女声",
"您的图像和视频数据仅保留": "您的图像和视频数据仅保留",
"后图像和视频数据将会失效,开通": "后图像和视频数据将会失效,开通",
"云存会员": "云存会员",
"服务,图像视频信息随心存!": "服务,图像视频信息随心存!",
"图像": "图像",
"视频": "视频"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Дауыстық бума параметрлерін құлыптау",
"(中国台湾)": "(中国台湾)",
"男声": "ер дауысы",
"女声": "Әйел дауысы"
"女声": "Әйел дауысы",
"您的图像和视频数据仅保留": "Сіздің кескініңіз бен бейне деректеріңіз тек сақталады",
"后图像和视频数据将会失效,开通": "Осыдан кейін кескін мен бейне деректер жарамсыз болып, белсендіріледі",
"云存会员": "Бұлтты сақтауға мүшелік",
"服务,图像视频信息随心存!": "Қызмет, бейне және бейне ақпарат сіздің жүрегіңізде жатыр!",
"图像": "кескіні",
"视频": "Бейне"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "음성팩 설정 잠금",
"(中国台湾)": "(中国台湾)",
"男声": "남성",
"女声": "여성 목소리"
"女声": "여성 목소리",
"您的图像和视频数据仅保留": "이미지 및 동영상 데이터만 보존됩니다.",
"后图像和视频数据将会失效,开通": "그 후 이미지 및 비디오 데이터는 유효하지 않고 활성화됩니다",
"云存会员": "클라우드 스토리지 멤버십",
"服务,图像视频信息随心存!": "서비스, 이미지 및 비디오 정보가 당신의 중심에 있습니다!",
"图像": "이미지",
"视频": "비디오"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Balso paketo nustatymų užrakinimas",
"(中国台湾)": "(中国台湾)",
"男声": "vyriškas balsas",
"女声": "Moteriškas balsas"
"女声": "Moteriškas balsas",
"您的图像和视频数据仅保留": "Vaizdo ir vaizdo įrašų duomenys saugomi tik",
"后图像和视频数据将会失效,开通": "Po to vaizdo ir vaizdo duomenys bus negaliojantys ir suaktyvinti",
"云存会员": "Debesies saugyklos narystė",
"服务,图像视频信息随心存!": "Aptarnavimas, vaizdas ir video informacija yra jūsų širdis!",
"图像": "vaizdas",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Balso paketo nustatymų užrakinimas",
"(中国台湾)": "(中国台湾)",
"男声": "vyriškas balsas",
"女声": "Moteriškas balsas"
"女声": "Moteriškas balsas",
"您的图像和视频数据仅保留": "Data imej dan video anda hanya dikekalkan",
"后图像和视频数据将会失效,开通": "Selepas itu, data imej dan video akan menjadi tidak sah dan diaktifkan",
"云存会员": "Keahlian Storan Awan",
"服务,图像视频信息随心存!": "Maklumat perkhidmatan, imej dan video adalah di hati anda!",
"图像": "Imej",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Instellingen voor spraakpakket vergrendelen",
"(中国台湾)": "(中国台湾)",
"男声": "mannelijke stem",
"女声": "Vrouwelijke stem"
"女声": "Vrouwelijke stem",
"您的图像和视频数据仅保留": "Uw beeld- en videogegevens worden alleen bewaard",
"后图像和视频数据将会失效,开通": "Daarna zijn de afbeeldings- en videogegevens ongeldig en geactiveerd",
"云存会员": "Lidmaatschap voor cloudopslag",
"服务,图像视频信息随心存!": "Service-, beeld- en video-informatie staan bij u centraal!",
"图像": "beeld",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Ustawienia blokowania pakietu głosowego",
"(中国台湾)": "(中国台湾)",
"男声": "Mężczyzna",
"女声": "Głos kobiecy"
"女声": "Głos kobiecy",
"您的图像和视频数据仅保留": "Dane obrazu i filmu są przechowywane tylko",
"后图像和视频数据将会失效,开通": "Po tym czasie dane obrazu i wideo zostaną nieważne i aktywowane",
"云存会员": "Członkostwo w usłudze Cloud Storage",
"服务,图像视频信息随心存!": "Informacje o serwisie, obrazie i wideo są w Twoim sercu!",
"图像": "obraz",
"视频": "Wideo"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Configurações do pacote de voz bloqueada",
"(中国台湾)": "(中国台湾)",
"男声": "Voz masculina",
"女声": "voz feminina"
"女声": "voz feminina",
"您的图像和视频数据仅保留": "Seus dados de imagem e vídeo são retidos apenas",
"后图像和视频数据将会失效,开通": "Depois disso, os dados de imagem e vídeo serão inválidos e ativados",
"云存会员": "Associação de armazenamento em nuvem",
"服务,图像视频信息随心存!": "Informações de serviço, imagem e vídeo estão no seu coração!",
"图像": "imagem",
"视频": "Vídeo"
}

View File

@ -1166,5 +1166,11 @@
"语音包设置": "Configurações do pacote de voz",
"(中国台湾)": "(中国台湾)",
"男声": "Macho",
"女声": "Garota"
"女声": "Garota",
"您的图像和视频数据仅保留": "Seus dados de imagem e vídeo são retidos apenas",
"后图像和视频数据将会失效,开通": "Depois disso, os dados de imagem e vídeo serão inválidos e ativados",
"云存会员": "Associação de armazenamento em nuvem",
"服务,图像视频信息随心存!": "Informações de serviço, imagem e vídeo estão no seu coração!",
"图像": "imagem",
"视频": "Vídeo"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Configurarea pachetului vocal de blocare",
"(中国台湾)": "(中国台湾)",
"男声": "vocea bărbatului",
"女声": "Voce feminină"
"女声": "Voce feminină",
"您的图像和视频数据仅保留": "Datele tale de imagine și video sunt păstrate numai",
"后图像和视频数据将会失效,开通": "După aceea, datele de imagine și video vor fi invalide și activate",
"云存会员": "Abonament de stocare în cloud",
"服务,图像视频信息随心存!": "Serviciile, imaginile și informațiile video sunt în centrul dumneavoastră!",
"图像": "imagine",
"视频": "Video"
}

View File

@ -1165,5 +1165,11 @@
"锁语音包设置": "Запустить настройки голосового пакета",
"(中国台湾)": "(中国台湾)",
"男声": "Мужской голос",
"女声": "Женские голоса"
"女声": "Женские голоса",
"您的图像和视频数据仅保留": "Ваши изображения и видеоданные сохраняются только",
"后图像和视频数据将会失效,开通": "После этого изображение и видео данные будут недействительными и активированы",
"云存会员": "Членство в облачном хранилище",
"服务,图像视频信息随心存!": "Сервисная, имиджевая и видеоинформация в Вашем сердце!",
"图像": "образ",
"视频": "Видео"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Zamknutie nastavení hlasového balíka",
"(中国台湾)": "(中国台湾)",
"男声": "mužský hlas",
"女声": "Ženský hlas"
"女声": "Ženský hlas",
"您的图像和视频数据仅保留": "Vaše údaje o obrázkoch a videách sa zachovajú iba",
"后图像和视频数据将会失效,开通": "Potom budú údaje o obrázku a videu neplatné a aktivované",
"云存会员": "Členstvo v cloudovom úložisku",
"服务,图像视频信息随心存!": "Informácie o službách, obrázkoch a videách sú vo vašom srdci!",
"图像": "obraz",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Закључајте подешавања говорног пакета",
"(中国台湾)": "(中国台湾)",
"男声": "мушки глас",
"女声": "женски глас"
"女声": "женски глас",
"您的图像和视频数据仅保留": "Ваши подаци о слици и видео записима се задржавају само",
"后图像和视频数据将会失效,开通": "Након тога, сликовни и видео подаци ће бити неважећи и активирани",
"云存会员": "Чланство у облаку за складиштење",
"服务,图像视频信息随心存!": "Сервис , слике и видео информације су у вашем срцу!",
"图像": "Слика",
"视频": "Пријава"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Lås inställningar för röstpaket",
"(中国台湾)": "(中国台湾)",
"男声": "Mänsklig röst",
"女声": "Kvinnlig röst"
"女声": "Kvinnlig röst",
"您的图像和视频数据仅保留": "Dina bild- och videodata sparas endast",
"后图像和视频数据将会失效,开通": "Efter det kommer bild- och videodata att vara ogiltiga och aktiverade",
"云存会员": "Medlemskap i molnlagring",
"服务,图像视频信息随心存!": "Service, bild- och videoinformation finns i ditt hjärta!",
"图像": "bild",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "ล็อคการตั้งค่า Voice Pack",
"(中国台湾)": "(中国台湾)",
"男声": "เสียงผู้ชาย",
"女声": "เสียงผู้หญิง"
"女声": "เสียงผู้หญิง",
"您的图像和视频数据仅保留": "ระบบจะเก็บข้อมูลรูปภาพและวิดีโอของคุณไว้เท่านั้น",
"后图像和视频数据将会失效,开通": "หลังจากนั้น ข้อมูลรูปภาพและวิดีโอจะไม่ถูกต้องและเปิดใช้งาน",
"云存会员": "สมาชิกที่เก็บข้อมูลบนคลาวด์",
"服务,图像视频信息随心存!": "ข้อมูลบริการ รูปภาพ และวิดีโออยู่ที่หัวใจของคุณ!",
"图像": "ภาพ",
"视频": "วีดิทัศน์"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Ses Paketi Ayarlarını Kilitle",
"(中国台湾)": "(中国台湾)",
"男声": "Erkek Sesi",
"女声": "Kadın Sesi"
"女声": "Kadın Sesi",
"您的图像和视频数据仅保留": "Görüntü ve video verileriniz yalnızca korunur",
"后图像和视频数据将会失效,开通": "Bundan sonra, görüntü ve video verileri geçersiz olacak ve etkinleştirilecektir",
"云存会员": "Bulut Depolama Üyeliği",
"服务,图像视频信息随心存!": "Servis, görüntü ve video bilgileri kalbinizde!",
"图像": "resim",
"视频": "Video"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "鎖語音包設定",
"(中国台湾)": "(中国台湾)",
"男声": "男聲",
"女声": "女聲"
"女声": "女聲",
"您的图像和视频数据仅保留": "您的圖像和視頻數據僅保留",
"后图像和视频数据将会失效,开通": "后圖像和視頻數據將會失效,開通",
"云存会员": "雲存會員",
"服务,图像视频信息随心存!": "服務,圖像視頻資訊隨心存!",
"图像": "圖像",
"视频": "視頻"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Параметри блокування голосового пакету",
"(中国台湾)": "(中国台湾)",
"男声": "Чоловічий голос",
"女声": "жіночий голос"
"女声": "жіночий голос",
"您的图像和视频数据仅保留": "Ваші зображення та відеодані зберігаються лише",
"后图像和视频数据将会失效,开通": "Після цього дані зображення та відео будуть недійсними та активованими",
"云存会员": "Членство в хмарних сховищах",
"服务,图像视频信息随心存!": "Сервіс, зображення та відео інформація у вашому серці!",
"图像": "образ",
"视频": "Відео"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "صوتی پیکیج کی ترتیبات لاک کریں",
"(中国台湾)": "(中国台湾)",
"男声": "مردوں کی آواز",
"女声": "خواتین کی آواز"
"女声": "خواتین کی آواز",
"您的图像和视频数据仅保留": "آپ کی تصویر اور ویڈیو کا ڈیٹا صرف برقرار رکھا گیا ہے",
"后图像和视频数据将会失效,开通": "اس کے بعد ، تصویر اور ویڈیو کا ڈیٹا غیر قانونی اور فعال ہوجائے گا۔",
"云存会员": "Cloud Storage Membership",
"服务,图像视频信息随心存!": "خدمت، تصویر اور ویڈیو کی معلومات آپ کے دل میں ہیں!",
"图像": "روپ",
"视频": "ویڈیو"
}

View File

@ -1161,5 +1161,11 @@
"锁语音包设置": "Khóa cài đặt gói thoại",
"(中国台湾)": "(中国台湾)",
"男声": "Giọng nam",
"女声": "Giọng nữ"
"女声": "Giọng nữ",
"您的图像和视频数据仅保留": "Dữ liệu hình ảnh và video của bạn chỉ được giữ lại",
"后图像和视频数据将会失效,开通": "Sau đó, dữ liệu hình ảnh và video sẽ không hợp lệ và được kích hoạt",
"云存会员": "Tư cách thành viên lưu trữ đám mây",
"服务,图像视频信息随心存!": "Thông tin dịch vụ, hình ảnh và video là trọng tâm của bạn!",
"图像": "ảnh",
"视频": "Video"
}

View File

@ -63,7 +63,6 @@
"授权管理员拥有操作这把锁的重要权限,请确保只发给我你信任的人": "授权管理员拥有操作这把锁的重要权限,请确保只发给我你信任的人",
"功能开启后,你将可以通过网关远程开锁。此功能的开启和关闭只能在锁附近通过手机蓝牙进行。": "功能开启后,你将可以通过网关远程开锁。此功能的开启和关闭只能在锁附近通过手机蓝牙进行。",
"此功能的开启和关闭只能在锁附近通过手机蓝牙进行": "此功能的开启和关闭只能在锁附近通过手机蓝牙进行",
"功能开启后,你将可以通过网关远程开锁。": "功能开启后,你将可以通过网关远程开锁。",
"排列方式": "排列方式",
"早到榜": "早到榜",
@ -1174,5 +1173,11 @@
"语音包设置": "语音包设置",
"(中国台湾)": "(中国台湾)",
"男声": "男声",
"女声": "女声"
"女声": "女声",
"您的图像和视频数据仅保留": "您的图像和视频数据仅保留",
"后图像和视频数据将会失效,开通": "后图像和视频数据将会失效,开通",
"云存会员": "云存会员",
"服务,图像视频信息随心存!": "服务,图像视频信息随心存!",
"图像": "图像",
"视频": "视频"
}

View File

@ -48,7 +48,7 @@ class ReadLockCurrentVoicePacketReply extends Reply {
CommandType commandType, List<int> dataDetail)
: super.parseData(commandType, dataDetail) {
data = dataDetail;
status = data[6];
status = data[2];
errorWithStstus(status);
}
}

View File

@ -55,7 +55,7 @@ class SetVoicePackageFinalResultReply extends Reply {
CommandType commandType, List<int> dataDetail)
: super.parseData(commandType, dataDetail) {
data = dataDetail;
status = data[6];
status = data[2];
errorWithStstus(status);
}
}

View File

@ -18,9 +18,11 @@ import 'package:star_lock/blue/io_protocol/io_processOtaUpgrade.dart';
import 'package:star_lock/blue/io_protocol/io_readAdminPassword.dart';
import 'package:star_lock/blue/io_protocol/io_readSupportFunctionsNoParameters.dart';
import 'package:star_lock/blue/io_protocol/io_readSupportFunctionsWithParameters.dart';
import 'package:star_lock/blue/io_protocol/io_readVoicePackageFinalResult.dart';
import 'package:star_lock/blue/io_protocol/io_referEventRecordTime.dart';
import 'package:star_lock/blue/io_protocol/io_setSupportFunctionsNoParameters.dart';
import 'package:star_lock/blue/io_protocol/io_setSupportFunctionsWithParameters.dart';
import 'package:star_lock/blue/io_protocol/io_setVoicePackageFinalResult.dart';
import 'package:star_lock/blue/io_protocol/io_timing.dart';
import 'package:star_lock/blue/io_protocol/io_voicePackageConfigure.dart';
import 'package:star_lock/blue/io_protocol/io_voicePackageConfigureProcess.dart';
@ -317,6 +319,18 @@ class CommandReciverManager {
commandType, data);
}
break;
case CommandType.readLockCurrentVoicePacket:
{
reply =
ReadLockCurrentVoicePacketReply.parseData(commandType, data);
}
break;
case CommandType.setLockCurrentVoicePacket:
{
reply =
SetVoicePackageFinalResultReply.parseData(commandType, data);
}
break;
case CommandType.generalExtendedCommond:
{
//

View File

@ -125,4 +125,9 @@ class DoorLockLogDataItem {
data['recordDetailStr'] = recordDetailStr;
return data;
}
@override
String toString() {
return 'DoorLockLogDataItem{recordId: $recordId, lockId: $lockId, lockAlias: $lockAlias, recordType: $recordType, recordTypeName: $recordTypeName, username: $username, operateDate: $operateDate, imagesUrl: $imagesUrl, videoUrl: $videoUrl, headUrl: $headUrl, userid: $userid, keyboardPwd: $keyboardPwd, recordStr: $recordStr, recordDetailStr: $recordDetailStr}';
}
}

View File

@ -3,12 +3,15 @@ import 'dart:async';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:get/get.dart';
import 'package:star_lock/apm/apm_helper.dart';
import 'package:star_lock/appRouters.dart';
import 'package:star_lock/app_settings/app_settings.dart';
import 'package:star_lock/common/XSConstantMacro/XSConstantMacro.dart';
import 'package:star_lock/main/lockDetail/doorLockLog/date_time_extensions.dart';
import 'package:star_lock/main/lockDetail/doorLockLog/doorLockLog_entity.dart';
import 'package:star_lock/main/lockDetail/doorLockLog/doorLockLog_state.dart';
import 'package:star_lock/main/lockDetail/lockOperatingRecord/lockOperatingRecordGetLastRecordTime_entity.dart';
import 'package:star_lock/mine/valueAddedServices/advancedFeaturesWeb/advancedFeaturesWeb_entity.dart';
import 'package:star_lock/tools/commonDataManage.dart';
import 'package:star_lock/tools/dateTool.dart';
import 'package:star_lock/tools/eventBusEventManage.dart';
@ -402,7 +405,20 @@ class DoorLockLogLogic extends BaseGetXController {
void refreshWeek() {
_setWeekRange();
}
getWebPlayUrl() async {
final AdvancedFeaturesWebEntity entity =
await ApiRepository.to.getServicePackageBuyUrl();
if (entity.errorCode!.codeIsSuccessful) {
state.cloudStorageWebViewUrl.value = entity.data!.cloudStorage!;
final uploadReportBuyRequest = await ApiRepository.to
.uploadReportBuyRequest(lockId: state.keyInfos.value.lockId!);
if (uploadReportBuyRequest.errorCode!.codeIsSuccessful) {
Get.toNamed(Routers.advancedFeaturesWebPage, arguments: <String, int>{
'webBuyType': XSConstantMacro.webBuyTypeCloudStorage,
});
}
}
}
@override
Future<void> onClose() async {
super.onClose();

View File

@ -1,4 +1,5 @@
import 'package:flustars/flustars.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
@ -284,6 +285,17 @@ class _DoorLockLogPageState extends State<DoorLockLogPage> with RouteAware {
return formatter.format(dateTime);
}
bool _checkIsVideoOrImagesType(DoorLockLogDataItem item) {
final recordType = item.recordType;
switch (recordType) {
case 130:
case 220:
return true;
default:
return false;
}
}
String _buildIDByType(DoorLockLogDataItem item) {
final recordType = item.recordType;
switch (recordType) {
@ -407,6 +419,20 @@ class _DoorLockLogPageState extends State<DoorLockLogPage> with RouteAware {
itemCount: state.lockLogItemList.length,
contentsBuilder: (BuildContext context, int index) {
final DoorLockLogDataItem timelineData = state.lockLogItemList[index];
// 👇 videoUrl build
int? firstVideoIndex = state.lockLogItemList
.indexWhere((item) => _checkIsVideoOrImagesType(item));
bool isInvalid = _checkIsVideoOrImagesType(timelineData) &&
((timelineData.imagesUrl == null &&
timelineData.videoUrl == null) ||
(timelineData.videoUrl == '' && timelineData.imagesUrl == ''));
String typeText = '';
if (timelineData.recordType == 130) {
typeText = '图像'.tr;
} else if (timelineData.recordType == 220) {
typeText = '视频'.tr;
}
return GestureDetector(
onTap: () {
Get.toNamed(
@ -428,17 +454,37 @@ class _DoorLockLogPageState extends State<DoorLockLogPage> with RouteAware {
// 使 SingleChildScrollView
SingleChildScrollView(
scrollDirection: Axis.horizontal, //
child: Text(
_buildIDByType(timelineData),
child: RichText(
textAlign: TextAlign.left,
text: TextSpan(
style: TextStyle(
color: _buildTextColorByType(timelineData),
fontSize: 24.sp,
fontWeight: FontWeight.w600,
),
//
children: [
TextSpan(
text: _buildIDByType(timelineData) +
(isInvalid
? '${typeText}' +
'已失效'.tr +
''
: ''),
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Visibility(
visible: isInvalid,
child: Icon(
Icons.error,
size: 24.sp,
color: Colors.red,
),
),
),
],
),
maxLines: 1,
//
overflow: TextOverflow.ellipsis,
),
),
@ -455,8 +501,71 @@ class _DoorLockLogPageState extends State<DoorLockLogPage> with RouteAware {
),
),
SizedBox(
height: 20.h,
height: 12.h,
),
Visibility(
visible: _checkIsVideoOrImagesType(timelineData) &&
index == firstVideoIndex,
child: GestureDetector(
onTap: () async {
await logic.getWebPlayUrl();
},
child: Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8.r)),
),
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
children: [
//
TextSpan(
text:
'${'您的图像和视频数据仅保留'.tr} ${state.rollingStorageDays.value} ${''.tr} ,${state.rollingStorageDays.value} ${''.tr} ${'后图像和视频数据将会失效,开通'.tr}',
style: TextStyle(
color: Colors.grey,
fontSize: 16.sp,
fontWeight: FontWeight.w600,
height: 1.8,
),
),
// 🔥
TextSpan(
text: '云存会员'.tr,
style: TextStyle(
color: AppColors.mainColor,
fontSize: 22.sp,
fontWeight: FontWeight.w800,
//
decoration: TextDecoration.underline,
decorationThickness: 1.5,
height: 1.8,
),
recognizer: TapGestureRecognizer()
..onTap = () async {
// 👉
print('点击了“云存会员”');
await logic.getWebPlayUrl();
// Navigator.push(context, MaterialPageRoute(builder: ...));
},
),
//
TextSpan(
text: '服务,图像视频信息随心存!'.tr,
style: TextStyle(
color: Colors.grey,
fontSize: 16.sp,
fontWeight: FontWeight.w600,
height: 1.8,
),
),
],
),
),
),
),
)
],
),
),

View File

@ -1,4 +1,5 @@
import 'package:get/get.dart';
import 'package:get/get_rx/get_rx.dart';
import 'package:star_lock/common/XSConstantMacro/XSConstantMacro.dart';
import 'package:star_lock/main/lockDetail/doorLockLog/doorLockLog_entity.dart';
import 'package:star_lock/tools/advancedCalendar/src/controller.dart';
@ -73,4 +74,6 @@ class DoorLockLogState {
int logCountPage = 10; //
Rx<DateTime> currentSelectDate = DateTime.now().obs;
bool isLockReceiveResponse = false; //
RxString cloudStorageWebViewUrl = ''.obs;
RxInt rollingStorageDays = 3.obs; //
}

View File

@ -89,27 +89,27 @@ class _CatEyeCustomModePageState extends State<CatEyeCustomModePage> {
SizedBox(
height: 30.h,
),
Container(
margin: EdgeInsets.only(left: 20.w),
child: CommonItem(
leftTitel: '实时画面'.tr,
rightTitle: state.realTimeMode.value,
isHaveLine: false,
isHaveDirection: true,
isHaveRightWidget: false,
action: () {
Navigator.pushNamed(context, Routers.liveVideoPage,
arguments: {
'lockSetInfoData': state.lockSetInfoData.value,
'catEyeConfigData': state.lockSetInfoData.value
.lockSettingInfo!.catEyeConfig!.isNotEmpty
? state.lockSetInfoData.value.lockSettingInfo!
.catEyeConfig![0]
: null
});
},
),
)
// Container(
// margin: EdgeInsets.only(left: 20.w),
// child: CommonItem(
// leftTitel: '实时画面'.tr,
// rightTitle: state.realTimeMode.value,
// isHaveLine: false,
// isHaveDirection: true,
// isHaveRightWidget: false,
// action: () {
// Navigator.pushNamed(context, Routers.liveVideoPage,
// arguments: {
// 'lockSetInfoData': state.lockSetInfoData.value,
// 'catEyeConfigData': state.lockSetInfoData.value
// .lockSettingInfo!.catEyeConfig!.isNotEmpty
// ? state.lockSetInfoData.value.lockSettingInfo!
// .catEyeConfig![0]
// : null
// });
// },
// ),
// )
],
),
),

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:get/get.dart';
import 'package:star_lock/app_settings/app_settings.dart';
import 'package:star_lock/blue/blue_manage.dart';
import 'package:star_lock/blue/io_protocol/io_setSupportFunctionsWithParameters.dart';
import 'package:star_lock/blue/io_reply.dart';
@ -82,30 +83,31 @@ class CatEyeSetLogic extends BaseGetXController {
//
cancelBlueConnetctToastTimer();
dismissEasyLoading();
AppLog.log('state.settingOptions.value:${state.settingOptions.value}');
switch (state.settingOptions.value) {
case 1: //
{
updateAutoLightScreenConfig();
await updateAutoLightScreenConfig();
}
break;
case 2: //
{
updateStayWarnConfig();
await updateStayWarnConfig();
}
break;
case 3: //
{
updateAbnormalWarnConfig();
await updateAbnormalWarnConfig();
}
break;
case 4: //
{
updateLightScreenTimeConfig();
await updateLightScreenTimeConfig();
}
break;
case 5: //
{
updateCatEyeModeConfig();
await updateCatEyeModeConfig();
}
break;
default:
@ -288,6 +290,7 @@ class CatEyeSetLogic extends BaseGetXController {
.catEyeConfig![0]
.catEyeModeConfig
?.realTimeMode = state.catEyeConfig.value.realTimeMode;
eventBus
.fire(PassCurrentLockInformationEvent(state.lockSetInfoData.value));
}
@ -456,6 +459,10 @@ class CatEyeSetLogic extends BaseGetXController {
}
void sendBlueMessage() {
showEasyLoading();
showBlueConnetctToastTimer(action: () {
dismissEasyLoading();
});
final message = _buildCatEyeSetBlueMessage();
BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState connectionState) async {

View File

@ -80,12 +80,12 @@ class _CatEyeSetPageState extends State<CatEyeSetPage> {
isHaveRightWidget: true,
rightWidget: _otherToDoSwitch(2),
)),
Obx(() => CommonItem(
leftTitel: '异常警告'.tr,
rightTitle: '',
isHaveLine: true,
isHaveRightWidget: true,
rightWidget: _otherToDoSwitch(3))),
// Obx(() => CommonItem(
// leftTitel: '异常警告'.tr,
// rightTitle: '',
// isHaveLine: true,
// isHaveRightWidget: true,
// rightWidget: _otherToDoSwitch(3))),
//ToDo
CommonItem(
leftTitel: '呼叫目标'.tr,

View File

@ -12,6 +12,7 @@ import 'package:star_lock/blue/blue_manage.dart';
import 'package:star_lock/blue/io_protocol/io_getDeviceModel.dart';
import 'package:star_lock/blue/io_protocol/io_otaUpgrade.dart';
import 'package:star_lock/blue/io_protocol/io_processOtaUpgrade.dart';
import 'package:star_lock/blue/io_protocol/io_readVoicePackageFinalResult.dart';
import 'package:star_lock/blue/io_protocol/io_setVoicePackageFinalResult.dart';
import 'package:star_lock/blue/io_protocol/io_voicePackageConfigure.dart';
import 'package:star_lock/blue/io_protocol/io_voicePackageConfigureProcess.dart';
@ -55,9 +56,12 @@ class SpeechLanguageSettingsLogic extends BaseGetXController {
handleVoiceConfigureThrottled(reply);
} else if (reply is SetVoicePackageFinalResultReply) {
handleSetResult(reply);
} else if (reply is ReadLockCurrentVoicePacketReply) {
handleLockCurrentVoicePacketResult(reply);
}
});
await initList();
readLockLanguage();
}
///
@ -435,9 +439,24 @@ class SpeechLanguageSettingsLogic extends BaseGetXController {
_handlerVoicePackageConfigureConfirmation(
VoicePackageConfigureConfirmationReply reply,
) async {
final int status = reply.data[2];
switch (status) {
case 0x00:
showEasyLoading();
showBlueConnetctToastTimer(action: () {
dismissEasyLoading();
});
final LoginEntity entity = await ApiRepository.to.settingCurrentVoiceTimbre(
data: {
'lang': state.tempLangStr.value,
'timbre': state.tempTimbreStr.value,
},
lockId: state.lockSetInfoData.value.lockId!,
);
if (entity.errorCode!.codeIsSuccessful) {
showSuccess('设置成功'.tr, something: () async {
state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre?.lang =
state.tempLangStr.value;
state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre
?.timbre = state.tempTimbreStr.value;
await BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState deviceConnectionState) async {
if (deviceConnectionState == BluetoothConnectionState.connected) {
@ -454,10 +473,8 @@ class SpeechLanguageSettingsLogic extends BaseGetXController {
showBlueConnetctToast();
}
});
break;
default:
showToast('设置'.tr + '失败'.tr);
break;
await Future.delayed(Duration(seconds: 1));
});
}
}
@ -466,24 +483,6 @@ class SpeechLanguageSettingsLogic extends BaseGetXController {
switch (status) {
case 0x00:
cancelBlueConnetctToastTimer();
final LoginEntity entity =
await ApiRepository.to.settingCurrentVoiceTimbre(
data: {
'lang': state.tempLangStr.value,
'timbre': state.tempTimbreStr.value,
},
lockId: state.lockSetInfoData.value.lockId!,
);
if (entity.errorCode!.codeIsSuccessful) {
showSuccess('设置成功'.tr, something: () {
state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre
?.lang = state.tempLangStr.value;
state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre
?.timbre = state.tempTimbreStr.value;
eventBus.fire(
PassCurrentLockInformationEvent(state.lockSetInfoData.value));
});
}
dismissEasyLoading();
break;
default:
@ -491,4 +490,74 @@ class SpeechLanguageSettingsLogic extends BaseGetXController {
break;
}
}
void handleLockCurrentVoicePacketResult(
ReadLockCurrentVoicePacketReply reply) {
final int status = reply.data[2];
switch (status) {
case 0x00:
//
cancelBlueConnetctToastTimer();
const int languageCodeStartIndex = 3;
const int languageCodeLength = 20;
const int languageCodeEndIndex =
languageCodeStartIndex + languageCodeLength; // 23
if (reply.data.length < languageCodeEndIndex) {
throw Exception(
'Reply data is too short to contain LanguageCode. Expected at least $languageCodeEndIndex bytes, got ${reply.data.length}');
}
List<int> languageCodeBytes =
reply.data.sublist(languageCodeStartIndex, languageCodeEndIndex);
String languageCode = String.fromCharCodes(languageCodeBytes);
languageCode = languageCode.trim(); //
languageCode =
languageCode.replaceAll('\u0000', ''); // (null bytes)
if (languageCode != null && languageCode != '') {
final indexWhere = state.languages
.indexWhere((element) => element.lang == languageCode);
if (indexWhere != -1) {
print('锁板上的语言是:$languageCode,下标是:$indexWhere');
state.selectPassthroughListIndex.value = indexWhere;
}
}
dismissEasyLoading();
break;
case 0x06:
//
final List<int> token = reply.data.sublist(2, 6);
if (state.data != null) {
sendFileToDevice(state.data!, token);
}
break;
default:
break;
}
}
void readLockLanguage() async {
showEasyLoading();
showBlueConnetctToastTimer(action: () {
dismissEasyLoading();
});
await BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState deviceConnectionState) async {
if (deviceConnectionState == BluetoothConnectionState.connected) {
await BlueManage().writeCharacteristicWithResponse(
ReadLockCurrentVoicePacket(
lockID: BlueManage().connectDeviceName,
).packageData(),
);
} else if (deviceConnectionState ==
BluetoothConnectionState.disconnected) {
dismissEasyLoading();
cancelBlueConnetctToastTimer();
showBlueConnetctToast();
}
});
}
}

View File

@ -63,6 +63,12 @@ class _SpeechLanguageSettingsPageState
final soundType = state.soundTypeList.value[index];
return CommonItem(
leftTitel: soundType,
leftTitleStyle: TextStyle(
fontSize: 20.sp,
fontWeight: state.selectSoundTypeIndex.value == index
? FontWeight.bold
: null,
),
rightTitle: '',
isHaveLine: !isLastItem,
isHaveDirection: false,
@ -94,7 +100,8 @@ class _SpeechLanguageSettingsPageState
height: 8.h,
),
//
Container(
Obx(
() => Container(
color: Colors.transparent,
child: Column(
children: List.generate(
@ -103,11 +110,18 @@ class _SpeechLanguageSettingsPageState
final item = state.languages[index];
return CommonItem(
leftTitel: item.langText,
leftTitleStyle: TextStyle(
fontSize: 20.sp,
fontWeight: state.selectPassthroughListIndex.value == index
? FontWeight.bold
: null,
),
rightTitle: '',
isHaveLine: true,
isHaveDirection: false,
isHaveRightWidget: true,
leftTitleMaxWidth: 0.9.sw, //
leftTitleMaxWidth: 0.9.sw,
//
rightWidget:
state.selectPassthroughListIndex.value == index
? Image(
@ -126,6 +140,7 @@ class _SpeechLanguageSettingsPageState
),
),
),
),
],
),
),
@ -133,16 +148,6 @@ class _SpeechLanguageSettingsPageState
}
List<Widget> _buildList() {
final appLocalLanguages = state.languages;
return List.generate(
appLocalLanguages.length,
(index) => _buildItem(
appLocalLanguages[index],
index,
),
);
}
@override
void dispose() {
@ -152,24 +157,4 @@ class _SpeechLanguageSettingsPageState
}
}
_buildItem(PassthroughItem item, index) {
return CommonItem(
leftTitel: item.langText,
rightTitle: '',
isHaveLine: true,
isHaveDirection: false,
isHaveRightWidget: true,
rightWidget: state.selectPassthroughListIndex.value == index
? Image(
image: const AssetImage('images/icon_item_checked.png'),
width: 30.w,
height: 30.w,
fit: BoxFit.contain,
)
: Container(),
action: () {
state.selectPassthroughListIndex.value = index;
},
);
}
}

View File

@ -12,6 +12,7 @@ import 'package:star_lock/app_settings/app_colors.dart';
import 'package:star_lock/blue/blue_manage.dart';
import 'package:star_lock/blue/io_protocol/io_getDeviceModel.dart';
import 'package:star_lock/blue/io_protocol/io_readVoicePackageFinalResult.dart';
import 'package:star_lock/blue/io_protocol/io_setVoicePackageFinalResult.dart';
import 'package:star_lock/blue/io_protocol/io_voicePackageConfigure.dart';
import 'package:star_lock/blue/io_protocol/io_voicePackageConfigureProcess.dart';
import 'package:star_lock/blue/io_reply.dart';
@ -53,6 +54,8 @@ class LockVoiceSettingLogic extends BaseGetXController {
handleVoiceConfigureThrottled(reply);
} else if (reply is ReadLockCurrentVoicePacketReply) {
handleLockCurrentVoicePacketResult(reply);
} else if (reply is SetVoicePackageFinalResultReply) {
handleSetResult(reply);
}
});
initList();
@ -77,6 +80,10 @@ class LockVoiceSettingLogic extends BaseGetXController {
Future<void> _executeLogic(
VoicePackageConfigureConfirmationReply reply) async {
showEasyLoading();
showBlueConnetctToastTimer(action: () {
dismissEasyLoading();
});
final LoginEntity entity = await ApiRepository.to.settingCurrentVoiceTimbre(
data: {
'lang': state.tempLangStr.value,
@ -85,18 +92,47 @@ class LockVoiceSettingLogic extends BaseGetXController {
lockId: state.lockSetInfoData.value.lockId!,
);
if (entity.errorCode!.codeIsSuccessful) {
showSuccess('设置成功'.tr, something: () {
showSuccess('设置成功'.tr, something: () async {
state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre?.lang =
state.tempLangStr.value;
state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre
?.timbre = state.tempTimbreStr.value;
await BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState deviceConnectionState) async {
if (deviceConnectionState == BluetoothConnectionState.connected) {
await BlueManage().writeCharacteristicWithResponse(
SetVoicePackageFinalResult(
lockID: BlueManage().connectDeviceName,
languageCode: state.tempLangStr.value,
).packageData(),
);
} else if (deviceConnectionState ==
BluetoothConnectionState.disconnected) {
dismissEasyLoading();
cancelBlueConnetctToastTimer();
showBlueConnetctToast();
}
});
await Future.delayed(Duration(seconds: 1));
eventBus
.fire(PassCurrentLockInformationEvent(state.lockSetInfoData.value));
Get.offAllNamed(Routers.starLockMain);
});
}
}
void handleSetResult(SetVoicePackageFinalResultReply reply) async {
final int status = reply.data[2];
switch (status) {
case 0x00:
cancelBlueConnetctToastTimer();
dismissEasyLoading();
break;
default:
showToast('设置'.tr + '失败'.tr);
break;
}
}
void saveSpeechLanguageSettings() async {
@ -201,14 +237,12 @@ class LockVoiceSettingLogic extends BaseGetXController {
//
void _handlerStartVoicePackageConfigure(
VoicePackageConfigureReply reply) async {
final int status = reply.data[3];
final int status = reply.data[6];
switch (status) {
case 0x00:
//
cancelBlueConnetctToastTimer();
_startSendLanguageFile();
break;
case 0x06:
//
@ -218,7 +252,8 @@ class LockVoiceSettingLogic extends BaseGetXController {
}
break;
default:
showToast('获取设备型号失败'.tr);
dismissEasyLoading();
cancelBlueConnetctToastTimer();
break;
}
}
@ -409,6 +444,10 @@ class LockVoiceSettingLogic extends BaseGetXController {
}
void readLockLanguage() async {
showEasyLoading();
showBlueConnetctToastTimer(action: () {
dismissEasyLoading();
});
await BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState deviceConnectionState) async {
if (deviceConnectionState == BluetoothConnectionState.connected) {
@ -428,40 +467,42 @@ class LockVoiceSettingLogic extends BaseGetXController {
void handleLockCurrentVoicePacketResult(
ReadLockCurrentVoicePacketReply reply) {
final int status = reply.data[6];
final int status = reply.data[2];
switch (status) {
case 0x00:
//
cancelBlueConnetctToastTimer();
// 1. LanguageCode
// CmdID (2 bytes) + Status (1 byte) = 3 bytes -> LanguageCode 3
const int languageCodeStartIndex = 3;
const int languageCodeLength = 20;
const int languageCodeEndIndex =
languageCodeStartIndex + languageCodeLength; // 23
// 2.
if (reply.data.length < languageCodeEndIndex) {
throw Exception(
'Reply data is too short to contain LanguageCode. Expected at least $languageCodeEndIndex bytes, got ${reply.data.length}');
}
// 3. LanguageCode
List<int> languageCodeBytes =
reply.data.sublist(languageCodeStartIndex, languageCodeEndIndex);
// 4.
// UTF-8 ASCII
String languageCode = String.fromCharCodes(languageCodeBytes);
// 5. () '\0'
// 20 '\0'
languageCode = languageCode.trim(); //
languageCode =
languageCode.replaceAll('\u0000', ''); // (null bytes)
// 6. 使 languageCode
print('LanguageCode: $languageCode'); // : zh_CN, en_US
if (languageCode != null && languageCode != '') {
final indexWhere = state.languages
.indexWhere((element) => element.lang == languageCode);
if (indexWhere != -1) {
print('锁板上的语言是:$languageCode,下标是:$indexWhere');
state.selectPassthroughListIndex.value = indexWhere;
}
}
dismissEasyLoading();
break;
case 0x06:
//

View File

@ -96,7 +96,7 @@ class _LockVoiceSettingState extends State<LockVoiceSetting> {
return CommonItem(
leftTitel: soundType,
leftTitleStyle: TextStyle(
fontSize: 22.sp,
fontSize: 20.sp,
fontWeight: state.selectSoundTypeIndex.value == index
? FontWeight.bold
: null,
@ -142,7 +142,7 @@ class _LockVoiceSettingState extends State<LockVoiceSetting> {
return CommonItem(
leftTitel: item.langText,
leftTitleStyle: TextStyle(
fontSize: 22.sp,
fontSize: 20.sp,
fontWeight:
state.selectPassthroughListIndex.value == index
? FontWeight.bold
@ -152,7 +152,7 @@ class _LockVoiceSettingState extends State<LockVoiceSetting> {
isHaveLine: true,
isHaveDirection: false,
isHaveRightWidget: true,
leftTitleMaxWidth: 0.9.sw,
leftTitleMaxWidth: 0.85.sw,
rightWidget:
state.selectPassthroughListIndex.value == index
? Image(

View File

@ -110,22 +110,8 @@ class ImageTransmissionLogic extends BaseGetXController {
//
switch (contentType) {
case TalkData_ContentTypeE.G711:
// //
if (_isFirstAudioFrame) {
_startAudioTime = currentTime;
_isFirstAudioFrame = false;
}
//
final expectedTime = _startAudioTime + talkData.durationMs;
final audioDelay = currentTime - expectedTime;
//
if (audioDelay > 500) {
state.audioBuffer.clear();
if (state.isOpenVoice.value) {
_playAudioFrames();
}
//
if (!state.isOpenVoice.value && state.isRecordingAudio.value) {
return;
}
if (state.audioBuffer.length >= audioBufferSize) {
@ -212,7 +198,8 @@ class ImageTransmissionLogic extends BaseGetXController {
///
void _playAudioData(TalkData talkData) async {
if (state.isOpenVoice.value) {
if (state.isOpenVoice.value &&
state.isRecordingAudio.value == false) {
final list =
G711().decodeAndDenoise(talkData.content, true, 8000, 300, 150);
// // PCM PcmArrayInt16
@ -565,7 +552,6 @@ class ImageTransmissionLogic extends BaseGetXController {
//
Future<void> startProcessingAudio() async {
try {
if (await state.voiceProcessor?.hasRecordAudioPermission() ?? false) {
await state.voiceProcessor?.start(state.frameLength, state.sampleRate);
@ -602,12 +588,23 @@ class ImageTransmissionLogic extends BaseGetXController {
} on PlatformException catch (ex) {
// state.errorMessage.value = 'Failed to stop recorder: $ex';
} finally {
final bool? isRecording = await state.voiceProcessor?.isRecording();
state.isRecordingAudio.value = isRecording!;
//
if (_startProcessingAudioTimer != null) {
// 53200
for (int i = 0; i < 5; i++) {
_bufferedAudioFrames.addAll(List.filled(chunkSize, 0));
}
Future.delayed(const Duration(milliseconds: 300), () {
_startProcessingAudioTimer?.cancel();
_startProcessingAudioTimer = null;
_bufferedAudioFrames.clear();
});
} else {
_bufferedAudioFrames.clear();
}
final bool? isRecording = await state.voiceProcessor?.isRecording();
state.isRecordingAudio.value = isRecording!;
}
}
static const int chunkSize = 320; // 32010ms G.711
@ -645,38 +642,24 @@ class ImageTransmissionLogic extends BaseGetXController {
List<int> encodedData = G711Tool.encode(applyGain, 0); // 0A-law
_bufferedAudioFrames.addAll(encodedData);
//
if (_startProcessingAudioTimer == null && _bufferedAudioFrames.length > chunkSize) {
_startProcessingAudioTimer = Timer.periodic(Duration(milliseconds: intervalMs), _sendAudioChunk);
if (_startProcessingAudioTimer == null &&
_bufferedAudioFrames.length > chunkSize) {
_startProcessingAudioTimer =
Timer.periodic(Duration(milliseconds: intervalMs), _sendAudioChunk);
}
}
//
void _onError(VoiceProcessorException error) {
AppLog.log(error.message!);
}
//
List<int> _applyGain(List<int> pcmData, double gainFactor) {
List<int> result = List<int>.filled(pcmData.length, 0);
for (int i = 0; i < pcmData.length; i++) {
// PCM数据通常是有符号的16位整数
int sample = pcmData[i];
//
double amplified = sample * gainFactor;
//
if (amplified > 32767) {
amplified = 32767;
} else if (amplified < -32768) {
amplified = -32768;
}
result[i] = amplified.toInt();
}
return result;
return pcmData.map((sample) {
//
int amplified = (sample * gainFactor).round();
return amplified.clamp(-32768, 32767);
}).toList();
}
}

View File

@ -104,10 +104,10 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
codecType: 'h264',
);
// textureId
AppLog.log('StartChartManage().videoWidth:${StartChartManage()
.videoWidth}');
AppLog.log('StartChartManage().videoHeight:${StartChartManage()
.videoHeight}');
AppLog.log(
'StartChartManage().videoWidth:${StartChartManage().videoWidth}');
AppLog.log(
'StartChartManage().videoHeight:${StartChartManage().videoHeight}');
final textureId = await VideoDecodePlugin.initDecoder(config);
if (textureId != null) {
Future.microtask(() => state.textureId.value = textureId);
@ -493,7 +493,9 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
///
void _playAudioData(TalkData talkData) async {
if (state.isOpenVoice.value && state.isLoading.isFalse) {
if (state.isOpenVoice.value &&
state.isLoading.isFalse &&
state.isRecordingAudio.value == false) {
List<int> encodedData = G711Tool.decode(talkData.content, 0); // 0A-law
// PCM PcmArrayInt16
final PcmArrayInt16 fromList = PcmArrayInt16.fromList(encodedData);
@ -746,7 +748,6 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
//
Future<void> startProcessingAudio() async {
try {
if (await state.voiceProcessor?.hasRecordAudioPermission() ?? false) {
await state.voiceProcessor?.start(state.frameLength, state.sampleRate);
@ -783,39 +784,36 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
} on PlatformException catch (ex) {
// state.errorMessage.value = 'Failed to stop recorder: $ex';
} finally {
final bool? isRecording = await state.voiceProcessor?.isRecording();
state.isRecordingAudio.value = isRecording!;
//
if (_startProcessingAudioTimer != null) {
// 53200
for (int i = 0; i < 5; i++) {
_bufferedAudioFrames.addAll(List.filled(chunkSize, 0));
}
Future.delayed(const Duration(milliseconds: 300), () {
_startProcessingAudioTimer?.cancel();
_startProcessingAudioTimer = null;
_bufferedAudioFrames.clear();
});
} else {
_bufferedAudioFrames.clear();
}
final bool? isRecording = await state.voiceProcessor?.isRecording();
state.isRecordingAudio.value = isRecording!;
}
}
//
List<int> _applyGain(List<int> pcmData, double gainFactor) {
List<int> result = List<int>.filled(pcmData.length, 0);
for (int i = 0; i < pcmData.length; i++) {
// PCM数据通常是有符号的16位整数
int sample = pcmData[i];
//
double amplified = sample * gainFactor;
//
if (amplified > 32767) {
amplified = 32767;
} else if (amplified < -32768) {
amplified = -32768;
return pcmData.map((sample) {
//
int amplified = (sample * gainFactor).round();
return amplified.clamp(-32768, 32767);
}).toList();
}
result[i] = amplified.toInt();
}
return result;
}
static const int chunkSize = 320; // 32010ms G.711
static const int intervalMs = 40; // 40ms发送一次4chunk
static const int intervalMs = 35; // 40ms发送一次4chunk
void _sendAudioChunk(Timer timer) async {
if (_bufferedAudioFrames.length < chunkSize) {
//
@ -849,10 +847,11 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
List<int> encodedData = G711Tool.encode(applyGain, 0); // 0A-law
_bufferedAudioFrames.addAll(encodedData);
//
if (_startProcessingAudioTimer == null && _bufferedAudioFrames.length > chunkSize) {
_startProcessingAudioTimer = Timer.periodic(Duration(milliseconds: intervalMs), _sendAudioChunk);
if (_startProcessingAudioTimer == null &&
_bufferedAudioFrames.length > chunkSize) {
_startProcessingAudioTimer =
Timer.periodic(Duration(milliseconds: intervalMs), _sendAudioChunk);
}
}
@ -973,7 +972,8 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
//
switch (contentType) {
case TalkData_ContentTypeE.G711:
if (!state.isOpenVoice.value) {
//
if (!state.isOpenVoice.value || state.isRecordingAudio.value) {
return;
}
if (state.audioBuffer.length >= audioBufferSize) {

View File

@ -109,22 +109,10 @@ class TalkViewLogic extends BaseGetXController {
//
switch (contentType) {
case TalkData_ContentTypeE.G711:
// //
if (_isFirstAudioFrame) {
_startAudioTime = currentTime;
_isFirstAudioFrame = false;
}
//
final expectedTime = _startAudioTime + talkData.durationMs;
final audioDelay = currentTime - expectedTime;
//
if (audioDelay > 500) {
state.audioBuffer.clear();
if (state.isOpenVoice.value) {
_playAudioFrames();
}
//
if (!state.isOpenVoice.value || state.isRecordingAudio.value) {
print(
'录音时丢弃数据:${state.isOpenVoice.value}-${state.isRecordingAudio.value}');
return;
}
if (state.audioBuffer.length >= audioBufferSize) {
@ -388,9 +376,11 @@ class TalkViewLogic extends BaseGetXController {
if (state.videoBuffer.isNotEmpty) {
final TalkData oldestFrame = state.videoBuffer.removeAt(0);
if (oldestFrame.content.isNotEmpty) {
state.listData.value = Uint8List.fromList(oldestFrame.content); //
state.listData.value =
Uint8List.fromList(oldestFrame.content); //
final int decodeStart = DateTime.now().millisecondsSinceEpoch;
decodeImageFromList(Uint8List.fromList(oldestFrame.content)).then((ui.Image img) {
decodeImageFromList(Uint8List.fromList(oldestFrame.content))
.then((ui.Image img) {
final int decodeEnd = DateTime.now().millisecondsSinceEpoch;
state.currentImage.value = img;
_renderedFrameCount++;
@ -562,7 +552,6 @@ class TalkViewLogic extends BaseGetXController {
//
Future<void> startProcessingAudio() async {
try {
if (await state.voiceProcessor?.hasRecordAudioPermission() ?? false) {
await state.voiceProcessor?.start(state.frameLength, state.sampleRate);
@ -599,12 +588,23 @@ class TalkViewLogic extends BaseGetXController {
} on PlatformException catch (ex) {
// state.errorMessage.value = 'Failed to stop recorder: $ex';
} finally {
final bool? isRecording = await state.voiceProcessor?.isRecording();
state.isRecordingAudio.value = isRecording!;
//
if (_startProcessingAudioTimer != null) {
// 53200
for (int i = 0; i < 5; i++) {
_bufferedAudioFrames.addAll(List.filled(chunkSize, 0));
}
Future.delayed(const Duration(milliseconds: 300), () {
_startProcessingAudioTimer?.cancel();
_startProcessingAudioTimer = null;
_bufferedAudioFrames.clear();
});
} else {
_bufferedAudioFrames.clear();
}
final bool? isRecording = await state.voiceProcessor?.isRecording();
state.isRecordingAudio.value = isRecording!;
}
}
static const int chunkSize = 320; // 32010ms G.711
@ -642,10 +642,11 @@ class TalkViewLogic extends BaseGetXController {
List<int> encodedData = G711Tool.encode(applyGain, 0); // 0A-law
_bufferedAudioFrames.addAll(encodedData);
//
if (_startProcessingAudioTimer == null && _bufferedAudioFrames.length > chunkSize) {
_startProcessingAudioTimer = Timer.periodic(Duration(milliseconds: intervalMs), _sendAudioChunk);
if (_startProcessingAudioTimer == null &&
_bufferedAudioFrames.length > chunkSize) {
_startProcessingAudioTimer =
Timer.periodic(Duration(milliseconds: intervalMs), _sendAudioChunk);
}
}
@ -656,25 +657,10 @@ class TalkViewLogic extends BaseGetXController {
//
List<int> _applyGain(List<int> pcmData, double gainFactor) {
List<int> result = List<int>.filled(pcmData.length, 0);
for (int i = 0; i < pcmData.length; i++) {
// PCM数据通常是有符号的16位整数
int sample = pcmData[i];
//
double amplified = sample * gainFactor;
//
if (amplified > 32767) {
amplified = 32767;
} else if (amplified < -32768) {
amplified = -32768;
}
result[i] = amplified.toInt();
}
return result;
return pcmData.map((sample) {
//
int amplified = (sample * gainFactor).round();
return amplified.clamp(-32768, 32767);
}).toList();
}
}