1.4.0+140
This commit is contained in:
parent
77d1fd13b1
commit
125b852590
12
android/app/proguard-rules.pro
vendored
12
android/app/proguard-rules.pro
vendored
@ -27,11 +27,15 @@
|
||||
-keep class com.google.android.play.core.tasks.** { *; }
|
||||
-keep class io.flutter.embedding.engine.deferredcomponents.** { *; }
|
||||
|
||||
# 应用特定
|
||||
# 应用特定
|
||||
-keep class com.org.yumiparty.** { *; }
|
||||
|
||||
# Provider
|
||||
-keep class * extends ChangeNotifier { *; }
|
||||
|
||||
# Tencent TRTC
|
||||
-keep class com.tencent.** { *; }
|
||||
-dontwarn com.tencent.**
|
||||
|
||||
# Provider
|
||||
-keep class * extends ChangeNotifier { *; }
|
||||
-keep class * implements Listenable { *; }
|
||||
|
||||
# 其他保留
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
@ -125,13 +126,13 @@
|
||||
android:exported="true"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
||||
|
||||
<!-- Remove Agora local screen sharing declarations; we do not ship this capability. -->
|
||||
<!-- Remove Tencent TRTC screen sharing declarations; voice rooms only use audio. -->
|
||||
<activity
|
||||
android:name="io.agora.rtc2.extensions.MediaProjectionMgr$LocalScreenCaptureAssistantActivity"
|
||||
android:name="com.tencent.rtmp.video.TXScreenCapture$TXScreenCaptureAssistantActivity"
|
||||
tools:node="remove" />
|
||||
|
||||
<service
|
||||
android:name="io.agora.rtc2.extensions.MediaProjectionMgr$LocalScreenSharingService"
|
||||
android:name="com.tencent.rtmp.video.ScreenCaptureService"
|
||||
tools:node="remove" />
|
||||
|
||||
<service
|
||||
|
||||
730
assets/l10n/intl_fr.json
Normal file
730
assets/l10n/intl_fr.json
Normal file
@ -0,0 +1,730 @@
|
||||
{
|
||||
"signInWithGoogle": "Connectez-vous avec Google",
|
||||
"or": "Ou",
|
||||
"signInWithYourAccount": "Connectez-vous avec votre compte",
|
||||
"signInWithApple": "Connectez-vous avec Apple",
|
||||
"loginRepresentsAgreementTo": "La connexion représente l'accord de",
|
||||
"termsofService": "Conditions d'utilisation",
|
||||
"privaceyPolicy": "Politique de confidentialité",
|
||||
"tips": "Conseils",
|
||||
"searchNoDataTips": "Saisissez la salle ou l'ID utilisateur que vous souhaitez rechercher.",
|
||||
"games": "Jeux",
|
||||
"mine": "Le mien",
|
||||
"official": "Officiel",
|
||||
"party": "Fête",
|
||||
"other": "Autre",
|
||||
"yes": "Oui",
|
||||
"bag": "Sac",
|
||||
"sound2": "Son",
|
||||
"startYourBrandNewJourney": "Commencez votre tout nouveau voyage",
|
||||
"maliciousHarassment": "Harcèlement malveillant",
|
||||
"event": "Événement",
|
||||
"roomEdit": "Modifier la pièce",
|
||||
"cancelRoomPassword": "Êtes-vous sûr de vouloir supprimer le mot de passe de la salle ?",
|
||||
"roomMemberFee": "Frais de membre de la chambre",
|
||||
"roomTheme2": "Thème de la chambre",
|
||||
"background": "Contexte",
|
||||
"blockedList2": "Liste bloquée",
|
||||
"roomPassword": "Mot de passe de la salle",
|
||||
"numberOfMic": "Nombre de micro",
|
||||
"pleaseEnterContent": "Veuillez saisir le contenu",
|
||||
"profilePhoto": "Photo de profil",
|
||||
"aboutMe": "À propos de moi",
|
||||
"myRoom": "Ma chambre",
|
||||
"noHistoricalRecordsAvailable": "Aucun document historique disponible.",
|
||||
"inviteNewUsersToEarnCoins": "Invitez de nouveaux utilisateurs à gagner des pièces",
|
||||
"crateMyRoom": "CRÉEZ VOTRE PROPRE CHAMBRE.",
|
||||
"casualInteraction": "Interaction occasionnelle",
|
||||
"haveGamePlayingTips": "Vous avez un jeu en cours. Veuillez quitter le jeu en cours. Êtes-vous sûr de vouloir quitter ?",
|
||||
"historicalTour": "Visite historique",
|
||||
"gameCenter": "Centre de jeux",
|
||||
"returnToVoiceChat": "Revenir au chat vocal ?",
|
||||
"exitGameMode": "Quitter le mode jeu",
|
||||
"enterTheRoom": "Entrez dans la pièce",
|
||||
"invite": "Inviter",
|
||||
"recent": "Récent",
|
||||
"popularEvents": "Événements populaires",
|
||||
"inviteGoRoomTips": "Toujours là pour vous, beau temps, mauvais temps. Venez nous dire bonjour !",
|
||||
"confirmInviteThisUserToTheRoom": "Confirmer l'invitation de cet utilisateur (ID : {1}) dans le salon ?",
|
||||
"clearCacheSuccessfully": "Vider le cache avec succès",
|
||||
"sent": "Envoyé",
|
||||
"keep": "Garder",
|
||||
"open": "Ouvert",
|
||||
"deleteAccountTips2": "*Si vous changez d'avis, vous pouvez vous reconnecter à votre compte actuel dans les sept jours et nous restaurerons automatiquement votre compte. Si aucune restauration n'intervient dans les sept jours, le compte sera définitivement supprimé.",
|
||||
"deleteAccountTips": "Vous disposez de tous les droits d'administration sur ce compte. Si vous avez l'intention de supprimer le compte, veuillez être conscient des risques suivants associés à cette opération :\n1. Une fois le compte supprimé avec succès, vous ne pourrez plus vous connecter à votre compte actuel. La suppression du compte est une action permanente.\n2. Une fois le compte supprimé avec succès, vous ne pourrez plus récupérer aucune donnée du compte. Toutes les informations (y compris les chambres et les amis), la monnaie virtuelle, les cadeaux et les objets virtuels seront définitivement supprimés et ne pourront pas être restaurés.\n3. Délai de réflexion : si vous ne restaurez pas le compte, vous ne pourrez pas accéder à la page d'achat, à la page de retrait ou à toute autre page de l'application.\n4. Pendant la période de réflexion ou après la suppression du compte, la page de profil indiquera qu'il a été supprimé. Pour protéger votre compte contre toute recherche ou accès par d'autres personnes, vos informations personnelles seront supprimées des systèmes liés aux fonctions quotidiennes. Lorsque la suppression du compte implique la sécurité nationale, des procédures civiles ou pénales, ou la protection des droits et intérêts légitimes de tiers, l'équipe officielle se réserve le droit de rejeter la demande de suppression du compte de l'utilisateur.\nSi vous êtes certain de vouloir supprimer toutes les données personnelles de votre compte actuel, veuillez cliquer sur « Supprimer le compte ».",
|
||||
"accountDeletionNotice": "Avis de suppression de compte :",
|
||||
"thisUserHasBeenBlacklisted": "Cet utilisateur a été mis sur liste noire.",
|
||||
"trend": "Tendance",
|
||||
"like": "Comme",
|
||||
"more": "Plus",
|
||||
"discard": "Jeter",
|
||||
"catchFirstComment": "Attrapez le premier commentaire",
|
||||
"reply": "Répondre",
|
||||
"posting": "Publication",
|
||||
"multiple": "Plusieurs",
|
||||
"successfullyRemovedFromTheBlacklist": "Supprimé avec succès de la liste noire !",
|
||||
"successfullyAddedToTheBlacklist": "Ajout réussi à la liste noire !",
|
||||
"youAreCurrentlyCPRelationshipPleaseDissolve": "Vous êtes actuellement en relation CP.\nVeuillez d'abord le dissoudre.",
|
||||
"areYouSureToCancelBlacklist": "Etes-vous sûr d'annuler la liste noire ?",
|
||||
"areYouSureYouWantToBlockThisUser": "Êtes-vous sûr de vouloir bloquer cet utilisateur ?",
|
||||
"removeFromBlacklist": "Supprimer de la liste noire",
|
||||
"moveToBlacklist": "Passer à la liste noire",
|
||||
"userBlacklist": "Liste noire des utilisateurs",
|
||||
"specialEffectsManagement": "Gestion des effets spéciaux",
|
||||
"wishingYouHappinessEveryDay": "Je vous souhaite du bonheur chaque jour.",
|
||||
"newMessage": "Nouveau message",
|
||||
"createRoomSuccsess": "Salle créée avec succès !",
|
||||
"contactUs": "Contactez-nous",
|
||||
"systemAnnouncementTips1": "Se prémunir contre la fraude :",
|
||||
"systemAnnouncementTips": "Vérifiez les informations uniquement via les canaux officiels. Ne téléchargez jamais de logiciels tiers, ne partagez jamais de données personnelles et ne transférez jamais d’argent sur la base de demandes externes. Les identifiants officiels du personnel sont uniquement 10000, 10003 et 10086. Pour tout soupçon, arrêtez-vous et signalez-le via",
|
||||
"systemAnnouncement": "Annonce du système",
|
||||
"doNotClickUnfamiliarTips": "Ne cliquez pas sur des liens inconnus, car ils peuvent exposer vos informations personnelles. Ne partagez jamais vos informations d’identification ou de carte bancaire avec qui que ce soit.",
|
||||
"atTag": "@Étiquette",
|
||||
"sayHi2": "Dis bonjour",
|
||||
"roomBottomGreeting": "Salut...",
|
||||
"canSendMsgTips": "Les deux parties doivent se suivre avant de pouvoir envoyer des messages privés.",
|
||||
"msgSendRedEnvelopeTips": "*Des frais de service de 10 % seront facturés sur les enveloppes rouges et les destinataires ne recevront que 90 % de la valeur de l'enveloppe rouge. Le niveau de richesse de l'expéditeur doit être supérieur au niveau 10.",
|
||||
"reapply": "Réappliquer",
|
||||
"cancelRequest": "Annuler la demande",
|
||||
"pending": "En attente",
|
||||
"supporter": "Partisan",
|
||||
"coinsReceived": "Pièces reçues",
|
||||
"numberOfSign": "Numéro de signe : {1}",
|
||||
"hostWeeklyRank": "Classement hebdomadaire des hôtes",
|
||||
"supporterWeeklyRank": "Classement hebdomadaire des supporters",
|
||||
"memberList": "Liste des membres",
|
||||
"treasureChest": "Coffre au trésor",
|
||||
"applicationRecord": "Dossier de candidature",
|
||||
"appUpdateTip": "L'application a une nouvelle version ({1}). Souhaitez-vous le télécharger maintenant ?",
|
||||
"ownerIncomeCoins": "Revenu du propriétaire : pièces {1}",
|
||||
"game": "Jeu",
|
||||
"skip2": "Sauter",
|
||||
"skipCountdown": "Sauter {1}s",
|
||||
"coins4": "Pièces de monnaie",
|
||||
"weekStart": "Début de semaine",
|
||||
"forMoreRewardsPleaseCheckTheTaskCenter": "Pour plus de récompenses, veuillez consulter le centre de tâches",
|
||||
"kingQuuen": "Roi-Reine",
|
||||
"ramadan": "Ramadan",
|
||||
"updateNow": "Mettre à jour maintenant",
|
||||
"allGames": "Tous les jeux",
|
||||
"fishClass": "Classe de poisson",
|
||||
"greedyClass": "Classe gourmande",
|
||||
"raceSeries": "Série de courses",
|
||||
"slotsClass": "Classe de machines à sous",
|
||||
"others": "D'autres",
|
||||
"hotGames": "Jeux chauds",
|
||||
"chatBox": "Boîte de discussion",
|
||||
"termsOfServicePrivacyPolicyTips": "En continuant, vous acceptez les conditions d'utilisation et la politique de confidentialité",
|
||||
"and": "et",
|
||||
"pleaseSelectTheTypeContent": "Veuillez sélectionner le type de contenu offensant.",
|
||||
"illegalInformation": "Informations illégales",
|
||||
"inappropriateContent": "Contenu inapproprié",
|
||||
"personalAttack": "Attaque personnelle",
|
||||
"confirm": "Confirmer",
|
||||
"spam": "Pourriel",
|
||||
"countdownMinutes": "Minutes du compte à rebours :",
|
||||
"number2": "Numéro:",
|
||||
"fraud": "Fraude",
|
||||
"received": "Reçu",
|
||||
"currentProgress": "Progrès actuels",
|
||||
"currentStage": "Stade actuel : {1}",
|
||||
"roomReward2": "Récompense de salle : {1}",
|
||||
"roomReward": "Récompense de chambre",
|
||||
"expirationTime": "Délai d'expiration",
|
||||
"ownerSendTheRedEnvelope": "Le propriétaire a envoyé des pièces de récompense.",
|
||||
"rewardCoins": "Pièces de récompense : pièces {1}",
|
||||
"signInRewardReceived": "Connectez-vous avec succès. Récompense : {1}",
|
||||
"registerRewardReceived": "Récompense d'inscription reçue : {1}",
|
||||
"lastWeekProgress": "Les progrès de la semaine dernière",
|
||||
"redEnvelopeTips2": "*Si l'enveloppe rouge n'est pas réclamée dans le délai imparti, les pièces restantes seront restituées à l'utilisateur qui a envoyé l'enveloppe rouge.",
|
||||
"goToRecharge": "Allez recharger",
|
||||
"deleteAccount2": "Supprimer le compte ({1}s)",
|
||||
"areYouSureYouWantToDeleteYourAccount": "Êtes-vous sûr de vouloir supprimer votre compte ?",
|
||||
"insufhcientGoldsGoToRecharge": "Or insuffisant. Rechargez maintenant !",
|
||||
"coins2": "Pièces {1}",
|
||||
"remainingNumberTips": "Restant disponible : ({1}/{2})",
|
||||
"collectionTimeTips": "Heure de collecte : {1} ({2}/{3})",
|
||||
"sendARedEnvelope": "Envoyez une enveloppe rouge",
|
||||
"sendRedPackConfirmTips": "Êtes-vous sûr de vouloir envoyer le paquet rouge ?",
|
||||
"redEnvelopeSendingRecords": "Enveloppe rouge envoyant des enregistrements :",
|
||||
"redEnvelope": "Enveloppe rouge",
|
||||
"roomRedPacketGoldCoins": "Pièces d'or",
|
||||
"roomRedPacketQuantity": "Quantité",
|
||||
"roomRedPacketPickupTime": "Heure de ramassage",
|
||||
"roomRedPacketInMinutes": "En minutes {1}",
|
||||
"roomRedPacketClaimNow": "Réclamez maintenant",
|
||||
"roomRedPacketSendLuckyPack": "Envoyez un pack porte-bonheur",
|
||||
"roomRedPacketSending": "Envoi...",
|
||||
"roomRedPacketSendSuccess": "Pack porte-bonheur envoyé",
|
||||
"roomRedPacketComingSoon": "Bientôt disponible",
|
||||
"roomRedPacketRulesTitle": "Règles",
|
||||
"roomRedPacketRulesContent": "1. Vous pouvez dépenser des pièces pour envoyer un Lucky Pack. Si d'autres personnes ouvrent votre Lucky Pack, ils peuvent recevoir une quantité aléatoire de pièces.\n\n2. Deux heures après l'envoi d'un Lucky Pack, toutes les pièces non réclamées seront restituées dans votre portefeuille.\n\n3. Après avoir envoyé un Lucky Pack à l'échelle du serveur, une bannière Lucky Pack à l'échelle du serveur sera déclenchée, vous attirant davantage d'attention.\n\n4. Vous pouvez envoyer des Lucky Packs à la salle %1$s par jour.\n\n5. Les Lucky Packs envoyés par des utilisateurs en dehors de votre région ne peuvent pas être réclamés.",
|
||||
"roomRedPacketHistoryTitle": "Historique de livraison des packs chanceux",
|
||||
"roomRedPacketHistoryItemTitle": "Sac porte-bonheur (enveloppes rouges {1})",
|
||||
"roomRedPacketHistoryStatusSent": "Envoyé",
|
||||
"roomRedPacketHistoryStatusFinished": "Entièrement réclamé",
|
||||
"roomRedPacketHistoryStatusPending": "En attente",
|
||||
"roomRedPacketHistoryStatusReturned": "Retourné",
|
||||
"roomRedPacketHistoryRefund": "Remboursement : {1}",
|
||||
"roomRedPacketClaimRecordsTitle": "Dossiers de réclamation",
|
||||
"roomRedPacketChatMessage": "Je t'ai envoyé une enveloppe rouge, ouvre-la tout de suite !",
|
||||
"roomRedPacketReceivedMessage": "Réclamez des pièces {1} dans le pack chanceux {2}",
|
||||
"roomRedPacketOpenSubtitle": "Le Lucky Pack est arrivé",
|
||||
"roomRedPacketOpenYouGot": "Vous obtenez",
|
||||
"roomRedPacketClaimSuccessTips": "Les pièces ont été envoyées dans votre portefeuille.",
|
||||
"roomRedPacketOpenSoldOut": "Le Lucky Pack est déjà épuisé.",
|
||||
"roomRedPacketOpenExpired": "Le Lucky Pack a expiré.",
|
||||
"roomRedPacketRegionNotMatch": "Seuls les utilisateurs de la même région linguistique peuvent réclamer ce pack chanceux.",
|
||||
"roomRedPacketNotStarted": "Le pack porte-bonheur n’est pas encore prêt.",
|
||||
"roomRedPacketDailyLimitExceeded": "La limite d'envoi d'aujourd'hui a été atteinte.",
|
||||
"roomRedPacketInsufficientBalance": "Pièces d'or insuffisantes.",
|
||||
"roomRedPacketPresenceRequired": "Veuillez rester dans la chambre pour utiliser les packs porte-bonheur.",
|
||||
"roomRedPacketNetworkError": "Impossible de traiter le pack porte-bonheur pour le moment.",
|
||||
"roomRedPacketAlreadyClaimed": "Vous avez déjà réclamé ce pack porte-bonheur.",
|
||||
"roomRedPacketOpenWaiting": "Peut être ouvert en quelques minutes {1}.",
|
||||
"roomRedPacketOpenWaitingCountdown": "Disponible pour ramassage après {1}",
|
||||
"roomRedPacketOkay": "D'accord",
|
||||
"roomRedPacketFloatingTitle": "{1} a envoyé une enveloppe rouge",
|
||||
"roomRedPacketFloatingSubtitle": "J'ai envoyé une enveloppe rouge",
|
||||
"redEnvelopeRecTips2": "Les enveloppes rouges ont toutes été réclamées.",
|
||||
"redEnvelopeRecTips3": "Le délai de collecte des enveloppes rouges est expiré !",
|
||||
"openTheTreasureChest": "Ouvrez le coffre au trésor",
|
||||
"redEnvelopeRecTips1": "Les pièces gagnées ont été déposées dans votre portefeuille.",
|
||||
"redEnvelopeTips1": "Pièces :",
|
||||
"roomTools": "Outils de la salle :",
|
||||
"entertainment": "Divertissement :",
|
||||
"reportSucc": "Rapport réussi",
|
||||
"pornography": "Pornographie",
|
||||
"reportInputTips": "Veuillez décrire le problème de manière aussi détaillée que possible afin que nous puissions le comprendre et le résoudre.",
|
||||
"cancel": "Annuler",
|
||||
"join": "Rejoindre",
|
||||
"items": "Articles",
|
||||
"vistors": "Visiteurs",
|
||||
"fans": "Ventilateurs",
|
||||
"balanceNotEnough": "Solde de pièces d’or insuffisant. Voulez-vous aller recharger ?",
|
||||
"skip": "Sauter {1}",
|
||||
"letGoToWatch": "Allons regarder !",
|
||||
"launchedARocket": "a lancé une fusée",
|
||||
"sendUserId": "Envoyer l'ID utilisateur : {1}",
|
||||
"giveUpIdentity": "Abandonner son identité",
|
||||
"leaveRoomIdentityTips": "Êtes-vous sûr de vouloir abandonner l'identité de la salle ?",
|
||||
"joinMemberTips2": "Voulez-vous confirmer votre adhésion à la salle en tant que membre ?",
|
||||
"sureUnfollowThisRoom": "Vous êtes sûr de ne plus suivre cette salle ?",
|
||||
"welcomeMessage": "Bienvenue sur notre application, {name} !",
|
||||
"settings": "Paramètres",
|
||||
"account": "Compte",
|
||||
"common": "Commun",
|
||||
"delete": "Supprimer",
|
||||
"copy": "Copier",
|
||||
"bio": "Biographie",
|
||||
"useCoupontips": "Êtes-vous sûr de vouloir utiliser le coupon ?",
|
||||
"searchUserId": "Rechercher l'ID de l'utilisateur",
|
||||
"sendUser": "Envoyer l'utilisateur",
|
||||
"hobby": "Passe-temps",
|
||||
"sendCoupontips": "Êtes-vous sûr de vouloir envoyer ce coupon à cet utilisateur ?",
|
||||
"youDontHaveAnyCouponsYet": "Vous n'avez pas encore de coupons.",
|
||||
"recall": "Rappel",
|
||||
"youHaventFollowed": "Vous n'avez suivi aucune pièce",
|
||||
"deleteFromMyDevice": "Supprimer de mon appareil",
|
||||
"deleteOnAllDevices": "Supprimer sur tous les appareils",
|
||||
"messageHasBeenRecalled": "Ce message a été rappelé",
|
||||
"recallThisMessage": "Vous vous souvenez de ce message ?",
|
||||
"language": "Langue",
|
||||
"feedback": "Commentaires",
|
||||
"signedin": "Connecté",
|
||||
"receiveSucc": "Revendiqué avec succès",
|
||||
"about": "À propos",
|
||||
"aboutUs": "À propos de nous",
|
||||
"theme": "Thème",
|
||||
"wealthLevel": "Niveau de richesse",
|
||||
"userLevel": "Niveau utilisateur",
|
||||
"goToUpload": "Aller au téléchargement",
|
||||
"logout": "Déconnexion",
|
||||
"luck": "Chance",
|
||||
"level": "Niveau",
|
||||
"themeGoToUploadTips": "1. L'examen sera terminé dans les 24 heures suivant la réussite du téléchargement.\n2. Toutes les pièces seront restituées si l'examen échoue.",
|
||||
"pleaseUploadAccordingToExample": "Veuillez télécharger selon l'exemple fourni.",
|
||||
"home": "Accueil",
|
||||
"explore": "Explorer",
|
||||
"me": "Moi",
|
||||
"socialPrivilege": "Privilège social",
|
||||
"information": "Informations",
|
||||
"myPhoto": "Ma photo",
|
||||
"areYouSureYouWantToSpend3": "*Si l'autre partie refuse l'invitation CP, vos pièces seront restituées dans votre portefeuille.",
|
||||
"areYouSureYouWantToSpend": "Êtes-vous sûr de vouloir dépenser",
|
||||
"areYouSureYouWantToSpend2": "envoyer une invitation CP à cet utilisateur ?",
|
||||
"underReview": "En cours de révision",
|
||||
"doYouWantToDeleteIt": "Voulez-vous le supprimer ?",
|
||||
"chooseFromAblum": "Choisissez parmi l'album",
|
||||
"spaceBackground": "Fond d'espace",
|
||||
"editProfile": "Modifier le profil",
|
||||
"sendTheCpRequest": "Envoyer la demande de CP",
|
||||
"addCp": "Ajouter un PC",
|
||||
"partWays": "Chemins partiels",
|
||||
"reconcile": "Réconcilier",
|
||||
"separated": "Séparé",
|
||||
"areYouSureYouWantToSpend5": "{1} vous a avoué ce sentiment ; si vous acceptez, vous deviendrez un couple.",
|
||||
"areYouSureYouWantToSpend6": "{1} souhaite se remettre avec vous. Si vous décidez de procéder à une réconciliation, toutes vos données précédentes seront restaurées.",
|
||||
"reconcileInvitationTips": "*Si l'autre partie refuse l'invitation CP, vos pièces seront restituées dans votre portefeuille.",
|
||||
"reconcileInvitation": "Réconcilier l'invitation",
|
||||
"areYouSureYouWantToSpend4": "envoyer une invitation de rapprochement à cet utilisateur ?",
|
||||
"partWaysTips": "*Si l'un des partenaires d'un couple choisit de se séparer, il y aura un délai de réflexion de 7 jours. Pendant ce temps, les deux parties peuvent choisir de se réconcilier et toutes les données seront restaurées lors du rapprochement. Si aucun rapprochement n'est choisi à l'issue du délai de 7 jours, les données du couple seront effacées.",
|
||||
"areYouSureYouWantToPartWaysWithYourCP": "Êtes-vous sûr de vouloir vous séparer de votre CP ?",
|
||||
"timeSpentTogether": "Temps passé ensemble : jours {1}",
|
||||
"firstDay": "Premier jour : {1}",
|
||||
"numberOfMyCPs": "Nombre de mes CP :({1}/{2})",
|
||||
"props": "Accessoires",
|
||||
"win": "Gagner",
|
||||
"dice": "Dés",
|
||||
"rps": "RPS",
|
||||
"operationFail": "L'opération a échoué.",
|
||||
"enterRoomFailedRetry": "Impossible d'entrer dans la pièce. Veuillez réessayer.",
|
||||
"voiceConnectionFailedRetry": "La connexion vocale a échoué. Veuillez réessayer.",
|
||||
"likedYourComment": "J'ai aimé votre commentaire.",
|
||||
"doYouWantToKeepTheDraft": "Voulez-vous conserver le brouillon ?",
|
||||
"operationsAreTooFrequent": "Les opérations sont trop fréquentes",
|
||||
"luckNumber": "Numéro de chance",
|
||||
"relationShip": "Relation",
|
||||
"couple": "Couple {1} :",
|
||||
"couple2": "couple",
|
||||
"reject": "Rejeter",
|
||||
"accept": "Accepter",
|
||||
"noMatchedCP": "Aucun CP correspondant",
|
||||
"inviteYouToBecomeBD": "Vous invite à devenir BD.",
|
||||
"adminInviteRechargeAgent": "Vous invite à devenir agent Recharge.",
|
||||
"confirmAcceptTheInvitation": "Confirmer pour accepter l'invitation ?",
|
||||
"confirmDeclineTheInvitation": "Confirmer le refus de l'invitation ?",
|
||||
"host": "Hôte",
|
||||
"following": "Suite",
|
||||
"agent": "Agence",
|
||||
"approved": "Approuvé",
|
||||
"approvedWithinOneMinute": "Approuvé et affiché dans la minute suivant la soumission",
|
||||
"onlineUsers": "Utilisateurs en ligne ({1}/{2}) :",
|
||||
"applyToJoin": "Postuler pour rejoindre",
|
||||
"supporterList": "Liste des supporters",
|
||||
"hostList": "Liste des hôtes",
|
||||
"upToAdmins": "Jusqu'à Administrateurs {1}",
|
||||
"upToMembers": "Jusqu'à membres {1}",
|
||||
"levelPrivileges": "Privilèges de niveau",
|
||||
"ra": "RA",
|
||||
"roomAnnouncement": "Annonce de la salle",
|
||||
"help": "Aide",
|
||||
"rejected": "Rejeté",
|
||||
"boxContributeTips": "Des investissements ont déjà été réalisés aujourd'hui. S’il vous plaît, n’investissez plus.",
|
||||
"bd": "BD",
|
||||
"coupon": "Coupon",
|
||||
"search": "Rechercher",
|
||||
"get": "Obtenir",
|
||||
"inRocket": "Dans la fusée",
|
||||
"roomRocketHelpTips": "1. Envoyer des cadeaux dans la pièce augmente l’énergie de la fusée. *1 cadeau de pièce d'or = 1 point d'énergie de fusée ; Les cadeaux porte-bonheur augmentent l'énergie de la fusée de 4 % de la valeur de la pièce d'or du cadeau.\n2. Une fois l’énergie de la fusée complètement chargée, la pièce peut lancer la fusée. Les récompenses seront automatiquement distribuées après le lancement.\n3. Différents niveaux de fusée offrent différentes récompenses.\n4. Lorsque la fusée décolle, tous les utilisateurs présents dans la pièce peuvent réclamer la récompense de la fusée.\n5. L'énergie de la fusée est réinitialisée à minuit chaque jour.",
|
||||
"couponRecord": "Historique d'utilisation du coupon",
|
||||
"inRoom": "Dans la chambre",
|
||||
"searchCouponHint": "Rechercher un coupon",
|
||||
"giftCounter": "Compteur de cadeaux",
|
||||
"bDLeaderInviteYouToBecomeBDLeader": "Vous invite à devenir BDLeader",
|
||||
"wins": "gagne",
|
||||
"inviteYouToBecomeHost": "Vous invite à devenir hôte.",
|
||||
"friends": "Amis",
|
||||
"deleteConversationTips": "Êtes-vous sûr de vouloir supprimer l'historique des discussions avec cet utilisateur ?",
|
||||
"propMessagePrompt": "Invite de message d'accessoire",
|
||||
"inputUserId": "Entrez l'ID utilisateur",
|
||||
"fromLuckyGifts": "Des cadeaux chanceux/magiques",
|
||||
"receive": "Recevoir",
|
||||
"checkInSuccessful": "Enregistrement réussi",
|
||||
"sginTips": "Vous recevrez une récompense lors de votre première connexion chaque jour. Si vous interrompez votre séquence de connexion, la récompense sera recalculée à partir du premier jour de votre nouvelle connexion.",
|
||||
"popular": "Populaire",
|
||||
"recommend": "Recommander",
|
||||
"follow": "Suivre",
|
||||
"history": "Histoire",
|
||||
"hotRooms": "Chambres chaudes",
|
||||
"viewMore": "Voir plus",
|
||||
"noData": "Aucune donnée",
|
||||
"users": "Utilisateurs",
|
||||
"rooms": "Chambres",
|
||||
"coins": "pièces de monnaie",
|
||||
"unread": "Non lu",
|
||||
"read": "Lire",
|
||||
"image": "[image]",
|
||||
"video": "[Vidéo]",
|
||||
"sound": "[Son]",
|
||||
"gift2": "[Cadeau]",
|
||||
"clickHereToStartChatting": "Dis quelque chose.....",
|
||||
"receivedAMessage": "[Reçu un message]",
|
||||
"confirmSwitchMicModelTips": "Confirmez-vous le passage en mode assis ?",
|
||||
"number": "Numéro",
|
||||
"album": "Album",
|
||||
"camera": "Appareil photo",
|
||||
"system": "Système",
|
||||
"notifcation": "Notification",
|
||||
"inviteYouToBecomeAgent": "Vous invite à devenir une agence.",
|
||||
"myMusic": "Ma musique",
|
||||
"add": "Ajouter",
|
||||
"pullToLoadMore": "Tirez pour charger davantage",
|
||||
"loadingFailedClickToRetry": "Échec du chargement, cliquez pour réessayer",
|
||||
"releaseToLoadMore": "Relâchez pour charger plus",
|
||||
"haveMyLimits": "---J'ai mes limites.---",
|
||||
"music": "Musique",
|
||||
"free": "Gratuit",
|
||||
"charm": "Charme cadeau",
|
||||
"start": "Commencer",
|
||||
"stop": "Arrêter",
|
||||
"chats": "Discussions",
|
||||
"refuse": "Refuser",
|
||||
"agree": "D'accord",
|
||||
"thisFeatureIsCurrentlyUnavailable": "Cette fonctionnalité est actuellement indisponible.",
|
||||
"pleaseSelectTheRecipient": "Veuillez sélectionner le destinataire.",
|
||||
"searchMemberIdHint": "Veuillez saisir l'identifiant du membre",
|
||||
"unclaimedRedEnvelopes": "Les enveloppes rouges non réclamées sont remboursées en 24 heures.",
|
||||
"redEnvelopeNotYetClaimed": "Enveloppe rouge pas encore réclamée.",
|
||||
"redEnvelopeAmount": "Montant de l'enveloppe rouge : pièces {1}",
|
||||
"sentARedEnvelope": "envoyé une enveloppe rouge.",
|
||||
"theRedEnvelopeHasExpired": "L'enveloppe rouge est expirée.",
|
||||
"joinRequest": "Demande d'adhésion",
|
||||
"scrollToTheBottom": "Faites défiler vers le bas",
|
||||
"gameRules": "Règles du jeu :",
|
||||
"charmGameRulesTips": "Allumez le tableau de bord pour afficher les pièces cadeaux reçues de tous les utilisateurs sur le micro. 1 pièce = 1 score (Cadeau porte-bonheur : 1 pièce = 0,04 score).",
|
||||
"inputYourOldPassword": "Saisissez votre ancien mot de passe",
|
||||
"enterYourOldPassword": "Entrez votre ancien mot de passe",
|
||||
"setYourPassword": "Définissez votre mot de passe",
|
||||
"enterYourNewPassword": "Entrez votre nouveau mot de passe",
|
||||
"confirmYourPassword": "Confirmez votre mot de passe",
|
||||
"theTwoPasswordsDoNotMatch": "Les deux mots de passe ne correspondent pas.",
|
||||
"resetLoginPasswordtTips2": "Le mot de passe doit comporter entre 8 et 16 caractères et doit être une combinaison de lettres et de chiffres anglais majuscules et minuscules (pas seulement des chiffres).",
|
||||
"resetLoginPassword": "Réinitialiser le mot de passe de connexion",
|
||||
"resetLoginPasswordtTips1": "Connectez-vous avec votre identifiant utilisateur ou votre identifiant personnalisé. Il est plus sûr de se connecter avec un compte Yumi.",
|
||||
"localMusic": "Musique locale",
|
||||
"setLoginPassword": "Définir le mot de passe de connexion",
|
||||
"confirmSwitchMicThemeTips": "Confirmez-vous le changement de style de siège ?",
|
||||
"micTheme": "Thème du micro",
|
||||
"classicMic": "Micro classique {1}",
|
||||
"yesterday": "Hier {1}",
|
||||
"monday": "Lundi {1}",
|
||||
"tuesday": "Mardi {1}",
|
||||
"wednesday": "Mercredi {1}",
|
||||
"thursday": "Jeudi {1}",
|
||||
"friday": "Vendredi {1}",
|
||||
"saturday": "Samedi {1}",
|
||||
"sunday": "Dimanche {1}",
|
||||
"acceptedYour": "{1} a accepté votre",
|
||||
"youAccepted": "Vous avez accepté {1}",
|
||||
"openRedPackDialogTip": "L'enveloppe rouge",
|
||||
"micManagement": "Gestion des micros",
|
||||
"bdCenter": "Centre BD",
|
||||
"rechargeAgency": "Agence de recharge",
|
||||
"adminCenter": "Centre d'administration",
|
||||
"managerCenter": "Centre des gestionnaires",
|
||||
"goldList": "Liste d'or",
|
||||
"followList": "Suivre la liste",
|
||||
"fansList": "Liste des fans",
|
||||
"daily": "Quotidiennement",
|
||||
"female": "Femme",
|
||||
"male": "Mâle",
|
||||
"identity": "Identité",
|
||||
"adjust": "Ajuster",
|
||||
"warning": "Avertissement",
|
||||
"screenshotTips": "Capture d'écran (jusqu'à 3)",
|
||||
"roomNotice": "Avis de chambre",
|
||||
"roomTheme": "Thème de la chambre",
|
||||
"description": "Descriptif :",
|
||||
"inputDesHint": "Veuillez décrire le problème de manière aussi détaillée que possible afin que nous puissions le comprendre et le résoudre.",
|
||||
"roomProfilePicture": "Photo de profil de la chambre",
|
||||
"userProfilePicture": "Photo de profil utilisateur",
|
||||
"userName": "Nom d'utilisateur",
|
||||
"pleaseSelectTheTypeToProcess": "Veuillez sélectionner le type à traiter :",
|
||||
"roomEditing": "Modification de la salle",
|
||||
"setAccount": "Définir un compte",
|
||||
"userEditing": "Modification par l'utilisateur",
|
||||
"enterTheUserId": "Entrez l'ID utilisateur",
|
||||
"enterTheRoomId": "Entrez l'identifiant de la pièce",
|
||||
"deleteAccount": "Supprimer le compte",
|
||||
"becomeAgent": "Devenez agent",
|
||||
"enterNickname": "Entrez le pseudo",
|
||||
"enterInvitationCode": "entrez le code d'invitation",
|
||||
"selectYourCountry": "Sélectionnez votre pays",
|
||||
"inviteCode": "Code d'invitation",
|
||||
"magic": "Magie",
|
||||
"luckGiftSpecialEffects": "Effets d'animation de cadeaux chanceux",
|
||||
"theVideoSizeCannotExceed": "La taille de la vidéo ne peut pas dépasser 50 M",
|
||||
"weekly": "Hebdomadaire",
|
||||
"customizedGiftRulesContent": "Comment recevoir mon cadeau personnalisé :\n1. Déterminez si l'utilisateur est éligible pour recevoir un cadeau personnalisé en fonction de son niveau de richesse actuel.\n(1) Lorsque le niveau de richesse de l'utilisateur est ≥35 : l'utilisateur peut télécharger une fois une vidéo pour le cadeau personnalisé. Nous utiliserons la photo de profil actuelle de l'utilisateur comme image de cadeau et la vidéo fournie comme effet de cadeau sur « Personnalisé ». La production prendra un certain temps et nous vous informerons dès qu'elle sera disponible en magasin.\n(2) Lorsque le niveau de richesse de l'utilisateur est ≥45 : l'utilisateur peut télécharger une fois une vidéo pour le cadeau personnalisé. Nous utiliserons la photo de profil actuelle de l'utilisateur comme image de cadeau et la vidéo fournie comme effet de cadeau sur « Personnalisé ». La production prendra un certain temps et nous vous informerons dès qu'elle sera disponible en magasin.\n2.Vous pouvez participer à des activités spécifiques sur l'application et, après avoir rempli les critères, nous contacter via la section « Message » → « Contactez-nous ». Fournissez des captures d'écran de l'activité et de la vidéo que vous souhaitez utiliser pour le cadeau personnalisé. Nous utiliserons votre photo de profil actuelle comme image de cadeau et la vidéo fournie comme effet de cadeau, puis nous la listerons sous « Personnalisé ». La production prendra un certain temps et nous vous informerons dès qu'elle sera disponible en magasin.\nPériode de validité du cadeau personnalisé et comment la prolonger :\nLes cadeaux personnalisés exclusifs seront disponibles en rayon pour une durée de validité de 30 jours. Pour permettre aux utilisateurs influents et inspirants de continuer à profiter de cette nouvelle expérience et de mettre en valeur leur style personnel, les utilisateurs possédant des cadeaux personnalisés peuvent prolonger la durée de conservation de tous leurs cadeaux personnalisés de 30 jours en rechargeant 500 $ chaque mois.",
|
||||
"customizedGiftRules": "Règles de cadeaux personnalisées",
|
||||
"rulesUpload": "Règles et téléchargement",
|
||||
"monthly": "Mensuel",
|
||||
"message": "Message",
|
||||
"clearCache": "Vider le cache",
|
||||
"customized": "Personnalisé",
|
||||
"searchInputHint": "Entrez le numéro de compte/de chambre",
|
||||
"kickRoomTips": "Vous avez été expulsé de la pièce.",
|
||||
"joinRoomTips": "J'ai rejoint la salle !",
|
||||
"roomSetting": "Cadre de la pièce",
|
||||
"roomDetails": "Détails de la chambre",
|
||||
"systemRoomTips": "Soyez poli et respectueux. Tout contenu pornographique ou vulgaire est strictement interdit sur yumi. Une fois découvert, le compte sera banni définitivement. Veuillez respecter consciemment les réglementations de la plateforme Yumi.",
|
||||
"copiedToClipboard": "Copié dans le presse-papiers",
|
||||
"recharge": "Recharger",
|
||||
"receivedFromALuckyGift": "Reçu d'un cadeau chanceux/magique.",
|
||||
"followedYou": "Je t'ai suivi",
|
||||
"agentCenter": "Centre d'agence",
|
||||
"areYouSureYouWantToClearLocalCache": "Êtes-vous sûr de vouloir vider le cache local ?",
|
||||
"clearMessage": "Effacer les messages à l'écran",
|
||||
"report": "Rapport",
|
||||
"coins3": "Pièces de monnaie",
|
||||
"giftSpecialEffects": "Effets spéciaux cadeaux",
|
||||
"basicFeatures": "Fonctionnalités de base",
|
||||
"task": "Tâche",
|
||||
"importantReminder": "Rappel important",
|
||||
"entryVehicleAnimation": "Animation du véhicule d'entrée",
|
||||
"floatingAnimationInGlobal": "Animation flottante en global",
|
||||
"broadcast": "Diffusion",
|
||||
"broadcastDisplay": "Affichage de diffusion",
|
||||
"broadcastAllPlaces": "Tous les lieux",
|
||||
"broadcastRoomOnly": "Dans la chambre",
|
||||
"broadcastOff": "Fermer la diffusion",
|
||||
"entryVehicleAnimation2": "Les utilisateurs disposant de privilèges VIP4 ou supérieurs peuvent utiliser cette fonction pour désactiver les animations des véhicules.",
|
||||
"dailyTasks": "Tâches quotidiennes",
|
||||
"exclusiveForNewcomers": "Exclusif pour les nouveaux arrivants",
|
||||
"resetsDailyAtMidnight": "Se réinitialise quotidiennement à minuit",
|
||||
"limitedToOneTime": "Limité à une seule fois",
|
||||
"useMicrophoneForOneMin": "Utilisez le microphone pendant 1 min",
|
||||
"taskRequirement": "Exigence",
|
||||
"taskProgress": "Progrès",
|
||||
"rewardClaimedSuccessfully": "Récompense réclamée avec succès",
|
||||
"rewardClaimFailed": "La demande de récompense a échoué",
|
||||
"enterRoomConfirmTips": "Êtes-vous sûr de vouloir entrer dans la pièce ?",
|
||||
"followSucc": "Suivi avec succès",
|
||||
"goldListort": "Liste d'or",
|
||||
"rechargeList": "Liste de recharge",
|
||||
"edit": "Modifier",
|
||||
"swipeLeftOnTheFloatingScreenAreaToQuicklyCloseIt": "*Conseil : faites glisser votre doigt vers la gauche sur la zone de l'écran flottant pour la fermer rapidement.",
|
||||
"enterThisVoiceChatRoom": "Entrer dans cette salle de chat vocal ?",
|
||||
"go": "Allez",
|
||||
"done": "Terminé",
|
||||
"improvementTasks": "Tâches d'amélioration",
|
||||
"save": "Enregistrer",
|
||||
"kickPrevention": "Prévention des coups de pied",
|
||||
"freeChatSpeak": "Chat et conversation gratuits",
|
||||
"nickName": "Surnom",
|
||||
"gender": "Sexe",
|
||||
"unFollow": "Ne plus suivre",
|
||||
"days": "Jours",
|
||||
"permanent": "Permanent",
|
||||
"country": "Pays",
|
||||
"birthday": "Anniversaire",
|
||||
"man": "Homme",
|
||||
"woman": "Femme",
|
||||
"apple": "Pomme",
|
||||
"google": "Google",
|
||||
"startVoiceParty": "Commencez une soirée vocale !",
|
||||
"enterRoomTips": "{1} Entrez dans la pièce",
|
||||
"roomName": "Nom de la pièce",
|
||||
"roomMember": "Membre de la salle",
|
||||
"pleaseSelectYourCountry": "Veuillez sélectionner votre pays.",
|
||||
"pleaseSelectYourGender": "Veuillez sélectionner votre sexe.",
|
||||
"pleaseEnterNickname": "Veuillez entrer un pseudo.",
|
||||
"countryRegion": "Pays et région",
|
||||
"dateOfBirth": "Date de naissance",
|
||||
"mute": "Muet",
|
||||
"exit": "Quitter",
|
||||
"mysteriousInvisibility": "Invisibilité mystérieuse",
|
||||
"antiBlock": "Anti-blocage",
|
||||
"privateChat": "Chat privé",
|
||||
"storeDiscount": "Remise en magasin {1} de réduction",
|
||||
"membershipFreeChatSpeak": "Chat et conversation sans abonnement",
|
||||
"priorityRoomSorting": "Tri prioritaire des chambres",
|
||||
"userColoredID": "ID coloré de l'utilisateur",
|
||||
"pleaseSelectaItem": "Veuillez sélectionner un article",
|
||||
"areYouRureRoRecharge": "Etes-vous sûr de vouloir recharger ?",
|
||||
"mInimize": "Garder",
|
||||
"everyone": "Tout le monde",
|
||||
"shop": "Boutique",
|
||||
"roomOwner": "Propriétaire de la chambre",
|
||||
"dailyTaskRewardBonus": "Bonus de récompense de tâche quotidienne ({1} XP)",
|
||||
"userLevelXPBoost": "Boost XP au niveau utilisateur ({1} XP)",
|
||||
"andAboveUsers": "Utilisateurs {1} et supérieurs",
|
||||
"privileges": "Privilèges {1}",
|
||||
"basicPermissions": "Autorisations de base",
|
||||
"hostCenter": "Centre d'accueil",
|
||||
"allOnMicrophone": "Tout au micro",
|
||||
"usersOnMicrophone": "Utilisateurs au micro",
|
||||
"allInTheRoom": "Tous dans la chambre",
|
||||
"send": "Envoyer",
|
||||
"crop": "Recadrer",
|
||||
"goToUpgrade": "Aller à la mise à niveau",
|
||||
"exclusiveEmojiWillBeReleasedAfterBecoming": "Des emoji exclusifs seront publiés après être devenus",
|
||||
"preventBeingBlocked": "Empêcher d'être bloqué",
|
||||
"enableRankIncognitoMode": "Activer le mode navigation privée de classement",
|
||||
"avoidBeingKicked": "Évitez d'être frappé",
|
||||
"finish": "Terminer",
|
||||
"takeTheMic": "Prends le micro",
|
||||
"openTheMic": "Ouvre le micro",
|
||||
"muteTheMic": "Couper le micro",
|
||||
"unlockTheMic": "Déverrouillez le micro",
|
||||
"leavelTheMic": "Laisse le micro",
|
||||
"lockTheMic": "Verrouillez le micro",
|
||||
"removeTheMic": "Retirez le micro",
|
||||
"inviteToTheMicrophone": "Inviter au micro",
|
||||
"openUserProfleCard": "Ouvrir la fiche de profil utilisateur",
|
||||
"obtain": "obtenir",
|
||||
"win2": "Gagnez {1}",
|
||||
"backTheRoom": "Arrière-salle",
|
||||
"toConsume": "Consommer",
|
||||
"howToUpgrade": "Comment mettre à niveau ?",
|
||||
"spendCoinsToGainExperiencePoints": "Dépensez des pièces pour gagner des points d'expérience",
|
||||
"higherLevelFancierAvatarFrame": "Cadre de badges/avatar de niveau supérieur et plus sophistiqué",
|
||||
"all": "Tout",
|
||||
"gift": "Cadeau",
|
||||
"chat": "Discuter",
|
||||
"owner": "Propriétaire",
|
||||
"store": "Magasin",
|
||||
"admin": "Administrateur",
|
||||
"member": "Membre",
|
||||
"guest": "Invité",
|
||||
"submit": "Soumettre",
|
||||
"claim": "Réclamation",
|
||||
"claimed": "Réclamé",
|
||||
"complete": "Terminé",
|
||||
"shareTo": "Partager avec",
|
||||
"copyLink": "Copier le lien",
|
||||
"faceBook": "Facebook",
|
||||
"whatsApp": "WhatsApp",
|
||||
"snapChat": "Snapchat",
|
||||
"taskNamePersonalGameConsume": "Dépenses de jeu",
|
||||
"taskNameRoomNewMember": "Nouveaux membres de la salle",
|
||||
"taskNameRoomOwnerSendRedPacket": "Le propriétaire de la salle envoie un paquet rouge",
|
||||
"taskNameRoomOwnerSendGiftUser": "Le propriétaire de la chambre envoie un cadeau",
|
||||
"taskNameRoomMicUser120Min": "Membres avec plus de 120 minutes au micro",
|
||||
"taskNameRoomMicUser60Min": "Membres avec plus de 60 minutes au micro",
|
||||
"taskNameRoomMicUser30Min": "Membres avec plus de 30 minutes au micro",
|
||||
"taskNamePersonalSendGift": "Envoyer un cadeau à l'utilisateur",
|
||||
"taskNameRoomOnlineUserCount": "Chambre membres en ligne",
|
||||
"taskNameRoomOwnerInviteMic": "Inviter un membre sur Mic",
|
||||
"taskNameRoomUserSendGiftGold": "Pièces offertes par les membres",
|
||||
"taskNameRoomOwnerSendGiftGold": "Pièces offertes par le propriétaire de la chambre",
|
||||
"taskNamePersonalMagicGiftGold": "Pièces gagnées en envoyant des cadeaux magiques",
|
||||
"taskNamePersonalLuckyGiftGold": "Pièces gagnées en envoyant des cadeaux chanceux",
|
||||
"taskNameRoomOwnerMicTime": "Le propriétaire de la salle passe au micro dans la pièce",
|
||||
"taskNamePersonalActiveInRoom": "Soyez actif dans les pièces des autres",
|
||||
"taskNamePersonalMicInRoom": "Allez au micro",
|
||||
"dailyCoinBonanzaRulesDetail": "1. Les tâches personnelles quotidiennes et les tâches quotidiennes du propriétaire de la salle peuvent être effectuées une fois par utilisateur et par jour. Les tâches sont réinitialisées à 00:00:00, heure saoudienne.\n2. Les tâches personnelles quotidiennes ne peuvent être accomplies que dans la chambre des autres ; Les tâches du propriétaire de la salle ne peuvent être effectuées que dans votre propre chambre.\n3. Les tâches d'offre de cadeaux ne comptent que les cadeaux envoyés dans les salles, pas les cadeaux envoyés via les flux.\n4. Si plusieurs comptes sont créés en utilisant le même appareil ou la même carte SIM, les récompenses pour toutes les tâches ne peuvent être réclamées qu'une seule fois.",
|
||||
"dailyCoinBonanzaRules": "Règles quotidiennes du bonus de pièces",
|
||||
"roomOwnerTasks": "Tâches du propriétaire de la salle",
|
||||
"personalTasks": "Tâches personnelles",
|
||||
"noPromptsToday": "Aucune tâche aujourd'hui.",
|
||||
"getPaidToRefer": "Soyez payé pour parrainer",
|
||||
"membershipFee": "Frais d'adhésion",
|
||||
"membershipFeeTips1": "Veuillez fixer les frais d'adhésion pour votre chambre. Les utilisateurs peuvent rejoindre votre salle en payant les frais.",
|
||||
"membershipFeeTips2": "Les ors requis pour que l'utilisateur devienne membre de la salle. Le propriétaire de la salle recevra 50% des ors.",
|
||||
"freePrice": "Frais : 0-10 000",
|
||||
"touristsSendText": "Un touriste envoie un SMS",
|
||||
"touristsTakeToTheMic": "Les touristes prennent le micro",
|
||||
"theMembershipFee": "La cotisation",
|
||||
"theModificationsMade": "Les modifications apportées cette fois ne seront pas enregistrées après la sortie",
|
||||
"viewFrame": "Cadre de vue",
|
||||
"enterRoomName": "Entrez le nom de la salle",
|
||||
"headdress": "Cadres",
|
||||
"mountains": "Véhicules",
|
||||
"purchaseIsSuccessful": "L'achat est réussi",
|
||||
"buy": "Acheter",
|
||||
"followed": "Suivi",
|
||||
"follow2": "Suivre : {1}",
|
||||
"fans2": "Ventilateurs : {1}",
|
||||
"vistors2": "Visiteurs :{1}",
|
||||
"personal2": "Personnel :",
|
||||
"conntinue": "Continuer",
|
||||
"confirmBuyTips": "Êtes-vous sûr de vouloir acheter ?",
|
||||
"purchase": "Acheter",
|
||||
"setRoomPassword": "Définir le mot de passe de la salle",
|
||||
"inputRoomPassword": "Saisir le mot de passe de la salle",
|
||||
"enter": "Entrez",
|
||||
"deleteCommentTips": "Êtes-vous sûr de vouloir supprimer ce commentaire ?",
|
||||
"deleteSuccessful": "Suppression réussie !",
|
||||
"itemsLeft": "Objets restants",
|
||||
"password": "Mot de passe",
|
||||
"replySucc": "Réponse réussie",
|
||||
"comment": "Commentaire",
|
||||
"showMore": "Afficher plus",
|
||||
"showLess": "Afficher moins",
|
||||
"enterPassword": "Entrez le mot de passe",
|
||||
"enterAccount": "Entrer un compte",
|
||||
"logIn": "Connectez-vous",
|
||||
"saySomething": "Dis quelque chose...",
|
||||
"sayHi": "Dis bonjour..",
|
||||
"pleaseChatFfriendly": "Veuillez discuter gentiment",
|
||||
"unLockTheRoom": "Déverrouillez la pièce",
|
||||
"operationSuccessful": "L'opération a réussi.",
|
||||
"adminByHomeowner": "est défini comme administrateur par le propriétaire.",
|
||||
"memberByHomeowner": "est défini comme membres par le propriétaire.",
|
||||
"touristByHomeowner": "est défini comme touriste par le propriétaire.",
|
||||
"becomeHost": "Postulez pour devenir hôte",
|
||||
"superFans": "Super fans :",
|
||||
"setUpAnIdentity": "Créer une identité",
|
||||
"kickedOutOfRoom": "Expulsé de la pièce",
|
||||
"knapsack": "Sac à dos",
|
||||
"bdLeader": "Responsable BD",
|
||||
"picture": "Image",
|
||||
"theImageSizeCannotExceed": "La taille de l'image ne peut pas dépasser 4 M",
|
||||
"activity": "Activité",
|
||||
"alreadyAnAdministrator": "Déjà administrateur",
|
||||
"alreadyAnMember": "Déjà membre",
|
||||
"alreadyAnTourist": "Déjà touriste",
|
||||
"touristsCannotSendMessages": "Les touristes ne peuvent pas envoyer de messages",
|
||||
"touristsAreNotAllowedToGoOnTheMic": "Les touristes ne sont pas autorisés à prendre le micro",
|
||||
"lockTheRoom": "Verrouiller la pièce",
|
||||
"special": "Spécial",
|
||||
"visitorList": "Liste des visiteurs",
|
||||
"successfulWear": "Un port réussi",
|
||||
"confirmUnUseTips": "Confirmez-vous pour le supprimer ?",
|
||||
"custom": "Personnalisé",
|
||||
"customBackground": "Arrière-plan personnalisé",
|
||||
"example": "Exemple",
|
||||
"endPreview": "Fin de l'aperçu",
|
||||
"selectAgain": "Sélectionnez à nouveau",
|
||||
"myItems": "Mes articles",
|
||||
"use": "Utiliser",
|
||||
"unUse": "Déséquiper",
|
||||
"renewal": "Renouvellement",
|
||||
"wallet": "Portefeuille",
|
||||
"profile": "Profil",
|
||||
"giftwall": "Mur de cadeaux",
|
||||
"announcement": "Annonce",
|
||||
"blockedList": "Liste bloquée",
|
||||
"country2": "Pays :",
|
||||
"sendTo": "Envoyer à",
|
||||
"credits": "Crédits : {1}",
|
||||
"successfullyUnloaded": "Déchargé avec succès",
|
||||
"expired": "Expiré",
|
||||
"day": "Jour",
|
||||
"inUse": "En cours d'utilisation",
|
||||
"staticOrGifImage": "Image statique / GIF",
|
||||
"confirmUseTips": "Voulez-vous confirmer son utilisation ?",
|
||||
"pleaseUploadUserAvatar": "Veuillez télécharger un avatar.",
|
||||
"joinMemberTips": "Si vous êtes un visiteur dans la salle, vous ne pouvez pas prendre le micro.",
|
||||
"giftGivingSuccessful": "Remise de cadeaux réussie.",
|
||||
"theAccountPasswordCannotBeEmpty": "Le compte ou le mot de passe ne peut pas être vide.",
|
||||
"invitesYouToTheMicrophone": "{1} vous invite au micro",
|
||||
"english": "Anglais",
|
||||
"chinese": "Chinois",
|
||||
"arabic": "arabe",
|
||||
"darkMode": "Mode sombre",
|
||||
"lightMode": "Mode lumière",
|
||||
"systemDefault": "Système par défaut",
|
||||
"pleaseGetOnTheMicFirst": "Veuillez d'abord prendre le micro.",
|
||||
"roomMusicAddMusic": "Ajouter de la musique",
|
||||
"roomMusicSelectAll": "Tout sélectionner",
|
||||
"roomMusicAddedCount": "Chansons {1} ajoutées",
|
||||
"roomMusicEmpty": "Pas de musique",
|
||||
"roomMusicScanFromPhone": "Numériser depuis le téléphone",
|
||||
"roomMusicTotalSongs": "Total : chansons {1}",
|
||||
"roomMusicNotFound": "Aucune musique trouvée",
|
||||
"roomMusicDeleteConfirm": "Supprimer cette musique ?",
|
||||
"roomMusicDeleted": "Musique supprimée",
|
||||
"roomMusicListMode": "Jouer dans l'ordre",
|
||||
"roomMusicShuffleMode": "Lecture aléatoire",
|
||||
"roomMusicSingleMode": "Boucle unique",
|
||||
"duration2": "Durée : {1}",
|
||||
"roomRocketNoteTitle": "Remarque",
|
||||
"roomRocketNoteSectionTitle": "Chaque fois un VIP",
|
||||
"roomRocketNoteSectionBody": "1. Chaque fois qu'un niveau VIP est acheté, il est valable 30 jours et peut être renouvelé manuellement après expiration.",
|
||||
"roomRocketRewardEmptyText": "Aucune récompense gagnée. Plus de chance la prochaine fois.",
|
||||
"roomRocketRecordEmptyText": "Aucun enregistrement de récompense",
|
||||
"launchRocket": "Lancement de fusée"
|
||||
}
|
||||
730
assets/l10n/intl_it.json
Normal file
730
assets/l10n/intl_it.json
Normal file
@ -0,0 +1,730 @@
|
||||
{
|
||||
"signInWithGoogle": "Accedi con Google",
|
||||
"or": "Oppure",
|
||||
"signInWithYourAccount": "Accedi con il tuo account",
|
||||
"signInWithApple": "Accedi con Apple",
|
||||
"loginRepresentsAgreementTo": "L'accesso rappresenta l'accordo",
|
||||
"termsofService": "Termini di servizio",
|
||||
"privaceyPolicy": "Informativa sulla privacy",
|
||||
"tips": "Suggerimenti",
|
||||
"searchNoDataTips": "Inserisci la stanza o l'ID utente che desideri cercare.",
|
||||
"games": "Giochi",
|
||||
"mine": "Il mio",
|
||||
"official": "Ufficiale",
|
||||
"party": "Festa",
|
||||
"other": "Altro",
|
||||
"yes": "Sì",
|
||||
"bag": "Borsa",
|
||||
"sound2": "Suono",
|
||||
"startYourBrandNewJourney": "Inizia il tuo nuovo viaggio",
|
||||
"maliciousHarassment": "Molestie dannose",
|
||||
"event": "Evento",
|
||||
"roomEdit": "Modifica della stanza",
|
||||
"cancelRoomPassword": "Sei sicuro di voler eliminare la password della stanza?",
|
||||
"roomMemberFee": "Quota per il membro della camera",
|
||||
"roomTheme2": "Tema della stanza",
|
||||
"background": "Sfondo",
|
||||
"blockedList2": "Elenco bloccati",
|
||||
"roomPassword": "Password della stanza",
|
||||
"numberOfMic": "Numero di mic",
|
||||
"pleaseEnterContent": "Inserisci il contenuto",
|
||||
"profilePhoto": "Foto del profilo",
|
||||
"aboutMe": "Su di me",
|
||||
"myRoom": "La mia stanza",
|
||||
"noHistoricalRecordsAvailable": "Nessun documento storico disponibile.",
|
||||
"inviteNewUsersToEarnCoins": "Invita nuovi utenti a guadagnare monete",
|
||||
"crateMyRoom": "CREA LA TUA STANZA.",
|
||||
"casualInteraction": "Interazione casuale",
|
||||
"haveGamePlayingTips": "Hai una partita in corso. Per favore, esci dal gioco corrente. Sei sicuro di voler uscire?",
|
||||
"historicalTour": "Giro storico",
|
||||
"gameCenter": "Centro giochi",
|
||||
"returnToVoiceChat": "Tornare alla chat vocale?",
|
||||
"exitGameMode": "Esci dalla modalità gioco",
|
||||
"enterTheRoom": "Entra nella stanza",
|
||||
"invite": "Invita",
|
||||
"recent": "Recente",
|
||||
"popularEvents": "Eventi popolari",
|
||||
"inviteGoRoomTips": "Sempre qui per te, con la pioggia o con il sole. Entra e saluta!",
|
||||
"confirmInviteThisUserToTheRoom": "Confermare l'invito di questo utente (ID:{1}) nella stanza virtuale?",
|
||||
"clearCacheSuccessfully": "Svuota la cache con successo",
|
||||
"sent": "Inviato",
|
||||
"keep": "Tieni",
|
||||
"open": "Aperto",
|
||||
"deleteAccountTips2": "*Se cambi idea, puoi accedere nuovamente al tuo account corrente entro sette giorni e ripristineremo automaticamente il tuo account. Se entro sette giorni non viene effettuato alcun ripristino, l'account verrà eliminato definitivamente.",
|
||||
"deleteAccountTips": "Disponi di diritti amministrativi completi per questo account. Se intendi eliminare l'account, tieni presente i seguenti rischi associati a questa operazione:\n1. Una volta eliminato con successo l'account, non sarai più in grado di accedere al tuo account corrente. La cancellazione dell'account è un'azione permanente.\n2. Una volta eliminato con successo l'account, non sarai in grado di recuperare alcun dato dell'account. Tutte le informazioni (incluse stanze e amici), la valuta virtuale, i regali e gli oggetti virtuali verranno eliminati in modo permanente e non potranno essere ripristinati.\n3. Periodo di riflessione: se non ripristini l'account, non potrai accedere alla pagina di acquisto, alla pagina di prelievo o a qualsiasi altra pagina dell'app.\n4. Durante il periodo di riflessione o dopo che l'account è stato eliminato, la pagina del profilo indicherà che è stato eliminato. Per proteggere il tuo account da ricerche o accessi da parte di altri, le tue informazioni personali verranno rimosse dai sistemi relativi alle funzioni quotidiane. Quando la cancellazione dell'account implica la sicurezza nazionale, procedimenti civili o penali o la protezione dei diritti e degli interessi legittimi di terzi, il team ufficiale si riserva il diritto di respingere la richiesta di cancellazione dell'account dell'utente.\nSe sei sicuro di voler eliminare tutti i dati personali dal tuo account corrente, fai clic su \"Elimina account\".",
|
||||
"accountDeletionNotice": "Avviso di cancellazione dell'account:",
|
||||
"thisUserHasBeenBlacklisted": "Questo utente è stato inserito nella lista nera.",
|
||||
"trend": "Tendenza",
|
||||
"like": "Mi piace",
|
||||
"more": "Di più",
|
||||
"discard": "Scartare",
|
||||
"catchFirstComment": "Cattura il primo commento",
|
||||
"reply": "Rispondi",
|
||||
"posting": "Pubblicazione",
|
||||
"multiple": "Molteplici",
|
||||
"successfullyRemovedFromTheBlacklist": "Rimosso con successo dalla lista nera!",
|
||||
"successfullyAddedToTheBlacklist": "Aggiunto con successo alla lista nera!",
|
||||
"youAreCurrentlyCPRelationshipPleaseDissolve": "Attualmente hai una relazione CP.\nPer favore, prima scioglietelo.",
|
||||
"areYouSureToCancelBlacklist": "Sei sicuro di voler cancellare la lista nera?",
|
||||
"areYouSureYouWantToBlockThisUser": "Sei sicuro di voler bloccare questo utente?",
|
||||
"removeFromBlacklist": "Rimuovi dalla lista nera",
|
||||
"moveToBlacklist": "Passa alla lista nera",
|
||||
"userBlacklist": "Lista nera degli utenti",
|
||||
"specialEffectsManagement": "Gestione degli effetti speciali",
|
||||
"wishingYouHappinessEveryDay": "Ti auguro felicità ogni giorno.",
|
||||
"newMessage": "Nuovo messaggio",
|
||||
"createRoomSuccsess": "Stanza creata con successo!",
|
||||
"contactUs": "Contattaci",
|
||||
"systemAnnouncementTips1": "Difendersi dalle frodi:",
|
||||
"systemAnnouncementTips": "Verificare le informazioni esclusivamente attraverso i canali ufficiali. Non scaricare mai software di terze parti, condividere dati personali o trasferire denaro in base a richieste esterne. Gli ID ufficiali dello staff sono solo 10000, 10003 e 10086. Per qualsiasi sospetto, fermati e segnala tramite",
|
||||
"systemAnnouncement": "Annuncio di sistema",
|
||||
"doNotClickUnfamiliarTips": "Non fare clic su collegamenti sconosciuti, poiché potrebbero esporre le tue informazioni personali. Non condividere mai con nessuno il tuo documento d'identità o i dettagli della tua carta bancaria.",
|
||||
"atTag": "@Tag",
|
||||
"sayHi2": "Salutami",
|
||||
"roomBottomGreeting": "Ciao...",
|
||||
"canSendMsgTips": "Entrambe le parti devono seguirsi a vicenda prima di poter inviare messaggi privati.",
|
||||
"msgSendRedEnvelopeTips": "* Sulle buste rosse verrà addebitata una commissione di servizio del 10% e i destinatari riceveranno solo il 90% del valore della busta rossa. Il livello di ricchezza del mittente deve essere superiore al livello 10.",
|
||||
"reapply": "Riapplicare",
|
||||
"cancelRequest": "Annulla richiesta",
|
||||
"pending": "In sospeso",
|
||||
"supporter": "Sostenitore",
|
||||
"coinsReceived": "Monete ricevute",
|
||||
"numberOfSign": "Numero di segno: {1}",
|
||||
"hostWeeklyRank": "Classifica settimanale dell'host",
|
||||
"supporterWeeklyRank": "Classifica settimanale dei sostenitori",
|
||||
"memberList": "Elenco dei membri",
|
||||
"treasureChest": "Scrigno del tesoro",
|
||||
"applicationRecord": "Registro della domanda",
|
||||
"appUpdateTip": "L'app ha una nuova versione ({1}). Vuoi scaricarlo adesso?",
|
||||
"ownerIncomeCoins": "Reddito del proprietario: monete {1}",
|
||||
"game": "Gioco",
|
||||
"skip2": "Salta",
|
||||
"skipCountdown": "Salta {1}s",
|
||||
"coins4": "Monete",
|
||||
"weekStart": "Inizio settimana",
|
||||
"forMoreRewardsPleaseCheckTheTaskCenter": "Per ulteriori premi, controlla il centro attività",
|
||||
"kingQuuen": "Re-Regina",
|
||||
"ramadan": "Ramadan",
|
||||
"updateNow": "Aggiorna ora",
|
||||
"allGames": "Tutti i giochi",
|
||||
"fishClass": "Classe di pesce",
|
||||
"greedyClass": "Classe golosa",
|
||||
"raceSeries": "Serie di gare",
|
||||
"slotsClass": "Classe di slot",
|
||||
"others": "Altri",
|
||||
"hotGames": "Giochi caldi",
|
||||
"chatBox": "Casella di chat",
|
||||
"termsOfServicePrivacyPolicyTips": "Continuando accetti i Termini di servizio e l'Informativa sulla privacy",
|
||||
"and": "e",
|
||||
"pleaseSelectTheTypeContent": "Seleziona il tipo di contenuto offensivo.",
|
||||
"illegalInformation": "Informazioni illegali",
|
||||
"inappropriateContent": "Contenuti inappropriati",
|
||||
"personalAttack": "Attacco personale",
|
||||
"confirm": "Conferma",
|
||||
"spam": "Spam",
|
||||
"countdownMinutes": "Minuti del conto alla rovescia:",
|
||||
"number2": "Numero:",
|
||||
"fraud": "Frode",
|
||||
"received": "Ricevuto",
|
||||
"currentProgress": "Progressi attuali",
|
||||
"currentStage": "Fase attuale:{1}",
|
||||
"roomReward2": "Premio camera:{1}",
|
||||
"roomReward": "Premio per la camera",
|
||||
"expirationTime": "Tempo di scadenza",
|
||||
"ownerSendTheRedEnvelope": "Il proprietario ha inviato monete di ricompensa.",
|
||||
"rewardCoins": "Monete premio: monete {1}",
|
||||
"signInRewardReceived": "Accesso effettuato con successo. Ricompensa: {1}",
|
||||
"registerRewardReceived": "Premio di registrazione ricevuto: {1}",
|
||||
"lastWeekProgress": "I progressi della settimana scorsa",
|
||||
"redEnvelopeTips2": "*Se la busta rossa non viene ritirata entro il tempo limite, le monete rimanenti verranno restituite all'utente che ha inviato la busta rossa.",
|
||||
"goToRecharge": "Vai a ricaricare",
|
||||
"deleteAccount2": "Elimina account ({1}s)",
|
||||
"areYouSureYouWantToDeleteYourAccount": "Sei sicuro di voler eliminare il tuo account?",
|
||||
"insufhcientGoldsGoToRecharge": "Oro insufficiente. Ricarica ora!",
|
||||
"coins2": "Monete {1}",
|
||||
"remainingNumberTips": "Rimanenza disponibile: ({1}/{2})",
|
||||
"collectionTimeTips": "Orario di ritiro:{1}({2}/{3})",
|
||||
"sendARedEnvelope": "Invia una busta rossa",
|
||||
"sendRedPackConfirmTips": "Sei sicuro di voler inviare il pacchetto rosso?",
|
||||
"redEnvelopeSendingRecords": "Record di invio busta rossa:",
|
||||
"redEnvelope": "Busta rossa",
|
||||
"roomRedPacketGoldCoins": "Monete d'oro",
|
||||
"roomRedPacketQuantity": "Quantità",
|
||||
"roomRedPacketPickupTime": "Orario di ritiro",
|
||||
"roomRedPacketInMinutes": "In {1} minuti",
|
||||
"roomRedPacketClaimNow": "Richiedi ora",
|
||||
"roomRedPacketSendLuckyPack": "Invia un pacchetto fortunato",
|
||||
"roomRedPacketSending": "Invio...",
|
||||
"roomRedPacketSendSuccess": "Pacchetto fortunato inviato",
|
||||
"roomRedPacketComingSoon": "Prossimamente",
|
||||
"roomRedPacketRulesTitle": "Regole",
|
||||
"roomRedPacketRulesContent": "1. Puoi spendere monete per inviare un pacchetto fortunato. Se altri aprono il tuo pacchetto fortunato, possono ricevere una quantità casuale di monete.\n\n2. Due ore dopo l'invio del Lucky Pack, tutte le monete non reclamate verranno restituite al tuo portafoglio.\n\n3. Dopo aver inviato un pacchetto fortunato a livello di server, verrà attivato un banner pacchetto fortunato a livello di server, attirandoti maggiore attenzione.\n\n4. Puoi inviare pacchetti fortunati della stanza %1$s al giorno.\n\n5. I pacchetti fortunati inviati da utenti al di fuori della tua regione non possono essere rivendicati.",
|
||||
"roomRedPacketHistoryTitle": "Cronologia delle consegne del pacchetto fortunato",
|
||||
"roomRedPacketHistoryItemTitle": "Borsa Portafortuna (buste rosse {1})",
|
||||
"roomRedPacketHistoryStatusSent": "Inviato",
|
||||
"roomRedPacketHistoryStatusFinished": "Completamente rivendicato",
|
||||
"roomRedPacketHistoryStatusPending": "In sospeso",
|
||||
"roomRedPacketHistoryStatusReturned": "Restituito",
|
||||
"roomRedPacketHistoryRefund": "Rimborso: {1}",
|
||||
"roomRedPacketClaimRecordsTitle": "Richiedi record",
|
||||
"roomRedPacketChatMessage": "Ti ho mandato una busta rossa, aprila subito!",
|
||||
"roomRedPacketReceivedMessage": "Richiedi le monete {1} dal pacchetto fortunato {2}",
|
||||
"roomRedPacketOpenSubtitle": "Il pacchetto fortunato è arrivato",
|
||||
"roomRedPacketOpenYouGot": "Ottieni",
|
||||
"roomRedPacketClaimSuccessTips": "Le monete sono state inviate al tuo portafoglio.",
|
||||
"roomRedPacketOpenSoldOut": "Il pacchetto fortunato è già esaurito.",
|
||||
"roomRedPacketOpenExpired": "Il pacchetto fortunato è scaduto.",
|
||||
"roomRedPacketRegionNotMatch": "Solo gli utenti nella stessa regione linguistica possono richiedere questo pacchetto fortunato.",
|
||||
"roomRedPacketNotStarted": "Il pacchetto fortunato non è ancora pronto.",
|
||||
"roomRedPacketDailyLimitExceeded": "Il limite di invio di oggi è stato raggiunto.",
|
||||
"roomRedPacketInsufficientBalance": "Monete d'oro insufficienti.",
|
||||
"roomRedPacketPresenceRequired": "Si prega di rimanere nella stanza per utilizzare i pacchetti fortunati.",
|
||||
"roomRedPacketNetworkError": "Impossibile elaborare il pacchetto fortunato in questo momento.",
|
||||
"roomRedPacketAlreadyClaimed": "Hai già richiesto questo pacchetto fortunato.",
|
||||
"roomRedPacketOpenWaiting": "Può essere aperto in {1} minuti.",
|
||||
"roomRedPacketOpenWaitingCountdown": "Disponibile per il ritiro dopo {1}",
|
||||
"roomRedPacketOkay": "Ok",
|
||||
"roomRedPacketFloatingTitle": "{1} ha inviato una busta rossa",
|
||||
"roomRedPacketFloatingSubtitle": "Ho inviato una busta rossa",
|
||||
"redEnvelopeRecTips2": "Le buste rosse sono state tutte reclamate.",
|
||||
"redEnvelopeRecTips3": "Il tempo per il ritiro della busta rossa è scaduto!",
|
||||
"openTheTreasureChest": "Apri lo scrigno del tesoro",
|
||||
"redEnvelopeRecTips1": "Le monete guadagnate sono state depositate nel tuo portafoglio.",
|
||||
"redEnvelopeTips1": "Monete:",
|
||||
"roomTools": "Strumenti della stanza:",
|
||||
"entertainment": "Intrattenimento:",
|
||||
"reportSucc": "Segnalazione riuscita",
|
||||
"pornography": "Pornografia",
|
||||
"reportInputTips": "Si prega di descrivere il problema nel modo più dettagliato possibile in modo che possiamo capirlo e risolverlo.",
|
||||
"cancel": "Annulla",
|
||||
"join": "Partecipa",
|
||||
"items": "Articoli",
|
||||
"vistors": "Visitatori",
|
||||
"fans": "Tifosi",
|
||||
"balanceNotEnough": "Saldo in monete d'oro insufficiente. Vuoi andare a ricaricare?",
|
||||
"skip": "Salta {1}",
|
||||
"letGoToWatch": "Andiamo a guardare!",
|
||||
"launchedARocket": "lanciato un razzo",
|
||||
"sendUserId": "Invia ID utente:{1}",
|
||||
"giveUpIdentity": "Rinunciare all'identità",
|
||||
"leaveRoomIdentityTips": "Sei sicuro di voler rinunciare all'identità della stanza?",
|
||||
"joinMemberTips2": "Vuoi confermare l'adesione alla stanza come membro?",
|
||||
"sureUnfollowThisRoom": "Sei sicuro di voler smettere di seguire questa stanza?",
|
||||
"welcomeMessage": "Benvenuti nella nostra applicazione, {name}!",
|
||||
"settings": "Impostazioni",
|
||||
"account": "Conto",
|
||||
"common": "Comune",
|
||||
"delete": "Elimina",
|
||||
"copy": "Copia",
|
||||
"bio": "Bio",
|
||||
"useCoupontips": "Sei sicuro di voler utilizzare il coupon?",
|
||||
"searchUserId": "Cerca l'ID dell'utente",
|
||||
"sendUser": "Invia utente",
|
||||
"hobby": "Hobby",
|
||||
"sendCoupontips": "Sei sicuro di voler inviare questo coupon a questo utente?",
|
||||
"youDontHaveAnyCouponsYet": "Non hai ancora alcun coupon.",
|
||||
"recall": "Ricorda",
|
||||
"youHaventFollowed": "Non hai seguito nessuna stanza",
|
||||
"deleteFromMyDevice": "Elimina dal mio dispositivo",
|
||||
"deleteOnAllDevices": "Elimina su tutti i dispositivi",
|
||||
"messageHasBeenRecalled": "Questo messaggio è stato richiamato",
|
||||
"recallThisMessage": "Ricordi questo messaggio?",
|
||||
"language": "Lingua",
|
||||
"feedback": "Feedback",
|
||||
"signedin": "Effettuato l'accesso",
|
||||
"receiveSucc": "Rivendicato con successo",
|
||||
"about": "Circa",
|
||||
"aboutUs": "Chi siamo",
|
||||
"theme": "Tema",
|
||||
"wealthLevel": "Livello di ricchezza",
|
||||
"userLevel": "Livello utente",
|
||||
"goToUpload": "Vai al caricamento",
|
||||
"logout": "Esci",
|
||||
"luck": "Fortuna",
|
||||
"level": "Livello",
|
||||
"themeGoToUploadTips": "1. La revisione verrà completata entro 24 ore dal completamento del caricamento.\n2. Tutte le monete verranno restituite se la revisione fallisce.",
|
||||
"pleaseUploadAccordingToExample": "Si prega di caricare secondo l'esempio fornito.",
|
||||
"home": "Casa",
|
||||
"explore": "Esplora",
|
||||
"me": "Io",
|
||||
"socialPrivilege": "Privilegio sociale",
|
||||
"information": "Informazioni",
|
||||
"myPhoto": "La mia foto",
|
||||
"areYouSureYouWantToSpend3": "*Se l'altra parte rifiuta l'invito CP, le tue monete verranno restituite al tuo portafoglio.",
|
||||
"areYouSureYouWantToSpend": "Sei sicuro di voler spendere?",
|
||||
"areYouSureYouWantToSpend2": "inviare un invito CP a questo utente?",
|
||||
"underReview": "In corso di revisione",
|
||||
"doYouWantToDeleteIt": "Vuoi eliminarlo?",
|
||||
"chooseFromAblum": "Scegli dall'album",
|
||||
"spaceBackground": "Sfondo dello spazio",
|
||||
"editProfile": "Modifica profilo",
|
||||
"sendTheCpRequest": "Invia la richiesta CP",
|
||||
"addCp": "Aggiungi CP",
|
||||
"partWays": "Modi di parte",
|
||||
"reconcile": "Riconciliarsi",
|
||||
"separated": "Separato",
|
||||
"areYouSureYouWantToSpend5": "{1} ti ha confessato la sensazione; se accetti diventerai una coppia.",
|
||||
"areYouSureYouWantToSpend6": "{1} vuole tornare insieme a te. Se decidi di effettuare la riconciliazione, tutti i tuoi dati precedenti verranno ripristinati.",
|
||||
"reconcileInvitationTips": "*Se l'altra parte rifiuta l'invito CP, le tue monete verranno restituite al tuo portafoglio.",
|
||||
"reconcileInvitation": "Invito alla riconciliazione",
|
||||
"areYouSureYouWantToSpend4": "inviare un invito alla riconciliazione a questo utente?",
|
||||
"partWaysTips": "*Se uno dei partner di una coppia sceglie di separarsi, ci sarà un periodo di riflessione di 7 giorni. Durante questo periodo, entrambe le parti possono scegliere di riconciliarsi e tutti i dati verranno ripristinati al momento della riconciliazione. Se non viene scelta alcuna riconciliazione entro la fine del periodo di 7 giorni, i dati della coppia verranno cancellati.",
|
||||
"areYouSureYouWantToPartWaysWithYourCP": "Sei sicuro di voler separarti dal tuo CP?",
|
||||
"timeSpentTogether": "Tempo trascorso insieme: {1} giorni",
|
||||
"firstDay": "Primo giorno:{1}",
|
||||
"numberOfMyCPs": "Numero dei miei CP:({1}/{2})",
|
||||
"props": "Oggetti di scena",
|
||||
"win": "Vinci",
|
||||
"dice": "Dadi",
|
||||
"rps": "RPS",
|
||||
"operationFail": "L'operazione è fallita.",
|
||||
"enterRoomFailedRetry": "Impossibile entrare nella stanza. Per favore riprova.",
|
||||
"voiceConnectionFailedRetry": "Connessione vocale non riuscita. Per favore riprova.",
|
||||
"likedYourComment": "Mi è piaciuto il tuo commento.",
|
||||
"doYouWantToKeepTheDraft": "Vuoi mantenere la bozza?",
|
||||
"operationsAreTooFrequent": "Le operazioni sono troppo frequenti",
|
||||
"luckNumber": "Numero di fortuna",
|
||||
"relationShip": "Relazione",
|
||||
"couple": "Coppia {1}:",
|
||||
"couple2": "Coppia",
|
||||
"reject": "Rifiutare",
|
||||
"accept": "Accetta",
|
||||
"noMatchedCP": "Nessun CP corrispondente",
|
||||
"inviteYouToBecomeBD": "Ti invito a diventare un BD.",
|
||||
"adminInviteRechargeAgent": "Invitati a diventare un agente Recharge.",
|
||||
"confirmAcceptTheInvitation": "Confermare per accettare l'invito?",
|
||||
"confirmDeclineTheInvitation": "Confermi di rifiutare l'invito?",
|
||||
"host": "Ospite",
|
||||
"following": "Segue",
|
||||
"agent": "Agenzia",
|
||||
"approved": "Approvato",
|
||||
"approvedWithinOneMinute": "Approvato e visualizzato entro 1 minuto dall'invio",
|
||||
"onlineUsers": "Utenti online({1}/{2}):",
|
||||
"applyToJoin": "Fai domanda per aderire",
|
||||
"supporterList": "Elenco dei sostenitori",
|
||||
"hostList": "Elenco degli host",
|
||||
"upToAdmins": "Fino agli amministratori {1}",
|
||||
"upToMembers": "Fino a membri {1}",
|
||||
"levelPrivileges": "Privilegi di livello",
|
||||
"ra": "RA",
|
||||
"roomAnnouncement": "Annuncio della stanza",
|
||||
"help": "Aiuto",
|
||||
"rejected": "Rifiutato",
|
||||
"boxContributeTips": "Gli investimenti sono già stati fatti oggi. Per favore, non investire di nuovo.",
|
||||
"bd": "B.D",
|
||||
"coupon": "Buono",
|
||||
"search": "Cerca",
|
||||
"get": "Ottieni",
|
||||
"inRocket": "Nel razzo",
|
||||
"roomRocketHelpTips": "1. L'invio di regali nella stanza aumenta l'energia del razzo. *1 regalo in moneta d'oro = 1 punto energia razzo; i regali fortunati aumentano l'energia del razzo del 4% del valore delle monete d'oro del regalo.\n2. Una volta che l'energia del razzo è completamente carica, la stanza può lanciare il razzo. I premi verranno distribuiti automaticamente dopo il lancio.\n3. Diversi livelli di razzi offrono ricompense diverse.\n4. Quando il razzo viene lanciato, tutti gli utenti nella stanza possono richiedere la ricompensa del razzo.\n5. L'energia del razzo viene ripristinata ogni giorno alle 00:00.",
|
||||
"couponRecord": "Registro dell'utilizzo del coupon",
|
||||
"inRoom": "Nella stanza",
|
||||
"searchCouponHint": "Cerca buono",
|
||||
"giftCounter": "Contatore di regali",
|
||||
"bDLeaderInviteYouToBecomeBDLeader": "Ti invito a diventare un BDLeader",
|
||||
"wins": "vince",
|
||||
"inviteYouToBecomeHost": "Invitarti a diventare un host.",
|
||||
"friends": "Amici",
|
||||
"deleteConversationTips": "Sei sicuro di voler eliminare la cronologia chat con questo utente?",
|
||||
"propMessagePrompt": "Prompt del messaggio di scena",
|
||||
"inputUserId": "Inserisci l'ID utente",
|
||||
"fromLuckyGifts": "Dai regali fortunati/magici",
|
||||
"receive": "Ricevi",
|
||||
"checkInSuccessful": "Check-in riuscito",
|
||||
"sginTips": "Riceverai un premio la prima volta che accedi ogni giorno. Se interrompi la serie di accessi, il premio verrà ricalcolato a partire dal primo giorno in cui effettuerai nuovamente l'accesso.",
|
||||
"popular": "Popolare",
|
||||
"recommend": "Consiglia",
|
||||
"follow": "Segui",
|
||||
"history": "Storia",
|
||||
"hotRooms": "Camere calde",
|
||||
"viewMore": "Visualizza altro",
|
||||
"noData": "Nessun dato",
|
||||
"users": "Utenti",
|
||||
"rooms": "Camere",
|
||||
"coins": "monete",
|
||||
"unread": "Non letto",
|
||||
"read": "Leggi",
|
||||
"image": "[Immagine]",
|
||||
"video": "[Video]",
|
||||
"sound": "[Suono]",
|
||||
"gift2": "[Regalo]",
|
||||
"clickHereToStartChatting": "Di' qualcosa.....",
|
||||
"receivedAMessage": "[Ricevuto un messaggio]",
|
||||
"confirmSwitchMicModelTips": "Confermi il passaggio in modalità seduta?",
|
||||
"number": "Numero",
|
||||
"album": "Album",
|
||||
"camera": "Fotocamera",
|
||||
"system": "Sistema",
|
||||
"notifcation": "Notifica",
|
||||
"inviteYouToBecomeAgent": "Invitati a diventare un'agenzia.",
|
||||
"myMusic": "La mia musica",
|
||||
"add": "Aggiungi",
|
||||
"pullToLoadMore": "Tirare per caricare altro",
|
||||
"loadingFailedClickToRetry": "Caricamento non riuscito, fare clic per riprovare",
|
||||
"releaseToLoadMore": "Rilascia per caricare altro",
|
||||
"haveMyLimits": "---Ho i miei limiti.---",
|
||||
"music": "Musica",
|
||||
"free": "Gratuito",
|
||||
"charm": "Fascino del regalo",
|
||||
"start": "Inizia",
|
||||
"stop": "Fermare",
|
||||
"chats": "Chat",
|
||||
"refuse": "Rifiutare",
|
||||
"agree": "D'accordo",
|
||||
"thisFeatureIsCurrentlyUnavailable": "Questa funzionalità non è attualmente disponibile.",
|
||||
"pleaseSelectTheRecipient": "Seleziona il destinatario.",
|
||||
"searchMemberIdHint": "Inserisci l'ID del membro",
|
||||
"unclaimedRedEnvelopes": "Le buste rosse non ritirate vengono rimborsate in 24 ore.",
|
||||
"redEnvelopeNotYetClaimed": "Busta rossa non ancora reclamata.",
|
||||
"redEnvelopeAmount": "Importo busta rossa: monete {1}",
|
||||
"sentARedEnvelope": "ha inviato una busta rossa.",
|
||||
"theRedEnvelopeHasExpired": "La busta rossa è scaduta.",
|
||||
"joinRequest": "Richiesta di adesione",
|
||||
"scrollToTheBottom": "Scorri fino in fondo",
|
||||
"gameRules": "Regole del gioco:",
|
||||
"charmGameRulesTips": "Accendi la dashboard per mostrare sul microfono le monete regalo ricevute da tutti gli utenti. 1 moneta = 1 punteggio (Regalo fortunato: 1 moneta = 0,04 punteggio).",
|
||||
"inputYourOldPassword": "Inserisci la tua vecchia password",
|
||||
"enterYourOldPassword": "Inserisci la tua vecchia password",
|
||||
"setYourPassword": "Imposta la tua password",
|
||||
"enterYourNewPassword": "Inserisci la tua nuova password",
|
||||
"confirmYourPassword": "Conferma la tua password",
|
||||
"theTwoPasswordsDoNotMatch": "Le due password non corrispondono.",
|
||||
"resetLoginPasswordtTips2": "La password deve contenere da 8 a 16 caratteri e deve essere una combinazione di lettere inglesi maiuscole e minuscole e numeri (non solo numeri).",
|
||||
"resetLoginPassword": "Reimposta password di accesso",
|
||||
"resetLoginPasswordtTips1": "Accedi con il tuo ID utente o Vanity ID. È più sicuro accedere con un account yumi.",
|
||||
"localMusic": "Musica locale",
|
||||
"setLoginPassword": "Imposta la password di accesso",
|
||||
"confirmSwitchMicThemeTips": "Confermi il cambio di stile del sedile?",
|
||||
"micTheme": "Tema del microfono",
|
||||
"classicMic": "Microfono classico {1}",
|
||||
"yesterday": "Ieri {1}",
|
||||
"monday": "Lunedì {1}",
|
||||
"tuesday": "Martedì {1}",
|
||||
"wednesday": "Mercoledì {1}",
|
||||
"thursday": "Giovedì {1}",
|
||||
"friday": "Venerdì {1}",
|
||||
"saturday": "Sabato {1}",
|
||||
"sunday": "Domenica {1}",
|
||||
"acceptedYour": "{1} ha accettato il tuo",
|
||||
"youAccepted": "Hai accettato {1}",
|
||||
"openRedPackDialogTip": "La busta rossa",
|
||||
"micManagement": "Gestione del microfono",
|
||||
"bdCenter": "Centro B.D",
|
||||
"rechargeAgency": "Agenzia di ricarica",
|
||||
"adminCenter": "Centro di amministrazione",
|
||||
"managerCenter": "Centro Dirigente",
|
||||
"goldList": "Lista d'oro",
|
||||
"followList": "Segui l'elenco",
|
||||
"fansList": "Elenco dei tifosi",
|
||||
"daily": "Ogni giorno",
|
||||
"female": "Femmina",
|
||||
"male": "Maschio",
|
||||
"identity": "Identità",
|
||||
"adjust": "Regolare",
|
||||
"warning": "Avvertimento",
|
||||
"screenshotTips": "Schermata (fino a 3)",
|
||||
"roomNotice": "Avviso di sala",
|
||||
"roomTheme": "Tema della stanza",
|
||||
"description": "Descrizione:",
|
||||
"inputDesHint": "Si prega di descrivere il problema nel modo più dettagliato possibile in modo che possiamo capirlo e risolverlo.",
|
||||
"roomProfilePicture": "Immagine del profilo della stanza",
|
||||
"userProfilePicture": "Immagine del profilo utente",
|
||||
"userName": "Nome utente",
|
||||
"pleaseSelectTheTypeToProcess": "Seleziona il tipo da elaborare:",
|
||||
"roomEditing": "Modifica della stanza",
|
||||
"setAccount": "Imposta conto",
|
||||
"userEditing": "Modifica utente",
|
||||
"enterTheUserId": "Inserisci l'ID utente",
|
||||
"enterTheRoomId": "Inserisci l'ID della stanza",
|
||||
"deleteAccount": "Elimina account",
|
||||
"becomeAgent": "Diventa un agente",
|
||||
"enterNickname": "Inserisci il soprannome",
|
||||
"enterInvitationCode": "inserisci il codice invito",
|
||||
"selectYourCountry": "Seleziona il tuo paese",
|
||||
"inviteCode": "Codice invito",
|
||||
"magic": "Magia",
|
||||
"luckGiftSpecialEffects": "Effetti di animazione regalo fortunato",
|
||||
"theVideoSizeCannotExceed": "La dimensione del video non può superare i 50 MB",
|
||||
"weekly": "Settimanale",
|
||||
"customizedGiftRulesContent": "Come ricevere il mio regalo personalizzato:\n1.Determinare se l'utente ha diritto a ricevere un regalo personalizzato in base al suo attuale livello di ricchezza.\n(1) Quando il livello di ricchezza dell'utente è ≥ 35: l'utente può caricare un video per il regalo personalizzato una volta. Utilizzeremo l'immagine del profilo attuale dell'utente come immagine regalo e il video fornito come effetto regalo su \"Personalizzato\". La produzione richiederà del tempo e ti avviseremo non appena sarà disponibile nel negozio.\n(2) Quando il livello di ricchezza dell'utente è ≥45: l'utente può caricare un video per il regalo personalizzato una volta. Utilizzeremo l'immagine del profilo attuale dell'utente come immagine regalo e il video fornito come effetto regalo su \"Personalizzato\". La produzione richiederà del tempo e ti avviseremo non appena sarà disponibile nel negozio.\n2.Puoi partecipare ad attività specifiche sull'app e, dopo aver soddisfatto i criteri, contattarci tramite la sezione \"Messaggio\" → \"Contattaci\". Fornisci gli screenshot dell'attività e il video che desideri utilizzare per il regalo personalizzato. Utilizzeremo la tua attuale immagine del profilo come immagine regalo e il video fornito come effetto regalo, quindi lo elencheremo in \"Personalizzato\". La produzione richiederà del tempo e ti avviseremo non appena sarà disponibile nel negozio.\nPeriodo di validità del regalo personalizzato e come estenderlo:\nGli esclusivi regali personalizzati saranno disponibili sullo scaffale per un periodo di validità di 30 giorni. Per consentire agli utenti influenti e stimolanti di continuare a godere di questa nuova esperienza e mostrare il proprio stile personale, gli utenti che possiedono regali personalizzati possono estendere la durata di conservazione di tutti i loro regali personalizzati di 30 giorni ricaricando $ 500 in un dato mese.",
|
||||
"customizedGiftRules": "Regole regalo personalizzate",
|
||||
"rulesUpload": "Regole e caricamento",
|
||||
"monthly": "Mensile",
|
||||
"message": "Messaggio",
|
||||
"clearCache": "Cancella cache",
|
||||
"customized": "Personalizzato",
|
||||
"searchInputHint": "Inserisci il numero di conto/stanza",
|
||||
"kickRoomTips": "Sei stato cacciato dalla stanza.",
|
||||
"joinRoomTips": "Entra nella stanza!",
|
||||
"roomSetting": "Impostazione della stanza",
|
||||
"roomDetails": "Dettagli della camera",
|
||||
"systemRoomTips": "Sii educato e rispettoso. Qualsiasi contenuto pornografico o volgare è severamente proibito su Yumi. Una volta scoperto, l'account verrà bannato permanentemente. Si prega di rispettare consapevolmente i regolamenti della piattaforma yumi.",
|
||||
"copiedToClipboard": "Copiato negli appunti",
|
||||
"recharge": "Ricaricare",
|
||||
"receivedFromALuckyGift": "Ricevuto da un regalo fortunato/magico.",
|
||||
"followedYou": "Ti ho seguito",
|
||||
"agentCenter": "Centro Agenzia",
|
||||
"areYouSureYouWantToClearLocalCache": "Sei sicuro di voler svuotare la cache locale?",
|
||||
"clearMessage": "Cancella i messaggi sullo schermo",
|
||||
"report": "Rapporto",
|
||||
"coins3": "Monete",
|
||||
"giftSpecialEffects": "Regala effetti speciali",
|
||||
"basicFeatures": "Funzionalità di base",
|
||||
"task": "Compito",
|
||||
"importantReminder": "Promemoria importante",
|
||||
"entryVehicleAnimation": "Animazione del veicolo in entrata",
|
||||
"floatingAnimationInGlobal": "Animazione mobile in globale",
|
||||
"broadcast": "Trasmettere",
|
||||
"broadcastDisplay": "Visualizzazione della trasmissione",
|
||||
"broadcastAllPlaces": "Tutti i posti",
|
||||
"broadcastRoomOnly": "Nella stanza",
|
||||
"broadcastOff": "Chiudi trasmissione",
|
||||
"entryVehicleAnimation2": "Gli utenti con privilegi VIP4 o superiori possono utilizzare questa funzione per disabilitare le animazioni del veicolo.",
|
||||
"dailyTasks": "Compiti quotidiani",
|
||||
"exclusiveForNewcomers": "Esclusivo per i nuovi arrivati",
|
||||
"resetsDailyAtMidnight": "Si ripristina ogni giorno a mezzanotte",
|
||||
"limitedToOneTime": "Limitato a una volta",
|
||||
"useMicrophoneForOneMin": "Usa il microfono per 1 minuto",
|
||||
"taskRequirement": "Requisito",
|
||||
"taskProgress": "Progresso",
|
||||
"rewardClaimedSuccessfully": "Premio richiesto con successo",
|
||||
"rewardClaimFailed": "La richiesta del premio non è riuscita",
|
||||
"enterRoomConfirmTips": "Sei sicuro di voler entrare nella stanza?",
|
||||
"followSucc": "Seguito con successo",
|
||||
"goldListort": "Lista d'oro",
|
||||
"rechargeList": "Elenco ricarica",
|
||||
"edit": "Modifica",
|
||||
"swipeLeftOnTheFloatingScreenAreaToQuicklyCloseIt": "*Suggerimento: scorri verso sinistra nell'area dello schermo mobile per chiuderla rapidamente.",
|
||||
"enterThisVoiceChatRoom": "Entra in questa chat room vocale?",
|
||||
"go": "Vai",
|
||||
"done": "Fatto",
|
||||
"improvementTasks": "Compiti di miglioramento",
|
||||
"save": "Salva",
|
||||
"kickPrevention": "Prevenzione dei calci",
|
||||
"freeChatSpeak": "Chatta e parla gratuitamente",
|
||||
"nickName": "NickNome",
|
||||
"gender": "Genere",
|
||||
"unFollow": "Smetti di seguire",
|
||||
"days": "Giorni",
|
||||
"permanent": "Permanente",
|
||||
"country": "Paese",
|
||||
"birthday": "Compleanno",
|
||||
"man": "L'uomo",
|
||||
"woman": "Donna",
|
||||
"apple": "mela",
|
||||
"google": "Google",
|
||||
"startVoiceParty": "Inizia una festa vocale!",
|
||||
"enterRoomTips": "{1} Entra nella stanza",
|
||||
"roomName": "Nome della stanza",
|
||||
"roomMember": "Membro della stanza",
|
||||
"pleaseSelectYourCountry": "Seleziona il tuo paese.",
|
||||
"pleaseSelectYourGender": "Seleziona il tuo sesso.",
|
||||
"pleaseEnterNickname": "Inserisci un soprannome.",
|
||||
"countryRegion": "Paese e regione",
|
||||
"dateOfBirth": "Data di nascita",
|
||||
"mute": "Muto",
|
||||
"exit": "Esci",
|
||||
"mysteriousInvisibility": "Invisibilità misteriosa",
|
||||
"antiBlock": "Antiblocco",
|
||||
"privateChat": "Chat privata",
|
||||
"storeDiscount": "Sconto negozio {1} Off",
|
||||
"membershipFreeChatSpeak": "Chatta e parla senza abbonamento",
|
||||
"priorityRoomSorting": "Ordinamento delle stanze prioritarie",
|
||||
"userColoredID": "ID colorato dell'utente",
|
||||
"pleaseSelectaItem": "Seleziona un articolo",
|
||||
"areYouRureRoRecharge": "Sei sicuro di voler ricaricare?",
|
||||
"mInimize": "Tieni",
|
||||
"everyone": "Tutti",
|
||||
"shop": "Negozio",
|
||||
"roomOwner": "Proprietario della stanza",
|
||||
"dailyTaskRewardBonus": "Bonus ricompensa attività giornaliera ({1} XP)",
|
||||
"userLevelXPBoost": "Incremento XP livello utente ({1} XP)",
|
||||
"andAboveUsers": "{1} e utenti superiori",
|
||||
"privileges": "{1} Privilegi",
|
||||
"basicPermissions": "Autorizzazioni di base",
|
||||
"hostCenter": "Centro ospitante",
|
||||
"allOnMicrophone": "Tutto al microfono",
|
||||
"usersOnMicrophone": "Utenti al microfono",
|
||||
"allInTheRoom": "Tutto in camera",
|
||||
"send": "Invia",
|
||||
"crop": "Ritaglia",
|
||||
"goToUpgrade": "Vai all'aggiornamento",
|
||||
"exclusiveEmojiWillBeReleasedAfterBecoming": "Le emoji esclusive verranno rilasciate dopo essere diventate",
|
||||
"preventBeingBlocked": "Evita di essere bloccato",
|
||||
"enableRankIncognitoMode": "Abilita la modalità di navigazione in incognito del ranking",
|
||||
"avoidBeingKicked": "Evitare di essere presi a calci",
|
||||
"finish": "Finisci",
|
||||
"takeTheMic": "Prendi il microfono",
|
||||
"openTheMic": "Apri il microfono",
|
||||
"muteTheMic": "Disattiva il microfono",
|
||||
"unlockTheMic": "Sblocca il microfono",
|
||||
"leavelTheMic": "Lascia il microfono",
|
||||
"lockTheMic": "Blocca il microfono",
|
||||
"removeTheMic": "Rimuovi il microfono",
|
||||
"inviteToTheMicrophone": "Invito al microfono",
|
||||
"openUserProfleCard": "Apri la scheda del profilo utente",
|
||||
"obtain": "ottenere",
|
||||
"win2": "Vinci {1}",
|
||||
"backTheRoom": "Stanza sul retro",
|
||||
"toConsume": "Consumare",
|
||||
"howToUpgrade": "Come aggiornare?",
|
||||
"spendCoinsToGainExperiencePoints": "Spendi monete per guadagnare punti esperienza",
|
||||
"higherLevelFancierAvatarFrame": "Distintivi/cornice avatar di livello superiore e più elaborati",
|
||||
"all": "Tutto",
|
||||
"gift": "Regalo",
|
||||
"chat": "Chatta",
|
||||
"owner": "Proprietario",
|
||||
"store": "Negozio",
|
||||
"admin": "Ammin",
|
||||
"member": "Membro",
|
||||
"guest": "Ospite",
|
||||
"submit": "Invia",
|
||||
"claim": "Richiedi",
|
||||
"claimed": "Reclamato",
|
||||
"complete": "Completo",
|
||||
"shareTo": "Condividi su",
|
||||
"copyLink": "Copia collegamento",
|
||||
"faceBook": "Facebook",
|
||||
"whatsApp": "Whatsapp",
|
||||
"snapChat": "Snapchat",
|
||||
"taskNamePersonalGameConsume": "Spesa di gioco",
|
||||
"taskNameRoomNewMember": "Nuovi membri della stanza",
|
||||
"taskNameRoomOwnerSendRedPacket": "Il proprietario della stanza invia un pacchetto rosso",
|
||||
"taskNameRoomOwnerSendGiftUser": "Il proprietario della stanza invia un regalo",
|
||||
"taskNameRoomMicUser120Min": "Membri con più di 120 minuti al microfono",
|
||||
"taskNameRoomMicUser60Min": "Membri con più di 60 minuti al microfono",
|
||||
"taskNameRoomMicUser30Min": "Membri con più di 30 minuti al microfono",
|
||||
"taskNamePersonalSendGift": "Invia regalo all'utente",
|
||||
"taskNameRoomOnlineUserCount": "Membri online della stanza",
|
||||
"taskNameRoomOwnerInviteMic": "Invita membro al Mic",
|
||||
"taskNameRoomUserSendGiftGold": "Monete donate dai membri",
|
||||
"taskNameRoomOwnerSendGiftGold": "Monete regalate dal proprietario della stanza",
|
||||
"taskNamePersonalMagicGiftGold": "Monete vinte inviando regali magici",
|
||||
"taskNamePersonalLuckyGiftGold": "Monete vinte inviando regali fortunati",
|
||||
"taskNameRoomOwnerMicTime": "Il proprietario della stanza accende il microfono nella stanza",
|
||||
"taskNamePersonalActiveInRoom": "Sii attivo nelle stanze degli altri",
|
||||
"taskNamePersonalMicInRoom": "Vai al microfono",
|
||||
"dailyCoinBonanzaRulesDetail": "1. Le attività personali giornaliere e le attività giornaliere del proprietario della stanza possono essere completate una volta per utente al giorno. Le attività vengono ripristinate alle 00:00:00 ora saudita.\n2.Le attività personali quotidiane possono essere completate solo nelle stanze degli altri; Le attività del proprietario della stanza possono essere completate solo nella tua stanza.\n3. Le attività di donazione contano solo i regali inviati nelle stanze virtuali, non i regali inviati sui feed.\n4. Se vengono creati più account utilizzando lo stesso dispositivo o la stessa carta SIM in qualsiasi modo, i premi per tutte le attività possono essere richiesti una sola volta.",
|
||||
"dailyCoinBonanzaRules": "Regole della Bonanza giornaliera di monete",
|
||||
"roomOwnerTasks": "Compiti del proprietario della sala",
|
||||
"personalTasks": "Compiti personali",
|
||||
"noPromptsToday": "Nessun compito oggi.",
|
||||
"getPaidToRefer": "Fatti pagare per fare riferimento",
|
||||
"membershipFee": "Quota associativa",
|
||||
"membershipFeeTips1": "Imposta la quota associativa per la tua camera. Gli utenti possono unirsi alla tua stanza pagando la quota.",
|
||||
"membershipFeeTips2": "I gold necessari affinché l'utente diventi membro della stanza. Il proprietario della stanza riceverà il 50% dei gold.",
|
||||
"freePrice": "Tariffa: 0-10000",
|
||||
"touristsSendText": "Il turista invia un messaggio",
|
||||
"touristsTakeToTheMic": "I turisti prendono il microfono",
|
||||
"theMembershipFee": "La quota associativa",
|
||||
"theModificationsMade": "Le modifiche apportate questa volta non verranno salvate all'uscita",
|
||||
"viewFrame": "Visualizza cornice",
|
||||
"enterRoomName": "Inserisci il nome della stanza",
|
||||
"headdress": "Cornici",
|
||||
"mountains": "Veicoli",
|
||||
"purchaseIsSuccessful": "L'acquisto è andato a buon fine",
|
||||
"buy": "Acquista",
|
||||
"followed": "Seguito",
|
||||
"follow2": "Segui:{1}",
|
||||
"fans2": "Ventole:{1}",
|
||||
"vistors2": "Visitatori:{1}",
|
||||
"personal2": "Personale:",
|
||||
"conntinue": "Continua",
|
||||
"confirmBuyTips": "Sei sicuro di voler acquistare?",
|
||||
"purchase": "Acquistare",
|
||||
"setRoomPassword": "Imposta la password della stanza",
|
||||
"inputRoomPassword": "Inserisci la password della stanza",
|
||||
"enter": "Entra",
|
||||
"deleteCommentTips": "Sei sicuro di voler eliminare questo commento?",
|
||||
"deleteSuccessful": "Cancellazione riuscita!",
|
||||
"itemsLeft": "Oggetti rimasti",
|
||||
"password": "Parola d'ordine",
|
||||
"replySucc": "Risposta riuscita",
|
||||
"comment": "Commento",
|
||||
"showMore": "Mostra di più",
|
||||
"showLess": "Mostra meno",
|
||||
"enterPassword": "Inserisci la password",
|
||||
"enterAccount": "Inserisci Conto",
|
||||
"logIn": "Accedi",
|
||||
"saySomething": "Di' qualcosa...",
|
||||
"sayHi": "Di' ciao..",
|
||||
"pleaseChatFfriendly": "Per favore chatta gentilmente",
|
||||
"unLockTheRoom": "Sblocca la stanza",
|
||||
"operationSuccessful": "L'operazione ha avuto successo.",
|
||||
"adminByHomeowner": "è impostato come amministratore dal proprietario della casa.",
|
||||
"memberByHomeowner": "è impostato come membro dal proprietario della casa.",
|
||||
"touristByHomeowner": "è impostato come turista dal proprietario della casa.",
|
||||
"becomeHost": "Fai domanda per diventare un host",
|
||||
"superFans": "Super tifosi:",
|
||||
"setUpAnIdentity": "Configura un'identità",
|
||||
"kickedOutOfRoom": "Cacciato dalla stanza",
|
||||
"knapsack": "Zaino",
|
||||
"bdLeader": "Leader del BD",
|
||||
"picture": "Immagine",
|
||||
"theImageSizeCannotExceed": "La dimensione dell'immagine non può superare i 4M",
|
||||
"activity": "Attività",
|
||||
"alreadyAnAdministrator": "Già amministratore",
|
||||
"alreadyAnMember": "Già membro",
|
||||
"alreadyAnTourist": "Già turista",
|
||||
"touristsCannotSendMessages": "I turisti non possono inviare messaggi",
|
||||
"touristsAreNotAllowedToGoOnTheMic": "Ai turisti non è consentito avvicinarsi al microfono",
|
||||
"lockTheRoom": "Chiudi la stanza",
|
||||
"special": "Speciale",
|
||||
"visitorList": "Elenco dei visitatori",
|
||||
"successfulWear": "Indossare con successo",
|
||||
"confirmUnUseTips": "Confermi di rimuoverlo?",
|
||||
"custom": "Personalizzato",
|
||||
"customBackground": "Sfondo personalizzato",
|
||||
"example": "Esempio",
|
||||
"endPreview": "Fine anteprima",
|
||||
"selectAgain": "Seleziona di nuovo",
|
||||
"myItems": "I miei articoli",
|
||||
"use": "Utilizzare",
|
||||
"unUse": "Disequipaggiare",
|
||||
"renewal": "Rinnovo",
|
||||
"wallet": "Portafoglio",
|
||||
"profile": "Profilo",
|
||||
"giftwall": "Giftwall",
|
||||
"announcement": "Annuncio",
|
||||
"blockedList": "Elenco bloccato",
|
||||
"country2": "Paese:",
|
||||
"sendTo": "Invia a",
|
||||
"credits": "Crediti: {1}",
|
||||
"successfullyUnloaded": "Scaricato con successo",
|
||||
"expired": "Scaduto",
|
||||
"day": "Giorno",
|
||||
"inUse": "In uso",
|
||||
"staticOrGifImage": "Immagine statica/GIF",
|
||||
"confirmUseTips": "Vuoi confermarne l'utilizzo?",
|
||||
"pleaseUploadUserAvatar": "Per favore carica un avatar.",
|
||||
"joinMemberTips": "Se sei un visitatore nella stanza, non puoi prendere il microfono.",
|
||||
"giftGivingSuccessful": "Fare regali con successo.",
|
||||
"theAccountPasswordCannotBeEmpty": "L'account o la password non possono essere vuoti.",
|
||||
"invitesYouToTheMicrophone": "{1} ti invita al microfono",
|
||||
"english": "Inglese",
|
||||
"chinese": "Cinese",
|
||||
"arabic": "Arabo",
|
||||
"darkMode": "Modalità oscura",
|
||||
"lightMode": "Modalità luce",
|
||||
"systemDefault": "Predefinito del sistema",
|
||||
"pleaseGetOnTheMicFirst": "Per favore, prima prendi il microfono.",
|
||||
"roomMusicAddMusic": "Aggiungi musica",
|
||||
"roomMusicSelectAll": "Seleziona tutto",
|
||||
"roomMusicAddedCount": "Aggiunti brani {1}",
|
||||
"roomMusicEmpty": "Niente musica",
|
||||
"roomMusicScanFromPhone": "Scansione dal telefono",
|
||||
"roomMusicTotalSongs": "Totale: brani {1}",
|
||||
"roomMusicNotFound": "Nessuna musica trovata",
|
||||
"roomMusicDeleteConfirm": "Eliminare questa musica?",
|
||||
"roomMusicDeleted": "Musica cancellata",
|
||||
"roomMusicListMode": "Gioca in ordine",
|
||||
"roomMusicShuffleMode": "Riproduzione casuale",
|
||||
"roomMusicSingleMode": "Ciclo singolo",
|
||||
"duration2": "Durata:{1}",
|
||||
"roomRocketNoteTitle": "Nota",
|
||||
"roomRocketNoteSectionTitle": "Ogni volta un VIP",
|
||||
"roomRocketNoteSectionBody": "1. Ogni volta che viene acquistato un livello VIP, è valido per 30 giorni e può essere rinnovato manualmente dopo la scadenza.",
|
||||
"roomRocketRewardEmptyText": "Nessun premio guadagnato. Più fortuna la prossima volta.",
|
||||
"roomRocketRecordEmptyText": "Nessun record di ricompensa",
|
||||
"launchRocket": "Lancia il razzo"
|
||||
}
|
||||
730
assets/l10n/intl_pt.json
Normal file
730
assets/l10n/intl_pt.json
Normal file
@ -0,0 +1,730 @@
|
||||
{
|
||||
"signInWithGoogle": "Faça login com o Google",
|
||||
"or": "Ou",
|
||||
"signInWithYourAccount": "Faça login com sua conta",
|
||||
"signInWithApple": "Faça login com a Apple",
|
||||
"loginRepresentsAgreementTo": "Login representa acordo para",
|
||||
"termsofService": "Termos de serviço",
|
||||
"privaceyPolicy": "Política de Privacidade",
|
||||
"tips": "Dicas",
|
||||
"searchNoDataTips": "Insira a sala ou ID de usuário que você deseja pesquisar.",
|
||||
"games": "Jogos",
|
||||
"mine": "Meu",
|
||||
"official": "Oficial",
|
||||
"party": "Festa",
|
||||
"other": "Outro",
|
||||
"yes": "Sim",
|
||||
"bag": "Bolsa",
|
||||
"sound2": "Som",
|
||||
"startYourBrandNewJourney": "Comece sua nova jornada",
|
||||
"maliciousHarassment": "Assédio malicioso",
|
||||
"event": "Evento",
|
||||
"roomEdit": "Edição de sala",
|
||||
"cancelRoomPassword": "Tem certeza de que deseja excluir a senha da sala?",
|
||||
"roomMemberFee": "Taxa de membro do quarto",
|
||||
"roomTheme2": "Tema do quarto",
|
||||
"background": "Plano de fundo",
|
||||
"blockedList2": "Lista de Bloqueados",
|
||||
"roomPassword": "Senha da sala",
|
||||
"numberOfMic": "Número de microfone",
|
||||
"pleaseEnterContent": "Por favor insira o conteúdo",
|
||||
"profilePhoto": "Foto do perfil",
|
||||
"aboutMe": "Sobre mim",
|
||||
"myRoom": "Meu quarto",
|
||||
"noHistoricalRecordsAvailable": "Não há registros históricos disponíveis.",
|
||||
"inviteNewUsersToEarnCoins": "Convide novos usuários para ganhar moedas",
|
||||
"crateMyRoom": "CRIE SEU PRÓPRIO QUARTO.",
|
||||
"casualInteraction": "Interação casual",
|
||||
"haveGamePlayingTips": "Você tem um jogo em andamento. Por favor, saia do jogo atual. Tem certeza de que deseja sair?",
|
||||
"historicalTour": "Passeio Histórico",
|
||||
"gameCenter": "Centro de jogos",
|
||||
"returnToVoiceChat": "Voltar ao bate-papo por voz?",
|
||||
"exitGameMode": "Sair do modo de jogo",
|
||||
"enterTheRoom": "Entre na sala",
|
||||
"invite": "Convidar",
|
||||
"recent": "Recente",
|
||||
"popularEvents": "Eventos populares",
|
||||
"inviteGoRoomTips": "Sempre aqui para você, faça chuva ou faça sol. Entre e diga oi!",
|
||||
"confirmInviteThisUserToTheRoom": "Confirmar o convite deste usuário (ID:{1}) para a sala?",
|
||||
"clearCacheSuccessfully": "Limpe o cache com sucesso",
|
||||
"sent": "Enviado",
|
||||
"keep": "Manter",
|
||||
"open": "Abrir",
|
||||
"deleteAccountTips2": "*Se mudar de ideia, você poderá fazer login novamente em sua conta atual dentro de sete dias e nós restauraremos sua conta automaticamente. Se nenhuma restauração ocorrer dentro de sete dias, a conta será excluída permanentemente.",
|
||||
"deleteAccountTips": "Você tem direitos administrativos totais sobre esta conta. Caso pretenda eliminar a conta, esteja ciente dos seguintes riscos associados a esta operação:\n1. Depois que a conta for excluída com sucesso, você não poderá mais fazer login na sua conta atual. A exclusão da conta é uma ação permanente.\n2. Depois que a conta for excluída com sucesso, você não poderá recuperar nenhum dado da conta. Todas as informações (incluindo salas e amigos), moeda virtual, presentes e itens virtuais serão excluídos permanentemente e não poderão ser restaurados.\n3. Período de reflexão: Se você não restaurar a conta, não poderá acessar a página de compra, a página de retirada ou qualquer outra página do aplicativo.\n4. Durante o período de reflexão ou após a exclusão da conta, a página do perfil indicará que ela foi excluída. Para proteger a sua conta de ser pesquisada ou acessada por outras pessoas, suas informações pessoais serão removidas dos sistemas relacionados às funções diárias. Quando a exclusão de conta envolver segurança nacional, processos civis ou criminais, ou a proteção de direitos e interesses legítimos de terceiros, a equipe oficial reserva-se o direito de rejeitar a solicitação de exclusão de conta do usuário.\nSe você tiver certeza de que deseja excluir todos os dados pessoais da sua conta atual, clique em “Excluir conta”.",
|
||||
"accountDeletionNotice": "Aviso de exclusão de conta:",
|
||||
"thisUserHasBeenBlacklisted": "Este usuário foi colocado na lista negra.",
|
||||
"trend": "Tendência",
|
||||
"like": "Gosto",
|
||||
"more": "Mais",
|
||||
"discard": "Descartar",
|
||||
"catchFirstComment": "Pegue o primeiro comentário",
|
||||
"reply": "Responder",
|
||||
"posting": "Postagem",
|
||||
"multiple": "Múltiplo",
|
||||
"successfullyRemovedFromTheBlacklist": "Removido com sucesso da lista negra!",
|
||||
"successfullyAddedToTheBlacklist": "Adicionado com sucesso à lista negra!",
|
||||
"youAreCurrentlyCPRelationshipPleaseDissolve": "Você está atualmente em um relacionamento CP.\nPor favor, dissolva-o primeiro.",
|
||||
"areYouSureToCancelBlacklist": "Tem certeza de cancelar a lista negra?",
|
||||
"areYouSureYouWantToBlockThisUser": "Tem certeza de que deseja bloquear este usuário?",
|
||||
"removeFromBlacklist": "Remover da lista negra",
|
||||
"moveToBlacklist": "Mover para a lista negra",
|
||||
"userBlacklist": "Lista negra de usuários",
|
||||
"specialEffectsManagement": "Gerenciamento de efeitos especiais",
|
||||
"wishingYouHappinessEveryDay": "Desejando-lhe felicidade todos os dias.",
|
||||
"newMessage": "Nova mensagem",
|
||||
"createRoomSuccsess": "Sala criada com sucesso!",
|
||||
"contactUs": "Contate-nos",
|
||||
"systemAnnouncementTips1": "Proteja-se contra fraudes:",
|
||||
"systemAnnouncementTips": "Verifique as informações exclusivamente através dos canais oficiais. Nunca baixe software de terceiros, compartilhe dados pessoais ou transfira dinheiro com base em solicitações externas. Os IDs oficiais dos funcionários são apenas 10.000, 10.003 e 10.086. Para qualquer suspeita, pare e informe via",
|
||||
"systemAnnouncement": "Anúncio do sistema",
|
||||
"doNotClickUnfamiliarTips": "Não clique em links desconhecidos, pois eles podem expor suas informações pessoais. Nunca compartilhe seus dados de identidade ou cartão bancário com ninguém.",
|
||||
"atTag": "@Etiqueta",
|
||||
"sayHi2": "Diga oi",
|
||||
"roomBottomGreeting": "Olá...",
|
||||
"canSendMsgTips": "Ambas as partes precisam seguir uma à outra antes de enviar mensagens privadas.",
|
||||
"msgSendRedEnvelopeTips": "*Será cobrada uma taxa de serviço de 10% sobre os envelopes vermelhos e os destinatários receberão apenas 90% do valor do envelope vermelho. O nível de riqueza do remetente deve ser superior ao Nível 10.",
|
||||
"reapply": "Reaplicar",
|
||||
"cancelRequest": "Cancelar solicitação",
|
||||
"pending": "Pendente",
|
||||
"supporter": "Apoiador",
|
||||
"coinsReceived": "Moedas recebidas",
|
||||
"numberOfSign": "Número do sinal: {1}",
|
||||
"hostWeeklyRank": "Classificação semanal do anfitrião",
|
||||
"supporterWeeklyRank": "Classificação Semanal do Apoiador",
|
||||
"memberList": "Lista de membros",
|
||||
"treasureChest": "Baú do Tesouro",
|
||||
"applicationRecord": "Registro de aplicação",
|
||||
"appUpdateTip": "O aplicativo possui uma nova versão ({1}). Gostaria de baixá-lo agora?",
|
||||
"ownerIncomeCoins": "Renda do proprietário: moedas {1}",
|
||||
"game": "Jogo",
|
||||
"skip2": "Pular",
|
||||
"skipCountdown": "Pular {1}s",
|
||||
"coins4": "Moedas",
|
||||
"weekStart": "Início da semana",
|
||||
"forMoreRewardsPleaseCheckTheTaskCenter": "Para mais recompensas, verifique o centro de tarefas",
|
||||
"kingQuuen": "Rei-Rainha",
|
||||
"ramadan": "Ramadã",
|
||||
"updateNow": "Atualizar agora",
|
||||
"allGames": "Todos os jogos",
|
||||
"fishClass": "Classe de Peixe",
|
||||
"greedyClass": "Classe gananciosa",
|
||||
"raceSeries": "Série de corridas",
|
||||
"slotsClass": "Classe de caça-níqueis",
|
||||
"others": "Outros",
|
||||
"hotGames": "Jogos quentes",
|
||||
"chatBox": "Caixa de bate-papo",
|
||||
"termsOfServicePrivacyPolicyTips": "Ao continuar você concorda com os Termos de Serviço e Política de Privacidade",
|
||||
"and": "e",
|
||||
"pleaseSelectTheTypeContent": "Selecione o tipo de conteúdo ofensivo.",
|
||||
"illegalInformation": "Informação ilegal",
|
||||
"inappropriateContent": "Conteúdo impróprio",
|
||||
"personalAttack": "Ataque pessoal",
|
||||
"confirm": "Confirmar",
|
||||
"spam": "Spam",
|
||||
"countdownMinutes": "Minutos de contagem regressiva:",
|
||||
"number2": "Número:",
|
||||
"fraud": "Fraude",
|
||||
"received": "Recebido",
|
||||
"currentProgress": "Progresso atual",
|
||||
"currentStage": "Estágio atual: {1}",
|
||||
"roomReward2": "Recompensa do Quarto:{1}",
|
||||
"roomReward": "Recompensa de quarto",
|
||||
"expirationTime": "Prazo de validade",
|
||||
"ownerSendTheRedEnvelope": "O proprietário enviou moedas de recompensa.",
|
||||
"rewardCoins": "Moedas de recompensa: moedas {1}",
|
||||
"signInRewardReceived": "Login realizado com sucesso. Recompensa: {1}",
|
||||
"registerRewardReceived": "Recompensa de registro recebida: {1}",
|
||||
"lastWeekProgress": "Progresso da semana passada",
|
||||
"redEnvelopeTips2": "*Se o envelope vermelho não for reclamado dentro do prazo, as restantes moedas serão devolvidas ao utilizador que enviou o envelope vermelho.",
|
||||
"goToRecharge": "Vá para recarregar",
|
||||
"deleteAccount2": "Excluir conta ({1}s)",
|
||||
"areYouSureYouWantToDeleteYourAccount": "Tem certeza de que deseja excluir sua conta?",
|
||||
"insufhcientGoldsGoToRecharge": "Ouro insuficiente. Recarregue agora!",
|
||||
"coins2": "Moedas {1}",
|
||||
"remainingNumberTips": "Restante disponível: ({1}/{2})",
|
||||
"collectionTimeTips": "Tempo de coleta: {1} ({2}/{3})",
|
||||
"sendARedEnvelope": "Envie um envelope vermelho",
|
||||
"sendRedPackConfirmTips": "Tem certeza de que deseja enviar o pacote vermelho?",
|
||||
"redEnvelopeSendingRecords": "Registros de envio de envelope vermelho:",
|
||||
"redEnvelope": "Envelope Vermelho",
|
||||
"roomRedPacketGoldCoins": "Moedas de ouro",
|
||||
"roomRedPacketQuantity": "Quantidade",
|
||||
"roomRedPacketPickupTime": "Horário de coleta",
|
||||
"roomRedPacketInMinutes": "Em minutos {1}",
|
||||
"roomRedPacketClaimNow": "Reivindique agora",
|
||||
"roomRedPacketSendLuckyPack": "Envie um pacote da sorte",
|
||||
"roomRedPacketSending": "Enviando...",
|
||||
"roomRedPacketSendSuccess": "Pacote da sorte enviado",
|
||||
"roomRedPacketComingSoon": "Em breve",
|
||||
"roomRedPacketRulesTitle": "Regras",
|
||||
"roomRedPacketRulesContent": "1. Você pode gastar moedas para enviar um Lucky Pack. Se outras pessoas abrirem seu Lucky Pack, elas poderão receber uma quantidade aleatória de moedas.\n\n2. Duas horas após o envio do Lucky Pack, todas as moedas não reclamadas serão devolvidas à sua carteira.\n\n3. Depois de enviar um Lucky Pack para todo o servidor, um banner do Lucky Pack para todo o servidor será acionado, trazendo mais atenção para você.\n\n4. Você pode enviar Lucky Packs da sala %1$s por dia.\n\n5. Os Lucky Packs enviados por usuários fora da sua região não podem ser resgatados.",
|
||||
"roomRedPacketHistoryTitle": "Histórico de entrega do Lucky Pack",
|
||||
"roomRedPacketHistoryItemTitle": "Lucky Bag (envelopes vermelhos {1})",
|
||||
"roomRedPacketHistoryStatusSent": "Enviado",
|
||||
"roomRedPacketHistoryStatusFinished": "Totalmente reivindicado",
|
||||
"roomRedPacketHistoryStatusPending": "Pendente",
|
||||
"roomRedPacketHistoryStatusReturned": "Devolvido",
|
||||
"roomRedPacketHistoryRefund": "Reembolso: {1}",
|
||||
"roomRedPacketClaimRecordsTitle": "Registros de reivindicações",
|
||||
"roomRedPacketChatMessage": "Mandei para você um envelope vermelho, abra agora mesmo!",
|
||||
"roomRedPacketReceivedMessage": "Reivindique moedas {1} do Lucky Pack {2}",
|
||||
"roomRedPacketOpenSubtitle": "O Pacote da Sorte Chegou",
|
||||
"roomRedPacketOpenYouGot": "Você consegue",
|
||||
"roomRedPacketClaimSuccessTips": "As moedas foram enviadas para sua carteira.",
|
||||
"roomRedPacketOpenSoldOut": "O Lucky Pack já está esgotado.",
|
||||
"roomRedPacketOpenExpired": "O Pacote da Sorte expirou.",
|
||||
"roomRedPacketRegionNotMatch": "Somente usuários na mesma região linguística podem reivindicar este pacote da sorte.",
|
||||
"roomRedPacketNotStarted": "O pacote da sorte ainda não está pronto.",
|
||||
"roomRedPacketDailyLimitExceeded": "O limite de envio de hoje foi atingido.",
|
||||
"roomRedPacketInsufficientBalance": "Moedas de ouro insuficientes.",
|
||||
"roomRedPacketPresenceRequired": "Por favor, fique no quarto para usar os pacotes da sorte.",
|
||||
"roomRedPacketNetworkError": "Não é possível processar o pacote da sorte no momento.",
|
||||
"roomRedPacketAlreadyClaimed": "Você já reivindicou este pacote da sorte.",
|
||||
"roomRedPacketOpenWaiting": "Pode ser aberto em minutos {1}.",
|
||||
"roomRedPacketOpenWaitingCountdown": "Disponível para retirada após {1}",
|
||||
"roomRedPacketOkay": "Ok",
|
||||
"roomRedPacketFloatingTitle": "{1} enviou um envelope vermelho",
|
||||
"roomRedPacketFloatingSubtitle": "Enviou um envelope vermelho",
|
||||
"redEnvelopeRecTips2": "Os envelopes vermelhos foram todos reivindicados.",
|
||||
"redEnvelopeRecTips3": "O prazo de coleta do envelope vermelho expirou!",
|
||||
"openTheTreasureChest": "Abra o baú do tesouro",
|
||||
"redEnvelopeRecTips1": "As moedas ganhas foram depositadas em sua carteira.",
|
||||
"redEnvelopeTips1": "Moedas:",
|
||||
"roomTools": "Ferramentas da sala:",
|
||||
"entertainment": "Entretenimento:",
|
||||
"reportSucc": "Relatório bem-sucedido",
|
||||
"pornography": "Pornografia",
|
||||
"reportInputTips": "Descreva o problema com o máximo de detalhes possível para que possamos entendê-lo e resolvê-lo.",
|
||||
"cancel": "Cancelar",
|
||||
"join": "Junte-se",
|
||||
"items": "Itens",
|
||||
"vistors": "Visitantes",
|
||||
"fans": "Fãs",
|
||||
"balanceNotEnough": "Saldo insuficiente de moedas de ouro. Você quer ir para recarregar?",
|
||||
"skip": "Pular {1}",
|
||||
"letGoToWatch": "Vamos assistir!",
|
||||
"launchedARocket": "lançou um foguete",
|
||||
"sendUserId": "Enviar ID do usuário:{1}",
|
||||
"giveUpIdentity": "Desista da identidade",
|
||||
"leaveRoomIdentityTips": "Tem certeza de que deseja desistir da identidade do quarto?",
|
||||
"joinMemberTips2": "Deseja confirmar a entrada na sala como membro?",
|
||||
"sureUnfollowThisRoom": "Tem certeza de parar de seguir esta sala?",
|
||||
"welcomeMessage": "Bem-vindo ao nosso aplicativo, {name}!",
|
||||
"settings": "Configurações",
|
||||
"account": "Conta",
|
||||
"common": "Comum",
|
||||
"delete": "Excluir",
|
||||
"copy": "Copiar",
|
||||
"bio": "Biografia",
|
||||
"useCoupontips": "Tem certeza que deseja usar o cupom?",
|
||||
"searchUserId": "Pesquisar ID do usuário",
|
||||
"sendUser": "Enviar usuário",
|
||||
"hobby": "Passatempo",
|
||||
"sendCoupontips": "Tem certeza de que deseja enviar este cupom para este usuário?",
|
||||
"youDontHaveAnyCouponsYet": "Você ainda não tem cupons.",
|
||||
"recall": "Lembre-se",
|
||||
"youHaventFollowed": "Você não seguiu nenhuma sala",
|
||||
"deleteFromMyDevice": "Excluir do meu dispositivo",
|
||||
"deleteOnAllDevices": "Excluir em todos os dispositivos",
|
||||
"messageHasBeenRecalled": "Esta mensagem foi recuperada",
|
||||
"recallThisMessage": "Lembra desta mensagem?",
|
||||
"language": "Idioma",
|
||||
"feedback": "Comentários",
|
||||
"signedin": "Conectado",
|
||||
"receiveSucc": "Reivindicado com sucesso",
|
||||
"about": "Sobre",
|
||||
"aboutUs": "Sobre nós",
|
||||
"theme": "Tema",
|
||||
"wealthLevel": "Nível de riqueza",
|
||||
"userLevel": "Nível de usuário",
|
||||
"goToUpload": "Ir para carregar",
|
||||
"logout": "Sair",
|
||||
"luck": "Sorte",
|
||||
"level": "Nível",
|
||||
"themeGoToUploadTips": "1. A revisão será concluída dentro de 24 horas após o sucesso do upload.\n2. Todas as moedas serão devolvidas se a revisão falhar.",
|
||||
"pleaseUploadAccordingToExample": "Faça upload de acordo com o exemplo fornecido.",
|
||||
"home": "Página inicial",
|
||||
"explore": "Explorar",
|
||||
"me": "Eu",
|
||||
"socialPrivilege": "Privilégio social",
|
||||
"information": "Informação",
|
||||
"myPhoto": "Minha foto",
|
||||
"areYouSureYouWantToSpend3": "*Se a outra parte recusar o convite do CP, suas moedas serão devolvidas à sua carteira.",
|
||||
"areYouSureYouWantToSpend": "Tem certeza que deseja gastar",
|
||||
"areYouSureYouWantToSpend2": "enviar um convite CP para este usuário?",
|
||||
"underReview": "Em revisão",
|
||||
"doYouWantToDeleteIt": "Deseja excluí-lo?",
|
||||
"chooseFromAblum": "Escolha do álbum",
|
||||
"spaceBackground": "Fundo do espaço",
|
||||
"editProfile": "Editar perfil",
|
||||
"sendTheCpRequest": "Envie a solicitação de CP",
|
||||
"addCp": "Adicionar CP",
|
||||
"partWays": "Partes",
|
||||
"reconcile": "Reconciliar",
|
||||
"separated": "Separado",
|
||||
"areYouSureYouWantToSpend5": "{1} confessou o sentimento para você; se você aceitar, vocês se tornarão um casal.",
|
||||
"areYouSureYouWantToSpend6": "{1} quer voltar com você. Se você decidir reconciliar, todos os seus dados anteriores serão restaurados.",
|
||||
"reconcileInvitationTips": "*Se a outra parte recusar o convite do CP, suas moedas serão devolvidas à sua carteira.",
|
||||
"reconcileInvitation": "Convite de reconciliação",
|
||||
"areYouSureYouWantToSpend4": "enviar um convite de reconciliação para este usuário?",
|
||||
"partWaysTips": "*Se um dos parceiros de um casal decidir se separar, haverá um período de reflexão de 7 dias. Durante esse período, ambas as partes podem optar pela reconciliação e todos os dados serão restaurados após a reconciliação. Caso não seja escolhida nenhuma conciliação até o final do período de 7 dias, os dados do casal serão apagados.",
|
||||
"areYouSureYouWantToPartWaysWithYourCP": "Tem certeza de que deseja se separar do seu CP?",
|
||||
"timeSpentTogether": "Tempo que passamos juntos: dias {1}",
|
||||
"firstDay": "Primeiro dia: {1}",
|
||||
"numberOfMyCPs": "Número dos meus CPs:({1}/{2})",
|
||||
"props": "Adereços",
|
||||
"win": "Ganhar",
|
||||
"dice": "Dados",
|
||||
"rps": "RPS",
|
||||
"operationFail": "A operação falhou.",
|
||||
"enterRoomFailedRetry": "Falha ao entrar na sala. Por favor, tente novamente.",
|
||||
"voiceConnectionFailedRetry": "A conexão de voz falhou. Por favor, tente novamente.",
|
||||
"likedYourComment": "Gostei do seu comentário.",
|
||||
"doYouWantToKeepTheDraft": "Você quer manter o rascunho?",
|
||||
"operationsAreTooFrequent": "As operações são muito frequentes",
|
||||
"luckNumber": "Número da sorte",
|
||||
"relationShip": "Relacionamento",
|
||||
"couple": "Casal {1}:",
|
||||
"couple2": "Casal",
|
||||
"reject": "Rejeitar",
|
||||
"accept": "Aceitar",
|
||||
"noMatchedCP": "Nenhum CP correspondente",
|
||||
"inviteYouToBecomeBD": "Convido você a se tornar um BD.",
|
||||
"adminInviteRechargeAgent": "Convido você a se tornar um agente Recharge.",
|
||||
"confirmAcceptTheInvitation": "Confirmar para aceitar o convite?",
|
||||
"confirmDeclineTheInvitation": "Confirmar a recusa do convite?",
|
||||
"host": "Anfitrião",
|
||||
"following": "Seguindo",
|
||||
"agent": "Agência",
|
||||
"approved": "Aprovado",
|
||||
"approvedWithinOneMinute": "Aprovado e exibido dentro de 1 minuto após o envio",
|
||||
"onlineUsers": "Usuários on-line ({1}/{2}):",
|
||||
"applyToJoin": "Inscreva-se para participar",
|
||||
"supporterList": "Lista de apoiadores",
|
||||
"hostList": "Lista de anfitriões",
|
||||
"upToAdmins": "Até administradores {1}",
|
||||
"upToMembers": "Até membros {1}",
|
||||
"levelPrivileges": "Privilégios de nível",
|
||||
"ra": "RA",
|
||||
"roomAnnouncement": "Anúncio da sala",
|
||||
"help": "Ajuda",
|
||||
"rejected": "Rejeitado",
|
||||
"boxContributeTips": "O investimento já foi feito hoje. Por favor, não invista novamente.",
|
||||
"bd": "BD",
|
||||
"coupon": "Cupom",
|
||||
"search": "Pesquisar",
|
||||
"get": "Obter",
|
||||
"inRocket": "No foguete",
|
||||
"roomRocketHelpTips": "1. Enviar presentes na sala aumenta a energia do foguete. *1 presente de moeda de ouro = 1 ponto de energia de foguete; presentes da sorte aumentam a energia do foguete em 4% do valor da moeda de ouro do presente.\n2. Assim que a energia do foguete estiver totalmente carregada, a sala poderá lançar o foguete. As recompensas serão distribuídas automaticamente após o lançamento.\n3. Diferentes níveis de foguete oferecem recompensas diferentes.\n4. Quando o foguete for lançado, todos os usuários na sala poderão reivindicar a recompensa do foguete.\n5. A energia do foguete é reiniciada às 00:00 todos os dias.",
|
||||
"couponRecord": "Registro de uso de cupom",
|
||||
"inRoom": "No quarto",
|
||||
"searchCouponHint": "Cupom de pesquisa",
|
||||
"giftCounter": "Contador de presentes",
|
||||
"bDLeaderInviteYouToBecomeBDLeader": "Convido você para se tornar um BDLader",
|
||||
"wins": "vence",
|
||||
"inviteYouToBecomeHost": "Convido você para se tornar um anfitrião.",
|
||||
"friends": "Amigos",
|
||||
"deleteConversationTips": "Tem certeza de que deseja excluir o histórico de bate-papo com este usuário?",
|
||||
"propMessagePrompt": "Prompt de mensagem prop",
|
||||
"inputUserId": "Insira o ID do usuário",
|
||||
"fromLuckyGifts": "De presentes de sorte/mágicos",
|
||||
"receive": "Receber",
|
||||
"checkInSuccessful": "Check-in bem-sucedido",
|
||||
"sginTips": "Você receberá uma recompensa na primeira vez que fizer login todos os dias. Se você interromper sua sequência de login, a recompensa será recalculada a partir do primeiro dia quando você fizer login novamente.",
|
||||
"popular": "Populares",
|
||||
"recommend": "Recomendar",
|
||||
"follow": "Siga",
|
||||
"history": "História",
|
||||
"hotRooms": "Salas Quentes",
|
||||
"viewMore": "Ver mais",
|
||||
"noData": "Sem dados",
|
||||
"users": "Usuários",
|
||||
"rooms": "Quartos",
|
||||
"coins": "moedas",
|
||||
"unread": "Não lido",
|
||||
"read": "Leia",
|
||||
"image": "[Imagem]",
|
||||
"video": "[Vídeo]",
|
||||
"sound": "[Som]",
|
||||
"gift2": "[Presente]",
|
||||
"clickHereToStartChatting": "Diga alguma coisa.....",
|
||||
"receivedAMessage": "[Recebi uma mensagem]",
|
||||
"confirmSwitchMicModelTips": "Você confirma a mudança no modo sentado?",
|
||||
"number": "Número",
|
||||
"album": "Álbum",
|
||||
"camera": "Câmera",
|
||||
"system": "Sistema",
|
||||
"notifcation": "Notificação",
|
||||
"inviteYouToBecomeAgent": "Convido você a se tornar uma agência.",
|
||||
"myMusic": "Minha música",
|
||||
"add": "Adicionar",
|
||||
"pullToLoadMore": "Puxe para carregar mais",
|
||||
"loadingFailedClickToRetry": "Falha ao carregar. Clique para tentar novamente",
|
||||
"releaseToLoadMore": "Solte para carregar mais",
|
||||
"haveMyLimits": "---Eu tenho meus limites.---",
|
||||
"music": "Música",
|
||||
"free": "Grátis",
|
||||
"charm": "Amuleto de presente",
|
||||
"start": "Começar",
|
||||
"stop": "Pare",
|
||||
"chats": "Bate-papos",
|
||||
"refuse": "Recusar",
|
||||
"agree": "Concordo",
|
||||
"thisFeatureIsCurrentlyUnavailable": "Este recurso não está disponível no momento.",
|
||||
"pleaseSelectTheRecipient": "Selecione o destinatário.",
|
||||
"searchMemberIdHint": "Por favor insira o ID do membro",
|
||||
"unclaimedRedEnvelopes": "Envelopes vermelhos não reclamados serão reembolsados em 24 horas.",
|
||||
"redEnvelopeNotYetClaimed": "Envelope vermelho ainda não reclamado.",
|
||||
"redEnvelopeAmount": "Valor do envelope vermelho: moedas {1}",
|
||||
"sentARedEnvelope": "enviou um envelope vermelho.",
|
||||
"theRedEnvelopeHasExpired": "O envelope vermelho expirou.",
|
||||
"joinRequest": "Solicitação de adesão",
|
||||
"scrollToTheBottom": "Role até o final",
|
||||
"gameRules": "Regras do jogo:",
|
||||
"charmGameRulesTips": "Ative o painel para mostrar as moedas de presente recebidas de todos os usuários no microfone. 1 moeda = 1 pontuação (Presente da sorte: 1 moeda = 0,04 pontuação).",
|
||||
"inputYourOldPassword": "Insira sua senha antiga",
|
||||
"enterYourOldPassword": "Digite sua senha antiga",
|
||||
"setYourPassword": "Defina sua senha",
|
||||
"enterYourNewPassword": "Digite sua nova senha",
|
||||
"confirmYourPassword": "Confirme sua senha",
|
||||
"theTwoPasswordsDoNotMatch": "As duas senhas não coincidem.",
|
||||
"resetLoginPasswordtTips2": "A senha deve ter de 8 a 16 caracteres e ser uma combinação de letras e números ingleses maiúsculos e minúsculos (não apenas números).",
|
||||
"resetLoginPassword": "Redefinir senha de login",
|
||||
"resetLoginPasswordtTips1": "Faça login com seu ID de usuário ou ID personalizado. É mais seguro fazer login com uma conta yumi.",
|
||||
"localMusic": "Música local",
|
||||
"setLoginPassword": "Definir senha de login",
|
||||
"confirmSwitchMicThemeTips": "Você confirma a mudança de estilo do assento?",
|
||||
"micTheme": "Tema do microfone",
|
||||
"classicMic": "Microfone {1} clássico",
|
||||
"yesterday": "Ontem {1}",
|
||||
"monday": "Segunda-feira {1}",
|
||||
"tuesday": "Terça-feira {1}",
|
||||
"wednesday": "Quarta-feira {1}",
|
||||
"thursday": "Quinta-feira {1}",
|
||||
"friday": "Sexta-feira {1}",
|
||||
"saturday": "Sábado {1}",
|
||||
"sunday": "Domingo {1}",
|
||||
"acceptedYour": "{1} aceitou seu",
|
||||
"youAccepted": "Você aceitou {1}",
|
||||
"openRedPackDialogTip": "O envelope vermelho",
|
||||
"micManagement": "Gerenciamento de microfone",
|
||||
"bdCenter": "Centro de BD",
|
||||
"rechargeAgency": "Agência de recarga",
|
||||
"adminCenter": "Centro de administração",
|
||||
"managerCenter": "Centro de gerenciamento",
|
||||
"goldList": "Lista de Ouro",
|
||||
"followList": "Seguir lista",
|
||||
"fansList": "Lista de fãs",
|
||||
"daily": "Diariamente",
|
||||
"female": "Feminino",
|
||||
"male": "Masculino",
|
||||
"identity": "Identidade",
|
||||
"adjust": "Ajustar",
|
||||
"warning": "Aviso",
|
||||
"screenshotTips": "Captura de tela (até 3)",
|
||||
"roomNotice": "Aviso de sala",
|
||||
"roomTheme": "Tema do quarto",
|
||||
"description": "Descrição:",
|
||||
"inputDesHint": "Descreva o problema com o máximo de detalhes possível para que possamos entendê-lo e resolvê-lo.",
|
||||
"roomProfilePicture": "Foto do perfil da sala",
|
||||
"userProfilePicture": "Foto do perfil do usuário",
|
||||
"userName": "Nome de usuário",
|
||||
"pleaseSelectTheTypeToProcess": "Selecione o tipo a ser processado:",
|
||||
"roomEditing": "Edição de sala",
|
||||
"setAccount": "Definir conta",
|
||||
"userEditing": "Edição do usuário",
|
||||
"enterTheUserId": "Insira o ID do usuário",
|
||||
"enterTheRoomId": "Insira o roomId",
|
||||
"deleteAccount": "Excluir conta",
|
||||
"becomeAgent": "Torne-se um agente",
|
||||
"enterNickname": "Digite o apelido",
|
||||
"enterInvitationCode": "insira o código do convite",
|
||||
"selectYourCountry": "Selecione seu país",
|
||||
"inviteCode": "Código de convite",
|
||||
"magic": "Magia",
|
||||
"luckGiftSpecialEffects": "Efeitos de animação de presente da sorte",
|
||||
"theVideoSizeCannotExceed": "O tamanho do vídeo não pode exceder 50M",
|
||||
"weekly": "Semanalmente",
|
||||
"customizedGiftRulesContent": "Como obter meu presente personalizado:\n1.Determine se o usuário está qualificado para receber um presente personalizado com base em seu nível de riqueza atual.\n(1) Quando o nível de riqueza do usuário for ≥35: O usuário pode enviar um vídeo para o presente personalizado uma vez. Usaremos a foto do perfil atual do usuário como imagem de presente e o vídeo fornecido como efeito de presente em \"Personalizado\". A produção levará algum tempo e avisaremos assim que estiver disponível na loja.\n(2) Quando o nível de riqueza do usuário for ≥45: O usuário pode enviar um vídeo para o presente personalizado uma vez. Usaremos a foto do perfil atual do usuário como imagem de presente e o vídeo fornecido como efeito de presente em \"Personalizado\". A produção levará algum tempo e avisaremos assim que estiver disponível na loja.\n2.Você pode participar de atividades específicas no app e, após atender aos critérios, entrar em contato conosco através da seção “Mensagem” → “Fale conosco”. Forneça capturas de tela da atividade e do vídeo que deseja usar para o presente personalizado. Usaremos sua foto de perfil atual como imagem de presente e o vídeo fornecido como efeito de presente e, em seguida, listaremos em \"Personalizado\". A produção levará algum tempo e avisaremos assim que estiver disponível na loja.\nPeríodo de validade do presente personalizado e como estendê-lo:\nOs brindes personalizados exclusivos ficarão disponíveis na gôndola pelo prazo de validade de 30 dias. Para permitir que usuários influentes e inspiradores continuem aproveitando essa nova experiência e mostrem seu estilo pessoal, os usuários que possuem presentes personalizados podem estender o prazo de validade de todos os seus presentes personalizados em 30 dias, recarregando US$ 500 em qualquer mês.",
|
||||
"customizedGiftRules": "Regras de presentes personalizados",
|
||||
"rulesUpload": "Regras e upload",
|
||||
"monthly": "Mensalmente",
|
||||
"message": "Mensagem",
|
||||
"clearCache": "Limpar Cache",
|
||||
"customized": "Personalizado",
|
||||
"searchInputHint": "Insira o número da conta/quarto",
|
||||
"kickRoomTips": "Você foi expulso da sala.",
|
||||
"joinRoomTips": "Entrei na sala!",
|
||||
"roomSetting": "Configuração da sala",
|
||||
"roomDetails": "Detalhes do quarto",
|
||||
"systemRoomTips": "Seja educado e respeitoso. Qualquer conteúdo pornográfico ou vulgar é estritamente proibido no yumi. Uma vez descoberta, a conta será banida permanentemente. Por favor, cumpra conscientemente os regulamentos da plataforma yumi.",
|
||||
"copiedToClipboard": "Copiado para a área de transferência",
|
||||
"recharge": "Recarregar",
|
||||
"receivedFromALuckyGift": "Recebido de um presente de sorte/mágico.",
|
||||
"followedYou": "Segui você",
|
||||
"agentCenter": "Central de Agências",
|
||||
"areYouSureYouWantToClearLocalCache": "Tem certeza de que deseja limpar o cache local?",
|
||||
"clearMessage": "Limpar mensagens da tela",
|
||||
"report": "Relatório",
|
||||
"coins3": "Moedas",
|
||||
"giftSpecialEffects": "Efeitos especiais de presente",
|
||||
"basicFeatures": "Recursos básicos",
|
||||
"task": "Tarefa",
|
||||
"importantReminder": "Lembrete importante",
|
||||
"entryVehicleAnimation": "Animação de veículo de entrada",
|
||||
"floatingAnimationInGlobal": "Animação flutuante em global",
|
||||
"broadcast": "Transmitir",
|
||||
"broadcastDisplay": "Exibição de transmissão",
|
||||
"broadcastAllPlaces": "Todos os lugares",
|
||||
"broadcastRoomOnly": "No quarto",
|
||||
"broadcastOff": "Fechar transmissão",
|
||||
"entryVehicleAnimation2": "Usuários com privilégios VIP4 ou superiores podem usar esta função para desativar animações de veículos.",
|
||||
"dailyTasks": "Tarefas Diárias",
|
||||
"exclusiveForNewcomers": "Exclusivo para recém-chegados",
|
||||
"resetsDailyAtMidnight": "Reinicia diariamente à meia-noite",
|
||||
"limitedToOneTime": "Limitado a uma única vez",
|
||||
"useMicrophoneForOneMin": "Use o microfone por 1 minuto",
|
||||
"taskRequirement": "Requisito",
|
||||
"taskProgress": "Progresso",
|
||||
"rewardClaimedSuccessfully": "Recompensa reivindicada com sucesso",
|
||||
"rewardClaimFailed": "Falha na reivindicação do prêmio",
|
||||
"enterRoomConfirmTips": "Tem certeza de que deseja entrar na sala?",
|
||||
"followSucc": "Seguido com sucesso",
|
||||
"goldListort": "Lista de Ouro",
|
||||
"rechargeList": "Lista de recarga",
|
||||
"edit": "Editar",
|
||||
"swipeLeftOnTheFloatingScreenAreaToQuicklyCloseIt": "*Dica: deslize para a esquerda na área flutuante da tela para fechá-la rapidamente.",
|
||||
"enterThisVoiceChatRoom": "Entrar nesta sala de bate-papo por voz?",
|
||||
"go": "Vá",
|
||||
"done": "Concluído",
|
||||
"improvementTasks": "Tarefas de melhoria",
|
||||
"save": "Salvar",
|
||||
"kickPrevention": "Prevenção de chutes",
|
||||
"freeChatSpeak": "Bate-papo e conversa grátis",
|
||||
"nickName": "Apelido",
|
||||
"gender": "Gênero",
|
||||
"unFollow": "Deixar de seguir",
|
||||
"days": "Dias",
|
||||
"permanent": "Permanente",
|
||||
"country": "País",
|
||||
"birthday": "Aniversário",
|
||||
"man": "Homem",
|
||||
"woman": "Mulher",
|
||||
"apple": "maçã",
|
||||
"google": "Google",
|
||||
"startVoiceParty": "Comece uma festa de voz!",
|
||||
"enterRoomTips": "{1} Entre na sala",
|
||||
"roomName": "Nome do quarto",
|
||||
"roomMember": "Membro da Sala",
|
||||
"pleaseSelectYourCountry": "Por favor selecione seu país.",
|
||||
"pleaseSelectYourGender": "Por favor selecione seu gênero.",
|
||||
"pleaseEnterNickname": "Por favor insira um apelido.",
|
||||
"countryRegion": "País e região",
|
||||
"dateOfBirth": "Data de nascimento",
|
||||
"mute": "Mudo",
|
||||
"exit": "Sair",
|
||||
"mysteriousInvisibility": "Invisibilidade misteriosa",
|
||||
"antiBlock": "Anti-bloqueio",
|
||||
"privateChat": "Bate-papo privado",
|
||||
"storeDiscount": "Desconto da loja {1} desativado",
|
||||
"membershipFreeChatSpeak": "Bate-papo e conversa sem assinatura",
|
||||
"priorityRoomSorting": "Classificação prioritária de salas",
|
||||
"userColoredID": "ID colorido do usuário",
|
||||
"pleaseSelectaItem": "Selecione um item",
|
||||
"areYouRureRoRecharge": "Tem certeza de que deseja recarregar?",
|
||||
"mInimize": "Mantenha",
|
||||
"everyone": "Todos",
|
||||
"shop": "Loja",
|
||||
"roomOwner": "Proprietário do quarto",
|
||||
"dailyTaskRewardBonus": "Bônus de recompensa de tarefa diária ({1} XP)",
|
||||
"userLevelXPBoost": "Aumento de XP no nível do usuário ({1} XP)",
|
||||
"andAboveUsers": "{1} e usuários superiores",
|
||||
"privileges": "Privilégios {1}",
|
||||
"basicPermissions": "Permissões básicas",
|
||||
"hostCenter": "Centro de hospedagem",
|
||||
"allOnMicrophone": "Tudo no microfone",
|
||||
"usersOnMicrophone": "Usuários no microfone",
|
||||
"allInTheRoom": "Todos no quarto",
|
||||
"send": "Enviar",
|
||||
"crop": "Cortar",
|
||||
"goToUpgrade": "Ir para atualizar",
|
||||
"exclusiveEmojiWillBeReleasedAfterBecoming": "Emoji exclusivo será lançado após se tornar",
|
||||
"preventBeingBlocked": "Evite ser bloqueado",
|
||||
"enableRankIncognitoMode": "Ativar modo de classificação anônima",
|
||||
"avoidBeingKicked": "Evite ser chutado",
|
||||
"finish": "Concluir",
|
||||
"takeTheMic": "Pegue o microfone",
|
||||
"openTheMic": "Abra o microfone",
|
||||
"muteTheMic": "Silenciar o microfone",
|
||||
"unlockTheMic": "Desbloqueie o microfone",
|
||||
"leavelTheMic": "Deixe o microfone",
|
||||
"lockTheMic": "Bloqueie o microfone",
|
||||
"removeTheMic": "Remova o microfone",
|
||||
"inviteToTheMicrophone": "Convide para o microfone",
|
||||
"openUserProfleCard": "Abra o cartão de perfil do usuário",
|
||||
"obtain": "obter",
|
||||
"win2": "Ganhe {1}",
|
||||
"backTheRoom": "Sala dos fundos",
|
||||
"toConsume": "Para consumir",
|
||||
"howToUpgrade": "Como atualizar?",
|
||||
"spendCoinsToGainExperiencePoints": "Gaste moedas para ganhar pontos de experiência",
|
||||
"higherLevelFancierAvatarFrame": "Emblemas/moldura de avatar de nível superior e mais sofisticados",
|
||||
"all": "Todos",
|
||||
"gift": "Presente",
|
||||
"chat": "Bate-papo",
|
||||
"owner": "Proprietário",
|
||||
"store": "Loja",
|
||||
"admin": "Administrador",
|
||||
"member": "Membro",
|
||||
"guest": "Convidado",
|
||||
"submit": "Enviar",
|
||||
"claim": "Reivindicar",
|
||||
"claimed": "Reivindicado",
|
||||
"complete": "Completo",
|
||||
"shareTo": "Compartilhar para",
|
||||
"copyLink": "Copiar Link",
|
||||
"faceBook": "Facebook",
|
||||
"whatsApp": "Whatsapp",
|
||||
"snapChat": "Snapchat",
|
||||
"taskNamePersonalGameConsume": "Gastos com jogos",
|
||||
"taskNameRoomNewMember": "Novos membros da sala",
|
||||
"taskNameRoomOwnerSendRedPacket": "O proprietário do quarto envia um pacote vermelho",
|
||||
"taskNameRoomOwnerSendGiftUser": "Proprietário do quarto envia presente",
|
||||
"taskNameRoomMicUser120Min": "Membros com mais de 120 minutos no microfone",
|
||||
"taskNameRoomMicUser60Min": "Membros com mais de 60 minutos no microfone",
|
||||
"taskNameRoomMicUser30Min": "Membros com mais de 30 minutos no microfone",
|
||||
"taskNamePersonalSendGift": "Enviar presente ao usuário",
|
||||
"taskNameRoomOnlineUserCount": "Membros on-line da sala",
|
||||
"taskNameRoomOwnerInviteMic": "Convidar membro para o microfone",
|
||||
"taskNameRoomUserSendGiftGold": "Moedas doadas por membros",
|
||||
"taskNameRoomOwnerSendGiftGold": "Moedas doadas pelo proprietário do quarto",
|
||||
"taskNamePersonalMagicGiftGold": "Moedas ganhas com o envio de presentes mágicos",
|
||||
"taskNamePersonalLuckyGiftGold": "Moedas ganhas com o envio de presentes da sorte",
|
||||
"taskNameRoomOwnerMicTime": "O proprietário da sala liga o microfone na sala",
|
||||
"taskNamePersonalActiveInRoom": "Seja ativo nas salas dos outros",
|
||||
"taskNamePersonalMicInRoom": "Vá no microfone",
|
||||
"dailyCoinBonanzaRulesDetail": "1. As Tarefas Pessoais Diárias e as Tarefas Diárias do Proprietário da Sala podem ser concluídas uma vez por usuário por dia. As tarefas foram redefinidas às 00:00:00, horário da Arábia Saudita.\n2.As tarefas pessoais diárias só podem ser realizadas nos quartos de outras pessoas; as tarefas do proprietário da sala só podem ser concluídas em sua própria sala.\n3. As tarefas de dar presentes contam apenas os presentes enviados nas salas, não os presentes enviados nos feeds.\n4. Se várias contas forem criadas usando o mesmo dispositivo ou cartão SIM de alguma forma, as recompensas para todas as tarefas só poderão ser reivindicadas uma vez.",
|
||||
"dailyCoinBonanzaRules": "Regras diárias de bonança de moedas",
|
||||
"roomOwnerTasks": "Tarefas do proprietário da sala",
|
||||
"personalTasks": "Tarefas Pessoais",
|
||||
"noPromptsToday": "Nenhuma tarefa hoje.",
|
||||
"getPaidToRefer": "Seja pago para indicar",
|
||||
"membershipFee": "Taxa de adesão",
|
||||
"membershipFeeTips1": "Por favor, defina a taxa de adesão para o seu quarto. Os usuários podem ingressar na sua sala pagando a taxa.",
|
||||
"membershipFeeTips2": "Os ouros necessários para o usuário se tornar membro da sala. O proprietário da sala receberá 50% dos ouros.",
|
||||
"freePrice": "Taxa:0-10000",
|
||||
"touristsSendText": "Turista envia texto",
|
||||
"touristsTakeToTheMic": "Turistas pegam o microfone",
|
||||
"theMembershipFee": "A taxa de adesão",
|
||||
"theModificationsMade": "As modificações feitas desta vez não serão salvas após sair",
|
||||
"viewFrame": "Ver quadro",
|
||||
"enterRoomName": "Digite o nome da sala",
|
||||
"headdress": "Molduras",
|
||||
"mountains": "Veículos",
|
||||
"purchaseIsSuccessful": "A compra foi bem-sucedida",
|
||||
"buy": "Comprar",
|
||||
"followed": "Seguido",
|
||||
"follow2": "Siga: {1}",
|
||||
"fans2": "Fãs: {1}",
|
||||
"vistors2": "Visitantes:{1}",
|
||||
"personal2": "Pessoal:",
|
||||
"conntinue": "Continuar",
|
||||
"confirmBuyTips": "Tem certeza que deseja comprar?",
|
||||
"purchase": "Comprar",
|
||||
"setRoomPassword": "Definir senha da sala",
|
||||
"inputRoomPassword": "Insira a senha da sala",
|
||||
"enter": "Entrar",
|
||||
"deleteCommentTips": "Tem certeza de que deseja excluir este comentário?",
|
||||
"deleteSuccessful": "Exclusão bem-sucedida!",
|
||||
"itemsLeft": "Itens restantes",
|
||||
"password": "Senha",
|
||||
"replySucc": "Resposta bem-sucedida",
|
||||
"comment": "Comentário",
|
||||
"showMore": "Mostrar mais",
|
||||
"showLess": "Mostrar menos",
|
||||
"enterPassword": "Digite a senha",
|
||||
"enterAccount": "Insira a conta",
|
||||
"logIn": "Faça login",
|
||||
"saySomething": "Diga alguma coisa...",
|
||||
"sayHi": "Diga oi..",
|
||||
"pleaseChatFfriendly": "Por favor, converse gentilmente",
|
||||
"unLockTheRoom": "Desbloqueie a sala",
|
||||
"operationSuccessful": "A operação foi bem sucedida.",
|
||||
"adminByHomeowner": "é definido como administrador pelo proprietário.",
|
||||
"memberByHomeowner": "é definido como membro pelo proprietário.",
|
||||
"touristByHomeowner": "é definido como turista pelo proprietário.",
|
||||
"becomeHost": "Inscreva-se para se tornar um anfitrião",
|
||||
"superFans": "Superfãs:",
|
||||
"setUpAnIdentity": "Configure uma identidade",
|
||||
"kickedOutOfRoom": "Expulso da sala",
|
||||
"knapsack": "Mochila",
|
||||
"bdLeader": "Líder de BD",
|
||||
"picture": "Foto",
|
||||
"theImageSizeCannotExceed": "O tamanho da imagem não pode exceder 4M",
|
||||
"activity": "Atividade",
|
||||
"alreadyAnAdministrator": "Já sou administrador",
|
||||
"alreadyAnMember": "Já é membro",
|
||||
"alreadyAnTourist": "Já sou turista",
|
||||
"touristsCannotSendMessages": "Turistas não podem enviar mensagens",
|
||||
"touristsAreNotAllowedToGoOnTheMic": "Os turistas não estão autorizados a usar o microfone",
|
||||
"lockTheRoom": "Tranque a sala",
|
||||
"special": "Especial",
|
||||
"visitorList": "Lista de visitantes",
|
||||
"successfulWear": "Desgaste bem sucedido",
|
||||
"confirmUnUseTips": "Você confirma para removê-lo?",
|
||||
"custom": "Personalizado",
|
||||
"customBackground": "Plano de fundo personalizado",
|
||||
"example": "Exemplo",
|
||||
"endPreview": "Finalizar visualização",
|
||||
"selectAgain": "Selecione novamente",
|
||||
"myItems": "Meus itens",
|
||||
"use": "Usar",
|
||||
"unUse": "Desequipar",
|
||||
"renewal": "Renovação",
|
||||
"wallet": "Carteira",
|
||||
"profile": "Perfil",
|
||||
"giftwall": "Parede de presentes",
|
||||
"announcement": "Anúncio",
|
||||
"blockedList": "Lista bloqueada",
|
||||
"country2": "País:",
|
||||
"sendTo": "Enviar para",
|
||||
"credits": "Créditos: {1}",
|
||||
"successfullyUnloaded": "Descarregado com sucesso",
|
||||
"expired": "Expirado",
|
||||
"day": "Dia",
|
||||
"inUse": "Em uso",
|
||||
"staticOrGifImage": "Imagem estática/GIF",
|
||||
"confirmUseTips": "Quer confirmar o uso?",
|
||||
"pleaseUploadUserAvatar": "Por favor carregue um avatar.",
|
||||
"joinMemberTips": "Se você for um visitante na sala, não poderá levar o microfone.",
|
||||
"giftGivingSuccessful": "Doação de presentes com sucesso.",
|
||||
"theAccountPasswordCannotBeEmpty": "A conta ou senha não pode estar vazia.",
|
||||
"invitesYouToTheMicrophone": "{1} convida você para o microfone",
|
||||
"english": "Inglês",
|
||||
"chinese": "Chinês",
|
||||
"arabic": "Árabe",
|
||||
"darkMode": "Modo escuro",
|
||||
"lightMode": "Modo claro",
|
||||
"systemDefault": "Padrão do sistema",
|
||||
"pleaseGetOnTheMicFirst": "Por favor, pegue o microfone primeiro.",
|
||||
"roomMusicAddMusic": "Adicionar música",
|
||||
"roomMusicSelectAll": "Selecionar tudo",
|
||||
"roomMusicAddedCount": "Adicionadas músicas {1}",
|
||||
"roomMusicEmpty": "Sem música",
|
||||
"roomMusicScanFromPhone": "Digitalizar do telefone",
|
||||
"roomMusicTotalSongs": "Total: músicas {1}",
|
||||
"roomMusicNotFound": "Nenhuma música encontrada",
|
||||
"roomMusicDeleteConfirm": "Excluir esta música?",
|
||||
"roomMusicDeleted": "Música excluída",
|
||||
"roomMusicListMode": "Jogue em ordem",
|
||||
"roomMusicShuffleMode": "Reprodução aleatória",
|
||||
"roomMusicSingleMode": "Loop único",
|
||||
"duration2": "Duração: {1}",
|
||||
"roomRocketNoteTitle": "Nota",
|
||||
"roomRocketNoteSectionTitle": "Cada vez que um VIP",
|
||||
"roomRocketNoteSectionBody": "1. Cada vez que um nível VIP é adquirido, ele é válido por 30 dias, podendo ser renovado manualmente após expirar.",
|
||||
"roomRocketRewardEmptyText": "Nenhuma recompensa ganha. Melhor sorte da próxima vez.",
|
||||
"roomRocketRecordEmptyText": "Nenhum registro de recompensa",
|
||||
"launchRocket": "Lançar foguete"
|
||||
}
|
||||
@ -1,50 +1,4 @@
|
||||
PODS:
|
||||
- agora_rtc_engine (6.5.3):
|
||||
- AgoraIrisRTC_iOS (= 4.5.2-build.1)
|
||||
- AgoraRtcEngine_iOS (= 4.5.2)
|
||||
- Flutter
|
||||
- AgoraInfra_iOS (1.2.13.1)
|
||||
- AgoraIrisRTC_iOS (4.5.2-build.1)
|
||||
- AgoraRtcEngine_iOS (4.5.2):
|
||||
- AgoraRtcEngine_iOS/AIAEC (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/AIAECLL (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/AINS (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/AINSLL (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/AudioBeauty (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/ClearVision (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/ContentInspect (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/FaceCapture (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/FaceDetection (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/LipSync (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/ReplayKit (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/RtcBasic (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/SpatialAudio (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/VideoAv1CodecDec (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/VideoAv1CodecEnc (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/VideoCodecDec (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/VideoCodecEnc (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/VirtualBackground (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/VQA (= 4.5.2)
|
||||
- AgoraRtcEngine_iOS/AIAEC (4.5.2)
|
||||
- AgoraRtcEngine_iOS/AIAECLL (4.5.2)
|
||||
- AgoraRtcEngine_iOS/AINS (4.5.2)
|
||||
- AgoraRtcEngine_iOS/AINSLL (4.5.2)
|
||||
- AgoraRtcEngine_iOS/AudioBeauty (4.5.2)
|
||||
- AgoraRtcEngine_iOS/ClearVision (4.5.2)
|
||||
- AgoraRtcEngine_iOS/ContentInspect (4.5.2)
|
||||
- AgoraRtcEngine_iOS/FaceCapture (4.5.2)
|
||||
- AgoraRtcEngine_iOS/FaceDetection (4.5.2)
|
||||
- AgoraRtcEngine_iOS/LipSync (4.5.2)
|
||||
- AgoraRtcEngine_iOS/ReplayKit (4.5.2)
|
||||
- AgoraRtcEngine_iOS/RtcBasic (4.5.2):
|
||||
- AgoraInfra_iOS (= 1.2.13.1)
|
||||
- AgoraRtcEngine_iOS/SpatialAudio (4.5.2)
|
||||
- AgoraRtcEngine_iOS/VideoAv1CodecDec (4.5.2)
|
||||
- AgoraRtcEngine_iOS/VideoAv1CodecEnc (4.5.2)
|
||||
- AgoraRtcEngine_iOS/VideoCodecDec (4.5.2)
|
||||
- AgoraRtcEngine_iOS/VideoCodecEnc (4.5.2)
|
||||
- AgoraRtcEngine_iOS/VirtualBackground (4.5.2)
|
||||
- AgoraRtcEngine_iOS/VQA (4.5.2)
|
||||
- app_links (6.4.1):
|
||||
- Flutter
|
||||
- AppAuth (1.7.6):
|
||||
@ -187,8 +141,6 @@ PODS:
|
||||
- in_app_purchase_storekit (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- iris_method_channel (0.0.1):
|
||||
- Flutter
|
||||
- libpag (4.5.52)
|
||||
- libwebp (1.5.0):
|
||||
- libwebp/demux (= 1.5.0)
|
||||
@ -245,8 +197,16 @@ PODS:
|
||||
- Flutter
|
||||
- HydraAsync
|
||||
- TXIMSDK_Plus_iOS_XCFramework (= 8.3.6498)
|
||||
- tencent_rtc_sdk (0.0.1):
|
||||
- Flutter
|
||||
- TXCustomBeautyProcesserPlugin (= 1.0.2)
|
||||
- TXLiteAVSDK_Professional (~> 13.2.20652)
|
||||
- TOCropViewController (2.6.1)
|
||||
- TXCustomBeautyProcesserPlugin (1.0.2)
|
||||
- TXIMSDK_Plus_iOS_XCFramework (8.3.6498)
|
||||
- TXLiteAVSDK_Professional (13.2.20652):
|
||||
- TXLiteAVSDK_Professional/Professional (= 13.2.20652)
|
||||
- TXLiteAVSDK_Professional/Professional (13.2.20652)
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
- video_player_avfoundation (0.0.1):
|
||||
@ -262,7 +222,6 @@ PODS:
|
||||
- FlutterMacOS
|
||||
|
||||
DEPENDENCIES:
|
||||
- agora_rtc_engine (from `.symlinks/plugins/agora_rtc_engine/ios`)
|
||||
- app_links (from `.symlinks/plugins/app_links/ios`)
|
||||
- audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/darwin`)
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
@ -276,7 +235,6 @@ DEPENDENCIES:
|
||||
- image_cropper (from `.symlinks/plugins/image_cropper/ios`)
|
||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||
- in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`)
|
||||
- iris_method_channel (from `.symlinks/plugins/iris_method_channel/ios`)
|
||||
- on_audio_query_ios (from `.symlinks/plugins/on_audio_query_ios/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- pag (from `.symlinks/plugins/pag/ios`)
|
||||
@ -286,6 +244,7 @@ DEPENDENCIES:
|
||||
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
|
||||
- tancent_vap (from `.symlinks/plugins/tancent_vap/ios`)
|
||||
- tencent_cloud_chat_sdk (from `.symlinks/plugins/tencent_cloud_chat_sdk/ios`)
|
||||
- tencent_rtc_sdk (from `.symlinks/plugins/tencent_rtc_sdk/ios`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
|
||||
- video_thumbnail (from `.symlinks/plugins/video_thumbnail/ios`)
|
||||
@ -294,9 +253,6 @@ DEPENDENCIES:
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- AgoraInfra_iOS
|
||||
- AgoraIrisRTC_iOS
|
||||
- AgoraRtcEngine_iOS
|
||||
- AppAuth
|
||||
- AppCheckCore
|
||||
- Firebase
|
||||
@ -327,11 +283,11 @@ SPEC REPOS:
|
||||
- SDWebImageWebPCoder
|
||||
- SwiftyBeaver
|
||||
- TOCropViewController
|
||||
- TXCustomBeautyProcesserPlugin
|
||||
- TXIMSDK_Plus_iOS_XCFramework
|
||||
- TXLiteAVSDK_Professional
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
agora_rtc_engine:
|
||||
:path: ".symlinks/plugins/agora_rtc_engine/ios"
|
||||
app_links:
|
||||
:path: ".symlinks/plugins/app_links/ios"
|
||||
audioplayers_darwin:
|
||||
@ -358,8 +314,6 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||
in_app_purchase_storekit:
|
||||
:path: ".symlinks/plugins/in_app_purchase_storekit/darwin"
|
||||
iris_method_channel:
|
||||
:path: ".symlinks/plugins/iris_method_channel/ios"
|
||||
on_audio_query_ios:
|
||||
:path: ".symlinks/plugins/on_audio_query_ios/ios"
|
||||
package_info_plus:
|
||||
@ -378,6 +332,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/tancent_vap/ios"
|
||||
tencent_cloud_chat_sdk:
|
||||
:path: ".symlinks/plugins/tencent_cloud_chat_sdk/ios"
|
||||
tencent_rtc_sdk:
|
||||
:path: ".symlinks/plugins/tencent_rtc_sdk/ios"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
video_player_avfoundation:
|
||||
@ -390,10 +346,6 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
agora_rtc_engine: 0c7d50312967c4dc31c3c45e50589ce48f57e08a
|
||||
AgoraInfra_iOS: 3691b2b277a1712a35ae96de25af319de0d73d08
|
||||
AgoraIrisRTC_iOS: eab58c126439adf5ec99632828a558ea216860da
|
||||
AgoraRtcEngine_iOS: 97e2398a2addda9057815a2a583a658e36796ff6
|
||||
app_links: 3dbc685f76b1693c66a6d9dd1e9ab6f73d97dc0a
|
||||
AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73
|
||||
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
|
||||
@ -426,7 +378,6 @@ SPEC CHECKSUMS:
|
||||
image_cropper: 655b3ba703c9e15e3111e79151624d6154288774
|
||||
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
|
||||
in_app_purchase_storekit: 22cca7d08eebca9babdf4d07d0baccb73325d3c8
|
||||
iris_method_channel: 7d661cf3259b3009ae423508470dbeb9374446ee
|
||||
libpag: 98742fad4b3ac2a2ee31e383d2483495fb9a5fb4
|
||||
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
|
||||
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
||||
@ -446,8 +397,11 @@ SPEC CHECKSUMS:
|
||||
SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82
|
||||
tancent_vap: fa8ad93814a9f950514a7074c662d2c937084c68
|
||||
tencent_cloud_chat_sdk: 55e5fffe20f6b7937a26a674ccccb639563a9790
|
||||
tencent_rtc_sdk: f77558b6b436a149d378557c2a837f73c09061bc
|
||||
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
|
||||
TXCustomBeautyProcesserPlugin: 099393b941cb40eda12b3a80bf6c0319957b1cfd
|
||||
TXIMSDK_Plus_iOS_XCFramework: 5d1933192fb3b7ef2fe933f1623de4a0486a7fe2
|
||||
TXLiteAVSDK_Professional: e792e6084a208ecef6196c5f28237ad8078b4d7c
|
||||
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
||||
video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a
|
||||
video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140
|
||||
|
||||
@ -73,8 +73,8 @@ abstract class AppConfig {
|
||||
/// 腾讯IM App ID
|
||||
String get tencentImAppid;
|
||||
|
||||
/// Agora RTC App ID
|
||||
String get agoraRtcAppid;
|
||||
/// TRTC SDK App ID
|
||||
int get trtcSdkAppId;
|
||||
|
||||
/// 游戏App ID
|
||||
num get gameAppid;
|
||||
|
||||
@ -89,7 +89,8 @@ class SCVariant1Config implements AppConfig {
|
||||
String get tencentImAppid => '20036101';
|
||||
|
||||
@override
|
||||
String get agoraRtcAppid => '4b5e5cea3b86476caf7f7a57d05b82d1';
|
||||
int get trtcSdkAppId =>
|
||||
const int.fromEnvironment('TRTC_SDK_APP_ID', defaultValue: 20036101);
|
||||
|
||||
@override
|
||||
num get gameAppid => 9999999999; // 需要注册新的游戏服务账户并获取独立App ID
|
||||
@ -143,7 +144,7 @@ class SCVariant1Config implements AppConfig {
|
||||
'managerCenterUrl': managerCenterUrl,
|
||||
'primaryColor': primaryColor,
|
||||
'tencentImAppid': tencentImAppid,
|
||||
'agoraRtcAppid': agoraRtcAppid,
|
||||
'trtcSdkAppId': trtcSdkAppId,
|
||||
'gameAppid': gameAppid,
|
||||
'gameAppChannel': gameAppChannel,
|
||||
'bigBroadcastGroup': bigBroadcastGroup,
|
||||
@ -166,6 +167,9 @@ class SCVariant1Config implements AppConfig {
|
||||
if (apiHost.isEmpty || !apiHost.startsWith('http')) {
|
||||
errors.add('API主机地址无效或未设置');
|
||||
}
|
||||
if (trtcSdkAppId <= 0) {
|
||||
errors.add('TRTC SDK App ID 无效或未设置');
|
||||
}
|
||||
|
||||
// 如果有错误,根据模式处理
|
||||
if (errors.isNotEmpty) {
|
||||
|
||||
@ -55,7 +55,7 @@ class SCGlobalConfig {
|
||||
static String originChild = "LIKEI";
|
||||
|
||||
static String get tencentImAppid => AppConfig.current.tencentImAppid;
|
||||
static String get agoraRtcAppid => AppConfig.current.agoraRtcAppid;
|
||||
static int get trtcSdkAppId => AppConfig.current.trtcSdkAppId;
|
||||
|
||||
static num get gameAppid => AppConfig.current.gameAppid;
|
||||
static String get gameAppChannel => AppConfig.current.gameAppChannel;
|
||||
|
||||
@ -8,6 +8,49 @@ class SCAppLocalizations {
|
||||
|
||||
SCAppLocalizations(this.locale);
|
||||
|
||||
static const List<String> supportedLanguageCodes = [
|
||||
'en',
|
||||
'ar',
|
||||
'tr',
|
||||
'bn',
|
||||
'it',
|
||||
'pt',
|
||||
'fr',
|
||||
];
|
||||
|
||||
static const List<Locale> supportedLocales = [
|
||||
Locale('en', ''),
|
||||
Locale('ar', ''),
|
||||
Locale('tr', ''),
|
||||
Locale('bn', ''),
|
||||
Locale('it', ''),
|
||||
Locale('pt', ''),
|
||||
Locale('fr', ''),
|
||||
];
|
||||
|
||||
static bool isSupportedLanguageCode(String languageCode) {
|
||||
return supportedLanguageCodes.contains(
|
||||
_normalizeLanguageCode(languageCode),
|
||||
);
|
||||
}
|
||||
|
||||
static String resolveSupportedLanguageCode(String languageCode) {
|
||||
final normalizedLanguageCode = _normalizeLanguageCode(languageCode);
|
||||
if (supportedLanguageCodes.contains(normalizedLanguageCode)) {
|
||||
return normalizedLanguageCode;
|
||||
}
|
||||
return 'en';
|
||||
}
|
||||
|
||||
static String _normalizeLanguageCode(String languageCode) {
|
||||
return languageCode
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replaceAll('_', '-')
|
||||
.split('-')
|
||||
.first;
|
||||
}
|
||||
|
||||
static SCAppLocalizations? of(BuildContext context) {
|
||||
return Localizations.of<SCAppLocalizations>(context, SCAppLocalizations);
|
||||
}
|
||||
@ -18,8 +61,9 @@ class SCAppLocalizations {
|
||||
Map<String, String>? _localizedStrings;
|
||||
|
||||
Future<bool> load() async {
|
||||
final languageCode = resolveSupportedLanguageCode(locale.languageCode);
|
||||
final jsonString = await rootBundle.loadString(
|
||||
'assets/l10n/intl_${locale.languageCode}.json',
|
||||
'assets/l10n/intl_$languageCode.json',
|
||||
);
|
||||
|
||||
final Map<String, dynamic> jsonMap = json.decode(jsonString);
|
||||
@ -1792,7 +1836,7 @@ class _SCAppLocalizationsDelegate
|
||||
|
||||
@override
|
||||
bool isSupported(Locale locale) =>
|
||||
['en', 'zh', 'ar', 'bn', 'tr'].contains(locale.languageCode);
|
||||
SCAppLocalizations.isSupportedLanguageCode(locale.languageCode);
|
||||
|
||||
@override
|
||||
Future<SCAppLocalizations> load(Locale locale) async {
|
||||
|
||||
@ -417,13 +417,7 @@ class _YumiApplicationState extends State<YumiApplication>
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: const [
|
||||
Locale('en', ''),
|
||||
Locale('zh', ''),
|
||||
Locale('ar', ''),
|
||||
Locale('tr', ''),
|
||||
Locale('bn', ''),
|
||||
],
|
||||
supportedLocales: SCAppLocalizations.supportedLocales,
|
||||
navigatorKey: navigatorKey,
|
||||
debugShowCheckedModeBanner: false,
|
||||
onGenerateRoute: SCLkApplication.router.generator,
|
||||
|
||||
@ -1,245 +1,156 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:yumi/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:yumi/ui_kit/components/appbar/socialchat_appbar.dart';
|
||||
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
||||
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
||||
import 'package:yumi/services/localization/localization_manager.dart';
|
||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
||||
|
||||
///语言选择
|
||||
class LanguagePage extends StatefulWidget {
|
||||
@override
|
||||
_LanguagePageState createState() => _LanguagePageState();
|
||||
}
|
||||
|
||||
class _LanguagePageState extends State<LanguagePage> {
|
||||
int selectType = 1;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
String langCode = DataPersistence.getLang();
|
||||
if (langCode == "ar") {
|
||||
selectType = 1;
|
||||
}else if(langCode == "tr"){
|
||||
selectType = 2;
|
||||
}else if(langCode == "bn"){
|
||||
selectType = 3;
|
||||
} else {
|
||||
selectType = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Image.asset(
|
||||
SCGlobalConfig.businessLogicStrategy.getLanguagePageBackgroundImage(),
|
||||
width: ScreenUtil().screenWidth,
|
||||
height: ScreenUtil().screenHeight,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
Scaffold(
|
||||
backgroundColor: SCGlobalConfig.businessLogicStrategy.getLanguagePageScaffoldBackgroundColor(),
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: SocialChatStandardAppBar(
|
||||
title: SCAppLocalizations.of(context)!.language,
|
||||
actions: [],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(width: 15.w),
|
||||
Expanded(
|
||||
child: text(
|
||||
"English",
|
||||
textColor: SCGlobalConfig.businessLogicStrategy.getLanguagePagePrimaryTextColor(),
|
||||
fontSize: 15.sp,
|
||||
),
|
||||
),
|
||||
Checkbox(
|
||||
value: selectType == 0,
|
||||
checkColor: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckColor(),
|
||||
activeColor: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxActiveColor(),
|
||||
onChanged: (b) {
|
||||
if (selectType == 0) {
|
||||
selectType = 1;
|
||||
} else {
|
||||
selectType = 0;
|
||||
}
|
||||
setState(() {});
|
||||
if (b!) {
|
||||
Provider.of<LocalizationManager>(
|
||||
context,
|
||||
listen: false,
|
||||
).changeAppLanguage(const Locale('en', ''));
|
||||
}
|
||||
},
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderRadius()),
|
||||
),
|
||||
side: WidgetStateBorderSide.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return BorderSide(
|
||||
color: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderColor(),
|
||||
width: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderWidth(),
|
||||
); // 选中时保留蓝色边框
|
||||
}
|
||||
return BorderSide(
|
||||
color: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderColor(),
|
||||
width: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderWidth(),
|
||||
); // 未选中时灰色边框
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(width: 15.w),
|
||||
Expanded(
|
||||
child: text(
|
||||
"العربية",
|
||||
textColor: SCGlobalConfig.businessLogicStrategy.getLanguagePagePrimaryTextColor(),
|
||||
fontSize: 15.sp,
|
||||
),
|
||||
),
|
||||
Checkbox(
|
||||
value: selectType == 1,
|
||||
checkColor: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckColor(),
|
||||
activeColor: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxActiveColor(),
|
||||
onChanged: (b) {
|
||||
if (selectType == 1) {
|
||||
selectType = 0;
|
||||
} else {
|
||||
selectType = 1;
|
||||
}
|
||||
setState(() {});
|
||||
if (b!) {
|
||||
Provider.of<LocalizationManager>(
|
||||
context,
|
||||
listen: false,
|
||||
).changeAppLanguage(const Locale('ar', ''));
|
||||
}
|
||||
},
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderRadius()),
|
||||
),
|
||||
side: WidgetStateBorderSide.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return BorderSide(
|
||||
color: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderColor(),
|
||||
width: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderWidth(),
|
||||
); // 选中时保留蓝色边框
|
||||
}
|
||||
return BorderSide(
|
||||
color: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderColor(),
|
||||
width: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderWidth(),
|
||||
); // 未选中时灰色边框
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(width: 15.w),
|
||||
Expanded(
|
||||
child: text(
|
||||
"Türkçe",
|
||||
textColor: SCGlobalConfig.businessLogicStrategy.getLanguagePagePrimaryTextColor(),
|
||||
fontSize: 15.sp,
|
||||
),
|
||||
),
|
||||
Checkbox(
|
||||
value: selectType == 2,
|
||||
checkColor: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckColor(),
|
||||
activeColor: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxActiveColor(),
|
||||
onChanged: (b) {
|
||||
if (selectType == 2) {
|
||||
selectType = 0;
|
||||
} else {
|
||||
selectType = 2;
|
||||
}
|
||||
setState(() {});
|
||||
if (b!) {
|
||||
Provider.of<LocalizationManager>(
|
||||
context,
|
||||
listen: false,
|
||||
).changeAppLanguage(const Locale('tr', ''));
|
||||
}
|
||||
},
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderRadius()),
|
||||
),
|
||||
side: WidgetStateBorderSide.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return BorderSide(
|
||||
color: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderColor(),
|
||||
width: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderWidth(),
|
||||
); // 选中时保留蓝色边框
|
||||
}
|
||||
return BorderSide(
|
||||
color: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderColor(),
|
||||
width: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderWidth(),
|
||||
); // 未选中时灰色边框
|
||||
}),
|
||||
),
|
||||
],
|
||||
), Row(
|
||||
children: [
|
||||
SizedBox(width: 15.w),
|
||||
Expanded(
|
||||
child: text(
|
||||
"বাংলা",
|
||||
textColor: SCGlobalConfig.businessLogicStrategy.getLanguagePagePrimaryTextColor(),
|
||||
fontSize: 15.sp,
|
||||
),
|
||||
),
|
||||
Checkbox(
|
||||
value: selectType == 3,
|
||||
checkColor: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckColor(),
|
||||
activeColor: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxActiveColor(),
|
||||
onChanged: (b) {
|
||||
if (selectType == 3) {
|
||||
selectType = 0;
|
||||
} else {
|
||||
selectType = 3;
|
||||
}
|
||||
setState(() {});
|
||||
if (b!) {
|
||||
Provider.of<LocalizationManager>(
|
||||
context,
|
||||
listen: false,
|
||||
).changeAppLanguage(const Locale('bn', ''));
|
||||
}
|
||||
},
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderRadius()),
|
||||
),
|
||||
side: WidgetStateBorderSide.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return BorderSide(
|
||||
color: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderColor(),
|
||||
width: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderWidth(),
|
||||
); // 选中时保留蓝色边框
|
||||
}
|
||||
return BorderSide(
|
||||
color: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderColor(),
|
||||
width: SCGlobalConfig.businessLogicStrategy.getLanguagePageCheckboxBorderWidth(),
|
||||
); // 未选中时灰色边框
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
||||
import 'package:yumi/app_localizations.dart';
|
||||
import 'package:yumi/services/localization/localization_manager.dart';
|
||||
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
||||
import 'package:yumi/ui_kit/components/appbar/socialchat_appbar.dart';
|
||||
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
||||
|
||||
/// 语言选择
|
||||
class LanguagePage extends StatefulWidget {
|
||||
const LanguagePage({super.key});
|
||||
|
||||
@override
|
||||
State<LanguagePage> createState() => _LanguagePageState();
|
||||
}
|
||||
|
||||
class _LanguageOption {
|
||||
const _LanguageOption({required this.code, required this.label});
|
||||
|
||||
final String code;
|
||||
final String label;
|
||||
}
|
||||
|
||||
class _LanguagePageState extends State<LanguagePage> {
|
||||
static const List<_LanguageOption> _languageOptions = [
|
||||
_LanguageOption(code: 'en', label: 'English'),
|
||||
_LanguageOption(code: 'ar', label: 'العربية'),
|
||||
_LanguageOption(code: 'tr', label: 'Türkçe'),
|
||||
_LanguageOption(code: 'bn', label: 'বাংলা'),
|
||||
_LanguageOption(code: 'it', label: 'Italiano'),
|
||||
_LanguageOption(code: 'pt', label: 'Português'),
|
||||
_LanguageOption(code: 'fr', label: 'Français'),
|
||||
];
|
||||
|
||||
String _selectedLanguageCode = 'en';
|
||||
bool _didResolveInitialLanguage = false;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
if (!_didResolveInitialLanguage) {
|
||||
_selectedLanguageCode = _resolveCurrentLanguageCode();
|
||||
_didResolveInitialLanguage = true;
|
||||
}
|
||||
}
|
||||
|
||||
String _resolveCurrentLanguageCode() {
|
||||
final persistedLanguageCode = DataPersistence.getLang();
|
||||
if (SCAppLocalizations.isSupportedLanguageCode(persistedLanguageCode)) {
|
||||
return SCAppLocalizations.resolveSupportedLanguageCode(
|
||||
persistedLanguageCode,
|
||||
);
|
||||
}
|
||||
final currentLanguageCode =
|
||||
context.read<LocalizationManager>().locale?.languageCode ?? 'en';
|
||||
return SCAppLocalizations.resolveSupportedLanguageCode(currentLanguageCode);
|
||||
}
|
||||
|
||||
void _selectLanguage(String languageCode) {
|
||||
final resolvedLanguageCode =
|
||||
SCAppLocalizations.resolveSupportedLanguageCode(languageCode);
|
||||
if (_selectedLanguageCode == resolvedLanguageCode) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_selectedLanguageCode = resolvedLanguageCode;
|
||||
});
|
||||
context.read<LocalizationManager>().changeAppLanguage(
|
||||
Locale(resolvedLanguageCode, ''),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Image.asset(
|
||||
SCGlobalConfig.businessLogicStrategy.getLanguagePageBackgroundImage(),
|
||||
width: ScreenUtil().screenWidth,
|
||||
height: ScreenUtil().screenHeight,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
Scaffold(
|
||||
backgroundColor:
|
||||
SCGlobalConfig.businessLogicStrategy
|
||||
.getLanguagePageScaffoldBackgroundColor(),
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: SocialChatStandardAppBar(
|
||||
title: SCAppLocalizations.of(context)!.language,
|
||||
actions: [],
|
||||
),
|
||||
body: ListView.builder(
|
||||
padding: EdgeInsets.only(top: 4.w),
|
||||
itemCount: _languageOptions.length,
|
||||
itemBuilder: (context, index) {
|
||||
return _buildLanguageRow(_languageOptions[index]);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLanguageRow(_LanguageOption option) {
|
||||
final selected = _selectedLanguageCode == option.code;
|
||||
return InkWell(
|
||||
onTap: () => _selectLanguage(option.code),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: 15.w),
|
||||
Expanded(
|
||||
child: text(
|
||||
option.label,
|
||||
textColor:
|
||||
SCGlobalConfig.businessLogicStrategy
|
||||
.getLanguagePagePrimaryTextColor(),
|
||||
fontSize: 15.sp,
|
||||
),
|
||||
),
|
||||
Checkbox(
|
||||
value: selected,
|
||||
checkColor:
|
||||
SCGlobalConfig.businessLogicStrategy
|
||||
.getLanguagePageCheckColor(),
|
||||
activeColor:
|
||||
SCGlobalConfig.businessLogicStrategy
|
||||
.getLanguagePageCheckboxActiveColor(),
|
||||
onChanged: (checked) {
|
||||
if (checked == true) {
|
||||
_selectLanguage(option.code);
|
||||
}
|
||||
},
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
SCGlobalConfig.businessLogicStrategy
|
||||
.getLanguagePageCheckboxBorderRadius(),
|
||||
),
|
||||
),
|
||||
side: WidgetStateBorderSide.resolveWith((states) {
|
||||
return BorderSide(
|
||||
color:
|
||||
SCGlobalConfig.businessLogicStrategy
|
||||
.getLanguagePageCheckboxBorderColor(),
|
||||
width:
|
||||
SCGlobalConfig.businessLogicStrategy
|
||||
.getLanguagePageCheckboxBorderWidth(),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
74
lib/services/audio/room_rtc_engine_adapter.dart
Normal file
74
lib/services/audio/room_rtc_engine_adapter.dart
Normal file
@ -0,0 +1,74 @@
|
||||
import 'room_rtc_types.dart';
|
||||
|
||||
enum RoomRtcEngineKind { trtc }
|
||||
|
||||
class RoomRtcEnterRoomConfig {
|
||||
const RoomRtcEnterRoomConfig({
|
||||
required this.identity,
|
||||
required this.trtcSdkAppId,
|
||||
required this.trtcUserSig,
|
||||
this.throwOnError = false,
|
||||
});
|
||||
|
||||
final RoomRtcSessionIdentity identity;
|
||||
final int trtcSdkAppId;
|
||||
final String trtcUserSig;
|
||||
final bool throwOnError;
|
||||
|
||||
bool get hasTrtcCredential =>
|
||||
trtcSdkAppId > 0 && trtcUserSig.trim().isNotEmpty && identity.isValid;
|
||||
}
|
||||
|
||||
class RoomRtcAnchorConfig {
|
||||
const RoomRtcAnchorConfig({required this.muted});
|
||||
|
||||
final bool muted;
|
||||
}
|
||||
|
||||
class RoomRtcAudioMixingConfig {
|
||||
const RoomRtcAudioMixingConfig({
|
||||
required this.filePath,
|
||||
required this.loopback,
|
||||
this.cycle = 1,
|
||||
this.startPos = 0,
|
||||
});
|
||||
|
||||
final String filePath;
|
||||
final bool loopback;
|
||||
final int cycle;
|
||||
final int startPos;
|
||||
}
|
||||
|
||||
abstract class RoomRtcEngineAdapter {
|
||||
RoomRtcEngineKind get kind;
|
||||
|
||||
Future<void> prewarm();
|
||||
|
||||
Future<void> enterRoomAsAudience(RoomRtcEnterRoomConfig config);
|
||||
|
||||
Future<bool> switchToAnchor(RoomRtcAnchorConfig config);
|
||||
|
||||
Future<bool> switchToAudience();
|
||||
|
||||
void muteAllRemoteAudio(bool muted);
|
||||
|
||||
Future<void> leaveRoom();
|
||||
|
||||
Future<void> release();
|
||||
|
||||
Future<void> startAudioMixing(RoomRtcAudioMixingConfig config);
|
||||
|
||||
Future<void> pauseAudioMixing();
|
||||
|
||||
Future<void> resumeAudioMixing();
|
||||
|
||||
Future<void> stopAudioMixing();
|
||||
|
||||
Future<void> setAudioMixingPosition(int positionMs);
|
||||
|
||||
Future<int> getAudioMixingPosition();
|
||||
|
||||
Future<int> getAudioMixingDuration();
|
||||
|
||||
Future<void> setAudioMixingVolume(int volume);
|
||||
}
|
||||
88
lib/services/audio/room_rtc_types.dart
Normal file
88
lib/services/audio/room_rtc_types.dart
Normal file
@ -0,0 +1,88 @@
|
||||
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/join_room_res.dart'
|
||||
as join_room;
|
||||
import 'package:yumi/shared/business_logic/models/res/room_res.dart';
|
||||
|
||||
enum RoomRtcAudioMixingState { playing, paused, stopped, failed }
|
||||
|
||||
enum RoomRtcAudioMixingReason {
|
||||
ok,
|
||||
allLoopsCompleted,
|
||||
oneLoopCompleted,
|
||||
stoppedByUser,
|
||||
failed,
|
||||
other,
|
||||
}
|
||||
|
||||
enum RoomRtcExitReason { local, kicked, dismissed, unknown }
|
||||
|
||||
class RoomRtcUserVolume {
|
||||
const RoomRtcUserVolume({
|
||||
required this.userId,
|
||||
required this.volume,
|
||||
required this.isLocalUser,
|
||||
});
|
||||
|
||||
final String userId;
|
||||
final int volume;
|
||||
final bool isLocalUser;
|
||||
}
|
||||
|
||||
typedef RoomMusicMixingStateListener =
|
||||
void Function(
|
||||
RoomRtcAudioMixingState state,
|
||||
RoomRtcAudioMixingReason reason,
|
||||
);
|
||||
typedef RoomMusicMicRouteChangedListener = void Function(bool isOnMic);
|
||||
typedef RoomRtcVoiceVolumeListener =
|
||||
void Function(List<RoomRtcUserVolume> userVolumes, int totalVolume);
|
||||
typedef RoomRtcRoomErrorListener = void Function(Object error);
|
||||
typedef RoomRtcRoomExitListener = void Function(RoomRtcExitReason reason);
|
||||
|
||||
class RoomRtcSessionIdentity {
|
||||
const RoomRtcSessionIdentity({
|
||||
required this.businessRoomId,
|
||||
required this.trtcStrRoomId,
|
||||
required this.trtcUserId,
|
||||
});
|
||||
|
||||
factory RoomRtcSessionIdentity.fromRoom({
|
||||
required SocialChatRoomRes? roomProfile,
|
||||
required SocialChatUserProfile? userProfile,
|
||||
}) {
|
||||
return RoomRtcSessionIdentity.fromValues(
|
||||
businessRoomId: roomProfile?.id,
|
||||
trtcStrRoomId: roomProfile?.roomAccount,
|
||||
trtcUserId: userProfile?.id,
|
||||
);
|
||||
}
|
||||
|
||||
factory RoomRtcSessionIdentity.fromJoinRoom({
|
||||
required join_room.RoomProfile2? roomProfile,
|
||||
required SocialChatUserProfile? userProfile,
|
||||
}) {
|
||||
return RoomRtcSessionIdentity.fromValues(
|
||||
businessRoomId: roomProfile?.id,
|
||||
trtcStrRoomId: roomProfile?.roomAccount,
|
||||
trtcUserId: userProfile?.id,
|
||||
);
|
||||
}
|
||||
|
||||
factory RoomRtcSessionIdentity.fromValues({
|
||||
required String? businessRoomId,
|
||||
required String? trtcStrRoomId,
|
||||
required String? trtcUserId,
|
||||
}) {
|
||||
return RoomRtcSessionIdentity(
|
||||
businessRoomId: (businessRoomId ?? '').trim(),
|
||||
trtcStrRoomId: (trtcStrRoomId ?? '').trim(),
|
||||
trtcUserId: (trtcUserId ?? '').trim(),
|
||||
);
|
||||
}
|
||||
|
||||
final String businessRoomId;
|
||||
final String trtcStrRoomId;
|
||||
final String trtcUserId;
|
||||
|
||||
bool get isValid => trtcStrRoomId.isNotEmpty && trtcUserId.isNotEmpty;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1746,6 +1746,27 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
currentRoomId == normalizedRoomId;
|
||||
}
|
||||
|
||||
bool _isCurrentHiddenVoiceRoom(String roomId) {
|
||||
final normalizedRoomId = roomId.trim();
|
||||
final currentContext = context;
|
||||
if (normalizedRoomId.isEmpty ||
|
||||
currentContext == null ||
|
||||
!currentContext.mounted) {
|
||||
return false;
|
||||
}
|
||||
final rtcProvider = Provider.of<RealTimeCommunicationManager>(
|
||||
currentContext,
|
||||
listen: false,
|
||||
);
|
||||
final currentRoomId =
|
||||
rtcProvider.currenRoom?.roomProfile?.roomProfile?.id?.trim() ?? '';
|
||||
if (currentRoomId.isEmpty || currentRoomId != normalizedRoomId) {
|
||||
return false;
|
||||
}
|
||||
return !rtcProvider.shouldShowRoomVisualEffects ||
|
||||
!rtcProvider.isVoiceRoomRouteVisible;
|
||||
}
|
||||
|
||||
String _currentVoiceRoomId() {
|
||||
final currentContext = context;
|
||||
if (currentContext == null || !currentContext.mounted) {
|
||||
@ -1854,6 +1875,8 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
|
||||
var isCurrentRoom = false;
|
||||
var isCurrentVisibleRoom = false;
|
||||
final currentContext = context;
|
||||
if (currentContext != null && currentContext.mounted) {
|
||||
final rtcProvider = Provider.of<RealTimeCommunicationManager>(
|
||||
@ -1863,10 +1886,19 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
final currentRoomId =
|
||||
(rtcProvider.currenRoom?.roomProfile?.roomProfile?.id ?? '').trim();
|
||||
if (currentRoomId == launch.roomId) {
|
||||
rtcProvider.handleRoomRocketLaunchBroadcast(launch);
|
||||
isCurrentRoom = true;
|
||||
isCurrentVisibleRoom =
|
||||
rtcProvider.shouldShowRoomVisualEffects &&
|
||||
rtcProvider.isVoiceRoomRouteVisible;
|
||||
if (isCurrentVisibleRoom) {
|
||||
rtcProvider.handleRoomRocketLaunchBroadcast(launch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isCurrentRoom && !isCurrentVisibleRoom) {
|
||||
return;
|
||||
}
|
||||
OverlayManager().addMessage(_buildRoomRocketLaunchFloatingMessage(launch));
|
||||
}
|
||||
|
||||
@ -1879,6 +1911,9 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
(currentRoomId.isNotEmpty && resolvedRoomId != currentRoomId)) {
|
||||
return;
|
||||
}
|
||||
if (!_isCurrentVoiceRoom(resolvedRoomId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final userIds = _payloadStringList(data['userIds']);
|
||||
final currentUserId =
|
||||
@ -2010,6 +2045,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
),
|
||||
role: _rocketRewardRole(record.rewardType, record.rewardCover),
|
||||
number: record.level <= 0 ? 1 : record.level,
|
||||
rocketIconUrl: _firstNonBlank([
|
||||
record.rocketIconUrl,
|
||||
_rocketIconUrlForLevel(record.level),
|
||||
]),
|
||||
giftBatchId: _rocketRewardRecordKey(record),
|
||||
);
|
||||
}
|
||||
@ -2039,6 +2078,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
user: _rocketRewardUserFromPayload(payload, userId),
|
||||
role: _rocketRewardRole(rewardType, rewardCover),
|
||||
number: level <= 0 ? 1 : level,
|
||||
rocketIconUrl: _rocketRewardRocketIconUrl(payload, level),
|
||||
giftBatchId: [
|
||||
'rocket_reward_fallback',
|
||||
_payloadText(payload['launchNo']),
|
||||
@ -2116,6 +2156,46 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
return rewardCover.trim().isEmpty ? 'GOLD' : normalized;
|
||||
}
|
||||
|
||||
String _rocketRewardRocketIconUrl(Map<String, dynamic> payload, int level) {
|
||||
final rocket = _broadcastPayloadMap(payload['rocket']);
|
||||
final levelInfo = _broadcastPayloadMap(payload['levelInfo']);
|
||||
final config = _broadcastPayloadMap(payload['config']);
|
||||
return _firstNonBlank([
|
||||
_payloadText(payload['rocket_icon_url']),
|
||||
_payloadText(payload['rocketIconUrl']),
|
||||
_payloadText(payload['rocketIcon']),
|
||||
_payloadText(payload['iconUrl']),
|
||||
_payloadText(rocket['rocket_icon_url']),
|
||||
_payloadText(rocket['rocketIconUrl']),
|
||||
_payloadText(rocket['rocketIcon']),
|
||||
_payloadText(levelInfo['rocket_icon_url']),
|
||||
_payloadText(levelInfo['rocketIconUrl']),
|
||||
_payloadText(levelInfo['rocketIcon']),
|
||||
_payloadText(config['rocket_icon_url']),
|
||||
_payloadText(config['rocketIconUrl']),
|
||||
_payloadText(config['rocketIcon']),
|
||||
_rocketIconUrlForLevel(level),
|
||||
]);
|
||||
}
|
||||
|
||||
String _rocketIconUrlForLevel(int level) {
|
||||
final currentContext = context;
|
||||
if (currentContext == null || !currentContext.mounted) {
|
||||
return '';
|
||||
}
|
||||
final rtcProvider = Provider.of<RealTimeCommunicationManager>(
|
||||
currentContext,
|
||||
listen: false,
|
||||
);
|
||||
final levels = rtcProvider.roomRocketStatus?.levels ?? const [];
|
||||
for (final item in levels) {
|
||||
if (item.level == level && item.rocketIconUrl.trim().isNotEmpty) {
|
||||
return item.rocketIconUrl;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
String _rocketRewardRecordKey(SCRoomRocketRewardRecordRes record) {
|
||||
return [
|
||||
'rocket_reward',
|
||||
@ -2360,6 +2440,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
} else if (type == "ROCKET_ENERGY_LAUNCH") {
|
||||
///火箭触发飘屏
|
||||
var fdata = data["data"];
|
||||
final launchRoomId = _payloadText(fdata["roomId"]);
|
||||
if (_isCurrentHiddenVoiceRoom(launchRoomId)) {
|
||||
return;
|
||||
}
|
||||
final rocketIconUrl = _firstNonBlank([
|
||||
_payloadText(fdata["rocketIconUrl"]),
|
||||
_payloadText(fdata["rocketIcon"]),
|
||||
@ -2370,7 +2454,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
]);
|
||||
SCFloatingMessage msg = SCFloatingMessage(
|
||||
type: 3,
|
||||
roomId: fdata["roomId"],
|
||||
roomId: launchRoomId,
|
||||
rocketLevel: fdata["fromLevel"],
|
||||
userAvatarUrl: _firstNonBlank([
|
||||
_payloadText(fdata["roomCoverUrl"]),
|
||||
|
||||
590
lib/services/audio/trtc_room_rtc_engine_adapter.dart
Normal file
590
lib/services/audio/trtc_room_rtc_engine_adapter.dart
Normal file
@ -0,0 +1,590 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:tencent_rtc_sdk/trtc_cloud.dart';
|
||||
import 'package:tencent_rtc_sdk/trtc_cloud_def.dart';
|
||||
import 'package:tencent_rtc_sdk/trtc_cloud_listener.dart';
|
||||
import 'package:tencent_rtc_sdk/tx_audio_effect_manager.dart';
|
||||
|
||||
import 'room_rtc_engine_adapter.dart';
|
||||
import 'room_rtc_types.dart';
|
||||
|
||||
abstract class RoomTrtcCloud {
|
||||
void registerListener(TRTCCloudListener listener);
|
||||
void unRegisterListener(TRTCCloudListener listener);
|
||||
void setDefaultStreamRecvMode(bool autoRecvAudio, bool autoRecvVideo);
|
||||
void enableAudioVolumeEvaluation(
|
||||
bool enable,
|
||||
TRTCAudioVolumeEvaluateParams params,
|
||||
);
|
||||
void enterRoom(TRTCParams param, TRTCAppScene scene);
|
||||
void exitRoom();
|
||||
void switchRole(TRTCRoleType role);
|
||||
void startLocalAudio(TRTCAudioQuality quality);
|
||||
void stopLocalAudio();
|
||||
void muteLocalAudio(bool mute);
|
||||
void muteAllRemoteAudio(bool mute);
|
||||
RoomTrtcAudioEffectManager getAudioEffectManager();
|
||||
}
|
||||
|
||||
abstract class RoomTrtcAudioEffectManager {
|
||||
void setMusicObserver(int musicId, TXMusicPlayObserver? observer);
|
||||
void startPlayMusic(AudioMusicParam musicParam);
|
||||
void stopPlayMusic(int id);
|
||||
void pausePlayMusic(int id);
|
||||
void resumePlayMusic(int id);
|
||||
void setMusicPublishVolume(int id, int volume);
|
||||
void setMusicPlayoutVolume(int id, int volume);
|
||||
int getMusicCurrentPosInMS(int id);
|
||||
int getMusicDurationInMS(String path);
|
||||
void seekMusicToPosInTime(int id, int pts);
|
||||
}
|
||||
|
||||
class TencentRoomTrtcCloud implements RoomTrtcCloud {
|
||||
TencentRoomTrtcCloud._(this._cloud);
|
||||
|
||||
static Future<TencentRoomTrtcCloud> sharedInstance() async {
|
||||
return TencentRoomTrtcCloud._(await TRTCCloud.sharedInstance());
|
||||
}
|
||||
|
||||
static void destroySharedInstance() {
|
||||
TRTCCloud.destroySharedInstance();
|
||||
}
|
||||
|
||||
final TRTCCloud _cloud;
|
||||
|
||||
@override
|
||||
void registerListener(TRTCCloudListener listener) {
|
||||
_cloud.registerListener(listener);
|
||||
}
|
||||
|
||||
@override
|
||||
void unRegisterListener(TRTCCloudListener listener) {
|
||||
_cloud.unRegisterListener(listener);
|
||||
}
|
||||
|
||||
@override
|
||||
void setDefaultStreamRecvMode(bool autoRecvAudio, bool autoRecvVideo) {
|
||||
_cloud.setDefaultStreamRecvMode(autoRecvAudio, autoRecvVideo);
|
||||
}
|
||||
|
||||
@override
|
||||
void enableAudioVolumeEvaluation(
|
||||
bool enable,
|
||||
TRTCAudioVolumeEvaluateParams params,
|
||||
) {
|
||||
_cloud.enableAudioVolumeEvaluation(enable, params);
|
||||
}
|
||||
|
||||
@override
|
||||
void enterRoom(TRTCParams param, TRTCAppScene scene) {
|
||||
_cloud.enterRoom(param, scene);
|
||||
}
|
||||
|
||||
@override
|
||||
void exitRoom() {
|
||||
_cloud.exitRoom();
|
||||
}
|
||||
|
||||
@override
|
||||
void switchRole(TRTCRoleType role) {
|
||||
_cloud.switchRole(role);
|
||||
}
|
||||
|
||||
@override
|
||||
void startLocalAudio(TRTCAudioQuality quality) {
|
||||
_cloud.startLocalAudio(quality);
|
||||
}
|
||||
|
||||
@override
|
||||
void stopLocalAudio() {
|
||||
_cloud.stopLocalAudio();
|
||||
}
|
||||
|
||||
@override
|
||||
void muteLocalAudio(bool mute) {
|
||||
_cloud.muteLocalAudio(mute);
|
||||
}
|
||||
|
||||
@override
|
||||
void muteAllRemoteAudio(bool mute) {
|
||||
_cloud.muteAllRemoteAudio(mute);
|
||||
}
|
||||
|
||||
@override
|
||||
RoomTrtcAudioEffectManager getAudioEffectManager() {
|
||||
return TencentRoomTrtcAudioEffectManager(_cloud.getAudioEffectManager());
|
||||
}
|
||||
}
|
||||
|
||||
class TencentRoomTrtcAudioEffectManager implements RoomTrtcAudioEffectManager {
|
||||
const TencentRoomTrtcAudioEffectManager(this._manager);
|
||||
|
||||
final TXAudioEffectManager _manager;
|
||||
|
||||
@override
|
||||
void setMusicObserver(int musicId, TXMusicPlayObserver? observer) {
|
||||
_manager.setMusicObserver(musicId, observer);
|
||||
}
|
||||
|
||||
@override
|
||||
void startPlayMusic(AudioMusicParam musicParam) {
|
||||
_manager.startPlayMusic(musicParam);
|
||||
}
|
||||
|
||||
@override
|
||||
void stopPlayMusic(int id) {
|
||||
_manager.stopPlayMusic(id);
|
||||
}
|
||||
|
||||
@override
|
||||
void pausePlayMusic(int id) {
|
||||
_manager.pausePlayMusic(id);
|
||||
}
|
||||
|
||||
@override
|
||||
void resumePlayMusic(int id) {
|
||||
_manager.resumePlayMusic(id);
|
||||
}
|
||||
|
||||
@override
|
||||
void setMusicPublishVolume(int id, int volume) {
|
||||
_manager.setMusicPublishVolume(id, volume);
|
||||
}
|
||||
|
||||
@override
|
||||
void setMusicPlayoutVolume(int id, int volume) {
|
||||
_manager.setMusicPlayoutVolume(id, volume);
|
||||
}
|
||||
|
||||
@override
|
||||
int getMusicCurrentPosInMS(int id) {
|
||||
return _manager.getMusicCurrentPosInMS(id);
|
||||
}
|
||||
|
||||
@override
|
||||
int getMusicDurationInMS(String path) {
|
||||
return _manager.getMusicDurationInMS(path);
|
||||
}
|
||||
|
||||
@override
|
||||
void seekMusicToPosInTime(int id, int pts) {
|
||||
_manager.seekMusicToPosInTime(id, pts);
|
||||
}
|
||||
}
|
||||
|
||||
class TrtcRoomRtcEngineAdapter implements RoomRtcEngineAdapter {
|
||||
TrtcRoomRtcEngineAdapter({
|
||||
Future<RoomTrtcCloud> Function()? cloudFactory,
|
||||
this.onVoiceVolume,
|
||||
this.onAudioMixingStateChanged,
|
||||
this.onRoomError,
|
||||
this.onRoomExit,
|
||||
this.enterRoomTimeout = const Duration(seconds: 8),
|
||||
}) : _cloudFactory =
|
||||
cloudFactory ?? (() => TencentRoomTrtcCloud.sharedInstance());
|
||||
|
||||
static const int _roomMusicId = 1;
|
||||
|
||||
final Future<RoomTrtcCloud> Function() _cloudFactory;
|
||||
final RoomRtcVoiceVolumeListener? onVoiceVolume;
|
||||
final RoomMusicMixingStateListener? onAudioMixingStateChanged;
|
||||
final RoomRtcRoomErrorListener? onRoomError;
|
||||
final RoomRtcRoomExitListener? onRoomExit;
|
||||
final Duration enterRoomTimeout;
|
||||
|
||||
RoomTrtcCloud? _cloud;
|
||||
RoomTrtcAudioEffectManager? _audioEffectManager;
|
||||
TRTCCloudListener? _listener;
|
||||
Completer<void>? _enterRoomCompleter;
|
||||
Completer<void>? _exitRoomCompleter;
|
||||
String? _localUserId;
|
||||
String? _currentMusicPath;
|
||||
bool _isEntered = false;
|
||||
bool _isLocalAudioStarted = false;
|
||||
bool _expectingLocalExit = false;
|
||||
|
||||
@override
|
||||
RoomRtcEngineKind get kind => RoomRtcEngineKind.trtc;
|
||||
|
||||
@override
|
||||
Future<void> prewarm() async {}
|
||||
|
||||
Future<RoomTrtcCloud> _ensureCloud() async {
|
||||
final existing = _cloud;
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
final cloud = await _cloudFactory();
|
||||
final listener = _createListener();
|
||||
cloud.registerListener(listener);
|
||||
_listener = listener;
|
||||
_cloud = cloud;
|
||||
return cloud;
|
||||
}
|
||||
|
||||
TRTCCloudListener _createListener() {
|
||||
return TRTCCloudListener(
|
||||
onEnterRoom: (result) {
|
||||
final completer = _enterRoomCompleter;
|
||||
if (result >= 0) {
|
||||
_isEntered = true;
|
||||
if (completer != null && !completer.isCompleted) {
|
||||
completer.complete();
|
||||
}
|
||||
return;
|
||||
}
|
||||
final error = StateError('TRTC enter room failed: $result');
|
||||
if (completer != null && !completer.isCompleted) {
|
||||
completer.completeError(error);
|
||||
} else {
|
||||
onRoomError?.call(error);
|
||||
}
|
||||
},
|
||||
onError: (errCode, errMsg) {
|
||||
final error = StateError('TRTC error: $errCode $errMsg');
|
||||
final completer = _enterRoomCompleter;
|
||||
if (completer != null && !completer.isCompleted) {
|
||||
completer.completeError(error);
|
||||
return;
|
||||
}
|
||||
if (_isEntered) {
|
||||
onRoomError?.call(error);
|
||||
}
|
||||
},
|
||||
onExitRoom: (reason) {
|
||||
final wasEntered = _isEntered;
|
||||
_isEntered = false;
|
||||
_isLocalAudioStarted = false;
|
||||
final completer = _exitRoomCompleter;
|
||||
if (completer != null && !completer.isCompleted) {
|
||||
completer.complete();
|
||||
}
|
||||
final exitReason = _mapExitReason(reason);
|
||||
if (wasEntered &&
|
||||
!_expectingLocalExit &&
|
||||
exitReason != RoomRtcExitReason.local) {
|
||||
onRoomExit?.call(exitReason);
|
||||
}
|
||||
_expectingLocalExit = false;
|
||||
},
|
||||
onUserVoiceVolume: (userVolumes, totalVolume) {
|
||||
final localUserId = (_localUserId ?? '').trim();
|
||||
final mapped = userVolumes
|
||||
.map((item) {
|
||||
final normalizedUserId =
|
||||
item.userId.trim().isEmpty ? localUserId : item.userId.trim();
|
||||
return RoomRtcUserVolume(
|
||||
userId: normalizedUserId,
|
||||
volume: item.volume,
|
||||
isLocalUser:
|
||||
localUserId.isNotEmpty && normalizedUserId == localUserId,
|
||||
);
|
||||
})
|
||||
.where((item) => item.userId.isNotEmpty)
|
||||
.toList(growable: false);
|
||||
onVoiceVolume?.call(mapped, totalVolume);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
RoomRtcExitReason _mapExitReason(int reason) {
|
||||
switch (reason) {
|
||||
case 0:
|
||||
return RoomRtcExitReason.local;
|
||||
case 1:
|
||||
return RoomRtcExitReason.kicked;
|
||||
case 2:
|
||||
return RoomRtcExitReason.dismissed;
|
||||
default:
|
||||
return RoomRtcExitReason.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> enterRoomAsAudience(RoomRtcEnterRoomConfig config) async {
|
||||
if (!config.hasTrtcCredential) {
|
||||
final error = StateError('TRTC room credential is invalid');
|
||||
if (config.throwOnError) {
|
||||
throw error;
|
||||
}
|
||||
onRoomError?.call(error);
|
||||
return;
|
||||
}
|
||||
|
||||
final cloud = await _ensureCloud();
|
||||
_localUserId = config.identity.trtcUserId;
|
||||
_isEntered = false;
|
||||
_isLocalAudioStarted = false;
|
||||
_expectingLocalExit = false;
|
||||
final joinCompleter = Completer<void>();
|
||||
_enterRoomCompleter = joinCompleter;
|
||||
|
||||
try {
|
||||
cloud.setDefaultStreamRecvMode(true, false);
|
||||
cloud.enableAudioVolumeEvaluation(
|
||||
true,
|
||||
TRTCAudioVolumeEvaluateParams(interval: 500),
|
||||
);
|
||||
cloud.enterRoom(
|
||||
TRTCParams(
|
||||
sdkAppId: config.trtcSdkAppId,
|
||||
userId: config.identity.trtcUserId,
|
||||
userSig: config.trtcUserSig.trim(),
|
||||
roomId: 0,
|
||||
strRoomId: config.identity.trtcStrRoomId,
|
||||
role: TRTCRoleType.audience,
|
||||
),
|
||||
TRTCAppScene.voiceChatRoom,
|
||||
);
|
||||
await joinCompleter.future.timeout(enterRoomTimeout);
|
||||
} catch (error) {
|
||||
try {
|
||||
cloud.exitRoom();
|
||||
} catch (_) {}
|
||||
if (config.throwOnError) {
|
||||
rethrow;
|
||||
}
|
||||
onRoomError?.call(error);
|
||||
} finally {
|
||||
if (identical(_enterRoomCompleter, joinCompleter)) {
|
||||
_enterRoomCompleter = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> switchToAnchor(RoomRtcAnchorConfig config) async {
|
||||
final cloud = _cloud;
|
||||
if (!_isEntered || cloud == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
cloud.switchRole(TRTCRoleType.anchor);
|
||||
cloud.startLocalAudio(TRTCAudioQuality.defaultMode);
|
||||
cloud.muteLocalAudio(config.muted);
|
||||
_isLocalAudioStarted = true;
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> switchToAudience() async {
|
||||
final cloud = _cloud;
|
||||
if (cloud == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
cloud.switchRole(TRTCRoleType.audience);
|
||||
cloud.muteLocalAudio(true);
|
||||
if (_isLocalAudioStarted) {
|
||||
cloud.stopLocalAudio();
|
||||
}
|
||||
_isLocalAudioStarted = false;
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void muteAllRemoteAudio(bool muted) {
|
||||
_cloud?.muteAllRemoteAudio(muted);
|
||||
}
|
||||
|
||||
RoomTrtcAudioEffectManager? get _musicManager {
|
||||
final existing = _audioEffectManager;
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
final cloud = _cloud;
|
||||
if (cloud == null) {
|
||||
return null;
|
||||
}
|
||||
return _audioEffectManager = cloud.getAudioEffectManager();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> startAudioMixing(RoomRtcAudioMixingConfig config) async {
|
||||
final path = config.filePath.trim();
|
||||
if (path.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final manager = _musicManager;
|
||||
if (manager == null) {
|
||||
throw StateError('TRTC audio effect manager is not ready');
|
||||
}
|
||||
_currentMusicPath = path;
|
||||
manager.setMusicObserver(_roomMusicId, _createMusicObserver());
|
||||
manager.startPlayMusic(
|
||||
AudioMusicParam(
|
||||
id: _roomMusicId,
|
||||
path: path,
|
||||
loopCount: _audioMixingCycleToTrtcLoopCount(config.cycle),
|
||||
publish: !config.loopback,
|
||||
startTimeMS: config.startPos,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
int _audioMixingCycleToTrtcLoopCount(int cycle) {
|
||||
if (cycle < 0) {
|
||||
return 1000000;
|
||||
}
|
||||
if (cycle <= 1) {
|
||||
return 0;
|
||||
}
|
||||
return cycle - 1;
|
||||
}
|
||||
|
||||
TXMusicPlayObserver _createMusicObserver() {
|
||||
return TXMusicPlayObserver(
|
||||
onStart: (id, errorCode) {
|
||||
if (id != _roomMusicId) {
|
||||
return;
|
||||
}
|
||||
if (errorCode == 0) {
|
||||
onAudioMixingStateChanged?.call(
|
||||
RoomRtcAudioMixingState.playing,
|
||||
RoomRtcAudioMixingReason.ok,
|
||||
);
|
||||
} else {
|
||||
onAudioMixingStateChanged?.call(
|
||||
RoomRtcAudioMixingState.failed,
|
||||
RoomRtcAudioMixingReason.failed,
|
||||
);
|
||||
}
|
||||
},
|
||||
onPlayProgress: (_, __, ___) {},
|
||||
onComplete: (id, errorCode) {
|
||||
if (id != _roomMusicId) {
|
||||
return;
|
||||
}
|
||||
onAudioMixingStateChanged?.call(
|
||||
errorCode == 0
|
||||
? RoomRtcAudioMixingState.stopped
|
||||
: RoomRtcAudioMixingState.failed,
|
||||
errorCode == 0
|
||||
? RoomRtcAudioMixingReason.allLoopsCompleted
|
||||
: RoomRtcAudioMixingReason.failed,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> pauseAudioMixing() async {
|
||||
_musicManager?.pausePlayMusic(_roomMusicId);
|
||||
onAudioMixingStateChanged?.call(
|
||||
RoomRtcAudioMixingState.paused,
|
||||
RoomRtcAudioMixingReason.ok,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> resumeAudioMixing() async {
|
||||
_musicManager?.resumePlayMusic(_roomMusicId);
|
||||
onAudioMixingStateChanged?.call(
|
||||
RoomRtcAudioMixingState.playing,
|
||||
RoomRtcAudioMixingReason.ok,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stopAudioMixing() async {
|
||||
_musicManager?.stopPlayMusic(_roomMusicId);
|
||||
onAudioMixingStateChanged?.call(
|
||||
RoomRtcAudioMixingState.stopped,
|
||||
RoomRtcAudioMixingReason.stoppedByUser,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setAudioMixingPosition(int positionMs) async {
|
||||
_musicManager?.seekMusicToPosInTime(_roomMusicId, positionMs);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getAudioMixingPosition() async {
|
||||
return _musicManager?.getMusicCurrentPosInMS(_roomMusicId) ?? 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getAudioMixingDuration() async {
|
||||
final path = (_currentMusicPath ?? '').trim();
|
||||
if (path.isEmpty) {
|
||||
return 0;
|
||||
}
|
||||
return _musicManager?.getMusicDurationInMS(path) ?? 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setAudioMixingVolume(int volume) async {
|
||||
final safeVolume = volume.clamp(0, 100).toInt();
|
||||
_musicManager?.setMusicPlayoutVolume(_roomMusicId, safeVolume);
|
||||
_musicManager?.setMusicPublishVolume(_roomMusicId, safeVolume);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> leaveRoom() async {
|
||||
final cloud = _cloud;
|
||||
if (cloud == null) {
|
||||
_isEntered = false;
|
||||
_isLocalAudioStarted = false;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await stopAudioMixing();
|
||||
} catch (_) {}
|
||||
try {
|
||||
if (_isLocalAudioStarted) {
|
||||
cloud.stopLocalAudio();
|
||||
}
|
||||
cloud.enableAudioVolumeEvaluation(
|
||||
false,
|
||||
TRTCAudioVolumeEvaluateParams(interval: 0),
|
||||
);
|
||||
} catch (_) {}
|
||||
if (!_isEntered) {
|
||||
_isLocalAudioStarted = false;
|
||||
return;
|
||||
}
|
||||
final exitCompleter = Completer<void>();
|
||||
_exitRoomCompleter = exitCompleter;
|
||||
_expectingLocalExit = true;
|
||||
try {
|
||||
cloud.exitRoom();
|
||||
await exitCompleter.future.timeout(const Duration(seconds: 3));
|
||||
} catch (_) {
|
||||
_isEntered = false;
|
||||
_isLocalAudioStarted = false;
|
||||
_expectingLocalExit = false;
|
||||
} finally {
|
||||
if (identical(_exitRoomCompleter, exitCompleter)) {
|
||||
_exitRoomCompleter = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> release() async {
|
||||
final cloud = _cloud;
|
||||
final listener = _listener;
|
||||
await leaveRoom();
|
||||
try {
|
||||
if (cloud != null && listener != null) {
|
||||
cloud.unRegisterListener(listener);
|
||||
}
|
||||
TencentRoomTrtcCloud.destroySharedInstance();
|
||||
} catch (_) {}
|
||||
_listener = null;
|
||||
_cloud = null;
|
||||
_audioEffectManager = null;
|
||||
_currentMusicPath = null;
|
||||
_localUserId = null;
|
||||
_isEntered = false;
|
||||
_isLocalAudioStarted = false;
|
||||
_expectingLocalExit = false;
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ import 'dart:ui' as ui;
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
||||
import 'package:yumi/app_localizations.dart';
|
||||
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
||||
|
||||
class LocalizationManager with ChangeNotifier {
|
||||
@ -19,31 +20,19 @@ class LocalizationManager with ChangeNotifier {
|
||||
}
|
||||
|
||||
Locale _resolveInitialLocale() {
|
||||
String language = DataPersistence.getLang();
|
||||
final language = DataPersistence.getLang();
|
||||
if (language.isEmpty) {
|
||||
Locale systemLocale = ui.PlatformDispatcher.instance.locale;
|
||||
if (systemLocale.languageCode.startsWith("ar")) {
|
||||
return const Locale('ar', '');
|
||||
} else if (systemLocale.languageCode == "en") {
|
||||
return const Locale('en', '');
|
||||
} else if (systemLocale.languageCode == "tr") {
|
||||
return const Locale('tr', '');
|
||||
} else if (systemLocale.languageCode == "bn") {
|
||||
return const Locale('bn', '');
|
||||
} else {
|
||||
return const Locale('en', '');
|
||||
}
|
||||
} else {
|
||||
if (language.startsWith("ar")) {
|
||||
return const Locale('ar', '');
|
||||
} else if (language == "tr") {
|
||||
return const Locale('tr', '');
|
||||
} else if (language == "bn") {
|
||||
return const Locale('bn', '');
|
||||
} else {
|
||||
return const Locale('en', '');
|
||||
}
|
||||
final systemLocale = ui.PlatformDispatcher.instance.locale;
|
||||
return _localeFromLanguageCode(systemLocale.languageCode);
|
||||
}
|
||||
return _localeFromLanguageCode(language);
|
||||
}
|
||||
|
||||
Locale _localeFromLanguageCode(String languageCode) {
|
||||
return Locale(
|
||||
SCAppLocalizations.resolveSupportedLanguageCode(languageCode),
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
void changeAppLanguage(Locale locale) {
|
||||
@ -51,9 +40,9 @@ class LocalizationManager with ChangeNotifier {
|
||||
_locale?.countryCode == locale.countryCode) {
|
||||
return;
|
||||
}
|
||||
_locale = locale;
|
||||
SCGlobalConfig.lang = locale.languageCode;
|
||||
unawaited(DataPersistence.setLang(locale.languageCode));
|
||||
_locale = _localeFromLanguageCode(locale.languageCode);
|
||||
SCGlobalConfig.lang = _locale!.languageCode;
|
||||
unawaited(DataPersistence.setLang(_locale!.languageCode));
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,9 +2,9 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:yumi/services/audio/rtc_manager.dart';
|
||||
import 'package:yumi/services/audio/room_rtc_types.dart';
|
||||
import 'package:yumi/services/music/room_music_repository.dart';
|
||||
import 'package:yumi/shared/data_sources/models/sc_music_folder_mode.dart';
|
||||
import 'package:yumi/shared/data_sources/models/sc_music_mode.dart';
|
||||
@ -436,25 +436,25 @@ class RoomMusicManager extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void _handleMixingStateChanged(
|
||||
AudioMixingStateType state,
|
||||
AudioMixingReasonType reason,
|
||||
RoomRtcAudioMixingState state,
|
||||
RoomRtcAudioMixingReason reason,
|
||||
) {
|
||||
switch (state) {
|
||||
case AudioMixingStateType.audioMixingStatePlaying:
|
||||
case RoomRtcAudioMixingState.playing:
|
||||
_isPlaying = true;
|
||||
_isPaused = false;
|
||||
_startPositionTimer();
|
||||
_syncPublishingState();
|
||||
notifyListeners();
|
||||
break;
|
||||
case AudioMixingStateType.audioMixingStatePaused:
|
||||
case RoomRtcAudioMixingState.paused:
|
||||
_isPlaying = false;
|
||||
_isPaused = true;
|
||||
_stopPositionTimer();
|
||||
_syncPublishingState();
|
||||
notifyListeners();
|
||||
break;
|
||||
case AudioMixingStateType.audioMixingStateStopped:
|
||||
case RoomRtcAudioMixingState.stopped:
|
||||
if (_isRoutingSwitching) {
|
||||
return;
|
||||
}
|
||||
@ -462,8 +462,7 @@ class RoomMusicManager extends ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
_stopPositionTimer();
|
||||
if (reason ==
|
||||
AudioMixingReasonType.audioMixingReasonAllLoopsCompleted) {
|
||||
if (reason == RoomRtcAudioMixingReason.allLoopsCompleted) {
|
||||
unawaited(next(fromAutoComplete: true));
|
||||
return;
|
||||
}
|
||||
@ -475,7 +474,7 @@ class RoomMusicManager extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
break;
|
||||
case AudioMixingStateType.audioMixingStateFailed:
|
||||
case RoomRtcAudioMixingState.failed:
|
||||
_stopPositionTimer();
|
||||
_isPlaying = false;
|
||||
_isPaused = false;
|
||||
|
||||
@ -180,14 +180,14 @@ class SocialChatUserProfile {
|
||||
_backgroundPhotos = backgroundPhotos;
|
||||
_personalPhotos = personalPhotos;
|
||||
_cpList = cpList;
|
||||
}
|
||||
|
||||
SocialChatUserProfile.fromJson(dynamic json) {
|
||||
final sourceJson = json;
|
||||
if (sourceJson is Map && sourceJson['userProfile'] is Map) {
|
||||
json = sourceJson['userProfile'];
|
||||
}
|
||||
_account = json['account'];
|
||||
}
|
||||
|
||||
SocialChatUserProfile.fromJson(dynamic json) {
|
||||
final sourceJson = json;
|
||||
if (sourceJson is Map && sourceJson['userProfile'] is Map) {
|
||||
json = sourceJson['userProfile'];
|
||||
}
|
||||
_account = json['account'];
|
||||
_accountStatus = json['accountStatus'];
|
||||
_age = json['age'];
|
||||
_bornDay = json['bornDay'];
|
||||
@ -210,10 +210,10 @@ class SocialChatUserProfile {
|
||||
json['ownSpecialId'] != null
|
||||
? OwnSpecialId.fromJson(json['ownSpecialId'])
|
||||
: null;
|
||||
_vipLevel = _firstNonBlankString([
|
||||
..._vipLevelHintValues(json),
|
||||
..._vipLevelHintValues(sourceJson),
|
||||
]);
|
||||
_vipLevel = _firstNonBlankString([
|
||||
..._vipLevelHintValues(json),
|
||||
..._vipLevelHintValues(sourceJson),
|
||||
]);
|
||||
_sameRegion = json['sameRegion'];
|
||||
_isCpRelation = json['isCpRelation'];
|
||||
_firstRechargeEndTime = json['firstRechargeEndTime'];
|
||||
@ -654,100 +654,100 @@ class SocialChatUserProfile {
|
||||
return resourceLevel!;
|
||||
}
|
||||
|
||||
return vipResource == null ? 0 : 1;
|
||||
}
|
||||
|
||||
int get vipVisualResourceLevelForColoredId {
|
||||
for (final resource in [
|
||||
getVipBadge(),
|
||||
getHeaddress(),
|
||||
getChatBox(),
|
||||
getFloatPicture(),
|
||||
getDataCard(),
|
||||
getMountains(),
|
||||
]) {
|
||||
final level = _vipLevelFromVisualResource(resource);
|
||||
if ((level ?? 0) > 0) {
|
||||
return level!;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// ID 数字渐变字的 VIP 条件。
|
||||
/// 新 VIP 接口优先读等级字段;旧数据继续兼容 useProps 里的 NOBLE_VIP。
|
||||
bool hasVipPrivilegeForColoredId() {
|
||||
return vipLevelForColoredId > 0;
|
||||
}
|
||||
|
||||
bool hasVipVisualResourceForColoredId() {
|
||||
return vipVisualResourceLevelForColoredId > 0;
|
||||
}
|
||||
|
||||
bool shouldShowColoredSpecialIdText() {
|
||||
return hasVipPrivilegeForColoredId() || hasVipVisualResourceForColoredId();
|
||||
}
|
||||
return vipResource == null ? 0 : 1;
|
||||
}
|
||||
|
||||
int get vipVisualResourceLevelForColoredId {
|
||||
for (final resource in [
|
||||
getVipBadge(),
|
||||
getHeaddress(),
|
||||
getChatBox(),
|
||||
getFloatPicture(),
|
||||
getDataCard(),
|
||||
getMountains(),
|
||||
]) {
|
||||
final level = _vipLevelFromVisualResource(resource);
|
||||
if ((level ?? 0) > 0) {
|
||||
return level!;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// ID 数字渐变字的 VIP 条件。
|
||||
/// 新 VIP 接口优先读等级字段;旧数据继续兼容 useProps 里的 NOBLE_VIP。
|
||||
bool hasVipPrivilegeForColoredId() {
|
||||
return vipLevelForColoredId > 0;
|
||||
}
|
||||
|
||||
bool hasVipVisualResourceForColoredId() {
|
||||
return vipVisualResourceLevelForColoredId > 0;
|
||||
}
|
||||
|
||||
bool shouldShowColoredSpecialIdText() {
|
||||
return hasVipPrivilegeForColoredId() || hasVipVisualResourceForColoredId();
|
||||
}
|
||||
|
||||
void setHeartbeatVal(num? heartbeatVal) {
|
||||
_heartbeatVal = (_heartbeatVal ?? 0) + (heartbeatVal ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
String? socialChatVipLevelHintFromJson(dynamic json) {
|
||||
return _firstNonBlankString(_vipLevelHintValues(json));
|
||||
}
|
||||
|
||||
SocialChatUserProfile? socialChatProfileWithVipLevelHint(
|
||||
SocialChatUserProfile? profile,
|
||||
dynamic json,
|
||||
) {
|
||||
final vipLevel = socialChatVipLevelHintFromJson(json);
|
||||
if (profile == null || vipLevel == null) {
|
||||
return profile;
|
||||
}
|
||||
return profile.copyWith(vipLevel: vipLevel);
|
||||
}
|
||||
|
||||
SocialChatUserProfile? socialChatUserProfileFromJsonWithVipHint(
|
||||
dynamic json, {
|
||||
String userProfileKey = 'userProfile',
|
||||
}) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
if (json is Map && json[userProfileKey] != null) {
|
||||
return socialChatProfileWithVipLevelHint(
|
||||
SocialChatUserProfile.fromJson(json[userProfileKey]),
|
||||
json,
|
||||
);
|
||||
}
|
||||
return SocialChatUserProfile.fromJson(json);
|
||||
}
|
||||
|
||||
Iterable<dynamic> _vipLevelHintValues(dynamic json) sync* {
|
||||
if (json is! Map) {
|
||||
return;
|
||||
}
|
||||
yield json['vipLevel'];
|
||||
yield json['svipLevel'];
|
||||
yield json['sVipLevel'];
|
||||
yield json['userSVipLevel'];
|
||||
yield json['supperVip'];
|
||||
yield json['superVip'];
|
||||
yield json['nobleVipLevel'];
|
||||
yield json['vipLevelCode'];
|
||||
yield json['levelCode'];
|
||||
yield _nestedVipLevel(json['vipStatus']);
|
||||
yield _nestedVipLevel(json['vipState']);
|
||||
yield _nestedVipLevel(json['vip']);
|
||||
final nestedProfile = json['userProfile'];
|
||||
if (nestedProfile is Map) {
|
||||
yield* _vipLevelHintValues(nestedProfile);
|
||||
}
|
||||
}
|
||||
|
||||
String? _firstNonBlankString(Iterable<dynamic> values) {
|
||||
for (final value in values) {
|
||||
}
|
||||
|
||||
String? socialChatVipLevelHintFromJson(dynamic json) {
|
||||
return _firstNonBlankString(_vipLevelHintValues(json));
|
||||
}
|
||||
|
||||
SocialChatUserProfile? socialChatProfileWithVipLevelHint(
|
||||
SocialChatUserProfile? profile,
|
||||
dynamic json,
|
||||
) {
|
||||
final vipLevel = socialChatVipLevelHintFromJson(json);
|
||||
if (profile == null || vipLevel == null) {
|
||||
return profile;
|
||||
}
|
||||
return profile.copyWith(vipLevel: vipLevel);
|
||||
}
|
||||
|
||||
SocialChatUserProfile? socialChatUserProfileFromJsonWithVipHint(
|
||||
dynamic json, {
|
||||
String userProfileKey = 'userProfile',
|
||||
}) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
if (json is Map && json[userProfileKey] != null) {
|
||||
return socialChatProfileWithVipLevelHint(
|
||||
SocialChatUserProfile.fromJson(json[userProfileKey]),
|
||||
json,
|
||||
);
|
||||
}
|
||||
return SocialChatUserProfile.fromJson(json);
|
||||
}
|
||||
|
||||
Iterable<dynamic> _vipLevelHintValues(dynamic json) sync* {
|
||||
if (json is! Map) {
|
||||
return;
|
||||
}
|
||||
yield json['vipLevel'];
|
||||
yield json['svipLevel'];
|
||||
yield json['sVipLevel'];
|
||||
yield json['userSVipLevel'];
|
||||
yield json['supperVip'];
|
||||
yield json['superVip'];
|
||||
yield json['nobleVipLevel'];
|
||||
yield json['vipLevelCode'];
|
||||
yield json['levelCode'];
|
||||
yield _nestedVipLevel(json['vipStatus']);
|
||||
yield _nestedVipLevel(json['vipState']);
|
||||
yield _nestedVipLevel(json['vip']);
|
||||
final nestedProfile = json['userProfile'];
|
||||
if (nestedProfile is Map) {
|
||||
yield* _vipLevelHintValues(nestedProfile);
|
||||
}
|
||||
}
|
||||
|
||||
String? _firstNonBlankString(Iterable<dynamic> values) {
|
||||
for (final value in values) {
|
||||
final text = value?.toString().trim();
|
||||
if (text != null && text.isNotEmpty) {
|
||||
return text;
|
||||
@ -756,54 +756,41 @@ String? _firstNonBlankString(Iterable<dynamic> values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
dynamic _nestedVipLevel(dynamic value) {
|
||||
if (value is Map) {
|
||||
final active = value['active'];
|
||||
if (active == false ||
|
||||
active?.toString().trim().toLowerCase() == 'false') {
|
||||
return '0';
|
||||
}
|
||||
return value['level'] ??
|
||||
value['vipLevel'] ??
|
||||
dynamic _nestedVipLevel(dynamic value) {
|
||||
if (value is Map) {
|
||||
final active = value['active'];
|
||||
if (active == false || active?.toString().trim().toLowerCase() == 'false') {
|
||||
return '0';
|
||||
}
|
||||
return value['level'] ??
|
||||
value['vipLevel'] ??
|
||||
value['userSVipLevel'] ??
|
||||
value['supperVip'] ??
|
||||
value['levelCode'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int? _vipLevelFromVisualResource(PropsResources? resource) {
|
||||
if (resource == null) {
|
||||
return null;
|
||||
}
|
||||
return _parseVipLevelForColoredId(
|
||||
resource.name,
|
||||
allowPlainNumber: false,
|
||||
) ??
|
||||
_parseVipLevelForColoredId(
|
||||
resource.code,
|
||||
allowPlainNumber: false,
|
||||
) ??
|
||||
_parseVipLevelForColoredId(
|
||||
resource.cover,
|
||||
allowPlainNumber: false,
|
||||
) ??
|
||||
_parseVipLevelForColoredId(
|
||||
resource.sourceUrl,
|
||||
allowPlainNumber: false,
|
||||
) ??
|
||||
_parseVipLevelForColoredId(
|
||||
resource.roomOpenedUrl,
|
||||
allowPlainNumber: false,
|
||||
) ??
|
||||
_parseVipLevelForColoredId(
|
||||
resource.roomSendCoverUrl,
|
||||
allowPlainNumber: false,
|
||||
);
|
||||
}
|
||||
|
||||
int? _parseVipLevelForColoredId(
|
||||
dynamic value, {
|
||||
return null;
|
||||
}
|
||||
|
||||
int? _vipLevelFromVisualResource(PropsResources? resource) {
|
||||
if (resource == null) {
|
||||
return null;
|
||||
}
|
||||
return _parseVipLevelForColoredId(resource.name, allowPlainNumber: false) ??
|
||||
_parseVipLevelForColoredId(resource.code, allowPlainNumber: false) ??
|
||||
_parseVipLevelForColoredId(resource.cover, allowPlainNumber: false) ??
|
||||
_parseVipLevelForColoredId(resource.sourceUrl, allowPlainNumber: false) ??
|
||||
_parseVipLevelForColoredId(
|
||||
resource.roomOpenedUrl,
|
||||
allowPlainNumber: false,
|
||||
) ??
|
||||
_parseVipLevelForColoredId(
|
||||
resource.roomSendCoverUrl,
|
||||
allowPlainNumber: false,
|
||||
);
|
||||
}
|
||||
|
||||
int? _parseVipLevelForColoredId(
|
||||
dynamic value, {
|
||||
bool allowPlainNumber = true,
|
||||
bool allowTrailingNumber = true,
|
||||
}) {
|
||||
@ -878,30 +865,25 @@ class WearBadge {
|
||||
_userId = userId;
|
||||
}
|
||||
|
||||
WearBadge.fromJson(dynamic json) {
|
||||
if (json is! Map) {
|
||||
return;
|
||||
}
|
||||
_animationUrl = _stringValue(json['animationUrl']);
|
||||
_badgeKey = _stringValue(json['badgeKey']);
|
||||
_badgeLevel = _numValue(json['badgeLevel'] ?? json['level']);
|
||||
_badgeName = _stringValue(
|
||||
json['badgeName'] ?? json['name'] ?? json['displayName'],
|
||||
);
|
||||
_expireTime = _intValue(json['expireTime'] ?? json['expireAt']);
|
||||
_id = _stringValue(json['id'] ?? json['badgeId']);
|
||||
_use = _boolValue(json['use']);
|
||||
_milestone = _stringValue(json['milestone']);
|
||||
_notSelectUrl = _stringValue(json['notSelectUrl']);
|
||||
_selectUrl = _stringValue(
|
||||
json['url'] ??
|
||||
json['selectUrl'] ??
|
||||
json['notSelectUrl'] ??
|
||||
json['animationUrl'],
|
||||
);
|
||||
_type = _stringValue(json['type']);
|
||||
_userId = _stringValue(json['userId']);
|
||||
}
|
||||
WearBadge.fromJson(dynamic json) {
|
||||
if (json is! Map) {
|
||||
return;
|
||||
}
|
||||
_animationUrl = _stringValue(json['animationUrl']);
|
||||
_badgeKey = _stringValue(json['badgeKey']);
|
||||
_badgeLevel = _numValue(json['badgeLevel'] ?? json['level']);
|
||||
_badgeName = _stringValue(
|
||||
json['badgeName'] ?? json['name'] ?? json['displayName'],
|
||||
);
|
||||
_expireTime = _intValue(json['expireTime'] ?? json['expireAt']);
|
||||
_id = _stringValue(json['id'] ?? json['badgeId']);
|
||||
_use = _boolValue(json['use']);
|
||||
_milestone = _stringValue(json['milestone']);
|
||||
_notSelectUrl = _stringValue(json['notSelectUrl']);
|
||||
_selectUrl = _stringValue(json['url'] ?? json['selectUrl']);
|
||||
_type = _stringValue(json['type']);
|
||||
_userId = _stringValue(json['userId']);
|
||||
}
|
||||
|
||||
String? _animationUrl;
|
||||
String? _badgeKey;
|
||||
@ -968,7 +950,7 @@ class WearBadge {
|
||||
|
||||
String? get userId => _userId;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['animationUrl'] = _animationUrl;
|
||||
map['badgeKey'] = _badgeKey;
|
||||
@ -982,48 +964,48 @@ class WearBadge {
|
||||
map['selectUrl'] = _selectUrl;
|
||||
map['type'] = _type;
|
||||
map['userId'] = _userId;
|
||||
return map;
|
||||
}
|
||||
|
||||
static String? _stringValue(dynamic value) {
|
||||
final text = value?.toString().trim();
|
||||
return text == null || text.isEmpty ? null : text;
|
||||
}
|
||||
|
||||
static int? _intValue(dynamic value) {
|
||||
if (value is int) {
|
||||
return value;
|
||||
}
|
||||
if (value is num) {
|
||||
return value.toInt();
|
||||
}
|
||||
return int.tryParse(value?.toString() ?? "");
|
||||
}
|
||||
|
||||
static num? _numValue(dynamic value) {
|
||||
if (value is num) {
|
||||
return value;
|
||||
}
|
||||
return num.tryParse(value?.toString() ?? "");
|
||||
}
|
||||
|
||||
static bool? _boolValue(dynamic value) {
|
||||
if (value is bool) {
|
||||
return value;
|
||||
}
|
||||
if (value is num) {
|
||||
return value != 0;
|
||||
}
|
||||
final text = value?.toString().trim().toLowerCase();
|
||||
if (text == "true" || text == "1") {
|
||||
return true;
|
||||
}
|
||||
if (text == "false" || text == "0") {
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
static String? _stringValue(dynamic value) {
|
||||
final text = value?.toString().trim();
|
||||
return text == null || text.isEmpty ? null : text;
|
||||
}
|
||||
|
||||
static int? _intValue(dynamic value) {
|
||||
if (value is int) {
|
||||
return value;
|
||||
}
|
||||
if (value is num) {
|
||||
return value.toInt();
|
||||
}
|
||||
return int.tryParse(value?.toString() ?? "");
|
||||
}
|
||||
|
||||
static num? _numValue(dynamic value) {
|
||||
if (value is num) {
|
||||
return value;
|
||||
}
|
||||
return num.tryParse(value?.toString() ?? "");
|
||||
}
|
||||
|
||||
static bool? _boolValue(dynamic value) {
|
||||
if (value is bool) {
|
||||
return value;
|
||||
}
|
||||
if (value is num) {
|
||||
return value != 0;
|
||||
}
|
||||
final text = value?.toString().trim().toLowerCase();
|
||||
if (text == "true" || text == "1") {
|
||||
return true;
|
||||
}
|
||||
if (text == "false" || text == "0") {
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class PersonPhoto {
|
||||
PersonPhoto({String? url, num? status}) {
|
||||
|
||||
@ -10,6 +10,8 @@ class SCRoomRocketLevelRes {
|
||||
required this.shakeThresholdPercent,
|
||||
required this.enabled,
|
||||
required this.sort,
|
||||
this.rewardPreview,
|
||||
this.rocketKings = const <SCRoomRocketKingUserRes>[],
|
||||
});
|
||||
|
||||
factory SCRoomRocketLevelRes.fromJson(dynamic json) {
|
||||
@ -192,6 +194,40 @@ class SCRoomRocketLevelRes {
|
||||
95,
|
||||
enabled: _boolValue(data['enabled'], fallback: true),
|
||||
sort: _intValue(data['sort']),
|
||||
rewardPreview: SCRoomRocketRewardPreviewRes.tryFromJson(
|
||||
_firstPayload([
|
||||
data['rewardPreview'],
|
||||
data['reward_preview'],
|
||||
data['rewardConfig'],
|
||||
data['reward_config'],
|
||||
data['rewards'],
|
||||
data['rewardList'],
|
||||
levelInfo['rewardPreview'],
|
||||
levelInfo['rewardConfig'],
|
||||
levelInfo['rewards'],
|
||||
levelInfo['rewardList'],
|
||||
config['rewardPreview'],
|
||||
config['rewardConfig'],
|
||||
config['rewards'],
|
||||
config['rewardList'],
|
||||
data,
|
||||
]),
|
||||
),
|
||||
rocketKings: SCRoomRocketKingUserRes.listFromJson(
|
||||
_firstPayload([
|
||||
data['rocketKings'],
|
||||
data['rocket_kings'],
|
||||
data['kings'],
|
||||
data['kingUsers'],
|
||||
data['podium'],
|
||||
levelInfo['rocketKings'],
|
||||
levelInfo['kings'],
|
||||
levelInfo['podium'],
|
||||
config['rocketKings'],
|
||||
config['kings'],
|
||||
config['podium'],
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -205,6 +241,8 @@ class SCRoomRocketLevelRes {
|
||||
final num shakeThresholdPercent;
|
||||
final bool enabled;
|
||||
final int sort;
|
||||
final SCRoomRocketRewardPreviewRes? rewardPreview;
|
||||
final List<SCRoomRocketKingUserRes> rocketKings;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
@ -218,6 +256,18 @@ class SCRoomRocketLevelRes {
|
||||
'shakeThresholdPercent': shakeThresholdPercent,
|
||||
'enabled': enabled,
|
||||
'sort': sort,
|
||||
'rewardPreview':
|
||||
rewardPreview == null
|
||||
? null
|
||||
: {
|
||||
'top1':
|
||||
rewardPreview!.top1.map((item) => item.toJson()).toList(),
|
||||
'ignite':
|
||||
rewardPreview!.ignite.map((item) => item.toJson()).toList(),
|
||||
'inRoom':
|
||||
rewardPreview!.inRoom.map((item) => item.toJson()).toList(),
|
||||
},
|
||||
'rocketKings': rocketKings.map((item) => item.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -229,6 +279,7 @@ class SCRoomRocketRewardRes {
|
||||
required this.rewardItemId,
|
||||
required this.rewardName,
|
||||
required this.rewardCover,
|
||||
required this.rocketIconUrl,
|
||||
required this.rewardAmount,
|
||||
required this.expireDays,
|
||||
});
|
||||
@ -241,6 +292,21 @@ class SCRoomRocketRewardRes {
|
||||
rewardItemId: _stringValue(data['rewardItemId']),
|
||||
rewardName: _stringValue(data['rewardName']),
|
||||
rewardCover: _stringValue(data['rewardCover']),
|
||||
rocketIconUrl: _firstAssetString([
|
||||
data['rocket_icon_url'],
|
||||
data['rocketIconUrl'],
|
||||
data['rocketIcon'],
|
||||
data['iconUrl'],
|
||||
_asMap(data['rocket'])['rocket_icon_url'],
|
||||
_asMap(data['rocket'])['rocketIconUrl'],
|
||||
_asMap(data['rocket'])['rocketIcon'],
|
||||
_asMap(data['levelInfo'])['rocket_icon_url'],
|
||||
_asMap(data['levelInfo'])['rocketIconUrl'],
|
||||
_asMap(data['levelInfo'])['rocketIcon'],
|
||||
_asMap(data['config'])['rocket_icon_url'],
|
||||
_asMap(data['config'])['rocketIconUrl'],
|
||||
_asMap(data['config'])['rocketIcon'],
|
||||
]),
|
||||
rewardAmount: _numValue(data['rewardAmount']) ?? 0,
|
||||
expireDays: _intValue(data['expireDays']),
|
||||
);
|
||||
@ -251,9 +317,23 @@ class SCRoomRocketRewardRes {
|
||||
final String rewardItemId;
|
||||
final String rewardName;
|
||||
final String rewardCover;
|
||||
final String rocketIconUrl;
|
||||
final num rewardAmount;
|
||||
final int expireDays;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'rewardScene': rewardScene,
|
||||
'rewardType': rewardType,
|
||||
'rewardItemId': rewardItemId,
|
||||
'rewardName': rewardName,
|
||||
'rewardCover': rewardCover,
|
||||
'rocketIconUrl': rocketIconUrl,
|
||||
'rewardAmount': rewardAmount,
|
||||
'expireDays': expireDays,
|
||||
};
|
||||
}
|
||||
|
||||
String get displayAmount {
|
||||
if (rewardAmount <= 0) {
|
||||
return '1';
|
||||
@ -273,11 +353,80 @@ class SCRoomRocketRewardPreviewRes {
|
||||
});
|
||||
|
||||
factory SCRoomRocketRewardPreviewRes.fromJson(dynamic json) {
|
||||
if (json is Iterable && json is! String) {
|
||||
return _fromFlatRewards(_rewardList(json));
|
||||
}
|
||||
final data = _asMap(json);
|
||||
return SCRoomRocketRewardPreviewRes(
|
||||
top1: _rewardList(data['top1']),
|
||||
ignite: _rewardList(data['ignite']),
|
||||
inRoom: _rewardList(data['inRoom']),
|
||||
final nested = tryFromJson(
|
||||
_firstPayload([
|
||||
data['rewardPreview'],
|
||||
data['reward_preview'],
|
||||
data['rewardConfig'],
|
||||
data['reward_config'],
|
||||
]),
|
||||
);
|
||||
if (nested != null) {
|
||||
return nested;
|
||||
}
|
||||
final preview = SCRoomRocketRewardPreviewRes(
|
||||
top1: _rewardList(
|
||||
_firstPayload([
|
||||
data['top1'],
|
||||
data['top_1'],
|
||||
data['topOne'],
|
||||
data['topOneRewards'],
|
||||
data['top1Rewards'],
|
||||
data['rank1'],
|
||||
data['rankOne'],
|
||||
data['first'],
|
||||
data['firstRewards'],
|
||||
data['king'],
|
||||
data['rocketKing'],
|
||||
]),
|
||||
),
|
||||
ignite: _rewardList(
|
||||
_firstPayload([
|
||||
data['ignite'],
|
||||
data['igniteRewards'],
|
||||
data['setOff'],
|
||||
data['set_off'],
|
||||
data['setOffRewards'],
|
||||
data['launch'],
|
||||
data['launchRewards'],
|
||||
data['launcher'],
|
||||
data['trigger'],
|
||||
data['triggerRewards'],
|
||||
]),
|
||||
),
|
||||
inRoom: _rewardList(
|
||||
_firstPayload([
|
||||
data['inRoom'],
|
||||
data['in_room'],
|
||||
data['inRoomRewards'],
|
||||
data['room'],
|
||||
data['roomRewards'],
|
||||
data['audience'],
|
||||
data['audienceRewards'],
|
||||
data['participants'],
|
||||
data['allUsers'],
|
||||
data['allUserRewards'],
|
||||
]),
|
||||
),
|
||||
);
|
||||
if (preview.isNotEmpty) {
|
||||
return preview;
|
||||
}
|
||||
return _fromFlatRewards(
|
||||
_rewardList(
|
||||
_firstPayload([
|
||||
data['rewards'],
|
||||
data['rewardList'],
|
||||
data['rewardPreviewList'],
|
||||
data['items'],
|
||||
data['list'],
|
||||
data['records'],
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -287,12 +436,86 @@ class SCRoomRocketRewardPreviewRes {
|
||||
|
||||
List<SCRoomRocketRewardRes> get all => [...top1, ...ignite, ...inRoom];
|
||||
|
||||
bool get isEmpty => top1.isEmpty && ignite.isEmpty && inRoom.isEmpty;
|
||||
|
||||
bool get isNotEmpty => !isEmpty;
|
||||
|
||||
static SCRoomRocketRewardPreviewRes? tryFromJson(dynamic json) {
|
||||
if (!_hasPayload(json)) {
|
||||
return null;
|
||||
}
|
||||
final preview = SCRoomRocketRewardPreviewRes.fromJson(json);
|
||||
return preview.isEmpty ? null : preview;
|
||||
}
|
||||
|
||||
static List<SCRoomRocketRewardRes> _rewardList(dynamic value) {
|
||||
if (value is! Iterable) {
|
||||
if (!_hasPayload(value)) {
|
||||
return const <SCRoomRocketRewardRes>[];
|
||||
}
|
||||
if (value is Map) {
|
||||
final data = _asMap(value);
|
||||
if (_looksLikeReward(data)) {
|
||||
return [SCRoomRocketRewardRes.fromJson(data)];
|
||||
}
|
||||
return data.values.expand(_rewardList).toList(growable: false);
|
||||
}
|
||||
if (value is! Iterable || value is String) {
|
||||
return const <SCRoomRocketRewardRes>[];
|
||||
}
|
||||
return value.map(SCRoomRocketRewardRes.fromJson).toList(growable: false);
|
||||
}
|
||||
|
||||
static SCRoomRocketRewardPreviewRes _fromFlatRewards(
|
||||
List<SCRoomRocketRewardRes> rewards,
|
||||
) {
|
||||
if (rewards.isEmpty) {
|
||||
return SCRoomRocketRewardPreviewRes(
|
||||
top1: const <SCRoomRocketRewardRes>[],
|
||||
ignite: const <SCRoomRocketRewardRes>[],
|
||||
inRoom: const <SCRoomRocketRewardRes>[],
|
||||
);
|
||||
}
|
||||
final top1 = <SCRoomRocketRewardRes>[];
|
||||
final ignite = <SCRoomRocketRewardRes>[];
|
||||
final inRoom = <SCRoomRocketRewardRes>[];
|
||||
for (final reward in rewards) {
|
||||
final scene = _normalizedScene(reward.rewardScene);
|
||||
if (scene.contains('TOP1') ||
|
||||
scene.contains('TOP_1') ||
|
||||
scene.contains('RANK1') ||
|
||||
scene.contains('RANK_1') ||
|
||||
scene.contains('FIRST') ||
|
||||
scene.contains('KING')) {
|
||||
top1.add(reward);
|
||||
} else if (scene.contains('IGNITE') ||
|
||||
scene.contains('SET_OFF') ||
|
||||
scene.contains('SETOFF') ||
|
||||
scene.contains('LAUNCH') ||
|
||||
scene.contains('TRIGGER')) {
|
||||
ignite.add(reward);
|
||||
} else {
|
||||
inRoom.add(reward);
|
||||
}
|
||||
}
|
||||
return SCRoomRocketRewardPreviewRes(
|
||||
top1: top1,
|
||||
ignite: ignite,
|
||||
inRoom: inRoom,
|
||||
);
|
||||
}
|
||||
|
||||
static bool _looksLikeReward(Map<String, dynamic> data) {
|
||||
return data.containsKey('rewardScene') ||
|
||||
data.containsKey('rewardType') ||
|
||||
data.containsKey('rewardItemId') ||
|
||||
data.containsKey('rewardName') ||
|
||||
data.containsKey('rewardCover') ||
|
||||
data.containsKey('rewardAmount');
|
||||
}
|
||||
|
||||
static String _normalizedScene(String value) {
|
||||
return value.trim().toUpperCase().replaceAll(RegExp(r'[^A-Z0-9]+'), '_');
|
||||
}
|
||||
}
|
||||
|
||||
class SCRoomRocketKingUserRes {
|
||||
@ -344,6 +567,19 @@ class SCRoomRocketKingUserRes {
|
||||
final String countryName;
|
||||
final num scoreEnergy;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'rank': rank,
|
||||
'userId': userId,
|
||||
'userAvatar': userAvatar,
|
||||
'userNickname': userNickname,
|
||||
'account': account,
|
||||
'countryCode': countryCode,
|
||||
'countryName': countryName,
|
||||
'scoreEnergy': scoreEnergy,
|
||||
};
|
||||
}
|
||||
|
||||
String get displayName {
|
||||
if (userNickname.trim().isNotEmpty) {
|
||||
return userNickname;
|
||||
@ -353,6 +589,44 @@ class SCRoomRocketKingUserRes {
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
static List<SCRoomRocketKingUserRes> listFromJson(dynamic value) {
|
||||
if (!_hasPayload(value)) {
|
||||
return const <SCRoomRocketKingUserRes>[];
|
||||
}
|
||||
if (value is Map) {
|
||||
final data = _asMap(value);
|
||||
final nested = _firstPayload([
|
||||
data['records'],
|
||||
data['podium'],
|
||||
data['rocketKings'],
|
||||
data['kings'],
|
||||
data['items'],
|
||||
data['list'],
|
||||
]);
|
||||
if (nested != null) {
|
||||
return listFromJson(nested);
|
||||
}
|
||||
if (_looksLikeKingUser(data)) {
|
||||
return [SCRoomRocketKingUserRes.fromJson(data)];
|
||||
}
|
||||
return data.values.expand(listFromJson).toList(growable: false);
|
||||
}
|
||||
if (value is! Iterable || value is String) {
|
||||
return const <SCRoomRocketKingUserRes>[];
|
||||
}
|
||||
return value.map(SCRoomRocketKingUserRes.fromJson).toList(growable: false);
|
||||
}
|
||||
|
||||
static bool _looksLikeKingUser(Map<String, dynamic> data) {
|
||||
return data.containsKey('rank') ||
|
||||
data.containsKey('ranking') ||
|
||||
data.containsKey('userId') ||
|
||||
data.containsKey('userAvatar') ||
|
||||
data.containsKey('avatar') ||
|
||||
data.containsKey('userProfile') ||
|
||||
data.containsKey('user');
|
||||
}
|
||||
}
|
||||
|
||||
class SCRoomRocketKingRes {
|
||||
@ -403,10 +677,7 @@ class SCRoomRocketKingRes {
|
||||
}
|
||||
|
||||
static List<SCRoomRocketKingUserRes> _kingList(dynamic value) {
|
||||
if (value is! Iterable) {
|
||||
return const <SCRoomRocketKingUserRes>[];
|
||||
}
|
||||
return value.map(SCRoomRocketKingUserRes.fromJson).toList(growable: false);
|
||||
return SCRoomRocketKingUserRes.listFromJson(value);
|
||||
}
|
||||
|
||||
static SCRoomRocketKingUserRes? _kingUser(dynamic value) {
|
||||
@ -501,6 +772,7 @@ class SCRoomRocketRewardRecordRes {
|
||||
required this.rewardName,
|
||||
required this.rewardCover,
|
||||
required this.rewardAmount,
|
||||
required this.rocketIconUrl,
|
||||
required this.expireDays,
|
||||
required this.grantStatus,
|
||||
required this.popupStatus,
|
||||
@ -544,6 +816,21 @@ class SCRoomRocketRewardRecordRes {
|
||||
rewardName: _stringValue(data['rewardName']),
|
||||
rewardCover: _stringValue(data['rewardCover']),
|
||||
rewardAmount: _numValue(data['rewardAmount']) ?? 0,
|
||||
rocketIconUrl: _firstAssetString([
|
||||
data['rocket_icon_url'],
|
||||
data['rocketIconUrl'],
|
||||
data['rocketIcon'],
|
||||
data['iconUrl'],
|
||||
_asMap(data['rocket'])['rocket_icon_url'],
|
||||
_asMap(data['rocket'])['rocketIconUrl'],
|
||||
_asMap(data['rocket'])['rocketIcon'],
|
||||
_asMap(data['levelInfo'])['rocket_icon_url'],
|
||||
_asMap(data['levelInfo'])['rocketIconUrl'],
|
||||
_asMap(data['levelInfo'])['rocketIcon'],
|
||||
_asMap(data['config'])['rocket_icon_url'],
|
||||
_asMap(data['config'])['rocketIconUrl'],
|
||||
_asMap(data['config'])['rocketIcon'],
|
||||
]),
|
||||
expireDays: _intValue(data['expireDays']),
|
||||
grantStatus: _stringValue(data['grantStatus']),
|
||||
popupStatus: _stringValue(data['popupStatus']),
|
||||
@ -569,6 +856,7 @@ class SCRoomRocketRewardRecordRes {
|
||||
final String rewardName;
|
||||
final String rewardCover;
|
||||
final num rewardAmount;
|
||||
final String rocketIconUrl;
|
||||
final int expireDays;
|
||||
final String grantStatus;
|
||||
final String popupStatus;
|
||||
@ -617,6 +905,7 @@ class SCRoomRocketRewardRecordRes {
|
||||
"rewardName": rewardName,
|
||||
"rewardCover": rewardCover,
|
||||
"rewardAmount": rewardAmount,
|
||||
"rocketIconUrl": rocketIconUrl,
|
||||
"expireDays": expireDays,
|
||||
"grantStatus": grantStatus,
|
||||
"popupStatus": popupStatus,
|
||||
@ -709,6 +998,35 @@ String _assetString(dynamic value) {
|
||||
return text;
|
||||
}
|
||||
|
||||
dynamic _firstPayload(Iterable<dynamic> values) {
|
||||
for (final value in values) {
|
||||
if (_hasPayload(value)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool _hasPayload(dynamic value) {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
if (value is String) {
|
||||
final text = value.trim();
|
||||
return text.isNotEmpty &&
|
||||
text != '{}' &&
|
||||
text != '[]' &&
|
||||
text.toLowerCase() != 'null';
|
||||
}
|
||||
if (value is Map) {
|
||||
return value.isNotEmpty;
|
||||
}
|
||||
if (value is Iterable) {
|
||||
return value.isNotEmpty;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int _intValue(dynamic value, {int fallback = 0}) {
|
||||
if (value is int) {
|
||||
return value;
|
||||
|
||||
@ -1,186 +1,273 @@
|
||||
/// enabled : true
|
||||
/// id : 3
|
||||
/// level : 3
|
||||
/// levelName : "三级火箭"
|
||||
/// maxEnergy : 1000000
|
||||
/// normalRewardConfig : []
|
||||
/// rewardConfig : [{"amount":1969014438069481474,"content":"1969014438069481474","cover":"https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/svga_cover/manager-5058b89a-8654-4895-866e-fe4295e88408.png","createTime":1758286290000,"detailType":"AVATAR_FRAME","groupId":1966024426660499457,"id":1969021529953873921,"name":"yumi-KingTOP1头饰","quantity":7,"remark":"","sort":1,"sourceUrl":"https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/svgasource/manager-975e24c5-e87a-4236-9256-799c127a06bc.svga","type":"PROPS","updateTime":1758286290000},{"amount":1969014872674873345,"badgeName":"yumi-King'Medal","content":"1969014872674873345","cover":"https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/other/manager-fc5ad0a4-5e4a-498b-8494-2aee2efe5b99.png","createTime":1758286290000,"detailType":"BADGE","groupId":1966024426660499457,"id":1969021529953873922,"quantity":7,"remark":"","sort":2,"sourceUrl":"https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/other/manager-176fcf7e-787b-4bf6-bdb4-238d8388a5e2.svga","type":"BADGE","updateTime":1758286290000},{"amount":30000,"content":"30000","createTime":1758286290000,"detailType":"GOLD","groupId":1966024426660499457,"id":1969021529953873923,"quantity":0,"remark":"","sort":3,"type":"GOLD","updateTime":1758286290000},{"amount":1962463978629165057,"content":"1962463978629165057","cover":"https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/svga_cover/manager-6633f4cb-58b8-4b7d-8075-7f0a6f29fae0.png","createTime":1758286290000,"detailType":"RIDE","groupId":1966024426660499457,"id":1969021529953873924,"name":"后续活动座驾","quantity":7,"remark":"","sort":4,"sourceUrl":"https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/svgasource/manager-536ac482-8e9d-4a21-acff-531d51ff5b66.mp4","type":"PROPS","updateTime":1758286290000}]
|
||||
/// status : 1
|
||||
|
||||
class SCRoomRocketConfigRes {
|
||||
SCRoomRocketConfigRes({
|
||||
bool? enabled,
|
||||
String? id,
|
||||
num? level,
|
||||
String? levelName,
|
||||
int? maxEnergy,
|
||||
List<RewardConfig>? rewardConfig,
|
||||
num? status,}){
|
||||
_enabled = enabled;
|
||||
_id = id;
|
||||
_level = level;
|
||||
_levelName = levelName;
|
||||
_maxEnergy = maxEnergy;
|
||||
_rewardConfig = rewardConfig;
|
||||
_status = status;
|
||||
}
|
||||
|
||||
SCRoomRocketConfigRes.fromJson(dynamic json) {
|
||||
_enabled = json['enabled'];
|
||||
_id = json['id'];
|
||||
_level = json['level'];
|
||||
_levelName = json['levelName'];
|
||||
_maxEnergy = json['maxEnergy'];
|
||||
if (json['rewardConfig'] != null) {
|
||||
_rewardConfig = [];
|
||||
json['rewardConfig'].forEach((v) {
|
||||
_rewardConfig?.add(RewardConfig.fromJson(v));
|
||||
});
|
||||
}
|
||||
_status = json['status'];
|
||||
}
|
||||
bool? _enabled;
|
||||
String? _id;
|
||||
num? _level;
|
||||
String? _levelName;
|
||||
int? _maxEnergy;
|
||||
List<RewardConfig>? _rewardConfig;
|
||||
num? _status;
|
||||
|
||||
bool? get enabled => _enabled;
|
||||
String? get id => _id;
|
||||
num? get level => _level;
|
||||
String? get levelName => _levelName;
|
||||
int? get maxEnergy => _maxEnergy;
|
||||
List<RewardConfig>? get rewardConfig => _rewardConfig;
|
||||
num? get status => _status;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['enabled'] = _enabled;
|
||||
map['id'] = _id;
|
||||
map['level'] = _level;
|
||||
map['levelName'] = _levelName;
|
||||
map['maxEnergy'] = _maxEnergy;
|
||||
if (_rewardConfig != null) {
|
||||
map['rewardConfig'] = _rewardConfig?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
map['status'] = _status;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// amount : 1969014438069481474
|
||||
/// content : "1969014438069481474"
|
||||
/// cover : "https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/svga_cover/manager-5058b89a-8654-4895-866e-fe4295e88408.png"
|
||||
/// createTime : 1758286290000
|
||||
/// detailType : "AVATAR_FRAME"
|
||||
/// groupId : 1966024426660499457
|
||||
/// id : 1969021529953873921
|
||||
/// name : "Yumi-KingTOP1头饰"
|
||||
/// quantity : 7
|
||||
/// remark : ""
|
||||
/// sort : 1
|
||||
/// sourceUrl : "https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/svgasource/manager-975e24c5-e87a-4236-9256-799c127a06bc.svga"
|
||||
/// type : "PROPS"
|
||||
/// updateTime : 1758286290000
|
||||
|
||||
class RewardConfig {
|
||||
RewardConfig({
|
||||
num? amount,
|
||||
String? content,
|
||||
String? cover,
|
||||
num? createTime,
|
||||
String? detailType,
|
||||
String? groupId,
|
||||
String? id,
|
||||
String? name,
|
||||
num? quantity,
|
||||
String? remark,
|
||||
num? sort,
|
||||
String? sourceUrl,
|
||||
String? type,
|
||||
num? updateTime,}){
|
||||
_amount = amount;
|
||||
_content = content;
|
||||
_cover = cover;
|
||||
_createTime = createTime;
|
||||
_detailType = detailType;
|
||||
_groupId = groupId;
|
||||
_id = id;
|
||||
_name = name;
|
||||
_quantity = quantity;
|
||||
_remark = remark;
|
||||
_sort = sort;
|
||||
_sourceUrl = sourceUrl;
|
||||
_type = type;
|
||||
_updateTime = updateTime;
|
||||
}
|
||||
|
||||
RewardConfig.fromJson(dynamic json) {
|
||||
_amount = json['amount'];
|
||||
_content = json['content'];
|
||||
_cover = json['cover'];
|
||||
_createTime = json['createTime'];
|
||||
_detailType = json['detailType'];
|
||||
_groupId = json['groupId'];
|
||||
_id = json['id'];
|
||||
_name = json['name'];
|
||||
_quantity = json['quantity'];
|
||||
_remark = json['remark'];
|
||||
_sort = json['sort'];
|
||||
_sourceUrl = json['sourceUrl'];
|
||||
_type = json['type'];
|
||||
_updateTime = json['updateTime'];
|
||||
}
|
||||
num? _amount;
|
||||
String? _content;
|
||||
String? _cover;
|
||||
num? _createTime;
|
||||
String? _detailType;
|
||||
String? _groupId;
|
||||
String? _id;
|
||||
String? _name;
|
||||
num? _quantity;
|
||||
String? _remark;
|
||||
num? _sort;
|
||||
String? _sourceUrl;
|
||||
String? _type;
|
||||
num? _updateTime;
|
||||
|
||||
num? get amount => _amount;
|
||||
String? get content => _content;
|
||||
String? get cover => _cover;
|
||||
num? get createTime => _createTime;
|
||||
String? get detailType => _detailType;
|
||||
String? get groupId => _groupId;
|
||||
String? get id => _id;
|
||||
String? get name => _name;
|
||||
num? get quantity => _quantity;
|
||||
String? get remark => _remark;
|
||||
num? get sort => _sort;
|
||||
String? get sourceUrl => _sourceUrl;
|
||||
String? get type => _type;
|
||||
num? get updateTime => _updateTime;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['amount'] = _amount;
|
||||
map['content'] = _content;
|
||||
map['cover'] = _cover;
|
||||
map['createTime'] = _createTime;
|
||||
map['detailType'] = _detailType;
|
||||
map['groupId'] = _groupId;
|
||||
map['id'] = _id;
|
||||
map['name'] = _name;
|
||||
map['quantity'] = _quantity;
|
||||
map['remark'] = _remark;
|
||||
map['sort'] = _sort;
|
||||
map['sourceUrl'] = _sourceUrl;
|
||||
map['type'] = _type;
|
||||
map['updateTime'] = _updateTime;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
// enabled : true
|
||||
// id : 3
|
||||
// level : 3
|
||||
// levelName : "三级火箭"
|
||||
// maxEnergy : 1000000
|
||||
// normalRewardConfig : []
|
||||
// rewardConfig : [{"amount":1969014438069481474,"content":"1969014438069481474","cover":"https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/svga_cover/manager-5058b89a-8654-4895-866e-fe4295e88408.png","createTime":1758286290000,"detailType":"AVATAR_FRAME","groupId":1966024426660499457,"id":1969021529953873921,"name":"yumi-KingTOP1头饰","quantity":7,"remark":"","sort":1,"sourceUrl":"https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/svgasource/manager-975e24c5-e87a-4236-9256-799c127a06bc.svga","type":"PROPS","updateTime":1758286290000},{"amount":1969014872674873345,"badgeName":"yumi-King'Medal","content":"1969014872674873345","cover":"https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/other/manager-fc5ad0a4-5e4a-498b-8494-2aee2efe5b99.png","createTime":1758286290000,"detailType":"BADGE","groupId":1966024426660499457,"id":1969021529953873922,"quantity":7,"remark":"","sort":2,"sourceUrl":"https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/other/manager-176fcf7e-787b-4bf6-bdb4-238d8388a5e2.svga","type":"BADGE","updateTime":1758286290000},{"amount":30000,"content":"30000","createTime":1758286290000,"detailType":"GOLD","groupId":1966024426660499457,"id":1969021529953873923,"quantity":0,"remark":"","sort":3,"type":"GOLD","updateTime":1758286290000},{"amount":1962463978629165057,"content":"1962463978629165057","cover":"https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/svga_cover/manager-6633f4cb-58b8-4b7d-8075-7f0a6f29fae0.png","createTime":1758286290000,"detailType":"RIDE","groupId":1966024426660499457,"id":1969021529953873924,"name":"后续活动座驾","quantity":7,"remark":"","sort":4,"sourceUrl":"https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/svgasource/manager-536ac482-8e9d-4a21-acff-531d51ff5b66.mp4","type":"PROPS","updateTime":1758286290000}]
|
||||
// status : 1
|
||||
|
||||
class SCRoomRocketConfigRes {
|
||||
SCRoomRocketConfigRes({
|
||||
bool? enabled,
|
||||
String? id,
|
||||
num? level,
|
||||
String? levelName,
|
||||
int? maxEnergy,
|
||||
List<RewardConfig>? rewardConfig,
|
||||
List<RewardConfig>? normalRewardConfig,
|
||||
List<RewardConfig>? igniteRewardConfig,
|
||||
List<RewardConfig>? inRoomRewardConfig,
|
||||
num? status,
|
||||
}) {
|
||||
_enabled = enabled;
|
||||
_id = id;
|
||||
_level = level;
|
||||
_levelName = levelName;
|
||||
_maxEnergy = maxEnergy;
|
||||
_rewardConfig = rewardConfig;
|
||||
_normalRewardConfig = normalRewardConfig;
|
||||
_igniteRewardConfig = igniteRewardConfig;
|
||||
_inRoomRewardConfig = inRoomRewardConfig;
|
||||
_status = status;
|
||||
}
|
||||
|
||||
SCRoomRocketConfigRes.fromJson(dynamic json) {
|
||||
_enabled = json['enabled'];
|
||||
_id = json['id'];
|
||||
_level = json['level'];
|
||||
_levelName = json['levelName'];
|
||||
_maxEnergy = json['maxEnergy'];
|
||||
_rewardConfig = _rewardList(
|
||||
json['rewardConfig'] ??
|
||||
json['top1RewardConfig'] ??
|
||||
json['topOneRewardConfig'] ??
|
||||
json['kingRewardConfig'] ??
|
||||
json['rocketKingRewardConfig'],
|
||||
);
|
||||
_normalRewardConfig = _rewardList(
|
||||
json['normalRewardConfig'] ??
|
||||
json['normalRewards'] ??
|
||||
json['ordinaryRewardConfig'],
|
||||
);
|
||||
_igniteRewardConfig = _rewardList(
|
||||
json['igniteRewardConfig'] ??
|
||||
json['setOffRewardConfig'] ??
|
||||
json['launchRewardConfig'] ??
|
||||
json['triggerRewardConfig'],
|
||||
);
|
||||
_inRoomRewardConfig = _rewardList(
|
||||
json['inRoomRewardConfig'] ??
|
||||
json['roomRewardConfig'] ??
|
||||
json['audienceRewardConfig'] ??
|
||||
json['allUserRewardConfig'],
|
||||
);
|
||||
_status = json['status'];
|
||||
}
|
||||
|
||||
bool? _enabled;
|
||||
String? _id;
|
||||
num? _level;
|
||||
String? _levelName;
|
||||
int? _maxEnergy;
|
||||
List<RewardConfig>? _rewardConfig;
|
||||
List<RewardConfig>? _normalRewardConfig;
|
||||
List<RewardConfig>? _igniteRewardConfig;
|
||||
List<RewardConfig>? _inRoomRewardConfig;
|
||||
num? _status;
|
||||
|
||||
bool? get enabled => _enabled;
|
||||
|
||||
String? get id => _id;
|
||||
|
||||
num? get level => _level;
|
||||
|
||||
String? get levelName => _levelName;
|
||||
|
||||
int? get maxEnergy => _maxEnergy;
|
||||
|
||||
List<RewardConfig>? get rewardConfig => _rewardConfig;
|
||||
|
||||
List<RewardConfig>? get normalRewardConfig => _normalRewardConfig;
|
||||
|
||||
List<RewardConfig>? get igniteRewardConfig => _igniteRewardConfig;
|
||||
|
||||
List<RewardConfig>? get inRoomRewardConfig => _inRoomRewardConfig;
|
||||
|
||||
num? get status => _status;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['enabled'] = _enabled;
|
||||
map['id'] = _id;
|
||||
map['level'] = _level;
|
||||
map['levelName'] = _levelName;
|
||||
map['maxEnergy'] = _maxEnergy;
|
||||
if (_rewardConfig != null) {
|
||||
map['rewardConfig'] = _rewardConfig?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
if (_normalRewardConfig != null) {
|
||||
map['normalRewardConfig'] =
|
||||
_normalRewardConfig?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
if (_igniteRewardConfig != null) {
|
||||
map['igniteRewardConfig'] =
|
||||
_igniteRewardConfig?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
if (_inRoomRewardConfig != null) {
|
||||
map['inRoomRewardConfig'] =
|
||||
_inRoomRewardConfig?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
map['status'] = _status;
|
||||
return map;
|
||||
}
|
||||
|
||||
static List<RewardConfig>? _rewardList(dynamic value) {
|
||||
if (value is! Iterable) {
|
||||
return null;
|
||||
}
|
||||
return value.map(RewardConfig.fromJson).toList(growable: false);
|
||||
}
|
||||
}
|
||||
|
||||
// amount : 1969014438069481474
|
||||
// content : "1969014438069481474"
|
||||
// cover : "https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/svga_cover/manager-5058b89a-8654-4895-866e-fe4295e88408.png"
|
||||
// createTime : 1758286290000
|
||||
// detailType : "AVATAR_FRAME"
|
||||
// groupId : 1966024426660499457
|
||||
// id : 1969021529953873921
|
||||
// name : "Yumi-KingTOP1头饰"
|
||||
// quantity : 7
|
||||
// remark : ""
|
||||
// sort : 1
|
||||
// sourceUrl : "https://tkm-yumi.oss-ap-southeast-1.aliyuncs.com/svgasource/manager-975e24c5-e87a-4236-9256-799c127a06bc.svga"
|
||||
// type : "PROPS"
|
||||
// updateTime : 1758286290000
|
||||
|
||||
class RewardConfig {
|
||||
RewardConfig({
|
||||
num? amount,
|
||||
String? content,
|
||||
String? cover,
|
||||
num? createTime,
|
||||
String? detailType,
|
||||
String? groupId,
|
||||
String? id,
|
||||
String? name,
|
||||
String? badgeName,
|
||||
num? quantity,
|
||||
String? remark,
|
||||
num? sort,
|
||||
String? sourceUrl,
|
||||
String? type,
|
||||
String? rewardScene,
|
||||
num? updateTime,
|
||||
}) {
|
||||
_amount = amount;
|
||||
_content = content;
|
||||
_cover = cover;
|
||||
_createTime = createTime;
|
||||
_detailType = detailType;
|
||||
_groupId = groupId;
|
||||
_id = id;
|
||||
_name = name;
|
||||
_badgeName = badgeName;
|
||||
_quantity = quantity;
|
||||
_remark = remark;
|
||||
_sort = sort;
|
||||
_sourceUrl = sourceUrl;
|
||||
_type = type;
|
||||
_rewardScene = rewardScene;
|
||||
_updateTime = updateTime;
|
||||
}
|
||||
|
||||
RewardConfig.fromJson(dynamic json) {
|
||||
_amount = json['amount'];
|
||||
_content = json['content'];
|
||||
_cover = json['cover'];
|
||||
_createTime = json['createTime'];
|
||||
_detailType = json['detailType'];
|
||||
_groupId = json['groupId'];
|
||||
_id = json['id'];
|
||||
_name = json['name'];
|
||||
_badgeName = json['badgeName'];
|
||||
_quantity = json['quantity'];
|
||||
_remark = json['remark'];
|
||||
_sort = json['sort'];
|
||||
_sourceUrl = json['sourceUrl'];
|
||||
_type = json['type'];
|
||||
_rewardScene = json['rewardScene'] ?? json['scene'];
|
||||
_updateTime = json['updateTime'];
|
||||
}
|
||||
|
||||
num? _amount;
|
||||
String? _content;
|
||||
String? _cover;
|
||||
num? _createTime;
|
||||
String? _detailType;
|
||||
String? _groupId;
|
||||
String? _id;
|
||||
String? _name;
|
||||
String? _badgeName;
|
||||
num? _quantity;
|
||||
String? _remark;
|
||||
num? _sort;
|
||||
String? _sourceUrl;
|
||||
String? _type;
|
||||
String? _rewardScene;
|
||||
num? _updateTime;
|
||||
|
||||
num? get amount => _amount;
|
||||
|
||||
String? get content => _content;
|
||||
|
||||
String? get cover => _cover;
|
||||
|
||||
num? get createTime => _createTime;
|
||||
|
||||
String? get detailType => _detailType;
|
||||
|
||||
String? get groupId => _groupId;
|
||||
|
||||
String? get id => _id;
|
||||
|
||||
String? get name => _name;
|
||||
|
||||
String? get badgeName => _badgeName;
|
||||
|
||||
num? get quantity => _quantity;
|
||||
|
||||
String? get remark => _remark;
|
||||
|
||||
num? get sort => _sort;
|
||||
|
||||
String? get sourceUrl => _sourceUrl;
|
||||
|
||||
String? get type => _type;
|
||||
|
||||
String? get rewardScene => _rewardScene;
|
||||
|
||||
num? get updateTime => _updateTime;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['amount'] = _amount;
|
||||
map['content'] = _content;
|
||||
map['cover'] = _cover;
|
||||
map['createTime'] = _createTime;
|
||||
map['detailType'] = _detailType;
|
||||
map['groupId'] = _groupId;
|
||||
map['id'] = _id;
|
||||
map['name'] = _name;
|
||||
map['badgeName'] = _badgeName;
|
||||
map['quantity'] = _quantity;
|
||||
map['remark'] = _remark;
|
||||
map['sort'] = _sort;
|
||||
map['sourceUrl'] = _sourceUrl;
|
||||
map['type'] = _type;
|
||||
map['rewardScene'] = _rewardScene;
|
||||
map['updateTime'] = _updateTime;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +45,9 @@ class SCRoomRocketStatusRes {
|
||||
bool? shake,
|
||||
List<SCRoomRocketLevelRes>? levels,
|
||||
SCRoomRocketRewardPreviewRes? rewardPreview,
|
||||
Map<int, SCRoomRocketRewardPreviewRes>? rewardPreviewByLevel,
|
||||
List<SCRoomRocketKingUserRes>? rocketKings,
|
||||
Map<int, List<SCRoomRocketKingUserRes>>? rocketKingsByLevel,
|
||||
String? timezone,
|
||||
int? resetRemainingSeconds,
|
||||
Object? resetTime,
|
||||
@ -75,7 +77,9 @@ class SCRoomRocketStatusRes {
|
||||
_shake = shake;
|
||||
_levels = levels;
|
||||
_rewardPreview = rewardPreview;
|
||||
_rewardPreviewByLevel = rewardPreviewByLevel;
|
||||
_rocketKings = rocketKings;
|
||||
_rocketKingsByLevel = rocketKingsByLevel;
|
||||
_timezone = timezone;
|
||||
_resetRemainingSeconds = resetRemainingSeconds;
|
||||
_resetTime = resetTime;
|
||||
@ -98,7 +102,38 @@ class SCRoomRocketStatusRes {
|
||||
_energyPercent = _numValue(json['energyPercent']) ?? _displayPercent;
|
||||
_status = json['status'];
|
||||
_statusDesc = json['statusDesc'];
|
||||
_rocketKings = _kingList(json['rocketKings']);
|
||||
_levels = _levelList(json['levels']);
|
||||
_rewardPreviewByLevel = _rewardPreviewMap(json, _levels);
|
||||
_rewardPreview =
|
||||
SCRoomRocketRewardPreviewRes.tryFromJson(
|
||||
_firstPayload([
|
||||
json['rewardPreview'],
|
||||
json['reward_preview'],
|
||||
json['rewardConfig'],
|
||||
json['reward_config'],
|
||||
json['rewards'],
|
||||
json['rewardList'],
|
||||
]),
|
||||
) ??
|
||||
_rewardPreviewForCurrentLevel(
|
||||
_rewardPreviewByLevel,
|
||||
_currentLevel ?? _intValue(_level),
|
||||
);
|
||||
_rocketKingsByLevel = _rocketKingsMap(json, _levels);
|
||||
_rocketKings = _kingList(
|
||||
_firstPayload([
|
||||
json['rocketKings'],
|
||||
json['rocket_kings'],
|
||||
json['kings'],
|
||||
json['podium'],
|
||||
]),
|
||||
);
|
||||
if ((_rocketKings?.isEmpty ?? true) && _rocketKingsByLevel != null) {
|
||||
_rocketKings = _rocketKingsForCurrentLevel(
|
||||
_rocketKingsByLevel,
|
||||
_currentLevel ?? _intValue(_level),
|
||||
);
|
||||
}
|
||||
_totalContributors =
|
||||
json['totalContributors'] ?? (_rocketKings?.length ?? 0);
|
||||
_claimExpireTime = json['claimExpireTime'];
|
||||
@ -107,11 +142,6 @@ class SCRoomRocketStatusRes {
|
||||
_hasClaimed = json['hasClaimed'];
|
||||
_myContribution = json['myContribution'];
|
||||
_myContributionRate = json['myContributionRate'];
|
||||
_levels = _levelList(json['levels']);
|
||||
_rewardPreview =
|
||||
json['rewardPreview'] == null
|
||||
? null
|
||||
: SCRoomRocketRewardPreviewRes.fromJson(json['rewardPreview']);
|
||||
_timezone = _stringValue(json['timezone'] ?? json['timeZone']);
|
||||
final resetRemainingMillis = _intValue(
|
||||
json['resetRemainingMillis'] ??
|
||||
@ -180,7 +210,9 @@ class SCRoomRocketStatusRes {
|
||||
bool? _shake;
|
||||
List<SCRoomRocketLevelRes>? _levels;
|
||||
SCRoomRocketRewardPreviewRes? _rewardPreview;
|
||||
Map<int, SCRoomRocketRewardPreviewRes>? _rewardPreviewByLevel;
|
||||
List<SCRoomRocketKingUserRes>? _rocketKings;
|
||||
Map<int, List<SCRoomRocketKingUserRes>>? _rocketKingsByLevel;
|
||||
String? _timezone;
|
||||
int? _resetRemainingSeconds;
|
||||
Object? _resetTime;
|
||||
@ -234,8 +266,14 @@ class SCRoomRocketStatusRes {
|
||||
|
||||
SCRoomRocketRewardPreviewRes? get rewardPreview => _rewardPreview;
|
||||
|
||||
Map<int, SCRoomRocketRewardPreviewRes>? get rewardPreviewByLevel =>
|
||||
_rewardPreviewByLevel;
|
||||
|
||||
List<SCRoomRocketKingUserRes>? get rocketKings => _rocketKings;
|
||||
|
||||
Map<int, List<SCRoomRocketKingUserRes>>? get rocketKingsByLevel =>
|
||||
_rocketKingsByLevel;
|
||||
|
||||
String? get timezone => _timezone;
|
||||
|
||||
int? get resetRemainingSeconds => _resetRemainingSeconds;
|
||||
@ -244,6 +282,60 @@ class SCRoomRocketStatusRes {
|
||||
|
||||
Object? get serverTime => _serverTime;
|
||||
|
||||
SCRoomRocketRewardPreviewRes? rewardPreviewForLevel(int level) {
|
||||
final normalizedLevel = level.clamp(1, 99).toInt();
|
||||
final byLevel = _rewardPreviewByLevel?[normalizedLevel];
|
||||
if (byLevel != null) {
|
||||
return byLevel;
|
||||
}
|
||||
final levelPreview = _levelFor(normalizedLevel)?.rewardPreview;
|
||||
if (levelPreview != null) {
|
||||
return levelPreview;
|
||||
}
|
||||
final responseLevel = (_level?.toInt() ?? 0).clamp(0, 99).toInt();
|
||||
if (responseLevel > 0) {
|
||||
return normalizedLevel == responseLevel ? _rewardPreview : null;
|
||||
}
|
||||
final current = (_currentLevel ?? 0).clamp(0, 99).toInt();
|
||||
if (current <= 0 || normalizedLevel == current) {
|
||||
return _rewardPreview;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
List<SCRoomRocketKingUserRes> rocketKingsForLevel(int level) {
|
||||
final normalizedLevel = level.clamp(1, 99).toInt();
|
||||
final byLevel = _rocketKingsByLevel?[normalizedLevel];
|
||||
if (byLevel != null) {
|
||||
return byLevel;
|
||||
}
|
||||
final levelKings = _levelFor(normalizedLevel)?.rocketKings;
|
||||
if (levelKings != null && levelKings.isNotEmpty) {
|
||||
return levelKings;
|
||||
}
|
||||
final responseLevel = (_level?.toInt() ?? 0).clamp(0, 99).toInt();
|
||||
if (responseLevel > 0) {
|
||||
return normalizedLevel == responseLevel
|
||||
? _rocketKings ?? const <SCRoomRocketKingUserRes>[]
|
||||
: const <SCRoomRocketKingUserRes>[];
|
||||
}
|
||||
final current = (_currentLevel ?? 0).clamp(0, 99).toInt();
|
||||
if (current <= 0 || normalizedLevel == current) {
|
||||
return _rocketKings ?? const <SCRoomRocketKingUserRes>[];
|
||||
}
|
||||
return const <SCRoomRocketKingUserRes>[];
|
||||
}
|
||||
|
||||
SCRoomRocketLevelRes? _levelFor(int level) {
|
||||
final list = _levels ?? const <SCRoomRocketLevelRes>[];
|
||||
for (final item in list) {
|
||||
if (item.level == level) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['roomId'] = _roomId;
|
||||
@ -268,6 +360,19 @@ class SCRoomRocketStatusRes {
|
||||
map['needEnergy'] = _needEnergy;
|
||||
map['displayPercent'] = _displayPercent;
|
||||
map['shake'] = _shake;
|
||||
map['rewardPreviewByLevel'] = _rewardPreviewByLevel?.map(
|
||||
(level, preview) => MapEntry(level.toString(), {
|
||||
'top1': preview.top1.map((item) => item.toJson()).toList(),
|
||||
'ignite': preview.ignite.map((item) => item.toJson()).toList(),
|
||||
'inRoom': preview.inRoom.map((item) => item.toJson()).toList(),
|
||||
}),
|
||||
);
|
||||
map['rocketKingsByLevel'] = _rocketKingsByLevel?.map(
|
||||
(level, users) => MapEntry(
|
||||
level.toString(),
|
||||
users.map((item) => item.toJson()).toList(),
|
||||
),
|
||||
);
|
||||
map['timezone'] = _timezone;
|
||||
map['resetRemainingSeconds'] = _resetRemainingSeconds;
|
||||
map['resetTime'] = _resetTime;
|
||||
@ -335,9 +440,290 @@ class SCRoomRocketStatusRes {
|
||||
}
|
||||
|
||||
static List<SCRoomRocketKingUserRes>? _kingList(dynamic value) {
|
||||
if (value is! Iterable) {
|
||||
final list = SCRoomRocketKingUserRes.listFromJson(value);
|
||||
return list.isEmpty ? null : list;
|
||||
}
|
||||
|
||||
static SCRoomRocketRewardPreviewRes? _rewardPreviewForCurrentLevel(
|
||||
Map<int, SCRoomRocketRewardPreviewRes>? values,
|
||||
int? currentLevel,
|
||||
) {
|
||||
if (values == null || values.isEmpty || currentLevel == null) {
|
||||
return null;
|
||||
}
|
||||
return value.map(SCRoomRocketKingUserRes.fromJson).toList(growable: false);
|
||||
return values[currentLevel];
|
||||
}
|
||||
|
||||
static List<SCRoomRocketKingUserRes>? _rocketKingsForCurrentLevel(
|
||||
Map<int, List<SCRoomRocketKingUserRes>>? values,
|
||||
int? currentLevel,
|
||||
) {
|
||||
if (values == null || values.isEmpty || currentLevel == null) {
|
||||
return null;
|
||||
}
|
||||
return values[currentLevel];
|
||||
}
|
||||
|
||||
static Map<int, SCRoomRocketRewardPreviewRes>? _rewardPreviewMap(
|
||||
dynamic json,
|
||||
List<SCRoomRocketLevelRes>? levels,
|
||||
) {
|
||||
final result = <int, SCRoomRocketRewardPreviewRes>{};
|
||||
for (final level in levels ?? const <SCRoomRocketLevelRes>[]) {
|
||||
final preview = level.rewardPreview;
|
||||
if (preview != null) {
|
||||
result[level.level] = preview;
|
||||
}
|
||||
}
|
||||
final data = _asMap(json);
|
||||
for (final candidate in [
|
||||
data['rewardPreviewByLevel'],
|
||||
data['rewardPreviewsByLevel'],
|
||||
data['levelRewardPreview'],
|
||||
data['levelRewardPreviews'],
|
||||
data['rewardsByLevel'],
|
||||
data['levelRewards'],
|
||||
data['rocketRewards'],
|
||||
data['rewardPreview'],
|
||||
]) {
|
||||
_collectRewardPreviewMap(candidate, result);
|
||||
}
|
||||
return result.isEmpty ? null : result;
|
||||
}
|
||||
|
||||
static void _collectRewardPreviewMap(
|
||||
dynamic value,
|
||||
Map<int, SCRoomRocketRewardPreviewRes> result,
|
||||
) {
|
||||
if (!_hasPayload(value)) {
|
||||
return;
|
||||
}
|
||||
if (value is Iterable && value is! String) {
|
||||
for (final item in value) {
|
||||
final data = _asMap(item);
|
||||
final level = _levelFromValue(
|
||||
_firstPayload([
|
||||
data['level'],
|
||||
data['rocketLevel'],
|
||||
data['levelNo'],
|
||||
data['currentLevel'],
|
||||
]),
|
||||
);
|
||||
final preview = SCRoomRocketRewardPreviewRes.tryFromJson(
|
||||
_firstPayload([
|
||||
data['rewardPreview'],
|
||||
data['rewardConfig'],
|
||||
data['rewards'],
|
||||
data['rewardList'],
|
||||
data,
|
||||
]),
|
||||
);
|
||||
if (level != null && preview != null) {
|
||||
result[level] = preview;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
final data = _asMap(value);
|
||||
final singleLevel = _levelFromValue(
|
||||
_firstPayload([
|
||||
data['level'],
|
||||
data['rocketLevel'],
|
||||
data['levelNo'],
|
||||
data['currentLevel'],
|
||||
]),
|
||||
);
|
||||
if (singleLevel != null) {
|
||||
final preview = SCRoomRocketRewardPreviewRes.tryFromJson(
|
||||
_firstPayload([
|
||||
data['rewardPreview'],
|
||||
data['rewardConfig'],
|
||||
data['rewards'],
|
||||
data['rewardList'],
|
||||
data,
|
||||
]),
|
||||
);
|
||||
if (preview != null) {
|
||||
result[singleLevel] = preview;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (final entry in data.entries) {
|
||||
final level = _levelFromMapKey(entry.key);
|
||||
final preview = SCRoomRocketRewardPreviewRes.tryFromJson(entry.value);
|
||||
if (level != null && preview != null) {
|
||||
result[level] = preview;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Map<int, List<SCRoomRocketKingUserRes>>? _rocketKingsMap(
|
||||
dynamic json,
|
||||
List<SCRoomRocketLevelRes>? levels,
|
||||
) {
|
||||
final result = <int, List<SCRoomRocketKingUserRes>>{};
|
||||
for (final level in levels ?? const <SCRoomRocketLevelRes>[]) {
|
||||
if (level.rocketKings.isNotEmpty) {
|
||||
result[level.level] = level.rocketKings;
|
||||
}
|
||||
}
|
||||
final data = _asMap(json);
|
||||
for (final candidate in [
|
||||
data['rocketKingsByLevel'],
|
||||
data['rocketKingByLevel'],
|
||||
data['levelRocketKings'],
|
||||
data['levelKings'],
|
||||
data['kingsByLevel'],
|
||||
data['podiumByLevel'],
|
||||
data['rocketKings'],
|
||||
]) {
|
||||
_collectRocketKingsMap(candidate, result);
|
||||
}
|
||||
return result.isEmpty ? null : result;
|
||||
}
|
||||
|
||||
static void _collectRocketKingsMap(
|
||||
dynamic value,
|
||||
Map<int, List<SCRoomRocketKingUserRes>> result,
|
||||
) {
|
||||
if (!_hasPayload(value)) {
|
||||
return;
|
||||
}
|
||||
if (value is Iterable && value is! String) {
|
||||
for (final item in value) {
|
||||
final data = _asMap(item);
|
||||
final level = _levelFromValue(
|
||||
_firstPayload([
|
||||
data['level'],
|
||||
data['rocketLevel'],
|
||||
data['levelNo'],
|
||||
data['currentLevel'],
|
||||
]),
|
||||
);
|
||||
final kings = SCRoomRocketKingUserRes.listFromJson(
|
||||
_firstPayload([
|
||||
data['rocketKings'],
|
||||
data['kings'],
|
||||
data['podium'],
|
||||
data['records'],
|
||||
data['items'],
|
||||
data['list'],
|
||||
]),
|
||||
);
|
||||
if (level != null && kings.isNotEmpty) {
|
||||
result[level] = kings;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
final data = _asMap(value);
|
||||
final singleLevel = _levelFromValue(
|
||||
_firstPayload([
|
||||
data['level'],
|
||||
data['rocketLevel'],
|
||||
data['levelNo'],
|
||||
data['currentLevel'],
|
||||
]),
|
||||
);
|
||||
if (singleLevel != null) {
|
||||
final kings = SCRoomRocketKingUserRes.listFromJson(
|
||||
_firstPayload([
|
||||
data['rocketKings'],
|
||||
data['kings'],
|
||||
data['podium'],
|
||||
data['records'],
|
||||
data['items'],
|
||||
data['list'],
|
||||
]),
|
||||
);
|
||||
if (kings.isNotEmpty) {
|
||||
result[singleLevel] = kings;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (final entry in data.entries) {
|
||||
final level = _levelFromMapKey(entry.key);
|
||||
final kings = SCRoomRocketKingUserRes.listFromJson(entry.value);
|
||||
if (level != null && kings.isNotEmpty) {
|
||||
result[level] = kings;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Map<String, dynamic> _asMap(dynamic value) {
|
||||
if (value is Map<String, dynamic>) {
|
||||
return value;
|
||||
}
|
||||
if (value is Map) {
|
||||
return value.map((key, item) => MapEntry(key.toString(), item));
|
||||
}
|
||||
return const <String, dynamic>{};
|
||||
}
|
||||
|
||||
static dynamic _firstPayload(Iterable<dynamic> values) {
|
||||
for (final value in values) {
|
||||
if (_hasPayload(value)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool _hasPayload(dynamic value) {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
if (value is String) {
|
||||
final text = value.trim();
|
||||
return text.isNotEmpty &&
|
||||
text != '{}' &&
|
||||
text != '[]' &&
|
||||
text.toLowerCase() != 'null';
|
||||
}
|
||||
if (value is Map) {
|
||||
return value.isNotEmpty;
|
||||
}
|
||||
if (value is Iterable) {
|
||||
return value.isNotEmpty;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int? _levelFromValue(dynamic value) {
|
||||
if (value is int) {
|
||||
return value;
|
||||
}
|
||||
if (value is num) {
|
||||
return value.toInt();
|
||||
}
|
||||
final text = value?.toString().trim() ?? '';
|
||||
final exact = int.tryParse(text);
|
||||
if (exact != null) {
|
||||
return exact;
|
||||
}
|
||||
final match = RegExp(r'\d+').firstMatch(text);
|
||||
if (match == null) {
|
||||
return null;
|
||||
}
|
||||
return int.tryParse(match.group(0) ?? '');
|
||||
}
|
||||
|
||||
static int? _levelFromMapKey(dynamic value) {
|
||||
if (value is int || value is num) {
|
||||
return _levelFromValue(value);
|
||||
}
|
||||
final text = value?.toString().trim() ?? '';
|
||||
final exact = int.tryParse(text);
|
||||
if (exact != null) {
|
||||
return exact;
|
||||
}
|
||||
final match = RegExp(
|
||||
r'^(?:lv|level|rocketLevel|levelNo)[_\-\s.]*([0-9]+)$',
|
||||
caseSensitive: false,
|
||||
).firstMatch(text);
|
||||
if (match == null) {
|
||||
return null;
|
||||
}
|
||||
return int.tryParse(match.group(1) ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
/// newServiceToken : ""
|
||||
/// rtcToken : ""
|
||||
|
||||
class SCRtcTokenRes {
|
||||
SCRtcTokenRes({
|
||||
String? newServiceToken,
|
||||
String? rtcToken,}){
|
||||
_newServiceToken = newServiceToken;
|
||||
_rtcToken = rtcToken;
|
||||
}
|
||||
|
||||
SCRtcTokenRes.fromJson(dynamic json) {
|
||||
_newServiceToken = json['newServiceToken'];
|
||||
_rtcToken = json['rtcToken'];
|
||||
}
|
||||
String? _newServiceToken;
|
||||
String? _rtcToken;
|
||||
SCRtcTokenRes copyWith({ String? newServiceToken,
|
||||
String? rtcToken,
|
||||
}) => SCRtcTokenRes( newServiceToken: newServiceToken ?? _newServiceToken,
|
||||
rtcToken: rtcToken ?? _rtcToken,
|
||||
);
|
||||
String? get newServiceToken => _newServiceToken;
|
||||
String? get rtcToken => _rtcToken;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['newServiceToken'] = _newServiceToken;
|
||||
map['rtcToken'] = _rtcToken;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@ -59,14 +59,29 @@ class SCUserBadgeRes {
|
||||
}
|
||||
return value
|
||||
.whereType<Map>()
|
||||
.where(_isDisplayBadgePayload)
|
||||
.map((item) => WearBadge.fromJson(item))
|
||||
.where((badge) => _badgeUrl(badge).isNotEmpty)
|
||||
.toList();
|
||||
}
|
||||
|
||||
static String _badgeUrl(WearBadge badge) {
|
||||
return (badge.selectUrl ?? badge.notSelectUrl ?? badge.animationUrl ?? "")
|
||||
.trim();
|
||||
return (badge.selectUrl ?? "").trim();
|
||||
}
|
||||
|
||||
static bool _isDisplayBadgePayload(Map item) {
|
||||
final type = _stringValue(item['type'])?.toUpperCase();
|
||||
if (type != null && type != "LONG" && type != "SHORT") {
|
||||
return false;
|
||||
}
|
||||
|
||||
final sourceType = _stringValue(item['sourceType'])?.toUpperCase();
|
||||
if (sourceType == null) {
|
||||
return true;
|
||||
}
|
||||
return sourceType == "VIP" ||
|
||||
sourceType == "ACTIVITY" ||
|
||||
sourceType == "ACHIEVEMENT";
|
||||
}
|
||||
|
||||
static String? _stringValue(dynamic value) {
|
||||
|
||||
@ -202,7 +202,7 @@ abstract class SocialChatRoomRepository {
|
||||
});
|
||||
|
||||
///查询房间火箭状态
|
||||
Future<SCRoomRocketStatusRes> rocketStatus(String roomId);
|
||||
Future<SCRoomRocketStatusRes> rocketStatus(String roomId, {int? level});
|
||||
|
||||
///查询火箭王榜单
|
||||
Future<SCRoomRocketKingRes> roomRocketKing(
|
||||
|
||||
@ -18,7 +18,6 @@ import 'package:yumi/shared/business_logic/models/res/room_black_list_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/room_res.dart'
|
||||
hide WearBadge;
|
||||
import 'package:yumi/shared/business_logic/models/req/sc_user_profile_cmd.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_rtc_token_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_sign_in_reward_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_sign_in_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_task_list_res.dart';
|
||||
@ -78,13 +77,6 @@ abstract class SocialChatUserRepository {
|
||||
///上麦心跳
|
||||
Future anchorHeartbeat(String roomId);
|
||||
|
||||
///声网token
|
||||
Future<SCRtcTokenRes> getRtcToken(
|
||||
String channel,
|
||||
String userId, {
|
||||
bool isPublisher = false,
|
||||
});
|
||||
|
||||
///退出房间
|
||||
Future<bool> quitRoom(String roomId);
|
||||
|
||||
|
||||
@ -161,8 +161,6 @@ const Map<String, String> _localRouteOverrides = {
|
||||
"/room/live-voice/entry",
|
||||
"58f9485b5b9f401c6a14bc1e6534122c7a085bb5f6a613094188ca56fe8bc03d":
|
||||
"/live/user/heartbeat",
|
||||
"2572c69d25a0946b0cb35b24bdfcd61c9bcef4afdaced7dfc47a641bd9584f96":
|
||||
"/external/im-trtc/tmp/agora/sig",
|
||||
"3e451c31ab651a577a3b7796b32041e7": "/live/room/quit",
|
||||
"2fa1d4f56bb726558904ce2a50b83f961e1cf9b945681bdfb3d4cf7a5ecfc943":
|
||||
"/user/user-profile",
|
||||
@ -357,7 +355,7 @@ class ApiInterceptor extends Interceptor {
|
||||
// MaterialPageRoute(builder: (_) {
|
||||
// Provider.of<RtmProvider>(_, listen: false).destoryRtm();
|
||||
// Provider.of<ProfileManager>(_, listen: false).logout();
|
||||
// Provider.of<RtcProvider>(_, listen: false).engine?.leaveChannel();
|
||||
// Provider.of<RtcProvider>(_, listen: false).releaseRtcEngineForAppTermination();
|
||||
// return const LoginPage();
|
||||
// }),
|
||||
// (route) => false // 清除所有路由
|
||||
|
||||
@ -1001,9 +1001,15 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
||||
|
||||
///go/app/voice-room/rocket/status
|
||||
@override
|
||||
Future<SCRoomRocketStatusRes> rocketStatus(String roomId) async {
|
||||
Future<SCRoomRocketStatusRes> rocketStatus(
|
||||
String roomId, {
|
||||
int? level,
|
||||
}) async {
|
||||
Map<String, dynamic> params = {};
|
||||
params["roomId"] = roomId;
|
||||
if (level != null && level > 0) {
|
||||
params["level"] = level;
|
||||
}
|
||||
const path = "/go/app/voice-room/rocket/status";
|
||||
_rocketApiDebug('request GET $path', query: params);
|
||||
final result = await http.get<SCRoomRocketStatusRes>(
|
||||
|
||||
@ -9,7 +9,6 @@ import 'package:yumi/shared/business_logic/models/res/sc_prop_coupon_record_list
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_public_message_page_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/room_res.dart'
|
||||
hide WearBadge;
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_rtc_token_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_task_list_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_user_counter_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_user_badge_res.dart';
|
||||
@ -228,25 +227,6 @@ class SCAccountRepository implements SocialChatUserRepository {
|
||||
return result;
|
||||
}
|
||||
|
||||
///external/im-trtc/tmp/agora/sig
|
||||
@override
|
||||
Future<SCRtcTokenRes> getRtcToken(
|
||||
String channel,
|
||||
String userId, {
|
||||
bool isPublisher = false,
|
||||
}) async {
|
||||
final result = await http.get(
|
||||
"2572c69d25a0946b0cb35b24bdfcd61c9bcef4afdaced7dfc47a641bd9584f96",
|
||||
queryParams: {
|
||||
"channelName": channel,
|
||||
"userId": userId,
|
||||
"isPublisher": isPublisher,
|
||||
},
|
||||
fromJson: (json) => SCRtcTokenRes.fromJson(json),
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
///live/room/quit
|
||||
@override
|
||||
Future<bool> quitRoom(String roomId) async {
|
||||
|
||||
@ -528,7 +528,7 @@ class _UpdateNotice extends StatelessWidget {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
child: const Text(
|
||||
'This Agora-based version has expired. Please update to the latest '
|
||||
'This RTC version has expired. Please update to the latest '
|
||||
'version to continue using voice rooms normally.',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
|
||||
@ -163,8 +163,7 @@ class _SCUserBadgeStripState extends State<SCUserBadgeStrip> {
|
||||
}
|
||||
|
||||
String _badgeUrl(WearBadge badge) {
|
||||
return (badge.selectUrl ?? badge.notSelectUrl ?? badge.animationUrl ?? "")
|
||||
.trim();
|
||||
return (badge.selectUrl ?? "").trim();
|
||||
}
|
||||
|
||||
List<Widget> _withSpacing(List<Widget> children, double spacing) {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_room_rocket_api_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_room_rocket_config_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_room_rocket_status_res.dart';
|
||||
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_dialog.dart';
|
||||
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_note_dialog.dart';
|
||||
@ -42,9 +43,12 @@ class RoomRocketApiMapper {
|
||||
}
|
||||
|
||||
static List<RoomRocketRewardItem>? rewardsFromStatus(
|
||||
SCRoomRocketStatusRes? status,
|
||||
) {
|
||||
final rewards = status?.rewardPreview?.all ?? const [];
|
||||
SCRoomRocketStatusRes? status, {
|
||||
int? level,
|
||||
}) {
|
||||
final rewards =
|
||||
status?.rewardPreviewForLevel(level ?? status.currentLevel ?? 1)?.all ??
|
||||
const [];
|
||||
if (rewards.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
@ -52,9 +56,18 @@ class RoomRocketApiMapper {
|
||||
}
|
||||
|
||||
static RoomRocketRewardGroups? rewardGroupsFromStatus(
|
||||
SCRoomRocketStatusRes? status,
|
||||
SCRoomRocketStatusRes? status, {
|
||||
int? level,
|
||||
}) {
|
||||
final preview = status?.rewardPreviewForLevel(
|
||||
level ?? status.currentLevel ?? 1,
|
||||
);
|
||||
return rewardGroupsFromPreview(preview);
|
||||
}
|
||||
|
||||
static RoomRocketRewardGroups? rewardGroupsFromPreview(
|
||||
SCRoomRocketRewardPreviewRes? preview,
|
||||
) {
|
||||
final preview = status?.rewardPreview;
|
||||
if (preview == null) {
|
||||
return null;
|
||||
}
|
||||
@ -65,6 +78,44 @@ class RoomRocketApiMapper {
|
||||
);
|
||||
}
|
||||
|
||||
static Map<int, RoomRocketRewardGroups> rewardGroupsByLevelFromConfigs(
|
||||
List<SCRoomRocketConfigRes> configs,
|
||||
) {
|
||||
final result = <int, RoomRocketRewardGroups>{};
|
||||
for (final config in configs) {
|
||||
final level = config.level?.toInt();
|
||||
if (level == null || level <= 0) {
|
||||
continue;
|
||||
}
|
||||
final groups = rewardGroupsFromConfig(config);
|
||||
if (groups != null) {
|
||||
result[level] = groups;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static RoomRocketRewardGroups? rewardGroupsFromConfig(
|
||||
SCRoomRocketConfigRes config,
|
||||
) {
|
||||
final explicitTop1 = _rewardItemsFromConfig(config.rewardConfig);
|
||||
final explicitIgnite = _rewardItemsFromConfig(config.igniteRewardConfig);
|
||||
final explicitInRoom = _rewardItemsFromConfig(config.inRoomRewardConfig);
|
||||
final normal = _rewardItemsFromConfig(config.normalRewardConfig);
|
||||
final top1 = <RoomRocketRewardItem>[...explicitTop1];
|
||||
final setOff = <RoomRocketRewardItem>[...explicitIgnite];
|
||||
final inRoom = <RoomRocketRewardItem>[...explicitInRoom, ...normal];
|
||||
final groups = RoomRocketRewardGroups(
|
||||
top1: top1,
|
||||
setOff: setOff,
|
||||
inRoom: inRoom,
|
||||
);
|
||||
if (groups.top1.isEmpty && groups.setOff.isEmpty && groups.inRoom.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
static RoomRocketRewardItem rewardItemFromPreview(
|
||||
SCRoomRocketRewardRes reward,
|
||||
) {
|
||||
@ -76,14 +127,89 @@ class RoomRocketApiMapper {
|
||||
);
|
||||
}
|
||||
|
||||
static List<RoomRocketCrewMember>? crewFromStatus(
|
||||
SCRoomRocketStatusRes? status,
|
||||
static List<RoomRocketRewardItem> _rewardItemsFromConfig(
|
||||
List<RewardConfig>? configs,
|
||||
) {
|
||||
final source = configs ?? const <RewardConfig>[];
|
||||
if (source.isEmpty) {
|
||||
return const <RoomRocketRewardItem>[];
|
||||
}
|
||||
final sorted = [...source]..sort((a, b) {
|
||||
final aSort = a.sort ?? 0;
|
||||
final bSort = b.sort ?? 0;
|
||||
return aSort.compareTo(bSort);
|
||||
});
|
||||
return sorted.map(_rewardItemFromConfig).toList(growable: false);
|
||||
}
|
||||
|
||||
static RoomRocketRewardItem _rewardItemFromConfig(RewardConfig config) {
|
||||
final rawType = (config.type ?? '').trim();
|
||||
final detailType = (config.detailType ?? '').trim();
|
||||
final type =
|
||||
detailType.toUpperCase() == 'GOLD'
|
||||
? detailType
|
||||
: (rawType.isEmpty ? detailType : rawType);
|
||||
final amount = _rewardAmountText(config);
|
||||
final cover = (config.cover ?? config.sourceUrl ?? '').trim();
|
||||
return RoomRocketRewardItem(
|
||||
imageUrl: cover.isEmpty ? null : cover,
|
||||
amount: amount,
|
||||
rewardType: type.isEmpty ? 'PROPS' : type,
|
||||
expireDays: (config.quantity ?? 0).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
static String _rewardAmountText(RewardConfig config) {
|
||||
final type = (config.type ?? '').trim().toUpperCase();
|
||||
final detailType = (config.detailType ?? '').trim().toUpperCase();
|
||||
if (type == 'GOLD' || detailType == 'GOLD') {
|
||||
final amount = config.amount ?? num.tryParse(config.content ?? '');
|
||||
if (amount == null || amount <= 0) {
|
||||
return '0';
|
||||
}
|
||||
return amount % 1 == 0 ? amount.toInt().toString() : amount.toString();
|
||||
}
|
||||
final quantity = config.quantity ?? 0;
|
||||
if (quantity > 0) {
|
||||
return quantity.toInt().toString();
|
||||
}
|
||||
final name =
|
||||
(config.name ??
|
||||
config.badgeName ??
|
||||
config.remark ??
|
||||
config.content ??
|
||||
'')
|
||||
.trim();
|
||||
return name.isEmpty ? '1' : name;
|
||||
}
|
||||
|
||||
static List<RoomRocketCrewMember>? crewFromStatus(
|
||||
SCRoomRocketStatusRes? status, {
|
||||
int? level,
|
||||
}) {
|
||||
final kings = status?.rocketKingsForLevel(
|
||||
level ?? status.currentLevel ?? 1,
|
||||
);
|
||||
return crewFromKingUsers(kings ?? const <SCRoomRocketKingUserRes>[]);
|
||||
}
|
||||
|
||||
static List<RoomRocketCrewMember> crewFromKing(SCRoomRocketKingRes king) {
|
||||
final source = king.podium.isNotEmpty ? king.podium : king.displayRecords;
|
||||
return crewFromKingUsers(source);
|
||||
}
|
||||
|
||||
static List<RoomRocketCrewMember> crewFromKingUsers(
|
||||
List<SCRoomRocketKingUserRes> kings,
|
||||
) {
|
||||
final kings = status?.rocketKings ?? const [];
|
||||
if (kings.isEmpty) {
|
||||
return const <RoomRocketCrewMember>[];
|
||||
}
|
||||
return kings
|
||||
final sorted = [...kings]..sort((a, b) {
|
||||
final aRank = a.rank <= 0 ? 999 : a.rank;
|
||||
final bRank = b.rank <= 0 ? 999 : b.rank;
|
||||
return aRank.compareTo(bRank);
|
||||
});
|
||||
return sorted
|
||||
.take(3)
|
||||
.map(
|
||||
(item) => RoomRocketCrewMember(
|
||||
|
||||
@ -172,15 +172,13 @@ class RoomRocketDialog extends StatelessWidget {
|
||||
) {
|
||||
final hasNoContributors =
|
||||
status != null && (status.totalContributors ?? 0) <= 0;
|
||||
if (hasNoContributors) {
|
||||
return const <RoomRocketCrewMember>[];
|
||||
}
|
||||
final source =
|
||||
crew?.where((item) => item.asset != null || item.imageUrl != null) ??
|
||||
const <RoomRocketCrewMember>[];
|
||||
final source = crew ?? const <RoomRocketCrewMember>[];
|
||||
if (source.isNotEmpty) {
|
||||
return source.take(3).toList();
|
||||
}
|
||||
if (hasNoContributors) {
|
||||
return const <RoomRocketCrewMember>[];
|
||||
}
|
||||
return const <RoomRocketCrewMember>[];
|
||||
}
|
||||
|
||||
@ -376,6 +374,7 @@ class _RocketCanvas extends StatelessWidget {
|
||||
_buildResetTime(),
|
||||
_buildRewardPanel(),
|
||||
_buildBottomStrip(),
|
||||
_buildCloseButton(),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -563,6 +562,40 @@ class _RocketCanvas extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCloseButton() {
|
||||
return Positioned(
|
||||
left: s(337),
|
||||
top: s(94),
|
||||
width: s(30),
|
||||
height: s(30),
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: onClose,
|
||||
child: Center(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: const Color(0xFF75F3CB).withValues(alpha: 0.82),
|
||||
width: s(1),
|
||||
),
|
||||
color: const Color(0xFF002F26).withValues(alpha: 0.35),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: s(20),
|
||||
height: s(20),
|
||||
child: Icon(
|
||||
Icons.close_rounded,
|
||||
size: s(17),
|
||||
color: const Color(0xFF75F3CB),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildResetTime() {
|
||||
return Stack(
|
||||
children: [
|
||||
@ -916,11 +949,18 @@ class _CrewAvatar extends StatelessWidget {
|
||||
child: SizedBox(
|
||||
width: 42 * scale,
|
||||
height: 42 * scale,
|
||||
child: _RocketImage(
|
||||
asset: member.asset,
|
||||
imageUrl: member.imageUrl,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
child:
|
||||
member.asset != null || member.imageUrl != null
|
||||
? _RocketImage(
|
||||
asset: member.asset,
|
||||
imageUrl: member.imageUrl,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: Image.asset(
|
||||
_RocketAssets.defaultAvatar,
|
||||
fit: BoxFit.cover,
|
||||
gaplessPlayback: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -957,9 +997,15 @@ class _RocketImage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
if (RoomRocketPagEffectOverlay.isPag(source)) {
|
||||
RoomRocketPagEffectOverlay.debugPagResource(
|
||||
source,
|
||||
place: debugName ?? 'dialog-image-network',
|
||||
);
|
||||
return RoomRocketPagPreview(
|
||||
resource: source,
|
||||
loop: true,
|
||||
fit: fit,
|
||||
underlayBuilder: (_) => _imageFallback(),
|
||||
defaultBuilder: (_) => const SCRotatingDotsLoading(),
|
||||
);
|
||||
}
|
||||
@ -1000,9 +1046,15 @@ class _RocketImage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
if (RoomRocketPagEffectOverlay.isPag(source)) {
|
||||
RoomRocketPagEffectOverlay.debugPagResource(
|
||||
source,
|
||||
place: debugName ?? 'dialog-image-asset',
|
||||
);
|
||||
return RoomRocketPagPreview(
|
||||
resource: source,
|
||||
loop: true,
|
||||
fit: fit,
|
||||
underlayBuilder: (_) => _imageFallback(),
|
||||
defaultBuilder: (_) => _imageFallback(),
|
||||
);
|
||||
}
|
||||
@ -1061,7 +1113,11 @@ class _RocketImage extends StatelessWidget {
|
||||
}
|
||||
|
||||
Widget _imageFallback() {
|
||||
if (asset != null && asset!.trim().isNotEmpty && !_isSvg(asset!)) {
|
||||
if (asset != null &&
|
||||
asset!.trim().isNotEmpty &&
|
||||
!_isSvg(asset!) &&
|
||||
!RoomRocketPagEffectOverlay.isPag(asset!) &&
|
||||
!SCNetworkSvgaWidget.isSvga(asset!)) {
|
||||
return Image.asset(asset!, fit: fit, gaplessPlayback: true);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
@ -1691,6 +1747,8 @@ class _RocketAssets {
|
||||
static const String rewardBottom =
|
||||
'sc_images/room/rocket/reward_dialog_bottom.png';
|
||||
static const String rewardThumb = 'sc_images/room/rocket/reward_thumb.png';
|
||||
static const String defaultAvatar =
|
||||
'sc_images/general/sc_icon_avar_defalt.png';
|
||||
static const String coin = 'sc_images/register_reward/yumi_coin.png';
|
||||
}
|
||||
|
||||
|
||||
@ -44,6 +44,7 @@ class RoomRocketFloatingEntry extends StatelessWidget {
|
||||
child: _RoomRocketEntryButton(
|
||||
iconUrl: level?.rocketIconUrl ?? '',
|
||||
progressUrl: level?.progressBarUrl ?? '',
|
||||
progressPercent: percent,
|
||||
shake: shouldShake,
|
||||
loading: status == null,
|
||||
),
|
||||
@ -111,12 +112,14 @@ class _RoomRocketEntryButton extends StatefulWidget {
|
||||
const _RoomRocketEntryButton({
|
||||
required this.iconUrl,
|
||||
required this.progressUrl,
|
||||
required this.progressPercent,
|
||||
required this.shake,
|
||||
required this.loading,
|
||||
});
|
||||
|
||||
final String iconUrl;
|
||||
final String progressUrl;
|
||||
final double progressPercent;
|
||||
final bool shake;
|
||||
final bool loading;
|
||||
|
||||
@ -197,7 +200,7 @@ class _RoomRocketEntryButtonState extends State<_RoomRocketEntryButton>
|
||||
height: 8.w,
|
||||
child:
|
||||
widget.progressUrl.trim().isEmpty
|
||||
? const SizedBox.shrink()
|
||||
? _RocketEntryProgressBar(percent: widget.progressPercent)
|
||||
: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(5.w),
|
||||
child: _RocketEntryImage(
|
||||
@ -214,6 +217,44 @@ class _RoomRocketEntryButtonState extends State<_RoomRocketEntryButton>
|
||||
}
|
||||
}
|
||||
|
||||
class _RocketEntryProgressBar extends StatelessWidget {
|
||||
const _RocketEntryProgressBar({required this.percent});
|
||||
|
||||
final double percent;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final value = (percent / 100).clamp(0.0, 1.0).toDouble();
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5.w),
|
||||
color: const Color(0xFF082D27).withValues(alpha: 0.86),
|
||||
border: Border.all(
|
||||
color: const Color(0xFF75F3CB).withValues(alpha: 0.52),
|
||||
width: 0.8.w,
|
||||
),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(5.w),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: FractionallySizedBox(
|
||||
widthFactor: value,
|
||||
heightFactor: 1,
|
||||
child: const DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [Color(0xFFB8FFE6), Color(0xFF1BD091)],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RocketEntryImage extends StatelessWidget {
|
||||
const _RocketEntryImage({
|
||||
required this.source,
|
||||
@ -232,9 +273,14 @@ class _RocketEntryImage extends StatelessWidget {
|
||||
return _placeholder();
|
||||
}
|
||||
if (RoomRocketPagEffectOverlay.isPag(value)) {
|
||||
RoomRocketPagEffectOverlay.debugPagResource(
|
||||
value,
|
||||
place: 'floating-entry',
|
||||
);
|
||||
return RoomRocketPagPreview(
|
||||
resource: value,
|
||||
loop: true,
|
||||
fit: fit,
|
||||
defaultBuilder: (_) => _placeholder(),
|
||||
);
|
||||
}
|
||||
@ -305,6 +351,11 @@ class _RoomRocketDialogLoader extends StatefulWidget {
|
||||
class _RoomRocketDialogLoaderState extends State<_RoomRocketDialogLoader> {
|
||||
SCRoomRocketStatusRes? _status;
|
||||
List<RoomRocketNoteSection>? _noteSections;
|
||||
Map<int, RoomRocketRewardGroups> _configRewardGroupsByLevel = {};
|
||||
final Map<int, RoomRocketRewardGroups> _statusRewardGroupsByLevel = {};
|
||||
final Set<int> _loadingRewardLevels = {};
|
||||
final Map<int, List<RoomRocketCrewMember>> _crewByLevel = {};
|
||||
final Set<int> _loadingCrewLevels = {};
|
||||
int _selectedLevel = 0;
|
||||
RoomRocketRewardTab _selectedRewardTab = RoomRocketRewardTab.top1;
|
||||
bool _levelTouched = false;
|
||||
@ -314,7 +365,10 @@ class _RoomRocketDialogLoaderState extends State<_RoomRocketDialogLoader> {
|
||||
super.initState();
|
||||
_status = widget.initialStatus;
|
||||
_selectedLevel = _levelIndexFromStatus(widget.initialStatus);
|
||||
_cacheRewardGroupsFromStatus(widget.initialStatus, _selectedLevel + 1);
|
||||
unawaited(_load());
|
||||
unawaited(_loadRewardForLevel(_selectedLevel + 1));
|
||||
unawaited(_loadConfigs());
|
||||
unawaited(_loadRule());
|
||||
}
|
||||
|
||||
@ -335,7 +389,10 @@ class _RoomRocketDialogLoaderState extends State<_RoomRocketDialogLoader> {
|
||||
if (!_levelTouched) {
|
||||
_selectedLevel = _levelIndexFromStatus(res);
|
||||
}
|
||||
_cacheRewardGroupsFromStatus(res, _selectedLevel + 1);
|
||||
});
|
||||
unawaited(_loadCrewForLevel(_selectedLevel + 1));
|
||||
unawaited(_loadRewardForLevel(_selectedLevel + 1));
|
||||
try {
|
||||
Provider.of<RtcProvider>(
|
||||
context,
|
||||
@ -362,20 +419,65 @@ class _RoomRocketDialogLoaderState extends State<_RoomRocketDialogLoader> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadConfigs() async {
|
||||
try {
|
||||
_rocketUiDebug('request enabled rocket configs');
|
||||
final configs = await SCChatRoomRepository().rocketConfigEnabled();
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final groups = RoomRocketApiMapper.rewardGroupsByLevelFromConfigs(
|
||||
configs,
|
||||
);
|
||||
setState(() {
|
||||
_configRewardGroupsByLevel = groups;
|
||||
});
|
||||
_rocketUiDebug(
|
||||
'rocket configs loaded rewardLevels=${groups.keys.toList()}',
|
||||
);
|
||||
} catch (error) {
|
||||
_rocketUiDebug('rocket configs request failed: $error');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final status = _status;
|
||||
final selectedLevel = _selectedLevel.clamp(0, 5).toInt();
|
||||
final selectedLevelNumber = selectedLevel + 1;
|
||||
final currentLevelNumber = _levelNumberFromStatus(status);
|
||||
final statusRewardGroups = RoomRocketApiMapper.rewardGroupsFromStatus(
|
||||
status,
|
||||
level: selectedLevelNumber,
|
||||
);
|
||||
final liveRewardGroups = _statusRewardGroupsByLevel[selectedLevelNumber];
|
||||
final configRewardGroups = _configRewardGroupsByLevel[selectedLevelNumber];
|
||||
final rewardGroupsFromStatus = liveRewardGroups ?? statusRewardGroups;
|
||||
final rewardGroups =
|
||||
selectedLevelNumber == currentLevelNumber
|
||||
? _mergeRewardGroups(rewardGroupsFromStatus, configRewardGroups)
|
||||
: rewardGroupsFromStatus ?? const RoomRocketRewardGroups();
|
||||
return RoomRocketDialog(
|
||||
status: status,
|
||||
levels: RoomRocketApiMapper.levelsFromStatus(status),
|
||||
selectedLevel: selectedLevel,
|
||||
onLevelSelected: _onLevelSelected,
|
||||
rewards: RoomRocketApiMapper.rewardsFromStatus(status),
|
||||
rewardGroups: RoomRocketApiMapper.rewardGroupsFromStatus(status),
|
||||
rewards:
|
||||
rewardGroups == null
|
||||
? RoomRocketApiMapper.rewardsFromStatus(
|
||||
status,
|
||||
level: selectedLevelNumber,
|
||||
)
|
||||
: null,
|
||||
rewardGroups: rewardGroups,
|
||||
selectedRewardTab: _selectedRewardTab,
|
||||
onRewardTabSelected: _onRewardTabSelected,
|
||||
crew: RoomRocketApiMapper.crewFromStatus(status),
|
||||
crew:
|
||||
_crewByLevel[selectedLevelNumber] ??
|
||||
RoomRocketApiMapper.crewFromStatus(
|
||||
status,
|
||||
level: selectedLevelNumber,
|
||||
),
|
||||
onClose: widget.onClose,
|
||||
onOpenRank: () => _openRank(selectedLevel),
|
||||
onOpenRecord: _openRecord,
|
||||
@ -383,12 +485,45 @@ class _RoomRocketDialogLoaderState extends State<_RoomRocketDialogLoader> {
|
||||
);
|
||||
}
|
||||
|
||||
RoomRocketRewardGroups? _mergeRewardGroups(
|
||||
RoomRocketRewardGroups? primary,
|
||||
RoomRocketRewardGroups? fallback,
|
||||
) {
|
||||
if (primary == null) {
|
||||
return fallback;
|
||||
}
|
||||
if (fallback == null) {
|
||||
return primary;
|
||||
}
|
||||
return RoomRocketRewardGroups(
|
||||
top1: primary.top1.isNotEmpty ? primary.top1 : fallback.top1,
|
||||
setOff: primary.setOff.isNotEmpty ? primary.setOff : fallback.setOff,
|
||||
inRoom: primary.inRoom.isNotEmpty ? primary.inRoom : fallback.inRoom,
|
||||
);
|
||||
}
|
||||
|
||||
int _levelIndexFromStatus(SCRoomRocketStatusRes? status) {
|
||||
return ((status?.currentLevel ?? status?.level ?? 1) - 1)
|
||||
.clamp(0, 5)
|
||||
.toInt();
|
||||
}
|
||||
|
||||
int _levelNumberFromStatus(SCRoomRocketStatusRes? status) {
|
||||
return (status?.currentLevel ?? status?.level ?? 1).clamp(1, 99).toInt();
|
||||
}
|
||||
|
||||
void _cacheRewardGroupsFromStatus(SCRoomRocketStatusRes? status, int level) {
|
||||
final normalizedLevel = level.clamp(1, 99).toInt();
|
||||
final groups = RoomRocketApiMapper.rewardGroupsFromStatus(
|
||||
status,
|
||||
level: normalizedLevel,
|
||||
);
|
||||
if (groups == null) {
|
||||
return;
|
||||
}
|
||||
_statusRewardGroupsByLevel[normalizedLevel] = groups;
|
||||
}
|
||||
|
||||
void _debugStatusAssets(SCRoomRocketStatusRes status) {
|
||||
final levels = status.levels ?? const <SCRoomRocketLevelRes>[];
|
||||
_rocketUiDebug(
|
||||
@ -407,11 +542,133 @@ class _RoomRocketDialogLoaderState extends State<_RoomRocketDialogLoader> {
|
||||
}
|
||||
|
||||
void _onLevelSelected(int index) {
|
||||
_rocketUiDebug('select rocket level=${index + 1}');
|
||||
final selectedIndex = index.clamp(0, 5).toInt();
|
||||
_rocketUiDebug('select rocket level=${selectedIndex + 1}');
|
||||
setState(() {
|
||||
_levelTouched = true;
|
||||
_selectedLevel = index.clamp(0, 5).toInt();
|
||||
_selectedLevel = selectedIndex;
|
||||
});
|
||||
unawaited(_loadRewardForLevel(selectedIndex + 1));
|
||||
unawaited(_loadCrewForLevel(selectedIndex + 1));
|
||||
}
|
||||
|
||||
Future<void> _loadRewardForLevel(int level) async {
|
||||
final normalizedLevel = level.clamp(1, 99).toInt();
|
||||
if (_statusRewardGroupsByLevel.containsKey(normalizedLevel) ||
|
||||
_loadingRewardLevels.contains(normalizedLevel)) {
|
||||
return;
|
||||
}
|
||||
if (widget.roomId.trim().isEmpty) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_loadingRewardLevels.add(normalizedLevel);
|
||||
});
|
||||
try {
|
||||
_rocketUiDebug(
|
||||
'request dialog rewards roomId=${widget.roomId} level=$normalizedLevel',
|
||||
);
|
||||
final res = await SCChatRoomRepository().rocketStatus(
|
||||
widget.roomId,
|
||||
level: normalizedLevel,
|
||||
);
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final groups =
|
||||
RoomRocketApiMapper.rewardGroupsFromStatus(
|
||||
res,
|
||||
level: normalizedLevel,
|
||||
) ??
|
||||
(_statusResponseMatchesLevel(res, normalizedLevel)
|
||||
? RoomRocketApiMapper.rewardGroupsFromPreview(res.rewardPreview)
|
||||
: null);
|
||||
if (groups == null) {
|
||||
_rocketUiDebug('dialog rewards empty level=$normalizedLevel');
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_statusRewardGroupsByLevel[normalizedLevel] = groups;
|
||||
});
|
||||
_rocketUiDebug(
|
||||
'dialog rewards loaded level=$normalizedLevel '
|
||||
'top1=${groups.top1.length} setOff=${groups.setOff.length} '
|
||||
'inRoom=${groups.inRoom.length}',
|
||||
);
|
||||
} catch (error) {
|
||||
_rocketUiDebug(
|
||||
'dialog rewards request failed level=$normalizedLevel error=$error',
|
||||
);
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_loadingRewardLevels.remove(normalizedLevel);
|
||||
});
|
||||
} else {
|
||||
_loadingRewardLevels.remove(normalizedLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _statusResponseMatchesLevel(SCRoomRocketStatusRes status, int level) {
|
||||
final normalizedLevel = level.clamp(1, 99).toInt();
|
||||
final byLevel = status.rewardPreviewByLevel;
|
||||
if (byLevel != null && byLevel.containsKey(normalizedLevel)) {
|
||||
return true;
|
||||
}
|
||||
return status.level?.toInt() == normalizedLevel ||
|
||||
status.currentLevel == normalizedLevel;
|
||||
}
|
||||
|
||||
Future<void> _loadCrewForLevel(int level) async {
|
||||
final normalizedLevel = level.clamp(1, 99).toInt();
|
||||
if (_crewByLevel.containsKey(normalizedLevel) ||
|
||||
_loadingCrewLevels.contains(normalizedLevel)) {
|
||||
return;
|
||||
}
|
||||
if (widget.roomId.trim().isEmpty) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_loadingCrewLevels.add(normalizedLevel);
|
||||
});
|
||||
try {
|
||||
_rocketUiDebug(
|
||||
'request dialog king avatars roomId=${widget.roomId} level=$normalizedLevel',
|
||||
);
|
||||
final res = await SCChatRoomRepository().roomRocketKing(
|
||||
widget.roomId,
|
||||
level: normalizedLevel,
|
||||
limit: 3,
|
||||
);
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final crew = RoomRocketApiMapper.crewFromKing(res);
|
||||
setState(() {
|
||||
_crewByLevel[normalizedLevel] = crew;
|
||||
});
|
||||
_rocketUiDebug(
|
||||
'dialog king avatars loaded level=$normalizedLevel count=${crew.length}',
|
||||
);
|
||||
} catch (error) {
|
||||
_rocketUiDebug(
|
||||
'dialog king avatars request failed level=$normalizedLevel error=$error',
|
||||
);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_crewByLevel[normalizedLevel] = const <RoomRocketCrewMember>[];
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_loadingCrewLevels.remove(normalizedLevel);
|
||||
});
|
||||
} else {
|
||||
_loadingCrewLevels.remove(normalizedLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _onRewardTabSelected(RoomRocketRewardTab tab) {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@ -22,6 +24,19 @@ class RoomRocketPagEffectOverlay extends StatefulWidget {
|
||||
return lower.contains('.pag');
|
||||
}
|
||||
|
||||
static void debugPagResource(String resource, {required String place}) {
|
||||
if (!kDebugMode) {
|
||||
return;
|
||||
}
|
||||
final value = resource.trim();
|
||||
debugPrint(
|
||||
'[RoomRocketPag] detected place=$place '
|
||||
'kind=${_pagSourceKind(value)} '
|
||||
'isPag=${isPag(value)} '
|
||||
'resource=${_shortPagResource(value)}',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
State<RoomRocketPagEffectOverlay> createState() =>
|
||||
_RoomRocketPagEffectOverlayState();
|
||||
@ -55,6 +70,7 @@ class _RoomRocketPagEffectOverlayState
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
loop: false,
|
||||
fit: BoxFit.cover,
|
||||
onCompleted: _complete,
|
||||
defaultBuilder:
|
||||
(_) => const Center(child: SCRotatingDotsLoading()),
|
||||
@ -117,6 +133,8 @@ class RoomRocketPagPreview extends StatelessWidget {
|
||||
this.width,
|
||||
this.height,
|
||||
this.loop = true,
|
||||
this.fit = BoxFit.contain,
|
||||
this.underlayBuilder,
|
||||
this.defaultBuilder,
|
||||
});
|
||||
|
||||
@ -124,6 +142,8 @@ class RoomRocketPagPreview extends StatelessWidget {
|
||||
final double? width;
|
||||
final double? height;
|
||||
final bool loop;
|
||||
final BoxFit fit;
|
||||
final WidgetBuilder? underlayBuilder;
|
||||
final WidgetBuilder? defaultBuilder;
|
||||
|
||||
@override
|
||||
@ -141,6 +161,8 @@ class RoomRocketPagPreview extends StatelessWidget {
|
||||
width: resolvedWidth,
|
||||
height: resolvedHeight,
|
||||
loop: loop,
|
||||
fit: fit,
|
||||
underlayBuilder: underlayBuilder,
|
||||
defaultBuilder:
|
||||
defaultBuilder ??
|
||||
(_) => const Center(child: SCRotatingDotsLoading()),
|
||||
@ -156,6 +178,8 @@ class _RoomRocketPagView extends StatefulWidget {
|
||||
this.width,
|
||||
this.height,
|
||||
required this.loop,
|
||||
required this.fit,
|
||||
this.underlayBuilder,
|
||||
this.onCompleted,
|
||||
this.defaultBuilder,
|
||||
});
|
||||
@ -164,6 +188,8 @@ class _RoomRocketPagView extends StatefulWidget {
|
||||
final double? width;
|
||||
final double? height;
|
||||
final bool loop;
|
||||
final BoxFit fit;
|
||||
final WidgetBuilder? underlayBuilder;
|
||||
final VoidCallback? onCompleted;
|
||||
final WidgetBuilder? defaultBuilder;
|
||||
|
||||
@ -172,22 +198,49 @@ class _RoomRocketPagView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _RoomRocketPagViewState extends State<_RoomRocketPagView> {
|
||||
late Future<Uint8List?> _bytesFuture;
|
||||
static int _nextDebugId = 1;
|
||||
|
||||
final int _debugId = _nextDebugId++;
|
||||
late GlobalKey<PAGViewState> _pagKey;
|
||||
Uint8List? _pagBytes;
|
||||
bool _initialized = false;
|
||||
bool _loadFailed = false;
|
||||
Timer? _startWatchdog;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_bytesFuture = _loadPagBytes(widget.resource.trim());
|
||||
_pagKey = GlobalKey<PAGViewState>();
|
||||
_debug('mount');
|
||||
_prepareResource();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant _RoomRocketPagView oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.resource.trim() != widget.resource.trim()) {
|
||||
_bytesFuture = _loadPagBytes(widget.resource.trim());
|
||||
_pagKey = GlobalKey<PAGViewState>();
|
||||
_pagBytes = null;
|
||||
_initialized = false;
|
||||
_loadFailed = false;
|
||||
_debug(
|
||||
'resource changed old=${_shortPagResource(oldWidget.resource)} '
|
||||
'new=${_shortPagResource(widget.resource)}',
|
||||
);
|
||||
_prepareResource();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_debug(
|
||||
'dispose initialized=$_initialized '
|
||||
'hasBytes=${_pagBytes != null} loadFailed=$_loadFailed',
|
||||
);
|
||||
_startWatchdog?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final path = widget.resource.trim();
|
||||
@ -199,45 +252,281 @@ class _RoomRocketPagViewState extends State<_RoomRocketPagView> {
|
||||
final defaultChild =
|
||||
widget.defaultBuilder?.call(context) ??
|
||||
const Center(child: SCRotatingDotsLoading());
|
||||
return FutureBuilder<Uint8List?>(
|
||||
future: _bytesFuture,
|
||||
builder: (context, snapshot) {
|
||||
final bytes = snapshot.data;
|
||||
if (snapshot.connectionState != ConnectionState.done ||
|
||||
bytes == null ||
|
||||
bytes.isEmpty) {
|
||||
return defaultChild;
|
||||
}
|
||||
return PAGView.bytes(
|
||||
bytes,
|
||||
key: ValueKey('room-rocket-pag:$path:${widget.loop}'),
|
||||
width: widget.width,
|
||||
height: widget.height,
|
||||
autoPlay: true,
|
||||
repeatCount: repeatCount,
|
||||
onAnimationEnd: widget.onCompleted,
|
||||
defaultBuilder: (_) => defaultChild,
|
||||
);
|
||||
},
|
||||
final underlay = widget.underlayBuilder?.call(context);
|
||||
if (_loadFailed) {
|
||||
return _withUnderlay(underlay, defaultChild);
|
||||
}
|
||||
final bytes = _pagBytes;
|
||||
if (bytes == null) {
|
||||
return _withUnderlay(underlay, defaultChild);
|
||||
}
|
||||
final pagView = PAGView.bytes(
|
||||
bytes,
|
||||
key: _pagKey,
|
||||
width: null,
|
||||
height: null,
|
||||
autoPlay: false,
|
||||
repeatCount: repeatCount,
|
||||
onInit: _handleInit,
|
||||
onAnimationStart: _handleAnimationStart,
|
||||
onAnimationEnd: _handleAnimationEnd,
|
||||
onAnimationCancel: _handleAnimationCancel,
|
||||
onAnimationRepeat: _handleAnimationRepeat,
|
||||
defaultBuilder: (_) => const SizedBox(width: 1, height: 1),
|
||||
);
|
||||
return SizedBox(
|
||||
width: widget.width,
|
||||
height: widget.height,
|
||||
child: ClipRect(
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
if (underlay != null) underlay,
|
||||
FittedBox(fit: widget.fit, child: pagView),
|
||||
if (!_initialized) Positioned.fill(child: defaultChild),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Uint8List?> _loadPagBytes(String path) async {
|
||||
void _prepareResource() {
|
||||
final path = widget.resource.trim();
|
||||
_debug(
|
||||
'prepare kind=${_pagSourceKind(path)} '
|
||||
'isPag=${RoomRocketPagEffectOverlay.isPag(path)}',
|
||||
);
|
||||
_restartWatchdog();
|
||||
unawaited(_loadBytes(path));
|
||||
}
|
||||
|
||||
Future<void> _loadBytes(String path) async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
try {
|
||||
final data =
|
||||
_isNetwork(path)
|
||||
? await NetworkAssetBundle(Uri.parse(path)).load(path)
|
||||
: await rootBundle.load(path);
|
||||
return data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
|
||||
} catch (error) {
|
||||
if (kDebugMode) {
|
||||
debugPrint('[RoomRocketPag] load failed path=$path error=$error');
|
||||
final isNetwork = _isPagNetwork(path);
|
||||
_debug(
|
||||
'${isNetwork ? 'download' : 'asset load'} start '
|
||||
'host=${_pagHost(path)}',
|
||||
);
|
||||
final data = await (isNetwork
|
||||
? NetworkAssetBundle(Uri.parse(path)).load(path)
|
||||
: rootBundle.load(path))
|
||||
.timeout(const Duration(seconds: 15));
|
||||
final bytes = Uint8List.fromList(
|
||||
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes),
|
||||
);
|
||||
if (!mounted || widget.resource.trim() != path) {
|
||||
_debug(
|
||||
'bytes ignored stale mounted=$mounted '
|
||||
'elapsedMs=${stopwatch.elapsedMilliseconds}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
return null;
|
||||
if (bytes.isEmpty) {
|
||||
_markLoadFailed('download empty');
|
||||
return;
|
||||
}
|
||||
_debug(
|
||||
'bytes ready bytes=${bytes.length} '
|
||||
'head=${_pagByteHead(bytes)} '
|
||||
'elapsedMs=${stopwatch.elapsedMilliseconds}',
|
||||
);
|
||||
setState(() {
|
||||
_pagBytes = bytes;
|
||||
});
|
||||
_restartWatchdog();
|
||||
} catch (error) {
|
||||
if (!mounted || widget.resource.trim() != path) {
|
||||
return;
|
||||
}
|
||||
_markLoadFailed(
|
||||
'bytes load failed elapsedMs=${stopwatch.elapsedMilliseconds}: $error',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _isNetwork(String value) {
|
||||
return value.startsWith('http://') || value.startsWith('https://');
|
||||
void _handleInit() {
|
||||
_debug('init');
|
||||
_startWatchdog?.cancel();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final state = _pagKey.currentState;
|
||||
if (state == null) {
|
||||
_markLoadFailed('init failed state=null');
|
||||
return;
|
||||
}
|
||||
final rawWidth = state.rawWidth;
|
||||
final rawHeight = state.rawHeight;
|
||||
if (rawWidth <= 0 || rawHeight <= 0) {
|
||||
_markLoadFailed('init failed rawSize=${rawWidth}x$rawHeight');
|
||||
return;
|
||||
}
|
||||
_debug(
|
||||
'ready rawSize=${rawWidth}x$rawHeight '
|
||||
'viewSize=${widget.width?.toStringAsFixed(1)}x'
|
||||
'${widget.height?.toStringAsFixed(1)} fit=${widget.fit.name}',
|
||||
);
|
||||
unawaited(_debugLayers(state, rawWidth, rawHeight));
|
||||
if (!_initialized) {
|
||||
setState(() => _initialized = true);
|
||||
}
|
||||
try {
|
||||
_debug('start requested');
|
||||
state.start();
|
||||
} catch (error) {
|
||||
_markLoadFailed('start threw: $error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _handleAnimationStart() {
|
||||
_debug('start');
|
||||
}
|
||||
|
||||
void _handleAnimationEnd() {
|
||||
_debug('end');
|
||||
widget.onCompleted?.call();
|
||||
}
|
||||
|
||||
void _handleAnimationCancel() {
|
||||
_debug('cancel');
|
||||
}
|
||||
|
||||
void _handleAnimationRepeat() {
|
||||
_debug('repeat');
|
||||
}
|
||||
|
||||
Future<void> _debugLayers(
|
||||
PAGViewState state,
|
||||
double rawWidth,
|
||||
double rawHeight,
|
||||
) async {
|
||||
if (!kDebugMode) {
|
||||
return;
|
||||
}
|
||||
final points = <String, Offset>{
|
||||
'center': Offset(rawWidth / 2, rawHeight / 2),
|
||||
'upper': Offset(rawWidth / 2, rawHeight * 0.25),
|
||||
'lower': Offset(rawWidth / 2, rawHeight * 0.75),
|
||||
};
|
||||
for (final entry in points.entries) {
|
||||
try {
|
||||
final layers = await state.getLayersUnderPoint(
|
||||
entry.value.dx,
|
||||
entry.value.dy,
|
||||
);
|
||||
_debug(
|
||||
'layers point=${entry.key} '
|
||||
'x=${entry.value.dx.toStringAsFixed(1)} '
|
||||
'y=${entry.value.dy.toStringAsFixed(1)} '
|
||||
'count=${layers.length} names=$layers',
|
||||
);
|
||||
} catch (error) {
|
||||
_debug('layers point=${entry.key} failed: $error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _restartWatchdog() {
|
||||
_startWatchdog?.cancel();
|
||||
_debug(
|
||||
'watchdog armed initialized=$_initialized hasBytes=${_pagBytes != null}',
|
||||
);
|
||||
_startWatchdog = Timer(const Duration(seconds: 8), () {
|
||||
if (mounted && !_initialized) {
|
||||
if (_pagBytes == null && !_loadFailed) {
|
||||
_debug('waiting for pag bytes');
|
||||
return;
|
||||
}
|
||||
_markLoadFailed('waiting for pag init');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Widget _withUnderlay(Widget? underlay, Widget foreground) {
|
||||
if (underlay == null) {
|
||||
return foreground;
|
||||
}
|
||||
return SizedBox(
|
||||
width: widget.width,
|
||||
height: widget.height,
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [underlay, Positioned.fill(child: foreground)],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _markLoadFailed(String reason) {
|
||||
_debug('failed reason=$reason');
|
||||
_startWatchdog?.cancel();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_initialized = false;
|
||||
_loadFailed = true;
|
||||
});
|
||||
} else {
|
||||
_loadFailed = true;
|
||||
}
|
||||
if (!widget.loop) {
|
||||
widget.onCompleted?.call();
|
||||
}
|
||||
}
|
||||
|
||||
void _debug(String event) {
|
||||
if (!kDebugMode) {
|
||||
return;
|
||||
}
|
||||
debugPrint(
|
||||
'[RoomRocketPag#$_debugId] $event '
|
||||
'loop=${widget.loop} '
|
||||
'size=${widget.width?.toStringAsFixed(1)}x'
|
||||
'${widget.height?.toStringAsFixed(1)} '
|
||||
'kind=${_pagSourceKind(widget.resource)} '
|
||||
'resource=${_shortPagResource(widget.resource)}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool _isPagNetwork(String value) {
|
||||
return value.startsWith('http://') || value.startsWith('https://');
|
||||
}
|
||||
|
||||
String _pagSourceKind(String value) {
|
||||
final text = value.trim();
|
||||
if (text.isEmpty) {
|
||||
return 'empty';
|
||||
}
|
||||
if (_isPagNetwork(text)) {
|
||||
return 'network';
|
||||
}
|
||||
if (text.startsWith('sc_images/') || text.startsWith('assets/')) {
|
||||
return 'asset';
|
||||
}
|
||||
return 'local-or-unknown';
|
||||
}
|
||||
|
||||
String _pagHost(String value) {
|
||||
if (!_isPagNetwork(value)) {
|
||||
return '-';
|
||||
}
|
||||
return Uri.tryParse(value)?.host ?? '-';
|
||||
}
|
||||
|
||||
String _shortPagResource(String value) {
|
||||
final text = value.trim();
|
||||
if (text.length <= 220) {
|
||||
return text;
|
||||
}
|
||||
return '${text.substring(0, 120)}...${text.substring(text.length - 80)}';
|
||||
}
|
||||
|
||||
String _pagByteHead(Uint8List bytes) {
|
||||
final length = bytes.length < 12 ? bytes.length : 12;
|
||||
return bytes
|
||||
.take(length)
|
||||
.map((byte) => byte.toRadixString(16).padLeft(2, '0'))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
@ -320,7 +320,7 @@ class _RewardDialogCanvas extends StatelessWidget {
|
||||
top: s(482),
|
||||
width: s(_designWidth),
|
||||
child: _SectionTitle(
|
||||
text: '12 users received rewards',
|
||||
text: _receivedRewardsText(winners.length),
|
||||
scale: scale,
|
||||
),
|
||||
),
|
||||
@ -329,6 +329,11 @@ class _RewardDialogCanvas extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
String _receivedRewardsText(int count) {
|
||||
final unit = count == 1 ? 'user' : 'users';
|
||||
return '$count $unit received rewards';
|
||||
}
|
||||
|
||||
Widget _buildEmptyState() {
|
||||
return Positioned(
|
||||
left: s(0),
|
||||
|
||||
@ -711,13 +711,7 @@ class _MsgItemState extends State<MsgItem> {
|
||||
),
|
||||
);
|
||||
spans.add(
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Image.asset(
|
||||
"sc_images/room/sc_icon_room_rocket_lv${widget.msg.number}.png",
|
||||
width: 18.w,
|
||||
),
|
||||
),
|
||||
WidgetSpan(alignment: PlaceholderAlignment.middle, child: _rocketIcon()),
|
||||
);
|
||||
spans.add(
|
||||
TextSpan(
|
||||
@ -762,6 +756,14 @@ class _MsgItemState extends State<MsgItem> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _rocketIcon() {
|
||||
final iconUrl = widget.msg.rocketIconUrl?.trim() ?? '';
|
||||
if (iconUrl.isEmpty) {
|
||||
return SizedBox(width: 4.w);
|
||||
}
|
||||
return netImage(url: iconUrl, width: 18.w);
|
||||
}
|
||||
|
||||
///幸运礼物
|
||||
_buildGameLuckyGiftMsg(BuildContext context) {
|
||||
List<InlineSpan> spans = [];
|
||||
@ -1875,6 +1877,7 @@ class Msg {
|
||||
String? type;
|
||||
SocialChatUserProfile? user;
|
||||
String? role = "";
|
||||
String? rocketIconUrl = "";
|
||||
SocialChatGiftRes? gift;
|
||||
|
||||
//礼物个数
|
||||
@ -1904,6 +1907,7 @@ class Msg {
|
||||
this.toUser,
|
||||
this.userWheat,
|
||||
this.role = "",
|
||||
this.rocketIconUrl,
|
||||
this.gift,
|
||||
this.number,
|
||||
this.customAnimationCount,
|
||||
@ -1921,6 +1925,7 @@ class Msg {
|
||||
msg = json['msg'];
|
||||
type = json['type'];
|
||||
role = json['role'];
|
||||
rocketIconUrl = json['rocketIconUrl']?.toString();
|
||||
time = json['time'];
|
||||
number = json['number'];
|
||||
customAnimationCount = json['customAnimationCount'];
|
||||
@ -1957,6 +1962,9 @@ class Msg {
|
||||
map['msg'] = msg;
|
||||
map['type'] = type;
|
||||
map['role'] = role;
|
||||
if ((rocketIconUrl ?? "").trim().isNotEmpty) {
|
||||
map['rocketIconUrl'] = rocketIconUrl;
|
||||
}
|
||||
map['time'] = time;
|
||||
map['awardAmount'] = awardAmount;
|
||||
map['number'] = number;
|
||||
|
||||
30
local_packages/pag-1.0.7-patched/CHANGELOG.md
Normal file
30
local_packages/pag-1.0.7-patched/CHANGELOG.md
Normal file
@ -0,0 +1,30 @@
|
||||
## 1.0.7
|
||||
* 修复1.0.6安卓代码未正确包含的问题
|
||||
|
||||
## 1.0.6
|
||||
* 提升libpag库版本
|
||||
* 安卓库提升kotlin-gradle-plugin版本
|
||||
* 安卓原生侧添加缓存,加快加载速度
|
||||
* 安卓原生侧支持多线程,优化卡顿情况
|
||||
* !!!未正确发布,不可用!!!
|
||||
|
||||
## 1.0.5
|
||||
* 增加鸿蒙支持
|
||||
|
||||
## 1.0.4
|
||||
* 增加展位图参数
|
||||
|
||||
## 1.0.3
|
||||
* 删除gradle无用引用
|
||||
|
||||
## 1.0.2
|
||||
* 修正Android repeatCount空异常
|
||||
|
||||
## 1.0.1
|
||||
* 修正命名规范的疏漏,podspec未修改
|
||||
|
||||
## 1.0.0
|
||||
* 封装PAG动画使用方式
|
||||
* 支持二进制、网络连接、资源文件等加载方式
|
||||
* 提供动画流程中的事件监听,与原生API一致
|
||||
|
||||
1350
local_packages/pag-1.0.7-patched/LICENSE
Normal file
1350
local_packages/pag-1.0.7-patched/LICENSE
Normal file
File diff suppressed because it is too large
Load Diff
92
local_packages/pag-1.0.7-patched/README.md
Normal file
92
local_packages/pag-1.0.7-patched/README.md
Normal file
@ -0,0 +1,92 @@
|
||||
# 项目介绍
|
||||
为Flutter打造的PAG动画组件,以外接纹理的方式实现。
|
||||
|
||||
**注:如果遇到使用问题请在本仓库提 issue 与作者讨论,或直接提交 pr 参与共建。**
|
||||
|
||||
[**PAG官网**](https://pag.art/)
|
||||
|
||||
# 快速上手
|
||||
Flutter侧通过PAGView来使用动画
|
||||
|
||||
### 引用
|
||||
```
|
||||
dependencies:
|
||||
pag: 1.0.0
|
||||
```
|
||||
|
||||
Android端混淆文件中配置,避免影响
|
||||
```
|
||||
-keep class org.libpag.**{*;}
|
||||
```
|
||||
|
||||
### 使用本地资源
|
||||
```
|
||||
PAGView.asset(
|
||||
"assets/xxx.pag", //flutter侧资源路径
|
||||
repeatCount: PagView.REPEAT_COUNT_LOOP, // 循环次数
|
||||
initProgress: 0.25, // 初始进度
|
||||
key: pagKey, // 利用key进行主动调用
|
||||
autoPlay: true, // 是否自动播放
|
||||
)
|
||||
```
|
||||
### 使用网络资源
|
||||
```
|
||||
PAGView.url(
|
||||
"xxxx", //网络链接
|
||||
repeatCount: PagView.REPEAT_COUNT_LOOP, // 循环次数
|
||||
initProgress: 0.25, // 初始进度
|
||||
key: pagKey, // 利用key进行主动调用
|
||||
autoPlay: true, // 是否自动播放
|
||||
)
|
||||
```
|
||||
### 使用二进制数据
|
||||
```
|
||||
PAGView.bytes(
|
||||
"xxxx", //网络链接
|
||||
repeatCount: PagView.REPEAT_COUNT_LOOP, // 循环次数
|
||||
initProgress: 0.25, // 初始进度
|
||||
key: pagKey, // 利用key进行主动调用
|
||||
autoPlay: true, // 是否自动播放
|
||||
)
|
||||
```
|
||||
### 可以在PAGView中加入回调参数
|
||||
以下回调与原生PAG监听对齐
|
||||
```
|
||||
PAGView.asset(
|
||||
...
|
||||
onAnimationStart: (){ // 开始
|
||||
// do something
|
||||
},
|
||||
onAnimationEnd: (){ // 结束
|
||||
// do something
|
||||
},
|
||||
onAnimationRepeat: (){ // 重复
|
||||
// do something
|
||||
},
|
||||
onAnimationCancel: (){ // 取消
|
||||
// do something
|
||||
},
|
||||
```
|
||||
|
||||
### 通过key获取state进行主动调用
|
||||
```
|
||||
final GlobalKey<PAGViewState> pagKey = GlobalKey<PAGViewState>();
|
||||
|
||||
//传入key值
|
||||
PAGView.url(key:pagKey)
|
||||
|
||||
//播放
|
||||
pagKey.currentState?.start();
|
||||
|
||||
//暂停
|
||||
pagKey.currentState?.pause();
|
||||
|
||||
//停止
|
||||
pagKey.currentState?.stop();
|
||||
|
||||
//设置进度
|
||||
pagKey.currentState?.setProgress(xxx);
|
||||
|
||||
//获取坐标位置的图层名list
|
||||
pagKey.currentState?.getLayersUnderPoint(x,y);
|
||||
```
|
||||
47
local_packages/pag-1.0.7-patched/android/build.gradle
Normal file
47
local_packages/pag-1.0.7-patched/android/build.gradle
Normal file
@ -0,0 +1,47 @@
|
||||
group 'com.example.flutter_pag_plugin'
|
||||
version '1.0'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
namespace 'com.example.flutter_pag_plugin'
|
||||
compileSdkVersion 36
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 19
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation "com.tencent.tav:libpag:4.3.68"
|
||||
implementation 'com.jakewharton:disklrucache:2.0.2'
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
5
local_packages/pag-1.0.7-patched/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
local_packages/pag-1.0.7-patched/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
||||
1
local_packages/pag-1.0.7-patched/android/settings.gradle
Normal file
1
local_packages/pag-1.0.7-patched/android/settings.gradle
Normal file
@ -0,0 +1 @@
|
||||
rootProject.name = 'flutter_pag_plugin'
|
||||
@ -0,0 +1,3 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.flutter_pag_plugin">
|
||||
</manifest>
|
||||
@ -0,0 +1,210 @@
|
||||
package com.example.flutter_pag_plugin
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.util.LruCache
|
||||
import com.example.flutter_pag_plugin.utils.EncodeUtil
|
||||
import com.jakewharton.disklrucache.DiskLruCache
|
||||
import java.io.*
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
|
||||
//数据加载器
|
||||
object DataLoadHelper {
|
||||
private var diskCache: DiskLruCache? = null
|
||||
private val memoryCache by lazy { LruCache<String, ByteArray>(Runtime.getRuntime().maxMemory().toInt() / 50) }
|
||||
private const val TAG = "DataLoadHelper"
|
||||
const val DEFAULT_DIS_SIZE = 30 * 1024 * 1024L;
|
||||
|
||||
// 下载来源:flutter插件、其他
|
||||
const val FROM_PLUGIN = 0
|
||||
const val FROM_OTHER = 1
|
||||
|
||||
private val loadListeners = mutableListOf<ILoadListener>()
|
||||
|
||||
//初始化pag动画
|
||||
fun loadPag(src: String, addPag: (ByteArray?) -> Unit, from: Int = FROM_OTHER) {
|
||||
val time = System.currentTimeMillis();
|
||||
val bytes = memoryCache.get(hashKeyForDisk(src))
|
||||
|
||||
loadListeners.forEach {
|
||||
it.loadStart(src, from)
|
||||
}
|
||||
|
||||
if (bytes != null) {
|
||||
addPag(bytes)
|
||||
loadListeners.forEach {
|
||||
it.loadComplete(src, bytes, System.currentTimeMillis() - time, "", from)
|
||||
}
|
||||
} else {
|
||||
Thread {
|
||||
loadPagByDisk(src) { byteArray, errorMsg ->
|
||||
addPag.invoke(byteArray)
|
||||
loadListeners.forEach {
|
||||
it.loadComplete(src, byteArray, System.currentTimeMillis() - time, errorMsg, from)
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
fun initDiskCache(context: Context, size: Long = DEFAULT_DIS_SIZE) {
|
||||
if (diskCache != null) {
|
||||
Log.w(TAG, "diskCache do not need init again!")
|
||||
return
|
||||
}
|
||||
|
||||
val cacheDir = getDiskCacheDir(context, "pag")
|
||||
if (!cacheDir.exists()) {
|
||||
cacheDir.mkdirs()
|
||||
}
|
||||
try {
|
||||
diskCache = diskCache ?: DiskLruCache.open(
|
||||
cacheDir,
|
||||
context.packageManager.getPackageInfo(context.packageName, 0).versionCode,
|
||||
1, size
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "initDiskCache error: $e")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//获取硬盘缓存路径
|
||||
private fun getDiskCacheDir(context: Context, uniqueName: String): File {
|
||||
val cachePath: String = if (Environment.MEDIA_MOUNTED == Environment.getExternalStorageState()
|
||||
|| !Environment.isExternalStorageRemovable()
|
||||
) {
|
||||
context.externalCacheDir?.path
|
||||
} else {
|
||||
context.cacheDir.path
|
||||
} ?: ""
|
||||
return File(cachePath + File.separator.toString() + uniqueName)
|
||||
}
|
||||
|
||||
|
||||
//硬盘或者网络获取,
|
||||
@Synchronized
|
||||
private fun loadPagByDisk(src: String, addPag: (ByteArray?, String) -> Unit) {
|
||||
//从硬盘缓存中获取
|
||||
|
||||
var errorMsg = ""
|
||||
val key = hashKeyForDisk(src)
|
||||
var snapShot: DiskLruCache.Snapshot? = null
|
||||
try {
|
||||
snapShot = diskCache?.get(key)
|
||||
if (snapShot == null) {
|
||||
Log.d(TAG, "loadPag load from network")
|
||||
//没有,进行网络操作
|
||||
val editor = diskCache?.edit(key)
|
||||
if (editor != null) {
|
||||
val outputStream = editor.newOutputStream(0)
|
||||
if (downloadUrlToStream(src, outputStream)) {
|
||||
editor.commit()
|
||||
} else {
|
||||
editor.abort()
|
||||
}
|
||||
}
|
||||
//存入硬盘后再次读取
|
||||
diskCache?.flush()
|
||||
snapShot = diskCache?.get(key)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "loadPag load from network erro: $e")
|
||||
errorMsg = "loadPag load from network error: $e"
|
||||
}
|
||||
|
||||
//进行bytes读取
|
||||
var bytes: ByteArray? = null
|
||||
if (snapShot != null) {
|
||||
Log.d(TAG, "loadPag load from snapShot")
|
||||
try {
|
||||
val inputStream = snapShot.getInputStream(0)
|
||||
val outputStrem = ByteArrayOutputStream()
|
||||
inputStream.use { ins ->
|
||||
outputStrem.use { bos ->
|
||||
val buffer = ByteArray(1024)
|
||||
var len: Int
|
||||
|
||||
while (ins.read(buffer).also { len = it } != -1) {
|
||||
bos.write(buffer, 0, len)
|
||||
}
|
||||
bytes = bos.toByteArray()
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
Log.e(TAG, "loadPag load from snapShot erro: $e")
|
||||
errorMsg = "loadPag load from network error: $e"
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "loadPag bytes size: ${bytes?.size}")
|
||||
//存储进内存缓存
|
||||
if (bytes != null && bytes!!.isNotEmpty() && memoryCache.get(key) == null) {
|
||||
memoryCache.put(key, bytes)
|
||||
}
|
||||
addPag(bytes, errorMsg)
|
||||
}
|
||||
|
||||
|
||||
//下载
|
||||
private fun downloadUrlToStream(urlString: String, outputStream: OutputStream): Boolean {
|
||||
var urlConnection: HttpURLConnection? = null
|
||||
var outStream: BufferedOutputStream? = null
|
||||
var inStream: BufferedInputStream? = null
|
||||
try {
|
||||
val url = URL(urlString)
|
||||
urlConnection = url.openConnection() as HttpURLConnection
|
||||
inStream = BufferedInputStream(urlConnection.inputStream, 8 * 1024)
|
||||
outStream = BufferedOutputStream(outputStream, 8 * 1024)
|
||||
var b: Int
|
||||
while (inStream.read().also { b = it } != -1) {
|
||||
outStream.write(b)
|
||||
}
|
||||
return true
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
urlConnection?.disconnect()
|
||||
try {
|
||||
outStream?.close()
|
||||
inStream?.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//使用MD5算法对传入的key进行加密并返回
|
||||
private fun hashKeyForDisk(key: String): String? {
|
||||
return try {
|
||||
val mDigest: MessageDigest = MessageDigest.getInstance("MD5")
|
||||
mDigest.update(key.toByteArray())
|
||||
EncodeUtil.bytesToHexString(mDigest.digest())
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
key.hashCode().toString()
|
||||
}
|
||||
}
|
||||
|
||||
fun addLoadListener(loadListener: ILoadListener) {
|
||||
loadListeners.add(loadListener)
|
||||
}
|
||||
|
||||
|
||||
fun removeLoadListener(loadListener: ILoadListener) {
|
||||
loadListeners.remove(loadListener)
|
||||
}
|
||||
}
|
||||
|
||||
// 用于监听PAG加载情况
|
||||
interface ILoadListener {
|
||||
|
||||
fun loadStart(url: String, from: Int)
|
||||
|
||||
fun loadComplete(url: String, result: ByteArray?/*result为空则失败*/, useTime: Long, errorMsg: String, from: Int)
|
||||
}
|
||||
@ -0,0 +1,230 @@
|
||||
package com.example.flutter_pag_plugin;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
|
||||
import org.libpag.PAGFile;
|
||||
import org.libpag.PAGPlayer;
|
||||
import org.libpag.PAGSurface;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
|
||||
|
||||
public class FlutterPagPlayer extends PAGPlayer {
|
||||
|
||||
private final ValueAnimator animator = ValueAnimator.ofFloat(0.0F, 1.0F);
|
||||
private boolean isRelease;
|
||||
private long currentPlayTime = 0L;
|
||||
private double progress = 0;
|
||||
private double initProgress = 0;
|
||||
private SurfaceTexture surfaceTexture;
|
||||
|
||||
private MethodChannel channel;
|
||||
private long textureId;
|
||||
|
||||
|
||||
public FlutterPagPlayer() {
|
||||
super();
|
||||
animator.setInterpolator(new LinearInterpolator());
|
||||
animator.addUpdateListener(animatorUpdateListener);
|
||||
animator.addListener(animatorListenerAdapter);
|
||||
}
|
||||
|
||||
public boolean isRelease() {
|
||||
return isRelease;
|
||||
}
|
||||
|
||||
public void init(PAGFile file, int repeatCount, double initProgress, MethodChannel channel, long textureId) {
|
||||
if (WorkThreadExecutor.multiThread) {
|
||||
synchronized (this) {
|
||||
setComposition(file);
|
||||
}
|
||||
} else {
|
||||
setComposition(file);
|
||||
}
|
||||
|
||||
this.channel = channel;
|
||||
this.textureId = textureId;
|
||||
progress = initProgress;
|
||||
this.initProgress = initProgress;
|
||||
initAnimator(repeatCount);
|
||||
}
|
||||
|
||||
private boolean valid() {
|
||||
return getSurface() != null && surfaceTexture != null;
|
||||
}
|
||||
|
||||
private void initAnimator(int repeatCount) {
|
||||
animator.setDuration(duration() / 1000L);
|
||||
if (repeatCount < 0) {
|
||||
repeatCount = 0;
|
||||
}
|
||||
animator.setRepeatCount(repeatCount - 1);
|
||||
setProgressValue(initProgress);
|
||||
}
|
||||
|
||||
public void setProgressValue(double value) {
|
||||
this.progress = Math.max(0.0D, Math.min(value, 1.0D));
|
||||
this.currentPlayTime = (long) (progress * (double) this.animator.getDuration());
|
||||
this.animator.setCurrentPlayTime(currentPlayTime);
|
||||
setProgress(progress);
|
||||
flush();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
animator.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
pause();
|
||||
setProgressValue(initProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSurface(PAGSurface pagSurface) {
|
||||
super.setSurface(pagSurface);
|
||||
}
|
||||
|
||||
public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
|
||||
this.surfaceTexture = surfaceTexture;
|
||||
}
|
||||
|
||||
public void updateBufferSize(int width, int height) {
|
||||
if (WorkThreadExecutor.multiThread) {
|
||||
synchronized (this) {
|
||||
surfaceTexture.setDefaultBufferSize(width, height);
|
||||
getSurface().updateSize();
|
||||
getSurface().clearAll();
|
||||
}
|
||||
} else {
|
||||
surfaceTexture.setDefaultBufferSize(width, height);
|
||||
getSurface().updateSize();
|
||||
getSurface().clearAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
if (WorkThreadExecutor.multiThread) {
|
||||
synchronized (this) {
|
||||
setComposition(null);
|
||||
if (valid()) {
|
||||
getSurface().freeCache();
|
||||
getSurface().clearAll();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setComposition(null);
|
||||
if (valid()) {
|
||||
getSurface().freeCache();
|
||||
getSurface().clearAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
animator.cancel();
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
animator.pause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
super.release();
|
||||
animator.cancel();
|
||||
animator.removeAllUpdateListeners();
|
||||
animator.removeAllListeners();
|
||||
WorkThreadExecutor.getInstance().post(() -> {
|
||||
if (WorkThreadExecutor.multiThread) {
|
||||
synchronized (this) {
|
||||
if (getSurface() != null) getSurface().release();
|
||||
surfaceTexture.release();
|
||||
surfaceTexture = null;
|
||||
}
|
||||
} else {
|
||||
if (getSurface() != null) getSurface().release();
|
||||
surfaceTexture.release();
|
||||
surfaceTexture = null;
|
||||
}
|
||||
|
||||
});
|
||||
isRelease = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean flush() {
|
||||
if (isRelease) {
|
||||
return false;
|
||||
}
|
||||
WorkThreadExecutor.getInstance().post(() -> {
|
||||
if (WorkThreadExecutor.multiThread) {
|
||||
synchronized (this) {
|
||||
FlutterPagPlayer.super.flush();
|
||||
}
|
||||
} else {
|
||||
FlutterPagPlayer.super.flush();
|
||||
}
|
||||
|
||||
});
|
||||
return true;
|
||||
|
||||
// return super.flush();
|
||||
}
|
||||
|
||||
// 更新PAG渲染
|
||||
private final ValueAnimator.AnimatorUpdateListener animatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
|
||||
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
progress = (double) (Float) animation.getAnimatedValue();
|
||||
currentPlayTime = (long) (progress * (double) animator.getDuration());
|
||||
setProgress(progress);
|
||||
flush();
|
||||
}
|
||||
};
|
||||
|
||||
// 动画状态监听
|
||||
private final AnimatorListenerAdapter animatorListenerAdapter = new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
super.onAnimationStart(animator);
|
||||
notifyEvent(FlutterPagPlugin._eventStart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
// Align with iOS platform, avoid triggering this method when stopping
|
||||
int repeatCount = ((ValueAnimator) animation).getRepeatCount();
|
||||
if (repeatCount >= 0 && (animation.getDuration() > 0) &&
|
||||
(currentPlayTime / animation.getDuration() > repeatCount)) {
|
||||
notifyEvent(FlutterPagPlugin._eventEnd);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animator) {
|
||||
super.onAnimationCancel(animator);
|
||||
notifyEvent(FlutterPagPlugin._eventCancel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animator) {
|
||||
super.onAnimationRepeat(animator);
|
||||
notifyEvent(FlutterPagPlugin._eventRepeat);
|
||||
}
|
||||
};
|
||||
|
||||
void notifyEvent(String event) {
|
||||
final HashMap<String, Object> arguments = new HashMap<>();
|
||||
arguments.put(FlutterPagPlugin._argumentTextureId, textureId);
|
||||
arguments.put(FlutterPagPlugin._argumentEvent, event);
|
||||
channel.invokeMethod(FlutterPagPlugin._playCallback, arguments);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,387 @@
|
||||
package com.example.flutter_pag_plugin;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.libpag.PAGFile;
|
||||
import org.libpag.PAGLayer;
|
||||
import org.libpag.PAGSurface;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||
import io.flutter.plugin.common.MethodChannel.Result;
|
||||
import io.flutter.view.TextureRegistry;
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
|
||||
/**
|
||||
* FlutterPagPlugin
|
||||
*/
|
||||
public class FlutterPagPlugin implements FlutterPlugin, MethodCallHandler {
|
||||
/// The MethodChannel that will the communication between Flutter and native Android
|
||||
///
|
||||
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
||||
/// when the Flutter Engine is detached from the Activity
|
||||
private MethodChannel channel;
|
||||
TextureRegistry textureRegistry;
|
||||
Context context;
|
||||
FlutterPlugin.FlutterAssets flutterAssets;
|
||||
private Handler handler = new Handler(Looper.getMainLooper());
|
||||
|
||||
// 多引擎使用是plugin的集合,请留意该场景下需手动释放,否则存在内存泄漏的问题
|
||||
public static List<FlutterPagPlugin> pluginList = new ArrayList<FlutterPagPlugin>();
|
||||
|
||||
public HashMap<String, FlutterPagPlayer> layerMap = new HashMap<String, FlutterPagPlayer>();
|
||||
public HashMap<String, TextureRegistry.SurfaceTextureEntry> entryMap = new HashMap<String, TextureRegistry.SurfaceTextureEntry>();
|
||||
public LinkedList<String> freeEntryPool = new LinkedList<>();
|
||||
|
||||
// 原生接口
|
||||
final static String _nativeInit = "initPag";
|
||||
final static String _nativeRelease = "release";
|
||||
final static String _nativeStart = "start";
|
||||
final static String _nativeStop = "stop";
|
||||
final static String _nativePause = "pause";
|
||||
final static String _nativeSetProgress = "setProgress";
|
||||
final static String _nativeGetPointLayer = "getLayersUnderPoint";
|
||||
final static String _nativeEnableCache = "enableCache";
|
||||
final static String _nativeSetCacheSize = "setCacheSize";
|
||||
final static String _nativeEnableMultiThread = "enableMultiThread";
|
||||
|
||||
|
||||
// 参数
|
||||
final static String _argumentTextureId = "textureId";
|
||||
final static String _argumentAssetName = "assetName";
|
||||
final static String _argumentPackage = "package";
|
||||
final static String _argumentUrl = "url";
|
||||
final static String _argumentBytes = "bytesData";
|
||||
final static String _argumentRepeatCount = "repeatCount";
|
||||
final static String _argumentInitProgress = "initProgress";
|
||||
final static String _argumentAutoPlay = "autoPlay";
|
||||
final static String _argumentWidth = "width";
|
||||
final static String _argumentHeight = "height";
|
||||
final static String _argumentPointX = "x";
|
||||
final static String _argumentPointY = "y";
|
||||
final static String _argumentProgress = "progress";
|
||||
final static String _argumentEvent = "PAGEvent";
|
||||
final static String _argumentCacheEnabled = "cacheEnabled";
|
||||
final static String _argumentCacheSize = "cacheSize";
|
||||
final static String _argumentMultiThreadEnabled = "multiThreadEnabled";
|
||||
|
||||
// 回调
|
||||
final static String _playCallback = "PAGCallback";
|
||||
final static String _eventStart = "onAnimationStart";
|
||||
final static String _eventEnd = "onAnimationEnd";
|
||||
final static String _eventCancel = "onAnimationCancel";
|
||||
final static String _eventRepeat = "onAnimationRepeat";
|
||||
final static String _eventUpdate = "onAnimationUpdate";
|
||||
|
||||
private boolean useCache = true;
|
||||
private int maxFreePoolSize = 10;
|
||||
|
||||
public FlutterPagPlugin() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
|
||||
if (!pluginList.contains(this)) {
|
||||
pluginList.add(this);
|
||||
}
|
||||
flutterAssets = binding.getFlutterAssets();
|
||||
channel = new MethodChannel(binding.getBinaryMessenger(), "flutter_pag_plugin");
|
||||
channel.setMethodCallHandler(this);
|
||||
context = binding.getApplicationContext();
|
||||
textureRegistry = binding.getTextureRegistry();
|
||||
DataLoadHelper.INSTANCE.initDiskCache(context, DataLoadHelper.INSTANCE.DEFAULT_DIS_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
|
||||
switch (call.method) {
|
||||
case _nativeInit:
|
||||
initPag(call, result);
|
||||
break;
|
||||
case _nativeStart:
|
||||
start(call);
|
||||
result.success("");
|
||||
break;
|
||||
case _nativeStop:
|
||||
stop(call);
|
||||
result.success("");
|
||||
break;
|
||||
case _nativePause:
|
||||
pause(call);
|
||||
result.success("");
|
||||
break;
|
||||
case _nativeSetProgress:
|
||||
setProgress(call);
|
||||
result.success("");
|
||||
break;
|
||||
case _nativeRelease:
|
||||
release(call);
|
||||
result.success("");
|
||||
break;
|
||||
case _nativeGetPointLayer:
|
||||
result.success(getLayersUnderPoint(call));
|
||||
break;
|
||||
case _nativeEnableCache:
|
||||
enableCache(call);
|
||||
result.success("");
|
||||
break;
|
||||
case _nativeSetCacheSize:
|
||||
setCacheSize(call);
|
||||
result.success("");
|
||||
break;
|
||||
case _nativeEnableMultiThread:
|
||||
enableMultiThread(call);
|
||||
result.success("");
|
||||
break;
|
||||
default:
|
||||
result.notImplemented();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void enableCache(final MethodCall call) {
|
||||
useCache = call.argument(_argumentCacheEnabled);
|
||||
}
|
||||
|
||||
private void setCacheSize(final MethodCall call) {
|
||||
maxFreePoolSize = call.argument(_argumentCacheSize);
|
||||
}
|
||||
|
||||
private void enableMultiThread(final MethodCall call) {
|
||||
WorkThreadExecutor.getInstance().enableMultiThread(call.argument(_argumentMultiThreadEnabled));
|
||||
}
|
||||
|
||||
|
||||
private void initPag(final MethodCall call, final Result result) {
|
||||
String assetName = call.argument(_argumentAssetName);
|
||||
byte[] bytes = call.argument(_argumentBytes);
|
||||
String url = call.argument(_argumentUrl);
|
||||
String flutterPackage = call.argument(_argumentPackage);
|
||||
|
||||
if (bytes != null) {
|
||||
initPagPlayerAndCallback(PAGFile.Load(bytes), call, result);
|
||||
} else if (assetName != null) {
|
||||
String assetKey;
|
||||
if (flutterAssets != null) {
|
||||
if (flutterPackage == null || flutterPackage.isEmpty()) {
|
||||
assetKey = flutterAssets.getAssetFilePathByName(assetName);
|
||||
} else {
|
||||
assetKey = flutterAssets.getAssetFilePathByName(assetName, flutterPackage);
|
||||
}
|
||||
} else {
|
||||
assetKey = "";
|
||||
}
|
||||
|
||||
if (assetKey == null) {
|
||||
result.error("-1100", "asset资源加载错误", null);
|
||||
return;
|
||||
}
|
||||
WorkThreadExecutor.getInstance().post(() -> {
|
||||
PAGFile composition = PAGFile.Load(context.getAssets(), assetKey);
|
||||
handler.post(() -> initPagPlayerAndCallback(composition, call, result));
|
||||
});
|
||||
} else if (url != null) {
|
||||
DataLoadHelper.INSTANCE.loadPag(url, new Function1<byte[], Unit>() {
|
||||
@Override
|
||||
public Unit invoke(final byte[] bytes) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (bytes == null) {
|
||||
result.error("-1100", "url资源加载错误", null);
|
||||
return;
|
||||
}
|
||||
|
||||
initPagPlayerAndCallback(PAGFile.Load(bytes), call, result);
|
||||
}
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
}, DataLoadHelper.FROM_PLUGIN);
|
||||
} else {
|
||||
result.error("-1100", "未添加资源", null);
|
||||
}
|
||||
}
|
||||
|
||||
private void initPagPlayerAndCallback(PAGFile composition, MethodCall call, final Result result) {
|
||||
if (composition == null) {
|
||||
result.error("-1100", "load composition is null! ", null);
|
||||
return;
|
||||
}
|
||||
|
||||
final int repeatCount = call.argument(_argumentRepeatCount);
|
||||
final double initProgress = call.argument(_argumentInitProgress);
|
||||
final boolean autoPlay = call.argument(_argumentAutoPlay);
|
||||
final FlutterPagPlayer pagPlayer;
|
||||
final String currentId;
|
||||
if (freeEntryPool.isEmpty() || !useCache) {
|
||||
pagPlayer = new FlutterPagPlayer();
|
||||
final TextureRegistry.SurfaceTextureEntry entry = textureRegistry.createSurfaceTexture();
|
||||
currentId = String.valueOf(entry.id());
|
||||
entryMap.put(String.valueOf(entry.id()), entry);
|
||||
SurfaceTexture surfaceTexture = entry.surfaceTexture();
|
||||
|
||||
final Surface surface = new Surface(surfaceTexture);
|
||||
final PAGSurface pagSurface = PAGSurface.FromSurface(surface);
|
||||
pagPlayer.setSurface(pagSurface);
|
||||
pagPlayer.setSurfaceTexture(surfaceTexture);
|
||||
layerMap.put(String.valueOf(entry.id()), pagPlayer);
|
||||
} else {
|
||||
currentId = freeEntryPool.removeFirst();
|
||||
pagPlayer = layerMap.get(currentId);
|
||||
if (pagPlayer == null) {
|
||||
result.error("-1101", "id异常,未命中缓存!", null);
|
||||
return;
|
||||
}
|
||||
if (pagPlayer.isRelease()) {
|
||||
result.error("-1102", "PagPlayer异常!", null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WorkThreadExecutor.getInstance().post(() -> {
|
||||
pagPlayer.updateBufferSize(composition.width(), composition.height());
|
||||
pagPlayer.init(composition, repeatCount, initProgress, channel, Long.parseLong(currentId));
|
||||
final HashMap<String, Object> callback = new HashMap<String, Object>();
|
||||
callback.put(_argumentTextureId, Long.parseLong(currentId));
|
||||
callback.put(_argumentWidth, (double) composition.width());
|
||||
callback.put(_argumentHeight, (double) composition.height());
|
||||
pagPlayer.flush();
|
||||
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (autoPlay) {
|
||||
pagPlayer.start();
|
||||
}
|
||||
result.success(callback);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void start(MethodCall call) {
|
||||
FlutterPagPlayer flutterPagPlayer = getFlutterPagPlayer(call);
|
||||
if (flutterPagPlayer != null) {
|
||||
flutterPagPlayer.start();
|
||||
}
|
||||
}
|
||||
|
||||
void stop(MethodCall call) {
|
||||
FlutterPagPlayer flutterPagPlayer = getFlutterPagPlayer(call);
|
||||
if (flutterPagPlayer != null) {
|
||||
flutterPagPlayer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void pause(MethodCall call) {
|
||||
FlutterPagPlayer flutterPagPlayer = getFlutterPagPlayer(call);
|
||||
if (flutterPagPlayer != null) {
|
||||
flutterPagPlayer.pause();
|
||||
}
|
||||
}
|
||||
|
||||
void setProgress(MethodCall call) {
|
||||
double progress = call.argument(_argumentProgress);
|
||||
FlutterPagPlayer flutterPagPlayer = getFlutterPagPlayer(call);
|
||||
if (flutterPagPlayer != null) {
|
||||
flutterPagPlayer.setProgressValue(progress);
|
||||
}
|
||||
}
|
||||
|
||||
void release(MethodCall call) {
|
||||
if (useCache && freeEntryPool.size() < maxFreePoolSize) {
|
||||
FlutterPagPlayer flutterPagPlayer = layerMap.get(getTextureId(call));
|
||||
int id = call.argument(_argumentTextureId);
|
||||
if (flutterPagPlayer != null) {
|
||||
flutterPagPlayer.cancel();
|
||||
WorkThreadExecutor.getInstance().post(() -> {
|
||||
flutterPagPlayer.clear();
|
||||
if (id >= 0) {
|
||||
handler.post(() -> {
|
||||
freeEntryPool.add(id + "");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
FlutterPagPlayer flutterPagPlayer = layerMap.remove(getTextureId(call));
|
||||
if (flutterPagPlayer != null) {
|
||||
flutterPagPlayer.stop();
|
||||
flutterPagPlayer.release();
|
||||
}
|
||||
|
||||
TextureRegistry.SurfaceTextureEntry entry = entryMap.remove(getTextureId(call));
|
||||
if (entry != null) {
|
||||
entry.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<String> getLayersUnderPoint(MethodCall call) {
|
||||
FlutterPagPlayer flutterPagPlayer = getFlutterPagPlayer(call);
|
||||
|
||||
List<String> layerNames = new ArrayList<>();
|
||||
PAGLayer[] layers = null;
|
||||
if (flutterPagPlayer != null) {
|
||||
layers = flutterPagPlayer.getLayersUnderPoint(
|
||||
((Double) call.argument(_argumentPointX)).floatValue(), ((Double) call.argument(_argumentPointY)).floatValue());
|
||||
}
|
||||
|
||||
if (layers != null) {
|
||||
for (PAGLayer layer : layers) {
|
||||
layerNames.add(layer.layerName());
|
||||
}
|
||||
}
|
||||
|
||||
return layerNames;
|
||||
}
|
||||
|
||||
FlutterPagPlayer getFlutterPagPlayer(MethodCall call) {
|
||||
return layerMap.get(getTextureId(call));
|
||||
}
|
||||
|
||||
String getTextureId(MethodCall call) {
|
||||
return "" + call.argument(_argumentTextureId);
|
||||
}
|
||||
|
||||
//插件销毁
|
||||
public void onDestroy() {
|
||||
releaseAll();
|
||||
channel.setMethodCallHandler(null);
|
||||
}
|
||||
|
||||
// 释放全部资源
|
||||
public void releaseAll() {
|
||||
for (FlutterPagPlayer pagPlayer : layerMap.values()) {
|
||||
pagPlayer.release();
|
||||
}
|
||||
for (TextureRegistry.SurfaceTextureEntry entry : entryMap.values()) {
|
||||
entry.release();
|
||||
}
|
||||
freeEntryPool.clear();
|
||||
layerMap.clear();
|
||||
entryMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
|
||||
onDestroy();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package com.example.flutter_pag_plugin;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
|
||||
public class WorkThreadExecutor {
|
||||
private static volatile WorkThreadExecutor instance;
|
||||
private final ExecutorService executor;
|
||||
private final ExecutorService singleThreadExecutor;
|
||||
public static boolean multiThread = true;
|
||||
private WorkThreadExecutor() {
|
||||
executor = Executors.newCachedThreadPool();
|
||||
singleThreadExecutor = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
|
||||
public static WorkThreadExecutor getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (WorkThreadExecutor.class) {
|
||||
if (instance == null) {
|
||||
instance = new WorkThreadExecutor();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void enableMultiThread(boolean enabled) {
|
||||
multiThread = enabled;
|
||||
}
|
||||
|
||||
public void post(Runnable task) {
|
||||
if (multiThread) {
|
||||
executor.execute(task);
|
||||
} else {
|
||||
task.run();
|
||||
}
|
||||
}
|
||||
|
||||
public void postInCertainThread(Runnable task) {
|
||||
if (multiThread) {
|
||||
executor.execute(task);
|
||||
} else {
|
||||
task.run();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.example.flutter_pag_plugin.utils;
|
||||
|
||||
|
||||
/**
|
||||
* String 相关操作工具类
|
||||
* Created by Victorluo on 2016/8/11.
|
||||
*/
|
||||
public class EncodeUtil {
|
||||
|
||||
public static String bytesToHexString(byte[] src) {
|
||||
StringBuilder stringBuilder = new StringBuilder("");
|
||||
if (src == null || src.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < src.length; i++) {
|
||||
int v = src[i] & 0xFF;
|
||||
String hv = Integer.toHexString(v);
|
||||
if (hv.length() < 2) {
|
||||
stringBuilder.append(0);
|
||||
}
|
||||
stringBuilder.append(hv);
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
16
local_packages/pag-1.0.7-patched/example/README.md
Normal file
16
local_packages/pag-1.0.7-patched/example/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# flutter_pag_plugin_example
|
||||
|
||||
Demonstrates how to use the flutter_pag_plugin plugin.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
|
||||
|
||||
For help getting started with Flutter, view our
|
||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
@ -0,0 +1,54 @@
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||
if (flutterRoot == null) {
|
||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.example.flutter_pag_plugin_example"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 31
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source '../..'
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.flutter_pag_plugin_example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@ -0,0 +1,41 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.flutter_pag_plugin_example">
|
||||
<application
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:exported="true"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<!-- Displays an Android View that continues showing the launch screen
|
||||
Drawable until Flutter paints its first frame, then this splash
|
||||
screen fades out. A splash screen is useful to avoid any visual
|
||||
gap between the end of Android's launch screen and the painting of
|
||||
Flutter's first frame. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||
android:resource="@drawable/launch_background"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
</manifest>
|
||||
@ -0,0 +1,6 @@
|
||||
package com.example.flutter_pag_plugin_example;
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity;
|
||||
|
||||
public class MainActivity extends FlutterActivity {
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 544 B |
Binary file not shown.
|
After Width: | Height: | Size: 442 B |
Binary file not shown.
|
After Width: | Height: | Size: 721 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.flutter_pag_plugin_example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@ -0,0 +1,27 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.buildDir = '../build'
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
project.evaluationDependsOn(':app')
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
6
local_packages/pag-1.0.7-patched/example/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
local_packages/pag-1.0.7-patched/example/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
#Fri Jun 23 08:50:38 CEST 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
||||
@ -0,0 +1,11 @@
|
||||
include ':app'
|
||||
|
||||
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||
def properties = new Properties()
|
||||
|
||||
assert localPropertiesFile.exists()
|
||||
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
||||
BIN
local_packages/pag-1.0.7-patched/example/data/0.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/0.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/1.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/1.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/10.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/10.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/11.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/11.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/12.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/12.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/13.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/13.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/14.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/14.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/15.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/15.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/16.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/16.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/17.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/17.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/18.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/18.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/19.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/19.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/2.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/2.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/3.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/3.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/4.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/4.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/5.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/5.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/6.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/6.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/7.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/7.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/8.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/8.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/9.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/9.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/dance.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/dance.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/fans.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/fans.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/large.pag
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/large.pag
Normal file
Binary file not shown.
BIN
local_packages/pag-1.0.7-patched/example/data/test.png
Normal file
BIN
local_packages/pag-1.0.7-patched/example/data/test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>App</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.flutter.flutter.app</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>App</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>9.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -0,0 +1,2 @@
|
||||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
@ -0,0 +1,2 @@
|
||||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
39
local_packages/pag-1.0.7-patched/example/ios/Podfile
Normal file
39
local_packages/pag-1.0.7-patched/example/ios/Podfile
Normal file
@ -0,0 +1,39 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
# platform :ios, '9.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
project 'Runner', {
|
||||
'Debug' => :debug,
|
||||
'Profile' => :release,
|
||||
'Release' => :release,
|
||||
}
|
||||
|
||||
def flutter_root
|
||||
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
|
||||
unless File.exist?(generated_xcode_build_settings_path)
|
||||
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
|
||||
end
|
||||
|
||||
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||
return matches[1].strip if matches
|
||||
end
|
||||
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
|
||||
end
|
||||
|
||||
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||
|
||||
flutter_ios_podfile_setup
|
||||
|
||||
target 'Runner' do
|
||||
pod 'libpag', '4.3.68'
|
||||
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
flutter_additional_ios_build_settings(target)
|
||||
end
|
||||
end
|
||||
30
local_packages/pag-1.0.7-patched/example/ios/Podfile.lock
Normal file
30
local_packages/pag-1.0.7-patched/example/ios/Podfile.lock
Normal file
@ -0,0 +1,30 @@
|
||||
PODS:
|
||||
- Flutter (1.0.0)
|
||||
- libpag (4.2.11)
|
||||
- pag (0.0.1):
|
||||
- Flutter
|
||||
- libpag
|
||||
|
||||
DEPENDENCIES:
|
||||
- Flutter (from `Flutter`)
|
||||
- libpag (= 4.2.11)
|
||||
- pag (from `.symlinks/plugins/pag/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- libpag
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
pag:
|
||||
:path: ".symlinks/plugins/pag/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
|
||||
libpag: 38a4633825d5dd8161c4e84ca3469395f18fd639
|
||||
pag: ff30ba5e4be59380a5ddac6b183905729be15a3c
|
||||
|
||||
PODFILE CHECKSUM: 535ba81be026f5bbd306994284bed8b494a6faf5
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
@ -0,0 +1,554 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 51;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
|
||||
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
F508DC21C3FF0565306F1BA4 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4669C38C14B0AB5FAC6611C9 /* libPods-Runner.a */; };
|
||||
FB2F7A2327DB72F100A90D02 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = FB2F7A2227DB72F100A90D02 /* libc++.tbd */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
3DBDC017D50B82427AE7948E /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
4669C38C14B0AB5FAC6611C9 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
E8A1C220649E233F26806054 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
F16F7F92BD31D7A2AED62065 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
FB2F7A2227DB72F100A90D02 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
FB2F7A2327DB72F100A90D02 /* libc++.tbd in Frameworks */,
|
||||
F508DC21C3FF0565306F1BA4 /* libPods-Runner.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
01346A5F62851C62F25D73AE /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FB2F7A2227DB72F100A90D02 /* libc++.tbd */,
|
||||
4669C38C14B0AB5FAC6611C9 /* libPods-Runner.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
015AA143B0162E4AB723C99A /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F16F7F92BD31D7A2AED62065 /* Pods-Runner.debug.xcconfig */,
|
||||
E8A1C220649E233F26806054 /* Pods-Runner.release.xcconfig */,
|
||||
3DBDC017D50B82427AE7948E /* Pods-Runner.profile.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||
);
|
||||
name = Flutter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146E51CF9000F007C117D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9740EEB11CF90186004384FC /* Flutter */,
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
015AA143B0162E4AB723C99A /* Pods */,
|
||||
01346A5F62851C62F25D73AE /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146EF1CF9000F007C117D /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146F01CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
|
||||
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||
97C147021CF9000F007C117D /* Info.plist */,
|
||||
97C146F11CF9000F007C117D /* Supporting Files */,
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146F11CF9000F007C117D /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146F21CF9000F007C117D /* main.m */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
5F524B1F47B60F9D6A784898 /* [CP] Check Pods Manifest.lock */,
|
||||
9740EEB61CF901F6004384FC /* Run Script */,
|
||||
97C146EA1CF9000F007C117D /* Sources */,
|
||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
AE30BE6D72416E328EEC3D7A /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Runner;
|
||||
productName = Runner;
|
||||
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
97C146E61CF9000F007C117D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1300;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
97C146ED1CF9000F007C117D = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 97C146E51CF9000F007C117D;
|
||||
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
97C146ED1CF9000F007C117D /* Runner */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
97C146EC1CF9000F007C117D /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Thin Binary";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||
};
|
||||
5F524B1F47B60F9D6A784898 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||
};
|
||||
AE30BE6D72416E328EEC3D7A /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
97C146EA1CF9000F007C117D /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
|
||||
97C146F31CF9000F007C117D /* main.m in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
97C146FB1CF9000F007C117D /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
97C147001CF9000F007C117D /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 93R838NHT9;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterPagPluginExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
97C147031CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
97C147041CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
97C147061CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 93R838NHT9;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterPagPluginExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
97C147071CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 93R838NHT9;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterPagPluginExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
97C147031CF9000F007C117D /* Debug */,
|
||||
97C147041CF9000F007C117D /* Release */,
|
||||
249021D3217E4FDB00AE95B9 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
97C147061CF9000F007C117D /* Debug */,
|
||||
97C147071CF9000F007C117D /* Release */,
|
||||
249021D4217E4FDB00AE95B9 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user