From b7464ffca9cdf214d03e5fd13b8ae2af06194655 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 21 Nov 2024 12:29:31 -0500 Subject: [PATCH 01/56] Update backup integration tests. --- .../assets/backupTests/ad_hoc_call_00.binproto | Bin 417 -> 421 bytes .../assets/backupTests/ad_hoc_call_01.binproto | Bin 424 -> 428 bytes .../assets/backupTests/ad_hoc_call_02.binproto | Bin 424 -> 428 bytes ...oup_change_chat_multiple_update_00.binproto | Bin 0 -> 990 bytes ...oup_change_chat_multiple_update_01.binproto | Bin 0 -> 1032 bytes ...oup_change_chat_multiple_update_02.binproto | Bin 0 -> 992 bytes ...oup_change_chat_multiple_update_03.binproto | Bin 0 -> 1100 bytes ...oup_change_chat_multiple_update_04.binproto | Bin 0 -> 996 bytes ...oup_change_chat_multiple_update_05.binproto | Bin 0 -> 996 bytes ...oup_change_chat_multiple_update_06.binproto | Bin 0 -> 1012 bytes ...oup_change_chat_multiple_update_07.binproto | Bin 0 -> 1046 bytes ...oup_change_chat_multiple_update_08.binproto | Bin 0 -> 996 bytes ...t_item_group_change_chat_update_00.binproto | Bin 0 -> 946 bytes ...t_item_group_change_chat_update_01.binproto | Bin 0 -> 946 bytes ...t_item_group_change_chat_update_02.binproto | Bin 0 -> 946 bytes ...t_item_group_change_chat_update_03.binproto | Bin 0 -> 946 bytes ...t_item_group_change_chat_update_04.binproto | Bin 0 -> 946 bytes ...t_item_group_change_chat_update_05.binproto | Bin 0 -> 958 bytes ...t_item_group_change_chat_update_06.binproto | Bin 0 -> 963 bytes ...t_item_group_change_chat_update_07.binproto | Bin 0 -> 959 bytes ...t_item_group_change_chat_update_08.binproto | Bin 0 -> 948 bytes ...t_item_group_change_chat_update_09.binproto | Bin 0 -> 946 bytes ...t_item_group_change_chat_update_10.binproto | Bin 0 -> 946 bytes ...t_item_group_change_chat_update_11.binproto | Bin 0 -> 1002 bytes ...t_item_group_change_chat_update_12.binproto | Bin 0 -> 976 bytes ...t_item_group_change_chat_update_13.binproto | Bin 0 -> 966 bytes ...t_item_group_change_chat_update_14.binproto | Bin 0 -> 948 bytes ...t_item_group_change_chat_update_15.binproto | Bin 0 -> 948 bytes ...t_item_group_change_chat_update_16.binproto | Bin 0 -> 948 bytes ...t_item_group_change_chat_update_17.binproto | Bin 0 -> 948 bytes ...t_item_group_change_chat_update_18.binproto | Bin 0 -> 948 bytes ...t_item_group_change_chat_update_19.binproto | Bin 0 -> 948 bytes ...t_item_group_change_chat_update_20.binproto | Bin 0 -> 948 bytes ...t_item_group_change_chat_update_21.binproto | Bin 0 -> 946 bytes ...t_item_group_change_chat_update_22.binproto | Bin 0 -> 966 bytes ...t_item_group_change_chat_update_23.binproto | Bin 0 -> 964 bytes ...t_item_group_change_chat_update_24.binproto | Bin 0 -> 946 bytes ...t_item_group_change_chat_update_25.binproto | Bin 0 -> 946 bytes ...t_item_group_change_chat_update_26.binproto | Bin 0 -> 964 bytes ...t_item_group_change_chat_update_27.binproto | Bin 0 -> 964 bytes ...t_item_group_change_chat_update_28.binproto | Bin 0 -> 946 bytes ...t_item_group_change_chat_update_29.binproto | Bin 0 -> 946 bytes ...t_item_group_change_chat_update_30.binproto | Bin 0 -> 946 bytes ...t_item_group_change_chat_update_31.binproto | Bin 0 -> 946 bytes ...t_item_group_change_chat_update_32.binproto | Bin 0 -> 948 bytes ...t_item_group_change_chat_update_33.binproto | Bin 0 -> 948 bytes .../chat_item_payment_notification_02.binproto | Bin 764 -> 764 bytes .../chat_item_payment_notification_06.binproto | Bin 756 -> 756 bytes .../chat_item_payment_notification_07.binproto | Bin 754 -> 754 bytes .../chat_item_payment_notification_08.binproto | Bin 694 -> 694 bytes .../chat_item_payment_notification_12.binproto | Bin 686 -> 686 bytes .../chat_item_payment_notification_13.binproto | Bin 765 -> 765 bytes .../chat_item_payment_notification_14.binproto | Bin 766 -> 766 bytes .../chat_item_simple_updates_05.binproto | Bin 457 -> 466 bytes .../chat_item_standard_message_sms_02.binproto | Bin 1172 -> 993 bytes .../chat_item_standard_message_sms_05.binproto | Bin 1178 -> 999 bytes .../chat_item_standard_message_sms_08.binproto | Bin 1172 -> 993 bytes .../chat_item_standard_message_sms_11.binproto | Bin 1176 -> 997 bytes .../chat_item_standard_message_sms_14.binproto | Bin 1174 -> 995 bytes .../recipient_call_link_00.binproto | Bin 352 -> 350 bytes .../recipient_call_link_01.binproto | Bin 403 -> 403 bytes .../recipient_call_link_02.binproto | Bin 370 -> 370 bytes .../recipient_call_link_03.binproto | Bin 386 -> 384 bytes .../recipient_call_link_06.binproto | Bin 352 -> 350 bytes .../recipient_call_link_07.binproto | Bin 403 -> 403 bytes .../recipient_call_link_08.binproto | Bin 370 -> 370 bytes .../recipient_call_link_09.binproto | Bin 386 -> 384 bytes .../recipient_call_link_12.binproto | Bin 352 -> 350 bytes .../recipient_call_link_13.binproto | Bin 403 -> 403 bytes .../recipient_call_link_14.binproto | Bin 370 -> 370 bytes .../recipient_call_link_15.binproto | Bin 386 -> 384 bytes .../backup/v2/ArchiveImportExportTests.kt | 10 ++++++++++ .../v2/exporters/ChatItemArchiveExporter.kt | 2 +- 73 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_00.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_01.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_02.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_03.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_04.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_05.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_06.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_07.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_08.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_09.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_10.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_11.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_12.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_13.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_14.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_15.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_16.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_17.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_18.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_19.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_20.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_21.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_22.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_23.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_24.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_25.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_26.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_27.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_28.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_29.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_30.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_31.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_32.binproto create mode 100644 app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_33.binproto diff --git a/app/src/androidTest/assets/backupTests/ad_hoc_call_00.binproto b/app/src/androidTest/assets/backupTests/ad_hoc_call_00.binproto index 80fc79cc364ef8c8ce36e1b928386dbb958ac64b..ef6427e566f8acbecdbcac59f853b99f2b9842d0 100644 GIT binary patch delta 35 rcmZ3;yp(x^J0q))kQWE{WG_Y!K>;H^j+N{Fo`1Ax##%Q4ZV5&Jw&@D` delta 31 ncmZ3=ypVZ=J0r7~kjG>nMh|{IBOZ>G>;9g9v}nd!Hwi`nn9mBr diff --git a/app/src/androidTest/assets/backupTests/ad_hoc_call_01.binproto b/app/src/androidTest/assets/backupTests/ad_hoc_call_01.binproto index 88edcb3abc81cab7de2fc329c707a7cef29a1dd9..cd678780b5727914133decaee3b2bb55895a7c92 100644 GIT binary patch delta 42 zcmV+_0M-Af1FQqEM*#&)5=saQlS%0NMYn1E>SAM*#y$5=fIw0Z0}VG7|`p>zt*P`_j{F7y%%kucWBZGWIhL%m4rY diff --git a/app/src/androidTest/assets/backupTests/ad_hoc_call_02.binproto b/app/src/androidTest/assets/backupTests/ad_hoc_call_02.binproto index ed53188fd68e4bd14f5cd35f7e90d5a3d46a5a99..2633261e7531060ebe67d3f317982bbcfda1c0d8 100644 GIT binary patch delta 42 zcmV+_0M-Af1FQqEM*#&)5=saQlS%0NMYn1E>SAM*#y$5=fIw0Z0}VG7|{&`lHaaW7y%&TjKIdIF#1{!`Tzg` diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..00841df3c9e50e53afbe329cd9f5a83275860806 GIT binary patch literal 990 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}XUnDHzCLlxTl9`}B3QAciUzAtx>oE+I?{xI~1oib`P- F1ppSvq44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}b?HsTRm!lsMe{=JFqtAcm?up&Tv+DLG6Fgt$UW6r4+w xGIKH$xm2Z;v8dp8&d*CPO;vD9EH24R$;{+Zl#<7yjN3QAC@E9HB{Mm(1OP+&^XLEo literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..534337a0efe71ea6ab2dbec3e6869a7ddda21651 GIT binary patch literal 992 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}XUvDHO3 IqJ>2i0PL#eGynhq literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..113929678174271075bb45037c7ebd87486ef293 GIT binary patch literal 1100 zcmaKqYiQF@6vuPYrj40OyjZPe$fb(TZgkz0BGnICp9_l9DN508H|=$eHc4%gS%!#@ z4=M=0=L39CR1_3Hx(}lJAWk+!QRfqNpTq|uh#*Q$S4CP!!-4$ox#ylA=TXgrIPl}l z>F;$uyzl1hw}&@oyLv|*oc8K^^5Vv45I%r_347qmr=DoT z;*;l3)ZM$#cf9YR{%u$NyX4^N&y4kK0F9J&nY65@rWp)}ePIF9wzbBC(bq+8uzQQN z=xgVFy7K#;?UQzU-g^cn{|X*LBDoFcn8(rad+u=FfrgGRUZ>v~wX+1A3+*(KX#)P( zoxD3^x-WQsdf&xG_x3JmqJFZbdRc$$&Ff1``{@MaSk*M&3zB3P1YW>U1tFNZzcLCW z3UUUWbfYc_&(HUkYRT8DyuhFNG9&SbVGi85!y+Yca=sqRCt|qi?nWR{>oH zUhs9eLr^%bYwaYJQVeX66ve8dDJo892sNl?;si-6vSnbJ#R(;D5(8T%QLT(n y9|#STtx_dfMYZEIG;Y#O7PUp%N+?825Gqs!rVR52X}V!%EETJIf@X-RSjKO6Y7gT8 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..e851a0a01022abf7454e6527f62a56544b68eabf GIT binary patch literal 996 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}XUtDH6zFlxTl9`}B3QAciV0ArCGwBWxBh;t*%TAq44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}XUtDH6zFlsMe{=JFqtAciV0ArCGwtQIih5NE<6&I|yo CB<219 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_06.binproto new file mode 100644 index 0000000000000000000000000000000000000000..9613d1688a21873dabda4c5f390f3b42d911726d GIT binary patch literal 1012 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}XUuDHF(GlxTl9`}B3QAcm?ap$IN9Cu|lla)~$zVOq?k O;iZNtijn?+c>@3{_vi5d literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..e9a76f2174b64c54a7eecb67ff054431d2a1de3b GIT binary patch literal 1046 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}b?wQK^#=$Yhi_-2CS9ACn-4sw$xhE;TO|Oe=tC3N^wR RxzwVtt3z`JPIYMJ0RWD@>iGZw literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..17578455f714b783046c584313857c4562fdf9bf GIT binary patch literal 996 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}XUtDH6zFlxTl9`}B3QAciV0ArCIGBCHnR5NE_8&IACm C0OnW# literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_00.binproto new file mode 100644 index 0000000000000000000000000000000000000000..80ec8e85f0ccd3c5a15e0152d52bbb461be5430a GIT binary patch literal 946 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yf`Ngc>wlxTl9`}B3QAciUlAu%oyE+Nbm1pwmm;?@8F literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_01.binproto new file mode 100644 index 0000000000000000000000000000000000000000..e2f546f15a1daa457e9426a5c394468c09abf0eb GIT binary patch literal 946 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yf`Ngc>wlsMe{=JFqtAciUlAu%oyE+Ne51_0rL;!pqp literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_02.binproto new file mode 100644 index 0000000000000000000000000000000000000000..724d353d0b7332552c0576f7715f3e124befc5d7 GIT binary patch literal 946 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yf`Ngc>wlxTl9`}B3QAciUlAu%oyAtB5Z1pwoc;@$uN literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_03.binproto new file mode 100644 index 0000000000000000000000000000000000000000..f5b456e95bd527ee5b43e17ab4875986c66ac7cd GIT binary patch literal 946 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yf`Ngc>wlsMe{=JFqtAciUlAu%oyAtB7@1_0tB;#dFx literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_04.binproto new file mode 100644 index 0000000000000000000000000000000000000000..b33d825c9d3c6316b274a6175921ba41f5def386 GIT binary patch literal 946 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yf`Ngc>wlxTl9`}B3QAciUlAu%oyDIv@h1pwqS;^qJV literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_05.binproto new file mode 100644 index 0000000000000000000000000000000000000000..66bdbb63d7d89f1c948b36f7bda43ddbbbb0b932 GIT binary patch literal 958 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}d2d$r#9BlsMe{=JFqtAciUxAtf#aDLE|B%@tar;9Q!N InUk3a0H1^AW&i*H literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_06.binproto new file mode 100644 index 0000000000000000000000000000000000000000..e8bccd38d0f2393e1f4681454db2068db493ee21 GIT binary patch literal 963 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}d2T$pXk=lxTl9`}B3QAciVUA$2ZQDP=4vir+auFTFHX N!7Z`4Br_#5696{!=^Fq5 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_07.binproto new file mode 100644 index 0000000000000000000000000000000000000000..ea5ab2f54f70f1b8abbd29a530c8bb74334e561c GIT binary patch literal 959 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}d2V$ppw?lsMe{=JFqtAciVcA!RN_DS0f>&F!0Cl$5F9 Jl9`-X0sx2!=E(p6 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_08.binproto new file mode 100644 index 0000000000000000000000000000000000000000..50c03903355988131d5c30d2b5b1058332c2ee24 GIT binary patch literal 948 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ ymJ9@-YEffOfRRIrje}W$D}Yg3NfXFmlxTl9`}B3QAciU_Aqg%qC6EOeMgahp=; literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_09.binproto new file mode 100644 index 0000000000000000000000000000000000000000..f86713930725b38ce4b483e81f884864d6abcaab GIT binary patch literal 946 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yf`Ngc>wlsMe{=JFqtAciUlAu%oyB_Yh{1_0w?;%EQ> literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_10.binproto new file mode 100644 index 0000000000000000000000000000000000000000..ba9f823da00da968909ec03a9cc7d08eed11b486 GIT binary patch literal 946 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yf`Ngc>wlxTl9`}B3QAciUlAu%oyEg{Sl1pwu8;`RUl literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_11.binproto new file mode 100644 index 0000000000000000000000000000000000000000..96bae71a5a4a176e436692175668d39911235f33 GIT binary patch literal 1002 zcmaKqZAep57{||Zx9e_KliQ`$x=0+RR8E;M$%xg5T$XytT3IN=)N|*}t+w5rNq>uTj{16&S6xDhq^;2XLK?qT-J6FVp?C`*O&i_0-Kc1Ho2Dtdo z>(}Nh1bph@rm6D>V?*Oh&lZ0ecHdY_nF^P@Y7(c4cCESE_U^$PuwQ`1ggSHY>!_=~ z?#kdm#n`Qh%M;I)A15lOeKSpSDEcPO=Shl0+V;zuW_LIQ2aDy-7WG;FFh|df92RT8 z_dk{Q%=aBF>@|Nj$BTa3&+#021UJ!3*UG-fv?X5O{mo*u8eMu8n{k_-##zkb*%9B9 zs`Y|>aQ(#X+Od;c8)YkLF4;Nh`SkHl!=${5H|%Y!v4AM*1%U-Htb=mcaJs++#08QI zymjD)W@rZ=gdqk!0KGsBfQbP!LMV~+DBVjecm^{{phOODWWa`!7gdQ0c!4okSFoHO zo^}MvVvzWD#>ltmpX{MJMIId3j_@3t-yoObg=Uvxy&=MCpQeRXXIYsXBf)S$wtJOe8R;S#iP#T>J8d}( z#ZeYxIXIX9+lK!G=colWV-2{!vFVfJfDv*S1IN07Sle_5EThL~U;L`tjk?|Z4tBdu zTx8r1uM+Aafq-AdDhbFq=nwgWI2<9eDu*=OLBf71s$w~YJN#aas5q*TU^MavCYkuJ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_12.binproto new file mode 100644 index 0000000000000000000000000000000000000000..0ac35d5b0d5495fab1ca8f6e1aee89fe45c68a5a GIT binary patch literal 976 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}d2m$rZ?8lxTl9`}B3QAciV4ArmemEdwkmN+vh4A~UzN ZSfMC2xg@o)G!@7wC{4*s%u7j31prW_?)(4% literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_13.binproto new file mode 100644 index 0000000000000000000000000000000000000000..908889f30ecd04e715e58d5958ecaf27dfd85f60 GIT binary patch literal 966 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}d2f$r{LDlsMe{=JFqtAciU(AuTQqEj29BEtHpETvAk; QtB{+Yl3J8ll37{|0QLLofdBvi literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_14.binproto new file mode 100644 index 0000000000000000000000000000000000000000..d3ded9b3edd5da783f02e26755a5a14347628179 GIT binary patch literal 948 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ ymJ9@-YEffOfRRIrje}W$D}Yg3NfXFmlxTl9`}B3QAciU_Aqg%qBaj6cMgah=x#JrE literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_15.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_15.binproto new file mode 100644 index 0000000000000000000000000000000000000000..6da2f58114b513e0945792be41b3bf0f80d4bc4f GIT binary patch literal 948 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ ymJ9@-YEffOfRRIrje}W$D}Yg3NfXFmlsMe{=JFqtAciU_Aqg%qBd`T%nwS8qKH`=D literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_16.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_16.binproto new file mode 100644 index 0000000000000000000000000000000000000000..ee19b39d19ba53d04e850fdde510a28f79b772f4 GIT binary patch literal 948 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}Yg3NfXFmlxTl9`}B3QAciU_Aqg%qBaj8?Mlk~bt-IqL literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_17.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_17.binproto new file mode 100644 index 0000000000000000000000000000000000000000..3a984944c0732999c23a72cb04d241f811b11369 GIT binary patch literal 948 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ vmJ9@-YEffOfRRIrje}W$D}Yg3NfXFmlsMe{=JFqtAciU_Aqg%q>_!0qxMbsS literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_18.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_18.binproto new file mode 100644 index 0000000000000000000000000000000000000000..4f37a1f216fa52ded85d032074fbff6252eeba85 GIT binary patch literal 948 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yg3NfXFmlxTl9`}B3QAciU_Aqg%qhy`exm;j;k;!ywq literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_19.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_19.binproto new file mode 100644 index 0000000000000000000000000000000000000000..9754cd8d28db4c36deac843e668279a42035f161 GIT binary patch literal 948 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ wmJ9@-YEffOfRRIrje}W$D}Yg3NfXFmlsMe{=JFqtAciU_Aqg%qtVS^d0Jvu3asU7T literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_20.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_20.binproto new file mode 100644 index 0000000000000000000000000000000000000000..018c98dff4d302bbf4c24f6e31b9e42d6faaacf9 GIT binary patch literal 948 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ ymJ9@-YEffOfRRIrje}W$D}Yg3NfXFmlxTl9`}B3QAciU_Aqg%qCy)ggMgah?8RIJe literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_21.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_21.binproto new file mode 100644 index 0000000000000000000000000000000000000000..5a956278c7ad446dc0acb25121155df46b0a3781 GIT binary patch literal 946 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yf`Ngc>wlsMe{=JFqtAciUlAu%oyCn3z}1_0&a;)nnM literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_22.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_22.binproto new file mode 100644 index 0000000000000000000000000000000000000000..1e2a71c894573f22c6d127204e83db6084c20a9d GIT binary patch literal 966 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}d2f$r{LDlxTl9`}B3QAciU(AuTQqFEvaHFj5pSwF3aR C%H)aw literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_23.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_23.binproto new file mode 100644 index 0000000000000000000000000000000000000000..c3cf8bd64296f59401365b144e679173e10cf87b GIT binary patch literal 964 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}d2j$r8w5lsMe{=JFqtAciU}Aq_4yFBMD+fGG+!x)}lR CXXD=h literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_24.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_24.binproto new file mode 100644 index 0000000000000000000000000000000000000000..f2faf4eb1388d46c84765157940d127529f5c262 GIT binary patch literal 946 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yf`Ngc>wlxTl9`}B3QAciUlAu%qIAR){Y1pw%h;~oG2 literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_25.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_25.binproto new file mode 100644 index 0000000000000000000000000000000000000000..1c77bee33ca74f90854530a6c3093bf7ccb0d138 GIT binary patch literal 946 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yf`Ngc>wlsMe{=JFqtAciUlAu%qIAR)}?1_0+G;+Oyc literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_26.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_26.binproto new file mode 100644 index 0000000000000000000000000000000000000000..7cf9ec21c18933a007b2f4b1ed5fa56c7489002b GIT binary patch literal 964 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}d2j$r8w5lxTl9`}B3QAciU}Aq_6IC>2Z#Fj5pF0PfA? AbpQYW literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_27.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_27.binproto new file mode 100644 index 0000000000000000000000000000000000000000..a3b1fe8349158c0ded45d542cdc04b4849d2e246 GIT binary patch literal 964 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}d2j$r8w5lsMe{=JFqtAciU}Aq_6IC>2Z#fGG+!x)}lT C$K&q+ literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_28.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_28.binproto new file mode 100644 index 0000000000000000000000000000000000000000..06dd4f18d03b844eabab8e467a6b1ef8349029a3 GIT binary patch literal 946 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yf`Ngc>wlxTl9`}B3QAciUlAu%qIBq7Wc1pw*N<1PRI literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_29.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_29.binproto new file mode 100644 index 0000000000000000000000000000000000000000..4fe38766a16473eb52a49cc37118d7545723a309 GIT binary patch literal 946 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yf`Ngc>wlsMe{=JFqtAciUlAu%qIBq7Y`1_0<{;-~-s literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_30.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_30.binproto new file mode 100644 index 0000000000000000000000000000000000000000..f0726b85a4f54f2a04ff678691d6a2c022676792 GIT binary patch literal 946 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yf`Ngc>wlxTl9`}B3QAciUlAu%qIEFsJk1pw-D<2C>Q literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_31.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_31.binproto new file mode 100644 index 0000000000000000000000000000000000000000..49bb4169b26da74aa124765a9a0512356582094a GIT binary patch literal 946 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yf`Ngc>wlsMe{=JFqtAciUlAu%qIEFsM31_0>-;;;Y! literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_32.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_32.binproto new file mode 100644 index 0000000000000000000000000000000000000000..02893198bd4a8a3a7038cd24a5fa2a493906ff25 GIT binary patch literal 948 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ zmJ9@-YEffOfRRIrje}W$D}Yg3NfXFmlxTl9`}B3QAciU_Aqg(AB9H~>Mlk^ZvpVBk literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_33.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_33.binproto new file mode 100644 index 0000000000000000000000000000000000000000..ac7aaf22b0955818fae55d9465738d6c897227ba GIT binary patch literal 948 zcmdPqU=+CVYr*{QhEfXsyKEj#$*x#(ig%Lc-3q44 z*Nj{WiA)n$zdh{a=`nZlEW?va&(1u1DgSG`(W8vV;qMqqZ{85%Ny<-3j897~DbX`C zlQQE{NKKC^zQDIZfc5e28ZY-x(@v*`eD7>j?ht(`dPC)}-ee(x#Dk2B7_K^rcOGJu zxZzp#L4rkz#R+JN7Kc#a0X{LP;EKLUAAq zPBEfefFi=AFdbq6he8ux6vgVCSd^cW#O{@!kyo6b2k`|v#1{;=&@Dj`VN#d@v4qFq zFkTeJ?2?$5#OhR(Uk9OH6S8ffC(&$ xmJ9@-YEffOfRRIrje}W$D}Yg3NfXFmlsMe{=JFqtAciU_Aqg(ABCrMMngFo9;?)2E literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_02.binproto index dc276ac6ca2e44caaf0cb18a3416060764b8eec5..f0ec663b8ff64a35d01cdf7a0273bf218ecaab12 100644 GIT binary patch delta 46 zcmV+}0MY;a1^flDO9CPiAX)om8kIFA-Ce1wc`x!mG0GrFKKGznmKD^kz4n+~l#^`& EkI)|#1^@s6 delta 46 zcmV+}0MY;a1^flDO9CPaApI(*D+&XMW9zU#?SzK(8EbB3YzcQ3Z`1nq9vJ5b&y#He EkHzs65C8xG diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_06.binproto index 20a8ae038620c11deb4dcf431394a3216456997e..42a029b1d4ecf02fc94d6ede5973fc13fc263bba 100644 GIT binary patch delta 46 zcmV+}0MY;S1@r~5Py!+nAXs^oJ|%j&M@O}4<$pIW8<+Cf>R>A7$cz8GXn5JpRFiT7 Eg4b^r0RR91 delta 46 zcmV+}0MY;S1@r~5Py!+fAT|(g>5laq1+ Eg28{KYASLf}aC{0HRHuqlYMJXNmv6GAozFBWppP|pnVC%~QIlf= EkGYByJOBUy delta 46 zcmV+}0MY;Q1@Z;3K>{KQAds!Ax{7kXyG^+{;4~`WHQl~{R>A7$cz8GXn5JpRFfPx# delta 46 zcmV+}0MY-h1+E3K3IZYuAT|(g>5lank0 Ef}GhBe*gdg diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_13.binproto index f3520f18db3b2fd03441a1bca855103ceedde718..89e82218d45a6c92cd92f10997cb9f6f07f54248 100644 GIT binary patch delta 46 zcmV+}0MY;b1^orEOadYjASLf}aC{0HRHuqlYMJXNmv6GAozFBWppP|pnVC%~QIl>0 EkH1V4XaE2J delta 46 zcmV+}0MY;b1^orEOadYbAds!Ax{7kXyG^+{;4~`WHQl~{B@vjH}&1R|*f2m%lR7_iZt?ej1=0ZIl61PBBg09ug*5(f$eaAavp dS{h&qTp|i-ZDD6+FKTdQXOo`;Q?uFur2+5c6;uEK delta 160 zcmV;R0AK&%2b2l0vjHaG1tQ!92m%lR7_iZt?ej1=0ZIl61PBBg09wQa5(f$eaAavp zTC+O>rU4)ta0+Z94`Fa{Y-wX*bZKvHFK}dLIthALS4MGRV|sU!=>bzipaBY<0U`=% zZDD6+FKTdQXF3dIdPq!9Z);6jP;F{J@)St)7+M@mWJyn0KTbtCSu|9Ba8giGYDF`zC3JF

=h9{>OV diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_08.binproto index 0c3645f6948d4759340bb4306b457319eeade723..ccab74b8da3ba83fb9b861c53db17194e7b58780 100644 GIT binary patch delta 71 zcmV-N0J#5@3E>B@vjH}&1R|*f2m%lR7`D-z?ej1=0ZIl61PBB&09ug*5(f$eaAavp dS{h&qTp|i-ZDD6+FKTdQXOo`;Q?uFur2+KH6>rU4)ta0+Z94`Fa{Y-wX*bZKvHFK}dLIthALS4MGRV|sU!=>bzipaBY<0U`=% zZDD6+FKTdQXF3dIdPq!9Z);6jP;F{J@)St)7+M@mWJyn0KTbtCSu|9Ba8giGYDFfi)fg diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_14.binproto index 986de31c23dc0a596193d9959c96c0c19381a643..bd83375f674703a310f171a16e40edde88fb2a31 100644 GIT binary patch delta 73 zcmV-P0Ji^@3F8N_vjI4+1R|>h2m%lR7`)M(?ej1=0ZIr81_%U70tfPx# delta 16 Ycmeyw^oeOh79$6vM#GEU$7kyS067r`=l}o! diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_03.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_03.binproto index 4e2dc336bff676ec66c449f26c3cdbfde90f1918..2fcbfce7d4013affd5ee4083ed620e32994d597f 100644 GIT binary patch delta 17 YcmZo-ZeZTv&d6#ZWM*YD*@w{t04CN12mk;8 delta 20 bcmZo*Zerfx&d6#dWMO4C*@w}DNr4FfGj0Sj diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_06.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_06.binproto index fb19a76e1adbeed7587583de554236fc050174a5..e939040d5e7e7c6a70861a702246bd8317691a7e 100644 GIT binary patch delta 31 ncmaFBbdPC+JEORmkcgEKmw?WpEjPx# delta 16 Ycmeyw^oeOh79$6vM#GEU$7kyS067r`=l}o! diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_09.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_09.binproto index 4e2dc336bff676ec66c449f26c3cdbfde90f1918..2fcbfce7d4013affd5ee4083ed620e32994d597f 100644 GIT binary patch delta 17 YcmZo-ZeZTv&d6#ZWM*YD*@w{t04CN12mk;8 delta 20 bcmZo*Zerfx&d6#dWMO4C*@w}DNr4FfGj0Sj diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_12.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_12.binproto index fb19a76e1adbeed7587583de554236fc050174a5..e939040d5e7e7c6a70861a702246bd8317691a7e 100644 GIT binary patch delta 31 ncmaFBbdPC+JEORmkcgEKmw?WpEjPx# delta 16 Ycmeyw^oeOh79$6vM#GEU$7kyS067r`=l}o! diff --git a/app/src/androidTest/assets/backupTests/recipient_call_link_15.binproto b/app/src/androidTest/assets/backupTests/recipient_call_link_15.binproto index 4e2dc336bff676ec66c449f26c3cdbfde90f1918..2fcbfce7d4013affd5ee4083ed620e32994d597f 100644 GIT binary patch delta 17 YcmZo-ZeZTv&d6#ZWM*YD*@w{t04CN12mk;8 delta 20 bcmZo*Zerfx&d6#dWMO4C*@w}DNr4FfGj0Sj diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt index dd2a46e537..373b162685 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt @@ -88,6 +88,16 @@ class ArchiveImportExportTests { runTests { it.startsWith("chat_item_group_call_update_") } } +// @Test + fun chatItemGroupChangeChatMultipleUpdate() { + runTests { it.startsWith("chat_item_group_change_chat_multiple_update_") } + } + +// @Test + fun chatItemGroupChangeChatUpdate() { + runTests { it.startsWith("chat_item_group_change_chat_") } + } + // @Test fun chatItemIndividualCallUpdate() { runTests { it.startsWith("chat_item_individual_call_update_") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt index 8777585da2..d43bc84c2c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt @@ -394,7 +394,7 @@ private fun BackupMessageRecord.toBasicChatItemBuilder(selfRecipientId: Recipien expiresInMs = if (record.expiresIn > 0) record.expiresIn else 0 revisions = emptyList() sms = record.type.isSmsType() - if (record.type.isDirectionlessType()) { + if (record.type.isDirectionlessType() || record.messageExtras?.gv2UpdateDescription != null) { directionless = ChatItem.DirectionlessMessageDetails() } else if (MessageTypes.isOutgoingMessageType(record.type) || record.fromRecipientId == selfRecipientId.toLong()) { outgoing = ChatItem.OutgoingMessageDetails( From 91b411abb5178376d8af0d1fdfa8ad9cd14cc87c Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Thu, 21 Nov 2024 14:55:54 -0400 Subject: [PATCH 02/56] Add new rules to log scrubber. --- .../securesms/service/webrtc/links/CallLinkRoomId.kt | 6 +++++- .../java/org/signal/core/util/logging/Scrubber.kt | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId.kt index ca2fcd3624..d6ff220261 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId.kt @@ -10,6 +10,7 @@ import kotlinx.parcelize.Parcelize import okio.ByteString import okio.ByteString.Companion.toByteString import org.signal.core.util.Base64 +import org.signal.core.util.Hex import org.signal.core.util.Serializer import org.signal.ringrtc.CallLinkRootKey @@ -34,8 +35,11 @@ class CallLinkRoomId private constructor(private val roomId: ByteArray) : Parcel return roomId.contentHashCode() } + /** + * Prints call link room id as a hex string, explicitly for logging. + */ override fun toString(): String { - return DatabaseSerializer.serialize(this) + return Hex.toStringCondensed(roomId) } object DatabaseSerializer : Serializer { diff --git a/core-util-jvm/src/main/java/org/signal/core/util/logging/Scrubber.kt b/core-util-jvm/src/main/java/org/signal/core/util/logging/Scrubber.kt index 3766b5b5e8..288bd77015 100644 --- a/core-util-jvm/src/main/java/org/signal/core/util/logging/Scrubber.kt +++ b/core-util-jvm/src/main/java/org/signal/core/util/logging/Scrubber.kt @@ -75,6 +75,8 @@ object Scrubber { private val CALL_LINK_PATTERN = Pattern.compile("([bBcCdDfFgGhHkKmMnNpPqQrRsStTxXzZ]{4})(-[bBcCdDfFgGhHkKmMnNpPqQrRsStTxXzZ]{4}){7}") private const val CALL_LINK_CENSOR_SUFFIX = "-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX" + private val CALL_LINK_ROOM_ID_PATTERN = Pattern.compile("[0-9a-f]{61}([0-9a-f]{3})") + @JvmStatic @Volatile var identifierHmacKeyProvider: () -> ByteArray? = { null } @@ -97,6 +99,7 @@ object Scrubber { .scrubIpv4() .scrubIpv6() .scrubCallLinkKeys() + .scrubCallLinkRoomIds() } private fun CharSequence.scrubE164(): CharSequence { @@ -192,6 +195,15 @@ object Scrubber { } } + private fun CharSequence.scrubCallLinkRoomIds(): CharSequence { + return scrub(this, CALL_LINK_ROOM_ID_PATTERN) { matcher, output -> + val match = matcher.group(1) + output + .append("[REDACTED]") + .append(match) + } + } + private fun scrub(input: CharSequence, pattern: Pattern, processMatch: MatchProcessor): CharSequence { val output = StringBuilder(input.length) val matcher: Matcher = pattern.matcher(input) From 767261152af7c63691da1894f1cdd7afc2620d0f Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 21 Nov 2024 13:55:12 -0500 Subject: [PATCH 03/56] Potential fix for a backup validation error. --- .../v2/exporters/ChatItemArchiveExporter.kt | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt index d43bc84c2c..84dcbba7eb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt @@ -78,7 +78,6 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreview import org.thoughtcrime.securesms.mms.QuoteModel import org.thoughtcrime.securesms.payments.FailureReason import org.thoughtcrime.securesms.payments.State -import org.thoughtcrime.securesms.payments.proto.PaymentMetaData import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.util.JsonUtils import org.whispersystems.signalservice.api.push.ServiceId.ACI @@ -882,20 +881,18 @@ private fun PaymentTable.PaymentTransaction.toRemoteTransactionDetails(): Paymen timestamp = this.timestamp, blockIndex = this.blockIndex, blockTimestamp = this.blockTimestamp, - mobileCoinIdentification = this.paymentMetaData.mobileCoinTxoIdentification?.toRemote(), + mobileCoinIdentification = this.paymentMetaData.mobileCoinTxoIdentification?.let { + PaymentNotification.TransactionDetails.MobileCoinTxoIdentification( + publicKey = it.publicKey.takeIf { this.direction.isReceived } ?: emptyList(), + keyImages = it.keyImages.takeIf { this.direction.isSent } ?: emptyList() + ) + }, transaction = this.transaction?.toByteString(), receipt = this.receipt?.toByteString() ) ) } -private fun PaymentMetaData.MobileCoinTxoIdentification.toRemote(): PaymentNotification.TransactionDetails.MobileCoinTxoIdentification { - return PaymentNotification.TransactionDetails.MobileCoinTxoIdentification( - publicKey = this.publicKey, - keyImages = this.keyImages - ) -} - private fun State.toRemote(): PaymentNotification.TransactionDetails.Transaction.Status { return when (this) { State.INITIAL -> PaymentNotification.TransactionDetails.Transaction.Status.INITIAL From f16827d9eceba6680ed38ae03632a9d8932c9854 Mon Sep 17 00:00:00 2001 From: Jim Gustafson Date: Thu, 21 Nov 2024 12:41:07 -0800 Subject: [PATCH 04/56] Force relays only if remote user is not a 'Signal Connection'. --- .../securesms/service/webrtc/IncomingCallActionProcessor.java | 2 +- .../securesms/service/webrtc/OutgoingCallActionProcessor.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java index 7ab9100ea6..4491faf7e4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java @@ -94,7 +94,7 @@ public IncomingCallActionProcessor(@NonNull WebRtcInteractor webRtcInteractor) { return currentState; } - boolean hideIp = !activePeer.getRecipient().isSystemContact() || callSetupState.isAlwaysTurnServers(); + boolean hideIp = !activePeer.getRecipient().isProfileSharing() || callSetupState.isAlwaysTurnServers(); VideoState videoState = currentState.getVideoState(); CallParticipant callParticipant = Objects.requireNonNull(currentState.getCallInfoState().getRemoteCallParticipant(activePeer.getRecipient())); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/OutgoingCallActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/OutgoingCallActionProcessor.java index 4d85bdf70d..1f30e0fbae 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/OutgoingCallActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/OutgoingCallActionProcessor.java @@ -144,6 +144,7 @@ public OutgoingCallActionProcessor(@NonNull WebRtcInteractor webRtcInteractor) { return currentState; } + boolean hideIp = !activePeer.getRecipient().isProfileSharing() || callSetupState.isAlwaysTurnServers(); VideoState videoState = currentState.getVideoState(); CallParticipant callParticipant = Objects.requireNonNull(currentState.getCallInfoState().getRemoteCallParticipant(activePeer.getRecipient())); @@ -157,7 +158,7 @@ public OutgoingCallActionProcessor(@NonNull WebRtcInteractor webRtcInteractor) { callParticipant.getVideoSink(), videoState.requireCamera(), callSetupState.getIceServers(), - callSetupState.isAlwaysTurnServers(), + hideIp, NetworkUtil.getCallingDataMode(context), AUDIO_LEVELS_INTERVAL, currentState.getCallSetupState(activePeer).isEnableVideoOnCreate()); From bae86d127faea386a363a53b5228a189ea908698 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 22 Nov 2024 10:23:23 -0400 Subject: [PATCH 05/56] Add "your media will be deleted today" mechanism based off last checkin time and media TTL. --- .../securesms/backup/v2/BackupRepository.kt | 48 ++++++++++++++++++- .../backup/v2/ui/BackupAlertBottomSheet.kt | 1 + .../backup/v2/ui/BackupAlertDelegate.kt | 7 ++- .../subscription/InAppPaymentsRepository.kt | 27 ----------- .../InAppPaymentsBottomSheetDelegate.kt | 11 ----- .../securesms/jobs/BackupRefreshJob.kt | 1 + .../jobs/InAppPaymentRedemptionJob.kt | 1 + .../securesms/keyvalue/BackupValues.kt | 12 +++++ .../securesms/keyvalue/InAppPaymentValues.kt | 7 +-- 9 files changed, 65 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index 640d77181f..c2a4d1271c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -101,6 +101,7 @@ import java.time.ZonedDateTime import java.util.Locale import java.util.concurrent.atomic.AtomicLong import kotlin.time.Duration.Companion.days +import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.milliseconds import org.signal.libsignal.messagebackup.MessageBackupKey as LibSignalMessageBackupKey @@ -237,6 +238,49 @@ object BackupRepository { return System.currentTimeMillis().milliseconds > SignalStore.backup.nextBackupFailureSheetSnoozeTime } + fun snoozeYourMediaWillBeDeletedTodaySheet() { + SignalStore.backup.lastCheckInSnoozeMillis = System.currentTimeMillis() + } + + /** + * Whether or not the "Your media will be deleted today" sheet should be displayed. + */ + suspend fun shouldDisplayYourMediaWillBeDeletedTodaySheet(): Boolean { + if (shouldNotDisplayBackupFailedMessaging() || !SignalStore.backup.optimizeStorage) { + return false + } + + val paidType = try { + withContext(Dispatchers.IO) { + getPaidType() + } + } catch (e: IOException) { + Log.w(TAG, "Failed to retrieve paid type.", e) + return false + } + + if (paidType == null) { + Log.w(TAG, "Paid type is not available on this device.") + return false + } + + val lastCheckIn = SignalStore.backup.lastCheckInMillis.milliseconds + if (lastCheckIn == 0.milliseconds) { + Log.w(TAG, "LastCheckIn has not yet been set.") + return false + } + + val lastSnoozeTime = SignalStore.backup.lastCheckInSnoozeMillis.milliseconds + val now = System.currentTimeMillis().milliseconds + val mediaTtl = paidType.mediaTtl + val mediaExpiration = lastCheckIn + mediaTtl + + val isNowAfterSnooze = now < lastSnoozeTime || now >= lastSnoozeTime + 4.hours + val isNowWithin24HoursOfMediaExpiration = now < mediaExpiration && (mediaExpiration - now) <= 1.days + + return isNowAfterSnooze && isNowWithin24HoursOfMediaExpiration + } + private fun shouldNotDisplayBackupFailedMessaging(): Boolean { return !RemoteConfig.messageBackups || !SignalStore.backup.areBackupsEnabled || !SignalStore.backup.hasBackupBeenUploaded } @@ -1178,7 +1222,7 @@ object BackupRepository { } } - private suspend fun getFreeType(): MessageBackupsType { + private suspend fun getFreeType(): MessageBackupsType.Free { val config = getSubscriptionsConfiguration() return MessageBackupsType.Free( @@ -1186,7 +1230,7 @@ object BackupRepository { ) } - private suspend fun getPaidType(): MessageBackupsType? { + private suspend fun getPaidType(): MessageBackupsType.Paid? { val config = getSubscriptionsConfiguration() val product = AppDependencies.billingApi.queryProduct() ?: return null val backupLevelConfiguration = config.backupConfiguration.backupLevelConfigurationMap[SubscriptionsConfiguration.BACKUPS_LEVEL] ?: return null diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt index ba6c4a2bcf..436c410271 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt @@ -154,6 +154,7 @@ class BackupAlertBottomSheet : UpgradeToPaidTierBottomSheet() { when (backupAlert) { is BackupAlert.CouldNotCompleteBackup -> BackupRepository.markBackupFailedSheetDismissed() + is BackupAlert.MediaWillBeDeletedToday -> BackupRepository.snoozeYourMediaWillBeDeletedTodaySheet() else -> Unit } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt index 1ddbc8a08e..18b7249b80 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt @@ -25,10 +25,9 @@ object BackupAlertDelegate { BackupAlertBottomSheet.create(BackupAlert.CouldNotCompleteBackup(daysSinceLastBackup = SignalStore.backup.daysSinceLastBackup)).show(fragmentManager, null) } - // TODO [backups] Check if media will be deleted within 24hrs and display warning sheet. - - // TODO [backups] - // Get unnotified backup download failures & display sheet + if (BackupRepository.shouldDisplayYourMediaWillBeDeletedTodaySheet()) { + BackupAlertBottomSheet.create(BackupAlert.MediaWillBeDeletedToday) + } } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt index 32b71bc3f7..0a492a4c05 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt @@ -59,7 +59,6 @@ import java.util.concurrent.locks.Lock import kotlin.jvm.optionals.getOrNull import kotlin.time.Duration import kotlin.time.Duration.Companion.days -import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds /** @@ -390,32 +389,6 @@ object InAppPaymentsRepository { } } - /** - * Determines if we are in the timeout period to display the "your backup will be deleted today" message - */ - @WorkerThread - fun getExpiredBackupDeletionState(): ExpiredBackupDeletionState { - val inAppPayment = SignalDatabase.inAppPayments.getByLatestEndOfPeriod(InAppPaymentType.RECURRING_BACKUP) - if (inAppPayment == null) { - Log.w(TAG, "InAppPayment for recurring backup not found for last day check. Clearing check.") - SignalStore.inAppPayments.showLastDayToDownloadMediaDialog = false - return ExpiredBackupDeletionState.NONE - } - - val now = SignalStore.misc.estimatedServerTime.milliseconds - val lastEndOfPeriod = inAppPayment.endOfPeriod - val displayDialogStart = lastEndOfPeriod + backupExpirationTimeout - val displayDialogEnd = lastEndOfPeriod + backupExpirationDeletion - - return if (now in displayDialogStart..displayDialogEnd) { - ExpiredBackupDeletionState.DELETE_TODAY - } else if (now > displayDialogEnd) { - ExpiredBackupDeletionState.EXPIRED - } else { - ExpiredBackupDeletionState.NONE - } - } - @JvmStatic @WorkerThread fun setShouldCancelSubscriptionBeforeNextSubscribeAttempt(subscriber: InAppPaymentSubscriberRecord, shouldCancel: Boolean) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/InAppPaymentsBottomSheetDelegate.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/InAppPaymentsBottomSheetDelegate.kt index f81c086248..07c8abedbd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/InAppPaymentsBottomSheetDelegate.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/InAppPaymentsBottomSheetDelegate.kt @@ -19,7 +19,6 @@ import org.thoughtcrime.securesms.badges.Badges import org.thoughtcrime.securesms.badges.self.expired.MonthlyDonationCanceledBottomSheetDialogFragment import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPendingBottomSheet import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPendingBottomSheetArgs -import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository import org.thoughtcrime.securesms.components.settings.app.subscription.thanks.ThanksForYourSupportBottomSheetDialogFragment import org.thoughtcrime.securesms.components.settings.app.subscription.thanks.ThanksForYourSupportBottomSheetDialogFragmentArgs import org.thoughtcrime.securesms.database.InAppPaymentTable @@ -140,16 +139,6 @@ class InAppPaymentsBottomSheetDelegate( } } } - - if (SignalStore.inAppPayments.showLastDayToDownloadMediaDialog) { - lifecycleDisposable += Single.fromCallable { - InAppPaymentsRepository.getExpiredBackupDeletionState() - }.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribeBy { - if (it == InAppPaymentsRepository.ExpiredBackupDeletionState.DELETE_TODAY) { - BackupAlertBottomSheet.create(BackupAlert.MediaWillBeDeletedToday).show(fragmentManager, null) - } - } - } } private fun isUnexpectedCancellation(inAppPaymentState: InAppPaymentTable.State, inAppPaymentData: InAppPaymentData): Boolean { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRefreshJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRefreshJob.kt index 5a3c4af7d4..52be59e42c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRefreshJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupRefreshJob.kt @@ -82,6 +82,7 @@ class BackupRefreshJob private constructor( return when (result) { is NetworkResult.Success -> { SignalStore.backup.lastCheckInMillis = System.currentTimeMillis() + SignalStore.backup.lastCheckInSnoozeMillis = 0 Result.success() } else -> { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRedemptionJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRedemptionJob.kt index d8d3160c3b..a2c7b1144a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRedemptionJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentRedemptionJob.kt @@ -259,6 +259,7 @@ class InAppPaymentRedemptionJob private constructor( Log.i(TAG, "Setting backup tier to PAID", true) SignalStore.backup.backupTier = MessageBackupTier.PAID SignalStore.backup.lastCheckInMillis = System.currentTimeMillis() + SignalStore.backup.lastCheckInSnoozeMillis = 0 } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt index 75ab10bf35..2282fe09cd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt @@ -36,6 +36,7 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { private const val KEY_BACKUP_TIER = "backup.backupTier" private const val KEY_LATEST_BACKUP_TIER = "backup.latestBackupTier" private const val KEY_LAST_CHECK_IN_MILLIS = "backup.lastCheckInMilliseconds" + private const val KEY_LAST_CHECK_IN_SNOOZE_MILLIS = "backup.lastCheckInSnoozeMilliseconds" private const val KEY_NEXT_BACKUP_TIME = "backup.nextBackupTime" private const val KEY_LAST_BACKUP_TIME = "backup.lastBackupTime" @@ -95,8 +96,19 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { var userManuallySkippedMediaRestore: Boolean by booleanValue(KEY_USER_MANUALLY_SKIPPED_MEDIA_RESTORE, false) + /** + * The last time the device notified the server that the archive is still in use. + */ var lastCheckInMillis: Long by longValue(KEY_LAST_CHECK_IN_MILLIS, 0L) + /** + * The time we last displayed the "Your media will be deleted today" sheet. + * + * Set when the user dismisses the "Your media will be deleted today" alert + * Cleared when the system performs a check-in or the user subscribes to backups. + */ + var lastCheckInSnoozeMillis: Long by longValue(KEY_LAST_CHECK_IN_SNOOZE_MILLIS, 0) + /** * Key used to backup messages. */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InAppPaymentValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InAppPaymentValues.kt index 753813c3be..ebd8316962 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InAppPaymentValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InAppPaymentValues.kt @@ -71,7 +71,6 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor private const val SUBSCRIPTION_CANCELATION_TIMESTAMP = "donation.subscription.cancelation.timestamp" private const val SUBSCRIPTION_CANCELATION_WATERMARK = "donation.subscription.cancelation.watermark" private const val SHOW_CANT_PROCESS_DIALOG = "show.cant.process.dialog" - private const val SHOW_LAST_DAY_TO_DOWNLOAD_MEDIA_DIALOG = "inapppayment.show.last.day.to.download.media.dialog" /** * The current request context for subscription. This should be stored until either @@ -162,8 +161,7 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor SUBSCRIPTION_EOP_STARTED_TO_CONVERT, SUBSCRIPTION_EOP_STARTED_TO_REDEEM, SUBSCRIPTION_EOP_REDEEMED, - SUBSCRIPTION_PAYMENT_SOURCE_TYPE, - SHOW_LAST_DAY_TO_DOWNLOAD_MEDIA_DIALOG + SUBSCRIPTION_PAYMENT_SOURCE_TYPE ) private val recurringDonationCurrencyPublisher: Subject by lazy { BehaviorSubject.createDefault(getSubscriptionCurrency(InAppPaymentSubscriberRecord.Type.DONATION)) } @@ -420,9 +418,6 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor @get:JvmName("showCantProcessDialog") var showMonthlyDonationCanceledDialog: Boolean by booleanValue(SHOW_CANT_PROCESS_DIALOG, true) - @get:JvmName("showLastDayToDownloadMediaDialog") - var showLastDayToDownloadMediaDialog: Boolean by booleanValue(SHOW_LAST_DAY_TO_DOWNLOAD_MEDIA_DIALOG, false) - /** * Denotes that the previous attempt to subscribe failed in some way. Either an * automatic renewal failed resulting in an unexpected expiration, or payment failed From c7f226b5cc7a1c0e9906bcf5f074040747296d2f Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 22 Nov 2024 10:50:42 -0400 Subject: [PATCH 06/56] Remove orphaned attachments when creating a new backup. --- .../database/BackupMediaSnapshotTableTest.kt | 118 +++++++++++++++++ .../securesms/backup/v2/BackupRepository.kt | 39 +++++- .../securesms/database/AttachmentTable.kt | 8 ++ .../database/BackupMediaSnapshotTable.kt | 121 ++++++++++++++++++ .../securesms/database/SignalDatabase.kt | 7 + .../helpers/SignalDatabaseMigrations.kt | 6 +- .../V257_CreateBackupMediaSyncTable.kt | 26 ++++ .../jobs/BackupMediaSnapshotSyncJob.kt | 76 +++++++++++ .../securesms/jobs/BackupMessagesJob.kt | 20 ++- .../securesms/jobs/JobManagerFactories.java | 1 + app/src/main/protowire/JobData.proto | 4 + .../v2/ArchivedMediaObjectIteratorTest.kt | 43 +++++++ 12 files changed, 464 insertions(+), 5 deletions(-) create mode 100644 app/src/androidTest/java/org/thoughtcrime/securesms/database/BackupMediaSnapshotTableTest.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/BackupMediaSnapshotTable.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V257_CreateBackupMediaSyncTable.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMediaSnapshotSyncJob.kt create mode 100644 app/src/test/java/org/thoughtcrime/securesms/backup/v2/ArchivedMediaObjectIteratorTest.kt diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/BackupMediaSnapshotTableTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/BackupMediaSnapshotTableTest.kt new file mode 100644 index 0000000000..7f03c62d9c --- /dev/null +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/BackupMediaSnapshotTableTest.kt @@ -0,0 +1,118 @@ +package org.thoughtcrime.securesms.database + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.signal.core.util.count +import org.signal.core.util.readToSingleInt +import org.thoughtcrime.securesms.backup.v2.ArchivedMediaObject +import org.thoughtcrime.securesms.testing.SignalActivityRule +import org.thoughtcrime.securesms.testing.assertIs + +@RunWith(AndroidJUnit4::class) +class BackupMediaSnapshotTableTest { + + companion object { + private const val SEQUENCE_COUNT = 100 + } + + @get:Rule + val harness = SignalActivityRule() + + @Test + fun givenAnEmptyTable_whenIWriteToTable_thenIExpectEmptyTable() { + val pendingSyncTime = 1L + SignalDatabase.backupMediaSnapshots.writePendingMediaObjects(generateArchiveObjectSequence(), pendingSyncTime) + + val count = getSyncedItemCount(pendingSyncTime) + + count.assertIs(0) + } + + @Test + fun givenAnEmptyTable_whenIWriteToTableAndCommit_thenIExpectFilledTable() { + val pendingSyncTime = 1L + SignalDatabase.backupMediaSnapshots.writePendingMediaObjects(generateArchiveObjectSequence(), pendingSyncTime) + SignalDatabase.backupMediaSnapshots.commitPendingRows() + + val count = getSyncedItemCount(pendingSyncTime) + + count.assertIs(SEQUENCE_COUNT) + } + + @Test + fun givenAFilledTable_whenIInsertSimilarIds_thenIExpectUncommittedOverrides() { + SignalDatabase.backupMediaSnapshots.writePendingMediaObjects(generateArchiveObjectSequence(), 1L) + SignalDatabase.backupMediaSnapshots.commitPendingRows() + + val newPendingTime = 2L + val newObjectCount = 50 + SignalDatabase.backupMediaSnapshots.writePendingMediaObjects(generateArchiveObjectSequence(newObjectCount), newPendingTime) + + val count = SignalDatabase.backupMediaSnapshots.readableDatabase.count() + .from(BackupMediaSnapshotTable.TABLE_NAME) + .where("${BackupMediaSnapshotTable.LAST_SYNC_TIME} = 1 AND ${BackupMediaSnapshotTable.PENDING_SYNC_TIME} = $newPendingTime") + .run() + .readToSingleInt(-1) + + count.assertIs(50) + } + + @Test + fun givenAFilledTable_whenIInsertSimilarIdsAndCommit_thenIExpectCommittedOverrides() { + SignalDatabase.backupMediaSnapshots.writePendingMediaObjects(generateArchiveObjectSequence(), 1L) + SignalDatabase.backupMediaSnapshots.commitPendingRows() + + val newPendingTime = 2L + val newObjectCount = 50 + SignalDatabase.backupMediaSnapshots.writePendingMediaObjects(generateArchiveObjectSequence(newObjectCount), newPendingTime) + SignalDatabase.backupMediaSnapshots.commitPendingRows() + + val count = SignalDatabase.backupMediaSnapshots.readableDatabase.count() + .from(BackupMediaSnapshotTable.TABLE_NAME) + .where("${BackupMediaSnapshotTable.LAST_SYNC_TIME} = $newPendingTime AND ${BackupMediaSnapshotTable.PENDING_SYNC_TIME} = $newPendingTime") + .run() + .readToSingleInt(-1) + + val total = getTotalItemCount() + + count.assertIs(50) + total.assertIs(SEQUENCE_COUNT) + } + + @Test + fun givenAFilledTable_whenIInsertSimilarIdsAndCommitThenDelete_thenIExpectOnlyCommittedOverrides() { + SignalDatabase.backupMediaSnapshots.writePendingMediaObjects(generateArchiveObjectSequence(), 1L) + SignalDatabase.backupMediaSnapshots.commitPendingRows() + + val newPendingTime = 2L + val newObjectCount = 50 + SignalDatabase.backupMediaSnapshots.writePendingMediaObjects(generateArchiveObjectSequence(newObjectCount), newPendingTime) + SignalDatabase.backupMediaSnapshots.commitPendingRows() + + val page = SignalDatabase.backupMediaSnapshots.getPageOfOldMediaObjects(currentSyncTime = newPendingTime, pageSize = 100) + SignalDatabase.backupMediaSnapshots.deleteMediaObjects(page) + + val total = getTotalItemCount() + + total.assertIs(50) + } + + private fun getTotalItemCount(): Int { + return SignalDatabase.backupMediaSnapshots.readableDatabase.count().from(BackupMediaSnapshotTable.TABLE_NAME).run().readToSingleInt(-1) + } + + private fun getSyncedItemCount(pendingTime: Long): Int { + return SignalDatabase.backupMediaSnapshots.readableDatabase.count() + .from(BackupMediaSnapshotTable.TABLE_NAME) + .where("${BackupMediaSnapshotTable.LAST_SYNC_TIME} = $pendingTime AND ${BackupMediaSnapshotTable.PENDING_SYNC_TIME} = $pendingTime") + .run() + .readToSingleInt(-1) + } + + private fun generateArchiveObjectSequence(count: Int = SEQUENCE_COUNT): Sequence { + return generateSequence(0) { seed -> if (seed < (count - 1)) seed + 1 else null } + .map { ArchivedMediaObject(mediaId = "media_id_$it", 0) } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index c2a4d1271c..fd2b701221 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -5,6 +5,7 @@ package org.thoughtcrime.securesms.backup.v2 +import android.database.Cursor import android.os.Environment import android.os.StatFs import androidx.annotation.WorkerThread @@ -26,6 +27,8 @@ import org.signal.core.util.getAllTableDefinitions import org.signal.core.util.getAllTriggerDefinitions import org.signal.core.util.getForeignKeyViolations import org.signal.core.util.logging.Log +import org.signal.core.util.requireInt +import org.signal.core.util.requireNonNullString import org.signal.core.util.stream.NonClosingOutputStream import org.signal.core.util.urlEncode import org.signal.core.util.withinTransaction @@ -420,7 +423,8 @@ object BackupRepository { plaintext: Boolean = false, currentTime: Long = System.currentTimeMillis(), mediaBackupEnabled: Boolean = SignalStore.backup.backsUpMedia, - cancellationSignal: () -> Boolean = { false } + cancellationSignal: () -> Boolean = { false }, + exportExtras: ((SignalDatabase) -> Unit)? = null ) { val writer: BackupExportWriter = if (plaintext) { PlainTextBackupWriter(outputStream) @@ -433,7 +437,7 @@ object BackupRepository { ) } - export(currentTime = currentTime, isLocal = false, writer = writer, mediaBackupEnabled = mediaBackupEnabled, cancellationSignal = cancellationSignal) + export(currentTime = currentTime, isLocal = false, writer = writer, mediaBackupEnabled = mediaBackupEnabled, cancellationSignal = cancellationSignal, exportExtras = exportExtras) } /** @@ -1410,3 +1414,34 @@ sealed class ImportResult { data class Success(val backupTime: Long) : ImportResult() data object Failure : ImportResult() } + +/** + * Iterator that reads values from the given cursor. Expects that ARCHIVE_MEDIA_ID and ARCHIVE_CDN are both + * present and non-null in the cursor. + * + * This class does not assume ownership of the cursor. Recommended usage is within a use statement: + * + * + * ``` + * databaseCall().use { cursor -> + * val iterator = ArchivedMediaObjectIterator(cursor) + * // Use the iterator... + * } + * // Cursor is closed after use block. + * ``` + */ +class ArchivedMediaObjectIterator(private val cursor: Cursor) : Iterator { + + init { + cursor.moveToFirst() + } + + override fun hasNext(): Boolean = !cursor.isAfterLast + + override fun next(): ArchivedMediaObject { + val mediaId = cursor.requireNonNullString(AttachmentTable.ARCHIVE_MEDIA_ID) + val cdn = cursor.requireInt(AttachmentTable.ARCHIVE_CDN) + cursor.moveToNext() + return ArchivedMediaObject(mediaId, cdn) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt index 4d780c3003..317d98a2e0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -407,6 +407,14 @@ class AttachmentTable( } } + fun getMediaIdCursor(): Cursor { + return readableDatabase + .select(ARCHIVE_MEDIA_ID, ARCHIVE_CDN) + .from(TABLE_NAME) + .where("$ARCHIVE_MEDIA_ID IS NOT NULL") + .run() + } + fun getAttachment(attachmentId: AttachmentId): DatabaseAttachment? { return readableDatabase .select(*PROJECTION) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/BackupMediaSnapshotTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/BackupMediaSnapshotTable.kt new file mode 100644 index 0000000000..8d53b9a24b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/BackupMediaSnapshotTable.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database + +import android.content.Context +import androidx.annotation.VisibleForTesting +import androidx.core.content.contentValuesOf +import org.signal.core.util.SqlUtil +import org.signal.core.util.delete +import org.signal.core.util.exists +import org.signal.core.util.readToList +import org.signal.core.util.requireInt +import org.signal.core.util.requireNonNullString +import org.signal.core.util.select +import org.thoughtcrime.securesms.backup.v2.ArchivedMediaObject + +/** + * Helper table for attachment deletion sync + */ +class BackupMediaSnapshotTable(context: Context, database: SignalDatabase) : DatabaseTable(context, database) { + companion object { + + const val TABLE_NAME = "backup_media_snapshot" + + private const val ID = "_id" + + /** + * Generated media id matching that of the attachments table. + */ + private const val MEDIA_ID = "media_id" + + /** + * CDN where the data is stored + */ + private const val CDN = "cdn" + + /** + * Unique backup snapshot sync time. These are expected to increment in value + * where newer backups have a greater backup id value. + */ + @VisibleForTesting + const val LAST_SYNC_TIME = "last_sync_time" + + /** + * Pending sync time, set while a backup is in the process of being exported. + */ + @VisibleForTesting + const val PENDING_SYNC_TIME = "pending_sync_time" + + val CREATE_TABLE = """ + CREATE TABLE $TABLE_NAME ( + $ID INTEGER PRIMARY KEY, + $MEDIA_ID TEXT UNIQUE, + $CDN INTEGER, + $LAST_SYNC_TIME INTEGER DEFAULT 0, + $PENDING_SYNC_TIME INTEGER + ) + """.trimIndent() + + private const val ON_MEDIA_ID_CONFLICT = """ + ON CONFLICT($MEDIA_ID) DO UPDATE SET + $PENDING_SYNC_TIME = EXCLUDED.$PENDING_SYNC_TIME, + $CDN = EXCLUDED.$CDN + """ + } + + /** + * Creates the temporary table if it doesn't exist, clears it, then inserts the media objects into it. + */ + fun writePendingMediaObjects(mediaObjects: Sequence, pendingSyncTime: Long) { + mediaObjects.chunked(999) + .forEach { chunk -> + writePendingMediaObjectsChunk(chunk, pendingSyncTime) + } + } + + private fun writePendingMediaObjectsChunk(chunk: List, pendingSyncTime: Long) { + SqlUtil.buildBulkInsert( + TABLE_NAME, + arrayOf(MEDIA_ID, CDN, PENDING_SYNC_TIME), + chunk.map { + contentValuesOf(MEDIA_ID to it.mediaId, CDN to it.cdn, PENDING_SYNC_TIME to pendingSyncTime) + } + ).forEach { + writableDatabase.execSQL("${it.where} $ON_MEDIA_ID_CONFLICT", it.whereArgs) + } + } + + /** + * Copies all entries from the temporary table to the persistent table, then deletes the temporary table. + */ + fun commitPendingRows() { + writableDatabase.execSQL("UPDATE $TABLE_NAME SET $LAST_SYNC_TIME = $PENDING_SYNC_TIME") + } + + fun getPageOfOldMediaObjects(currentSyncTime: Long, pageSize: Int): List { + return readableDatabase.select(MEDIA_ID, CDN) + .from(TABLE_NAME) + .where("$LAST_SYNC_TIME < ? AND $LAST_SYNC_TIME = $PENDING_SYNC_TIME", currentSyncTime) + .limit(pageSize) + .run() + .readToList { + ArchivedMediaObject(mediaId = it.requireNonNullString(MEDIA_ID), cdn = it.requireInt(CDN)) + } + } + + fun deleteMediaObjects(mediaObjects: List) { + SqlUtil.buildCollectionQuery(MEDIA_ID, mediaObjects.map { it.mediaId }).forEach { + writableDatabase.delete(TABLE_NAME) + .where(it.where, it.whereArgs) + .run() + } + } + + fun hasOldMediaObjects(currentSyncTime: Long): Boolean { + return readableDatabase.exists(TABLE_NAME).where("$LAST_SYNC_TIME > ? AND $LAST_SYNC_TIME = $PENDING_SYNC_TIME", currentSyncTime).run() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt index a2ea24a7f6..21c4256336 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt @@ -77,6 +77,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data val inAppPaymentTable: InAppPaymentTable = InAppPaymentTable(context, this) val inAppPaymentSubscriberTable: InAppPaymentSubscriberTable = InAppPaymentSubscriberTable(context, this) val chatFoldersTable: ChatFolderTables = ChatFolderTables(context, this) + val backupMediaSnapshotTable: BackupMediaSnapshotTable = BackupMediaSnapshotTable(context, this) override fun onOpen(db: net.zetetic.database.sqlcipher.SQLiteDatabase) { db.setForeignKeyConstraintsEnabled(true) @@ -122,6 +123,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data executeStatements(db, NotificationProfileDatabase.CREATE_TABLE) executeStatements(db, DistributionListTables.CREATE_TABLE) executeStatements(db, ChatFolderTables.CREATE_TABLE) + db.execSQL(BackupMediaSnapshotTable.CREATE_TABLE) executeStatements(db, RecipientTable.CREATE_INDEXS) executeStatements(db, MessageTable.CREATE_INDEXS) @@ -566,5 +568,10 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data @get:JvmName("chatFolders") val chatFolders: ChatFolderTables get() = instance!!.chatFoldersTable + + @get:JvmStatic + @get:JvmName("backupMediaSnapshots") + val backupMediaSnapshots: BackupMediaSnapshotTable + get() = instance!!.backupMediaSnapshotTable } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 963d34530d..f543f6fba2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -113,6 +113,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V253_CreateChatFold import org.thoughtcrime.securesms.database.helpers.migration.V254_AddChatFolderConstraint import org.thoughtcrime.securesms.database.helpers.migration.V255_AddCallTableLogIndex import org.thoughtcrime.securesms.database.helpers.migration.V256_FixIncrementalDigestColumns +import org.thoughtcrime.securesms.database.helpers.migration.V257_CreateBackupMediaSyncTable /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -228,10 +229,11 @@ object SignalDatabaseMigrations { 253 to V253_CreateChatFolderTables, 254 to V254_AddChatFolderConstraint, 255 to V255_AddCallTableLogIndex, - 256 to V256_FixIncrementalDigestColumns + 256 to V256_FixIncrementalDigestColumns, + 257 to V257_CreateBackupMediaSyncTable ) - const val DATABASE_VERSION = 256 + const val DATABASE_VERSION = 257 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V257_CreateBackupMediaSyncTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V257_CreateBackupMediaSyncTable.kt new file mode 100644 index 0000000000..c502a0300e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V257_CreateBackupMediaSyncTable.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +@Suppress("ClassName") +object V257_CreateBackupMediaSyncTable : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL( + """ + CREATE TABLE backup_media_snapshot ( + _id INTEGER PRIMARY KEY, + media_id TEXT UNIQUE, + cdn INTEGER, + last_sync_time INTEGER DEFAULT 0, + pending_sync_time INTEGER + ) + """.trimIndent() + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMediaSnapshotSyncJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMediaSnapshotSyncJob.kt new file mode 100644 index 0000000000..f0a675e6f2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMediaSnapshotSyncJob.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.jobs + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.backup.v2.BackupRepository +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.AppDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobs.protos.BackupMediaSnapshotSyncJobData +import org.whispersystems.signalservice.api.NetworkResult + +/** + * Synchronizes the server media via bulk deletions of old attachments not present + * in the user's current backup. + */ +class BackupMediaSnapshotSyncJob private constructor(private val syncTime: Long, parameters: Parameters) : Job(parameters) { + + companion object { + + private val TAG = Log.tag(BackupMediaSnapshotSyncJob::class) + + const val KEY = "BackupMediaSnapshotSyncJob" + + private const val PAGE_SIZE = 500 + + fun enqueue(backupSnapshotId: Long) { + AppDependencies.jobManager.add( + BackupMediaSnapshotSyncJob( + backupSnapshotId, + Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setMaxInstancesForFactory(1) + .build() + ) + ) + } + } + + override fun serialize(): ByteArray = BackupMediaSnapshotSyncJobData(syncTime).encode() + + override fun getFactoryKey(): String = KEY + + override fun run(): Result { + while (SignalDatabase.backupMediaSnapshots.hasOldMediaObjects(syncTime)) { + val mediaObjects = SignalDatabase.backupMediaSnapshots.getPageOfOldMediaObjects(syncTime, PAGE_SIZE) + + when (val networkResult = BackupRepository.deleteAbandonedMediaObjects(mediaObjects)) { + is NetworkResult.Success -> { + SignalDatabase.backupMediaSnapshots.deleteMediaObjects(mediaObjects) + } + + else -> { + Log.w(TAG, "Failed to delete media objects.", networkResult.getCause()) + return Result.failure() + } + } + } + + return Result.success() + } + + override fun onFailure() = Unit + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): BackupMediaSnapshotSyncJob { + val syncTime: Long = BackupMediaSnapshotSyncJobData.ADAPTER.decode(serializedData!!).syncTime + + return BackupMediaSnapshotSyncJob(syncTime, parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt index 6e5bae07b3..0516e1af7d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt @@ -9,6 +9,7 @@ import org.signal.core.util.Stopwatch import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.backup.ArchiveUploadProgress import org.thoughtcrime.securesms.backup.v2.ArchiveValidator +import org.thoughtcrime.securesms.backup.v2.ArchivedMediaObjectIterator import org.thoughtcrime.securesms.backup.v2.BackupRepository import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.AppDependencies @@ -84,7 +85,11 @@ class BackupMessagesJob private constructor(parameters: Parameters) : Job(parame val outputStream = FileOutputStream(tempBackupFile) val backupKey = SignalStore.backup.messageBackupKey - BackupRepository.export(outputStream = outputStream, messageBackupKey = backupKey, append = { tempBackupFile.appendBytes(it) }, plaintext = false, cancellationSignal = { this.isCanceled }) + val currentTime = System.currentTimeMillis() + BackupRepository.export(outputStream = outputStream, messageBackupKey = backupKey, append = { tempBackupFile.appendBytes(it) }, plaintext = false, cancellationSignal = { this.isCanceled }, currentTime = currentTime) { + writeMediaCursorToTemporaryTable(it, currentTime = currentTime, mediaBackupEnabled = SignalStore.backup.backsUpMedia) + } + stopwatch.split("export") when (val result = ArchiveValidator.validate(tempBackupFile, backupKey)) { @@ -156,9 +161,22 @@ class BackupMessagesJob private constructor(parameters: Parameters) : Job(parame } SignalStore.backup.clearMessageBackupFailure() + SignalDatabase.backupMediaSnapshots.commitPendingRows() + BackupMediaSnapshotSyncJob.enqueue(currentTime) return Result.success() } + private fun writeMediaCursorToTemporaryTable(db: SignalDatabase, mediaBackupEnabled: Boolean, currentTime: Long) { + if (mediaBackupEnabled) { + db.attachmentTable.getMediaIdCursor().use { + SignalDatabase.backupMediaSnapshots.writePendingMediaObjects( + mediaObjects = ArchivedMediaObjectIterator(it).asSequence(), + pendingSyncTime = currentTime + ) + } + } + } + class Factory : Job.Factory { override fun create(parameters: Parameters, serializedData: ByteArray?): BackupMessagesJob { return BackupMessagesJob(parameters) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index 7ff3d3bfa4..b5c7eb616d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -271,6 +271,7 @@ public static Map getJobFactories(@NonNull Application appl put(BackfillDigestsMigrationJob.KEY, new BackfillDigestsMigrationJob.Factory()); put(BackfillDigestsForDuplicatesMigrationJob.KEY, new BackfillDigestsForDuplicatesMigrationJob.Factory()); put(BackupJitterMigrationJob.KEY, new BackupJitterMigrationJob.Factory()); + put(BackupMediaSnapshotSyncJob.KEY, new BackupMediaSnapshotSyncJob.Factory()); put(BackupNotificationMigrationJob.KEY, new BackupNotificationMigrationJob.Factory()); put(BackupRefreshJob.KEY, new BackupRefreshJob.Factory()); put(BlobStorageLocationMigrationJob.KEY, new BlobStorageLocationMigrationJob.Factory()); diff --git a/app/src/main/protowire/JobData.proto b/app/src/main/protowire/JobData.proto index c082cc67f7..bbd56caa5a 100644 --- a/app/src/main/protowire/JobData.proto +++ b/app/src/main/protowire/JobData.proto @@ -137,3 +137,7 @@ message UploadAttachmentToArchiveJobData { uint64 attachmentId = 1; ResumableUpload uploadSpec = 2; } + +message BackupMediaSnapshotSyncJobData { + uint64 syncTime = 1; +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/backup/v2/ArchivedMediaObjectIteratorTest.kt b/app/src/test/java/org/thoughtcrime/securesms/backup/v2/ArchivedMediaObjectIteratorTest.kt new file mode 100644 index 0000000000..c398c76876 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/backup/v2/ArchivedMediaObjectIteratorTest.kt @@ -0,0 +1,43 @@ +package org.thoughtcrime.securesms.backup.v2 + +import org.junit.Before +import org.junit.Test +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import org.thoughtcrime.securesms.MockCursor +import org.thoughtcrime.securesms.assertIsSize + +class ArchivedMediaObjectIteratorTest { + + private val cursor: MockCursor = mock() + + @Before + fun setUp() { + whenever(cursor.getString(any())).thenReturn("A") + whenever(cursor.moveToPosition(any())).thenCallRealMethod() + whenever(cursor.moveToNext()).thenCallRealMethod() + whenever(cursor.position).thenCallRealMethod() + whenever(cursor.isLast).thenCallRealMethod() + whenever(cursor.isAfterLast).thenCallRealMethod() + } + + @Test + fun `Given a cursor with 0 items, when I convert to a list, then I expect a size of 0`() { + runTest(0) + } + + @Test + fun `Given a cursor with 100 items, when I convert to a list, then I expect a size of 100`() { + runTest(100) + } + + private fun runTest(size: Int) { + whenever(cursor.count).thenReturn(size) + val iterator = ArchivedMediaObjectIterator(cursor) + + val list = iterator.asSequence().toList() + + list.assertIsSize(size) + } +} From 34eef0bf5c5ce3ef9bedb9c465b0be7c5e06f1db Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 22 Nov 2024 11:13:42 -0400 Subject: [PATCH 07/56] Add sgnl://ideal support to app deeplinks. --- app/src/main/AndroidManifest.xml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5497057201..1e65ade2ca 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -594,7 +594,17 @@ - + + + + + + + + + + + From 7d24bff134dad03ad985e2cfd8c55ec93580f8fc Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Fri, 22 Nov 2024 10:18:56 -0500 Subject: [PATCH 08/56] Add unit test for RegistrationUtil. --- .../components/emoji/EmojiUtilTest_isEmoji.kt | 12 +- .../account/export/ExportAccountDataTest.kt | 16 +- .../securesms/crash/CrashConfigTest.kt | 12 +- .../NotificationProfileDatabaseTest.kt | 11 +- .../MockApplicationDependencyProvider.kt | 88 +++++------ .../groups/GroupManagerV2Test_edit.kt | 11 +- .../processing/GroupsV2StateProcessorTest.kt | 11 +- .../securesms/keyvalue/PaymentsValuesTest.kt | 12 +- .../RemoteMegaphoneRepositoryTest.kt | 14 +- .../notifications/MarkReadReceiverTest.kt | 14 +- .../profiles/NotificationProfilesTest.kt | 12 +- .../registration/util/RegistrationUtilTest.kt | 143 ++++++++++++++++++ .../storage/ContactRecordProcessorTest.kt | 12 +- .../testutil/MockAppDependenciesRule.kt | 50 ++++++ .../securesms/testutil/MockSignalStoreRule.kt | 60 ++++++++ .../SignalMeUtilText_parseE164FromLink.kt | 12 +- 16 files changed, 356 insertions(+), 134 deletions(-) create mode 100644 app/src/test/java/org/thoughtcrime/securesms/registration/util/RegistrationUtilTest.kt create mode 100644 app/src/test/java/org/thoughtcrime/securesms/testutil/MockAppDependenciesRule.kt create mode 100644 app/src/test/java/org/thoughtcrime/securesms/testutil/MockSignalStoreRule.kt diff --git a/app/src/test/java/org/thoughtcrime/securesms/components/emoji/EmojiUtilTest_isEmoji.kt b/app/src/test/java/org/thoughtcrime/securesms/components/emoji/EmojiUtilTest_isEmoji.kt index d2b850f17c..da99a8d085 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/components/emoji/EmojiUtilTest_isEmoji.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/components/emoji/EmojiUtilTest_isEmoji.kt @@ -1,29 +1,27 @@ package org.thoughtcrime.securesms.components.emoji import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockkObject import org.junit.Assert +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.ParameterizedRobolectricTestRunner import org.robolectric.annotation.Config -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.emoji.EmojiSource +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule @RunWith(ParameterizedRobolectricTestRunner::class) @Config(manifest = Config.NONE, application = Application::class) class EmojiUtilTest_isEmoji(private val input: String?, private val output: Boolean) { + @get:Rule + val appDependencies = MockAppDependenciesRule() + @Throws(Exception::class) @Test fun isEmoji() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - val source = EmojiSource.loadAssetBasedEmojis() mockkObject(EmojiSource) { diff --git a/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataTest.kt b/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataTest.kt index 98469ed27e..9341e7830d 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataTest.kt @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.components.settings.app.account.export import android.app.Application -import androidx.test.core.app.ApplicationProvider import com.fasterxml.jackson.core.JsonParseException import io.mockk.every import io.mockk.mockk @@ -12,14 +11,13 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertThrows import org.junit.Assert.assertTrue -import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.providers.BlobProvider +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.util.JsonUtils import org.whispersystems.signalservice.api.SignalServiceAccountManager import java.io.IOException @@ -28,6 +26,9 @@ import java.io.IOException @Config(manifest = Config.NONE, application = Application::class) class ExportAccountDataTest { + @get:Rule + val appDependencies = MockAppDependenciesRule() + private val mockJson: String = """ { "reportId": "4c0ca2aa-151b-4e9e-8bf4-ea2c64345a22", @@ -64,13 +65,6 @@ class ExportAccountDataTest { } """ - @Before - fun setup() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - } - @Test fun `Export json without text field`() { val scheduler = TestScheduler() diff --git a/app/src/test/java/org/thoughtcrime/securesms/crash/CrashConfigTest.kt b/app/src/test/java/org/thoughtcrime/securesms/crash/CrashConfigTest.kt index 0a6b113a2c..e8a7581f80 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/crash/CrashConfigTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/crash/CrashConfigTest.kt @@ -1,20 +1,19 @@ package org.thoughtcrime.securesms.crash import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockkObject import io.mockk.unmockkAll import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.thoughtcrime.securesms.assertIs -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.util.RemoteConfig import org.whispersystems.signalservice.api.push.ServiceId import java.util.UUID @@ -23,14 +22,13 @@ import java.util.UUID @Config(manifest = Config.NONE, application = Application::class) class CrashConfigTest { + @get:Rule + val appDependencies = MockAppDependenciesRule() + @Before fun setup() { mockkObject(RemoteConfig) - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - mockkObject(SignalStore) every { SignalStore.account.aci } returns ServiceId.ACI.from(UUID.randomUUID()) } diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/NotificationProfileDatabaseTest.kt b/app/src/test/java/org/thoughtcrime/securesms/database/NotificationProfileDatabaseTest.kt index 94a5c0e05e..1d531f33e6 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/database/NotificationProfileDatabaseTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/database/NotificationProfileDatabaseTest.kt @@ -10,22 +10,25 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.thoughtcrime.securesms.conversation.colors.AvatarColor -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.testing.TestDatabaseUtil +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import java.time.DayOfWeek @RunWith(RobolectricTestRunner::class) @Config(manifest = Config.NONE, application = Application::class) class NotificationProfileDatabaseTest { + @get:Rule + val appDependencies = MockAppDependenciesRule() + private lateinit var db: SQLiteDatabase private lateinit var database: NotificationProfileDatabase @@ -42,10 +45,6 @@ class NotificationProfileDatabaseTest { } } - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - db = sqlCipher.writableDatabase database = NotificationProfileDatabase(ApplicationProvider.getApplicationContext(), sqlCipher) } diff --git a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt index f800ac79e5..4ed3a383b1 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt @@ -54,83 +54,83 @@ import java.util.function.Supplier class MockApplicationDependencyProvider : AppDependencies.Provider { override fun providePushServiceSocket(signalServiceConfiguration: SignalServiceConfiguration, groupsV2Operations: GroupsV2Operations): PushServiceSocket { - return mockk() + return mockk(relaxed = true) } override fun provideGroupsV2Operations(signalServiceConfiguration: SignalServiceConfiguration): GroupsV2Operations { - return mockk() + return mockk(relaxed = true) } override fun provideSignalServiceAccountManager(pushServiceSocket: PushServiceSocket, groupsV2Operations: GroupsV2Operations): SignalServiceAccountManager { - return mockk() + return mockk(relaxed = true) } override fun provideSignalServiceMessageSender(signalWebSocket: SignalWebSocket, protocolStore: SignalServiceDataStore, pushServiceSocket: PushServiceSocket): SignalServiceMessageSender { - return mockk() + return mockk(relaxed = true) } override fun provideSignalServiceMessageReceiver(pushServiceSocket: PushServiceSocket): SignalServiceMessageReceiver { - return mockk() + return mockk(relaxed = true) } override fun provideSignalServiceNetworkAccess(): SignalServiceNetworkAccess { - return mockk() + return mockk(relaxed = true) } override fun provideRecipientCache(): LiveRecipientCache { - return mockk() + return mockk(relaxed = true) } override fun provideJobManager(): JobManager { - return mockk() + return mockk(relaxed = true) } override fun provideFrameRateTracker(): FrameRateTracker { - return mockk() + return mockk(relaxed = true) } override fun provideMegaphoneRepository(): MegaphoneRepository { - return mockk() + return mockk(relaxed = true) } override fun provideEarlyMessageCache(): EarlyMessageCache { - return mockk() + return mockk(relaxed = true) } override fun provideMessageNotifier(): MessageNotifier { - return mockk() + return mockk(relaxed = true) } override fun provideIncomingMessageObserver(): IncomingMessageObserver { - return mockk() + return mockk(relaxed = true) } override fun provideTrimThreadsByDateManager(): TrimThreadsByDateManager { - return mockk() + return mockk(relaxed = true) } override fun provideViewOnceMessageManager(): ViewOnceMessageManager { - return mockk() + return mockk(relaxed = true) } override fun provideExpiringStoriesManager(): ExpiringStoriesManager { - return mockk() + return mockk(relaxed = true) } override fun provideExpiringMessageManager(): ExpiringMessageManager { - return mockk() + return mockk(relaxed = true) } override fun provideDeletedCallEventManager(): DeletedCallEventManager { - return mockk() + return mockk(relaxed = true) } override fun provideTypingStatusRepository(): TypingStatusRepository { - return mockk() + return mockk(relaxed = true) } override fun provideTypingStatusSender(): TypingStatusSender { - return mockk() + return mockk(relaxed = true) } override fun provideDatabaseObserver(): DatabaseObserver { @@ -138,98 +138,98 @@ class MockApplicationDependencyProvider : AppDependencies.Provider { } override fun providePayments(signalServiceAccountManager: SignalServiceAccountManager): Payments { - return mockk() + return mockk(relaxed = true) } override fun provideShakeToReport(): ShakeToReport { - return mockk() + return mockk(relaxed = true) } override fun provideSignalCallManager(): SignalCallManager { - return mockk() + return mockk(relaxed = true) } override fun providePendingRetryReceiptManager(): PendingRetryReceiptManager { - return mockk() + return mockk(relaxed = true) } override fun providePendingRetryReceiptCache(): PendingRetryReceiptCache { - return mockk() + return mockk(relaxed = true) } override fun provideSignalWebSocket(signalServiceConfigurationSupplier: Supplier, libSignalNetworkSupplier: Supplier): SignalWebSocket { - return mockk() + return mockk(relaxed = true) } override fun provideProtocolStore(): SignalServiceDataStoreImpl { - return mockk() + return mockk(relaxed = true) } override fun provideGiphyMp4Cache(): GiphyMp4Cache { - return mockk() + return mockk(relaxed = true) } override fun provideExoPlayerPool(): SimpleExoPlayerPool { - return mockk() + return mockk(relaxed = true) } override fun provideAndroidCallAudioManager(): AudioManagerCompat { - return mockk() + return mockk(relaxed = true) } override fun provideDonationsService(pushServiceSocket: PushServiceSocket): DonationsService { - return mockk() + return mockk(relaxed = true) } override fun provideCallLinksService(pushServiceSocket: PushServiceSocket): CallLinksService { - return mockk() + return mockk(relaxed = true) } override fun provideProfileService(profileOperations: ClientZkProfileOperations, signalServiceMessageReceiver: SignalServiceMessageReceiver, signalWebSocket: SignalWebSocket): ProfileService { - return mockk() + return mockk(relaxed = true) } override fun provideDeadlockDetector(): DeadlockDetector { - return mockk() + return mockk(relaxed = true) } override fun provideClientZkReceiptOperations(signalServiceConfiguration: SignalServiceConfiguration): ClientZkReceiptOperations { - return mockk() + return mockk(relaxed = true) } override fun provideScheduledMessageManager(): ScheduledMessageManager { - return mockk() + return mockk(relaxed = true) } override fun provideLibsignalNetwork(config: SignalServiceConfiguration): Network { - return mockk() + return mockk(relaxed = true) } override fun provideBillingApi(): BillingApi { - return mockk() + return mockk(relaxed = true) } override fun provideArchiveApi(pushServiceSocket: PushServiceSocket): ArchiveApi { - return mockk() + return mockk(relaxed = true) } override fun provideKeysApi(pushServiceSocket: PushServiceSocket): KeysApi { - return mockk() + return mockk(relaxed = true) } override fun provideAttachmentApi(signalWebSocket: SignalWebSocket, pushServiceSocket: PushServiceSocket): AttachmentApi { - return mockk() + return mockk(relaxed = true) } override fun provideLinkDeviceApi(pushServiceSocket: PushServiceSocket): LinkDeviceApi { - return mockk() + return mockk(relaxed = true) } override fun provideRegistrationApi(pushServiceSocket: PushServiceSocket): RegistrationApi { - return mockk() + return mockk(relaxed = true) } override fun provideStorageServiceApi(pushServiceSocket: PushServiceSocket): StorageServiceApi { - return mockk() + return mockk(relaxed = true) } } diff --git a/app/src/test/java/org/thoughtcrime/securesms/groups/GroupManagerV2Test_edit.kt b/app/src/test/java/org/thoughtcrime/securesms/groups/GroupManagerV2Test_edit.kt index 182ebdf3e4..aadf5f757f 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/groups/GroupManagerV2Test_edit.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/groups/GroupManagerV2Test_edit.kt @@ -15,6 +15,7 @@ import org.hamcrest.Matchers import org.hamcrest.Matchers.`is` import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -34,12 +35,11 @@ import org.thoughtcrime.securesms.TestZkGroupServer import org.thoughtcrime.securesms.database.GroupStateTestData import org.thoughtcrime.securesms.database.GroupTable import org.thoughtcrime.securesms.database.model.databaseprotos.member -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.groups.v2.GroupCandidateHelper import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.testutil.SystemOutLogger import org.thoughtcrime.securesms.util.RemoteConfig import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations @@ -68,6 +68,9 @@ class GroupManagerV2Test_edit { val others: List = listOf(member(otherAci)) } + @get:Rule + val appDependencies = MockAppDependenciesRule() + private lateinit var groupTable: GroupTable private lateinit var groupsV2API: GroupsV2Api private lateinit var groupsV2Operations: GroupsV2Operations @@ -81,10 +84,6 @@ class GroupManagerV2Test_edit { @Suppress("UsePropertyAccessSyntax") @Before fun setUp() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - mockkObject(RemoteConfig) mockkObject(SignalStore) every { RemoteConfig.internalUser } returns false diff --git a/app/src/test/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessorTest.kt b/app/src/test/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessorTest.kt index a24f940247..87cce35512 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessorTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessorTest.kt @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.groups.v2.processing import android.annotation.SuppressLint import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.justRun import io.mockk.mockk @@ -17,6 +16,7 @@ import org.hamcrest.Matchers.hasItem import org.hamcrest.Matchers.`is` import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -45,7 +45,6 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.requestingMember import org.thoughtcrime.securesms.database.setNewDescription import org.thoughtcrime.securesms.database.setNewTitle import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.groups.GroupId import org.thoughtcrime.securesms.groups.GroupNotAMemberException import org.thoughtcrime.securesms.groups.GroupsV2Authorization @@ -57,6 +56,7 @@ import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.testutil.SystemOutLogger import org.whispersystems.signalservice.api.NetworkResult import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupResponse @@ -90,6 +90,9 @@ class GroupsV2StateProcessorTest { private val others: List = listOf(member(otherAci)) } + @get:Rule + val appDependencies = MockAppDependenciesRule() + private lateinit var groupTable: GroupTable private lateinit var recipientTable: RecipientTable private lateinit var threadTable: ThreadTable @@ -103,10 +106,6 @@ class GroupsV2StateProcessorTest { @Before fun setUp() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - mockkObject(SignalStore) every { SignalStore.internal.gv2IgnoreP2PChanges } returns false diff --git a/app/src/test/java/org/thoughtcrime/securesms/keyvalue/PaymentsValuesTest.kt b/app/src/test/java/org/thoughtcrime/securesms/keyvalue/PaymentsValuesTest.kt index 4d6c7ed7d2..0460bef5c8 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/keyvalue/PaymentsValuesTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/keyvalue/PaymentsValuesTest.kt @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.keyvalue import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockk import io.mockk.mockkObject @@ -9,26 +8,25 @@ import io.mockk.unmockkAll import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.util.RemoteConfig @RunWith(RobolectricTestRunner::class) @Config(manifest = Config.NONE, application = Application::class) class PaymentsValuesTest { + @get:Rule + val appDependencies = MockAppDependenciesRule() + private lateinit var paymentValues: PaymentsValues @Before fun setup() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - mockkObject(RemoteConfig) mockkObject(SignalStore) diff --git a/app/src/test/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepositoryTest.kt b/app/src/test/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepositoryTest.kt index 948bcff8f7..7ad119773b 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepositoryTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepositoryTest.kt @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.megaphone import android.app.Application import android.net.Uri -import androidx.test.core.app.ApplicationProvider import io.mockk.clearMocks import io.mockk.every import io.mockk.mockk @@ -14,8 +13,8 @@ import org.hamcrest.Matchers.nullValue import org.json.JSONObject import org.junit.After import org.junit.AfterClass -import org.junit.Before import org.junit.BeforeClass +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -23,8 +22,7 @@ import org.robolectric.annotation.Config import org.thoughtcrime.securesms.database.RemoteMegaphoneTable import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.RemoteMegaphoneRecord -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.util.toMillis import java.time.LocalDateTime import java.util.UUID @@ -37,12 +35,8 @@ import java.util.UUID @Config(manifest = Config.NONE, application = Application::class) class RemoteMegaphoneRepositoryTest { - @Before - fun setUp() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - } + @get:Rule + val appDependencies = MockAppDependenciesRule() @After fun tearDown() { diff --git a/app/src/test/java/org/thoughtcrime/securesms/notifications/MarkReadReceiverTest.kt b/app/src/test/java/org/thoughtcrime/securesms/notifications/MarkReadReceiverTest.kt index f0dad25090..56b5239710 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/notifications/MarkReadReceiverTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/notifications/MarkReadReceiverTest.kt @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.notifications import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockkObject import io.mockk.mockkStatic @@ -9,6 +8,7 @@ import io.mockk.unmockkAll import org.junit.After import org.junit.Assert import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -20,13 +20,13 @@ import org.thoughtcrime.securesms.database.MessageTable.SyncMessageId import org.thoughtcrime.securesms.database.model.MessageId import org.thoughtcrime.securesms.database.model.StoryType import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.jobmanager.Job import org.thoughtcrime.securesms.jobmanager.JobManager import org.thoughtcrime.securesms.jobmanager.JsonJobData import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.util.TextSecurePreferences import java.util.LinkedList @@ -34,17 +34,13 @@ import java.util.LinkedList @Config(manifest = Config.NONE, application = Application::class) class MarkReadReceiverTest { + @get:Rule + val appDependencies = MockAppDependenciesRule() + private val jobs: MutableList = LinkedList() @Before fun setUp() { - if (!AppDependencies.isInitialized) { - AppDependencies.init( - ApplicationProvider.getApplicationContext(), - MockApplicationDependencyProvider() - ) - } - val jobManager: JobManager = AppDependencies.jobManager every { jobManager.add(capture(jobs)) } returns Unit diff --git a/app/src/test/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfilesTest.kt b/app/src/test/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfilesTest.kt index dcca57fbc1..597661a40b 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfilesTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfilesTest.kt @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.notifications.profiles import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockk import io.mockk.mockkObject @@ -11,14 +10,14 @@ import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.nullValue import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.keyvalue.NotificationProfileValues import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.util.toMillis import java.time.DayOfWeek import java.time.LocalDateTime @@ -29,6 +28,9 @@ import java.time.ZoneOffset @Config(manifest = Config.NONE, application = Application::class) class NotificationProfilesTest { + @get:Rule + val appDependencies = MockAppDependenciesRule() + private val sunday830am: LocalDateTime = LocalDateTime.of(2021, 7, 4, 8, 30, 0) private val sunday9am: LocalDateTime = LocalDateTime.of(2021, 7, 4, 9, 0, 0) private val sunday930am: LocalDateTime = LocalDateTime.of(2021, 7, 4, 9, 30, 0) @@ -55,10 +57,6 @@ class NotificationProfilesTest { @Before fun setUp() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - notificationProfileValues = mockk() every { notificationProfileValues.manuallyEnabledUntil } returns 0 every { notificationProfileValues.manuallyDisabledAt } returns 0 diff --git a/app/src/test/java/org/thoughtcrime/securesms/registration/util/RegistrationUtilTest.kt b/app/src/test/java/org/thoughtcrime/securesms/registration/util/RegistrationUtilTest.kt new file mode 100644 index 0000000000..b135c1253a --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/registration/util/RegistrationUtilTest.kt @@ -0,0 +1,143 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.util + +import android.app.Application +import io.mockk.Runs +import io.mockk.every +import io.mockk.just +import io.mockk.mockkObject +import io.mockk.unmockkAll +import io.mockk.verify +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.signal.core.util.logging.Log.initialize +import org.thoughtcrime.securesms.assertIs +import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues +import org.thoughtcrime.securesms.profiles.ProfileName +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.testutil.LogRecorder +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule +import org.thoughtcrime.securesms.testutil.MockSignalStoreRule +import org.thoughtcrime.securesms.util.RemoteConfig + +@RunWith(RobolectricTestRunner::class) +@Config(application = Application::class, manifest = Config.NONE) +class RegistrationUtilTest { + + @get:Rule + val signalStore = MockSignalStoreRule(relaxed = setOf(PhoneNumberPrivacyValues::class)) + + @get:Rule + val appDependencies = MockAppDependenciesRule() + + private lateinit var logRecorder: LogRecorder + + @Before + fun setup() { + mockkObject(Recipient) + mockkObject(RemoteConfig) + every { RemoteConfig.init() } just Runs + + logRecorder = LogRecorder() + initialize(logRecorder) + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun maybeMarkRegistrationComplete_allValidNoRestoreOption() { + every { signalStore.registration.isRegistrationComplete } returns false + every { signalStore.account.isRegistered } returns true + every { Recipient.self() } returns Recipient(profileName = ProfileName.fromParts("Dark", "Helmet")) + every { signalStore.svr.hasOptedInWithAccess() } returns true + every { RemoteConfig.restoreAfterRegistration } returns false + + RegistrationUtil.maybeMarkRegistrationComplete() + + verify { signalStore.registration.markRegistrationComplete() } + } + + @Test + fun maybeMarkRegistrationComplete_allValidNoRestoreOptionSvrOptOut() { + every { signalStore.registration.isRegistrationComplete } returns false + every { signalStore.account.isRegistered } returns true + every { Recipient.self() } returns Recipient(profileName = ProfileName.fromParts("Dark", "Helmet")) + every { signalStore.svr.hasOptedInWithAccess() } returns false + every { signalStore.svr.hasOptedOut() } returns true + every { RemoteConfig.restoreAfterRegistration } returns false + + RegistrationUtil.maybeMarkRegistrationComplete() + + verify { signalStore.registration.markRegistrationComplete() } + } + + @Test + fun maybeMarkRegistrationComplete_allValidWithRestoreOption() { + every { signalStore.registration.isRegistrationComplete } returns false + every { signalStore.account.isRegistered } returns true + every { Recipient.self() } returns Recipient(profileName = ProfileName.fromParts("Dark", "Helmet")) + every { signalStore.svr.hasOptedInWithAccess() } returns true + every { RemoteConfig.restoreAfterRegistration } returns true + every { signalStore.registration.hasSkippedTransferOrRestore() } returns true + + RegistrationUtil.maybeMarkRegistrationComplete() + + verify { signalStore.registration.markRegistrationComplete() } + } + + @Test + fun maybeMarkRegistrationComplete_missingData() { + every { signalStore.registration.isRegistrationComplete } returns false + every { signalStore.account.isRegistered } returns false + + RegistrationUtil.maybeMarkRegistrationComplete() + + every { signalStore.account.isRegistered } returns true + every { Recipient.self() } returns Recipient(profileName = ProfileName.EMPTY) + + RegistrationUtil.maybeMarkRegistrationComplete() + + every { Recipient.self() } returns Recipient(profileName = ProfileName.fromParts("Dark", "Helmet")) + every { signalStore.svr.hasOptedInWithAccess() } returns false + every { signalStore.svr.hasOptedOut() } returns false + + RegistrationUtil.maybeMarkRegistrationComplete() + + every { signalStore.svr.hasOptedInWithAccess() } returns true + every { RemoteConfig.restoreAfterRegistration } returns true + every { signalStore.registration.hasSkippedTransferOrRestore() } returns false + every { signalStore.registration.hasCompletedRestore() } returns false + + RegistrationUtil.maybeMarkRegistrationComplete() + + verify(exactly = 0) { signalStore.registration.markRegistrationComplete() } + + val regUtilLogs = logRecorder.information.filter { it.tag == "RegistrationUtil" } + regUtilLogs.size assertIs 4 + regUtilLogs.all { it.message == "Registration is not yet complete." } assertIs true + } + + @Test + fun maybeMarkRegistrationComplete_alreadyMarked() { + every { signalStore.registration.isRegistrationComplete } returns true + + RegistrationUtil.maybeMarkRegistrationComplete() + + verify(exactly = 0) { signalStore.registration.markRegistrationComplete() } + + val regUtilLogs = logRecorder.information.filter { it.tag == "RegistrationUtil" } + regUtilLogs.size assertIs 0 + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/storage/ContactRecordProcessorTest.kt b/app/src/test/java/org/thoughtcrime/securesms/storage/ContactRecordProcessorTest.kt index ac07974d84..463ac955ec 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/storage/ContactRecordProcessorTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/storage/ContactRecordProcessorTest.kt @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.storage import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockk import io.mockk.mockkObject @@ -12,16 +11,16 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.BeforeClass +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.database.RecipientTable -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.testutil.EmptyLogger +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.whispersystems.signalservice.api.push.ServiceId.ACI import org.whispersystems.signalservice.api.push.ServiceId.PNI import org.whispersystems.signalservice.api.storage.SignalContactRecord @@ -33,14 +32,13 @@ import java.util.UUID @Config(application = Application::class) class ContactRecordProcessorTest { + @get:Rule + val appDependencies = MockAppDependenciesRule() + lateinit var recipientTable: RecipientTable @Before fun setup() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - mockkObject(SignalStore) every { SignalStore.account.isPrimaryDevice } returns true diff --git a/app/src/test/java/org/thoughtcrime/securesms/testutil/MockAppDependenciesRule.kt b/app/src/test/java/org/thoughtcrime/securesms/testutil/MockAppDependenciesRule.kt new file mode 100644 index 0000000000..386c16bf8d --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/testutil/MockAppDependenciesRule.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.testutil + +import androidx.test.core.app.ApplicationProvider +import io.mockk.clearMocks +import org.junit.rules.ExternalResource +import org.thoughtcrime.securesms.dependencies.AppDependencies +import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider +import kotlin.reflect.KVisibility +import kotlin.reflect.full.memberProperties + +/** + * Facilitates mocking and clearing components of [AppDependencies]. Clearing is particularly important as the + * mocks will be reused since [AppDependencies] is scoped to the entire test suite and stays initialized with prior + * test runs leading to unpredictable results based on how tests are run. + */ +class MockAppDependenciesRule : ExternalResource() { + + private val skipList = setOf( + "application", + "databaseObserver", + "groupsV2Authorization", + "isInitialized", + "okHttpClient", + "signalOkHttpClient", + "webSocketObserver" + ) + + private val properties = AppDependencies::class + .memberProperties + .filter { it.visibility == KVisibility.PUBLIC } + .filterNot { skipList.contains(it.name) } + + override fun before() { + if (!AppDependencies.isInitialized) { + AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) + } + } + + override fun after() { + properties + .forEach { property -> + property.get(AppDependencies)?.let { clearMocks(it) } + } + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/testutil/MockSignalStoreRule.kt b/app/src/test/java/org/thoughtcrime/securesms/testutil/MockSignalStoreRule.kt new file mode 100644 index 0000000000..a0d82e6891 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/testutil/MockSignalStoreRule.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.testutil + +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.unmockkObject +import org.junit.rules.ExternalResource +import org.thoughtcrime.securesms.keyvalue.AccountValues +import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues +import org.thoughtcrime.securesms.keyvalue.RegistrationValues +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.keyvalue.SvrValues +import kotlin.reflect.KClass + +/** + * Mocks [SignalStore] to return mock versions of the various values. Mocks will default to not be relaxed (each + * method call on them will need to be mocked) except for unit functions which will do nothing. + * + * Expand mocked values as necessary when needed. + * + * @param relaxed Set of value classes that should default to relaxed thus defaulting all methods. Useful + * when value is not part of the input state under test but called within the under test code. + */ +@Suppress("MemberVisibilityCanBePrivate") +class MockSignalStoreRule(private val relaxed: Set> = emptySet()) : ExternalResource() { + + lateinit var account: AccountValues + private set + + lateinit var phoneNumberPrivacy: PhoneNumberPrivacyValues + private set + + lateinit var registration: RegistrationValues + private set + + lateinit var svr: SvrValues + private set + + override fun before() { + account = mockk(relaxed = relaxed.contains(AccountValues::class), relaxUnitFun = true) + phoneNumberPrivacy = mockk(relaxed = relaxed.contains(PhoneNumberPrivacyValues::class), relaxUnitFun = true) + registration = mockk(relaxed = relaxed.contains(RegistrationValues::class), relaxUnitFun = true) + svr = mockk(relaxed = relaxed.contains(SvrValues::class), relaxUnitFun = true) + + mockkObject(SignalStore) + every { SignalStore.account } returns account + every { SignalStore.phoneNumberPrivacy } returns phoneNumberPrivacy + every { SignalStore.registration } returns registration + every { SignalStore.svr } returns svr + } + + override fun after() { + unmockkObject(SignalStore) + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/util/SignalMeUtilText_parseE164FromLink.kt b/app/src/test/java/org/thoughtcrime/securesms/util/SignalMeUtilText_parseE164FromLink.kt index eff351652a..df4de4de3d 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/util/SignalMeUtilText_parseE164FromLink.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/util/SignalMeUtilText_parseE164FromLink.kt @@ -1,33 +1,31 @@ package org.thoughtcrime.securesms.util import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockkObject import io.mockk.unmockkAll import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.ParameterizedRobolectricTestRunner import org.robolectric.annotation.Config -import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.dependencies.AppDependencies.application -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.util.SignalMeUtil.parseE164FromLink @RunWith(ParameterizedRobolectricTestRunner::class) @Config(manifest = Config.NONE, application = Application::class) class SignalMeUtilText_parseE164FromLink(private val input: String?, private val output: String?) { + @get:Rule + val appDependencies = MockAppDependenciesRule() + @Before fun setUp() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - mockkObject(SignalStore) every { SignalStore.account.e164 } returns "+15555555555" } From a2330f443acb41157840386ee99be854d1cef8d6 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Fri, 22 Nov 2024 10:19:35 -0500 Subject: [PATCH 09/56] Use AEP for regv3 flows. --- .../backup/v2/ArchiveImportExportTests.kt | 4 +--- .../MessageBackupsFlowFragment.kt | 2 +- .../subscription/MessageBackupsFlowState.kt | 4 ++-- .../MessageBackupsKeyRecordScreen.kt | 11 +++++----- .../remote/BackupKeyDisplayFragment.kt | 2 +- .../securesms/keyvalue/SvrValues.kt | 20 ------------------- .../data/QuickRegistrationRepository.kt | 3 +-- .../ui/RegistrationViewModel.kt | 9 +++++---- .../ui/restore/EnterBackupKeyFragment.kt | 4 ++-- .../ui/restore/EnterBackupKeyViewModel.kt | 20 +++---------------- 10 files changed, 21 insertions(+), 58 deletions(-) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt index 373b162685..4f2ef39b58 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt @@ -24,7 +24,6 @@ import org.thoughtcrime.securesms.backup.v2.stream.PlainTextBackupReader import org.thoughtcrime.securesms.database.KeyValueDatabase import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.whispersystems.signalservice.api.kbs.MasterKey import org.whispersystems.signalservice.api.push.ServiceId import java.io.ByteArrayInputStream import java.util.UUID @@ -40,7 +39,6 @@ class ArchiveImportExportTests { val SELF_PNI = ServiceId.PNI.from(UUID.fromString("00000000-0000-4000-8000-000000000002")) val SELF_E164 = "+10000000000" val SELF_PROFILE_KEY: ByteArray = Base64.decode("YQKRq+3DQklInaOaMcmlzZnN0m/1hzLiaONX7gB12dg=") - val MASTER_KEY = Base64.decode("sHuBMP4ToZk4tcNU+S8eBUeCt8Am5EZnvuqTBJIR4Do") } @Before @@ -274,7 +272,7 @@ class ArchiveImportExportTests { KeyValueDatabase.getInstance(AppDependencies.application).clear() SignalStore.resetCache() - SignalStore.svr.setMasterKey(MasterKey(MASTER_KEY), "1234") + SignalStore.account.resetAccountEntropyPool() SignalStore.account.setE164(SELF_E164) SignalStore.account.setAci(SELF_ACI) SignalStore.account.setPni(SELF_PNI) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt index e2fefa24df..f839bd5ab9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt @@ -103,7 +103,7 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega val context = LocalContext.current MessageBackupsKeyRecordScreen( - messageBackupKey = state.messageBackupKey, + backupKey = state.accountEntropyPool.value, onNavigationClick = viewModel::goToPreviousStage, onNextClick = viewModel::goToNextStage, onCopyToClipboardClick = { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowState.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowState.kt index 38ddcfb053..698bc87440 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowState.kt @@ -8,7 +8,7 @@ package org.thoughtcrime.securesms.backup.v2.ui.subscription import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.database.InAppPaymentTable import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.whispersystems.signalservice.api.backup.MessageBackupKey +import org.whispersystems.signalservice.api.AccountEntropyPool data class MessageBackupsFlowState( val hasBackupSubscriberAvailable: Boolean = false, @@ -18,6 +18,6 @@ data class MessageBackupsFlowState( val inAppPayment: InAppPaymentTable.InAppPayment? = null, val startScreen: MessageBackupsStage, val stage: MessageBackupsStage = startScreen, - val messageBackupKey: MessageBackupKey = SignalStore.backup.messageBackupKey, + val accountEntropyPool: AccountEntropyPool = SignalStore.account.accountEntropyPool, val failure: Throwable? = null ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsKeyRecordScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsKeyRecordScreen.kt index 8c6d5611a0..1c79475757 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsKeyRecordScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsKeyRecordScreen.kt @@ -48,10 +48,9 @@ import org.signal.core.ui.Previews import org.signal.core.ui.Scaffolds import org.signal.core.ui.SignalPreview import org.signal.core.ui.theme.SignalTheme -import org.signal.core.util.Hex import org.thoughtcrime.securesms.R -import org.whispersystems.signalservice.api.backup.MessageBackupKey import kotlin.random.Random +import kotlin.random.nextInt import org.signal.core.ui.R as CoreUiR /** @@ -61,7 +60,7 @@ import org.signal.core.ui.R as CoreUiR @OptIn(ExperimentalMaterial3Api::class) @Composable fun MessageBackupsKeyRecordScreen( - messageBackupKey: MessageBackupKey, + backupKey: String, onNavigationClick: () -> Unit = {}, onCopyToClipboardClick: (String) -> Unit = {}, onNextClick: () -> Unit = {} @@ -105,8 +104,8 @@ fun MessageBackupsKeyRecordScreen( modifier = Modifier.padding(top = 12.dp) ) - val backupKeyString = remember(messageBackupKey) { - messageBackupKey.value.toList().chunked(2).map { Hex.toStringCondensed(it.toByteArray()) }.joinToString(" ") + val backupKeyString = remember(backupKey) { + backupKey.chunked(4).joinToString(" ") } Box( @@ -259,7 +258,7 @@ private fun BottomSheetContent( private fun MessageBackupsKeyRecordScreenPreview() { Previews.Preview { MessageBackupsKeyRecordScreen( - messageBackupKey = MessageBackupKey(Random.nextBytes(32)) + backupKey = (0 until 64).map { Random.nextInt(97..122).toChar() }.joinToString("") ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/BackupKeyDisplayFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/BackupKeyDisplayFragment.kt index 5497ee7f56..8ce22a2a99 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/BackupKeyDisplayFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/BackupKeyDisplayFragment.kt @@ -19,7 +19,7 @@ class BackupKeyDisplayFragment : ComposeFragment() { @Composable override fun FragmentContent() { MessageBackupsKeyRecordScreen( - messageBackupKey = SignalStore.backup.messageBackupKey, + backupKey = SignalStore.account.accountEntropyPool.value, onNavigationClick = { findNavController().popBackStack() }, onCopyToClipboardClick = { Util.copyToClipboard(requireContext(), it) }, onNextClick = { findNavController().popBackStack() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SvrValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SvrValues.kt index 6a517d9658..8e53ddf820 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SvrValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SvrValues.kt @@ -45,26 +45,6 @@ class SvrValues internal constructor(store: KeyValueStore) : SignalStoreValues(s .commit() } - @Deprecated("Switch to restoring AEP instead") - @Synchronized - fun setMasterKey(masterKey: MasterKey, pin: String?) { - store.beginWrite().apply { -// putBlob(MASTER_KEY, masterKey.serialize()) - putLong(LAST_CREATE_FAILED_TIMESTAMP, -1) - putBoolean(OPTED_OUT, false) - - if (pin != null) { - putString(LOCK_LOCAL_PIN_HASH, localPinHash(pin)) - putString(PIN, pin) - remove(RESTORED_VIA_ACCOUNT_ENTROPY_KEY) - } else { - putBoolean(RESTORED_VIA_ACCOUNT_ENTROPY_KEY, true) - remove(LOCK_LOCAL_PIN_HASH) - remove(PIN) - } - }.commit() - } - @Synchronized fun setPin(pin: String) { store.beginWrite() diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/data/QuickRegistrationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/data/QuickRegistrationRepository.kt index b7a966bf94..d593ca658d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/data/QuickRegistrationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/data/QuickRegistrationRepository.kt @@ -11,7 +11,6 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.withContext import org.signal.core.util.Base64.decode -import org.signal.core.util.Hex import org.signal.core.util.isNotNullOrBlank import org.signal.core.util.logging.Log import org.signal.libsignal.protocol.InvalidKeyException @@ -81,7 +80,7 @@ object QuickRegistrationRepository { RegistrationProvisionMessage( e164 = SignalStore.account.requireE164(), aci = SignalStore.account.requireAci().toByteString(), - accountEntropyPool = Hex.toStringCondensed(SignalStore.svr.masterKey.serialize()), + accountEntropyPool = SignalStore.account.accountEntropyPool.value, pin = pin, platform = RegistrationProvisionMessage.Platform.ANDROID, backupTimestampMs = SignalStore.backup.lastBackupTime.coerceAtLeast(0L), diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/RegistrationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/RegistrationViewModel.kt index ff8c911de7..4e6dcc2029 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/RegistrationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/RegistrationViewModel.kt @@ -21,7 +21,6 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.updateAndGet import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.signal.core.util.Hex import org.signal.core.util.Stopwatch import org.signal.core.util.isNotNullOrBlank import org.signal.core.util.logging.Log @@ -69,6 +68,7 @@ import org.thoughtcrime.securesms.registrationv3.data.RegistrationRepository import org.thoughtcrime.securesms.util.RemoteConfig import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.dualsim.MccMncProducer +import org.whispersystems.signalservice.api.AccountEntropyPool import org.whispersystems.signalservice.api.SvrNoDataException import org.whispersystems.signalservice.api.kbs.MasterKey import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse @@ -935,9 +935,10 @@ class RegistrationViewModel : ViewModel() { setPhoneNumber(PhoneNumberUtil.getInstance().parse(e164, null)) } - // TODO [backups] use new data and not master key - val masterKey = MasterKey(Hex.fromStringCondensed(backupKey)) - SignalStore.svr.setMasterKey(masterKey, pin) + val accountEntropyPool = AccountEntropyPool(backupKey) + SignalStore.account.restoreAccountEntropyPool(accountEntropyPool) + + val masterKey = accountEntropyPool.deriveMasterKey() setRecoveryPassword(masterKey.deriveRegistrationRecoveryPassword()) verifyReRegisterInternal(context = context, pin = pin, masterKey = masterKey) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/EnterBackupKeyFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/EnterBackupKeyFragment.kt index b0aa6de96a..82406b85f2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/EnterBackupKeyFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/EnterBackupKeyFragment.kt @@ -29,7 +29,6 @@ import androidx.compose.material3.TextField import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -48,6 +47,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.fragment.findNavController import kotlinx.coroutines.launch import org.signal.core.ui.BottomSheets @@ -81,7 +81,7 @@ class EnterBackupKeyFragment : ComposeFragment() { @Composable override fun FragmentContent() { val state by viewModel.state - val sharedState by sharedViewModel.state.collectAsState() + val sharedState by sharedViewModel.state.collectAsStateWithLifecycle() EnterBackupKeyScreen( state = state, diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/EnterBackupKeyViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/EnterBackupKeyViewModel.kt index 155d0001c9..1d2a91971d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/EnterBackupKeyViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/EnterBackupKeyViewModel.kt @@ -9,14 +9,11 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel -import org.signal.core.util.Hex -import java.io.IOException class EnterBackupKeyViewModel : ViewModel() { companion object { - // TODO [backups] Set actual valid characters for key input - private val VALID_CHARACTERS = setOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') + private val INVALID_CHARACTERS = Regex("[^0-9a-zA-Z]") } private val _state = mutableStateOf( @@ -37,22 +34,11 @@ class EnterBackupKeyViewModel : ViewModel() { } private fun validate(length: Int, backupKey: String): Boolean { - if (backupKey.length != length) { - return false - } - - try { - // TODO [backups] Actually validate key with requirements instead of just hex - Hex.fromStringCondensed(backupKey) - } catch (e: IOException) { - return false - } - - return true + return backupKey.length == length } private fun String.removeIllegalCharacters(): String { - return filter { VALID_CHARACTERS.contains(it) } + return this.replace(INVALID_CHARACTERS, "") } private inline fun MutableState.update(update: T.() -> T) { From 2d0e503b488d702b5807bb5f10fba1eeb7d28b85 Mon Sep 17 00:00:00 2001 From: bepaald <38437099+bepaald@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:35:22 +0000 Subject: [PATCH 10/56] Fix creating message_fts table during database migration. Fixes #13034 Fixes #13506 --- .../database/helpers/migration/V175_FixFullTextSearchLink.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V175_FixFullTextSearchLink.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V175_FixFullTextSearchLink.kt index c1ab575ebe..394d0a9c2f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V175_FixFullTextSearchLink.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V175_FixFullTextSearchLink.kt @@ -23,6 +23,10 @@ object V175_FixFullTextSearchLink : SignalDatabaseMigration { db.execSQL("CREATE VIRTUAL TABLE message_fts USING fts5(body, thread_id UNINDEXED, content=message, content_rowid=_id)") + // The newly created search table is empty, while its content-table (message) is not. To get the search + // table in a consistent state, it needs to be rebuilt. + db.execSQL("INSERT INTO message_fts(message_fts) VALUES ('rebuild')") + db.execSQL( """ CREATE TRIGGER message_ai AFTER INSERT ON message BEGIN From b09d433d5b64883bb35f483c8ab5fc021aa8e128 Mon Sep 17 00:00:00 2001 From: bepaald <38437099+bepaald@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:53:47 +0000 Subject: [PATCH 11/56] Rebuild FTS index in V239_MessageFullTextSearchEmojiSupport. --- .../helpers/migration/V239_MessageFullTextSearchEmojiSupport.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V239_MessageFullTextSearchEmojiSupport.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V239_MessageFullTextSearchEmojiSupport.kt index 093b670385..d76ea9c211 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V239_MessageFullTextSearchEmojiSupport.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V239_MessageFullTextSearchEmojiSupport.kt @@ -43,6 +43,8 @@ object V239_MessageFullTextSearchEmojiSupport : SignalDatabaseMigration { db.execSQL("""CREATE VIRTUAL TABLE message_fts USING fts5(body, thread_id UNINDEXED, content=message, content_rowid=_id, tokenize = "unicode61 categories 'L* N* Co Sc So'")""") + db.execSQL("INSERT INTO message_fts(message_fts) VALUES ('rebuild')") + db.execSQL( """ CREATE TRIGGER message_ai AFTER INSERT ON message BEGIN From 36c47e83460cedf9fc155c6daa843f26d6407a5c Mon Sep 17 00:00:00 2001 From: bepaald <38437099+bepaald@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:54:35 +0000 Subject: [PATCH 12/56] Rebuild FTS index in V242_MessageFullTextSearchEmojiSupportV2. Resolves #13810 --- .../migration/V242_MessageFullTextSearchEmojiSupportV2.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V242_MessageFullTextSearchEmojiSupportV2.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V242_MessageFullTextSearchEmojiSupportV2.kt index eb5e7d892c..57a2183646 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V242_MessageFullTextSearchEmojiSupportV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V242_MessageFullTextSearchEmojiSupportV2.kt @@ -34,6 +34,7 @@ object V242_MessageFullTextSearchEmojiSupportV2 : SignalDatabaseMigration { db.execSQL("""CREATE VIRTUAL TABLE message_fts USING fts5(body, thread_id UNINDEXED, content=message, content_rowid=_id, tokenize = "unicode61 categories 'L* N* Co Sc So'")""") db.execSQL("INSERT INTO $FTS_TABLE_NAME ($FTS_TABLE_NAME, rank) VALUES('secure-delete', 1)") + db.execSQL("INSERT INTO $FTS_TABLE_NAME ($FTS_TABLE_NAME) VALUES('rebuild')") db.execSQL( """ From f311a25c5898c2f1cb65b3826ea0c482048f5df1 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 22 Nov 2024 16:09:32 -0400 Subject: [PATCH 13/56] Fix multiple activity finish after donation. --- .../settings/app/subscription/donate/DonateToSignalFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalFragment.kt index a0e4146443..a96bb05854 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalFragment.kt @@ -527,6 +527,6 @@ class DonateToSignalFragment : } override fun exitCheckoutFlow() { - requireActivity().finishAffinity() + requireActivity().finish() } } From 0356b01866be78c97a839787719449f37145bdd8 Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Fri, 22 Nov 2024 16:09:24 -0800 Subject: [PATCH 14/56] Fix icon gradient. --- .../ic_app_icon_signal_color_top_preview.xml | 22 +- .../ic_app_icon_signal_dark_top_preview.xml | 308 +++++++----------- ...p_icon_signal_dark_variant_top_preview.xml | 308 +++++++----------- 3 files changed, 248 insertions(+), 390 deletions(-) diff --git a/app/src/main/res/drawable/ic_app_icon_signal_color_top_preview.xml b/app/src/main/res/drawable/ic_app_icon_signal_color_top_preview.xml index 9e8ce801a7..b424a3e67d 100644 --- a/app/src/main/res/drawable/ic_app_icon_signal_color_top_preview.xml +++ b/app/src/main/res/drawable/ic_app_icon_signal_color_top_preview.xml @@ -10,20 +10,14 @@ android:pathData="M32 0A32 32 0 1 0 32 64 32 32 0 1 0 32 0z"> - - - + android:centerX="6" + android:centerY="6" + android:gradientRadius="76" + android:type="radial"> + + + + diff --git a/app/src/main/res/drawable/ic_app_icon_signal_dark_top_preview.xml b/app/src/main/res/drawable/ic_app_icon_signal_dark_top_preview.xml index 1542b7ece8..11cefa7241 100644 --- a/app/src/main/res/drawable/ic_app_icon_signal_dark_top_preview.xml +++ b/app/src/main/res/drawable/ic_app_icon_signal_dark_top_preview.xml @@ -13,17 +13,13 @@ android:pathData="M32 12c1.04 0 2.06 0.08 3.05 0.23l-0.28 1.85c-0.9-0.13-1.83-0.2-2.77-0.2s-1.86 0.07-2.77 0.2l-0.28-1.85c1-0.15 2.01-0.23 3.05-0.23Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -31,17 +27,13 @@ android:pathData="M36.75 12.57l-0.45 1.82c1.83 0.44 3.55 1.16 5.11 2.12l0.97-1.6c-1.72-1.06-3.62-1.85-5.63-2.34Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -49,17 +41,13 @@ android:pathData="M43.82 15.86l-1.1 1.52c1.49 1.1 2.8 2.41 3.9 3.9l1.52-1.1c-1.21-1.65-2.67-3.1-4.32-4.32Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -67,17 +55,13 @@ android:pathData="M49.1 21.62l-1.6 0.97c0.95 1.56 1.67 3.28 2.11 5.1l1.82-0.44c-0.49-2.01-1.28-3.9-2.33-5.63Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -85,17 +69,13 @@ android:pathData="M51.77 28.95l-1.85 0.28c0.13 0.9 0.2 1.83 0.2 2.77s-0.07 1.86-0.2 2.77l1.85 0.28c0.15-1 0.23-2.01 0.23-3.05s-0.08-2.06-0.23-3.05Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -103,17 +83,13 @@ android:pathData="M47.5 41.41c0.95-1.56 1.67-3.28 2.11-5.1l1.82 0.44c-0.49 2.01-1.28 3.9-2.33 5.63l-1.6-0.97Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -121,17 +97,13 @@ android:pathData="M46.62 42.71l1.52 1.11c-1.21 1.65-2.67 3.1-4.32 4.32l-1.1-1.52c1.49-1.1 2.8-2.41 3.9-3.9Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -139,17 +111,13 @@ android:pathData="M41.41 47.5l0.97 1.6c-1.72 1.05-3.62 1.84-5.63 2.33l-0.45-1.82c1.83-0.44 3.55-1.16 5.11-2.12Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -157,17 +125,13 @@ android:pathData="M34.77 49.92l0.28 1.85C34.05 51.92 33.04 52 32 52s-2.06-0.08-3.05-0.23l0.28-1.85c0.9 0.13 1.83 0.2 2.77 0.2s1.86-0.07 2.77-0.2Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -175,17 +139,13 @@ android:pathData="M27.7 49.61l-0.45 1.82c-1.51-0.37-2.96-0.9-4.32-1.6l-1.9 0.44-0.42-1.82 2.57-0.6 0.6 0.31c1.23 0.63 2.55 1.12 3.92 1.45Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -193,17 +153,13 @@ android:pathData="M19.03 48.82l0.42 1.82-3.25 0.76c-2.17 0.5-4.1-1.43-3.6-3.6l0.76-3.25 1.82 0.42-0.76 3.26c-0.19 0.8 0.54 1.54 1.35 1.35l3.26-0.76Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -211,17 +167,13 @@ android:pathData="M15.55 43.39l-1.82-0.43 0.44-1.9c-0.7-1.35-1.23-2.8-1.6-4.31l1.82-0.45c0.33 1.37 0.82 2.69 1.45 3.91l0.3 0.62-0.59 2.56Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -229,17 +181,13 @@ android:pathData="M14.08 34.77l-1.85 0.28C12.08 34.05 12 33.04 12 32s0.08-2.06 0.23-3.05l1.85 0.28c-0.13 0.9-0.2 1.83-0.2 2.77s0.07 1.86 0.2 2.77Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -247,17 +195,13 @@ android:pathData="M14.39 27.7l-1.82-0.45c0.49-2.01 1.28-3.9 2.33-5.63l1.6 0.97c-0.95 1.56-1.67 3.28-2.11 5.1Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -265,17 +209,13 @@ android:pathData="M17.38 21.29l-1.52-1.11c1.21-1.65 2.67-3.1 4.32-4.32l1.1 1.52c-1.49 1.1-2.8 2.41-3.9 3.9Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -283,17 +223,13 @@ android:pathData="M22.59 16.5l-0.97-1.6c1.72-1.05 3.62-1.84 5.63-2.33l0.45 1.82c-1.83 0.44-3.55 1.16-5.11 2.12Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + @@ -301,20 +237,16 @@ android:pathData="M48.25 32c0 8.97-7.28 16.25-16.25 16.25-2.85 0-5.52-0.73-7.85-2.02-0.22-0.12-0.48-0.16-0.73-0.1l-7.23 1.68 1.68-7.23c0.06-0.25 0.02-0.5-0.1-0.73-1.29-2.33-2.02-5-2.02-7.85 0-8.97 7.28-16.25 16.25-16.25 8.97 0 16.25 7.28 16.25 16.25Z"> - - + android:centerX="32" + android:centerY="45" + android:gradientRadius="41" + android:type="radial"> + + + - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_app_icon_signal_dark_variant_top_preview.xml b/app/src/main/res/drawable/ic_app_icon_signal_dark_variant_top_preview.xml index 964289afd7..b88c055941 100644 --- a/app/src/main/res/drawable/ic_app_icon_signal_dark_variant_top_preview.xml +++ b/app/src/main/res/drawable/ic_app_icon_signal_dark_variant_top_preview.xml @@ -13,17 +13,13 @@ android:pathData="M32 12c1.04 0 2.06 0.08 3.05 0.23l-0.28 1.85c-0.9-0.13-1.83-0.2-2.77-0.2s-1.86 0.07-2.77 0.2l-0.28-1.85c1-0.15 2.01-0.23 3.05-0.23Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -31,17 +27,13 @@ android:pathData="M36.75 12.57l-0.45 1.82c1.83 0.44 3.55 1.16 5.11 2.12l0.97-1.6c-1.72-1.06-3.62-1.85-5.63-2.34Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -49,17 +41,13 @@ android:pathData="M43.82 15.86l-1.1 1.52c1.49 1.1 2.8 2.41 3.9 3.9l1.52-1.1c-1.21-1.65-2.67-3.1-4.32-4.32Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -67,17 +55,13 @@ android:pathData="M49.1 21.62l-1.6 0.97c0.95 1.56 1.67 3.28 2.11 5.1l1.82-0.44c-0.49-2.01-1.28-3.9-2.33-5.63Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -85,17 +69,13 @@ android:pathData="M51.77 28.95l-1.85 0.28c0.13 0.9 0.2 1.83 0.2 2.77s-0.07 1.86-0.2 2.77l1.85 0.28c0.15-1 0.23-2.01 0.23-3.05s-0.08-2.06-0.23-3.05Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -103,17 +83,13 @@ android:pathData="M47.5 41.41c0.95-1.56 1.67-3.28 2.11-5.1l1.82 0.44c-0.49 2.01-1.28 3.9-2.33 5.63l-1.6-0.97Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -121,17 +97,13 @@ android:pathData="M46.62 42.71l1.52 1.11c-1.21 1.65-2.67 3.1-4.32 4.32l-1.1-1.52c1.49-1.1 2.8-2.41 3.9-3.9Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -139,17 +111,13 @@ android:pathData="M41.41 47.5l0.97 1.6c-1.72 1.05-3.62 1.84-5.63 2.33l-0.45-1.82c1.83-0.44 3.55-1.16 5.11-2.12Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -157,17 +125,13 @@ android:pathData="M34.77 49.92l0.28 1.85C34.05 51.92 33.04 52 32 52s-2.06-0.08-3.05-0.23l0.28-1.85c0.9 0.13 1.83 0.2 2.77 0.2s1.86-0.07 2.77-0.2Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -175,17 +139,13 @@ android:pathData="M27.7 49.61l-0.45 1.82c-1.51-0.37-2.96-0.9-4.32-1.6l-1.9 0.44-0.42-1.82 2.57-0.6 0.6 0.31c1.23 0.63 2.55 1.12 3.92 1.45Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -193,17 +153,13 @@ android:pathData="M19.03 48.82l0.42 1.82-3.25 0.76c-2.17 0.5-4.1-1.43-3.6-3.6l0.76-3.25 1.82 0.42-0.76 3.26c-0.19 0.8 0.54 1.54 1.35 1.35l3.26-0.76Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -211,17 +167,13 @@ android:pathData="M15.55 43.39l-1.82-0.43 0.44-1.9c-0.7-1.35-1.23-2.8-1.6-4.31l1.82-0.45c0.33 1.37 0.82 2.69 1.45 3.91l0.3 0.62-0.59 2.56Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -229,17 +181,13 @@ android:pathData="M14.08 34.77l-1.85 0.28C12.08 34.05 12 33.04 12 32s0.08-2.06 0.23-3.05l1.85 0.28c-0.13 0.9-0.2 1.83-0.2 2.77s0.07 1.86 0.2 2.77Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -247,17 +195,13 @@ android:pathData="M14.39 27.7l-1.82-0.45c0.49-2.01 1.28-3.9 2.33-5.63l1.6 0.97c-0.95 1.56-1.67 3.28-2.11 5.1Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -265,17 +209,13 @@ android:pathData="M17.38 21.29l-1.52-1.11c1.21-1.65 2.67-3.1 4.32-4.32l1.1 1.52c-1.49 1.1-2.8 2.41-3.9 3.9Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -283,17 +223,13 @@ android:pathData="M22.59 16.5l-0.97-1.6c1.72-1.05 3.62-1.84 5.63-2.33l0.45 1.82c-1.83 0.44-3.55 1.16-5.11 2.12Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + @@ -301,20 +237,16 @@ android:pathData="M48.25 32c0 8.97-7.28 16.25-16.25 16.25-2.85 0-5.52-0.73-7.85-2.02-0.22-0.12-0.48-0.16-0.73-0.1l-7.23 1.68 1.68-7.23c0.06-0.25 0.02-0.5-0.1-0.73-1.29-2.33-2.02-5-2.02-7.85 0-8.97 7.28-16.25 16.25-16.25 8.97 0 16.25 7.28 16.25 16.25Z"> - - + android:centerX="56" + android:centerY="62" + android:gradientRadius="49.5" + android:type="radial"> + + + - + \ No newline at end of file From 9833101cd1300f35567918a0d3d6856c91a138c4 Mon Sep 17 00:00:00 2001 From: andrew-signal Date: Sat, 23 Nov 2024 18:15:14 -0500 Subject: [PATCH 15/56] Use ChatListener to get connection interrupted event from libsignal; clear connection state when received --- .../ApplicationDependencyProvider.java | 2 +- .../websocket/LibSignalChatConnection.kt | 18 +++++++- .../websocket/LibSignalNetworkExtensions.kt | 8 ++-- .../websocket/LibSignalChatConnectionTest.kt | 45 ++++++++++++++++++- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java index 1fff6ff1f4..948c40ae6f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java @@ -427,7 +427,7 @@ public WebSocketConnection createUnidentifiedWebSocket() { BuildConfig.SIGNAL_AGENT, healthMonitor, Stories.isFeatureEnabled(), - LibSignalNetworkExtensions.createChatService(libSignalNetworkSupplier.get(), null, Stories.isFeatureEnabled()), + LibSignalNetworkExtensions.createChatService(libSignalNetworkSupplier.get(), null, Stories.isFeatureEnabled(), null), shadowPercentage, bridge ); diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnection.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnection.kt index 00f7f19aea..181b7c8d6e 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnection.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnection.kt @@ -12,7 +12,9 @@ import io.reactivex.rxjava3.subjects.BehaviorSubject import io.reactivex.rxjava3.subjects.SingleSubject import org.signal.core.util.logging.Log import org.signal.libsignal.net.AuthenticatedChatService +import org.signal.libsignal.net.ChatListener import org.signal.libsignal.net.ChatService +import org.signal.libsignal.net.ChatServiceException import org.signal.libsignal.net.Network import org.signal.libsignal.net.UnauthenticatedChatService import org.whispersystems.signalservice.api.util.CredentialsProvider @@ -101,7 +103,7 @@ class LibSignalChatConnection( } Log.i(TAG, "$name Connecting...") - chatService = network.createChatService(credentialsProvider, receiveStories).apply { + chatService = network.createChatService(credentialsProvider, receiveStories, listener).apply { state.onNext(WebSocketConnectionState.CONNECTING) connect().whenComplete( onSuccess = { debugInfo -> @@ -220,4 +222,18 @@ class LibSignalChatConnection( override fun sendResponse(response: WebSocketResponseMessage?) { throw NotImplementedError() } + + private val listener = object : ChatListener { + override fun onIncomingMessage(chat: ChatService?, envelope: ByteArray?, serverDeliveryTimestamp: Long, sendAck: ChatListener.ServerMessageAck?) { + throw NotImplementedError() + } + + override fun onConnectionInterrupted(chat: ChatService?, disconnectReason: ChatServiceException?) { + CHAT_SERVICE_LOCK.withLock { + Log.i(TAG, "connection interrupted", disconnectReason) + state.onNext(WebSocketConnectionState.DISCONNECTED) + chatService = null + } + } + } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalNetworkExtensions.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalNetworkExtensions.kt index e17862c233..e3b8d80fd1 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalNetworkExtensions.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/LibSignalNetworkExtensions.kt @@ -7,6 +7,7 @@ package org.whispersystems.signalservice.internal.websocket import org.signal.core.util.orNull +import org.signal.libsignal.net.ChatListener import org.signal.libsignal.net.ChatService import org.signal.libsignal.net.Network import org.whispersystems.signalservice.api.util.CredentialsProvider @@ -17,14 +18,15 @@ import org.whispersystems.signalservice.internal.configuration.SignalServiceConf */ fun Network.createChatService( credentialsProvider: CredentialsProvider? = null, - receiveStories: Boolean + receiveStories: Boolean, + listener: ChatListener? = null ): ChatService { val username = credentialsProvider?.username ?: "" val password = credentialsProvider?.password ?: "" return if (username.isEmpty() && password.isEmpty()) { - this.createUnauthChatService(null) + this.createUnauthChatService(listener) } else { - this.createAuthChatService(username, password, receiveStories, null) + this.createAuthChatService(username, password, receiveStories, listener) } } diff --git a/libsignal-service/src/test/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnectionTest.kt b/libsignal-service/src/test/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnectionTest.kt index 6d24bc6558..12f0971860 100644 --- a/libsignal-service/src/test/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnectionTest.kt +++ b/libsignal-service/src/test/java/org/whispersystems/signalservice/internal/websocket/LibSignalChatConnectionTest.kt @@ -9,8 +9,10 @@ import io.reactivex.rxjava3.observers.TestObserver import org.junit.Before import org.junit.Test import org.signal.libsignal.internal.CompletableFuture +import org.signal.libsignal.net.ChatListener import org.signal.libsignal.net.ChatService import org.signal.libsignal.net.ChatService.DebugInfo +import org.signal.libsignal.net.ChatServiceException import org.signal.libsignal.net.IpType import org.signal.libsignal.net.Network import org.whispersystems.signalservice.api.websocket.HealthMonitor @@ -29,6 +31,7 @@ class LibSignalChatConnectionTest { private val chatService = mockk() private val network = mockk() private val connection = LibSignalChatConnection("test", network, null, false, healthMonitor) + private var chatListener: ChatListener? = null @Before fun before() { @@ -36,7 +39,14 @@ class LibSignalChatConnectionTest { mockkStatic(Network::createChatService) every { healthMonitor.onMessageError(any(), any()) } every { healthMonitor.onKeepAliveResponse(any(), any()) } - every { network.createChatService(any(), any()) } answers { chatService } + every { network.createChatService(any(), any(), any()) } answers { + // When mocking static methods in mockk, the mock target is included as the first + // argument in the answers block. This results in the thirdArgument() convenience method + // being off-by-one. Since we are interested in the last argument to createChatService, we need + // to manually fetch it from the args array and cast it ourselves. + chatListener = args[3] as ChatListener? + chatService + } } @Test @@ -276,6 +286,39 @@ class LibSignalChatConnectionTest { } } + @Test + fun connectionInterrupted() { + val disconnectReason = ChatServiceException("simulated interrupt") + val connectLatch = CountDownLatch(1) + + every { chatService.connect() } answers { + delay { + it.complete(DEBUG_INFO) + connectLatch.countDown() + } + } + + connection.connect() + connectLatch.await(100, TimeUnit.MILLISECONDS) + + val observer = TestObserver() + connection.state.subscribe(observer) + + chatListener!!.onConnectionInterrupted(chatService, disconnectReason) + + observer.assertNotComplete() + observer.assertValues( + // We start in the connected state + WebSocketConnectionState.CONNECTED, + // Disconnects as a result of the connection interrupted event + WebSocketConnectionState.DISCONNECTED + ) + verify(exactly = 0) { + healthMonitor.onKeepAliveResponse(any(), any()) + healthMonitor.onMessageError(any(), any()) + } + } + private fun delay(action: ((CompletableFuture) -> Unit)): CompletableFuture { val future = CompletableFuture() executor.submit { From f42bd0f374a99765a761354fae52f65736f539da Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 25 Nov 2024 09:56:53 -0500 Subject: [PATCH 16/56] Flesh out restore paths for regv3. --- .../securesms/backup/v2/BackupRepository.kt | 48 ++++-- .../securesms/keyvalue/BackupValues.kt | 4 + .../securesms/keyvalue/RegistrationValues.kt | 3 + .../data/QuickRegistrationRepository.kt | 9 +- .../ui/restore/NoBackupToRestoreFragment.kt | 118 +++++++++++++++ .../ui/restore/RemoteRestoreActivity.kt | 139 +++++++++++------- .../ui/restore/RemoteRestoreViewModel.kt | 47 ++++-- .../ui/restore/RestoreViaQrFragment.kt | 8 +- .../ui/restore/RestoreViaQrViewModel.kt | 13 ++ .../ui/restore/SelectRestoreMethodScreen.kt | 2 +- .../securesms/restore/RestoreActivity.kt | 10 +- .../securesms/restore/RestoreViewModel.kt | 28 ++++ .../selection/SelectRestoreMethodFragment.kt | 10 +- .../res/drawable/symbol_device_phone_24.xml | 16 ++ .../main/res/navigation/registration_v3.xml | 29 +++- app/src/main/res/values/strings.xml | 22 ++- .../main/java/org/signal/core/ui/Dialogs.kt | 3 +- .../protowire/RegistrationProvisioning.proto | 7 +- 18 files changed, 415 insertions(+), 101 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/NoBackupToRestoreFragment.kt create mode 100644 app/src/main/res/drawable/symbol_device_phone_24.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index fd2b701221..9c57ebabc7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -1164,25 +1164,43 @@ object BackupRepository { } fun restoreBackupTier(aci: ACI): MessageBackupTier? { - // TODO: more complete error handling - try { - val lastModified = getBackupFileLastModified().successOrThrow() - if (lastModified != null) { - SignalStore.backup.lastBackupTime = lastModified.toMillis() + val tierResult = getBackupTier(aci) + when { + tierResult is NetworkResult.Success -> { + SignalStore.backup.backupTier = tierResult.result + Log.d(TAG, "Backup tier restored: ${SignalStore.backup.backupTier}") + } + + tierResult is NetworkResult.StatusCodeError && tierResult.code == 404 -> { + Log.i(TAG, "Backups not enabled") + SignalStore.backup.backupTier = null + } + + else -> { + Log.w(TAG, "Could not retrieve backup tier.", tierResult.getCause()) + return SignalStore.backup.backupTier } - } catch (e: Exception) { - Log.i(TAG, "Could not check for backup file.", e) - SignalStore.backup.backupTier = null - return null - } - SignalStore.backup.backupTier = try { - getBackupTier(aci).successOrThrow() - } catch (e: Exception) { - Log.i(TAG, "Could not retrieve backup tier.", e) - null } + SignalStore.backup.isBackupTierRestored = true + if (SignalStore.backup.backupTier != null) { + val timestampResult = getBackupFileLastModified() + when { + timestampResult is NetworkResult.Success -> { + timestampResult.result?.let { SignalStore.backup.lastBackupTime = it.toMillis() } + } + + timestampResult is NetworkResult.StatusCodeError && timestampResult.code == 404 -> { + Log.i(TAG, "No backup file exists") + SignalStore.backup.lastBackupTime = 0L + } + + else -> { + Log.w(TAG, "Could not check for backup file.", timestampResult.getCause()) + } + } + SignalStore.uiHints.markHasEverEnabledRemoteBackups() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt index 2282fe09cd..ef0ed8b456 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt @@ -34,6 +34,7 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { private const val KEY_BACKUP_USED_MEDIA_SPACE = "backup.usedMediaSpace" private const val KEY_BACKUP_LAST_PROTO_SIZE = "backup.lastProtoSize" private const val KEY_BACKUP_TIER = "backup.backupTier" + private const val KEY_BACKUP_TIER_RESTORED = "backup.backupTierRestored" private const val KEY_LATEST_BACKUP_TIER = "backup.latestBackupTier" private const val KEY_LAST_CHECK_IN_MILLIS = "backup.lastCheckInMilliseconds" private const val KEY_LAST_CHECK_IN_SNOOZE_MILLIS = "backup.lastCheckInSnoozeMilliseconds" @@ -167,12 +168,15 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { store.beginWrite() .putLong(KEY_BACKUP_TIER, serializedValue) .putLong(KEY_LATEST_BACKUP_TIER, serializedValue) + .putBoolean(KEY_BACKUP_TIER_RESTORED, true) .apply() } else { putLong(KEY_BACKUP_TIER, serializedValue) } } + var isBackupTierRestored: Boolean by booleanValue(KEY_BACKUP_TIER_RESTORED, false) + /** * When uploading a backup, we store the progress state here so that it can remain across app restarts. */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/RegistrationValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/RegistrationValues.kt index f33ea9a642..268a4f3a8c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/RegistrationValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/RegistrationValues.kt @@ -15,6 +15,7 @@ class RegistrationValues internal constructor(store: KeyValueStore) : SignalStor private const val LOCAL_REGISTRATION_DATA = "registration.local_registration_data" private const val RESTORE_COMPLETED = "registration.backup_restore_completed" private const val RESTORE_METHOD_TOKEN = "registration.restore_method_token" + private const val IS_OTHER_DEVICE_ANDROID = "registration.is_other_device_android" private const val RESTORING_ON_NEW_DEVICE = "registration.restoring_on_new_device" } @@ -60,6 +61,8 @@ class RegistrationValues internal constructor(store: KeyValueStore) : SignalStor var hasUploadedProfile: Boolean by booleanValue(HAS_UPLOADED_PROFILE, true) var sessionId: String? by stringValue(SESSION_ID, null) var sessionE164: String? by stringValue(SESSION_E164, null) + + var isOtherDeviceAndroid: Boolean by booleanValue(IS_OTHER_DEVICE_ANDROID, false) var restoreMethodToken: String? by stringValue(RESTORE_METHOD_TOKEN, null) @get:JvmName("isRestoringOnNewDevice") diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/data/QuickRegistrationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/data/QuickRegistrationRepository.kt index d593ca658d..5029ac58ab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/data/QuickRegistrationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/data/QuickRegistrationRepository.kt @@ -86,9 +86,10 @@ object QuickRegistrationRepository { backupTimestampMs = SignalStore.backup.lastBackupTime.coerceAtLeast(0L), tier = when (SignalStore.backup.backupTier) { MessageBackupTier.PAID -> RegistrationProvisionMessage.Tier.PAID - MessageBackupTier.FREE, - null -> RegistrationProvisionMessage.Tier.FREE + MessageBackupTier.FREE -> RegistrationProvisionMessage.Tier.FREE + null -> null }, + backupSizeBytes = SignalStore.backup.totalBackupSize, restoreMethodToken = restoreMethodToken ) ) @@ -145,7 +146,7 @@ object QuickRegistrationRepository { Log.d(TAG, "Waiting for restore method with token: ***${restoreMethodToken.takeLast(4)}") while (retries-- > 0 && result !is NetworkResult.Success && coroutineContext.isActive) { - Log.d(TAG, "Remaining tries $retries...") + Log.d(TAG, "Waiting, remaining tries: $retries") val api = AppDependencies.registrationApi result = api.waitForRestoreMethod(restoreMethodToken) Log.d(TAG, "Result: $result") @@ -155,7 +156,7 @@ object QuickRegistrationRepository { Log.i(TAG, "Restore method selected on new device ${result.result}") return result.result } else { - Log.w(TAG, "Failed to determine restore method, using default") + Log.w(TAG, "Failed to determine restore method, using DECLINE") return RestoreMethod.DECLINE } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/NoBackupToRestoreFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/NoBackupToRestoreFragment.kt new file mode 100644 index 0000000000..c03302793b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/NoBackupToRestoreFragment.kt @@ -0,0 +1,118 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registrationv3.ui.restore + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.fragment.findNavController +import org.signal.core.ui.Buttons +import org.signal.core.ui.Previews +import org.signal.core.ui.SignalPreview +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.compose.ComposeFragment +import org.thoughtcrime.securesms.registrationv3.ui.shared.RegistrationScreen +import org.thoughtcrime.securesms.util.navigation.safeNavigate + +/** + * Shown when the old device is iOS and they are trying to transfer/restore on Android without a Signal Backup. + */ +class NoBackupToRestoreFragment : ComposeFragment() { + @Composable + override fun FragmentContent() { + NoBackupToRestoreContent( + onSkipRestore = {}, + onCancel = { + findNavController().safeNavigate(NoBackupToRestoreFragmentDirections.restartRegistrationFlow()) + } + ) + } +} + +@Composable +private fun NoBackupToRestoreContent( + onSkipRestore: () -> Unit = {}, + onCancel: () -> Unit = {} +) { + RegistrationScreen( + title = stringResource(id = R.string.NoBackupToRestore_title), + subtitle = stringResource(id = R.string.NoBackupToRestore_subtitle), + bottomContent = { + Column { + Buttons.LargeTonal( + onClick = onSkipRestore, + modifier = Modifier.fillMaxWidth() + ) { + Text(text = stringResource(id = R.string.NoBackupToRestore_skip_restore)) + } + + TextButton( + onClick = onCancel, + modifier = Modifier.fillMaxWidth() + ) { + Text(text = stringResource(id = android.R.string.cancel)) + } + } + } + ) { + Column( + verticalArrangement = Arrangement.spacedBy(24.dp), + modifier = Modifier.padding(horizontal = 32.dp) + ) { + StepRow(icon = painterResource(R.drawable.symbol_device_phone_24), text = stringResource(id = R.string.NoBackupToRestore_step1)) + + StepRow(icon = painterResource(R.drawable.symbol_backup_24), text = stringResource(id = R.string.NoBackupToRestore_step2)) + + StepRow(icon = painterResource(R.drawable.symbol_check_circle_24), text = stringResource(id = R.string.NoBackupToRestore_step3)) + } + } +} + +@Composable +private fun StepRow( + icon: Painter, + text: String +) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + painter = icon, + tint = MaterialTheme.colorScheme.onSurfaceVariant, + contentDescription = null + ) + + Spacer(modifier = Modifier.width(16.dp)) + + Text( + text = text, + style = MaterialTheme.typography.bodyLarge.copy(color = MaterialTheme.colorScheme.onSurfaceVariant) + ) + } +} + +@SignalPreview +@Composable +private fun NoBackupToRestoreContentPreview() { + Previews.Preview { + NoBackupToRestoreContent() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreActivity.kt index ccbc11376a..5de8a3bdf4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreActivity.kt @@ -9,7 +9,6 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.activity.compose.setContent -import androidx.activity.viewModels import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -30,10 +29,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.lifecycleScope @@ -63,6 +58,7 @@ import org.thoughtcrime.securesms.conversation.v2.registerForLifecycle import org.thoughtcrime.securesms.profiles.edit.CreateProfileActivity import org.thoughtcrime.securesms.registrationv3.ui.shared.RegistrationScreen import org.thoughtcrime.securesms.util.DateUtils +import org.thoughtcrime.securesms.util.viewModel import java.util.Locale /** @@ -70,12 +66,19 @@ import java.util.Locale */ class RemoteRestoreActivity : BaseActivity() { companion object { - fun getIntent(context: Context): Intent { - return Intent(context, RemoteRestoreActivity::class.java) + + private const val KEY_ONLY_OPTION = "ONLY_OPTION" + + fun getIntent(context: Context, isOnlyOption: Boolean = false): Intent { + return Intent(context, RemoteRestoreActivity::class.java).apply { + putExtra(KEY_ONLY_OPTION, isOnlyOption) + } } } - private val viewModel: RemoteRestoreViewModel by viewModels() + private val viewModel: RemoteRestoreViewModel by viewModel { + RemoteRestoreViewModel(intent.getBooleanExtra(KEY_ONLY_OPTION, false)) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -100,7 +103,14 @@ class RemoteRestoreActivity : BaseActivity() { RestoreFromBackupContent( state = state, onRestoreBackupClick = { viewModel.restore() }, - onCancelClick = { finish() }, + onCancelClick = { + if (state.isRemoteRestoreOnlyOption) { + viewModel.skipRestore() + startActivity(MainActivity.clearTop(this)) + } + + finish() + }, onErrorDialogDismiss = { viewModel.clearError() } ) } @@ -137,25 +147,57 @@ private fun RestoreFromBackupContent( onCancelClick: () -> Unit = {}, onErrorDialogDismiss: () -> Unit = {} ) { - val subtitle = buildAnnotatedString { - append( - stringResource( - id = R.string.RemoteRestoreActivity__backup_created_at, - DateUtils.formatDateWithoutDayOfWeek(Locale.getDefault(), state.backupTime), - DateUtils.getOnlyTimeString(LocalContext.current, state.backupTime) + when (state.loadState) { + RemoteRestoreViewModel.ScreenState.LoadState.LOADING -> { + Dialogs.IndeterminateProgressDialog( + message = stringResource(R.string.RemoteRestoreActivity__fetching_backup_details) + ) + } + + RemoteRestoreViewModel.ScreenState.LoadState.LOADED -> { + BackupAvailableContent( + state = state, + onRestoreBackupClick = onRestoreBackupClick, + onCancelClick = onCancelClick, + onErrorDialogDismiss = onErrorDialogDismiss ) - ) - append(" ") - if (state.backupTier != MessageBackupTier.PAID) { - withStyle(SpanStyle(fontWeight = FontWeight.SemiBold)) { - append(stringResource(id = R.string.RemoteRestoreActivity__only_media_sent_or_received)) - } + } + + RemoteRestoreViewModel.ScreenState.LoadState.NOT_FOUND -> { + RestoreFailedDialog(onDismiss = onCancelClick) + } + + RemoteRestoreViewModel.ScreenState.LoadState.FAILURE -> { + RestoreFailedDialog(onDismiss = onCancelClick) } } +} + +@Composable +private fun BackupAvailableContent( + state: RemoteRestoreViewModel.ScreenState, + onRestoreBackupClick: () -> Unit, + onCancelClick: () -> Unit, + onErrorDialogDismiss: () -> Unit +) { + val subtitle = if (state.backupSize.bytes > 0) { + stringResource( + id = R.string.RemoteRestoreActivity__backup_created_at_with_size, + DateUtils.formatDateWithoutDayOfWeek(Locale.getDefault(), state.backupTime), + DateUtils.getOnlyTimeString(LocalContext.current, state.backupTime), + state.backupSize.toUnitString() + ) + } else { + stringResource( + id = R.string.RemoteRestoreActivity__backup_created_at, + DateUtils.formatDateWithoutDayOfWeek(Locale.getDefault(), state.backupTime), + DateUtils.getOnlyTimeString(LocalContext.current, state.backupTime) + ) + } RegistrationScreen( title = stringResource(id = R.string.RemoteRestoreActivity__restore_from_backup), - subtitle = if (state.isLoaded()) subtitle else null, + subtitle = subtitle, bottomContent = { Column { if (state.isLoaded()) { @@ -171,44 +213,30 @@ private fun RestoreFromBackupContent( onClick = onCancelClick, modifier = Modifier.fillMaxWidth() ) { - Text(text = stringResource(id = android.R.string.cancel)) + Text(text = stringResource(id = if (state.isRemoteRestoreOnlyOption) R.string.RemoteRestoreActivity__skip_restore else android.R.string.cancel)) } } } ) { - when (state.loadState) { - RemoteRestoreViewModel.ScreenState.LoadState.LOADING -> { - Dialogs.IndeterminateProgressDialog( - message = stringResource(R.string.RemoteRestoreActivity__fetching_backup_details) - ) - } - - RemoteRestoreViewModel.ScreenState.LoadState.LOADED -> { - Column( - modifier = Modifier - .fillMaxWidth() - .background(color = SignalTheme.colors.colorSurface2, shape = RoundedCornerShape(18.dp)) - .padding(horizontal = 20.dp) - .padding(top = 20.dp, bottom = 18.dp) - ) { - Text( - text = stringResource(id = R.string.RemoteRestoreActivity__your_backup_includes), - style = MaterialTheme.typography.titleMedium, - modifier = Modifier.padding(bottom = 6.dp) - ) - - getFeatures(state.backupTier).forEach { - MessageBackupsTypeFeatureRow( - messageBackupsTypeFeature = it, - iconTint = MaterialTheme.colorScheme.primary, - modifier = Modifier.padding(start = 16.dp, top = 6.dp) - ) - } - } - } + Column( + modifier = Modifier + .fillMaxWidth() + .background(color = SignalTheme.colors.colorSurface2, shape = RoundedCornerShape(18.dp)) + .padding(horizontal = 20.dp) + .padding(top = 20.dp, bottom = 18.dp) + ) { + Text( + text = stringResource(id = R.string.RemoteRestoreActivity__your_backup_includes), + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(bottom = 6.dp) + ) - RemoteRestoreViewModel.ScreenState.LoadState.FAILURE -> { - RestoreFailedDialog(onDismiss = onCancelClick) + getFeatures(state.backupTier).forEach { + MessageBackupsTypeFeatureRow( + messageBackupsTypeFeature = it, + iconTint = MaterialTheme.colorScheme.primary, + modifier = Modifier.padding(start = 16.dp, top = 6.dp) + ) } } @@ -229,6 +257,7 @@ private fun RestoreFromBackupContentPreview() { state = RemoteRestoreViewModel.ScreenState( backupTier = MessageBackupTier.PAID, backupTime = System.currentTimeMillis(), + backupSize = 1234567.bytes, importState = RemoteRestoreViewModel.ImportState.None, restoreProgress = null ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreViewModel.kt index 3271834407..a333800d0f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RemoteRestoreViewModel.kt @@ -16,6 +16,8 @@ import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.signal.core.util.ByteSize +import org.signal.core.util.bytes import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.backup.v2.BackupRepository import org.thoughtcrime.securesms.backup.v2.MessageBackupTier @@ -28,9 +30,11 @@ import org.thoughtcrime.securesms.jobs.ProfileUploadJob import org.thoughtcrime.securesms.jobs.SyncArchivedMediaJob import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.registration.util.RegistrationUtil +import org.thoughtcrime.securesms.registrationv3.data.QuickRegistrationRepository import org.thoughtcrime.securesms.registrationv3.data.RegistrationRepository +import org.whispersystems.signalservice.api.registration.RestoreMethod -class RemoteRestoreViewModel : ViewModel() { +class RemoteRestoreViewModel(isOnlyRestoreOption: Boolean) : ViewModel() { companion object { private val TAG = Log.tag(RemoteRestoreViewModel::class) @@ -38,8 +42,10 @@ class RemoteRestoreViewModel : ViewModel() { private val store: MutableStateFlow = MutableStateFlow( ScreenState( + isRemoteRestoreOnlyOption = isOnlyRestoreOption, backupTier = SignalStore.backup.backupTier, - backupTime = SignalStore.backup.lastBackupTime + backupTime = SignalStore.backup.lastBackupTime, + backupSize = SignalStore.backup.totalBackupSize.bytes ) ) @@ -47,18 +53,23 @@ class RemoteRestoreViewModel : ViewModel() { init { viewModelScope.launch(Dispatchers.IO) { - val restored = BackupRepository.restoreBackupTier(SignalStore.account.requireAci()) != null + val tier: MessageBackupTier? = BackupRepository.restoreBackupTier(SignalStore.account.requireAci()) store.update { - if (restored) { + if (tier != null) { it.copy( loadState = ScreenState.LoadState.LOADED, backupTier = SignalStore.backup.backupTier, - backupTime = SignalStore.backup.lastBackupTime + backupTime = SignalStore.backup.lastBackupTime, + backupSize = SignalStore.backup.totalBackupSize.bytes ) } else { - it.copy( - loadState = ScreenState.LoadState.FAILURE - ) + if (SignalStore.backup.isBackupTierRestored) { + it.copy(loadState = ScreenState.LoadState.NOT_FOUND) + } else if (it.loadState == ScreenState.LoadState.LOADING) { + it.copy(loadState = ScreenState.LoadState.FAILURE) + } else { + it + } } } } @@ -69,6 +80,8 @@ class RemoteRestoreViewModel : ViewModel() { store.update { it.copy(importState = ImportState.InProgress) } withContext(Dispatchers.IO) { + QuickRegistrationRepository.setRestoreMethodForOldDevice(RestoreMethod.REMOTE_BACKUP) + val jobStateFlow = callbackFlow { val listener = JobTracker.JobListener { _, jobState -> trySend(jobState) @@ -129,9 +142,21 @@ class RemoteRestoreViewModel : ViewModel() { store.update { it.copy(importState = ImportState.None, restoreProgress = null) } } + fun skipRestore() { + SignalStore.registration.markSkippedTransferOrRestore() + + viewModelScope.launch { + withContext(Dispatchers.IO) { + QuickRegistrationRepository.setRestoreMethodForOldDevice(RestoreMethod.DECLINE) + } + } + } + data class ScreenState( + val isRemoteRestoreOnlyOption: Boolean = false, val backupTier: MessageBackupTier? = null, val backupTime: Long = -1, + val backupSize: ByteSize = 0.bytes, val importState: ImportState = ImportState.None, val restoreProgress: RestoreV2Event? = null, val loadState: LoadState = if (backupTier != null) LoadState.LOADED else LoadState.LOADING @@ -141,12 +166,8 @@ class RemoteRestoreViewModel : ViewModel() { return loadState == LoadState.LOADED } - fun isLoading(): Boolean { - return loadState == LoadState.LOADING - } - enum class LoadState { - LOADING, LOADED, FAILURE + LOADING, LOADED, NOT_FOUND, FAILURE } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RestoreViaQrFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RestoreViaQrFragment.kt index 59b64bcf00..58a689b8dd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RestoreViaQrFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RestoreViaQrFragment.kt @@ -59,12 +59,14 @@ import org.signal.core.ui.Previews import org.signal.core.ui.SignalPreview import org.signal.core.ui.horizontalGutters import org.signal.core.ui.theme.SignalTheme +import org.signal.registration.proto.RegistrationProvisionMessage import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.settings.app.usernamelinks.QrCode import org.thoughtcrime.securesms.components.settings.app.usernamelinks.QrCodeData import org.thoughtcrime.securesms.compose.ComposeFragment import org.thoughtcrime.securesms.registrationv3.ui.RegistrationViewModel import org.thoughtcrime.securesms.registrationv3.ui.shared.RegistrationScreen +import org.thoughtcrime.securesms.util.navigation.safeNavigate /** * Show QR code on new device to allow registration and restore via old device. @@ -84,7 +86,11 @@ class RestoreViaQrFragment : ComposeFragment() { .mapNotNull { it.provisioningMessage } .distinctUntilChanged() .collect { message -> - sharedViewModel.registerWithBackupKey(requireContext(), message.accountEntropyPool, message.e164, message.pin) + if (message.platform == RegistrationProvisionMessage.Platform.ANDROID || message.tier != null) { + sharedViewModel.registerWithBackupKey(requireContext(), message.accountEntropyPool, message.e164, message.pin) + } else { + findNavController().safeNavigate(RestoreViaQrFragmentDirections.goToNoBackupToRestore()) + } } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RestoreViaQrViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RestoreViaQrViewModel.kt index 5bdb7a01c0..d28bd4f4a9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RestoreViaQrViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/RestoreViaQrViewModel.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update import org.signal.core.util.logging.Log import org.signal.registration.proto.RegistrationProvisionMessage +import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.components.settings.app.usernamelinks.QrCodeData import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.dependencies.AppDependencies @@ -80,6 +81,18 @@ class RestoreViaQrViewModel : ViewModel() { if (result is SecondaryProvisioningCipher.RegistrationProvisionResult.Success) { Log.i(TAG, "Saving restore method token: ***${result.message.restoreMethodToken.takeLast(4)}") SignalStore.registration.restoreMethodToken = result.message.restoreMethodToken + SignalStore.registration.isOtherDeviceAndroid = result.message.platform == RegistrationProvisionMessage.Platform.ANDROID + if (result.message.backupTimestampMs > 0) { + SignalStore.backup.backupTier = result.message.tier.let { + when (it) { + RegistrationProvisionMessage.Tier.FREE -> MessageBackupTier.FREE + RegistrationProvisionMessage.Tier.PAID -> MessageBackupTier.PAID + null -> null + } + } + SignalStore.backup.lastBackupTime = result.message.backupTimestampMs + SignalStore.backup.usedBackupMediaSpace = result.message.backupSizeBytes + } store.update { it.copy(isRegistering = true, provisioningMessage = result.message, qrState = QrState.Scanned) } } else { store.update { it.copy(showProvisioningError = true, qrState = QrState.Scanned) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/SelectRestoreMethodScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/SelectRestoreMethodScreen.kt index 2815c9b383..31c550bbc9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/SelectRestoreMethodScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/restore/SelectRestoreMethodScreen.kt @@ -34,7 +34,7 @@ fun SelectRestoreMethodScreen( onClick = onSkip, modifier = Modifier.align(Alignment.Center) ) { - Text(text = stringResource(R.string.registration_activity__skip)) + Text(text = stringResource(R.string.registration_activity__skip_restore)) } } ) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreActivity.kt index 6dfb2a2183..d17879eebc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreActivity.kt @@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.BaseActivity import org.thoughtcrime.securesms.PassphraseRequiredActivity import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.RestoreDirections +import org.thoughtcrime.securesms.registrationv3.ui.restore.RemoteRestoreActivity import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme import org.thoughtcrime.securesms.util.RemoteConfig import org.thoughtcrime.securesms.util.navigation.safeNavigate @@ -62,7 +63,14 @@ class RestoreActivity : BaseActivity() { val navTarget = NavTarget.deserialize(intent.getIntExtra(EXTRA_NAV_TARGET, NavTarget.LEGACY_LANDING.value)) when (navTarget) { - NavTarget.NEW_LANDING -> navController.safeNavigate(RestoreDirections.goDirectlyToNewLanding()) + NavTarget.NEW_LANDING -> { + if (sharedViewModel.hasMultipleRestoreMethods()) { + navController.safeNavigate(RestoreDirections.goDirectlyToNewLanding()) + } else { + startActivity(RemoteRestoreActivity.getIntent(this, isOnlyOption = true)) + finish() + } + } NavTarget.LOCAL_RESTORE -> navController.safeNavigate(RestoreDirections.goDirectlyToChooseLocalBackup()) NavTarget.TRANSFER -> navController.safeNavigate(RestoreDirections.goDirectlyToDeviceTransfer()) else -> Unit diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreViewModel.kt index 87ec134b11..c47994300a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreViewModel.kt @@ -11,6 +11,9 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.asLiveData import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update +import org.thoughtcrime.securesms.backup.v2.MessageBackupTier +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.registrationv3.ui.restore.RestoreMethod import org.thoughtcrime.securesms.restore.transferorrestore.BackupRestorationType /** @@ -51,4 +54,29 @@ class RestoreViewModel : ViewModel() { fun getBackupFileUri(): Uri? = store.value.backupFile fun getNextIntent(): Intent? = store.value.nextIntent + + fun hasMultipleRestoreMethods(): Boolean { + return getAvailableRestoreMethods().size > 1 + } + + fun getAvailableRestoreMethods(): List { + if (SignalStore.registration.isOtherDeviceAndroid) { + val methods = mutableListOf(RestoreMethod.FROM_OLD_DEVICE, RestoreMethod.FROM_LOCAL_BACKUP_V1) + when (SignalStore.backup.backupTier) { + MessageBackupTier.FREE -> methods.add(1, RestoreMethod.FROM_SIGNAL_BACKUPS) + MessageBackupTier.PAID -> methods.add(0, RestoreMethod.FROM_SIGNAL_BACKUPS) + null -> if (!SignalStore.backup.isBackupTierRestored) { + methods.add(1, RestoreMethod.FROM_SIGNAL_BACKUPS) + } + } + + return methods + } + + if (SignalStore.backup.backupTier != null || !SignalStore.backup.isBackupTierRestored) { + return listOf(RestoreMethod.FROM_SIGNAL_BACKUPS) + } + + return emptyList() + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/selection/SelectRestoreMethodFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/selection/SelectRestoreMethodFragment.kt index 3e1fc2730d..d83f64db2a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/restore/selection/SelectRestoreMethodFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/selection/SelectRestoreMethodFragment.kt @@ -5,8 +5,8 @@ package org.thoughtcrime.securesms.restore.selection -import android.content.Intent import androidx.compose.runtime.Composable +import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import kotlinx.coroutines.launch @@ -17,6 +17,7 @@ import org.thoughtcrime.securesms.registrationv3.data.QuickRegistrationRepositor import org.thoughtcrime.securesms.registrationv3.ui.restore.RemoteRestoreActivity import org.thoughtcrime.securesms.registrationv3.ui.restore.RestoreMethod import org.thoughtcrime.securesms.registrationv3.ui.restore.SelectRestoreMethodScreen +import org.thoughtcrime.securesms.restore.RestoreViewModel import org.thoughtcrime.securesms.util.navigation.safeNavigate import org.whispersystems.signalservice.api.registration.RestoreMethod as ApiRestoreMethod @@ -24,10 +25,13 @@ import org.whispersystems.signalservice.api.registration.RestoreMethod as ApiRes * Provide options to select restore/transfer operation and flow during quick registration. */ class SelectRestoreMethodFragment : ComposeFragment() { + + private val viewModel: RestoreViewModel by activityViewModels() + @Composable override fun FragmentContent() { SelectRestoreMethodScreen( - restoreMethods = listOf(RestoreMethod.FROM_SIGNAL_BACKUPS, RestoreMethod.FROM_OLD_DEVICE, RestoreMethod.FROM_LOCAL_BACKUP_V1), // TODO [backups] make dynamic + restoreMethods = viewModel.getAvailableRestoreMethods(), onRestoreMethodClicked = this::startRestoreMethod, onSkip = { SignalStore.registration.markSkippedTransferOrRestore() @@ -54,7 +58,7 @@ class SelectRestoreMethodFragment : ComposeFragment() { } when (method) { - RestoreMethod.FROM_SIGNAL_BACKUPS -> startActivity(Intent(requireContext(), RemoteRestoreActivity::class.java)) + RestoreMethod.FROM_SIGNAL_BACKUPS -> startActivity(RemoteRestoreActivity.getIntent(requireContext())) RestoreMethod.FROM_OLD_DEVICE -> findNavController().safeNavigate(SelectRestoreMethodFragmentDirections.goToDeviceTransfer()) RestoreMethod.FROM_LOCAL_BACKUP_V1 -> findNavController().safeNavigate(SelectRestoreMethodFragmentDirections.goToLocalBackupRestore()) RestoreMethod.FROM_LOCAL_BACKUP_V2 -> error("Not currently supported") diff --git a/app/src/main/res/drawable/symbol_device_phone_24.xml b/app/src/main/res/drawable/symbol_device_phone_24.xml new file mode 100644 index 0000000000..3ef35d3b10 --- /dev/null +++ b/app/src/main/res/drawable/symbol_device_phone_24.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/main/res/navigation/registration_v3.xml b/app/src/main/res/navigation/registration_v3.xml index cb42553bbd..5d1f87c574 100644 --- a/app/src/main/res/navigation/registration_v3.xml +++ b/app/src/main/res/navigation/registration_v3.xml @@ -48,7 +48,8 @@ + android:label="fragment_grant_permissions" + tools:ignore="NewApi"> + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ac16f2a3b9..e9e1cc8b2a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1356,16 +1356,18 @@ All of your messages Restore from backup - - Only media sent or received in the past %1$d days is included. Your backup includes: Restore backup - + Your last backup was made on %1$s at %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Fetching backup details… + + Skip restore Notify me for Mentions @@ -4315,6 +4317,8 @@ Restore or transfer Transfer account Skip + + Skip restore Chat backups Transfer account Transfer account to a new Android device @@ -7956,6 +7960,18 @@ Okay + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/core-ui/src/main/java/org/signal/core/ui/Dialogs.kt b/core-ui/src/main/java/org/signal/core/ui/Dialogs.kt index f89c829926..d4ee8a4501 100644 --- a/core-ui/src/main/java/org/signal/core/ui/Dialogs.kt +++ b/core-ui/src/main/java/org/signal/core/ui/Dialogs.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog @@ -156,7 +157,7 @@ object Dialogs { Spacer(modifier = Modifier.size(24.dp)) CircularProgressIndicator() Spacer(modifier = Modifier.size(20.dp)) - Text(message) + Text(text = message, textAlign = TextAlign.Center) } }, modifier = Modifier diff --git a/libsignal-service/src/main/protowire/RegistrationProvisioning.proto b/libsignal-service/src/main/protowire/RegistrationProvisioning.proto index df52e7b0d2..4d7593f25f 100644 --- a/libsignal-service/src/main/protowire/RegistrationProvisioning.proto +++ b/libsignal-service/src/main/protowire/RegistrationProvisioning.proto @@ -25,7 +25,8 @@ message RegistrationProvisionMessage { string pin = 4; Platform platform = 5; uint64 backupTimestampMs = 6; - Tier tier = 7; - string restoreMethodToken = 8; - reserved 9; // iOSDeviceTransferMessage + optional Tier tier = 7; + uint64 backupSizeBytes = 8; + string restoreMethodToken = 9; + reserved 10; // iOSDeviceTransferMessage } From 263ea37a9ee03d359f93da8606d519804d195612 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 25 Nov 2024 10:25:11 -0500 Subject: [PATCH 17/56] Update to libsignal 0.63.0 --- gradle/libs.versions.toml | 2 +- gradle/verification-metadata.xml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 988f830b4b..3c5ee14e1b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,7 +12,7 @@ androidx-navigation = "2.8.0" androidx-window = "1.3.0" glide = "4.15.1" kotlin = "1.9.20" -libsignal-client = "0.62.0" +libsignal-client = "0.63.0" mp4parser = "1.9.39" android-gradle-plugin = "8.4.0" accompanist = "0.28.0" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index acd80cdabc..d463cb6610 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -6225,20 +6225,20 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - + + + - - + + - - - + + + - - + + From 1f91ed42741e6a3648bcf95ebb12628fba5968e0 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 25 Nov 2024 12:22:52 -0500 Subject: [PATCH 18/56] Add an internal sqlite playground. --- .../app/internal/InternalSettingsFragment.kt | 37 ++-- .../InternalSqlitePlaygroundFragment.kt | 209 ++++++++++++++++++ .../InternalSqlitePlaygroundViewModel.kt | 68 ++++++ .../app_settings_with_change_number.xml | 8 + 4 files changed, 309 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/sqlite/InternalSqlitePlaygroundFragment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/sqlite/InternalSqlitePlaygroundViewModel.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt index e8b765d97c..a77da4138d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt @@ -149,24 +149,15 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter onUnregisterClicked() } ) - dividerPref() - sectionHeaderPref(DSLSettingsText.from("Miscellaneous")) + sectionHeaderPref(DSLSettingsText.from("Playgrounds")) clickPref( - title = DSLSettingsText.from("Search for a recipient"), - summary = DSLSettingsText.from("Search by ID, ACI, or PNI."), + title = DSLSettingsText.from("SQLite Playground"), + summary = DSLSettingsText.from("Run raw SQLite queries."), onClick = { - findNavController().safeNavigate(InternalSettingsFragmentDirections.actionInternalSettingsFragmentToInternalSearchFragment()) - } - ) - - clickPref( - title = DSLSettingsText.from("SVR Playground"), - summary = DSLSettingsText.from("Quickly test various SVR options and error conditions."), - onClick = { - findNavController().safeNavigate(InternalSettingsFragmentDirections.actionInternalSettingsFragmentToInternalSvrPlaygroundFragment()) + findNavController().safeNavigate(InternalSettingsFragmentDirections.actionInternalSettingsFragmentToInternalSqlitePlaygroundFragment()) } ) @@ -186,6 +177,26 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter } ) + clickPref( + title = DSLSettingsText.from("SVR Playground"), + summary = DSLSettingsText.from("Quickly test various SVR options and error conditions."), + onClick = { + findNavController().safeNavigate(InternalSettingsFragmentDirections.actionInternalSettingsFragmentToInternalSvrPlaygroundFragment()) + } + ) + + dividerPref() + + sectionHeaderPref(DSLSettingsText.from("Miscellaneous")) + + clickPref( + title = DSLSettingsText.from("Search for a recipient"), + summary = DSLSettingsText.from("Search by ID, ACI, or PNI."), + onClick = { + findNavController().safeNavigate(InternalSettingsFragmentDirections.actionInternalSettingsFragmentToInternalSearchFragment()) + } + ) + switchPref( title = DSLSettingsText.from("'Internal Details' button"), summary = DSLSettingsText.from("Show a button in conversation settings that lets you see more information about a user."), diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/sqlite/InternalSqlitePlaygroundFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/sqlite/InternalSqlitePlaygroundFragment.kt new file mode 100644 index 0000000000..4981663354 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/sqlite/InternalSqlitePlaygroundFragment.kt @@ -0,0 +1,209 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.internal.sqlite + +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.clickable +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.signal.core.ui.Buttons +import org.signal.core.ui.Previews +import org.signal.core.ui.SignalPreview +import org.signal.libsignal.protocol.util.Hex +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.settings.app.internal.sqlite.InternalSqlitePlaygroundViewModel.QueryResult +import org.thoughtcrime.securesms.compose.ComposeFragment + +class InternalSqlitePlaygroundFragment : ComposeFragment() { + + val viewModel by viewModels() + + @Composable + override fun FragmentContent() { + val queryResults by viewModel.queryResults + + Screen( + onBackPressed = { findNavController().popBackStack() }, + queryResults = queryResults, + onQuerySubmitted = { viewModel.onQuerySubmitted(it) } + ) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun Screen( + onBackPressed: () -> Unit = {}, + queryResults: QueryResult?, + onQuerySubmitted: (String) -> Unit = {} +) { + Scaffold( + topBar = { + TopAppBar( + title = { Text("SQLite Playground") }, + navigationIcon = { + IconButton(onClick = onBackPressed) { + Icon( + painter = painterResource(R.drawable.symbol_arrow_left_24), + tint = MaterialTheme.colorScheme.onSurface, + contentDescription = null + ) + } + } + ) + } + ) { contentPadding -> + Surface(modifier = Modifier.padding(contentPadding)) { + Column(modifier = Modifier.padding(8.dp)) { + Text("Warning! This allows you to run arbitrary queries. Only use this if you know what you're doing!", color = Color.Red) + Spacer(Modifier.height(8.dp)) + QueryBox(onQuerySubmitted) + Spacer(Modifier.height(8.dp)) + QueryResults(queryResults) + } + } + } +} + +@Composable +private fun QueryBox(onQuerySubmitted: (String) -> Unit = {}) { + var queryText: String by remember { mutableStateOf("") } + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.End + ) { + OutlinedTextField( + value = queryText, + onValueChange = { queryText = it }, + modifier = Modifier.fillMaxWidth(), + minLines = 3, + textStyle = MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace) + ) + Spacer(Modifier.height(2.dp)) + Buttons.LargePrimary(onClick = { onQuerySubmitted(queryText) }) { + Text("Execute") + } + } +} + +@Composable +private fun QueryResults(results: QueryResult?) { + val columnWidth = LocalConfiguration.current.screenWidthDp.dp / 2 + val horizontalScrollState = rememberScrollState() + + if (results == null) { + Text("Waiting on query results.") + return + } + Text("${results.rows.size} rows in ${results.totalTimeString} ms", modifier = Modifier.padding(4.dp)) + QueryRow(data = results.columns, columnWidth = columnWidth, scrollState = horizontalScrollState, fontWeight = FontWeight.Bold) + + LazyColumn { + items(results.rows) { row -> + QueryRow(data = row, columnWidth = columnWidth, scrollState = horizontalScrollState) + } + } +} + +@Composable +private fun QueryRow(data: List, columnWidth: Dp, scrollState: ScrollState, fontWeight: FontWeight = FontWeight.Normal) { + val context = LocalContext.current + + Row( + modifier = Modifier + .fillMaxWidth() + .horizontalScroll(scrollState) + ) { + data.forEach { + Text( + text = it.toDisplayString(), + fontWeight = fontWeight, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + modifier = Modifier.width(columnWidth) + .padding(4.dp) + .clickable { + MaterialAlertDialogBuilder(context) + .setMessage(it.toDisplayString()) + .setPositiveButton("Ok", null) + .show() + } + ) + } + } +} + +private fun Any?.toDisplayString(): String { + return when (this) { + is ByteArray -> "Blob { ${Hex.toStringCondensed(this)} }" + else -> this.toString() + } +} + +@SignalPreview +@Composable +private fun ScreenPreview() { + Previews.Preview { + Screen( + queryResults = QueryResult( + columns = listOf("column1", "column2", "column3"), + rows = listOf( + listOf("a", 1, ByteArray(16) { 0 }), + listOf("b", 2, ByteArray(16) { 1 }), + listOf("c", 3, ByteArray(16) { 2 }) + ), + totalTimeString = "3.42" + ) + ) + } +} + +@SignalPreview +@Composable +private fun ScreenPreviewNoResults() { + Previews.Preview { + Screen( + queryResults = null + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/sqlite/InternalSqlitePlaygroundViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/sqlite/InternalSqlitePlaygroundViewModel.kt new file mode 100644 index 0000000000..4c2c19cc7a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/sqlite/InternalSqlitePlaygroundViewModel.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.internal.sqlite + +import android.database.Cursor +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.signal.core.util.ExceptionUtil +import org.signal.core.util.readToList +import org.signal.core.util.roundedString +import org.thoughtcrime.securesms.database.SignalDatabase +import kotlin.time.Duration.Companion.nanoseconds +import kotlin.time.DurationUnit + +class InternalSqlitePlaygroundViewModel : ViewModel() { + + private val _queryResults: MutableState = mutableStateOf(null) + val queryResults: State + get() = _queryResults + + fun onQuerySubmitted(query: String) { + viewModelScope.launch(Dispatchers.IO) { + _queryResults.value = null + + val startTime = System.nanoTime() + try { + SignalDatabase.rawDatabase.rawQuery(query).use { cursor -> + val columnNames = cursor.columnNames.toList() + val rows: List> = cursor.readToList { row -> + val out: MutableList = ArrayList(row.columnCount) + for (i in 0 until row.columnCount) { + if (row.getType(i) == Cursor.FIELD_TYPE_BLOB) { + out.add(row.getBlob(i)) + } else { + out.add(row.getString(i)) + } + } + out + } + + val endTime = System.nanoTime() + val timeMs: String = (endTime - startTime).nanoseconds.toDouble(DurationUnit.MILLISECONDS).roundedString(2) + _queryResults.value = QueryResult(columns = columnNames, rows = rows, totalTimeString = timeMs) + } + } catch (e: Exception) { + _queryResults.value = QueryResult( + columns = listOf("Query failed!"), + rows = listOf(listOf(ExceptionUtil.convertThrowableToString(e))), + totalTimeString = "" + ) + } + } + } + + data class QueryResult( + val columns: List, + val rows: List>, + val totalTimeString: String + ) +} diff --git a/app/src/main/res/navigation/app_settings_with_change_number.xml b/app/src/main/res/navigation/app_settings_with_change_number.xml index 99a7f55b41..5a4ebfbe74 100644 --- a/app/src/main/res/navigation/app_settings_with_change_number.xml +++ b/app/src/main/res/navigation/app_settings_with_change_number.xml @@ -751,6 +751,9 @@ + + + From ce69c5f7dae14fd69602b19ea670bde96e5dbbf9 Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Mon, 25 Nov 2024 10:09:35 -0800 Subject: [PATCH 19/56] Update linked devices strings. --- app/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e9e1cc8b2a..1a61205d04 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1039,11 +1039,11 @@ Transfer message history - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Don\'t transfer - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Unlink \"%s\"? From 3e699a132beda3f79aa13918e2be1f0673583008 Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Mon, 25 Nov 2024 13:45:20 -0800 Subject: [PATCH 20/56] Allow renaming of linked device. --- .../securesms/jobs/DeviceNameChangeJob.kt | 77 +++++++++ .../securesms/jobs/JobManagerFactories.java | 1 + .../linkdevice/EditDeviceNameFragment.kt | 153 ++++++++++++++++++ .../linkdevice/LinkDeviceFragment.kt | 88 ++++++++-- .../linkdevice/LinkDeviceRepository.kt | 30 ++++ .../linkdevice/LinkDeviceSettingsState.kt | 5 +- .../linkdevice/LinkDeviceViewModel.kt | 25 +++ app/src/main/protowire/JobData.proto | 4 + .../app_settings_with_change_number.xml | 11 ++ app/src/main/res/values/strings.xml | 14 ++ .../api/SignalServiceMessageSender.java | 9 ++ .../signalservice/api/link/LinkDeviceApi.kt | 15 ++ .../api/link/SetDeviceNameRequest.kt | 15 ++ .../multidevice/SignalServiceSyncMessage.java | 52 +++++- .../internal/push/PushServiceSocket.java | 7 + .../src/main/protowire/SignalService.proto | 6 + 16 files changed, 500 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/DeviceNameChangeJob.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/linkdevice/EditDeviceNameFragment.kt create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/link/SetDeviceNameRequest.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/DeviceNameChangeJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/DeviceNameChangeJob.kt new file mode 100644 index 0000000000..301473e815 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/DeviceNameChangeJob.kt @@ -0,0 +1,77 @@ +package org.thoughtcrime.securesms.jobs + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.dependencies.AppDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobs.protos.DeviceNameChangeJobData +import org.thoughtcrime.securesms.recipients.Recipient +import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException +import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage +import org.whispersystems.signalservice.internal.push.SyncMessage +import java.io.IOException +import java.util.concurrent.TimeUnit + +/** + * Sends a sync message that a linked device has changed its name + */ +class DeviceNameChangeJob private constructor( + private val data: DeviceNameChangeJobData, + parameters: Parameters +) : Job(parameters) { + companion object { + const val KEY: String = "DeviceNameChangeJob" + private val TAG = Log.tag(DeviceNameChangeJob::class.java) + } + + constructor( + deviceId: Int + ) : this( + DeviceNameChangeJobData(deviceId), + Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue("DeviceNameChangeJob") + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build() + ) + + override fun serialize(): ByteArray { + return data.encode() + } + + override fun getFactoryKey(): String = KEY + + override fun run(): Result { + if (!Recipient.self().isRegistered) { + Log.w(TAG, "Not registered") + return Result.failure() + } + + return try { + val result = AppDependencies.signalServiceMessageSender.sendSyncMessage( + SignalServiceSyncMessage.forDeviceNameChange(SyncMessage.DeviceNameChange(data.deviceId)) + ) + if (result.isSuccess) { + Result.success() + } else { + Log.w(TAG, "Unable to send device name sync - trying later") + Result.retry(defaultBackoff()) + } + } catch (e: IOException) { + Log.w(TAG, "Unable to send device name sync - trying later", e) + Result.retry(defaultBackoff()) + } catch (e: UntrustedIdentityException) { + Log.w(TAG, "Unable to send device name sync", e) + Result.failure() + } + } + + override fun onFailure() = Unit + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): DeviceNameChangeJob { + return DeviceNameChangeJob(DeviceNameChangeJobData.ADAPTER.decode(serializedData!!), parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index b5c7eb616d..b6f615e84f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -142,6 +142,7 @@ public static Map getJobFactories(@NonNull Application appl put(CopyAttachmentToArchiveJob.KEY, new CopyAttachmentToArchiveJob.Factory()); put(CreateReleaseChannelJob.KEY, new CreateReleaseChannelJob.Factory()); put(DeleteAbandonedAttachmentsJob.KEY, new DeleteAbandonedAttachmentsJob.Factory()); + put(DeviceNameChangeJob.KEY, new DeviceNameChangeJob.Factory()); put(DirectoryRefreshJob.KEY, new DirectoryRefreshJob.Factory()); put(DownloadLatestEmojiDataJob.KEY, new DownloadLatestEmojiDataJob.Factory()); put(EmojiSearchIndexDownloadJob.KEY, new EmojiSearchIndexDownloadJob.Factory()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/EditDeviceNameFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/EditDeviceNameFragment.kt new file mode 100644 index 0000000000..7bae8b4053 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/EditDeviceNameFragment.kt @@ -0,0 +1,153 @@ +package org.thoughtcrime.securesms.linkdevice + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextRange +import androidx.compose.ui.text.input.KeyboardCapitalization +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.dp +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController +import androidx.navigation.fragment.findNavController +import com.google.android.material.snackbar.Snackbar +import org.signal.core.ui.Buttons +import org.signal.core.ui.Previews +import org.signal.core.ui.Scaffolds +import org.signal.core.ui.SignalPreview +import org.signal.core.util.isNotNullOrBlank +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.compose.ComposeFragment + +/** + * Fragment for changing the name of a linked device + */ +class EditDeviceNameFragment : ComposeFragment() { + + companion object { + private val TAG = Log.tag(EditDeviceNameFragment::class) + const val MAX_LENGTH = 50 + } + + private val viewModel: LinkDeviceViewModel by activityViewModels() + + @Composable + override fun FragmentContent() { + val state by viewModel.state.collectAsStateWithLifecycle() + val navController: NavController by remember { mutableStateOf(findNavController()) } + val context = LocalContext.current + + LaunchedEffect(state.oneTimeEvent) { + when (state.oneTimeEvent) { + LinkDeviceSettingsState.OneTimeEvent.SnackbarNameChangeSuccess -> { + Snackbar.make(requireView(), context.getString(R.string.EditDeviceNameFragment__device_name_updated), Snackbar.LENGTH_LONG).show() + navController.popBackStack() + } + LinkDeviceSettingsState.OneTimeEvent.SnackbarNameChangeFailure -> { + Snackbar.make(requireView(), context.getString(R.string.EditDeviceNameFragment__unable_to_change), Snackbar.LENGTH_LONG).show() + } + LinkDeviceSettingsState.OneTimeEvent.HideFinishedSheet -> Unit + LinkDeviceSettingsState.OneTimeEvent.LaunchQrCodeScanner -> Unit + LinkDeviceSettingsState.OneTimeEvent.None -> Unit + LinkDeviceSettingsState.OneTimeEvent.ShowFinishedSheet -> Unit + is LinkDeviceSettingsState.OneTimeEvent.ToastLinked -> Unit + LinkDeviceSettingsState.OneTimeEvent.ToastNetworkFailed -> Unit + is LinkDeviceSettingsState.OneTimeEvent.ToastUnlinked -> Unit + } + } + + Scaffolds.Settings( + title = stringResource(id = R.string.EditDeviceNameFragment__edit), + onNavigationClick = { navController.popBackStack() }, + navigationIconPainter = painterResource(id = R.drawable.ic_arrow_left_24), + navigationContentDescription = stringResource(id = R.string.Material3SearchToolbar__close) + ) { contentPadding: PaddingValues -> + EditNameScreen( + state = state, + modifier = Modifier.padding(contentPadding), + onSave = { viewModel.saveName(it) } + ) + } + } +} + +@Composable +private fun EditNameScreen( + state: LinkDeviceSettingsState, + modifier: Modifier = Modifier, + onSave: (String) -> Unit = {} +) { + val focusRequester = remember { FocusRequester() } + val name = state.deviceToEdit!!.name ?: "" + var deviceName by remember { mutableStateOf(TextFieldValue(name, TextRange(name.length))) } + + Box( + modifier = modifier.fillMaxHeight() + ) { + TextField( + value = deviceName, + label = { Text(text = stringResource(id = R.string.EditDeviceNameFragment__device_name)) }, + onValueChange = { + deviceName = it.copy( + text = it.text.substring(0, minOf(it.text.length, EditDeviceNameFragment.MAX_LENGTH)) + ) + }, + keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences), + singleLine = true, + colors = TextFieldDefaults.colors( + focusedContainerColor = MaterialTheme.colorScheme.surfaceVariant + ), + modifier = Modifier + .fillMaxWidth() + .focusRequester(focusRequester) + .padding(top = 16.dp, bottom = 12.dp, start = 20.dp, end = 28.dp) + ) + Buttons.MediumTonal( + enabled = deviceName.text.isNotNullOrBlank() && (deviceName.text != name), + onClick = { onSave(deviceName.text) }, + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(end = 24.dp, bottom = 16.dp) + ) { + Text(text = stringResource(R.string.EditDeviceNameFragment__save)) + } + } + + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } +} + +@SignalPreview +@Composable +private fun DeviceListScreenLinkingPreview() { + Previews.Preview { + EditNameScreen( + state = LinkDeviceSettingsState( + deviceToEdit = Device(1, "Laptop", 0, 0) + ) + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt index 75dc61a952..6876518e13 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceFragment.kt @@ -9,6 +9,7 @@ import androidx.biometric.BiometricPrompt import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row @@ -59,6 +60,7 @@ import androidx.navigation.fragment.findNavController import org.signal.core.ui.Buttons import org.signal.core.ui.Dialogs import org.signal.core.ui.Dividers +import org.signal.core.ui.DropdownMenus import org.signal.core.ui.Previews import org.signal.core.ui.Scaffolds import org.signal.core.ui.SignalPreview @@ -146,6 +148,8 @@ class LinkDeviceFragment : ComposeFragment() { navController.popBackStack() } } + LinkDeviceSettingsState.OneTimeEvent.SnackbarNameChangeFailure -> Unit + LinkDeviceSettingsState.OneTimeEvent.SnackbarNameChangeSuccess -> Unit } if (state.oneTimeEvent != LinkDeviceSettingsState.OneTimeEvent.None) { @@ -176,7 +180,11 @@ class LinkDeviceFragment : ComposeFragment() { onDeviceSelectedForRemoval = { device -> viewModel.setDeviceToRemove(device) }, onDeviceRemovalConfirmed = { device -> viewModel.removeDevice(device) }, onSyncFailureRetryRequested = { deviceId -> viewModel.onSyncErrorRetryRequested(deviceId) }, - onSyncFailureIgnored = { viewModel.onSyncErrorIgnored() } + onSyncFailureIgnored = { viewModel.onSyncErrorIgnored() }, + onEditDevice = { device -> + viewModel.setDeviceToEdit(device) + navController.safeNavigate(R.id.action_linkDeviceFragment_to_editDeviceNameFragment) + } ) } } @@ -221,7 +229,8 @@ fun DeviceListScreen( onDeviceSelectedForRemoval: (Device?) -> Unit = {}, onDeviceRemovalConfirmed: (Device) -> Unit = {}, onSyncFailureRetryRequested: (Int?) -> Unit = {}, - onSyncFailureIgnored: () -> Unit = {} + onSyncFailureIgnored: () -> Unit = {}, + onEditDevice: (Device) -> Unit = {} ) { // If a bottom sheet is showing, we don't want the spinner underneath if (!state.bottomSheetVisible) { @@ -328,7 +337,7 @@ fun DeviceListScreen( ) } else { state.devices.forEach { device -> - DeviceRow(device, onDeviceSelectedForRemoval) + DeviceRow(device, onDeviceSelectedForRemoval, onEditDevice) } } } @@ -372,16 +381,14 @@ fun DeviceListScreen( } @Composable -fun DeviceRow(device: Device, setDeviceToRemove: (Device) -> Unit) { +fun DeviceRow(device: Device, setDeviceToRemove: (Device) -> Unit, onEditDevice: (Device) -> Unit) { val titleString = if (device.name.isNullOrEmpty()) stringResource(R.string.DeviceListItem_unnamed_device) else device.name val linkedDate = DateUtils.getDayPrecisionTimeSpanString(LocalContext.current, Locale.getDefault(), device.createdMillis) val lastActive = DateUtils.getDayPrecisionTimeSpanString(LocalContext.current, Locale.getDefault(), device.lastSeenMillis) - + val menuController = remember { DropdownMenus.MenuController() } Row( modifier = Modifier .fillMaxWidth() - .clickable { setDeviceToRemove(device) }, - verticalAlignment = Alignment.CenterVertically ) { Image( painter = painterResource(id = R.drawable.symbol_devices_24), @@ -395,13 +402,76 @@ fun DeviceRow(device: Device, setDeviceToRemove: (Device) -> Unit) { color = MaterialTheme.colorScheme.surfaceVariant, shape = CircleShape ) + .align(Alignment.CenterVertically) ) - Spacer(modifier = Modifier.size(16.dp)) - Column { + + Column( + modifier = Modifier.align(Alignment.CenterVertically).padding(start = 16.dp).weight(1f) + ) { Text(text = titleString, style = MaterialTheme.typography.bodyLarge) Text(stringResource(R.string.DeviceListItem_linked_s, linkedDate), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant) Text(stringResource(R.string.DeviceListItem_last_active_s, lastActive), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant) } + + Box { + Icon( + painterResource(id = R.drawable.symbol_more_vertical), + contentDescription = null, + modifier = Modifier.padding(top = 16.dp, end = 16.dp).clickable { menuController.show() } + ) + + DropdownMenus.Menu(controller = menuController, offsetX = 16.dp, offsetY = 4.dp) { controller -> + DropdownMenus.Item( + contentPadding = PaddingValues(0.dp), + text = { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp) + ) { + Icon( + painter = painterResource(id = R.drawable.symbol_link_slash_16), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + Text( + text = stringResource(R.string.LinkDeviceFragment__unlink), + modifier = Modifier.padding(horizontal = 16.dp), + style = MaterialTheme.typography.bodyLarge + ) + } + }, + onClick = { + setDeviceToRemove(device) + controller.hide() + } + ) + + DropdownMenus.Item( + contentPadding = PaddingValues(0.dp), + text = { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp) + ) { + Icon( + painter = painterResource(id = R.drawable.symbol_edit_24), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + Text( + text = stringResource(R.string.LinkDeviceFragment__edit_name), + modifier = Modifier.padding(horizontal = 16.dp), + style = MaterialTheme.typography.bodyLarge + ) + } + }, + onClick = { + onEditDevice(device) + controller.hide() + } + ) + } + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt index d2dee55fed..5aa62a84a0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt @@ -5,6 +5,7 @@ import org.signal.core.util.Base64 import org.signal.core.util.Stopwatch import org.signal.core.util.isNotNullOrBlank import org.signal.core.util.logging.Log +import org.signal.core.util.logging.logI import org.signal.core.util.logging.logW import org.signal.libsignal.protocol.InvalidKeyException import org.signal.libsignal.protocol.ecc.Curve @@ -13,6 +14,7 @@ import org.thoughtcrime.securesms.backup.v2.BackupRepository import org.thoughtcrime.securesms.crypto.ProfileKeyUtil import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.devicelist.protos.DeviceName +import org.thoughtcrime.securesms.jobs.DeviceNameChangeJob import org.thoughtcrime.securesms.jobs.LinkedDeviceInactiveCheckJob import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.net.SignalNetwork @@ -29,6 +31,7 @@ import java.io.File import java.io.FileInputStream import java.io.FileOutputStream import java.io.IOException +import java.nio.charset.StandardCharsets import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds @@ -334,6 +337,28 @@ object LinkDeviceRepository { return NetworkResult.NetworkError(IOException("Hit max retries!")) } + /** + * Changes the name of a linked device and sends a sync message if successful + */ + fun changeDeviceName(deviceName: String, deviceId: Int): DeviceNameChangeResult { + val encryptedDeviceName = Base64.encodeWithoutPadding(DeviceNameCipher.encryptDeviceName(deviceName.toByteArray(StandardCharsets.UTF_8), SignalStore.account.aciIdentityKey)) + return when (val result = SignalNetwork.linkDevice.setDeviceName(encryptedDeviceName, deviceId)) { + is NetworkResult.Success -> { + AppDependencies.jobManager.add(DeviceNameChangeJob(deviceId)) + DeviceNameChangeResult.Success.logI(TAG, "Successfully changed device name") + } + is NetworkResult.NetworkError -> { + DeviceNameChangeResult.NetworkError(result.exception).logW(TAG, "Could not change name due to network error.", result.exception) + } + is NetworkResult.StatusCodeError -> { + DeviceNameChangeResult.NetworkError(result.exception).logW(TAG, "Could not change name due to status code error ${result.code}") + } + is NetworkResult.ApplicationError -> { + throw result.throwable.logW(TAG, "Could not change name due to application error.") + } + } + } + sealed interface LinkDeviceResult { data object None : LinkDeviceResult data class Success(val token: String) : LinkDeviceResult @@ -350,4 +375,9 @@ object LinkDeviceRepository { data class BadRequest(val exception: IOException) : LinkUploadArchiveResult data class NetworkError(val exception: IOException) : LinkUploadArchiveResult } + + sealed interface DeviceNameChangeResult { + data object Success : DeviceNameChangeResult + data class NetworkError(val exception: IOException) : DeviceNameChangeResult + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSettingsState.kt index c97795cf38..d0a79c6a9b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceSettingsState.kt @@ -18,7 +18,8 @@ data class LinkDeviceSettingsState( val linkDeviceResult: LinkDeviceResult = LinkDeviceResult.None, val seenIntroSheet: Boolean = false, val seenEducationSheet: Boolean = false, - val bottomSheetVisible: Boolean = false + val bottomSheetVisible: Boolean = false, + val deviceToEdit: Device? = null ) { sealed interface DialogState { data object None : DialogState @@ -34,6 +35,8 @@ data class LinkDeviceSettingsState( data object ToastNetworkFailed : OneTimeEvent data class ToastUnlinked(val name: String) : OneTimeEvent data class ToastLinked(val name: String) : OneTimeEvent + data object SnackbarNameChangeSuccess : OneTimeEvent + data object SnackbarNameChangeFailure : OneTimeEvent data object ShowFinishedSheet : OneTimeEvent data object HideFinishedSheet : OneTimeEvent data object LaunchQrCodeScanner : OneTimeEvent diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt index 6937d83afd..1b0905b790 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceViewModel.kt @@ -323,4 +323,29 @@ class LinkDeviceViewModel : ViewModel() { ) } } + + fun setDeviceToEdit(device: Device) { + _state.update { + it.copy( + deviceToEdit = device + ) + } + } + + fun saveName(name: String) { + viewModelScope.launch(Dispatchers.IO) { + val device = _state.value.deviceToEdit!! + val result = LinkDeviceRepository.changeDeviceName(name, device.id) + val event = when (result) { + LinkDeviceRepository.DeviceNameChangeResult.Success -> OneTimeEvent.SnackbarNameChangeSuccess + is LinkDeviceRepository.DeviceNameChangeResult.NetworkError -> OneTimeEvent.SnackbarNameChangeFailure + } + + _state.update { + it.copy( + oneTimeEvent = event + ) + } + } + } } diff --git a/app/src/main/protowire/JobData.proto b/app/src/main/protowire/JobData.proto index bbd56caa5a..3ec13620c8 100644 --- a/app/src/main/protowire/JobData.proto +++ b/app/src/main/protowire/JobData.proto @@ -141,3 +141,7 @@ message UploadAttachmentToArchiveJobData { message BackupMediaSnapshotSyncJobData { uint64 syncTime = 1; } + +message DeviceNameChangeJobData { + uint32 deviceId = 1; +} diff --git a/app/src/main/res/navigation/app_settings_with_change_number.xml b/app/src/main/res/navigation/app_settings_with_change_number.xml index 5a4ebfbe74..53480bf079 100644 --- a/app/src/main/res/navigation/app_settings_with_change_number.xml +++ b/app/src/main/res/navigation/app_settings_with_change_number.xml @@ -244,6 +244,13 @@ app:exitAnim="@anim/fragment_open_exit" app:popEnterAnim="@anim/fragment_close_enter" app:popExitAnim="@anim/fragment_close_exit" /> + @@ -264,6 +271,10 @@ android:id="@+id/linkDeviceEducationSheet" android:name="org.thoughtcrime.securesms.linkdevice.LinkDeviceEducationSheet" /> + + Try linking again Continue without transferring + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index bc04bc50be..12eed31f9b 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -757,6 +757,8 @@ public SendMessageResult sendSyncMessage(SignalServiceSyncMessage message) content = createCallLinkUpdateContent(message.getCallLinkUpdate().get()); } else if (message.getCallLogEvent().isPresent()) { content = createCallLogEventContent(message.getCallLogEvent().get()); + } else if (message.getDeviceNameChange().isPresent()) { + content = createDeviceNameChangeContent(message.getDeviceNameChange().get()); } else { throw new IOException("Unsupported sync message!"); } @@ -1729,6 +1731,13 @@ private Content createCallLogEventContent(SyncMessage.CallLogEvent proto) { return container.syncMessage(builder.build()).build(); } + private Content createDeviceNameChangeContent(SyncMessage.DeviceNameChange proto) { + Content.Builder container = new Content.Builder(); + SyncMessage.Builder builder = createSyncMessageBuilder().deviceNameChange(proto); + + return container.syncMessage(builder.build()).build(); + } + private SyncMessage.Builder createSyncMessageBuilder() { byte[] padding = Util.getRandomLengthSecretBytes(512); diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/link/LinkDeviceApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/link/LinkDeviceApi.kt index 2460c3b3d3..ebb0e58278 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/link/LinkDeviceApi.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/link/LinkDeviceApi.kt @@ -131,4 +131,19 @@ class LinkDeviceApi(private val pushServiceSocket: PushServiceSocket) { ) } } + + /** + * Sets the name for a linked device + * + * PUT /v1/accounts/name + * + * - 204: Success. + * - 403: Not authorized to change the name of the device with the given ID + * - 404: No device found with the given ID + */ + fun setDeviceName(encryptedDeviceName: String, deviceId: Int): NetworkResult { + return NetworkResult.fromFetch { + pushServiceSocket.setDeviceName(deviceId, SetDeviceNameRequest(encryptedDeviceName)) + } + } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/link/SetDeviceNameRequest.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/link/SetDeviceNameRequest.kt new file mode 100644 index 0000000000..9723284d76 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/link/SetDeviceNameRequest.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.link + +import com.fasterxml.jackson.annotation.JsonProperty + +/** + * Request body for setting the name of a linked device. + */ +data class SetDeviceNameRequest( + @JsonProperty val deviceName: String +) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java index 4555b6a024..f54b3a13bc 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java @@ -6,6 +6,7 @@ package org.whispersystems.signalservice.api.messages.multidevice; +import org.whispersystems.signalservice.internal.push.SyncMessage.DeviceNameChange; import org.whispersystems.signalservice.internal.push.SyncMessage.CallEvent; import org.whispersystems.signalservice.internal.push.SyncMessage.CallLinkUpdate; import org.whispersystems.signalservice.internal.push.SyncMessage.CallLogEvent; @@ -35,6 +36,7 @@ public class SignalServiceSyncMessage { private final Optional callEvent; private final Optional callLinkUpdate; private final Optional callLogEvent; + private final Optional deviceNameChange; private SignalServiceSyncMessage(Optional sent, Optional contacts, @@ -52,7 +54,8 @@ private SignalServiceSyncMessage(Optional sent, Optional> views, Optional callEvent, Optional callLinkUpdate, - Optional callLogEvent) + Optional callLogEvent, + Optional deviceNameChange) { this.sent = sent; this.contacts = contacts; @@ -71,6 +74,7 @@ private SignalServiceSyncMessage(Optional sent, this.callEvent = callEvent; this.callLinkUpdate = callLinkUpdate; this.callLogEvent = callLogEvent; + this.deviceNameChange = deviceNameChange; } public static SignalServiceSyncMessage forSentTranscript(SentTranscriptMessage sent) { @@ -90,6 +94,7 @@ public static SignalServiceSyncMessage forSentTranscript(SentTranscriptMessage s Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -110,6 +115,7 @@ public static SignalServiceSyncMessage forContacts(ContactsMessage contacts) { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -130,6 +136,7 @@ public static SignalServiceSyncMessage forRequest(RequestMessage request) { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -150,6 +157,7 @@ public static SignalServiceSyncMessage forRead(List reads) { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -170,6 +178,7 @@ public static SignalServiceSyncMessage forViewed(List views) { Optional.of(views), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -190,6 +199,7 @@ public static SignalServiceSyncMessage forViewOnceOpen(ViewOnceOpenMessage timer Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -213,6 +223,7 @@ public static SignalServiceSyncMessage forRead(ReadMessage read) { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -233,6 +244,7 @@ public static SignalServiceSyncMessage forVerified(VerifiedMessage verifiedMessa Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -253,6 +265,7 @@ public static SignalServiceSyncMessage forBlocked(BlockedListMessage blocked) { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -273,6 +286,7 @@ public static SignalServiceSyncMessage forConfiguration(ConfigurationMessage con Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -293,6 +307,7 @@ public static SignalServiceSyncMessage forStickerPackOperations(List getCallLogEvent() { return callLogEvent; } + public Optional getDeviceNameChange() { + return deviceNameChange; + } + public enum FetchType { LOCAL_PROFILE, STORAGE_MANIFEST, diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java index 330deffa96..4bcddc21c8 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java @@ -66,6 +66,7 @@ import org.whispersystems.signalservice.api.groupsv2.CredentialResponse; import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString; import org.whispersystems.signalservice.api.link.LinkedDeviceVerificationCodeResponse; +import org.whispersystems.signalservice.api.link.SetDeviceNameRequest; import org.whispersystems.signalservice.api.link.SetLinkedDeviceTransferArchiveRequest; import org.whispersystems.signalservice.api.link.WaitForLinkedDeviceResponse; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener; @@ -239,6 +240,7 @@ public class PushServiceSocket { private static final String USERNAME_LINK_PATH = "/v1/accounts/username_link"; private static final String USERNAME_FROM_LINK_PATH = "/v1/accounts/username_link/%s"; private static final String DELETE_ACCOUNT_PATH = "/v1/accounts/me"; + private static final String SET_DEVICE_NAME_PATH = "/v1/accounts/name?deviceId=%s"; private static final String CHANGE_NUMBER_PATH = "/v2/accounts/number"; private static final String IDENTIFIER_REGISTERED_PATH = "/v1/accounts/account/%s"; private static final String REQUEST_ACCOUNT_DATA_PATH = "/v2/accounts/data_report"; @@ -1380,6 +1382,11 @@ public void deleteAccount() throws IOException { makeServiceRequest(DELETE_ACCOUNT_PATH, "DELETE", null); } + public void setDeviceName(int deviceId, @Nonnull SetDeviceNameRequest request) throws IOException { + String body = JsonUtil.toJson(request); + makeServiceRequest(String.format(Locale.US, SET_DEVICE_NAME_PATH, deviceId), "PUT", body); + } + public void requestRateLimitPushChallenge() throws IOException { makeServiceRequest(REQUEST_RATE_LIMIT_PUSH_CHALLENGE, "POST", ""); } diff --git a/libsignal-service/src/main/protowire/SignalService.proto b/libsignal-service/src/main/protowire/SignalService.proto index fcdfa96d88..dc3166c428 100644 --- a/libsignal-service/src/main/protowire/SignalService.proto +++ b/libsignal-service/src/main/protowire/SignalService.proto @@ -707,6 +707,11 @@ message SyncMessage { repeated AttachmentDelete attachmentDeletes = 4; } + message DeviceNameChange { + reserved /*name*/ 1; + optional uint32 deviceId = 2; + } + optional Sent sent = 1; optional Contacts contacts = 2; reserved /*groups*/ 3; @@ -729,6 +734,7 @@ message SyncMessage { optional CallLinkUpdate callLinkUpdate = 20; optional CallLogEvent callLogEvent = 21; optional DeleteForMe deleteForMe = 22; + optional DeviceNameChange deviceNameChange = 23; } message AttachmentPointer { From 84cb0d357b27139ac73a5249e754862569d91d68 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 25 Nov 2024 16:46:24 -0500 Subject: [PATCH 21/56] Fix export bugs causing validation errors. --- .../database/MessageTableArchiveExtensions.kt | 11 +++++++++++ .../v2/exporters/ChatItemArchiveExporter.kt | 12 ++++++++++-- .../model/GroupsV2UpdateMessageConverter.kt | 17 ++++++++++++----- .../signalservice/api/push/ServiceId.kt | 5 +++++ 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableArchiveExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableArchiveExtensions.kt index d3b49ce4cb..9893ca7dfe 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableArchiveExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableArchiveExtensions.kt @@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.backup.v2.exporters.ChatItemArchiveExporter import org.thoughtcrime.securesms.backup.v2.importer.ChatItemArchiveImporter import org.thoughtcrime.securesms.database.GroupTable import org.thoughtcrime.securesms.database.MessageTable +import org.thoughtcrime.securesms.database.MessageTypes import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.recipients.RecipientId @@ -78,6 +79,16 @@ fun MessageTable.getMessagesForBackup(db: SignalDatabase, backupTime: Long, medi """ ) + // If someone re-registers with a new phone number, previous outgoing messages will no longer be associated with self. + // This cleans it up by changing the from to be the current self id for all outgoing messages. + db.rawWritableDatabase.execSQL( + """ + UPDATE ${MessageTable.TABLE_NAME} + SET ${MessageTable.FROM_RECIPIENT_ID} = ${selfRecipientId.toLong()} + WHERE (${MessageTable.TYPE} & ${MessageTypes.BASE_TYPE_MASK}) IN (${MessageTypes.OUTGOING_MESSAGE_TYPES.joinToString(",")}) + """ + ) + return ChatItemArchiveExporter( db = db, backupStartTime = backupTime, diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt index 84dcbba7eb..d96302d163 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt @@ -237,7 +237,12 @@ class ChatItemArchiveExporter( } MessageTypes.isGroupV2(record.type) && MessageTypes.isGroupUpdate(record.type) -> { - builder.updateMessage = record.toRemoteGroupUpdate() ?: continue + val update = record.toRemoteGroupUpdate() ?: continue + if (update.groupChange!!.updates.isEmpty()) { + Log.w(TAG, "Group update record with ID ${record.id} missing updates. Skipping.") + continue + } + builder.updateMessage = update } MessageTypes.isGroupV1MigrationEvent(record.type) -> { @@ -541,7 +546,10 @@ private fun CallTable.Call.toRemoteCallUpdate(db: SignalDatabase, messageRecord: CallTable.Event.NOT_ACCEPTED -> IndividualCall.State.NOT_ACCEPTED CallTable.Event.ONGOING -> IndividualCall.State.ACCEPTED CallTable.Event.DELETE -> return null - else -> IndividualCall.State.UNKNOWN_STATE + else -> { + Log.w(TAG, "Unable to map 1:1 call state from event: ${this.event.name}") + IndividualCall.State.UNKNOWN_STATE + } }, startedCallTimestamp = this.timestamp, read = messageRecord.read diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageConverter.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageConverter.kt index 6e50e35f81..ae52e61b86 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageConverter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageConverter.kt @@ -6,6 +6,7 @@ package org.thoughtcrime.securesms.database.model import okio.ByteString +import okio.ByteString.Companion.toByteString import org.signal.core.util.StringUtil import org.signal.core.util.isNullOrEmpty import org.signal.storageservice.protos.groups.AccessControl @@ -50,7 +51,7 @@ import org.thoughtcrime.securesms.backup.v2.proto.SelfInvitedOtherUserToGroupUpd import org.thoughtcrime.securesms.backup.v2.proto.SelfInvitedToGroupUpdate import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil -import org.whispersystems.signalservice.api.push.ServiceId.Companion.parseOrNull +import org.whispersystems.signalservice.api.push.ServiceId import org.whispersystems.signalservice.api.push.ServiceIds import org.whispersystems.signalservice.api.util.UuidUtil import java.util.LinkedList @@ -131,7 +132,7 @@ object GroupsV2UpdateMessageConverter { } val updates: MutableList = LinkedList() var editorUnknown = change.editorServiceIdBytes.size == 0 - val editorServiceId = if (editorUnknown) null else parseOrNull(change.editorServiceIdBytes) + val editorServiceId = if (editorUnknown) null else ServiceId.parseOrNull(change.editorServiceIdBytes) if (editorServiceId == null || editorServiceId.isUnknown) { editorUnknown = true } @@ -253,10 +254,13 @@ object GroupsV2UpdateMessageConverter { ) ) } else { + val serviceId = ServiceId.parseOrNull(invitee.serviceIdBytes) revokedInvitees.add( - GroupInvitationRevokedUpdate.Invitee( - inviteeAci = invitee.serviceIdBytes - ) + when (serviceId) { + is ServiceId.ACI -> GroupInvitationRevokedUpdate.Invitee(inviteeAci = serviceId.toByteString()) + is ServiceId.PNI -> GroupInvitationRevokedUpdate.Invitee(inviteePni = serviceId.toByteStringWithoutPrefix()) + else -> throw IllegalStateException() + } ) } } @@ -465,6 +469,7 @@ object GroupsV2UpdateMessageConverter { } ) } + AccessRequired.ADMINISTRATOR -> { groupLinkEnabled = true updates.add( @@ -485,6 +490,7 @@ object GroupsV2UpdateMessageConverter { } ) } + AccessRequired.UNSATISFIABLE -> { updates.add( GroupChangeChatUpdate.Update( @@ -494,6 +500,7 @@ object GroupsV2UpdateMessageConverter { ) ) } + else -> {} } if (!groupLinkEnabled && change.newInviteLinkPassword.size > 0) { diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/ServiceId.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/ServiceId.kt index 6562589e48..0dc4de0076 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/ServiceId.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/ServiceId.kt @@ -1,12 +1,14 @@ package org.whispersystems.signalservice.api.push import okio.ByteString +import okio.ByteString.Companion.toByteString import org.signal.libsignal.protocol.ServiceId.InvalidServiceIdException import org.signal.libsignal.protocol.SignalProtocolAddress import org.signal.libsignal.protocol.logging.Log import org.whispersystems.signalservice.api.push.ServiceId.ACI import org.whispersystems.signalservice.api.push.ServiceId.PNI import org.whispersystems.signalservice.api.util.UuidUtil +import org.whispersystems.signalservice.api.util.toByteArray import java.util.UUID import org.signal.libsignal.protocol.ServiceId as LibSignalServiceId import org.signal.libsignal.protocol.ServiceId.Aci as LibSignalAci @@ -229,5 +231,8 @@ sealed class ServiceId(val libSignalServiceId: LibSignalServiceId) { /** String version without the PNI: prefix. This is only for specific proto fields. For application storage, prefer [toString]. */ fun toStringWithoutPrefix(): String = rawUuid.toString() + + /** [ByteString] version without the PNI byte prefix. */ + fun toByteStringWithoutPrefix(): ByteString = rawUuid.toByteArray().toByteString() } } From 8a2ac4b8dc2abb8f9e980a0d513fd55b969526fe Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 25 Nov 2024 17:07:34 -0500 Subject: [PATCH 22/56] Update GV2 spinner transformer to for group updates in message extras. --- .../database/GV2UpdateTransformer.kt | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/app/src/spinner/java/org/thoughtcrime/securesms/database/GV2UpdateTransformer.kt b/app/src/spinner/java/org/thoughtcrime/securesms/database/GV2UpdateTransformer.kt index c24a10834b..fd78a3231b 100644 --- a/app/src/spinner/java/org/thoughtcrime/securesms/database/GV2UpdateTransformer.kt +++ b/app/src/spinner/java/org/thoughtcrime/securesms/database/GV2UpdateTransformer.kt @@ -9,32 +9,49 @@ import org.signal.spinner.DefaultColumnTransformer import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.UpdateDescription import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context +import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExtras import org.thoughtcrime.securesms.dependencies.AppDependencies object GV2UpdateTransformer : ColumnTransformer { override fun matches(tableName: String?, columnName: String): Boolean { - return columnName == MessageTable.BODY && (tableName == null || tableName == MessageTable.TABLE_NAME) + return (columnName == MessageTable.BODY || columnName == MessageTable.MESSAGE_EXTRAS) && (tableName == null || tableName == MessageTable.TABLE_NAME) } override fun transform(tableName: String?, columnName: String, cursor: Cursor): String? { val type: Long = cursor.getMessageType() - if (type == -1L) { + if (type == -1L || !MessageTypes.isGroupV2(type) || !MessageTypes.isGroupUpdate(type)) { return DefaultColumnTransformer.transform(tableName, columnName, cursor) } - val body: String? = CursorUtil.requireString(cursor, MessageTable.BODY) + return when (columnName) { + MessageTable.BODY -> { + val body: String? = CursorUtil.requireString(cursor, MessageTable.BODY) + body?.let { bodyGroupUpdate(it) } + } - return if (MessageTypes.isGroupV2(type) && MessageTypes.isGroupUpdate(type) && body != null) { - val decoded = Base64.decode(body) - val decryptedGroupV2Context = DecryptedGroupV2Context.ADAPTER.decode(decoded) - val gv2ChangeDescription: UpdateDescription = MessageRecord.getGv2ChangeDescription(AppDependencies.application, body, null) + MessageTable.MESSAGE_EXTRAS -> { + val messageExtras = CursorUtil.requireBlob(cursor, MessageTable.MESSAGE_EXTRAS)?.let { MessageExtras.ADAPTER.decode(it) } + messageExtras?.let { messageExtrasGroupUpdate(messageExtras) } + } - "${gv2ChangeDescription.spannable}

${decryptedGroupV2Context.change}" - } else { - body + else -> DefaultColumnTransformer.transform(tableName, columnName, cursor) } } + + private fun bodyGroupUpdate(body: String): String { + val decoded = Base64.decode(body) + val decryptedGroupV2Context = DecryptedGroupV2Context.ADAPTER.decode(decoded) + val gv2ChangeDescription: UpdateDescription = MessageRecord.getGv2ChangeDescription(AppDependencies.application, body, null) + + return "${gv2ChangeDescription.spannable}

${decryptedGroupV2Context.change}" + } + + private fun messageExtrasGroupUpdate(messageExtras: MessageExtras): String { + val gv2ChangeDescription: UpdateDescription = MessageRecord.getGv2ChangeDescription(AppDependencies.application, messageExtras, null) + + return "${gv2ChangeDescription.spannable}

${messageExtras.gv2UpdateDescription!!.gv2ChangeDescription!!.change}" + } } private fun Cursor.getMessageType(): Long { From 4b21e6a39fad2b0afe379f03ce4ca70d4c1faa8c Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 26 Nov 2024 09:18:03 -0500 Subject: [PATCH 23/56] Improve storage service diff logging. --- .../securesms/storage/DefaultStorageRecordProcessor.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/DefaultStorageRecordProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/storage/DefaultStorageRecordProcessor.kt index 5ff6d74772..e172eab555 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/DefaultStorageRecordProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/DefaultStorageRecordProcessor.kt @@ -58,12 +58,12 @@ abstract class DefaultStorageRecordProcessor> : StorageRecor if (merged != local.get()) { val update = StorageRecordUpdate(local.get(), merged) - info(i, remote, "[Local Update] $update") + info(i, remote, "[Local Update] " + local.get().describeDiff(merged)) updateLocal(update) } } } else { - info(i, remote, "No matching local record. Inserting.") + info(i, remote, "[Local Insert] No matching local record. Inserting.") insertLocal(remote) } } From 878900c09cf43e33f7d83d6288665aabee7092b4 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 26 Nov 2024 11:06:48 -0500 Subject: [PATCH 24/56] Remove some unnecessary call tab requeries. --- .../securesms/calls/log/CallLogViewModel.kt | 1 + .../service/webrtc/CallLinkPeekInfo.kt | 4 ++++ .../signal/core/util/concurrent/RxExtensions.kt | 17 +++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogViewModel.kt index 778a6f9b78..a3f406454a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogViewModel.kt @@ -82,6 +82,7 @@ class CallLogViewModel( disposables += AppDependencies .signalCallManager .peekInfoCache + .skipWhile { cache -> cache.isEmpty() || cache.values.all { it.isCompletelyInactive } } .observeOn(Schedulers.computation()) .distinctUntilChanged() .subscribe { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/CallLinkPeekInfo.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/CallLinkPeekInfo.kt index 5e4ff78ad1..20769ef989 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/CallLinkPeekInfo.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/CallLinkPeekInfo.kt @@ -17,6 +17,10 @@ data class CallLinkPeekInfo( val isActive: Boolean, val isJoined: Boolean ) { + + val isCompletelyInactive + get() = callId == null && !isActive && !isJoined + companion object { @JvmStatic fun fromPeekInfo(peekInfo: PeekInfo): CallLinkPeekInfo { diff --git a/core-util/src/main/java/org/signal/core/util/concurrent/RxExtensions.kt b/core-util/src/main/java/org/signal/core/util/concurrent/RxExtensions.kt index aab2f92faf..2d2ad45552 100644 --- a/core-util/src/main/java/org/signal/core/util/concurrent/RxExtensions.kt +++ b/core-util/src/main/java/org/signal/core/util/concurrent/RxExtensions.kt @@ -73,3 +73,20 @@ fun , T : Any> Single.subscribeWithSubject( return subject } + +/** + * Skips the first item emitted from the flowable, but only if it matches the provided [predicate]. + */ +fun Flowable.skipFirstIf(predicate: (T) -> Boolean): Flowable { + return this + .scan(Pair(false, null)) { acc, item -> + val firstItemInList = !acc.first + if (firstItemInList && predicate(item)) { + true to null + } else { + true to item + } + } + .filter { it.second != null } + .map { it.second!! } +} From 0913b84657deeb154ccf8e6d96302441fdf142ef Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 26 Nov 2024 12:24:47 -0500 Subject: [PATCH 25/56] Verify group ids on peer-to-peer group changes. --- app/src/main/AndroidManifest.xml | 1 + .../securesms/groups/GroupManagerV2.java | 12 +++--- .../groupsv2/DecryptChangeVerificationMode.kt | 39 +++++++++++++++++++ .../api/groupsv2/GroupsV2Api.java | 2 +- .../api/groupsv2/GroupsV2Operations.java | 22 +++++++---- .../src/main/protowire/DecryptedGroups.proto | 1 + .../src/main/protowire/Groups.proto | 1 + .../GroupChangeUtil_changeIsEmpty_Test.java | 2 +- ...roupsV2Operations_decrypt_change_Test.java | 8 ++-- 9 files changed, 69 insertions(+), 19 deletions(-) create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/DecryptChangeVerificationMode.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1e65ade2ca..b33459568e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -90,6 +90,7 @@ + diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java index 7ca5faeb4c..26957f44a3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java @@ -50,8 +50,8 @@ import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.util.ProfileUtil; +import org.whispersystems.signalservice.api.groupsv2.DecryptChangeVerificationMode; import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupResponse; -import org.whispersystems.signalservice.api.groupsv2.ReceivedGroupSendEndorsements; import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil; import org.whispersystems.signalservice.api.groupsv2.GroupCandidate; import org.whispersystems.signalservice.api.groupsv2.GroupChangeReconstruct; @@ -61,6 +61,7 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException; import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException; +import org.whispersystems.signalservice.api.groupsv2.ReceivedGroupSendEndorsements; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.ServiceId.ACI; import org.whispersystems.signalservice.api.push.ServiceId.PNI; @@ -678,8 +679,7 @@ private GroupManager.GroupActionResult commitChange(@NonNull GroupChange.Actions GroupChangeResponse changeResponse = commitToServer(changeActions); GroupChange signedGroupChange = changeResponse.groupChange; try { - //noinspection OptionalGetWithoutIsPresent - decryptedChange = groupOperations.decryptChange(signedGroupChange, false).get(); + decryptedChange = groupOperations.decryptChange(signedGroupChange, DecryptChangeVerificationMode.alreadyTrusted()).get(); decryptedGroupState = DecryptedGroupUtil.apply(previousGroupState, decryptedChange); } catch (VerificationFailedException | InvalidGroupStateException | NotAbleToApplyGroupV2ChangeException e) { Log.w(TAG, e); @@ -766,7 +766,7 @@ private DecryptedGroupChange getDecryptedGroupChange(@Nullable byte[] signedGrou GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(GroupSecretParams.deriveFromMasterKey(groupMasterKey)); try { - return groupOperations.decryptChange(GroupChange.ADAPTER.decode(signedGroupChange), true) + return groupOperations.decryptChange(GroupChange.ADAPTER.decode(signedGroupChange), DecryptChangeVerificationMode.verify(GroupId.getIdentifierForMasterKey(groupMasterKey))) .orElse(null); } catch (VerificationFailedException | InvalidGroupStateException | IOException e) { Log.w(TAG, "Unable to verify supplied group change", e); @@ -989,7 +989,7 @@ private GroupManager.GroupActionResult fetchGroupStateAndSendUpdate(@NonNull Rec { try { //noinspection OptionalGetWithoutIsPresent - return groupOperations.decryptChange(signedGroupChange, false).get(); + return groupOperations.decryptChange(signedGroupChange, DecryptChangeVerificationMode.alreadyTrusted()).get(); } catch (VerificationFailedException | InvalidGroupStateException | IOException e) { Log.w(TAG, e); throw new GroupChangeFailedException(e); @@ -1159,7 +1159,7 @@ void cancelJoinRequest() try { //noinspection OptionalGetWithoutIsPresent - DecryptedGroupChange decryptedChange = groupOperations.decryptChange(signedGroupChange, false).get(); + DecryptedGroupChange decryptedChange = groupOperations.decryptChange(signedGroupChange, DecryptChangeVerificationMode.alreadyTrusted()).get(); DecryptedGroup newGroup = DecryptedGroupUtil.applyWithoutRevisionCheck(decryptedGroup, decryptedChange); groupDatabase.update(groupId, resetRevision(newGroup, decryptedGroup.revision), null); diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/DecryptChangeVerificationMode.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/DecryptChangeVerificationMode.kt new file mode 100644 index 0000000000..2fc1b5840e --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/DecryptChangeVerificationMode.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.groupsv2 + +import org.signal.libsignal.zkgroup.groups.GroupIdentifier + +/** + * Details what verification should take place when decrypting a group change. + * + * @param verify Should perform group change verification during decryption + * @param groupId The id this change should apply to and will be verified is set in change payload + */ +class DecryptChangeVerificationMode private constructor( + @get:JvmName("verify") val verify: Boolean, + @get:JvmName("groupId") val groupId: GroupIdentifier? +) { + companion object { + + /** + * Use when the changes are already trusted. This would be during group creation or when fetching + * group changes directly from the server. + */ + @JvmStatic + @get:JvmName("alreadyTrusted") + val alreadyTrusted by lazy { DecryptChangeVerificationMode(verify = false, groupId = null) } + + /** + * Use when the changes are from an untrusted source like a P2P message of a group change. This + * will verify the signature and group id match. + */ + @JvmStatic + fun verify(groupId: GroupIdentifier): DecryptChangeVerificationMode { + return DecryptChangeVerificationMode(verify = true, groupId = groupId) + } + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Api.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Api.java index 0ce4797ccb..40f7726311 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Api.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Api.java @@ -124,7 +124,7 @@ public GroupHistoryPage getGroupHistoryPage(GroupSecretParams groupSecretParams, for (GroupChanges.GroupChangeState change : group.getGroupChanges().groupChanges) { DecryptedGroup decryptedGroup = change.groupState != null ? groupOperations.decryptGroup(change.groupState) : null; - DecryptedGroupChange decryptedChange = change.groupChange != null ? groupOperations.decryptChange(change.groupChange, false).orElse(null) : null; + DecryptedGroupChange decryptedChange = change.groupChange != null ? groupOperations.decryptChange(change.groupChange, DecryptChangeVerificationMode.alreadyTrusted()).orElse(null) : null; result.add(new DecryptedGroupChangeLog(decryptedGroup, decryptedChange)); } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Operations.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Operations.java index 4019528390..0f71acf647 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Operations.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Operations.java @@ -7,6 +7,7 @@ import org.signal.libsignal.zkgroup.VerificationFailedException; import org.signal.libsignal.zkgroup.auth.ClientZkAuthOperations; import org.signal.libsignal.zkgroup.groups.ClientZkGroupCipher; +import org.signal.libsignal.zkgroup.groups.GroupIdentifier; import org.signal.libsignal.zkgroup.groups.GroupSecretParams; import org.signal.libsignal.zkgroup.groups.ProfileKeyCiphertext; import org.signal.libsignal.zkgroup.groups.UuidCiphertext; @@ -45,6 +46,7 @@ import java.io.IOException; import java.security.SecureRandom; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -486,14 +488,13 @@ public DecryptedGroup decryptGroup(Group group) } /** - * @param verifySignature You might want to avoid verification if you already know it's correct, or you - * are not going to pass to other clients. - *

- * Also, if you know it's version 0, do not verify because changes for version 0 - * are not signed, but should be empty. + * @param verification You might want to avoid verification if you already know it's correct, or you are not going to pass to other clients. + *

+ * Also, if you know it's version 0, do not verify because changes for version 0 are not signed, but should be empty. + * * @return {@link Optional#empty()} if the epoch for the change is higher that this code can decrypt. */ - public Optional decryptChange(GroupChange groupChange, boolean verifySignature) + public Optional decryptChange(GroupChange groupChange, @Nonnull DecryptChangeVerificationMode verification) throws IOException, VerificationFailedException, InvalidGroupStateException { if (groupChange.changeEpoch > HIGHEST_KNOWN_EPOCH) { @@ -501,7 +502,14 @@ public Optional decryptChange(GroupChange groupChange, boo return Optional.empty(); } - GroupChange.Actions actions = verifySignature ? getVerifiedActions(groupChange) : getActions(groupChange); + GroupChange.Actions actions = verification.verify() ? getVerifiedActions(groupChange) : getActions(groupChange); + + if (verification.verify()) { + GroupIdentifier groupId = verification.groupId(); + if (groupId == null || !Arrays.equals(groupId.serialize(), actions.groupId.toByteArray())) { + throw new VerificationFailedException("Invalid group id"); + } + } return Optional.of(decryptChange(actions)); } diff --git a/libsignal-service/src/main/protowire/DecryptedGroups.proto b/libsignal-service/src/main/protowire/DecryptedGroups.proto index fb8263ecb3..86b0595438 100644 --- a/libsignal-service/src/main/protowire/DecryptedGroups.proto +++ b/libsignal-service/src/main/protowire/DecryptedGroups.proto @@ -75,6 +75,7 @@ message DecryptedGroup { // Keep field numbers in step message DecryptedGroupChange { bytes editorServiceIdBytes = 1; + reserved 25; // groupId used only during verification + decrypt, only provided by server uint32 revision = 2; repeated DecryptedMember newMembers = 3; repeated bytes deleteMembers = 4; diff --git a/libsignal-service/src/main/protowire/Groups.proto b/libsignal-service/src/main/protowire/Groups.proto index 63970c7741..e202eb2aaf 100644 --- a/libsignal-service/src/main/protowire/Groups.proto +++ b/libsignal-service/src/main/protowire/Groups.proto @@ -183,6 +183,7 @@ message GroupChange { } bytes sourceServiceId = 1; + bytes groupId = 25; // Only set when receiving from server uint32 revision = 2; repeated AddMemberAction addMembers = 3; repeated DeleteMemberAction deleteMembers = 4; diff --git a/libsignal-service/src/test/java/org/whispersystems/signalservice/api/groupsv2/GroupChangeUtil_changeIsEmpty_Test.java b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/groupsv2/GroupChangeUtil_changeIsEmpty_Test.java index 24ff9e4f9a..7a58d848ca 100644 --- a/libsignal-service/src/test/java/org/whispersystems/signalservice/api/groupsv2/GroupChangeUtil_changeIsEmpty_Test.java +++ b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/groupsv2/GroupChangeUtil_changeIsEmpty_Test.java @@ -22,7 +22,7 @@ public void ensure_GroupChangeUtil_knows_about_all_fields_of_GroupChange_Actions int maxFieldFound = getMaxDeclaredFieldNumber(GroupChange.Actions.class); assertEquals("GroupChangeUtil and its tests need updating to account for new fields on " + GroupChange.Actions.class.getName(), - 24, maxFieldFound); + 25, maxFieldFound); } @Test diff --git a/libsignal-service/src/test/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Operations_decrypt_change_Test.java b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Operations_decrypt_change_Test.java index 1b963514f9..1ba884d1e7 100644 --- a/libsignal-service/src/test/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Operations_decrypt_change_Test.java +++ b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/groupsv2/GroupsV2Operations_decrypt_change_Test.java @@ -82,7 +82,7 @@ public void cannot_decrypt_change_with_epoch_higher_than_known() throws IOExcept .changeEpoch(GroupsV2Operations.HIGHEST_KNOWN_EPOCH + 1) .build(); - Optional decryptedGroupChangeOptional = groupOperations.decryptChange(change, false); + Optional decryptedGroupChangeOptional = groupOperations.decryptChange(change, DecryptChangeVerificationMode.alreadyTrusted()); assertFalse(decryptedGroupChangeOptional.isPresent()); } @@ -159,7 +159,7 @@ public void cannot_decrypt_member_additions_with_bad_cipher_text_field3() throws actions.addMembers(List.of(new GroupChange.Actions.AddMemberAction.Builder().added(new Member.Builder().role(Member.Role.DEFAULT) .presentation(ByteString.of(randomPresentation)).build()).build())); - groupOperations.decryptChange(new GroupChange.Builder().actions(actions.build().encodeByteString()).build(), false); + groupOperations.decryptChange(new GroupChange.Builder().actions(actions.build().encodeByteString()).build(), DecryptChangeVerificationMode.alreadyTrusted()); } @Test @@ -180,7 +180,7 @@ public void cannot_decrypt_member_removals_with_bad_cipher_text_field4() throws actions.deleteMembers(List.of(new GroupChange.Actions.DeleteMemberAction.Builder().deletedUserId(ByteString.of(randomPresentation)).build())); - groupOperations.decryptChange(new GroupChange.Builder().actions(actions.build().encodeByteString()).build(), false); + groupOperations.decryptChange(new GroupChange.Builder().actions(actions.build().encodeByteString()).build(), DecryptChangeVerificationMode.alreadyTrusted()); } @Test @@ -518,7 +518,7 @@ void assertDecryptionWithEditorSet(GroupChange.Actions.Builder inputChange, private DecryptedGroupChange decrypt(GroupChange build) { try { - return groupOperations.decryptChange(build, false).get(); + return groupOperations.decryptChange(build, DecryptChangeVerificationMode.alreadyTrusted()).get(); } catch (IOException | VerificationFailedException | InvalidGroupStateException e) { throw new AssertionError(e); } From 10d394f39e917557b1b6d996b144c093f9469b44 Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Fri, 22 Nov 2024 20:02:57 +0100 Subject: [PATCH 26/56] Inject SignalWebSocket into IncomingMessageObserver. Resolves #13820 --- .../securesms/dependencies/AppDependencies.kt | 2 +- .../dependencies/ApplicationDependencyProvider.java | 4 ++-- .../dependencies/NetworkDependenciesModule.kt | 2 +- .../securesms/messages/IncomingMessageObserver.kt | 10 +++++----- .../dependencies/MockApplicationDependencyProvider.kt | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt index 778e081aff..549dd5ff75 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt @@ -339,7 +339,7 @@ object AppDependencies { fun provideMegaphoneRepository(): MegaphoneRepository fun provideEarlyMessageCache(): EarlyMessageCache fun provideMessageNotifier(): MessageNotifier - fun provideIncomingMessageObserver(): IncomingMessageObserver + fun provideIncomingMessageObserver(signalWebSocket: SignalWebSocket): IncomingMessageObserver fun provideTrimThreadsByDateManager(): TrimThreadsByDateManager fun provideViewOnceMessageManager(): ViewOnceMessageManager fun provideExpiringStoriesManager(): ExpiringStoriesManager diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java index 948c40ae6f..cabd791375 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java @@ -207,8 +207,8 @@ public ApplicationDependencyProvider(@NonNull Application context) { } @Override - public @NonNull IncomingMessageObserver provideIncomingMessageObserver() { - return new IncomingMessageObserver(context); + public @NonNull IncomingMessageObserver provideIncomingMessageObserver(@NonNull SignalWebSocket signalWebSocket) { + return new IncomingMessageObserver(context, signalWebSocket); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt index 0e253e5b47..a5348627ae 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/NetworkDependenciesModule.kt @@ -75,7 +75,7 @@ class NetworkDependenciesModule( val signalServiceMessageSender: SignalServiceMessageSender by _signalServiceMessageSender val incomingMessageObserver: IncomingMessageObserver by lazy { - provider.provideIncomingMessageObserver() + provider.provideIncomingMessageObserver(signalWebSocket) } val pushServiceSocket: PushServiceSocket by lazy { diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt index 6ee92aa296..9edee87427 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt @@ -31,6 +31,7 @@ import org.thoughtcrime.securesms.util.AlarmSleepTimer import org.thoughtcrime.securesms.util.AppForegroundObserver import org.thoughtcrime.securesms.util.SignalLocalMetrics import org.thoughtcrime.securesms.util.asChain +import org.whispersystems.signalservice.api.SignalWebSocket import org.whispersystems.signalservice.api.push.ServiceId import org.whispersystems.signalservice.api.util.SleepTimer import org.whispersystems.signalservice.api.util.UptimeSleepTimer @@ -54,7 +55,7 @@ import kotlin.time.Duration.Companion.seconds * * This class is responsible for opening/closing the websocket based on the app's state and observing new inbound messages received on the websocket. */ -class IncomingMessageObserver(private val context: Application) { +class IncomingMessageObserver(private val context: Application, private val signalWebSocket: SignalWebSocket) { companion object { private val TAG = Log.tag(IncomingMessageObserver::class.java) @@ -238,7 +239,7 @@ class IncomingMessageObserver(private val context: Application) { } private fun disconnect() { - AppDependencies.signalWebSocket.disconnect() + signalWebSocket.disconnect() } @JvmOverloads @@ -378,8 +379,7 @@ class IncomingMessageObserver(private val context: Application) { waitForConnectionNecessary() Log.i(TAG, "Making websocket connection....") - val signalWebSocket = AppDependencies.signalWebSocket - val webSocketDisposable = AppDependencies.webSocketObserver.subscribe { state: WebSocketConnectionState -> + val webSocketDisposable = signalWebSocket.webSocketState.subscribe { state: WebSocketConnectionState -> Log.d(TAG, "WebSocket State: $state") // Any change to a non-connected state means that we are not drained @@ -394,7 +394,7 @@ class IncomingMessageObserver(private val context: Application) { signalWebSocket.connect() try { - while (isConnectionNecessary()) { + while (!terminated && isConnectionNecessary()) { try { Log.d(TAG, "Reading message...") diff --git a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt index 4ed3a383b1..5e51598d97 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt @@ -101,7 +101,7 @@ class MockApplicationDependencyProvider : AppDependencies.Provider { return mockk(relaxed = true) } - override fun provideIncomingMessageObserver(): IncomingMessageObserver { + override fun provideIncomingMessageObserver(signalWebSocket: SignalWebSocket): IncomingMessageObserver { return mockk(relaxed = true) } From 39b44848870e3687eb500463fd678e6b614c4c49 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 26 Nov 2024 16:49:04 -0500 Subject: [PATCH 27/56] Use calling service type for calls. --- app/src/main/AndroidManifest.xml | 8 +- .../service/webrtc/ActiveCallManager.kt | 16 +- .../service/webrtc/AndroidCallConnection.kt | 8 +- .../service/webrtc/WebRtcCallService.java | 471 ------------------ .../service/webrtc/WebRtcInteractor.java | 30 +- .../securesms/util/RemoteConfig.kt | 9 - .../webrtc/CallNotificationBuilder.java | 8 +- 7 files changed, 39 insertions(+), 511 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcCallService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b33459568e..2189a1a380 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1144,12 +1144,6 @@ android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:exported="false"/> - - + android:foregroundServiceType="dataSync|microphone|camera" /> diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/ActiveCallManager.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/ActiveCallManager.kt index 5e2c243e7f..4753906de6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/ActiveCallManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/ActiveCallManager.kt @@ -5,6 +5,7 @@ package org.thoughtcrime.securesms.service.webrtc +import android.Manifest import android.app.Notification import android.app.PendingIntent import android.content.BroadcastReceiver @@ -30,6 +31,7 @@ import org.signal.core.util.ThreadUtil import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobs.UnableToStartException +import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.service.SafeForegroundService @@ -295,7 +297,19 @@ class ActiveCallManager( @get:RequiresApi(30) override val serviceType: Int - get() = ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC or ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE + get() { + var type = ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC + + if (Permissions.hasAll(this, Manifest.permission.RECORD_AUDIO)) { + type = type or ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE + } + + if (Permissions.hasAll(this, Manifest.permission.CAMERA)) { + type = type or ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA + } + + return type + } private var hangUpRtcOnDeviceCallAnswered: PhoneStateListener? = null private var notificationDisposable: Disposable = Disposable.disposed() diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/AndroidCallConnection.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/AndroidCallConnection.kt index c47194add7..09567e6551 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/AndroidCallConnection.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/AndroidCallConnection.kt @@ -38,7 +38,7 @@ class AndroidCallConnection( override fun onShowIncomingCallUi() { Log.i(TAG, "onShowIncomingCallUi()") - WebRtcCallService.update(context, CallNotificationBuilder.TYPE_INCOMING_CONNECTING, recipientId, isVideoCall) + ActiveCallManager.update(context, CallNotificationBuilder.TYPE_INCOMING_CONNECTING, recipientId, isVideoCall) setRinging() } @@ -75,17 +75,17 @@ class AndroidCallConnection( } override fun onSilence() { - WebRtcCallService.sendAudioManagerCommand(context, AudioManagerCommand.SilenceIncomingRinger()) + ActiveCallManager.sendAudioManagerCommand(context, AudioManagerCommand.SilenceIncomingRinger()) } override fun onReject() { Log.i(TAG, "onReject()") - WebRtcCallService.denyCall(context) + ActiveCallManager.denyCall() } override fun onDisconnect() { Log.i(TAG, "onDisconnect()") - WebRtcCallService.hangup(context) + ActiveCallManager.hangup() } companion object { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcCallService.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcCallService.java deleted file mode 100644 index bad1aca0f1..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcCallService.java +++ /dev/null @@ -1,471 +0,0 @@ -package org.thoughtcrime.securesms.service.webrtc; - -import android.app.Notification; -import android.app.PendingIntent; -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ServiceInfo; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Build; -import android.os.IBinder; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; - -import androidx.annotation.MainThread; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.signal.core.util.PendingIntentFlags; -import org.signal.core.util.ThreadUtil; -import org.signal.core.util.concurrent.SignalExecutors; -import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.dependencies.AppDependencies; -import org.thoughtcrime.securesms.jobs.ForegroundServiceUtil; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientId; -import org.thoughtcrime.securesms.util.RemoteConfig; -import org.thoughtcrime.securesms.util.TelephonyUtil; -import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder; -import org.thoughtcrime.securesms.webrtc.UncaughtExceptionHandlerManager; -import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCommand; -import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager; -import org.thoughtcrime.securesms.webrtc.locks.LockManager; - -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; - -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.schedulers.Schedulers; - -/** - * Provide a foreground service for {@link SignalCallManager} to leverage to run in the background when necessary. Also - * provides devices listeners needed for during a call (i.e., bluetooth, power button). - */ -public final class WebRtcCallService extends Service implements SignalAudioManager.EventListener { - - private static final String TAG = Log.tag(WebRtcCallService.class); - private static final String WEBSOCKET_KEEP_ALIVE_TOKEN = WebRtcCallService.class.getName(); - - private static final String ACTION_UPDATE = "UPDATE"; - private static final String ACTION_STOP = "STOP"; - private static final String ACTION_DENY_CALL = "DENY_CALL"; - private static final String ACTION_LOCAL_HANGUP = "LOCAL_HANGUP"; - private static final String ACTION_CHANGE_POWER_BUTTON = "CHANGE_POWER_BUTTON"; - private static final String ACTION_SEND_AUDIO_COMMAND = "SEND_AUDIO_COMMAND"; - - private static final String EXTRA_UPDATE_TYPE = "UPDATE_TYPE"; - private static final String EXTRA_RECIPIENT_ID = "RECIPIENT_ID"; - private static final String EXTRA_ENABLED = "ENABLED"; - private static final String EXTRA_AUDIO_COMMAND = "AUDIO_COMMAND"; - private static final String EXTRA_IS_VIDEO_CALL = "IS_VIDEO_CALL"; - - private static final int INVALID_NOTIFICATION_ID = -1; - private static final long REQUEST_WEBSOCKET_STAY_OPEN_DELAY = TimeUnit.MINUTES.toMillis(1); - private static final long FOREGROUND_SERVICE_TIMEOUT = TimeUnit.SECONDS.toMillis(10); - - private final WebSocketKeepAliveTask webSocketKeepAliveTask = new WebSocketKeepAliveTask(); - private final Executor singleThreadExecutor = SignalExecutors.newCachedSingleThreadExecutor("signal-webrtc-in-call", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD); - - private SignalCallManager callManager; - - private NetworkReceiver networkReceiver; - private PowerButtonReceiver powerButtonReceiver; - private UncaughtExceptionHandlerManager uncaughtExceptionHandlerManager; - private PhoneStateListener hangUpRtcOnDeviceCallAnswered; - private SignalAudioManager signalAudioManager; - private int lastNotificationId; - private Notification lastNotification; - private long lastNotificationRequestTime; - private Disposable lastNotificationDisposable = Disposable.disposed(); - private boolean stopping = false; - - private static boolean useActiveCallManager() { - return Build.VERSION.SDK_INT >= 34 || RemoteConfig.useActiveCallManager(); - } - - public synchronized static void update(@NonNull Context context, int type, @NonNull RecipientId recipientId, boolean isVideoCall) { - if (useActiveCallManager()) { - ActiveCallManager.update(context, type, recipientId, isVideoCall); - - return; - } - - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(ACTION_UPDATE) - .putExtra(EXTRA_UPDATE_TYPE, type) - .putExtra(EXTRA_RECIPIENT_ID, recipientId) - .putExtra(EXTRA_IS_VIDEO_CALL, isVideoCall); - - ForegroundServiceUtil.tryToStartWhenCapable(context, intent, FOREGROUND_SERVICE_TIMEOUT); - } - - public static void denyCall(@NonNull Context context) { - if (useActiveCallManager()) { - ActiveCallManager.denyCall(); - return; - } - - ForegroundServiceUtil.tryToStartWhenCapable(context, new Intent(context, WebRtcCallService.class).setAction(ACTION_DENY_CALL), FOREGROUND_SERVICE_TIMEOUT); - } - - public static void hangup(@NonNull Context context) { - if (useActiveCallManager()) { - ActiveCallManager.hangup(); - return; - } - - ForegroundServiceUtil.tryToStartWhenCapable(context, new Intent(context, WebRtcCallService.class).setAction(ACTION_LOCAL_HANGUP), FOREGROUND_SERVICE_TIMEOUT); - } - - public synchronized static void stop(@NonNull Context context) { - if (useActiveCallManager()) { - ActiveCallManager.stop(); - return; - } - - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(ACTION_STOP); - - ForegroundServiceUtil.tryToStartWhenCapable(context, intent, FOREGROUND_SERVICE_TIMEOUT); - } - - public synchronized static @NonNull PendingIntent denyCallIntent(@NonNull Context context) { - if (useActiveCallManager()) { - return ActiveCallManager.denyCallIntent(context); - } - - return getServicePendingIntent(context, new Intent(context, WebRtcCallService.class).setAction(ACTION_DENY_CALL)); - } - - public synchronized static @NonNull PendingIntent hangupIntent(@NonNull Context context) { - if (useActiveCallManager()) { - return ActiveCallManager.hangupIntent(context); - } - - return getServicePendingIntent(context, new Intent(context, WebRtcCallService.class).setAction(ACTION_LOCAL_HANGUP)); - } - - public synchronized static void sendAudioManagerCommand(@NonNull Context context, @NonNull AudioManagerCommand command) { - if (useActiveCallManager()) { - ActiveCallManager.sendAudioManagerCommand(context, command); - return; - } - - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(ACTION_SEND_AUDIO_COMMAND) - .putExtra(EXTRA_AUDIO_COMMAND, command); - ForegroundServiceUtil.tryToStartWhenCapable(context, intent, FOREGROUND_SERVICE_TIMEOUT); - } - - public synchronized static void changePowerButtonReceiver(@NonNull Context context, boolean register) { - if (useActiveCallManager()) { - ActiveCallManager.changePowerButtonReceiver(context, register); - return; - } - - Intent intent = new Intent(context, WebRtcCallService.class); - intent.setAction(ACTION_CHANGE_POWER_BUTTON) - .putExtra(EXTRA_ENABLED, register); - - ForegroundServiceUtil.tryToStartWhenCapable(context, intent, FOREGROUND_SERVICE_TIMEOUT); - } - - @Override - public void onCreate() { - Log.v(TAG, "onCreate"); - super.onCreate(); - this.callManager = AppDependencies.getSignalCallManager(); - this.hangUpRtcOnDeviceCallAnswered = new HangUpRtcOnPstnCallAnsweredListener(); - this.lastNotificationId = INVALID_NOTIFICATION_ID; - - registerUncaughtExceptionHandler(); - registerNetworkReceiver(); - - if (!AndroidTelecomUtil.getTelecomSupported()) { - try { - TelephonyUtil.getManager(this).listen(hangUpRtcOnDeviceCallAnswered, PhoneStateListener.LISTEN_CALL_STATE); - } catch (SecurityException e) { - Log.w(TAG, "Failed to listen to PSTN call answers!", e); - } - } - } - - @Override - public void onDestroy() { - Log.v(TAG, "onDestroy"); - super.onDestroy(); - - if (uncaughtExceptionHandlerManager != null) { - uncaughtExceptionHandlerManager.unregister(); - } - - if (signalAudioManager != null) { - signalAudioManager.shutdown(); - } - - unregisterNetworkReceiver(); - unregisterPowerButtonReceiver(); - - if (!AndroidTelecomUtil.getTelecomSupported()) { - TelephonyUtil.getManager(this).listen(hangUpRtcOnDeviceCallAnswered, PhoneStateListener.LISTEN_NONE); - } - - webSocketKeepAliveTask.stop(); - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - if (intent == null || intent.getAction() == null) { - Log.w(TAG, "Service running with null intent/action likely from system restart, stopping"); - stop(); - return START_NOT_STICKY; - } - - Log.i(TAG, "action: " + intent.getAction()); - webSocketKeepAliveTask.start(); - - switch (intent.getAction()) { - case ACTION_UPDATE: - setCallInProgressNotification(intent.getIntExtra(EXTRA_UPDATE_TYPE, 0), - Objects.requireNonNull(intent.getParcelableExtra(EXTRA_RECIPIENT_ID)), - intent.getBooleanExtra(EXTRA_IS_VIDEO_CALL, false)); - return START_STICKY; - case ACTION_SEND_AUDIO_COMMAND: - setCallNotification(); - if (signalAudioManager == null) { - signalAudioManager = SignalAudioManager.create(this, this); - } - AudioManagerCommand audioCommand = Objects.requireNonNull(intent.getParcelableExtra(EXTRA_AUDIO_COMMAND)); - Log.i(TAG, "Sending audio command [" + audioCommand.getClass().getSimpleName() + "] to " + signalAudioManager.getClass().getSimpleName()); - signalAudioManager.handleCommand(audioCommand); - return START_STICKY; - case ACTION_CHANGE_POWER_BUTTON: - setCallNotification(); - if (intent.getBooleanExtra(EXTRA_ENABLED, false)) { - registerPowerButtonReceiver(); - } else { - unregisterPowerButtonReceiver(); - } - return START_STICKY; - case ACTION_STOP: - setCallNotification(true); - stop(); - return START_NOT_STICKY; - case ACTION_DENY_CALL: - setCallNotification(); - callManager.denyCall(); - return START_NOT_STICKY; - case ACTION_LOCAL_HANGUP: - setCallNotification(); - callManager.localHangup(); - return START_NOT_STICKY; - default: - throw new AssertionError("Unknown action: " + intent.getAction()); - } - } - - private void setCallNotification() { - setCallNotification(false); - } - - private void setCallNotification(boolean stopping) { - if (!stopping && lastNotificationId != INVALID_NOTIFICATION_ID) { - startForegroundCompat(lastNotificationId, lastNotification); - } else { - if (!stopping) { - Log.i(TAG, "Service was started without calling UPDATE first, using temporary notification."); - } - startForegroundCompat(CallNotificationBuilder.getStartingStoppingNotificationId(), stopping ? CallNotificationBuilder.getStoppingNotification(this) - : CallNotificationBuilder.getStartingNotification(this)); - } - } - - public void setCallInProgressNotification(int type, @NonNull RecipientId id, boolean isVideoCall) { - lastNotificationDisposable.dispose(); - - boolean requiresAsyncNotificationLoad = Build.VERSION.SDK_INT <= 29; - - lastNotificationId = CallNotificationBuilder.getNotificationId(type); - lastNotification = CallNotificationBuilder.getCallInProgressNotification(this, type, Recipient.resolved(id), isVideoCall, requiresAsyncNotificationLoad); - - startForegroundCompat(lastNotificationId, lastNotification); - - if (requiresAsyncNotificationLoad) { - final long requestTime = System.currentTimeMillis(); - lastNotificationRequestTime = requestTime; - lastNotificationDisposable = Single - .fromCallable(() -> CallNotificationBuilder.getCallInProgressNotification(this, type, Recipient.resolved(id), isVideoCall, false)) - .subscribeOn(Schedulers.from(singleThreadExecutor)) - .observeOn(AndroidSchedulers.mainThread()) - .filter(unused -> requestTime == lastNotificationRequestTime && !stopping) - .subscribe(notification -> { - lastNotification = notification; - startForegroundCompat(lastNotificationId, lastNotification); - }); - } - } - - private synchronized void startForegroundCompat(int notificationId, Notification notification) { - if (stopping) { - return; - } - - if (Build.VERSION.SDK_INT >= 30) { - startForeground(notificationId, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC); - } else { - startForeground(notificationId, notification); - } - } - - private synchronized void stop() { - stopping = true; - stopForeground(true); - stopSelf(); - } - - private void registerUncaughtExceptionHandler() { - uncaughtExceptionHandlerManager = new UncaughtExceptionHandlerManager(); - uncaughtExceptionHandlerManager.registerHandler(new ProximityLockRelease(callManager.getLockManager())); - } - - private void registerNetworkReceiver() { - if (networkReceiver == null) { - networkReceiver = new NetworkReceiver(); - - registerReceiver(networkReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); - } - } - - private void unregisterNetworkReceiver() { - if (networkReceiver != null) { - unregisterReceiver(networkReceiver); - - networkReceiver = null; - } - } - - public void registerPowerButtonReceiver() { - if (!AndroidTelecomUtil.getTelecomSupported() && powerButtonReceiver == null) { - powerButtonReceiver = new PowerButtonReceiver(); - - registerReceiver(powerButtonReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); - } - } - - public void unregisterPowerButtonReceiver() { - if (powerButtonReceiver != null) { - unregisterReceiver(powerButtonReceiver); - - powerButtonReceiver = null; - } - } - - @Override - public @Nullable IBinder onBind(Intent intent) { - return null; - } - - @Override - public void onAudioDeviceChanged(@NonNull SignalAudioManager.AudioDevice activeDevice, @NonNull Set availableDevices) { - callManager.onAudioDeviceChanged(activeDevice, availableDevices); - } - - @Override - public void onBluetoothPermissionDenied() { - callManager.onBluetoothPermissionDenied(); - } - - public static PendingIntent getServicePendingIntent(@NonNull Context context, @NonNull Intent intent) { - return Build.VERSION.SDK_INT >= 26 ? PendingIntent.getForegroundService(context, 0, intent, PendingIntentFlags.mutable()) - : PendingIntent.getService(context, 0, intent, PendingIntentFlags.mutable()); - } - - @SuppressWarnings("deprecation") - private class HangUpRtcOnPstnCallAnsweredListener extends PhoneStateListener { - @Override - public void onCallStateChanged(int state, @NonNull String phoneNumber) { - super.onCallStateChanged(state, phoneNumber); - if (state == TelephonyManager.CALL_STATE_OFFHOOK) { - hangup(); - Log.i(TAG, "Device phone call ended Signal call."); - } - } - - private void hangup() { - callManager.localHangup(); - } - } - - /** - * Periodically request the web socket stay open if we are doing anything call related. - */ - private class WebSocketKeepAliveTask implements Runnable { - private boolean keepRunning = false; - - @MainThread - public void start() { - if (!keepRunning) { - keepRunning = true; - run(); - } - } - - @MainThread - public void stop() { - keepRunning = false; - ThreadUtil.cancelRunnableOnMain(webSocketKeepAliveTask); - AppDependencies.getIncomingMessageObserver().removeKeepAliveToken(WEBSOCKET_KEEP_ALIVE_TOKEN); - } - - @MainThread - @Override - public void run() { - if (keepRunning) { - AppDependencies.getIncomingMessageObserver().registerKeepAliveToken(WEBSOCKET_KEEP_ALIVE_TOKEN); - ThreadUtil.runOnMainDelayed(this, REQUEST_WEBSOCKET_STAY_OPEN_DELAY); - } - } - } - - private static class NetworkReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); - - AppDependencies.getSignalCallManager().networkChange(activeNetworkInfo != null && activeNetworkInfo.isConnected()); - AppDependencies.getSignalCallManager().dataModeUpdate(); - } - } - - private static class PowerButtonReceiver extends BroadcastReceiver { - @Override - public void onReceive(@NonNull Context context, @NonNull Intent intent) { - if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { - AppDependencies.getSignalCallManager().screenOff(); - } - } - } - - private static class ProximityLockRelease implements Thread.UncaughtExceptionHandler { - private final LockManager lockManager; - - private ProximityLockRelease(@NonNull LockManager lockManager) { - this.lockManager = lockManager; - } - - @Override - public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) { - Log.i(TAG, "Uncaught exception - releasing proximity lock", throwable); - lockManager.updatePhoneState(LockManager.PhoneState.IDLE); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java index 2e5d5d248b..219c3431cd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java @@ -95,11 +95,11 @@ void updateGroupCallUpdateMessage(@NonNull RecipientId groupId, @Nullable String } void setCallInProgressNotification(int type, @NonNull RemotePeer remotePeer, boolean isVideoCall) { - WebRtcCallService.update(context, type, remotePeer.getRecipient().getId(), isVideoCall); + ActiveCallManager.update(context, type, remotePeer.getRecipient().getId(), isVideoCall); } void setCallInProgressNotification(int type, @NonNull Recipient recipient, boolean isVideoCall) { - WebRtcCallService.update(context, type, recipient.getId(), isVideoCall); + ActiveCallManager.update(context, type, recipient.getId(), isVideoCall); } void retrieveTurnServers(@NonNull RemotePeer remotePeer) { @@ -107,7 +107,7 @@ void retrieveTurnServers(@NonNull RemotePeer remotePeer) { } void stopForegroundService() { - WebRtcCallService.stop(context); + ActiveCallManager.stop(); } void insertMissedCall(@NonNull RemotePeer remotePeer, long timestamp, boolean isVideoOffer) { @@ -127,51 +127,51 @@ boolean startWebRtcCallActivityIfPossible() { } void registerPowerButtonReceiver() { - WebRtcCallService.changePowerButtonReceiver(context, true); + ActiveCallManager.changePowerButtonReceiver(context, true); } void unregisterPowerButtonReceiver() { - WebRtcCallService.changePowerButtonReceiver(context, false); + ActiveCallManager.changePowerButtonReceiver(context, false); } void silenceIncomingRinger() { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.SilenceIncomingRinger()); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.SilenceIncomingRinger()); } void initializeAudioForCall() { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.Initialize()); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.Initialize()); } void startIncomingRinger(@Nullable Uri ringtoneUri, boolean vibrate) { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.StartIncomingRinger(ringtoneUri, vibrate)); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.StartIncomingRinger(ringtoneUri, vibrate)); } void startOutgoingRinger() { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.StartOutgoingRinger()); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.StartOutgoingRinger()); } void stopAudio(boolean playDisconnect) { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.Stop(playDisconnect)); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.Stop(playDisconnect)); } void startAudioCommunication() { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.Start()); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.Start()); } public void setUserAudioDevice(@Nullable RecipientId recipientId, @NonNull SignalAudioManager.ChosenAudioDeviceIdentifier userDevice) { if (userDevice.isLegacy()) { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.SetUserDevice(recipientId, userDevice.getDesiredAudioDeviceLegacy().ordinal(), false)); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.SetUserDevice(recipientId, userDevice.getDesiredAudioDeviceLegacy().ordinal(), false)); } else { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.SetUserDevice(recipientId, userDevice.getDesiredAudioDevice31(), true)); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.SetUserDevice(recipientId, userDevice.getDesiredAudioDevice31(), true)); } } public void setDefaultAudioDevice(@NonNull RecipientId recipientId, @NonNull SignalAudioManager.AudioDevice userDevice, boolean clearUserEarpieceSelection) { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.SetDefaultDevice(recipientId, userDevice, clearUserEarpieceSelection)); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.SetDefaultDevice(recipientId, userDevice, clearUserEarpieceSelection)); } public void playStateChangeUp() { - WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.PlayStateChangeUp()); + ActiveCallManager.sendAudioManagerCommand(context, new AudioManagerCommand.PlayStateChangeUp()); } void peekGroupCallForRingingCheck(@NonNull GroupCallRingCheckInfo groupCallRingCheckInfo) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt b/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt index eca4900c2c..bd45472358 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt @@ -958,15 +958,6 @@ object RemoteConfig { hotSwappable = true ) - /** Whether or not to use active call manager instead of WebRtcCallService. */ - @JvmStatic - @get:JvmName("useActiveCallManager") - val useActiveCallManager: Boolean by remoteBoolean( - key = "android.calling.useActiveCallManager.6", - defaultValue = false, - hotSwappable = false - ) - /** Whether the in-app GIF search is available for use. */ @JvmStatic @get:JvmName("gifSearchAvailable") diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java index efa2bade61..a28d58bb7d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallNotificationBuilder.java @@ -18,7 +18,7 @@ import org.thoughtcrime.securesms.components.webrtc.v2.CallIntent; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.service.webrtc.WebRtcCallService; +import org.thoughtcrime.securesms.service.webrtc.ActiveCallManager; import org.thoughtcrime.securesms.util.ConversationUtil; /** @@ -113,7 +113,7 @@ public static Notification getCallInProgressNotification( if (deviceVersionSupportsIncomingCallStyle()) { builder.setStyle(NotificationCompat.CallStyle.forIncomingCall( person, - WebRtcCallService.denyCallIntent(context), + ActiveCallManager.denyCallIntent(context), getActivityPendingIntent(context, isVideoCall ? LaunchCallScreenIntentState.VIDEO : LaunchCallScreenIntentState.AUDIO) ).setIsVideo(isVideoCall)); } @@ -121,7 +121,7 @@ public static Notification getCallInProgressNotification( return builder.build(); } else if (type == TYPE_OUTGOING_RINGING) { builder.setContentText(context.getString(R.string.NotificationBarManager__establishing_signal_call)); - builder.addAction(getServiceNotificationAction(context, WebRtcCallService.hangupIntent(context), R.drawable.symbol_phone_down_fill_24, R.string.NotificationBarManager__cancel_call)); + builder.addAction(getServiceNotificationAction(context, ActiveCallManager.hangupIntent(context), R.drawable.symbol_phone_down_fill_24, R.string.NotificationBarManager__cancel_call)); return builder.build(); } else { builder.setContentText(getOngoingCallContentText(context, recipient, isVideoCall)); @@ -137,7 +137,7 @@ public static Notification getCallInProgressNotification( if (deviceVersionSupportsIncomingCallStyle()) { builder.setStyle(NotificationCompat.CallStyle.forOngoingCall( person, - WebRtcCallService.hangupIntent(context) + ActiveCallManager.hangupIntent(context) ).setIsVideo(isVideoCall)); } From d28fa304c8c6122f12781d1e3184fda198316e70 Mon Sep 17 00:00:00 2001 From: Jameson Williams Date: Fri, 22 Nov 2024 15:04:56 -0600 Subject: [PATCH 28/56] Kotlin-ize some more tests. Resolves #13813 --- .../recipients/RecipientExporterTest.java | 93 ------- .../recipients/RecipientExporterTest.kt | 91 +++++++ .../recipients/RecipientUtilTest.java | 234 ------------------ .../securesms/recipients/RecipientUtilTest.kt | 229 +++++++++++++++++ .../securesms/util/DelimiterUtilTest.java | 76 ------ .../securesms/util/DelimiterUtilTest.kt | 66 +++++ .../LatestPrioritizedSerialExecutorTest.java | 94 ------- .../LatestPrioritizedSerialExecutorTest.kt | 90 +++++++ .../api/services/DonationsServiceTest.java | 60 ----- .../api/services/DonationsServiceTest.kt | 49 ++++ 10 files changed, 525 insertions(+), 557 deletions(-) delete mode 100644 app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientExporterTest.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientExporterTest.kt delete mode 100644 app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientUtilTest.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientUtilTest.kt delete mode 100644 app/src/test/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.kt delete mode 100644 core-util/src/test/java/org/signal/core/util/concurrent/LatestPrioritizedSerialExecutorTest.java create mode 100644 core-util/src/test/java/org/signal/core/util/concurrent/LatestPrioritizedSerialExecutorTest.kt delete mode 100644 libsignal-service/src/test/java/org/whispersystems/signalservice/api/services/DonationsServiceTest.java create mode 100644 libsignal-service/src/test/java/org/whispersystems/signalservice/api/services/DonationsServiceTest.kt diff --git a/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientExporterTest.java b/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientExporterTest.java deleted file mode 100644 index 42c08da83a..0000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientExporterTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.thoughtcrime.securesms.recipients; - -import android.app.Application; -import android.content.Intent; -import android.provider.ContactsContract; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; -import org.thoughtcrime.securesms.profiles.ProfileName; - -import java.util.Optional; - -import static android.provider.ContactsContract.Intents.Insert.EMAIL; -import static android.provider.ContactsContract.Intents.Insert.NAME; -import static android.provider.ContactsContract.Intents.Insert.PHONE; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@RunWith(RobolectricTestRunner.class) -@Config(manifest = Config.NONE, application = Application.class) -public final class RecipientExporterTest { - - @Test - public void asAddContactIntent_with_phone_number() { - Recipient recipient = givenPhoneRecipient(ProfileName.fromParts("Alice", null), "+1555123456"); - - Intent intent = RecipientExporter.export(recipient).asAddContactIntent(); - - assertEquals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction()); - assertEquals(ContactsContract.Contacts.CONTENT_ITEM_TYPE, intent.getType()); - assertEquals("Alice", intent.getStringExtra(NAME)); - assertEquals("+1555123456", intent.getStringExtra(PHONE)); - assertNull(intent.getStringExtra(EMAIL)); - } - - @Test - public void asAddContactIntent_with_phone_number_should_not_show_number() { - Recipient recipient = givenPhoneRecipient(ProfileName.fromParts("Alice", null), "+1555123456", false); - - Intent intent = RecipientExporter.export(recipient).asAddContactIntent(); - - assertEquals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction()); - assertEquals(ContactsContract.Contacts.CONTENT_ITEM_TYPE, intent.getType()); - assertEquals("Alice", intent.getStringExtra(NAME)); - assertNull(intent.getStringExtra(PHONE)); - assertNull(intent.getStringExtra(EMAIL)); - } - - @Test - public void asAddContactIntent_with_email() { - Recipient recipient = givenEmailRecipient(ProfileName.fromParts("Bob", null), "bob@signal.org"); - - Intent intent = RecipientExporter.export(recipient).asAddContactIntent(); - - assertEquals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction()); - assertEquals(ContactsContract.Contacts.CONTENT_ITEM_TYPE, intent.getType()); - assertEquals("Bob", intent.getStringExtra(NAME)); - assertEquals("bob@signal.org", intent.getStringExtra(EMAIL)); - assertNull(intent.getStringExtra(PHONE)); - } - - - private Recipient givenPhoneRecipient(ProfileName profileName, String phone) { - return givenPhoneRecipient(profileName, phone, true); - } - - private Recipient givenPhoneRecipient(ProfileName profileName, String phone, boolean shouldShowPhoneNumber) { - Recipient recipient = mock(Recipient.class); - when(recipient.getProfileName()).thenReturn(profileName); - - when(recipient.requireE164()).thenReturn(phone); - when(recipient.getE164()).thenAnswer(i -> Optional.of(phone)); - when(recipient.getEmail()).thenAnswer(i -> Optional.empty()); - when(recipient.getShouldShowE164()).thenAnswer(i -> shouldShowPhoneNumber); - - return recipient; - } - - private Recipient givenEmailRecipient(ProfileName profileName, String email) { - Recipient recipient = mock(Recipient.class); - when(recipient.getProfileName()).thenReturn(profileName); - - when(recipient.requireEmail()).thenReturn(email); - when(recipient.getEmail()).thenAnswer(i -> Optional.of(email)); - when(recipient.getE164()).thenAnswer(i -> Optional.empty()); - - return recipient; - } -} diff --git a/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientExporterTest.kt b/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientExporterTest.kt new file mode 100644 index 0000000000..d5e449b94a --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientExporterTest.kt @@ -0,0 +1,91 @@ +package org.thoughtcrime.securesms.recipients + +import android.app.Application +import android.content.Intent +import android.provider.ContactsContract +import io.mockk.every +import io.mockk.mockk +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.thoughtcrime.securesms.profiles.ProfileName +import java.util.Optional + +@RunWith(RobolectricTestRunner::class) +@Config(manifest = Config.NONE, application = Application::class) +class RecipientExporterTest { + @Test + fun asAddContactIntent_with_phone_number() { + val recipient = givenPhoneRecipient( + profileName = ProfileName.fromParts("Alice", null), + phone = "+1555123456" + ) + + val intent = RecipientExporter.export(recipient).asAddContactIntent() + + assertEquals(Intent.ACTION_INSERT_OR_EDIT, intent.action) + assertEquals(ContactsContract.Contacts.CONTENT_ITEM_TYPE, intent.type) + assertEquals("Alice", intent.getStringExtra(ContactsContract.Intents.Insert.NAME)) + assertEquals("+1555123456", intent.getStringExtra(ContactsContract.Intents.Insert.PHONE)) + assertNull(intent.getStringExtra(ContactsContract.Intents.Insert.EMAIL)) + } + + @Test + fun asAddContactIntent_with_phone_number_should_not_show_number() { + val recipient = givenPhoneRecipient( + profileName = ProfileName.fromParts("Alice", null), + phone = "+1555123456", + shouldShowPhoneNumber = false + ) + + val intent = RecipientExporter.export(recipient).asAddContactIntent() + + assertEquals(Intent.ACTION_INSERT_OR_EDIT, intent.action) + assertEquals(ContactsContract.Contacts.CONTENT_ITEM_TYPE, intent.type) + assertEquals("Alice", intent.getStringExtra(ContactsContract.Intents.Insert.NAME)) + assertNull(intent.getStringExtra(ContactsContract.Intents.Insert.PHONE)) + assertNull(intent.getStringExtra(ContactsContract.Intents.Insert.EMAIL)) + } + + @Test + fun asAddContactIntent_with_email() { + val recipient = givenEmailRecipient( + profileName = ProfileName.fromParts("Bob", null), + email = "bob@signal.org" + ) + + val intent = RecipientExporter.export(recipient).asAddContactIntent() + + assertEquals(Intent.ACTION_INSERT_OR_EDIT, intent.action) + assertEquals(ContactsContract.Contacts.CONTENT_ITEM_TYPE, intent.type) + assertEquals("Bob", intent.getStringExtra(ContactsContract.Intents.Insert.NAME)) + assertEquals("bob@signal.org", intent.getStringExtra(ContactsContract.Intents.Insert.EMAIL)) + assertNull(intent.getStringExtra(ContactsContract.Intents.Insert.PHONE)) + } + + private fun givenPhoneRecipient(profileName: ProfileName, phone: String, shouldShowPhoneNumber: Boolean = true): Recipient { + val recipient = mockk() + every { recipient.profileName } returns profileName + + every { recipient.requireE164() } returns phone + every { recipient.e164 } returns Optional.of(phone) + every { recipient.email } returns Optional.empty() + every { recipient.shouldShowE164 } returns shouldShowPhoneNumber + + return recipient + } + + private fun givenEmailRecipient(profileName: ProfileName, email: String): Recipient { + val recipient = mockk() + every { recipient.profileName } returns profileName + + every { recipient.requireEmail() } returns email + every { recipient.email } returns Optional.of(email) + every { recipient.e164 } returns Optional.empty() + + return recipient + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientUtilTest.java b/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientUtilTest.java deleted file mode 100644 index 2a603bc0aa..0000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientUtilTest.java +++ /dev/null @@ -1,234 +0,0 @@ -package org.thoughtcrime.securesms.recipients; - -import android.content.Context; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.thoughtcrime.securesms.database.MessageTable; -import org.thoughtcrime.securesms.database.RecipientTable; -import org.thoughtcrime.securesms.database.SignalDatabase; -import org.thoughtcrime.securesms.database.ThreadTable; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class RecipientUtilTest { - - @Rule - public MockitoRule rule = MockitoJUnit.rule(); - - private Context context = mock(Context.class); - private Recipient recipient = mock(Recipient.class); - private ThreadTable mockThreadTable = mock(ThreadTable.class); - private MessageTable mockMessageTable = mock(MessageTable.class); - private RecipientTable mockRecipientTable = mock(RecipientTable.class); - - @Mock - private MockedStatic signalDatabaseMockedStatic; - - @Before - public void setUp() { - signalDatabaseMockedStatic.when(SignalDatabase::threads).thenReturn(mockThreadTable); - signalDatabaseMockedStatic.when(SignalDatabase::messages).thenReturn(mockMessageTable); - signalDatabaseMockedStatic.when(SignalDatabase::recipients).thenReturn(mockRecipientTable); - - when(recipient.getId()).thenReturn(RecipientId.from(5)); - when(recipient.resolve()).thenReturn(recipient); - } - - @Test - public void givenThreadIsNegativeOne_whenIsThreadMessageRequestAccepted_thenIExpectTrue() { - // WHEN - boolean result = RecipientUtil.isMessageRequestAccepted(context, -1L); - - // THEN - assertTrue(result); - } - - @Test - public void givenRecipientIsNullForThreadId_whenIsThreadMessageRequestAccepted_thenIExpectTrue() { - // WHEN - boolean result = RecipientUtil.isMessageRequestAccepted(context, 1L); - - // THEN - assertTrue(result); - } - - @Test - public void givenIHaveSentASecureMessageInThisThread_whenIsThreadMessageRequestAccepted_thenIExpectTrue() { - // GIVEN - when(mockThreadTable.getRecipientForThreadId(anyLong())).thenReturn(recipient); - when(mockMessageTable.getOutgoingSecureMessageCount(1L)).thenReturn(5); - - // WHEN - boolean result = RecipientUtil.isMessageRequestAccepted(context, 1L); - - // THEN - assertTrue(result); - } - - @Test - public void givenIHaveNotSentASecureMessageInThisThreadAndIAmProfileSharing_whenIsThreadMessageRequestAccepted_thenIExpectTrue() { - // GIVEN - when(recipient.isProfileSharing()).thenReturn(true); - when(mockThreadTable.getRecipientForThreadId(anyLong())).thenReturn(recipient); - when(mockMessageTable.getOutgoingSecureMessageCount(1L)).thenReturn(0); - - // WHEN - boolean result = RecipientUtil.isMessageRequestAccepted(context, 1L); - - // THEN - assertTrue(result); - } - - @Test - public void givenIHaveNotSentASecureMessageInThisThreadAndRecipientIsSystemContact_whenIsThreadMessageRequestAccepted_thenIExpectTrue() { - // GIVEN - when(recipient.isSystemContact()).thenReturn(true); - when(mockThreadTable.getRecipientForThreadId(anyLong())).thenReturn(recipient); - when(mockMessageTable.getOutgoingSecureMessageCount(1L)).thenReturn(0); - - // WHEN - boolean result = RecipientUtil.isMessageRequestAccepted(context, 1L); - - // THEN - assertTrue(result); - } - - @Ignore - @Test - public void givenIHaveReceivedASecureMessageIHaveNotSentASecureMessageAndRecipientIsNotSystemContactAndNotProfileSharing_whenIsThreadMessageRequestAccepted_thenIExpectFalse() { - // GIVEN - when(mockThreadTable.getRecipientForThreadId(anyLong())).thenReturn(recipient); - when(mockMessageTable.getOutgoingSecureMessageCount(1L)).thenReturn(0); - when(mockMessageTable.getSecureMessageCount(1L)).thenReturn(5); - - // WHEN - boolean result = RecipientUtil.isMessageRequestAccepted(context, 1L); - - // THEN - assertFalse(result); - } - - @Test - public void givenIHaveNotReceivedASecureMessageIHaveNotSentASecureMessageAndRecipientIsNotSystemContactAndNotProfileSharing_whenIsThreadMessageRequestAccepted_thenIExpectTrue() { - // GIVEN - when(mockThreadTable.getRecipientForThreadId(anyLong())).thenReturn(recipient); - when(mockMessageTable.getOutgoingSecureMessageCount(1L)).thenReturn(0); - when(mockMessageTable.getSecureMessageCount(1L)).thenReturn(0); - - // WHEN - boolean result = RecipientUtil.isMessageRequestAccepted(context, 1L); - - // THEN - assertTrue(result); - } - - @Test - public void givenRecipientIsNull_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() { - // WHEN - boolean result = RecipientUtil.isMessageRequestAccepted(context, null); - - // THEN - assertTrue(result); - } - - @Test - public void givenNonZeroOutgoingSecureMessageCount_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() { - // GIVEN - when(mockMessageTable.getOutgoingSecureMessageCount(anyLong())).thenReturn(1); - - // WHEN - boolean result = RecipientUtil.isMessageRequestAccepted(context, recipient); - - // THEN - assertTrue(result); - } - - @Test - public void givenIAmProfileSharing_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() { - // GIVEN - when(recipient.isProfileSharing()).thenReturn(true); - - // WHEN - boolean result = RecipientUtil.isMessageRequestAccepted(context, recipient); - - // THEN - assertTrue(result); - } - - @Test - public void givenRecipientIsASystemContact_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() { - // GIVEN - when(recipient.isSystemContact()).thenReturn(true); - - // WHEN - boolean result = RecipientUtil.isMessageRequestAccepted(context, recipient); - - // THEN - assertTrue(result); - } - - @Ignore - @Test - public void givenNoSecureMessagesSentSomeSecureMessagesReceivedNotSharingAndNotSystemContact_whenIsRecipientMessageRequestAccepted_thenIExpectFalse() { - // GIVEN - when(recipient.isRegistered()).thenReturn(true); - when(mockMessageTable.getSecureMessageCount(anyLong())).thenReturn(5); - - // WHEN - boolean result = RecipientUtil.isMessageRequestAccepted(context, recipient); - - // THEN - assertFalse(result); - } - - @Test - public void givenNoSecureMessagesSentNoSecureMessagesReceivedNotSharingAndNotSystemContact_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() { - // GIVEN - when(mockMessageTable.getSecureMessageCount(anyLong())).thenReturn(0); - - // WHEN - boolean result = RecipientUtil.isMessageRequestAccepted(context, recipient); - - // THEN - assertTrue(result); - } - - @Ignore - @Test - public void givenNoSecureMessagesSent_whenIShareProfileIfFirstSecureMessage_thenIShareProfile() { - // GIVEN - when(mockMessageTable.getOutgoingSecureMessageCount(anyLong())).thenReturn(0); - - // WHEN - RecipientUtil.shareProfileIfFirstSecureMessage(recipient); - - // THEN - verify(mockRecipientTable).setProfileSharing(recipient.getId(), true); - } - - @Ignore - @Test - public void givenSecureMessagesSent_whenIShareProfileIfFirstSecureMessage_thenIShareProfile() { - // GIVEN - when(mockMessageTable.getOutgoingSecureMessageCount(anyLong())).thenReturn(5); - - // WHEN - RecipientUtil.shareProfileIfFirstSecureMessage(recipient); - - // THEN - verify(mockRecipientTable, never()).setProfileSharing(recipient.getId(), true); - } -} \ No newline at end of file diff --git a/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientUtilTest.kt b/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientUtilTest.kt new file mode 100644 index 0000000000..7aba0ca8fb --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/recipients/RecipientUtilTest.kt @@ -0,0 +1,229 @@ +package org.thoughtcrime.securesms.recipients + +import android.content.Context +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.unmockkObject +import io.mockk.verify +import org.junit.After +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Ignore +import org.junit.Test +import org.thoughtcrime.securesms.database.MessageTable +import org.thoughtcrime.securesms.database.RecipientTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.ThreadTable + +class RecipientUtilTest { + private val context = mockk() + private val recipient = mockk(relaxed = true) + private val mockThreadTable = mockk(relaxed = true) + private val mockMessageTable = mockk() + private val mockRecipientTable = mockk() + + @Before + fun setUp() { + mockkObject(SignalDatabase.Companion) + every { SignalDatabase.Companion.instance } returns mockk { + every { threadTable } returns mockThreadTable + every { messageTable } returns mockMessageTable + every { recipientTable } returns mockRecipientTable + } + + every { recipient.id } returns RecipientId.from(5) + every { recipient.resolve() } returns recipient + } + + @After + fun cleanup() { + unmockkObject(SignalDatabase.Companion) + } + + @Test + fun givenThreadIsNegativeOne_whenIsThreadMessageRequestAccepted_thenIExpectTrue() { + // WHEN + val result = RecipientUtil.isMessageRequestAccepted(context, -1L) + + // THEN + assertTrue(result) + } + + @Test + fun givenRecipientIsNullForThreadId_whenIsThreadMessageRequestAccepted_thenIExpectTrue() { + // WHEN + val result = RecipientUtil.isMessageRequestAccepted(context, 1L) + + // THEN + assertTrue(result) + } + + @Test + fun givenIHaveSentASecureMessageInThisThread_whenIsThreadMessageRequestAccepted_thenIExpectTrue() { + // GIVEN + every { mockThreadTable.getRecipientForThreadId(any()) } returns recipient + every { mockMessageTable.getOutgoingSecureMessageCount(1L) } returns 5 + + // WHEN + val result = RecipientUtil.isMessageRequestAccepted(context, 1L) + + // THEN + assertTrue(result) + } + + @Test + fun givenIHaveNotSentASecureMessageInThisThreadAndIAmProfileSharing_whenIsThreadMessageRequestAccepted_thenIExpectTrue() { + // GIVEN + every { recipient.isProfileSharing } returns true + every { mockThreadTable.getRecipientForThreadId(any()) } returns recipient + every { mockMessageTable.getOutgoingSecureMessageCount(1L) } returns 0 + + // WHEN + val result = RecipientUtil.isMessageRequestAccepted(context, 1L) + + // THEN + assertTrue(result) + } + + @Test + fun givenIHaveNotSentASecureMessageInThisThreadAndRecipientIsSystemContact_whenIsThreadMessageRequestAccepted_thenIExpectTrue() { + // GIVEN + every { recipient.isSystemContact } returns true + every { mockThreadTable.getRecipientForThreadId(any()) } returns recipient + every { mockMessageTable.getOutgoingSecureMessageCount(1L) } returns 0 + + // WHEN + val result = RecipientUtil.isMessageRequestAccepted(context, 1L) + + // THEN + assertTrue(result) + } + + @Ignore + @Test + fun givenIHaveReceivedASecureMessageIHaveNotSentASecureMessageAndRecipientIsNotSystemContactAndNotProfileSharing_whenIsThreadMessageRequestAccepted_thenIExpectFalse() { + // GIVEN + every { mockThreadTable.getRecipientForThreadId(any()) } returns recipient + every { mockMessageTable.getOutgoingSecureMessageCount(1L) } returns 0 + every { mockMessageTable.getSecureMessageCount(1L) } returns 5 + + // WHEN + val result = RecipientUtil.isMessageRequestAccepted(context, 1L) + + // THEN + assertFalse(result) + } + + @Test + fun givenIHaveNotReceivedASecureMessageIHaveNotSentASecureMessageAndRecipientIsNotSystemContactAndNotProfileSharing_whenIsThreadMessageRequestAccepted_thenIExpectTrue() { + // GIVEN + every { mockThreadTable.getRecipientForThreadId(any()) } returns recipient + every { mockMessageTable.getOutgoingSecureMessageCount(1L) } returns 0 + every { mockMessageTable.getSecureMessageCount(1L) } returns 0 + + // WHEN + val result = RecipientUtil.isMessageRequestAccepted(context, 1L) + + // THEN + assertTrue(result) + } + + @Test + fun givenRecipientIsNull_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() { + // WHEN + val result = RecipientUtil.isMessageRequestAccepted(context, null) + + // THEN + assertTrue(result) + } + + @Test + fun givenNonZeroOutgoingSecureMessageCount_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() { + // GIVEN + every { mockMessageTable.getOutgoingSecureMessageCount(any()) } returns 1 + + // WHEN + val result = RecipientUtil.isMessageRequestAccepted(context, recipient) + + // THEN + assertTrue(result) + } + + @Test + fun givenIAmProfileSharing_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() { + // GIVEN + every { recipient.isProfileSharing } returns true + + // WHEN + val result = RecipientUtil.isMessageRequestAccepted(context, recipient) + + // THEN + assertTrue(result) + } + + @Test + fun givenRecipientIsASystemContact_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() { + // GIVEN + every { recipient.isSystemContact } returns true + + // WHEN + val result = RecipientUtil.isMessageRequestAccepted(context, recipient) + + // THEN + assertTrue(result) + } + + @Ignore + @Test + fun givenNoSecureMessagesSentSomeSecureMessagesReceivedNotSharingAndNotSystemContact_whenIsRecipientMessageRequestAccepted_thenIExpectFalse() { + // GIVEN + every { recipient.isRegistered } returns true + every { mockMessageTable.getSecureMessageCount(any()) } returns 5 + + // WHEN + val result = RecipientUtil.isMessageRequestAccepted(context, recipient) + + // THEN + assertFalse(result) + } + + @Test + fun givenNoSecureMessagesSentNoSecureMessagesReceivedNotSharingAndNotSystemContact_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() { + // GIVEN + every { mockMessageTable.getSecureMessageCount(any()) } returns 0 + + // WHEN + val result = RecipientUtil.isMessageRequestAccepted(context, recipient) + + // THEN + assertTrue(result) + } + + @Ignore + @Test + fun givenNoSecureMessagesSent_whenIShareProfileIfFirstSecureMessage_thenIShareProfile() { + // GIVEN + every { mockMessageTable.getOutgoingSecureMessageCount(any()) } returns 0 + + // WHEN + RecipientUtil.shareProfileIfFirstSecureMessage(recipient) + + // THEN + verify { mockRecipientTable.setProfileSharing(recipient.id, true) } + } + + @Ignore + @Test + fun givenSecureMessagesSent_whenIShareProfileIfFirstSecureMessage_thenIShareProfile() { + // GIVEN + every { mockMessageTable.getOutgoingSecureMessageCount(any()) } returns 5 + + // WHEN + RecipientUtil.shareProfileIfFirstSecureMessage(recipient) + + // THEN + verify(exactly = 0) { mockRecipientTable.setProfileSharing(recipient.id, true) } + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.java b/app/src/test/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.java deleted file mode 100644 index 585c54b3f2..0000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.thoughtcrime.securesms.util; - - -import android.text.TextUtils; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.mockito.stubbing.Answer; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.anyString; - -public class DelimiterUtilTest { - - @Rule - public MockitoRule rule = MockitoJUnit.rule(); - - @Mock - private MockedStatic textUtilsMockedStatic; - - @Before - public void setup() { - textUtilsMockedStatic.when(() -> TextUtils.isEmpty(anyString())).thenAnswer((Answer) invocation -> { - if (invocation.getArguments()[0] == null) return true; - return ((String) invocation.getArguments()[0]).isEmpty(); - }); - } - - @Test - public void testEscape() { - assertEquals(DelimiterUtil.escape("MTV Music", ' '), "MTV\\ Music"); - assertEquals(DelimiterUtil.escape("MTV Music", ' '), "MTV\\ \\ Music"); - - assertEquals(DelimiterUtil.escape("MTV,Music", ','), "MTV\\,Music"); - assertEquals(DelimiterUtil.escape("MTV,,Music", ','), "MTV\\,\\,Music"); - - assertEquals(DelimiterUtil.escape("MTV Music", '+'), "MTV Music"); - } - - @Test - public void testSplit() { - String[] parts = DelimiterUtil.split("MTV\\ Music", ' '); - assertEquals(parts.length, 1); - assertEquals(parts[0], "MTV\\ Music"); - - parts = DelimiterUtil.split("MTV Music", ' '); - assertEquals(parts.length, 2); - assertEquals(parts[0], "MTV"); - assertEquals(parts[1], "Music"); - } - - @Test - public void testEscapeSplit() { - String input = "MTV Music"; - String intermediate = DelimiterUtil.escape(input, ' '); - String[] parts = DelimiterUtil.split(intermediate, ' '); - - assertEquals(parts.length, 1); - assertEquals(parts[0], "MTV\\ Music"); - assertEquals(DelimiterUtil.unescape(parts[0], ' '), "MTV Music"); - - input = "MTV\\ Music"; - intermediate = DelimiterUtil.escape(input, ' '); - parts = DelimiterUtil.split(intermediate, ' '); - - assertEquals(parts.length, 1); - assertEquals(parts[0], "MTV\\\\ Music"); - assertEquals(DelimiterUtil.unescape(parts[0], ' '), "MTV\\ Music"); - } - -} diff --git a/app/src/test/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.kt b/app/src/test/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.kt new file mode 100644 index 0000000000..aaae0f3eb5 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.kt @@ -0,0 +1,66 @@ +package org.thoughtcrime.securesms.util + +import android.text.TextUtils +import io.mockk.every +import io.mockk.mockkStatic +import io.mockk.unmockkStatic +import org.junit.After +import org.junit.Assert.assertArrayEquals +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test + +class DelimiterUtilTest { + @Before + fun setup() { + mockkStatic(TextUtils::class) + every { TextUtils.isEmpty(any()) } answers { + (invocation.args.first() as? String)?.isEmpty() ?: true + } + } + + @After + fun cleanup() { + unmockkStatic(TextUtils::class) + } + + @Test + fun testEscape() { + assertEquals("MTV\\ Music", DelimiterUtil.escape("MTV Music", ' ')) + assertEquals("MTV\\ \\ Music", DelimiterUtil.escape("MTV Music", ' ')) + + assertEquals("MTV\\,Music", DelimiterUtil.escape("MTV,Music", ',')) + assertEquals("MTV\\,\\,Music", DelimiterUtil.escape("MTV,,Music", ',')) + + assertEquals("MTV Music", DelimiterUtil.escape("MTV Music", '+')) + } + + @Test + fun testSplit() { + assertArrayEquals( + arrayOf("MTV\\ Music"), + DelimiterUtil.split("MTV\\ Music", ' ') + ) + assertArrayEquals( + arrayOf("MTV", "Music"), + DelimiterUtil.split("MTV Music", ' ') + ) + } + + @Test + fun testEscapeSplit() { + "MTV Music".let { input -> + val intermediate = DelimiterUtil.escape(input, ' ') + val parts = DelimiterUtil.split(intermediate, ' ') + assertEquals("MTV\\ Music", parts.single()) + assertEquals("MTV Music", DelimiterUtil.unescape(parts.single(), ' ')) + } + + "MTV\\ Music".let { input -> + val intermediate = DelimiterUtil.escape(input, ' ') + val parts = DelimiterUtil.split(intermediate, ' ') + assertEquals("MTV\\\\ Music", parts.single()) + assertEquals("MTV\\ Music", DelimiterUtil.unescape(parts.single(), ' ')) + } + } +} diff --git a/core-util/src/test/java/org/signal/core/util/concurrent/LatestPrioritizedSerialExecutorTest.java b/core-util/src/test/java/org/signal/core/util/concurrent/LatestPrioritizedSerialExecutorTest.java deleted file mode 100644 index f1541594aa..0000000000 --- a/core-util/src/test/java/org/signal/core/util/concurrent/LatestPrioritizedSerialExecutorTest.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.signal.core.util.concurrent; - -import org.junit.Test; - -import java.util.LinkedList; -import java.util.Queue; -import java.util.concurrent.Executor; - -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -public final class LatestPrioritizedSerialExecutorTest { - - @Test - public void execute_sortsInPriorityOrder() { - TestExecutor executor = new TestExecutor(); - Runnable placeholder = new TestRunnable(); - - Runnable first = spy(new TestRunnable()); - Runnable second = spy(new TestRunnable()); - Runnable third = spy(new TestRunnable()); - - LatestPrioritizedSerialExecutor subject = new LatestPrioritizedSerialExecutor(executor); - subject.execute(0, placeholder); // The first thing we execute can't be sorted, so we put in this placeholder - subject.execute(1, third); - subject.execute(2, second); - subject.execute(3, first); - - executor.next(); // Clear the placeholder task - - executor.next(); - verify(first).run(); - - executor.next(); - verify(second).run(); - - executor.next(); - verify(third).run(); - } - - @Test - public void execute_replacesDupes() { - TestExecutor executor = new TestExecutor(); - Runnable placeholder = new TestRunnable(); - - Runnable firstReplaced = spy(new TestRunnable()); - Runnable first = spy(new TestRunnable()); - Runnable second = spy(new TestRunnable()); - Runnable thirdReplaced = spy(new TestRunnable()); - Runnable third = spy(new TestRunnable()); - - LatestPrioritizedSerialExecutor subject = new LatestPrioritizedSerialExecutor(executor); - subject.execute(0, placeholder); // The first thing we execute can't be sorted, so we put in this placeholder - subject.execute(1, thirdReplaced); - subject.execute(1, third); - subject.execute(2, second); - subject.execute(3, firstReplaced); - subject.execute(3, first); - - executor.next(); // Clear the placeholder task - - executor.next(); - verify(first).run(); - - executor.next(); - verify(second).run(); - - executor.next(); - verify(third).run(); - - verify(firstReplaced, never()).run(); - verify(thirdReplaced, never()).run(); - } - - private static final class TestExecutor implements Executor { - - private final Queue tasks = new LinkedList<>(); - - @Override - public void execute(Runnable command) { - tasks.add(command); - } - - public void next() { - tasks.remove().run(); - } - } - - public static class TestRunnable implements Runnable { - @Override - public void run() { } - } -} diff --git a/core-util/src/test/java/org/signal/core/util/concurrent/LatestPrioritizedSerialExecutorTest.kt b/core-util/src/test/java/org/signal/core/util/concurrent/LatestPrioritizedSerialExecutorTest.kt new file mode 100644 index 0000000000..9f53ebcbc3 --- /dev/null +++ b/core-util/src/test/java/org/signal/core/util/concurrent/LatestPrioritizedSerialExecutorTest.kt @@ -0,0 +1,90 @@ +package org.signal.core.util.concurrent + +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import java.util.concurrent.Executor + +class LatestPrioritizedSerialExecutorTest { + @Test + fun execute_sortsInPriorityOrder() { + val executor = TestExecutor() + val placeholder = TestRunnable() + + val first = TestRunnable() + val second = TestRunnable() + val third = TestRunnable() + + val subject = LatestPrioritizedSerialExecutor(executor) + subject.execute(0, placeholder) // The first thing we execute can't be sorted, so we put in this placeholder + subject.execute(1, third) + subject.execute(2, second) + subject.execute(3, first) + + executor.next() // Clear the placeholder task + + executor.next() + assertTrue(first.didRun) + + executor.next() + assertTrue(second.didRun) + + executor.next() + assertTrue(third.didRun) + } + + @Test + fun execute_replacesDupes() { + val executor = TestExecutor() + val placeholder = TestRunnable() + + val firstReplaced = TestRunnable() + val first = TestRunnable() + val second = TestRunnable() + val thirdReplaced = TestRunnable() + val third = TestRunnable() + + val subject = LatestPrioritizedSerialExecutor(executor) + subject.execute(0, placeholder) // The first thing we execute can't be sorted, so we put in this placeholder + subject.execute(1, thirdReplaced) + subject.execute(1, third) + subject.execute(2, second) + subject.execute(3, firstReplaced) + subject.execute(3, first) + + executor.next() // Clear the placeholder task + + executor.next() + assertTrue(first.didRun) + + executor.next() + assertTrue(second.didRun) + + executor.next() + assertTrue(third.didRun) + + assertFalse(firstReplaced.didRun) + assertFalse(thirdReplaced.didRun) + } + + private class TestExecutor : Executor { + private val tasks = ArrayDeque() + + override fun execute(command: Runnable) { + tasks.add(command) + } + + fun next() { + tasks.removeLast().run() + } + } + + class TestRunnable : Runnable { + private var _didRun = false + val didRun get() = _didRun + + override fun run() { + _didRun = true + } + } +} diff --git a/libsignal-service/src/test/java/org/whispersystems/signalservice/api/services/DonationsServiceTest.java b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/services/DonationsServiceTest.java deleted file mode 100644 index 9f7a72a293..0000000000 --- a/libsignal-service/src/test/java/org/whispersystems/signalservice/api/services/DonationsServiceTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.whispersystems.signalservice.api.services; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.Mockito; -import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; -import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription; -import org.whispersystems.signalservice.api.subscriptions.SubscriberId; -import org.whispersystems.signalservice.internal.ServiceResponse; -import org.whispersystems.signalservice.internal.push.PushServiceSocket; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@RunWith(JUnit4.class) -public class DonationsServiceTest { - - private final PushServiceSocket pushServiceSocket = Mockito.mock(PushServiceSocket.class); - private final DonationsService testSubject = new DonationsService(pushServiceSocket); - - @Test - public void givenASubscriberId_whenIGetASuccessfulResponse_thenItIsMappedWithTheCorrectStatusCodeAndNonEmptyObject() throws Exception { - // GIVEN - SubscriberId subscriberId = SubscriberId.generate(); - when(pushServiceSocket.getSubscription(subscriberId.serialize())) - .thenReturn(getActiveSubscription()); - - // WHEN - ServiceResponse response = testSubject.getSubscription(subscriberId); - - // THEN - verify(pushServiceSocket).getSubscription(subscriberId.serialize()); - assertEquals(200, response.getStatus()); - assertTrue(response.getResult().isPresent()); - } - - @Test - public void givenASubscriberId_whenIGetAnUnsuccessfulResponse_thenItIsMappedWithTheCorrectStatusCodeAndEmptyObject() throws Exception { - // GIVEN - SubscriberId subscriberId = SubscriberId.generate(); - when(pushServiceSocket.getSubscription(subscriberId.serialize())) - .thenThrow(new NonSuccessfulResponseCodeException(403)); - - // WHEN - ServiceResponse response = testSubject.getSubscription(subscriberId); - - // THEN - verify(pushServiceSocket).getSubscription(subscriberId.serialize()); - assertEquals(403, response.getStatus()); - assertFalse(response.getResult().isPresent()); - } - - private ActiveSubscription getActiveSubscription() { - return ActiveSubscription.EMPTY; - } -} diff --git a/libsignal-service/src/test/java/org/whispersystems/signalservice/api/services/DonationsServiceTest.kt b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/services/DonationsServiceTest.kt new file mode 100644 index 0000000000..fa36648a79 --- /dev/null +++ b/libsignal-service/src/test/java/org/whispersystems/signalservice/api/services/DonationsServiceTest.kt @@ -0,0 +1,49 @@ +package org.whispersystems.signalservice.api.services + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException +import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription +import org.whispersystems.signalservice.api.subscriptions.SubscriberId +import org.whispersystems.signalservice.internal.push.PushServiceSocket + +class DonationsServiceTest { + private val pushServiceSocket: PushServiceSocket = mockk() + private val testSubject = DonationsService(pushServiceSocket) + private val activeSubscription = ActiveSubscription.EMPTY + + @Test + fun givenASubscriberId_whenIGetASuccessfulResponse_thenItIsMappedWithTheCorrectStatusCodeAndNonEmptyObject() { + // GIVEN + val subscriberId = SubscriberId.generate() + every { pushServiceSocket.getSubscription(subscriberId.serialize()) } returns activeSubscription + + // WHEN + val response = testSubject.getSubscription(subscriberId) + + // THEN + verify { pushServiceSocket.getSubscription(subscriberId.serialize()) } + assertEquals(200, response.status) + assertTrue(response.result.isPresent) + } + + @Test + fun givenASubscriberId_whenIGetAnUnsuccessfulResponse_thenItIsMappedWithTheCorrectStatusCodeAndEmptyObject() { + // GIVEN + val subscriberId = SubscriberId.generate() + every { pushServiceSocket.getSubscription(subscriberId.serialize()) } throws NonSuccessfulResponseCodeException(403) + + // WHEN + val response = testSubject.getSubscription(subscriberId) + + // THEN + verify { pushServiceSocket.getSubscription(subscriberId.serialize()) } + assertEquals(403, response.status) + assertFalse(response.result.isPresent) + } +} From fd80df68d8ca8aa674576b45b2cb2eda58f09060 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Wed, 27 Nov 2024 11:52:34 -0500 Subject: [PATCH 29/56] Fix empty username backupv2 export. --- .../backup/v2/processor/AccountDataArchiveProcessor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataArchiveProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataArchiveProcessor.kt index bf61d7b9dc..0330b99de1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataArchiveProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataArchiveProcessor.kt @@ -67,7 +67,7 @@ object AccountDataArchiveProcessor { givenName = selfRecord.signalProfileName.givenName, familyName = selfRecord.signalProfileName.familyName, avatarUrlPath = selfRecord.signalProfileAvatar ?: "", - username = selfRecord.username, + username = selfRecord.username?.takeIf { it.isNotBlank() }, usernameLink = if (signalStore.accountValues.usernameLink != null) { AccountData.UsernameLink( entropy = signalStore.accountValues.usernameLink?.entropy?.toByteString() ?: EMPTY, From 8e5640cffc0982a73d297004118ef78cf151521f Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Wed, 27 Nov 2024 10:59:12 -0800 Subject: [PATCH 30/56] Fix lost voice notes on orientation change. --- .../java/org/thoughtcrime/securesms/util/ConfigurationUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationUtil.java index f1f35d9bff..1eca39fd6c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationUtil.java @@ -24,6 +24,6 @@ public static float getFontScale(@NonNull Configuration configuration) { public static boolean isUiModeChanged(@NonNull Configuration configuration, @NonNull Configuration newConfiguration) { int oldTheme = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK; int newTheme = newConfiguration.uiMode & Configuration.UI_MODE_NIGHT_MASK; - return oldTheme == newTheme; + return oldTheme != newTheme; } } From cd7184332fcedad4c134ccd406d8dd00899226c4 Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Wed, 27 Nov 2024 11:34:29 -0800 Subject: [PATCH 31/56] Allow group removal shortcut in chat folders. --- .../securesms/conversationlist/ConversationListFragment.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index 2b9d22b02a..6cddac7627 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -1502,8 +1502,8 @@ public boolean onConversationLongClick(@NonNull Conversation conversation, @NonN items.add(new ActionItem(R.drawable.symbol_archive_up_24, getResources().getString(R.string.ConversationListFragment_unarchive), () -> handleArchive(id, false))); } else { if (viewModel.getCurrentFolder().getFolderType() == ChatFolderRecord.FolderType.ALL && - conversation.getThreadRecord().getRecipient().isIndividual() || - conversation.getThreadRecord().getRecipient().isPushV2Group()) { + (conversation.getThreadRecord().getRecipient().isIndividual() || + conversation.getThreadRecord().getRecipient().isPushV2Group())) { List folders = viewModel.getFolders().stream().map(ChatFolderMappingModel::getChatFolder).collect(Collectors.toList()); items.add(new ActionItem(R.drawable.symbol_folder_add, getString(R.string.ConversationListFragment_add_to_folder), () -> AddToFolderBottomSheet.showChatFolderSheet(folders, conversation.getThreadRecord().getThreadId(), conversation.getThreadRecord().getRecipient().isIndividual()).show(getParentFragmentManager(), BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) From 6cf6ae8f5f1b9a5165ae492f02ffb78f5e33b5a3 Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Wed, 27 Nov 2024 11:42:05 -0800 Subject: [PATCH 32/56] Use EmojiTextView for chat folder names. --- app/src/main/res/layout/chat_folder_item.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/chat_folder_item.xml b/app/src/main/res/layout/chat_folder_item.xml index 5c1ff21514..8ea0f551ca 100644 --- a/app/src/main/res/layout/chat_folder_item.xml +++ b/app/src/main/res/layout/chat_folder_item.xml @@ -12,7 +12,7 @@ android:backgroundTint="@color/signal_colorSurfaceVariant" android:background="@drawable/rounded_rectangle_surface_variant" > - Date: Mon, 2 Dec 2024 11:23:42 -0500 Subject: [PATCH 33/56] Center call link button. --- app/src/main/res/layout/share_contact_selection_item.xml | 2 +- app/src/main/res/layout/share_interstitial_activity.xml | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/layout/share_contact_selection_item.xml b/app/src/main/res/layout/share_contact_selection_item.xml index d256b28191..dd609f92ec 100644 --- a/app/src/main/res/layout/share_contact_selection_item.xml +++ b/app/src/main/res/layout/share_contact_selection_item.xml @@ -4,7 +4,7 @@ tools:viewBindingIgnore="true" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/recipient_view_name" - style="@style/Signal.Text.Preview" + style="@style/Signal.Text.Body" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" diff --git a/app/src/main/res/layout/share_interstitial_activity.xml b/app/src/main/res/layout/share_interstitial_activity.xml index 20038c27f7..ba8c398ed4 100644 --- a/app/src/main/res/layout/share_interstitial_activity.xml +++ b/app/src/main/res/layout/share_interstitial_activity.xml @@ -50,7 +50,8 @@ android:layout_height="2dp" android:alpha="1" android:background="@drawable/compose_divider_background" - app:layout_constraintBottom_toTopOf="@id/selected_list" /> + android:layout_marginBottom="16dp" + app:layout_constraintBottom_toTopOf="@id/share_confirm" /> @@ -72,7 +74,7 @@ android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_marginEnd="16dp" - android:layout_marginBottom="20dp" + android:layout_marginBottom="16dp" app:circularProgressMaterialButton__label="@string/ShareActivity__send" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" /> From 3c086f347e54935b1b8c432da157badcf1b0e6a8 Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Mon, 2 Dec 2024 09:07:46 -0800 Subject: [PATCH 34/56] Add validation error UI. --- .../securesms/backup/ArchiveUploadProgress.kt | 4 ++ .../securesms/backup/v2/BackupRepository.kt | 38 +++++++++++-- .../backup/v2/ui/BackupAlertBottomSheet.kt | 43 +++++++++++++- .../backup/v2/ui/BackupAlertDelegate.kt | 8 +-- .../backup/v2/ui/status/BackupStatusBanner.kt | 19 +++++++ .../backup/v2/ui/status/BackupStatusRow.kt | 57 +++++++++++++++++-- .../settings/app/AppSettingsFragment.kt | 2 +- .../settings/app/AppSettingsViewModel.kt | 2 + .../settings/app/BackupFailureState.kt | 1 + .../remote/RemoteBackupsSettingsFragment.kt | 21 ++++++- .../remote/RemoteBackupsSettingsViewModel.kt | 2 + .../securesms/jobs/BackupMessagesJob.kt | 2 +- app/src/main/res/values/strings.xml | 14 ++++- 13 files changed, 191 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/ArchiveUploadProgress.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/ArchiveUploadProgress.kt index f7a2073e6c..6f861efb00 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/ArchiveUploadProgress.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/ArchiveUploadProgress.kt @@ -104,6 +104,10 @@ object ArchiveUploadProgress { updateState(PROGRESS_NONE) } + fun onValidationFailure() { + updateState(PROGRESS_NONE) + } + private fun updateState(state: ArchiveUploadProgressState, notify: Boolean = true) { uploadProgress = state SignalStore.backup.archiveUploadState = state diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index 9c57ebabc7..4515e752a2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -204,14 +204,27 @@ object BackupRepository { } /** - * Whether the "Could not complete backup" row should be displayed in settings. + * Whether the "Backup Failed" row should be displayed in settings. + * Shown when the initial backup creation has failed */ fun shouldDisplayBackupFailedSettingsRow(): Boolean { if (shouldNotDisplayBackupFailedMessaging()) { return false } - return SignalStore.backup.hasBackupFailure + return !SignalStore.backup.hasBackupBeenUploaded && SignalStore.backup.hasBackupFailure + } + + /** + * Whether the "Could not complete backup" row should be displayed in settings. + * Shown when a new backup could not be created but there is an existing one already + */ + fun shouldDisplayCouldNotCompleteBackupSettingsRow(): Boolean { + if (shouldNotDisplayBackupFailedMessaging()) { + return false + } + + return SignalStore.backup.hasBackupBeenUploaded && SignalStore.backup.hasBackupFailure } /** @@ -230,7 +243,8 @@ object BackupRepository { } /** - * Whether or not the "Could not complete backup" sheet should be displayed. + * Whether or not the "Backup failed" sheet should be displayed. + * Should only be displayed if this is the failure of the initial backup creation. */ @JvmStatic fun shouldDisplayBackupFailedSheet(): Boolean { @@ -238,7 +252,19 @@ object BackupRepository { return false } - return System.currentTimeMillis().milliseconds > SignalStore.backup.nextBackupFailureSheetSnoozeTime + return !SignalStore.backup.hasBackupBeenUploaded && System.currentTimeMillis().milliseconds > SignalStore.backup.nextBackupFailureSheetSnoozeTime + } + + /** + * Whether or not the "Could not complete backup" sheet should be displayed. + */ + @JvmStatic + fun shouldDisplayCouldNotCompleteBackupSheet(): Boolean { + if (shouldNotDisplayBackupFailedMessaging()) { + return false + } + + return SignalStore.backup.hasBackupBeenUploaded && System.currentTimeMillis().milliseconds > SignalStore.backup.nextBackupFailureSheetSnoozeTime } fun snoozeYourMediaWillBeDeletedTodaySheet() { @@ -249,7 +275,7 @@ object BackupRepository { * Whether or not the "Your media will be deleted today" sheet should be displayed. */ suspend fun shouldDisplayYourMediaWillBeDeletedTodaySheet(): Boolean { - if (shouldNotDisplayBackupFailedMessaging() || !SignalStore.backup.optimizeStorage) { + if (shouldNotDisplayBackupFailedMessaging() || !SignalStore.backup.hasBackupBeenUploaded || !SignalStore.backup.optimizeStorage) { return false } @@ -285,7 +311,7 @@ object BackupRepository { } private fun shouldNotDisplayBackupFailedMessaging(): Boolean { - return !RemoteConfig.messageBackups || !SignalStore.backup.areBackupsEnabled || !SignalStore.backup.hasBackupBeenUploaded + return !RemoteConfig.messageBackups || !SignalStore.backup.areBackupsEnabled } /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt index 436c410271..44070c7671 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt @@ -57,6 +57,8 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobs.BackupMessagesJob import org.thoughtcrime.securesms.jobs.BackupRestoreMediaJob import org.thoughtcrime.securesms.payments.FiatMoneyUtil +import org.thoughtcrime.securesms.util.CommunicationActions +import org.thoughtcrime.securesms.util.PlayStoreUtil import kotlin.time.Duration import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.milliseconds @@ -67,6 +69,8 @@ import org.signal.core.ui.R as CoreUiR */ class BackupAlertBottomSheet : UpgradeToPaidTierBottomSheet() { + override val peekHeightPercentage: Float = 0.75f + companion object { private const val ARG_ALERT = "alert" @@ -126,6 +130,8 @@ class BackupAlertBottomSheet : UpgradeToPaidTierBottomSheet() { } is BackupAlert.DiskFull -> Unit + is BackupAlert.BackupFailed -> + PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(requireContext()) } dismissAllowingStateLoss() @@ -144,6 +150,8 @@ class BackupAlertBottomSheet : UpgradeToPaidTierBottomSheet() { is BackupAlert.DiskFull -> { displaySkipRestoreDialog() } + // TODO [backups] - Update support URL with backups page + BackupAlert.BackupFailed -> CommunicationActions.openBrowserLink(requireContext(), requireContext().getString(R.string.backup_support_url)) } dismissAllowingStateLoss() @@ -153,7 +161,7 @@ class BackupAlertBottomSheet : UpgradeToPaidTierBottomSheet() { super.onDismiss(dialog) when (backupAlert) { - is BackupAlert.CouldNotCompleteBackup -> BackupRepository.markBackupFailedSheetDismissed() + is BackupAlert.CouldNotCompleteBackup, BackupAlert.BackupFailed -> BackupRepository.markBackupFailedSheetDismissed() is BackupAlert.MediaWillBeDeletedToday -> BackupRepository.snoozeYourMediaWillBeDeletedTodaySheet() else -> Unit } @@ -261,6 +269,7 @@ private fun BackupAlertSheetContent( is BackupAlert.MediaBackupsAreOff -> MediaBackupsAreOffBody(backupAlert.endOfPeriodSeconds, mediaTtl) BackupAlert.MediaWillBeDeletedToday -> MediaWillBeDeletedTodayBody() is BackupAlert.DiskFull -> DiskFullBody(requiredSpace = backupAlert.requiredSpace) + BackupAlert.BackupFailed -> BackupFailedBody() } val secondaryActionResource = rememberSecondaryActionResource(backupAlert = backupAlert) @@ -366,12 +375,22 @@ private fun DiskFullBody(requiredSpace: String) { ) } +@Composable +private fun BackupFailedBody() { + Text( + text = stringResource(id = R.string.BackupAlertBottomSheet__an_error_occurred), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(bottom = 36.dp) + ) +} + @Composable private fun rememberBackupsIconColors(backupAlert: BackupAlert): BackupsIconColors { return remember(backupAlert) { when (backupAlert) { BackupAlert.FailedToRenew, is BackupAlert.MediaBackupsAreOff -> error("Not icon-based options.") - is BackupAlert.CouldNotCompleteBackup, is BackupAlert.DiskFull -> BackupsIconColors.Warning + is BackupAlert.CouldNotCompleteBackup, BackupAlert.BackupFailed, is BackupAlert.DiskFull -> BackupsIconColors.Warning BackupAlert.MediaWillBeDeletedToday -> BackupsIconColors.Error } } @@ -385,6 +404,7 @@ private fun titleString(backupAlert: BackupAlert): String { is BackupAlert.MediaBackupsAreOff -> stringResource(R.string.BackupAlertBottomSheet__your_backups_subscription_expired) BackupAlert.MediaWillBeDeletedToday -> stringResource(R.string.BackupAlertBottomSheet__your_media_will_be_deleted_today) is BackupAlert.DiskFull -> stringResource(R.string.BackupAlertBottomSheet__free_up_s_on_this_device, backupAlert.requiredSpace) + BackupAlert.BackupFailed -> stringResource(R.string.BackupAlertBottomSheet__backup_failed) } } @@ -399,6 +419,7 @@ private fun primaryActionString( is BackupAlert.MediaBackupsAreOff -> stringResource(R.string.BackupAlertBottomSheet__subscribe_for_s_month, pricePerMonth) BackupAlert.MediaWillBeDeletedToday -> stringResource(R.string.BackupAlertBottomSheet__download_media_now) is BackupAlert.DiskFull -> stringResource(R.string.BackupAlertBottomSheet__got_it) + is BackupAlert.BackupFailed -> stringResource(R.string.BackupAlertBottomSheet__check_for_update) } } @@ -411,6 +432,7 @@ private fun rememberSecondaryActionResource(backupAlert: BackupAlert): Int { is BackupAlert.MediaBackupsAreOff -> R.string.BackupAlertBottomSheet__not_now BackupAlert.MediaWillBeDeletedToday -> R.string.BackupAlertBottomSheet__dont_download_media is BackupAlert.DiskFull -> R.string.BackupAlertBottomSheet__skip_restore + is BackupAlert.BackupFailed -> R.string.BackupAlertBottomSheet__learn_more } } } @@ -471,6 +493,17 @@ private fun BackupAlertSheetContentPreviewDiskFull() { } } +@SignalPreview +@Composable +private fun BackupAlertSheetContentPreviewBackupFailed() { + Previews.BottomSheetPreview { + BackupAlertSheetContent( + backupAlert = BackupAlert.BackupFailed, + mediaTtl = 60.days + ) + } +} + /** * All necessary information to display the sheet should be handed in through the specific alert. */ @@ -485,6 +518,12 @@ sealed class BackupAlert : Parcelable { val daysSinceLastBackup: Int ) : BackupAlert() + /** + * This value is driven by the same watermarking system for [CouldNotCompleteBackup] so that only one of these sheets is shown by the system + * This value is driven by failure to complete the initial backup. + */ + data object BackupFailed : BackupAlert() + /** * This value is driven by InAppPayment state, and will be automatically cleared when the sheet is displayed. */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt index 18b7249b80..f6057c6840 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertDelegate.kt @@ -22,11 +22,11 @@ object BackupAlertDelegate { lifecycle.coroutineScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { if (BackupRepository.shouldDisplayBackupFailedSheet()) { + BackupAlertBottomSheet.create(BackupAlert.BackupFailed).show(fragmentManager, null) + } else if (BackupRepository.shouldDisplayCouldNotCompleteBackupSheet()) { BackupAlertBottomSheet.create(BackupAlert.CouldNotCompleteBackup(daysSinceLastBackup = SignalStore.backup.daysSinceLastBackup)).show(fragmentManager, null) - } - - if (BackupRepository.shouldDisplayYourMediaWillBeDeletedTodaySheet()) { - BackupAlertBottomSheet.create(BackupAlert.MediaWillBeDeletedToday) + } else if (BackupRepository.shouldDisplayYourMediaWillBeDeletedTodaySheet()) { + BackupAlertBottomSheet.create(BackupAlert.MediaWillBeDeletedToday).show(fragmentManager, null) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusBanner.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusBanner.kt index 2e00cf5d81..b69594ff8e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusBanner.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusBanner.kt @@ -195,6 +195,12 @@ fun BackupStatusBannerPreview() { BackupStatusBanner( data = BackupStatusData.CouldNotCompleteBackup ) + + HorizontalDivider() + + BackupStatusBanner( + data = BackupStatusData.BackupFailed + ) } } } @@ -235,6 +241,19 @@ sealed interface BackupStatusData { override val iconColors: BackupsIconColors = BackupsIconColors.Warning } + /** + * Initial backup creation failure + */ + data object BackupFailed : BackupStatusData { + override val iconRes: Int = R.drawable.symbol_backup_error_24 + + override val title: String + @Composable + get() = stringResource(androidx.biometric.R.string.default_error_msg) + + override val iconColors: BackupsIconColors = BackupsIconColors.Warning + } + /** * User does not have enough space on their device to complete backup restoration */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusRow.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusRow.kt index 4dd17140f2..d39f831a5e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusRow.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusRow.kt @@ -20,17 +20,19 @@ import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.LinkAnnotation import androidx.compose.ui.text.Placeholder import androidx.compose.ui.text.PlaceholderVerticalAlign +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextLinkStyles import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.withLink import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.signal.core.ui.Previews @@ -48,10 +50,13 @@ import org.signal.core.ui.R as CoreUiR fun BackupStatusRow( backupStatusData: BackupStatusData, onSkipClick: () -> Unit = {}, - onCancelClick: () -> Unit = {} + onCancelClick: () -> Unit = {}, + onLearnMoreClick: () -> Unit = {} ) { Column { - if (backupStatusData !is BackupStatusData.CouldNotCompleteBackup) { + if (backupStatusData !is BackupStatusData.CouldNotCompleteBackup && + backupStatusData !is BackupStatusData.BackupFailed + ) { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = dimensionResource(CoreUiR.dimen.gutter)) @@ -120,6 +125,40 @@ fun BackupStatusRow( modifier = Modifier.padding(horizontal = dimensionResource(CoreUiR.dimen.gutter)) ) } + BackupStatusData.BackupFailed -> { + val inlineContentMap = mapOf( + "yellow_bullet" to InlineTextContent( + Placeholder(12.sp, 12.sp, PlaceholderVerticalAlign.TextCenter) + ) { + Box( + modifier = Modifier + .size(12.dp) + .background(color = backupStatusData.iconColors.foreground, shape = CircleShape) + ) + } + ) + + Text( + text = buildAnnotatedString { + appendInlineContent("yellow_bullet") + append(" ") + append(stringResource(R.string.BackupStatusRow__your_last_backup_latest_version)) + append(" ") + withLink( + LinkAnnotation.Clickable( + stringResource(R.string.BackupStatusRow__learn_more), + styles = TextLinkStyles(style = SpanStyle(color = MaterialTheme.colorScheme.primary)) + ) { + onLearnMoreClick() + } + ) { + append(stringResource(R.string.BackupStatusRow__learn_more)) + } + }, + inlineContent = inlineContentMap, + modifier = Modifier.padding(horizontal = dimensionResource(CoreUiR.dimen.gutter)) + ) + } } } } @@ -241,3 +280,13 @@ fun BackupStatusRowCouldNotCompleteBackupPreview() { ) } } + +@SignalPreview +@Composable +fun BackupStatusRowBackupFailedPreview() { + Previews.Preview { + BackupStatusRow( + backupStatusData = BackupStatusData.BackupFailed + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt index 2e6c21768c..ba77211323 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt @@ -215,7 +215,7 @@ private fun AppSettingsContent( } } - BackupFailureState.COULD_NOT_COMPLETE_BACKUP -> { + BackupFailureState.BACKUP_FAILED, BackupFailureState.COULD_NOT_COMPLETE_BACKUP -> { item { Dividers.Default() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsViewModel.kt index 8b30532c5f..7d0ba2ddfa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsViewModel.kt @@ -72,6 +72,8 @@ class AppSettingsViewModel : ViewModel() { private fun getBackupFailureState(): BackupFailureState { return if (BackupRepository.shouldDisplayBackupFailedSettingsRow()) { + BackupFailureState.BACKUP_FAILED + } else if (BackupRepository.shouldDisplayCouldNotCompleteBackupSettingsRow()) { BackupFailureState.COULD_NOT_COMPLETE_BACKUP } else if (SignalStore.backup.subscriptionStateMismatchDetected) { BackupFailureState.SUBSCRIPTION_STATE_MISMATCH diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/BackupFailureState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/BackupFailureState.kt index d971b2d322..c3b3666d09 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/BackupFailureState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/BackupFailureState.kt @@ -10,6 +10,7 @@ package org.thoughtcrime.securesms.components.settings.app */ enum class BackupFailureState { NONE, + BACKUP_FAILED, COULD_NOT_COMPLETE_BACKUP, SUBSCRIPTION_STATE_MISMATCH } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt index 671dc83bb6..873dffc941 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt @@ -83,6 +83,8 @@ import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.backup.ArchiveUploadProgress import org.thoughtcrime.securesms.backup.v2.BackupFrequency import org.thoughtcrime.securesms.backup.v2.MessageBackupTier +import org.thoughtcrime.securesms.backup.v2.ui.BackupAlert +import org.thoughtcrime.securesms.backup.v2.ui.BackupAlertBottomSheet import org.thoughtcrime.securesms.backup.v2.ui.status.BackupStatusData import org.thoughtcrime.securesms.backup.v2.ui.status.BackupStatusRow import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType @@ -233,6 +235,10 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { requireActivity().finish() requireActivity().startActivity(AppSettingsActivity.help(requireContext(), HelpFragment.REMOTE_BACKUPS_INDEX)) } + + override fun onLearnMoreAboutBackupFailure() { + BackupAlertBottomSheet.create(BackupAlert.BackupFailed).show(parentFragmentManager, null) + } } private fun displayBackupKey() { @@ -314,6 +320,7 @@ private interface ContentCallbacks { fun onRenewLostSubscription() = Unit fun onLearnMoreAboutLostSubscription() = Unit fun onContactSupport() = Unit + fun onLearnMoreAboutBackupFailure() = Unit } @Composable @@ -392,7 +399,8 @@ private fun RemoteBackupsSettingsContent( BackupStatusRow( backupStatusData = backupRestoreState.backupStatusData, onCancelClick = contentCallbacks::onCancelMediaRestore, - onSkipClick = contentCallbacks::onSkipMediaRestore + onSkipClick = contentCallbacks::onSkipMediaRestore, + onLearnMoreClick = contentCallbacks::onLearnMoreAboutBackupFailure ) } } else if (backupRestoreState is BackupRestoreState.Ready && backupState is RemoteBackupsSettingsState.BackupState.Canceled) { @@ -420,7 +428,8 @@ private fun RemoteBackupsSettingsContent( BackupStatusRow( backupStatusData = backupRestoreState.backupStatusData, onCancelClick = contentCallbacks::onCancelMediaRestore, - onSkipClick = contentCallbacks::onSkipMediaRestore + onSkipClick = contentCallbacks::onSkipMediaRestore, + onLearnMoreClick = contentCallbacks::onLearnMoreAboutBackupFailure ) } } @@ -920,8 +929,14 @@ private fun InProgressBackupRow( ) } + val inProgressText = if (totalProgress == null || totalProgress == 0) { + stringResource(R.string.RemoteBackupsSettingsFragment__processing_backup) + } else { + stringResource(R.string.RemoteBackupsSettingsFragment__d_slash_d, progress ?: 0, totalProgress) + } + Text( - text = stringResource(R.string.RemoteBackupsSettingsFragment__d_slash_d, progress ?: 0, totalProgress ?: 0), + text = inProgressText, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt index e2852c2deb..915af9618a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt @@ -92,6 +92,8 @@ class RemoteBackupsSettingsViewModel : ViewModel() { } else if (SignalStore.backup.totalRestorableAttachmentSize > 0L) { _restoreState.update { BackupRestoreState.Ready(SignalStore.backup.totalRestorableAttachmentSize.bytes.toUnitString()) } } else if (BackupRepository.shouldDisplayBackupFailedSettingsRow()) { + _restoreState.update { BackupRestoreState.FromBackupStatusData(BackupStatusData.BackupFailed) } + } else if (BackupRepository.shouldDisplayCouldNotCompleteBackupSettingsRow()) { _restoreState.update { BackupRestoreState.FromBackupStatusData(BackupStatusData.CouldNotCompleteBackup) } } else { _restoreState.update { BackupRestoreState.None } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt index 0516e1af7d..f2c06ba2e4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt @@ -101,8 +101,8 @@ class BackupMessagesJob private constructor(parameters: Parameters) : Job(parame return Result.retry(defaultBackoff()) } is ArchiveValidator.ValidationResult.ValidationError -> { - // TODO [backup] UX Log.w(TAG, "The backup file fails validation! Message: " + result.exception.message) + ArchiveUploadProgress.onValidationFailure() return Result.failure() } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8ba143f27c..fe4257b44c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7504,6 +7504,12 @@ Skip restore? If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7536,7 +7542,11 @@ Skip download - Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-F and tap "Back up now" to try again. + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7740,6 +7750,8 @@ Renew Learn more + + Processing backup… You have %1$s of backup data that’s not on this device. Your backup will be deleted when your subscription ends in %2$d day. From d6be14a95fcd950452b81545890e36259678603f Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Mon, 2 Dec 2024 13:47:35 -0400 Subject: [PATCH 35/56] Split remote user check for group vs individual calls. --- .../securesms/components/webrtc/WebRtcCallViewModel.java | 2 +- .../securesms/components/webrtc/WebRtcControls.java | 1 + .../org/thoughtcrime/securesms/events/WebRtcViewModel.kt | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java index fb98b57c15..e37f0962bb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java @@ -320,7 +320,7 @@ public void updateFromWebRtcViewModel(@NonNull WebRtcViewModel webRtcViewModel, webRtcViewModel.isRemoteVideoEnabled(), webRtcViewModel.isRemoteVideoOffer(), localParticipant.isMoreThanOneCameraAvailable(), - webRtcViewModel.getRemoteDevicesCount().orElse(0L) > 0, + webRtcViewModel.hasAtLeastOneRemote(), webRtcViewModel.getActiveDevice(), webRtcViewModel.getAvailableDevices(), webRtcViewModel.getRemoteDevicesCount().orElse(0), diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java index 0f4d9acdf8..838f363959 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java @@ -8,6 +8,7 @@ import androidx.annotation.StringRes; import androidx.annotation.VisibleForTesting; +import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager; diff --git a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt index ee133e640c..44cc7dca80 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt @@ -105,6 +105,13 @@ class WebRtcViewModel(state: WebRtcServiceState) { val isCallLink: Boolean = state.callInfoState.callRecipient.isCallLink val callLinkDisconnectReason: CallLinkDisconnectReason? = state.callInfoState.callLinkDisconnectReason + @get:JvmName("hasAtLeastOneRemote") + val hasAtLeastOneRemote = if (state.callInfoState.callRecipient.isIndividual) { + remoteParticipants.isNotEmpty() + } else { + remoteDevicesCount.orElse(0L) > 0L + } + @get:JvmName("shouldRingGroup") val ringGroup: Boolean = state.getCallSetupState(state.callInfoState.activePeer?.callId).ringGroup val ringerRecipient: Recipient = state.getCallSetupState(state.callInfoState.activePeer?.callId).ringerRecipient From 9fa6d4774de0eff2569d49b09b5f5ea64dbde664 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Mon, 2 Dec 2024 15:41:29 -0400 Subject: [PATCH 36/56] Ensure updateMessageRequestAcceptedState runs on main thread. --- .../conversation/v2/ConversationFragment.kt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt index 28a2ca9e93..6481f7d0e4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt @@ -42,6 +42,7 @@ import android.widget.TextView.OnEditorActionListener import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.activity.result.ActivityResultLauncher +import androidx.annotation.MainThread import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode @@ -893,13 +894,13 @@ class ConversationFragment : .conversationThreadState .subscribeOn(Schedulers.io()) .doOnSuccess { state -> - updateMessageRequestAcceptedState(state.meta.messageRequestData.isMessageRequestAccepted) SignalLocalMetrics.ConversationOpen.onDataLoaded() conversationItemDecorations.setFirstUnreadCount(state.meta.unreadCount) colorizer.onGroupMembershipChanged(state.meta.groupMemberAcis) } .observeOn(AndroidSchedulers.mainThread()) .doOnSuccess { state -> + updateMessageRequestAcceptedState(state.meta.messageRequestData.isMessageRequestAccepted) moveToStartPosition(state.meta) } .flatMapObservable { it.items.data } @@ -1313,14 +1314,9 @@ class ConversationFragment : updateMessageRequestAcceptedState(!viewModel.hasMessageRequestState) } + @MainThread private fun updateMessageRequestAcceptedState(isMessageRequestAccepted: Boolean) { - if (binding.conversationItemRecycler.isInLayout) { - binding.conversationItemRecycler.doAfterNextLayout { - adapter.setMessageRequestIsAccepted(isMessageRequestAccepted) - } - } else { - adapter.setMessageRequestIsAccepted(isMessageRequestAccepted) - } + adapter.setMessageRequestIsAccepted(isMessageRequestAccepted) } private fun invalidateOptionsMenu() { From 7b0df17d9ab2c62b7c29c6d1027e4879ea45b977 Mon Sep 17 00:00:00 2001 From: Jameson Williams Date: Sat, 30 Nov 2024 20:33:57 -0600 Subject: [PATCH 37/56] Convert more tests to kotlin. Resolves #13825 --- .../CursorRecyclerViewAdapterTest.java | 79 - .../database/CursorRecyclerViewAdapterTest.kt | 61 + .../GroupsV2UpdateMessageProducerTest.java | 1534 ----------------- .../GroupsV2UpdateMessageProducerTest.kt | 1512 ++++++++++++++++ .../securesms/jobmanager/JobMigratorTest.java | 117 -- .../securesms/jobmanager/JobMigratorTest.kt | 130 ++ .../RecipientIdFollowUpJobMigrationTest.java | 63 - .../RecipientIdFollowUpJobMigrationTest.kt | 57 + .../RecipientIdJobMigrationTest.java | 286 --- .../migrations/RecipientIdJobMigrationTest.kt | 365 ++++ .../SendReadReceiptsJobMigrationTest.java | 107 -- .../SendReadReceiptsJobMigrationTest.kt | 104 ++ ...ributionSendJobRecipientMigrationTest.java | 89 - ...stributionSendJobRecipientMigrationTest.kt | 94 + .../GeographicalRestrictionsTest.java | 47 - .../payments/GeographicalRestrictionsTest.kt | 37 + .../fcm/PushChallengeRequestTest.java | 109 -- .../fcm/PushChallengeRequestTest.kt | 104 ++ .../registration/v2/PinHashKbsDataTest.java | 77 - .../registration/v2/PinHashKbsDataTest.kt | 72 + .../v2/testdata/KbsTestVector.java | 63 - .../registration/v2/testdata/KbsTestVector.kt | 62 + 22 files changed, 2598 insertions(+), 2571 deletions(-) delete mode 100644 app/src/test/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.kt delete mode 100644 app/src/test/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducerTest.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducerTest.kt delete mode 100644 app/src/test/java/org/thoughtcrime/securesms/jobmanager/JobMigratorTest.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/jobmanager/JobMigratorTest.kt delete mode 100644 app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdFollowUpJobMigrationTest.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdFollowUpJobMigrationTest.kt delete mode 100644 app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdJobMigrationTest.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdJobMigrationTest.kt delete mode 100644 app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SendReadReceiptsJobMigrationTest.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SendReadReceiptsJobMigrationTest.kt delete mode 100644 app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SenderKeyDistributionSendJobRecipientMigrationTest.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SenderKeyDistributionSendJobRecipientMigrationTest.kt delete mode 100644 app/src/test/java/org/thoughtcrime/securesms/payments/GeographicalRestrictionsTest.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/payments/GeographicalRestrictionsTest.kt delete mode 100644 app/src/test/java/org/thoughtcrime/securesms/registration/fcm/PushChallengeRequestTest.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/registration/fcm/PushChallengeRequestTest.kt delete mode 100644 app/src/test/java/org/thoughtcrime/securesms/registration/v2/PinHashKbsDataTest.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/registration/v2/PinHashKbsDataTest.kt delete mode 100644 app/src/test/java/org/thoughtcrime/securesms/registration/v2/testdata/KbsTestVector.java create mode 100644 app/src/test/java/org/thoughtcrime/securesms/registration/v2/testdata/KbsTestVector.kt diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.java b/app/src/test/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.java deleted file mode 100644 index 309ebcb10d..0000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.thoughtcrime.securesms.database; - -import android.content.Context; -import android.database.Cursor; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView.ViewHolder; - -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class CursorRecyclerViewAdapterTest { - private CursorRecyclerViewAdapter adapter; - private Context context; - private Cursor cursor; - - @Before - public void setUp() { - context = mock(Context.class); - cursor = mock(Cursor.class); - when(cursor.getCount()).thenReturn(100); - when(cursor.moveToPosition(anyInt())).thenReturn(true); - - adapter = new CursorRecyclerViewAdapter(context, cursor) { - @Override - public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) { - return null; - } - - @Override - public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) { - } - }; - } - - @Test - public void testSanityCount() throws Exception { - assertEquals(adapter.getItemCount(), 100); - } - - @Test - public void testHeaderCount() throws Exception { - adapter.setHeaderView(new View(context)); - assertEquals(adapter.getItemCount(), 101); - - assertEquals(adapter.getItemViewType(0), CursorRecyclerViewAdapter.HEADER_TYPE); - assertNotEquals(adapter.getItemViewType(1), CursorRecyclerViewAdapter.HEADER_TYPE); - assertNotEquals(adapter.getItemViewType(100), CursorRecyclerViewAdapter.HEADER_TYPE); - } - - @Test - public void testFooterCount() throws Exception { - adapter.setFooterView(new View(context)); - assertEquals(adapter.getItemCount(), 101); - assertEquals(adapter.getItemViewType(100), CursorRecyclerViewAdapter.FOOTER_TYPE); - assertNotEquals(adapter.getItemViewType(0), CursorRecyclerViewAdapter.FOOTER_TYPE); - assertNotEquals(adapter.getItemViewType(99), CursorRecyclerViewAdapter.FOOTER_TYPE); - } - - @Test - public void testHeaderFooterCount() throws Exception { - adapter.setHeaderView(new View(context)); - adapter.setFooterView(new View(context)); - assertEquals(adapter.getItemCount(), 102); - assertEquals(adapter.getItemViewType(101), CursorRecyclerViewAdapter.FOOTER_TYPE); - assertEquals(adapter.getItemViewType(0), CursorRecyclerViewAdapter.HEADER_TYPE); - assertNotEquals(adapter.getItemViewType(1), CursorRecyclerViewAdapter.HEADER_TYPE); - assertNotEquals(adapter.getItemViewType(100), CursorRecyclerViewAdapter.FOOTER_TYPE); - } -} - diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.kt b/app/src/test/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.kt new file mode 100644 index 0000000000..f023cb90cb --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.kt @@ -0,0 +1,61 @@ +package org.thoughtcrime.securesms.database + +import android.content.Context +import android.database.Cursor +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.mockk.every +import io.mockk.mockk +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals +import org.junit.Test +import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter.FOOTER_TYPE +import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter.HEADER_TYPE + +class CursorRecyclerViewAdapterTest { + private val context: Context = mockk() + private val cursor: Cursor = mockk(relaxUnitFun = true) { + every { count } returns 100 + every { moveToPosition(any()) } returns true + } + private val adapter = object : CursorRecyclerViewAdapter(context, cursor) { + override fun onCreateItemViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder? = null + override fun onBindItemViewHolder(viewHolder: RecyclerView.ViewHolder?, cursor: Cursor) = Unit + } + + @Test + fun testSanityCount() { + assertEquals(100, adapter.itemCount) + } + + @Test + fun testHeaderCount() { + adapter.headerView = View(context) + assertEquals(101, adapter.itemCount) + + assertEquals(HEADER_TYPE, adapter.getItemViewType(0)) + assertNotEquals(HEADER_TYPE, adapter.getItemViewType(1)) + assertNotEquals(HEADER_TYPE, adapter.getItemViewType(100)) + } + + @Test + fun testFooterCount() { + adapter.setFooterView(View(context)) + assertEquals(101, adapter.itemCount) + assertEquals(FOOTER_TYPE, adapter.getItemViewType(100)) + assertNotEquals(FOOTER_TYPE, adapter.getItemViewType(0)) + assertNotEquals(FOOTER_TYPE, adapter.getItemViewType(99)) + } + + @Test + fun testHeaderFooterCount() { + adapter.headerView = View(context) + adapter.setFooterView(View(context)) + assertEquals(102, adapter.itemCount) + assertEquals(FOOTER_TYPE, adapter.getItemViewType(101)) + assertEquals(HEADER_TYPE, adapter.getItemViewType(0)) + assertNotEquals(HEADER_TYPE, adapter.getItemViewType(1)) + assertNotEquals(FOOTER_TYPE, adapter.getItemViewType(100)) + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducerTest.java b/app/src/test/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducerTest.java deleted file mode 100644 index 23594e3943..0000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducerTest.java +++ /dev/null @@ -1,1534 +0,0 @@ -package org.thoughtcrime.securesms.database.model; - -import android.app.Application; -import android.text.Spannable; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.test.core.app.ApplicationProvider; - -import com.annimon.stream.Stream; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; -import org.signal.storageservice.protos.groups.AccessControl; -import org.signal.storageservice.protos.groups.local.DecryptedGroup; -import org.signal.storageservice.protos.groups.local.DecryptedGroupChange; -import org.signal.storageservice.protos.groups.local.DecryptedMember; -import org.signal.storageservice.protos.groups.local.DecryptedPendingMember; -import org.thoughtcrime.securesms.backup.v2.proto.GroupChangeChatUpdate; -import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientId; -import org.whispersystems.signalservice.api.push.ServiceId; -import org.whispersystems.signalservice.api.push.ServiceId.ACI; -import org.whispersystems.signalservice.api.push.ServiceId.PNI; -import org.whispersystems.signalservice.api.push.ServiceIds; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.ListIterator; -import java.util.UUID; -import java.util.stream.Collectors; - -import kotlin.collections.CollectionsKt; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.signal.core.util.StringUtil.isolateBidi; -import static org.thoughtcrime.securesms.groups.v2.ChangeBuilder.changeBy; -import static org.thoughtcrime.securesms.groups.v2.ChangeBuilder.changeByUnknown; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; - -@RunWith(RobolectricTestRunner.class) -@Config(manifest = Config.NONE, application = Application.class) -public final class GroupsV2UpdateMessageProducerTest { - - private ACI you; - private ACI alice; - private ACI bob; - - private ServiceIds selfIds; - - private GroupsV2UpdateMessageProducer producer; - - @Rule - public MockitoRule rule = MockitoJUnit.rule(); - - @Mock - public MockedStatic recipientMockedStatic; - - @Mock - public MockedStatic recipientIdMockedStatic; - - @Before - public void setup() { - you = ACI.from(UUID.randomUUID()); - alice = ACI.from(UUID.randomUUID()); - bob = ACI.from(UUID.randomUUID()); - - selfIds = new ServiceIds(you, PNI.from(UUID.randomUUID())); - - recipientIdMockedStatic.when(() -> RecipientId.from(anyLong())).thenCallRealMethod(); - - RecipientId aliceId = RecipientId.from(1); - RecipientId bobId = RecipientId.from(2); - - Recipient aliceRecipient = recipientWithName(aliceId, "Alice"); - Recipient bobRecipient = recipientWithName(bobId, "Bob"); - - producer = new GroupsV2UpdateMessageProducer(ApplicationProvider.getApplicationContext(), selfIds, null); - - recipientIdMockedStatic.when(() -> RecipientId.from(alice)).thenReturn(aliceId); - recipientIdMockedStatic.when(() -> RecipientId.from(bob)).thenReturn(bobId); - recipientMockedStatic.when(() -> Recipient.resolved(aliceId)).thenReturn(aliceRecipient); - recipientMockedStatic.when(() -> Recipient.resolved(bobId)).thenReturn(bobRecipient); - } - - private static Recipient recipientWithName(RecipientId id, String name) { - Recipient recipient = mock(Recipient.class); - when(recipient.getId()).thenReturn(id); - when(recipient.getDisplayName(any())).thenReturn(name); - return recipient; - } - - @Test - public void empty_change() { - DecryptedGroupChange change = changeBy(alice) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice updated the group."))); - } - - @Test - public void empty_change_by_you() { - DecryptedGroupChange change = changeBy(you) - .build(); - - assertThat(describeChange(change), is(singletonList("You updated the group."))); - } - - @Test - public void empty_change_by_unknown() { - DecryptedGroupChange change = changeByUnknown() - .build(); - - assertThat(describeChange(change), is(singletonList("The group was updated."))); - } - - // Member additions - - @Test - public void member_added_member() { - DecryptedGroupChange change = changeBy(alice) - .addMember(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice added Bob."))); - } - - @Test - public void member_added_member_mentions_both() { - DecryptedGroupChange change = changeBy(alice) - .addMember(bob) - .build(); - - assertSingleChangeMentioning(change, Arrays.asList(alice, bob)); - } - - @Test - public void you_added_member() { - DecryptedGroupChange change = changeBy(you) - .addMember(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("You added Bob."))); - } - - @Test - public void you_added_member_mentions_just_member() { - DecryptedGroupChange change = changeBy(you) - .addMember(bob) - .build(); - - assertSingleChangeMentioning(change, singletonList(bob)); - } - - @Test - public void member_added_you() { - DecryptedGroupChange change = changeBy(alice) - .addMember(you) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice added you to the group."))); - } - - @Test - public void you_added_you() { - DecryptedGroupChange change = changeBy(you) - .addMember(you) - .build(); - - assertThat(describeChange(change), is(singletonList("You joined the group via the group link."))); - } - - @Test - public void member_added_themselves() { - DecryptedGroupChange change = changeBy(bob) - .addMember(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("Bob joined the group via the group link."))); - } - - @Test - public void member_added_themselves_mentions_just_member() { - DecryptedGroupChange change = changeBy(bob) - .addMember(bob) - .build(); - - assertSingleChangeMentioning(change, singletonList(bob)); - } - - @Test - public void unknown_added_you() { - DecryptedGroupChange change = changeByUnknown() - .addMember(you) - .build(); - - assertThat(describeChange(change), is(singletonList("You joined the group."))); - } - - @Test - public void unknown_added_member() { - DecryptedGroupChange change = changeByUnknown() - .addMember(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("Bob joined the group."))); - } - - @Test - public void member_added_you_and_another_where_you_are_not_first() { - DecryptedGroupChange change = changeBy(bob) - .addMember(alice) - .addMember(you) - .build(); - - assertThat(describeChange(change), is(Arrays.asList("Bob added you to the group.", "Bob added Alice."))); - } - - @Test - public void unknown_member_added_you_and_another_where_you_are_not_first() { - DecryptedGroupChange change = changeByUnknown() - .addMember(alice) - .addMember(you) - .build(); - - assertThat(describeChange(change), is(Arrays.asList("You joined the group.", "Alice joined the group."))); - } - - @Test - public void you_added_you_and_another_where_you_are_not_first() { - DecryptedGroupChange change = changeBy(you) - .addMember(alice) - .addMember(you) - .build(); - - assertThat(describeChange(change), is(Arrays.asList("You joined the group via the group link.", "You added Alice."))); - } - - // Member removals - @Test - public void member_removed_member() { - DecryptedGroupChange change = changeBy(alice) - .deleteMember(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice removed Bob."))); - } - - @Test - public void you_removed_member() { - DecryptedGroupChange change = changeBy(you) - .deleteMember(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("You removed Bob."))); - } - - @Test - public void member_removed_you() { - DecryptedGroupChange change = changeBy(alice) - .deleteMember(you) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice removed you from the group."))); - } - - @Test - public void you_removed_you() { - DecryptedGroupChange change = changeBy(you) - .deleteMember(you) - .build(); - - assertThat(describeChange(change), is(singletonList("You left the group."))); - } - - @Test - public void member_removed_themselves() { - DecryptedGroupChange change = changeBy(bob) - .deleteMember(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("Bob left the group."))); - } - - @Test - public void unknown_removed_member() { - DecryptedGroupChange change = changeByUnknown() - .deleteMember(alice) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice is no longer in the group."))); - } - - @Test - public void unknown_removed_you() { - DecryptedGroupChange change = changeByUnknown() - .deleteMember(you) - .build(); - - assertThat(describeChange(change), is(singletonList("You are no longer in the group."))); - } - - // Member role modifications - - @Test - public void you_make_member_admin() { - DecryptedGroupChange change = changeBy(you) - .promoteToAdmin(alice) - .build(); - - assertThat(describeChange(change), is(singletonList("You made Alice an admin."))); - } - - @Test - public void member_makes_member_admin() { - DecryptedGroupChange change = changeBy(bob) - .promoteToAdmin(alice) - .build(); - - assertThat(describeChange(change), is(singletonList("Bob made Alice an admin."))); - } - - @Test - public void member_makes_you_admin() { - DecryptedGroupChange change = changeBy(alice) - .promoteToAdmin(you) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice made you an admin."))); - } - - @Test - public void you_revoked_member_admin() { - DecryptedGroupChange change = changeBy(you) - .demoteToMember(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("You revoked admin privileges from Bob."))); - } - - @Test - public void member_revokes_member_admin() { - DecryptedGroupChange change = changeBy(bob) - .demoteToMember(alice) - .build(); - - assertThat(describeChange(change), is(singletonList("Bob revoked admin privileges from Alice."))); - } - - @Test - public void member_revokes_your_admin() { - DecryptedGroupChange change = changeBy(alice) - .demoteToMember(you) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice revoked your admin privileges."))); - } - - @Test - public void unknown_makes_member_admin() { - DecryptedGroupChange change = changeByUnknown() - .promoteToAdmin(alice) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice is now an admin."))); - } - - @Test - public void unknown_makes_you_admin() { - DecryptedGroupChange change = changeByUnknown() - .promoteToAdmin(you) - .build(); - - assertThat(describeChange(change), is(singletonList("You are now an admin."))); - } - - @Test - public void unknown_revokes_member_admin() { - DecryptedGroupChange change = changeByUnknown() - .demoteToMember(alice) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice is no longer an admin."))); - } - - @Test - public void unknown_revokes_your_admin() { - DecryptedGroupChange change = changeByUnknown() - .demoteToMember(you) - .build(); - - assertThat(describeChange(change), is(singletonList("You are no longer an admin."))); - } - - // Member invitation - - @Test - public void you_invited_member() { - DecryptedGroupChange change = changeBy(you) - .invite(alice) - .build(); - - assertThat(describeChange(change), is(singletonList("You invited Alice to the group."))); - } - - @Test - public void member_invited_you() { - DecryptedGroupChange change = changeBy(alice) - .invite(you) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice invited you to the group."))); - } - - @Test - public void member_invited_1_person() { - DecryptedGroupChange change = changeBy(alice) - .invite(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice invited 1 person to the group."))); - } - - @Test - public void member_invited_2_persons() { - DecryptedGroupChange change = changeBy(alice) - .invite(bob) - .invite(ACI.from(UUID.randomUUID())) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice invited 2 people to the group."))); - } - - @Test - public void member_invited_3_persons_and_you() { - DecryptedGroupChange change = changeBy(bob) - .invite(alice) - .invite(you) - .invite(ACI.from(UUID.randomUUID())) - .invite(ACI.from(UUID.randomUUID())) - .build(); - - assertThat(describeChange(change), is(Arrays.asList("Bob invited you to the group.", "Bob invited 3 people to the group."))); - } - - @Test - public void unknown_editor_but_known_invitee_invited_you() { - DecryptedGroupChange change = changeByUnknown() - .inviteBy(you, alice) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice invited you to the group."))); - } - - @Test - public void unknown_editor_and_unknown_inviter_invited_you() { - DecryptedGroupChange change = changeByUnknown() - .invite(you) - .build(); - - assertThat(describeChange(change), is(singletonList("You were invited to the group."))); - } - - @Test - public void unknown_invited_1_person() { - DecryptedGroupChange change = changeByUnknown() - .invite(alice) - .build(); - - assertThat(describeChange(change), is(singletonList("1 person was invited to the group."))); - } - - @Test - public void unknown_invited_2_persons() { - DecryptedGroupChange change = changeByUnknown() - .invite(alice) - .invite(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("2 people were invited to the group."))); - } - - @Test - public void unknown_invited_3_persons_and_you() { - DecryptedGroupChange change = changeByUnknown() - .invite(alice) - .invite(you) - .invite(ACI.from(UUID.randomUUID())) - .invite(ACI.from(UUID.randomUUID())) - .build(); - - assertThat(describeChange(change), is(Arrays.asList("You were invited to the group.", "3 people were invited to the group."))); - } - - @Test - public void unknown_editor_invited_3_persons_and_you_inviter_known() { - DecryptedGroupChange change = changeByUnknown() - .invite(alice) - .inviteBy(you, bob) - .invite(ACI.from(UUID.randomUUID())) - .invite(ACI.from(UUID.randomUUID())) - .build(); - - assertThat(describeChange(change), is(Arrays.asList("Bob invited you to the group.", "3 people were invited to the group."))); - } - - @Test - public void member_invited_3_persons_and_you_and_added_another_where_you_were_not_first() { - DecryptedGroupChange change = changeBy(bob) - .addMember(alice) - .invite(you) - .invite(ACI.from(UUID.randomUUID())) - .invite(ACI.from(UUID.randomUUID())) - .build(); - - assertThat(describeChange(change), is(Arrays.asList("Bob invited you to the group.", "Bob added Alice.", "Bob invited 2 people to the group."))); - } - - @Test - public void unknown_editor_but_known_invitee_invited_you_and_added_another_where_you_were_not_first() { - DecryptedGroupChange change = changeByUnknown() - .addMember(bob) - .inviteBy(you, alice) - .build(); - - assertThat(describeChange(change), is(Arrays.asList("Alice invited you to the group.", "Bob joined the group."))); - } - - @Test - public void unknown_editor_and_unknown_inviter_invited_you_and_added_another_where_you_were_not_first() { - DecryptedGroupChange change = changeByUnknown() - .addMember(alice) - .invite(you) - .build(); - - assertThat(describeChange(change), is(Arrays.asList("You were invited to the group.", "Alice joined the group."))); - } - - // Member invitation revocation - - @Test - public void member_uninvited_1_person() { - DecryptedGroupChange change = changeBy(alice) - .uninvite(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice revoked an invitation to the group."))); - } - - @Test - public void member_uninvited_2_people() { - DecryptedGroupChange change = changeBy(alice) - .uninvite(bob) - .uninvite(ACI.from(UUID.randomUUID())) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice revoked 2 invitations to the group."))); - } - - @Test - public void you_uninvited_1_person() { - DecryptedGroupChange change = changeBy(you) - .uninvite(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("You revoked an invitation to the group."))); - } - - @Test - public void you_uninvited_2_people() { - DecryptedGroupChange change = changeBy(you) - .uninvite(bob) - .uninvite(ACI.from(UUID.randomUUID())) - .build(); - - assertThat(describeChange(change), is(singletonList("You revoked 2 invitations to the group."))); - } - - @Test - public void pending_member_declines_invite() { - DecryptedGroupChange change = changeBy(bob) - .uninvite(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("Someone declined an invitation to the group."))); - } - - @Test - public void you_decline_invite() { - DecryptedGroupChange change = changeBy(you) - .uninvite(you) - .build(); - - assertThat(describeChange(change), is(singletonList("You declined the invitation to the group."))); - } - - @Test - public void unknown_revokes_your_invite() { - DecryptedGroupChange change = changeByUnknown() - .uninvite(you) - .build(); - - assertThat(describeChange(change), is(singletonList("An admin revoked your invitation to the group."))); - } - - @Test - public void unknown_revokes_1_invite() { - DecryptedGroupChange change = changeByUnknown() - .uninvite(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("An invitation to the group was revoked."))); - } - - @Test - public void unknown_revokes_2_invites() { - DecryptedGroupChange change = changeByUnknown() - .uninvite(bob) - .uninvite(ACI.from(UUID.randomUUID())) - .build(); - - assertThat(describeChange(change), is(singletonList("2 invitations to the group were revoked."))); - } - - @Test - public void unknown_revokes_yours_and_three_other_invites() { - DecryptedGroupChange change = changeByUnknown() - .uninvite(bob) - .uninvite(you) - .uninvite(ACI.from(UUID.randomUUID())) - .uninvite(ACI.from(UUID.randomUUID())) - .build(); - - assertThat(describeChange(change), is(Arrays.asList("An admin revoked your invitation to the group.", "3 invitations to the group were revoked."))); - } - - @Test - public void your_invite_was_revoked_by_known_member() { - DecryptedGroupChange change = changeBy(bob) - .uninvite(you) - .build(); - - assertThat(describeChange(change), is(singletonList("Bob revoked your invitation to the group."))); - } - - // Promote pending members - - @Test - public void member_accepts_invite() { - DecryptedGroupChange change = changeBy(bob) - .promote(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("Bob accepted an invitation to the group."))); - } - - @Test - public void you_accept_invite() { - DecryptedGroupChange change = changeBy(you) - .promote(you) - .build(); - - assertThat(describeChange(change), is(singletonList("You accepted the invitation to the group."))); - } - - @Test - public void member_promotes_pending_member() { - DecryptedGroupChange change = changeBy(bob) - .promote(alice) - .build(); - - assertThat(describeChange(change), is(singletonList("Bob added invited member Alice."))); - } - - @Test - public void you_promote_pending_member() { - DecryptedGroupChange change = changeBy(you) - .promote(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("You added invited member Bob."))); - } - - @Test - public void member_promotes_you() { - DecryptedGroupChange change = changeBy(bob) - .promote(you) - .build(); - - assertThat(describeChange(change), is(singletonList("Bob added you to the group."))); - } - - @Test - public void unknown_added_by_invite() { - DecryptedGroupChange change = changeByUnknown() - .promote(you) - .build(); - - assertThat(describeChange(change), is(singletonList("You joined the group."))); - } - - @Test - public void unknown_promotes_pending_member() { - DecryptedGroupChange change = changeByUnknown() - .promote(alice) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice joined the group."))); - } - - // Title change - - @Test - public void member_changes_title() { - DecryptedGroupChange change = changeBy(alice) - .title("New title") - .build(); - - assertThat(describeChange(change), is(singletonList("Alice changed the group name to \"" + isolateBidi("New title") + "\"."))); - } - - @Test - public void you_change_title() { - DecryptedGroupChange change = changeBy(you) - .title("Title 2") - .build(); - - assertThat(describeChange(change), is(singletonList("You changed the group name to \"" + isolateBidi("Title 2") + "\"."))); - } - - @Test - public void unknown_changed_title() { - DecryptedGroupChange change = changeByUnknown() - .title("Title 3") - .build(); - - assertThat(describeChange(change), is(singletonList("The group name has changed to \"" + isolateBidi("Title 3") + "\"."))); - } - - // Avatar change - - @Test - public void member_changes_avatar() { - DecryptedGroupChange change = changeBy(alice) - .avatar("Avatar1") - .build(); - - assertThat(describeChange(change), is(singletonList("Alice changed the group avatar."))); - } - - @Test - public void you_change_avatar() { - DecryptedGroupChange change = changeBy(you) - .avatar("Avatar2") - .build(); - - assertThat(describeChange(change), is(singletonList("You changed the group avatar."))); - } - - @Test - public void unknown_changed_avatar() { - DecryptedGroupChange change = changeByUnknown() - .avatar("Avatar3") - .build(); - - assertThat(describeChange(change), is(singletonList("The group avatar has been changed."))); - } - - // Timer change - - @Test - public void member_changes_timer() { - DecryptedGroupChange change = changeBy(bob) - .timer(10) - .build(); - - assertThat(describeChange(change), is(singletonList("Bob set the disappearing message timer to 10 seconds."))); - } - - @Test - public void you_change_timer() { - DecryptedGroupChange change = changeBy(you) - .timer(60) - .build(); - - assertThat(describeChange(change), is(singletonList("You set the disappearing message timer to 1 minute."))); - } - - @Test - public void unknown_change_timer() { - DecryptedGroupChange change = changeByUnknown() - .timer(120) - .build(); - - assertThat(describeChange(change), is(singletonList("The disappearing message timer has been set to 2 minutes."))); - } - - @Test - public void unknown_change_timer_mentions_no_one() { - DecryptedGroupChange change = changeByUnknown() - .timer(120) - .build(); - - assertSingleChangeMentioning(change, emptyList()); - } - - // Attribute access change - - @Test - public void member_changes_attribute_access() { - DecryptedGroupChange change = changeBy(bob) - .attributeAccess(AccessControl.AccessRequired.MEMBER) - .build(); - - assertThat(describeChange(change), is(singletonList("Bob changed who can edit group info to \"All members\"."))); - } - - @Test - public void you_changed_attribute_access() { - DecryptedGroupChange change = changeBy(you) - .attributeAccess(AccessControl.AccessRequired.ADMINISTRATOR) - .build(); - - assertThat(describeChange(change), is(singletonList("You changed who can edit group info to \"Only admins\"."))); - } - - @Test - public void unknown_changed_attribute_access() { - DecryptedGroupChange change = changeByUnknown() - .attributeAccess(AccessControl.AccessRequired.ADMINISTRATOR) - .build(); - - assertThat(describeChange(change), is(singletonList("Who can edit group info has been changed to \"Only admins\"."))); - } - - // Membership access change - - @Test - public void member_changes_membership_access() { - DecryptedGroupChange change = changeBy(alice) - .membershipAccess(AccessControl.AccessRequired.ADMINISTRATOR) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice changed who can edit group membership to \"Only admins\"."))); - } - - @Test - public void you_changed_membership_access() { - DecryptedGroupChange change = changeBy(you) - .membershipAccess(AccessControl.AccessRequired.MEMBER) - .build(); - - assertThat(describeChange(change), is(singletonList("You changed who can edit group membership to \"All members\"."))); - } - - @Test - public void unknown_changed_membership_access() { - DecryptedGroupChange change = changeByUnknown() - .membershipAccess(AccessControl.AccessRequired.ADMINISTRATOR) - .build(); - - assertThat(describeChange(change), is(singletonList("Who can edit group membership has been changed to \"Only admins\"."))); - } - - // Group link access change - - @Test - public void you_changed_group_link_access_to_any() { - DecryptedGroupChange change = changeBy(you) - .inviteLinkAccess(AccessControl.AccessRequired.ANY) - .build(); - - assertThat(describeChange(change), is(singletonList("You turned on the group link with admin approval off."))); - } - - @Test - public void you_changed_group_link_access_to_administrator_approval() { - DecryptedGroupChange change = changeBy(you) - .inviteLinkAccess(AccessControl.AccessRequired.ADMINISTRATOR) - .build(); - - assertThat(describeChange(change), is(singletonList("You turned on the group link with admin approval on."))); - } - - @Test - public void you_turned_off_group_link_access() { - DecryptedGroupChange change = changeBy(you) - .inviteLinkAccess(AccessControl.AccessRequired.UNSATISFIABLE) - .build(); - - assertThat(describeChange(change), is(singletonList("You turned off the group link."))); - } - - @Test - public void member_changed_group_link_access_to_any() { - DecryptedGroupChange change = changeBy(alice) - .inviteLinkAccess(AccessControl.AccessRequired.ANY) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice turned on the group link with admin approval off."))); - } - - @Test - public void member_changed_group_link_access_to_administrator_approval() { - DecryptedGroupChange change = changeBy(bob) - .inviteLinkAccess(AccessControl.AccessRequired.ADMINISTRATOR) - .build(); - - assertThat(describeChange(change), is(singletonList("Bob turned on the group link with admin approval on."))); - } - - @Test - public void member_turned_off_group_link_access() { - DecryptedGroupChange change = changeBy(alice) - .inviteLinkAccess(AccessControl.AccessRequired.UNSATISFIABLE) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice turned off the group link."))); - } - - @Test - public void unknown_changed_group_link_access_to_any() { - DecryptedGroupChange change = changeByUnknown() - .inviteLinkAccess(AccessControl.AccessRequired.ANY) - .build(); - - assertThat(describeChange(change), is(singletonList("The group link has been turned on with admin approval off."))); - } - - @Test - public void unknown_changed_group_link_access_to_administrator_approval() { - DecryptedGroupChange change = changeByUnknown() - .inviteLinkAccess(AccessControl.AccessRequired.ADMINISTRATOR) - .build(); - - assertThat(describeChange(change), is(singletonList("The group link has been turned on with admin approval on."))); - } - - @Test - public void unknown_turned_off_group_link_access() { - DecryptedGroupChange change = changeByUnknown() - .inviteLinkAccess(AccessControl.AccessRequired.UNSATISFIABLE) - .build(); - - assertThat(describeChange(change), is(singletonList("The group link has been turned off."))); - } - - // Group link with known previous group state - - @Test - public void group_link_access_from_unknown_to_administrator() { - assertEquals("You turned on the group link with admin approval on.", describeGroupLinkChange(you, AccessControl.AccessRequired.UNKNOWN, AccessControl.AccessRequired.ADMINISTRATOR)); - assertEquals("Alice turned on the group link with admin approval on.", describeGroupLinkChange(alice, AccessControl.AccessRequired.UNKNOWN, AccessControl.AccessRequired.ADMINISTRATOR)); - assertEquals("The group link has been turned on with admin approval on.", describeGroupLinkChange(null, AccessControl.AccessRequired.UNKNOWN, AccessControl.AccessRequired.ADMINISTRATOR)); - } - - @Test - public void group_link_access_from_administrator_to_unsatisfiable() { - assertEquals("You turned off the group link.", describeGroupLinkChange(you, AccessControl.AccessRequired.ADMINISTRATOR, AccessControl.AccessRequired.UNSATISFIABLE)); - assertEquals("Bob turned off the group link.", describeGroupLinkChange(bob, AccessControl.AccessRequired.ADMINISTRATOR, AccessControl.AccessRequired.UNSATISFIABLE)); - assertEquals("The group link has been turned off.", describeGroupLinkChange(null, AccessControl.AccessRequired.ADMINISTRATOR, AccessControl.AccessRequired.UNSATISFIABLE)); - } - - @Test - public void group_link_access_from_unsatisfiable_to_administrator() { - assertEquals("You turned on the group link with admin approval on.", describeGroupLinkChange(you, AccessControl.AccessRequired.UNSATISFIABLE, AccessControl.AccessRequired.ADMINISTRATOR)); - assertEquals("Alice turned on the group link with admin approval on.", describeGroupLinkChange(alice, AccessControl.AccessRequired.UNSATISFIABLE, AccessControl.AccessRequired.ADMINISTRATOR)); - assertEquals("The group link has been turned on with admin approval on.", describeGroupLinkChange(null, AccessControl.AccessRequired.UNSATISFIABLE, AccessControl.AccessRequired.ADMINISTRATOR)); - } - - @Test - public void group_link_access_from_administrator_to_any() { - assertEquals("You turned off admin approval for the group link.", describeGroupLinkChange(you, AccessControl.AccessRequired.ADMINISTRATOR, AccessControl.AccessRequired.ANY)); - assertEquals("Bob turned off admin approval for the group link.", describeGroupLinkChange(bob, AccessControl.AccessRequired.ADMINISTRATOR, AccessControl.AccessRequired.ANY)); - assertEquals("The admin approval for the group link has been turned off.", describeGroupLinkChange(null, AccessControl.AccessRequired.ADMINISTRATOR, AccessControl.AccessRequired.ANY)); - } - - @Test - public void group_link_access_from_any_to_administrator() { - assertEquals("You turned on admin approval for the group link.", describeGroupLinkChange(you, AccessControl.AccessRequired.ANY, AccessControl.AccessRequired.ADMINISTRATOR)); - assertEquals("Bob turned on admin approval for the group link.", describeGroupLinkChange(bob, AccessControl.AccessRequired.ANY, AccessControl.AccessRequired.ADMINISTRATOR)); - assertEquals("The admin approval for the group link has been turned on.", describeGroupLinkChange(null, AccessControl.AccessRequired.ANY, AccessControl.AccessRequired.ADMINISTRATOR)); - } - - private String describeGroupLinkChange(@Nullable ACI editor, @NonNull AccessControl.AccessRequired fromAccess, AccessControl.AccessRequired toAccess){ - DecryptedGroup previousGroupState = new DecryptedGroup.Builder() - .accessControl(new AccessControl.Builder() - .addFromInviteLink(fromAccess) - .build()) - .build(); - DecryptedGroupChange change = (editor != null ? changeBy(editor) : changeByUnknown()).inviteLinkAccess(toAccess) - .build(); - - List strings = describeChange(previousGroupState, change); - assertEquals(1, strings.size()); - return strings.get(0); - } - - // Group link reset - - @Test - public void you_reset_group_link() { - DecryptedGroupChange change = changeBy(you) - .resetGroupLink() - .build(); - - assertThat(describeChange(change), is(singletonList("You reset the group link."))); - } - - @Test - public void member_reset_group_link() { - DecryptedGroupChange change = changeBy(alice) - .resetGroupLink() - .build(); - - assertThat(describeChange(change), is(singletonList("Alice reset the group link."))); - } - - @Test - public void unknown_reset_group_link() { - DecryptedGroupChange change = changeByUnknown() - .resetGroupLink() - .build(); - - assertThat(describeChange(change), is(singletonList("The group link has been reset."))); - } - - /** - * When the group link is turned on and reset in the same change, assume this is the first time - * the link password it being set and do not show reset message. - */ - @Test - public void member_changed_group_link_access_to_on_and_reset() { - DecryptedGroupChange change = changeBy(alice) - .inviteLinkAccess(AccessControl.AccessRequired.ANY) - .resetGroupLink() - .build(); - - assertThat(describeChange(change), is(singletonList("Alice turned on the group link with admin approval off."))); - } - - /** - * When the group link is turned on and reset in the same change, assume this is the first time - * the link password it being set and do not show reset message. - */ - @Test - public void you_changed_group_link_access_to_on_and_reset() { - DecryptedGroupChange change = changeBy(you) - .inviteLinkAccess(AccessControl.AccessRequired.ADMINISTRATOR) - .resetGroupLink() - .build(); - - assertThat(describeChange(change), is(singletonList("You turned on the group link with admin approval on."))); - } - - @Test - public void you_changed_group_link_access_to_off_and_reset() { - DecryptedGroupChange change = changeBy(you) - .inviteLinkAccess(AccessControl.AccessRequired.UNSATISFIABLE) - .resetGroupLink() - .build(); - - assertThat(describeChange(change), is(Arrays.asList("You turned off the group link.", "You reset the group link."))); - } - - // Group link request - - @Test - public void you_requested_to_join_the_group() { - DecryptedGroupChange change = changeBy(you) - .requestJoin() - .build(); - - assertThat(describeChange(change), is(singletonList("You sent a request to join the group."))); - } - - @Test - public void member_requested_to_join_the_group() { - DecryptedGroupChange change = changeBy(bob) - .requestJoin() - .build(); - - assertThat(describeChange(change), is(singletonList("Bob requested to join via the group link."))); - } - - @Test - public void unknown_requested_to_join_the_group() { - DecryptedGroupChange change = changeByUnknown() - .requestJoin(alice) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice requested to join via the group link."))); - } - - @Test - public void member_approved_your_join_request() { - DecryptedGroupChange change = changeBy(bob) - .approveRequest(you) - .build(); - - assertThat(describeChange(change), is(singletonList("Bob approved your request to join the group."))); - } - - @Test - public void member_approved_another_join_request() { - DecryptedGroupChange change = changeBy(alice) - .approveRequest(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice approved a request to join the group from Bob."))); - } - - @Test - public void you_approved_another_join_request() { - DecryptedGroupChange change = changeBy(you) - .approveRequest(alice) - .build(); - - assertThat(describeChange(change), is(singletonList("You approved a request to join the group from Alice."))); - } - - @Test - public void unknown_approved_your_join_request() { - DecryptedGroupChange change = changeByUnknown() - .approveRequest(you) - .build(); - - assertThat(describeChange(change), is(singletonList("Your request to join the group has been approved."))); - } - - @Test - public void unknown_approved_another_join_request() { - DecryptedGroupChange change = changeByUnknown() - .approveRequest(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("A request to join the group from Bob has been approved."))); - } - - @Test - public void member_denied_another_join_request() { - DecryptedGroupChange change = changeBy(alice) - .denyRequest(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice denied a request to join the group from Bob."))); - } - - @Test - public void member_denied_your_join_request() { - DecryptedGroupChange change = changeBy(alice) - .denyRequest(you) - .build(); - - assertThat(describeChange(change), is(singletonList("Your request to join the group has been denied by an admin."))); - } - - @Test - public void you_cancelled_your_join_request() { - DecryptedGroupChange change = changeBy(you) - .denyRequest(you) - .build(); - - assertThat(describeChange(change), is(singletonList("You canceled your request to join the group."))); - } - - @Test - public void member_cancelled_their_join_request() { - DecryptedGroupChange change = changeBy(alice) - .denyRequest(alice) - .build(); - - assertThat(describeChange(change), is(singletonList("Alice canceled their request to join the group."))); - } - - @Test - public void unknown_denied_your_join_request() { - DecryptedGroupChange change = changeByUnknown() - .denyRequest(you) - .build(); - - assertThat(describeChange(change), is(singletonList("Your request to join the group has been denied by an admin."))); - } - - @Test - public void unknown_denied_another_join_request() { - DecryptedGroupChange change = changeByUnknown() - .denyRequest(bob) - .build(); - - assertThat(describeChange(change), is(singletonList("A request to join the group from Bob has been denied."))); - } - - // Multiple changes - - @Test - public void multiple_changes() { - DecryptedGroupChange change = changeBy(alice) - .addMember(bob) - .membershipAccess(AccessControl.AccessRequired.MEMBER) - .title("Title") - .addMember(you) - .timer(300) - .build(); - - assertThat(describeChange(change), is(Arrays.asList( - "Alice added you to the group.", - "Alice added Bob.", - "Alice changed the group name to \"" + isolateBidi("Title") + "\".", - "Alice set the disappearing message timer to 5 minutes.", - "Alice changed who can edit group membership to \"All members\"."))); - } - - @Test - public void multiple_changes_leave_and_promote() { - DecryptedGroupChange change = changeBy(alice) - .deleteMember(alice) - .promoteToAdmin(bob) - .build(); - - assertThat(describeChange(change), is(Arrays.asList( - "Alice made Bob an admin.", - "Alice left the group."))); - } - - @Test - public void multiple_changes_leave_and_promote_by_unknown() { - DecryptedGroupChange change = changeByUnknown() - .deleteMember(alice) - .promoteToAdmin(bob) - .build(); - - assertThat(describeChange(change), is(Arrays.asList( - "Bob is now an admin.", - "Alice is no longer in the group."))); - } - - @Test - public void multiple_changes_by_unknown() { - DecryptedGroupChange change = changeByUnknown() - .addMember(bob) - .membershipAccess(AccessControl.AccessRequired.MEMBER) - .title("Title 2") - .avatar("Avatar 1") - .timer(600) - .build(); - - assertThat(describeChange(change), is(Arrays.asList( - "Bob joined the group.", - "The group name has changed to \"" + isolateBidi("Title 2") + "\".", - "The group avatar has been changed.", - "The disappearing message timer has been set to 10 minutes.", - "Who can edit group membership has been changed to \"All members\"."))); - } - - @Test - public void multiple_changes_join_and_leave_by_unknown() { - DecryptedGroupChange change = changeByUnknown() - .addMember(alice) - .promoteToAdmin(alice) - .deleteMember(alice) - .title("Updated title") - .build(); - - assertThat(describeChange(change), is(Arrays.asList( - "Alice joined the group.", - "Alice is now an admin.", - "The group name has changed to \"" + isolateBidi("Updated title") + "\".", - "Alice is no longer in the group."))); - } - - // Group state without a change record - - @Test - public void you_created_a_group_change_not_found() { - DecryptedGroup group = newGroupBy(you, 0) - .build(); - - assertThat(describeNewGroup(group), is("You joined the group.")); - } - - @Test - public void you_created_a_group() { - DecryptedGroup group = newGroupBy(you, 0) - .build(); - - DecryptedGroupChange change = changeBy(you) - .addMember(alice) - .addMember(you) - .addMember(bob) - .title("New title") - .build(); - - assertThat(describeNewGroup(group, change), is("You created the group.")); - } - - @Test - public void alice_created_a_group_change_not_found() { - DecryptedGroup group = newGroupBy(alice, 0) - .member(you) - .build(); - - assertThat(describeNewGroup(group), is("You joined the group.")); - } - - @Test - public void alice_created_a_group() { - DecryptedGroup group = newGroupBy(alice, 0) - .member(you) - .build(); - - DecryptedGroupChange change = changeBy(alice) - .addMember(you) - .addMember(alice) - .addMember(bob) - .title("New title") - .build(); - - assertThat(describeNewGroup(group, change), is("Alice added you to the group.")); - } - - @Test - public void alice_created_a_group_above_zero() { - DecryptedGroup group = newGroupBy(alice, 1) - .member(you) - .build(); - - assertThat(describeNewGroup(group), is("You joined the group.")); - } - - @Test - public void you_were_invited_to_a_group() { - DecryptedGroup group = newGroupBy(alice, 0) - .invite(bob, you) - .build(); - - assertThat(describeNewGroup(group), is("Bob invited you to the group.")); - } - - @Test - public void describe_a_group_you_are_not_in() { - DecryptedGroup group = newGroupBy(alice, 1) - .build(); - - assertThat(describeNewGroup(group), is("Group updated.")); - } - - @Test - public void makeRecipientsClickable_onePlaceholder() { - RecipientId id = RecipientId.from(1); - - Spannable result = GroupsV2UpdateMessageProducer.makeRecipientsClickable( - ApplicationProvider.getApplicationContext(), - GroupsV2UpdateMessageProducer.makePlaceholder(id), - Collections.singletonList(id), - null - ); - - assertEquals("Alice", result.toString()); - } - - @Test - public void makeRecipientsClickable_twoPlaceholders_sameRecipient() { - RecipientId id = RecipientId.from(1); - String placeholder = GroupsV2UpdateMessageProducer.makePlaceholder(id); - - Spannable result = GroupsV2UpdateMessageProducer.makeRecipientsClickable( - ApplicationProvider.getApplicationContext(), - placeholder + " " + placeholder, - Collections.singletonList(id), - null - ); - - assertEquals("Alice Alice", result.toString()); - } - - @Test - public void makeRecipientsClickable_twoPlaceholders_differentRecipient() { - RecipientId id1 = RecipientId.from(1); - RecipientId id2 = RecipientId.from(2); - - String placeholder1 = GroupsV2UpdateMessageProducer.makePlaceholder(id1); - String placeholder2 = GroupsV2UpdateMessageProducer.makePlaceholder(id2); - - Spannable result = GroupsV2UpdateMessageProducer.makeRecipientsClickable( - ApplicationProvider.getApplicationContext(), - placeholder1 + " " + placeholder2, - Arrays.asList(id1, id2), - null - ); - - assertEquals("Alice Bob", result.toString()); - } - - @Test - public void makeRecipientsClickable_complicated() { - RecipientId id1 = RecipientId.from(1); - RecipientId id2 = RecipientId.from(2); - - String placeholder1 = GroupsV2UpdateMessageProducer.makePlaceholder(id1); - String placeholder2 = GroupsV2UpdateMessageProducer.makePlaceholder(id2); - - Spannable result = GroupsV2UpdateMessageProducer.makeRecipientsClickable( - ApplicationProvider.getApplicationContext(), - placeholder1 + " said hello to " + placeholder2 + ", and " + placeholder2 + " said hello back to " + placeholder1 + ".", - Arrays.asList(id1, id2), - null - ); - - assertEquals("Alice said hello to Bob, and Bob said hello back to Alice.", result.toString()); - } - - private @NonNull String describeConvertedNewGroup(@NonNull DecryptedGroup groupState, @NonNull DecryptedGroupChange groupChange) { - GroupChangeChatUpdate update = GroupsV2UpdateMessageConverter.translateDecryptedChangeNewGroup(selfIds, new DecryptedGroupV2Context.Builder() - .change(groupChange) - .groupState(groupState) - .build()); - - return producer.describeChanges(update.updates).get(0).getSpannable().toString(); - } - - private @NonNull List describeConvertedChange(@Nullable DecryptedGroup previousGroupState, @NonNull DecryptedGroupChange change) { - GroupChangeChatUpdate update = GroupsV2UpdateMessageConverter.translateDecryptedChangeUpdate(selfIds, new DecryptedGroupV2Context.Builder() - .change(change) - .previousGroupState(previousGroupState) - .build()); - - return Stream.of(producer.describeChanges(update.updates)) - .map(UpdateDescription::getSpannable) - .map(Spannable::toString) - .toList(); - } - - private @NonNull List describeChange(@NonNull DecryptedGroupChange change) { - return describeChange(null, change); - } - - private @NonNull List describeChange(@Nullable DecryptedGroup previousGroupState, - @NonNull DecryptedGroupChange change) - { - List convertedChange = describeConvertedChange(previousGroupState, change); - List describedChange = Stream.of(producer.describeChanges(previousGroupState, change)) - .map(UpdateDescription::getSpannable) - .map(Spannable::toString) - .toList(); - assertEquals(describedChange.size(), convertedChange.size()); - - ListIterator convertedIterator = convertedChange.listIterator(); - ListIterator describedIterator = describedChange.listIterator(); - - while (convertedIterator.hasNext()) { - assertEquals(describedIterator.next(), convertedIterator.next()); - } - return describedChange; - } - - private @NonNull String describeNewGroup(@NonNull DecryptedGroup group) { - return describeNewGroup(group, new DecryptedGroupChange()); - } - - private @NonNull String describeNewGroup(@NonNull DecryptedGroup group, @NonNull DecryptedGroupChange groupChange) { - String newGroupString = producer.describeNewGroup(group, groupChange).getSpannable().toString(); - String convertedGroupString = describeConvertedNewGroup(group, groupChange); - - assertEquals(newGroupString, convertedGroupString); - - return newGroupString; - } - - private static GroupStateBuilder newGroupBy(ACI foundingMember, int revision) { - return new GroupStateBuilder(foundingMember, revision); - } - - private void assertSingleChangeMentioning(DecryptedGroupChange change, List expectedMentions) { - List expectedMentionSids = expectedMentions.stream().collect(Collectors.toList()); - - List changes = producer.describeChanges(null, change); - - assertThat(changes.size(), is(1)); - - UpdateDescription description = changes.get(0); - assertThat(description.getMentioned(), is(expectedMentionSids)); - - if (expectedMentions.isEmpty()) { - assertTrue(description.isStringStatic()); - } else { - assertFalse(description.isStringStatic()); - } - } - - private static class GroupStateBuilder { - - private final DecryptedGroup.Builder builder; - - GroupStateBuilder(@NonNull ACI foundingMember, int revision) { - builder = new DecryptedGroup.Builder() - .revision(revision) - .members(Collections.singletonList(new DecryptedMember.Builder().aciBytes(foundingMember.toByteString()).build())); - } - - GroupStateBuilder invite(@NonNull ACI inviter, @NonNull ServiceId invitee) { - builder.pendingMembers(CollectionsKt.plus(builder.pendingMembers, new DecryptedPendingMember.Builder().serviceIdBytes(invitee.toByteString()).addedByAci(inviter.toByteString()).build())); - return this; - } - - GroupStateBuilder member(@NonNull ACI member) { - builder.members(CollectionsKt.plus(builder.members, new DecryptedMember.Builder().aciBytes(member.toByteString()).build())); - return this; - } - - public DecryptedGroup build() { - return builder.build(); - } - } -} diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducerTest.kt b/app/src/test/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducerTest.kt new file mode 100644 index 0000000000..0893188785 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducerTest.kt @@ -0,0 +1,1512 @@ +package org.thoughtcrime.securesms.database.model + +import android.app.Application +import androidx.test.core.app.ApplicationProvider +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.mockkStatic +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.signal.core.util.StringUtil +import org.signal.storageservice.protos.groups.AccessControl +import org.signal.storageservice.protos.groups.AccessControl.AccessRequired +import org.signal.storageservice.protos.groups.AccessControl.AccessRequired.ADMINISTRATOR +import org.signal.storageservice.protos.groups.AccessControl.AccessRequired.ANY +import org.signal.storageservice.protos.groups.AccessControl.AccessRequired.MEMBER +import org.signal.storageservice.protos.groups.AccessControl.AccessRequired.UNKNOWN +import org.signal.storageservice.protos.groups.AccessControl.AccessRequired.UNSATISFIABLE +import org.signal.storageservice.protos.groups.local.DecryptedGroup +import org.signal.storageservice.protos.groups.local.DecryptedGroupChange +import org.signal.storageservice.protos.groups.local.DecryptedMember +import org.signal.storageservice.protos.groups.local.DecryptedPendingMember +import org.thoughtcrime.securesms.database.model.GroupsV2UpdateMessageConverter.translateDecryptedChangeNewGroup +import org.thoughtcrime.securesms.database.model.GroupsV2UpdateMessageConverter.translateDecryptedChangeUpdate +import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context +import org.thoughtcrime.securesms.groups.v2.ChangeBuilder +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.Recipient.Companion.resolved +import org.thoughtcrime.securesms.recipients.RecipientId +import org.whispersystems.signalservice.api.push.ServiceId +import org.whispersystems.signalservice.api.push.ServiceId.ACI +import org.whispersystems.signalservice.api.push.ServiceId.PNI +import org.whispersystems.signalservice.api.push.ServiceIds +import java.util.UUID + +@RunWith(RobolectricTestRunner::class) +@Config(manifest = Config.NONE, application = Application::class) +class GroupsV2UpdateMessageProducerTest { + private val you = ACI.from(UUID.randomUUID()) + private val alice = ACI.from(UUID.randomUUID()) + private val bob = ACI.from(UUID.randomUUID()) + private val selfIds = ServiceIds(you, PNI.from(UUID.randomUUID())) + private val producer = GroupsV2UpdateMessageProducer(ApplicationProvider.getApplicationContext(), selfIds, null) + + @Before + fun setup() { + mockkStatic(RecipientId::class) + val aliceId = RecipientId.from(1) + val bobId = RecipientId.from(2) + every { RecipientId.from(alice) } returns aliceId + every { RecipientId.from(bob) } returns bobId + + mockkObject(Recipient.Companion) + val aliceRecipient = recipientWithName(aliceId, "Alice") + val bobRecipient = recipientWithName(bobId, "Bob") + every { resolved(aliceId) } returns aliceRecipient + every { resolved(bobId) } returns bobRecipient + } + + @Test + fun empty_change() { + val change = ChangeBuilder.changeBy(alice) + .build() + + assertEquals(listOf("Alice updated the group."), describeChange(change)) + } + + @Test + fun empty_change_by_you() { + val change = ChangeBuilder.changeBy(you) + .build() + + assertEquals(listOf("You updated the group."), describeChange(change)) + } + + @Test + fun empty_change_by_unknown() { + val change = ChangeBuilder.changeByUnknown() + .build() + + assertEquals(listOf("The group was updated."), describeChange(change)) + } + + // Member additions + @Test + fun member_added_member() { + val change = ChangeBuilder.changeBy(alice) + .addMember(bob) + .build() + + assertEquals(listOf("Alice added Bob."), describeChange(change)) + } + + @Test + fun member_added_member_mentions_both() { + val change = ChangeBuilder.changeBy(alice) + .addMember(bob) + .build() + + assertSingleChangeMentioning(change, listOf(alice, bob)) + } + + @Test + fun you_added_member() { + val change = ChangeBuilder.changeBy(you) + .addMember(bob) + .build() + + assertEquals(listOf("You added Bob."), describeChange(change)) + } + + @Test + fun you_added_member_mentions_just_member() { + val change = ChangeBuilder.changeBy(you) + .addMember(bob) + .build() + + assertSingleChangeMentioning(change, listOf(bob)) + } + + @Test + fun member_added_you() { + val change = ChangeBuilder.changeBy(alice) + .addMember(you) + .build() + + assertEquals(listOf("Alice added you to the group."), describeChange(change)) + } + + @Test + fun you_added_you() { + val change = ChangeBuilder.changeBy(you) + .addMember(you) + .build() + + assertEquals(listOf("You joined the group via the group link."), describeChange(change)) + } + + @Test + fun member_added_themselves() { + val change = ChangeBuilder.changeBy(bob) + .addMember(bob) + .build() + + assertEquals(listOf("Bob joined the group via the group link."), describeChange(change)) + } + + @Test + fun member_added_themselves_mentions_just_member() { + val change = ChangeBuilder.changeBy(bob) + .addMember(bob) + .build() + + assertSingleChangeMentioning(change, listOf(bob)) + } + + @Test + fun unknown_added_you() { + val change = ChangeBuilder.changeByUnknown() + .addMember(you) + .build() + + assertEquals(listOf("You joined the group."), describeChange(change)) + } + + @Test + fun unknown_added_member() { + val change = ChangeBuilder.changeByUnknown() + .addMember(bob) + .build() + + assertEquals(listOf("Bob joined the group."), describeChange(change)) + } + + @Test + fun member_added_you_and_another_where_you_are_not_first() { + val change = ChangeBuilder.changeBy(bob) + .addMember(alice) + .addMember(you) + .build() + + assertEquals(listOf("Bob added you to the group.", "Bob added Alice."), describeChange(change)) + } + + @Test + fun unknown_member_added_you_and_another_where_you_are_not_first() { + val change = ChangeBuilder.changeByUnknown() + .addMember(alice) + .addMember(you) + .build() + + assertEquals(listOf("You joined the group.", "Alice joined the group."), describeChange(change)) + } + + @Test + fun you_added_you_and_another_where_you_are_not_first() { + val change = ChangeBuilder.changeBy(you) + .addMember(alice) + .addMember(you) + .build() + + assertEquals(listOf("You joined the group via the group link.", "You added Alice."), describeChange(change)) + } + + // Member removals + @Test + fun member_removed_member() { + val change = ChangeBuilder.changeBy(alice) + .deleteMember(bob) + .build() + + assertEquals(listOf("Alice removed Bob."), describeChange(change)) + } + + @Test + fun you_removed_member() { + val change = ChangeBuilder.changeBy(you) + .deleteMember(bob) + .build() + + assertEquals(listOf("You removed Bob."), describeChange(change)) + } + + @Test + fun member_removed_you() { + val change = ChangeBuilder.changeBy(alice) + .deleteMember(you) + .build() + + assertEquals(listOf("Alice removed you from the group."), describeChange(change)) + } + + @Test + fun you_removed_you() { + val change = ChangeBuilder.changeBy(you) + .deleteMember(you) + .build() + + assertEquals(listOf("You left the group."), describeChange(change)) + } + + @Test + fun member_removed_themselves() { + val change = ChangeBuilder.changeBy(bob) + .deleteMember(bob) + .build() + + assertEquals(listOf("Bob left the group."), describeChange(change)) + } + + @Test + fun unknown_removed_member() { + val change = ChangeBuilder.changeByUnknown() + .deleteMember(alice) + .build() + + assertEquals(listOf("Alice is no longer in the group."), describeChange(change)) + } + + @Test + fun unknown_removed_you() { + val change = ChangeBuilder.changeByUnknown() + .deleteMember(you) + .build() + + assertEquals(listOf("You are no longer in the group."), describeChange(change)) + } + + // Member role modifications + @Test + fun you_make_member_admin() { + val change = ChangeBuilder.changeBy(you) + .promoteToAdmin(alice) + .build() + + assertEquals(listOf("You made Alice an admin."), describeChange(change)) + } + + @Test + fun member_makes_member_admin() { + val change = ChangeBuilder.changeBy(bob) + .promoteToAdmin(alice) + .build() + + assertEquals(listOf("Bob made Alice an admin."), describeChange(change)) + } + + @Test + fun member_makes_you_admin() { + val change = ChangeBuilder.changeBy(alice) + .promoteToAdmin(you) + .build() + + assertEquals(listOf("Alice made you an admin."), describeChange(change)) + } + + @Test + fun you_revoked_member_admin() { + val change = ChangeBuilder.changeBy(you) + .demoteToMember(bob) + .build() + + assertEquals(listOf("You revoked admin privileges from Bob."), describeChange(change)) + } + + @Test + fun member_revokes_member_admin() { + val change = ChangeBuilder.changeBy(bob) + .demoteToMember(alice) + .build() + + assertEquals(listOf("Bob revoked admin privileges from Alice."), describeChange(change)) + } + + @Test + fun member_revokes_your_admin() { + val change = ChangeBuilder.changeBy(alice) + .demoteToMember(you) + .build() + + assertEquals(listOf("Alice revoked your admin privileges."), describeChange(change)) + } + + @Test + fun unknown_makes_member_admin() { + val change = ChangeBuilder.changeByUnknown() + .promoteToAdmin(alice) + .build() + + assertEquals(listOf("Alice is now an admin."), describeChange(change)) + } + + @Test + fun unknown_makes_you_admin() { + val change = ChangeBuilder.changeByUnknown() + .promoteToAdmin(you) + .build() + + assertEquals(listOf("You are now an admin."), describeChange(change)) + } + + @Test + fun unknown_revokes_member_admin() { + val change = ChangeBuilder.changeByUnknown() + .demoteToMember(alice) + .build() + + assertEquals(listOf("Alice is no longer an admin."), describeChange(change)) + } + + @Test + fun unknown_revokes_your_admin() { + val change = ChangeBuilder.changeByUnknown() + .demoteToMember(you) + .build() + + assertEquals(listOf("You are no longer an admin."), describeChange(change)) + } + + // Member invitation + @Test + fun you_invited_member() { + val change = ChangeBuilder.changeBy(you) + .invite(alice) + .build() + + assertEquals(listOf("You invited Alice to the group."), describeChange(change)) + } + + @Test + fun member_invited_you() { + val change = ChangeBuilder.changeBy(alice) + .invite(you) + .build() + + assertEquals(listOf("Alice invited you to the group."), describeChange(change)) + } + + @Test + fun member_invited_1_person() { + val change = ChangeBuilder.changeBy(alice) + .invite(bob) + .build() + + assertEquals(listOf("Alice invited 1 person to the group."), describeChange(change)) + } + + @Test + fun member_invited_2_persons() { + val change = ChangeBuilder.changeBy(alice) + .invite(bob) + .invite(ACI.from(UUID.randomUUID())) + .build() + + assertEquals(listOf("Alice invited 2 people to the group."), describeChange(change)) + } + + @Test + fun member_invited_3_persons_and_you() { + val change = ChangeBuilder.changeBy(bob) + .invite(alice) + .invite(you) + .invite(ACI.from(UUID.randomUUID())) + .invite(ACI.from(UUID.randomUUID())) + .build() + + assertEquals(listOf("Bob invited you to the group.", "Bob invited 3 people to the group."), describeChange(change)) + } + + @Test + fun unknown_editor_but_known_invitee_invited_you() { + val change = ChangeBuilder.changeByUnknown() + .inviteBy(you, alice) + .build() + + assertEquals(listOf("Alice invited you to the group."), describeChange(change)) + } + + @Test + fun unknown_editor_and_unknown_inviter_invited_you() { + val change = ChangeBuilder.changeByUnknown() + .invite(you) + .build() + + assertEquals(listOf("You were invited to the group."), describeChange(change)) + } + + @Test + fun unknown_invited_1_person() { + val change = ChangeBuilder.changeByUnknown() + .invite(alice) + .build() + + assertEquals(listOf("1 person was invited to the group."), describeChange(change)) + } + + @Test + fun unknown_invited_2_persons() { + val change = ChangeBuilder.changeByUnknown() + .invite(alice) + .invite(bob) + .build() + + assertEquals(listOf("2 people were invited to the group."), describeChange(change)) + } + + @Test + fun unknown_invited_3_persons_and_you() { + val change = ChangeBuilder.changeByUnknown() + .invite(alice) + .invite(you) + .invite(ACI.from(UUID.randomUUID())) + .invite(ACI.from(UUID.randomUUID())) + .build() + + assertEquals(listOf("You were invited to the group.", "3 people were invited to the group."), describeChange(change)) + } + + @Test + fun unknown_editor_invited_3_persons_and_you_inviter_known() { + val change = ChangeBuilder.changeByUnknown() + .invite(alice) + .inviteBy(you, bob) + .invite(ACI.from(UUID.randomUUID())) + .invite(ACI.from(UUID.randomUUID())) + .build() + + assertEquals(listOf("Bob invited you to the group.", "3 people were invited to the group."), describeChange(change)) + } + + @Test + fun member_invited_3_persons_and_you_and_added_another_where_you_were_not_first() { + val change = ChangeBuilder.changeBy(bob) + .addMember(alice) + .invite(you) + .invite(ACI.from(UUID.randomUUID())) + .invite(ACI.from(UUID.randomUUID())) + .build() + + assertEquals(listOf("Bob invited you to the group.", "Bob added Alice.", "Bob invited 2 people to the group."), describeChange(change)) + } + + @Test + fun unknown_editor_but_known_invitee_invited_you_and_added_another_where_you_were_not_first() { + val change = ChangeBuilder.changeByUnknown() + .addMember(bob) + .inviteBy(you, alice) + .build() + + assertEquals(listOf("Alice invited you to the group.", "Bob joined the group."), describeChange(change)) + } + + @Test + fun unknown_editor_and_unknown_inviter_invited_you_and_added_another_where_you_were_not_first() { + val change = ChangeBuilder.changeByUnknown() + .addMember(alice) + .invite(you) + .build() + + assertEquals(listOf("You were invited to the group.", "Alice joined the group."), describeChange(change)) + } + + // Member invitation revocation + @Test + fun member_uninvited_1_person() { + val change = ChangeBuilder.changeBy(alice) + .uninvite(bob) + .build() + + assertEquals(listOf("Alice revoked an invitation to the group."), describeChange(change)) + } + + @Test + fun member_uninvited_2_people() { + val change = ChangeBuilder.changeBy(alice) + .uninvite(bob) + .uninvite(ACI.from(UUID.randomUUID())) + .build() + + assertEquals(listOf("Alice revoked 2 invitations to the group."), describeChange(change)) + } + + @Test + fun you_uninvited_1_person() { + val change = ChangeBuilder.changeBy(you) + .uninvite(bob) + .build() + + assertEquals(listOf("You revoked an invitation to the group."), describeChange(change)) + } + + @Test + fun you_uninvited_2_people() { + val change = ChangeBuilder.changeBy(you) + .uninvite(bob) + .uninvite(ACI.from(UUID.randomUUID())) + .build() + + assertEquals(listOf("You revoked 2 invitations to the group."), describeChange(change)) + } + + @Test + fun pending_member_declines_invite() { + val change = ChangeBuilder.changeBy(bob) + .uninvite(bob) + .build() + + assertEquals(listOf("Someone declined an invitation to the group."), describeChange(change)) + } + + @Test + fun you_decline_invite() { + val change = ChangeBuilder.changeBy(you) + .uninvite(you) + .build() + + assertEquals(listOf("You declined the invitation to the group."), describeChange(change)) + } + + @Test + fun unknown_revokes_your_invite() { + val change = ChangeBuilder.changeByUnknown() + .uninvite(you) + .build() + + assertEquals(listOf("An admin revoked your invitation to the group."), describeChange(change)) + } + + @Test + fun unknown_revokes_1_invite() { + val change = ChangeBuilder.changeByUnknown() + .uninvite(bob) + .build() + + assertEquals(listOf("An invitation to the group was revoked."), describeChange(change)) + } + + @Test + fun unknown_revokes_2_invites() { + val change = ChangeBuilder.changeByUnknown() + .uninvite(bob) + .uninvite(ACI.from(UUID.randomUUID())) + .build() + + assertEquals(listOf("2 invitations to the group were revoked."), describeChange(change)) + } + + @Test + fun unknown_revokes_yours_and_three_other_invites() { + val change = ChangeBuilder.changeByUnknown() + .uninvite(bob) + .uninvite(you) + .uninvite(ACI.from(UUID.randomUUID())) + .uninvite(ACI.from(UUID.randomUUID())) + .build() + + assertEquals(listOf("An admin revoked your invitation to the group.", "3 invitations to the group were revoked."), describeChange(change)) + } + + @Test + fun your_invite_was_revoked_by_known_member() { + val change = ChangeBuilder.changeBy(bob) + .uninvite(you) + .build() + + assertEquals(listOf("Bob revoked your invitation to the group."), describeChange(change)) + } + + // Promote pending members + @Test + fun member_accepts_invite() { + val change = ChangeBuilder.changeBy(bob) + .promote(bob) + .build() + + assertEquals(listOf("Bob accepted an invitation to the group."), describeChange(change)) + } + + @Test + fun you_accept_invite() { + val change = ChangeBuilder.changeBy(you) + .promote(you) + .build() + + assertEquals(listOf("You accepted the invitation to the group."), describeChange(change)) + } + + @Test + fun member_promotes_pending_member() { + val change = ChangeBuilder.changeBy(bob) + .promote(alice) + .build() + + assertEquals(listOf("Bob added invited member Alice."), describeChange(change)) + } + + @Test + fun you_promote_pending_member() { + val change = ChangeBuilder.changeBy(you) + .promote(bob) + .build() + + assertEquals(listOf("You added invited member Bob."), describeChange(change)) + } + + @Test + fun member_promotes_you() { + val change = ChangeBuilder.changeBy(bob) + .promote(you) + .build() + + assertEquals(listOf("Bob added you to the group."), describeChange(change)) + } + + @Test + fun unknown_added_by_invite() { + val change = ChangeBuilder.changeByUnknown() + .promote(you) + .build() + + assertEquals(listOf("You joined the group."), describeChange(change)) + } + + @Test + fun unknown_promotes_pending_member() { + val change = ChangeBuilder.changeByUnknown() + .promote(alice) + .build() + + assertEquals(listOf("Alice joined the group."), describeChange(change)) + } + + // Title change + @Test + fun member_changes_title() { + val change = ChangeBuilder.changeBy(alice) + .title("New title") + .build() + + assertEquals(listOf("Alice changed the group name to \"" + StringUtil.isolateBidi("New title") + "\"."), describeChange(change)) + } + + @Test + fun you_change_title() { + val change = ChangeBuilder.changeBy(you) + .title("Title 2") + .build() + + assertEquals(listOf("You changed the group name to \"" + StringUtil.isolateBidi("Title 2") + "\"."), describeChange(change)) + } + + @Test + fun unknown_changed_title() { + val change = ChangeBuilder.changeByUnknown() + .title("Title 3") + .build() + + assertEquals(listOf("The group name has changed to \"" + StringUtil.isolateBidi("Title 3") + "\"."), describeChange(change)) + } + + // Avatar change + @Test + fun member_changes_avatar() { + val change = ChangeBuilder.changeBy(alice) + .avatar("Avatar1") + .build() + + assertEquals(listOf("Alice changed the group avatar."), describeChange(change)) + } + + @Test + fun you_change_avatar() { + val change = ChangeBuilder.changeBy(you) + .avatar("Avatar2") + .build() + + assertEquals(listOf("You changed the group avatar."), describeChange(change)) + } + + @Test + fun unknown_changed_avatar() { + val change = ChangeBuilder.changeByUnknown() + .avatar("Avatar3") + .build() + + assertEquals(listOf("The group avatar has been changed."), describeChange(change)) + } + + // Timer change + @Test + fun member_changes_timer() { + val change = ChangeBuilder.changeBy(bob) + .timer(10) + .build() + + assertEquals(listOf("Bob set the disappearing message timer to 10 seconds."), describeChange(change)) + } + + @Test + fun you_change_timer() { + val change = ChangeBuilder.changeBy(you) + .timer(60) + .build() + + assertEquals(listOf("You set the disappearing message timer to 1 minute."), describeChange(change)) + } + + @Test + fun unknown_change_timer() { + val change = ChangeBuilder.changeByUnknown() + .timer(120) + .build() + + assertEquals(listOf("The disappearing message timer has been set to 2 minutes."), describeChange(change)) + } + + @Test + fun unknown_change_timer_mentions_no_one() { + val change = ChangeBuilder.changeByUnknown() + .timer(120) + .build() + + assertSingleChangeMentioning(change, emptyList()) + } + + // Attribute access change + @Test + fun member_changes_attribute_access() { + val change = ChangeBuilder.changeBy(bob) + .attributeAccess(MEMBER) + .build() + + assertEquals(listOf("Bob changed who can edit group info to \"All members\"."), describeChange(change)) + } + + @Test + fun you_changed_attribute_access() { + val change = ChangeBuilder.changeBy(you) + .attributeAccess(ADMINISTRATOR) + .build() + + assertEquals(listOf("You changed who can edit group info to \"Only admins\"."), describeChange(change)) + } + + @Test + fun unknown_changed_attribute_access() { + val change = ChangeBuilder.changeByUnknown() + .attributeAccess(ADMINISTRATOR) + .build() + + assertEquals(listOf("Who can edit group info has been changed to \"Only admins\"."), describeChange(change)) + } + + // Membership access change + @Test + fun member_changes_membership_access() { + val change = ChangeBuilder.changeBy(alice) + .membershipAccess(ADMINISTRATOR) + .build() + + assertEquals(listOf("Alice changed who can edit group membership to \"Only admins\"."), describeChange(change)) + } + + @Test + fun you_changed_membership_access() { + val change = ChangeBuilder.changeBy(you) + .membershipAccess(MEMBER) + .build() + + assertEquals(listOf("You changed who can edit group membership to \"All members\"."), describeChange(change)) + } + + @Test + fun unknown_changed_membership_access() { + val change = ChangeBuilder.changeByUnknown() + .membershipAccess(ADMINISTRATOR) + .build() + + assertEquals(listOf("Who can edit group membership has been changed to \"Only admins\"."), describeChange(change)) + } + + // Group link access change + @Test + fun you_changed_group_link_access_to_any() { + val change = ChangeBuilder.changeBy(you) + .inviteLinkAccess(ANY) + .build() + + assertEquals(listOf("You turned on the group link with admin approval off."), describeChange(change)) + } + + @Test + fun you_changed_group_link_access_to_administrator_approval() { + val change = ChangeBuilder.changeBy(you) + .inviteLinkAccess(ADMINISTRATOR) + .build() + + assertEquals(listOf("You turned on the group link with admin approval on."), describeChange(change)) + } + + @Test + fun you_turned_off_group_link_access() { + val change = ChangeBuilder.changeBy(you) + .inviteLinkAccess(UNSATISFIABLE) + .build() + + assertEquals(listOf("You turned off the group link."), describeChange(change)) + } + + @Test + fun member_changed_group_link_access_to_any() { + val change = ChangeBuilder.changeBy(alice) + .inviteLinkAccess(ANY) + .build() + + assertEquals(listOf("Alice turned on the group link with admin approval off."), describeChange(change)) + } + + @Test + fun member_changed_group_link_access_to_administrator_approval() { + val change = ChangeBuilder.changeBy(bob) + .inviteLinkAccess(ADMINISTRATOR) + .build() + + assertEquals(listOf("Bob turned on the group link with admin approval on."), describeChange(change)) + } + + @Test + fun member_turned_off_group_link_access() { + val change = ChangeBuilder.changeBy(alice) + .inviteLinkAccess(UNSATISFIABLE) + .build() + + assertEquals(listOf("Alice turned off the group link."), describeChange(change)) + } + + @Test + fun unknown_changed_group_link_access_to_any() { + val change = ChangeBuilder.changeByUnknown() + .inviteLinkAccess(ANY) + .build() + + assertEquals(listOf("The group link has been turned on with admin approval off."), describeChange(change)) + } + + @Test + fun unknown_changed_group_link_access_to_administrator_approval() { + val change = ChangeBuilder.changeByUnknown() + .inviteLinkAccess(ADMINISTRATOR) + .build() + + assertEquals(listOf("The group link has been turned on with admin approval on."), describeChange(change)) + } + + @Test + fun unknown_turned_off_group_link_access() { + val change = ChangeBuilder.changeByUnknown() + .inviteLinkAccess(UNSATISFIABLE) + .build() + + assertEquals(listOf("The group link has been turned off."), describeChange(change)) + } + + // Group link with known previous group state + @Test + fun group_link_access_from_unknown_to_administrator() { + assertEquals("You turned on the group link with admin approval on.", describeGroupLinkChange(you, UNKNOWN, ADMINISTRATOR)) + assertEquals("Alice turned on the group link with admin approval on.", describeGroupLinkChange(alice, UNKNOWN, ADMINISTRATOR)) + assertEquals("The group link has been turned on with admin approval on.", describeGroupLinkChange(null, UNKNOWN, ADMINISTRATOR)) + } + + @Test + fun group_link_access_from_administrator_to_unsatisfiable() { + assertEquals("You turned off the group link.", describeGroupLinkChange(you, ADMINISTRATOR, UNSATISFIABLE)) + assertEquals("Bob turned off the group link.", describeGroupLinkChange(bob, ADMINISTRATOR, UNSATISFIABLE)) + assertEquals("The group link has been turned off.", describeGroupLinkChange(null, ADMINISTRATOR, UNSATISFIABLE)) + } + + @Test + fun group_link_access_from_unsatisfiable_to_administrator() { + assertEquals("You turned on the group link with admin approval on.", describeGroupLinkChange(you, UNSATISFIABLE, ADMINISTRATOR)) + assertEquals("Alice turned on the group link with admin approval on.", describeGroupLinkChange(alice, UNSATISFIABLE, ADMINISTRATOR)) + assertEquals("The group link has been turned on with admin approval on.", describeGroupLinkChange(null, UNSATISFIABLE, ADMINISTRATOR)) + } + + @Test + fun group_link_access_from_administrator_to_any() { + assertEquals("You turned off admin approval for the group link.", describeGroupLinkChange(you, ADMINISTRATOR, ANY)) + assertEquals("Bob turned off admin approval for the group link.", describeGroupLinkChange(bob, ADMINISTRATOR, ANY)) + assertEquals("The admin approval for the group link has been turned off.", describeGroupLinkChange(null, ADMINISTRATOR, ANY)) + } + + @Test + fun group_link_access_from_any_to_administrator() { + assertEquals("You turned on admin approval for the group link.", describeGroupLinkChange(you, ANY, ADMINISTRATOR)) + assertEquals("Bob turned on admin approval for the group link.", describeGroupLinkChange(bob, ANY, ADMINISTRATOR)) + assertEquals("The admin approval for the group link has been turned on.", describeGroupLinkChange(null, ANY, ADMINISTRATOR)) + } + + private fun describeGroupLinkChange(editor: ACI?, fromAccess: AccessRequired, toAccess: AccessRequired): String { + val previousGroupState = DecryptedGroup.Builder() + .accessControl( + AccessControl.Builder() + .addFromInviteLink(fromAccess) + .build() + ) + .build() + val change = (if (editor != null) ChangeBuilder.changeBy(editor) else ChangeBuilder.changeByUnknown()).inviteLinkAccess(toAccess) + .build() + + val strings = describeChange(previousGroupState, change) + return strings.single() + } + + // Group link reset + @Test + fun you_reset_group_link() { + val change = ChangeBuilder.changeBy(you) + .resetGroupLink() + .build() + + assertEquals(listOf("You reset the group link."), describeChange(change)) + } + + @Test + fun member_reset_group_link() { + val change = ChangeBuilder.changeBy(alice) + .resetGroupLink() + .build() + + assertEquals(listOf("Alice reset the group link."), describeChange(change)) + } + + @Test + fun unknown_reset_group_link() { + val change = ChangeBuilder.changeByUnknown() + .resetGroupLink() + .build() + + assertEquals(listOf("The group link has been reset."), describeChange(change)) + } + + /** + * When the group link is turned on and reset in the same change, assume this is the first time + * the link password it being set and do not show reset message. + */ + @Test + fun member_changed_group_link_access_to_on_and_reset() { + val change = ChangeBuilder.changeBy(alice) + .inviteLinkAccess(ANY) + .resetGroupLink() + .build() + + assertEquals(listOf("Alice turned on the group link with admin approval off."), describeChange(change)) + } + + /** + * When the group link is turned on and reset in the same change, assume this is the first time + * the link password it being set and do not show reset message. + */ + @Test + fun you_changed_group_link_access_to_on_and_reset() { + val change = ChangeBuilder.changeBy(you) + .inviteLinkAccess(ADMINISTRATOR) + .resetGroupLink() + .build() + + assertEquals(listOf("You turned on the group link with admin approval on."), describeChange(change)) + } + + @Test + fun you_changed_group_link_access_to_off_and_reset() { + val change = ChangeBuilder.changeBy(you) + .inviteLinkAccess(UNSATISFIABLE) + .resetGroupLink() + .build() + + assertEquals(listOf("You turned off the group link.", "You reset the group link."), describeChange(change)) + } + + // Group link request + @Test + fun you_requested_to_join_the_group() { + val change = ChangeBuilder.changeBy(you) + .requestJoin() + .build() + + assertEquals(listOf("You sent a request to join the group."), describeChange(change)) + } + + @Test + fun member_requested_to_join_the_group() { + val change = ChangeBuilder.changeBy(bob) + .requestJoin() + .build() + + assertEquals(listOf("Bob requested to join via the group link."), describeChange(change)) + } + + @Test + fun unknown_requested_to_join_the_group() { + val change = ChangeBuilder.changeByUnknown() + .requestJoin(alice) + .build() + + assertEquals(listOf("Alice requested to join via the group link."), describeChange(change)) + } + + @Test + fun member_approved_your_join_request() { + val change = ChangeBuilder.changeBy(bob) + .approveRequest(you) + .build() + + assertEquals(listOf("Bob approved your request to join the group."), describeChange(change)) + } + + @Test + fun member_approved_another_join_request() { + val change = ChangeBuilder.changeBy(alice) + .approveRequest(bob) + .build() + + assertEquals(listOf("Alice approved a request to join the group from Bob."), describeChange(change)) + } + + @Test + fun you_approved_another_join_request() { + val change = ChangeBuilder.changeBy(you) + .approveRequest(alice) + .build() + + assertEquals(listOf("You approved a request to join the group from Alice."), describeChange(change)) + } + + @Test + fun unknown_approved_your_join_request() { + val change = ChangeBuilder.changeByUnknown() + .approveRequest(you) + .build() + + assertEquals(listOf("Your request to join the group has been approved."), describeChange(change)) + } + + @Test + fun unknown_approved_another_join_request() { + val change = ChangeBuilder.changeByUnknown() + .approveRequest(bob) + .build() + + assertEquals(listOf("A request to join the group from Bob has been approved."), describeChange(change)) + } + + @Test + fun member_denied_another_join_request() { + val change = ChangeBuilder.changeBy(alice) + .denyRequest(bob) + .build() + + assertEquals(listOf("Alice denied a request to join the group from Bob."), describeChange(change)) + } + + @Test + fun member_denied_your_join_request() { + val change = ChangeBuilder.changeBy(alice) + .denyRequest(you) + .build() + + assertEquals(listOf("Your request to join the group has been denied by an admin."), describeChange(change)) + } + + @Test + fun you_cancelled_your_join_request() { + val change = ChangeBuilder.changeBy(you) + .denyRequest(you) + .build() + + assertEquals(listOf("You canceled your request to join the group."), describeChange(change)) + } + + @Test + fun member_cancelled_their_join_request() { + val change = ChangeBuilder.changeBy(alice) + .denyRequest(alice) + .build() + + assertEquals(listOf("Alice canceled their request to join the group."), describeChange(change)) + } + + @Test + fun unknown_denied_your_join_request() { + val change = ChangeBuilder.changeByUnknown() + .denyRequest(you) + .build() + + assertEquals(listOf("Your request to join the group has been denied by an admin."), describeChange(change)) + } + + @Test + fun unknown_denied_another_join_request() { + val change = ChangeBuilder.changeByUnknown() + .denyRequest(bob) + .build() + + assertEquals(listOf("A request to join the group from Bob has been denied."), describeChange(change)) + } + + // Multiple changes + @Test + fun multiple_changes() { + val change = ChangeBuilder.changeBy(alice) + .addMember(bob) + .membershipAccess(MEMBER) + .title("Title") + .addMember(you) + .timer(300) + .build() + + assertEquals( + listOf( + "Alice added you to the group.", + "Alice added Bob.", + "Alice changed the group name to \"" + StringUtil.isolateBidi("Title") + "\".", + "Alice set the disappearing message timer to 5 minutes.", + "Alice changed who can edit group membership to \"All members\"." + ), + describeChange(change) + ) + } + + @Test + fun multiple_changes_leave_and_promote() { + val change = ChangeBuilder.changeBy(alice) + .deleteMember(alice) + .promoteToAdmin(bob) + .build() + + assertEquals( + listOf( + "Alice made Bob an admin.", + "Alice left the group." + ), + describeChange(change) + ) + } + + @Test + fun multiple_changes_leave_and_promote_by_unknown() { + val change = ChangeBuilder.changeByUnknown() + .deleteMember(alice) + .promoteToAdmin(bob) + .build() + + assertEquals( + listOf( + "Bob is now an admin.", + "Alice is no longer in the group." + ), + describeChange(change) + ) + } + + @Test + fun multiple_changes_by_unknown() { + val change = ChangeBuilder.changeByUnknown() + .addMember(bob) + .membershipAccess(MEMBER) + .title("Title 2") + .avatar("Avatar 1") + .timer(600) + .build() + + assertEquals( + listOf( + "Bob joined the group.", + "The group name has changed to \"" + StringUtil.isolateBidi("Title 2") + "\".", + "The group avatar has been changed.", + "The disappearing message timer has been set to 10 minutes.", + "Who can edit group membership has been changed to \"All members\"." + ), + describeChange(change) + ) + } + + @Test + fun multiple_changes_join_and_leave_by_unknown() { + val change = ChangeBuilder.changeByUnknown() + .addMember(alice) + .promoteToAdmin(alice) + .deleteMember(alice) + .title("Updated title") + .build() + + assertEquals( + listOf( + "Alice joined the group.", + "Alice is now an admin.", + "The group name has changed to \"" + StringUtil.isolateBidi("Updated title") + "\".", + "Alice is no longer in the group." + ), + describeChange(change) + ) + } + + // Group state without a change record + @Test + fun you_created_a_group_change_not_found() { + val group = newGroupBy(you, 0) + .build() + + assertEquals("You joined the group.", describeNewGroup(group)) + } + + @Test + fun you_created_a_group() { + val group = newGroupBy(you, 0) + .build() + + val change = ChangeBuilder.changeBy(you) + .addMember(alice) + .addMember(you) + .addMember(bob) + .title("New title") + .build() + + assertEquals("You created the group.", describeNewGroup(group, change)) + } + + @Test + fun alice_created_a_group_change_not_found() { + val group = newGroupBy(alice, 0) + .member(you) + .build() + + assertEquals("You joined the group.", describeNewGroup(group)) + } + + @Test + fun alice_created_a_group() { + val group = newGroupBy(alice, 0) + .member(you) + .build() + + val change = ChangeBuilder.changeBy(alice) + .addMember(you) + .addMember(alice) + .addMember(bob) + .title("New title") + .build() + + assertEquals("Alice added you to the group.", describeNewGroup(group, change)) + } + + @Test + fun alice_created_a_group_above_zero() { + val group = newGroupBy(alice, 1) + .member(you) + .build() + + assertEquals("You joined the group.", describeNewGroup(group)) + } + + @Test + fun you_were_invited_to_a_group() { + val group = newGroupBy(alice, 0) + .invite(bob, you) + .build() + + assertEquals("Bob invited you to the group.", describeNewGroup(group)) + } + + @Test + fun describe_a_group_you_are_not_in() { + val group = newGroupBy(alice, 1) + .build() + + assertEquals("Group updated.", describeNewGroup(group)) + } + + @Test + fun makeRecipientsClickable_onePlaceholder() { + val id = RecipientId.from(1) + + val result = GroupsV2UpdateMessageProducer.makeRecipientsClickable( + /* context = */ + ApplicationProvider.getApplicationContext(), + /* template = */ + GroupsV2UpdateMessageProducer.makePlaceholder(id), + /* recipientIds = */ + listOf(id), + /* clickHandler = */ + null + ) + + assertEquals("Alice", result.toString()) + } + + @Test + fun makeRecipientsClickable_twoPlaceholders_sameRecipient() { + val id = RecipientId.from(1) + val placeholder = GroupsV2UpdateMessageProducer.makePlaceholder(id) + + val result = GroupsV2UpdateMessageProducer.makeRecipientsClickable( + /* context = */ + ApplicationProvider.getApplicationContext(), + /* template = */ + "$placeholder $placeholder", + /* recipientIds = */ + listOf(id), + /* clickHandler = */ + null + ) + + assertEquals("Alice Alice", result.toString()) + } + + @Test + fun makeRecipientsClickable_twoPlaceholders_differentRecipient() { + val id1 = RecipientId.from(1) + val id2 = RecipientId.from(2) + + val placeholder1 = GroupsV2UpdateMessageProducer.makePlaceholder(id1) + val placeholder2 = GroupsV2UpdateMessageProducer.makePlaceholder(id2) + + val result = GroupsV2UpdateMessageProducer.makeRecipientsClickable( + /* context = */ + ApplicationProvider.getApplicationContext(), + /* template = */ + "$placeholder1 $placeholder2", + /* recipientIds = */ + listOf(id1, id2), + /* clickHandler = */ + null + ) + + assertEquals("Alice Bob", result.toString()) + } + + @Test + fun makeRecipientsClickable_complicated() { + val id1 = RecipientId.from(1) + val id2 = RecipientId.from(2) + + val placeholder1 = GroupsV2UpdateMessageProducer.makePlaceholder(id1) + val placeholder2 = GroupsV2UpdateMessageProducer.makePlaceholder(id2) + + val result = GroupsV2UpdateMessageProducer.makeRecipientsClickable( + /* context = */ + ApplicationProvider.getApplicationContext(), + /* template = */ + "$placeholder1 said hello to $placeholder2, and $placeholder2 said hello back to $placeholder1.", + /* recipientIds = */ + listOf(id1, id2), + /* clickHandler = */ + null + ) + + assertEquals("Alice said hello to Bob, and Bob said hello back to Alice.", result.toString()) + } + + private fun describeConvertedNewGroup(groupState: DecryptedGroup, groupChange: DecryptedGroupChange): String { + val update = translateDecryptedChangeNewGroup( + selfIds, + DecryptedGroupV2Context.Builder() + .change(groupChange) + .groupState(groupState) + .build() + ) + + return producer.describeChanges(update.updates).single().spannable.toString() + } + + private fun describeConvertedChange(previousGroupState: DecryptedGroup?, change: DecryptedGroupChange): List { + val update = translateDecryptedChangeUpdate( + selfIds, + DecryptedGroupV2Context.Builder() + .change(change) + .previousGroupState(previousGroupState) + .build() + ) + + return producer.describeChanges(update.updates) + .map { it.spannable } + .map { it.toString() } + .toList() + } + + private fun describeChange(change: DecryptedGroupChange): List { + return describeChange(null, change) + } + + private fun describeChange( + previousGroupState: DecryptedGroup?, + change: DecryptedGroupChange + ): List { + val convertedChange = describeConvertedChange(previousGroupState, change) + val describedChange = producer.describeChanges(previousGroupState, change) + .map { it.spannable } + .map { it.toString() } + .toList() + assertEquals(describedChange.size, convertedChange.size) + + val convertedIterator = convertedChange.listIterator() + val describedIterator = describedChange.listIterator() + + while (convertedIterator.hasNext()) { + assertEquals(describedIterator.next(), convertedIterator.next()) + } + return describedChange + } + + private fun describeNewGroup(group: DecryptedGroup, groupChange: DecryptedGroupChange = DecryptedGroupChange()): String { + val newGroupString = producer.describeNewGroup(group, groupChange).spannable.toString() + val convertedGroupString = describeConvertedNewGroup(group, groupChange) + + assertEquals(newGroupString, convertedGroupString) + + return newGroupString + } + + private fun assertSingleChangeMentioning(change: DecryptedGroupChange, expectedMentions: List) { + val changes = producer.describeChanges(null, change) + + val description = changes.single() + assertEquals(expectedMentions, description.mentioned) + + if (expectedMentions.isEmpty()) { + assertTrue(description.isStringStatic) + } else { + assertFalse(description.isStringStatic) + } + } + + private class GroupStateBuilder(foundingMember: ACI, revision: Int) { + private val builder = DecryptedGroup.Builder() + .revision(revision) + .members(listOf(DecryptedMember.Builder().aciBytes(foundingMember.toByteString()).build())) + + fun invite(inviter: ACI, invitee: ServiceId): GroupStateBuilder { + builder.pendingMembers(builder.pendingMembers.plus(DecryptedPendingMember.Builder().serviceIdBytes(invitee.toByteString()).addedByAci(inviter.toByteString()).build())) + return this + } + + fun member(member: ACI): GroupStateBuilder { + builder.members(builder.members.plus(DecryptedMember.Builder().aciBytes(member.toByteString()).build())) + return this + } + + fun build(): DecryptedGroup { + return builder.build() + } + } + + companion object { + private fun recipientWithName(id: RecipientId, name: String): Recipient { + return mockk { + every { this@mockk.id } returns id + every { getDisplayName(any()) } returns name + } + } + + private fun newGroupBy(foundingMember: ACI, revision: Int): GroupStateBuilder { + return GroupStateBuilder(foundingMember, revision) + } + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/JobMigratorTest.java b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/JobMigratorTest.java deleted file mode 100644 index d713666152..0000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/JobMigratorTest.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.thoughtcrime.securesms.jobmanager; - -import androidx.annotation.NonNull; - -import org.junit.BeforeClass; -import org.junit.Test; -import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec; -import org.thoughtcrime.securesms.jobmanager.persistence.JobStorage; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; - -import kotlin.jvm.functions.Function1; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class JobMigratorTest { - - @BeforeClass - public static void init() { - Log.initialize(mock(Log.Logger.class)); - } - - @Test(expected = AssertionError.class) - public void JobMigrator_crashWhenTooFewMigrations() { - new JobMigrator(1, 2, Collections.emptyList()); - } - - @Test(expected = AssertionError.class) - public void JobMigrator_crashWhenTooManyMigrations() { - new JobMigrator(1, 2, Arrays.asList(new EmptyMigration(2), new EmptyMigration(3))); - } - - @Test(expected = AssertionError.class) - public void JobMigrator_crashWhenSkippingMigrations() { - new JobMigrator(1, 3, Arrays.asList(new EmptyMigration(2), new EmptyMigration(4))); - } - - @Test - public void JobMigrator_properInitialization() { - new JobMigrator(1, 3, Arrays.asList(new EmptyMigration(2), new EmptyMigration(3))); - } - - @Test - public void migrate_callsAppropriateMigrations_fullSet() { - JobMigration migration1 = spy(new EmptyMigration(2)); - JobMigration migration2 = spy(new EmptyMigration(3)); - - JobMigrator subject = new JobMigrator(1, 3, Arrays.asList(migration1, migration2)); - int version = subject.migrate(simpleJobStorage()); - - assertEquals(3, version); - verify(migration1).migrate(any()); - verify(migration2).migrate(any()); - } - - @Test - public void migrate_callsAppropriateMigrations_subset() { - JobMigration migration1 = spy(new EmptyMigration(2)); - JobMigration migration2 = spy(new EmptyMigration(3)); - - JobMigrator subject = new JobMigrator(2, 3, Arrays.asList(migration1, migration2)); - int version = subject.migrate(simpleJobStorage()); - - assertEquals(3, version); - verify(migration1, never()).migrate(any()); - verify(migration2).migrate(any()); - } - - @Test - public void migrate_callsAppropriateMigrations_none() { - JobMigration migration1 = spy(new EmptyMigration(2)); - JobMigration migration2 = spy(new EmptyMigration(3)); - - JobMigrator subject = new JobMigrator(3, 3, Arrays.asList(migration1, migration2)); - int version = subject.migrate(simpleJobStorage()); - - assertEquals(3, version); - verify(migration1, never()).migrate(any()); - verify(migration2, never()).migrate(any()); - } - - private static JobStorage simpleJobStorage() { - JobStorage jobStorage = mock(JobStorage.class); - JobSpec job = new JobSpec("1", "f1", null, 1, 1, 1, 1, 1, 1, null, null, false, false, 0, 0); - - when(jobStorage.debugGetJobSpecs(anyInt())).thenReturn(new ArrayList<>(Collections.singletonList(job))); - doAnswer(invocation -> { - Function1 transformer = invocation.getArgument(0); - return transformer.invoke(job); - }).when(jobStorage).transformJobs(any()); - - return jobStorage; - } - - private static class EmptyMigration extends JobMigration { - - protected EmptyMigration(int endVersion) { - super(endVersion); - } - - @Override - public @NonNull JobData migrate(@NonNull JobData jobData) { - return jobData; - } - } -} diff --git a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/JobMigratorTest.kt b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/JobMigratorTest.kt new file mode 100644 index 0000000000..b4b9e4bfa5 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/JobMigratorTest.kt @@ -0,0 +1,130 @@ +package org.thoughtcrime.securesms.jobmanager + +import io.mockk.every +import io.mockk.mockk +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertThrows +import org.junit.Assert.assertTrue +import org.junit.BeforeClass +import org.junit.Test +import org.signal.core.util.logging.Log.initialize +import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec +import org.thoughtcrime.securesms.jobmanager.persistence.JobStorage +import org.thoughtcrime.securesms.testutil.EmptyLogger + +class JobMigratorTest { + @Test + fun test_JobMigrator_crashWhenTooFewMigrations() { + val error = assertThrows(AssertionError::class.java) { + JobMigrator(1, 2, emptyList()) + } + assertEquals("You must have a migration for every version!", error.message) + } + + @Test + fun test_JobMigrator_crashWhenTooManyMigrations() { + val error = assertThrows(AssertionError::class.java) { + JobMigrator(1, 2, listOf(EmptyMigration(2), EmptyMigration(3))) + } + assertEquals("You must have a migration for every version!", error.message) + } + + @Test + fun test_JobMigrator_crashWhenSkippingMigrations() { + val error = assertThrows(AssertionError::class.java) { + JobMigrator(1, 3, listOf(EmptyMigration(2), EmptyMigration(4))) + } + assertEquals("Missing migration for version 3!", error.message) + } + + @Test + fun test_JobMigrator_properInitialization() { + JobMigrator(1, 3, listOf(EmptyMigration(2), EmptyMigration(3))) + } + + @Test + fun migrate_callsAppropriateMigrations_fullSet() { + val migration1 = EmptyMigration(2) + val migration2 = EmptyMigration(3) + + val subject = JobMigrator(1, 3, listOf(migration1, migration2)) + val version = subject.migrate(simpleJobStorage()) + + assertEquals(3, version) + assertTrue(migration1.migrated) + assertTrue(migration2.migrated) + } + + @Test + fun migrate_callsAppropriateMigrations_subset() { + val migration1 = EmptyMigration(2) + val migration2 = EmptyMigration(3) + + val subject = JobMigrator(2, 3, listOf(migration1, migration2)) + val version = subject.migrate(simpleJobStorage()) + + assertEquals(3, version) + assertFalse(migration1.migrated) + assertTrue(migration2.migrated) + } + + @Test + fun migrate_callsAppropriateMigrations_none() { + val migration1 = EmptyMigration(2) + val migration2 = EmptyMigration(3) + + val subject = JobMigrator(3, 3, listOf(migration1, migration2)) + val version = subject.migrate(simpleJobStorage()) + + assertEquals(3, version) + assertFalse(migration1.migrated) + assertFalse(migration2.migrated) + } + + private class EmptyMigration(endVersion: Int) : JobMigration(endVersion) { + private var _migrated: Boolean = false + val migrated: Boolean get() = _migrated + + override fun migrate(jobData: JobData): JobData { + _migrated = true + return jobData + } + } + + companion object { + @JvmStatic + @BeforeClass + fun init() { + initialize(EmptyLogger()) + } + + private fun simpleJobStorage(): JobStorage { + val job = JobSpec( + id = "1", + factoryKey = "f1", + queueKey = null, + createTime = 1, + lastRunAttemptTime = 1, + nextBackoffInterval = 1, + runAttempt = 1, + maxAttempts = 1, + lifespan = 1, + serializedData = null, + serializedInputData = null, + isRunning = false, + isMemoryOnly = false, + globalPriority = 0, + queuePriority = 0 + ) + return mockk { + every { debugGetJobSpecs(any()) } returns listOf(job) + every { transformJobs(any()) } answers { + @Suppress("UNCHECKED_CAST") + val transformer = invocation.args.single() as Function1 + transformer(job) + } + } + } + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdFollowUpJobMigrationTest.java b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdFollowUpJobMigrationTest.java deleted file mode 100644 index 3bfbdfcb06..0000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdFollowUpJobMigrationTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.thoughtcrime.securesms.jobmanager.migrations; - -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.thoughtcrime.securesms.jobmanager.JsonJobData; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData; -import org.thoughtcrime.securesms.jobs.FailingJob; -import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob; -import org.thoughtcrime.securesms.recipients.Recipient; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.mock; - -public class RecipientIdFollowUpJobMigrationTest { - - @Rule - public MockitoRule rule = MockitoJUnit.rule(); - - @Mock - private MockedStatic recipientMockedStatic; - - @Mock - private MockedStatic jobParametersMockedStatic; - - @Test - public void migrate_sendDeliveryReceiptJob_good() throws Exception { - JobData testData = new JobData("SendDeliveryReceiptJob", null, -1, -1, new JsonJobData.Builder().putString("recipient", "1") - .putLong("message_id", 1) - .putLong("timestamp", 2) - .serialize()); - RecipientIdFollowUpJobMigration subject = new RecipientIdFollowUpJobMigration(); - JobData converted = subject.migrate(testData); - - assertEquals("SendDeliveryReceiptJob", converted.getFactoryKey()); - assertNull(converted.getQueueKey()); - - JsonJobData data = JsonJobData.deserialize(converted.getData()); - assertEquals("1", data.getString("recipient")); - assertEquals(1, data.getLong("message_id")); - assertEquals(2, data.getLong("timestamp")); - - new SendDeliveryReceiptJob.Factory().create(mock(Job.Parameters.class), converted.getData()); - } - - @Test - public void migrate_sendDeliveryReceiptJob_bad() throws Exception { - JobData testData = new JobData("SendDeliveryReceiptJob", null, -1, -1, new JsonJobData.Builder().putString("recipient", "1") - .serialize()); - RecipientIdFollowUpJobMigration subject = new RecipientIdFollowUpJobMigration(); - JobData converted = subject.migrate(testData); - - assertEquals("FailingJob", converted.getFactoryKey()); - assertNull(converted.getQueueKey()); - - new FailingJob.Factory().create(mock(Job.Parameters.class), converted.getData()); - } -} diff --git a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdFollowUpJobMigrationTest.kt b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdFollowUpJobMigrationTest.kt new file mode 100644 index 0000000000..6899352352 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdFollowUpJobMigrationTest.kt @@ -0,0 +1,57 @@ +package org.thoughtcrime.securesms.jobmanager.migrations + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData +import org.thoughtcrime.securesms.jobmanager.JsonJobData +import org.thoughtcrime.securesms.jobs.FailingJob +import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob + +class RecipientIdFollowUpJobMigrationTest { + @Test + fun migrate_sendDeliveryReceiptJob_good() { + val testData = JobData( + "SendDeliveryReceiptJob", + null, + -1, + -1, + JsonJobData.Builder().putString("recipient", "1") + .putLong("message_id", 1) + .putLong("timestamp", 2) + .serialize() + ) + val subject = RecipientIdFollowUpJobMigration() + val converted = subject.migrate(testData) + + assertEquals("SendDeliveryReceiptJob", converted.factoryKey) + assertNull(converted.queueKey) + + val data = JsonJobData.deserialize(converted.data) + assertEquals("1", data.getString("recipient")) + assertEquals(1, data.getLong("message_id")) + assertEquals(2, data.getLong("timestamp")) + + SendDeliveryReceiptJob.Factory().create(Job.Parameters.Builder().build(), converted.data) + } + + @Test + fun migrate_sendDeliveryReceiptJob_bad() { + val testData = JobData( + "SendDeliveryReceiptJob", + null, + -1, + -1, + JsonJobData.Builder().putString("recipient", "1") + .serialize() + ) + val subject = RecipientIdFollowUpJobMigration() + val converted = subject.migrate(testData) + + assertEquals("FailingJob", converted.factoryKey) + assertNull(converted.queueKey) + + FailingJob.Factory().create(Job.Parameters.Builder().build(), converted.data) + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdJobMigrationTest.java b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdJobMigrationTest.java deleted file mode 100644 index 6c55198046..0000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdJobMigrationTest.java +++ /dev/null @@ -1,286 +0,0 @@ -package org.thoughtcrime.securesms.jobmanager.migrations; - -import android.app.Application; - -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.thoughtcrime.securesms.jobmanager.JsonJobData; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData; -import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration.NewSerializableSyncMessageId; -import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration.OldSerializableSyncMessageId; -import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob; -import org.thoughtcrime.securesms.jobs.PushGroupSendJob; -import org.thoughtcrime.securesms.jobs.IndividualSendJob; -import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob; -import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientId; -import org.thoughtcrime.securesms.util.JsonUtils; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class RecipientIdJobMigrationTest { - - @Rule - public MockitoRule rule = MockitoJUnit.rule(); - - @Mock - private MockedStatic recipientMockedStatic; - - @Mock - private MockedStatic jobParametersMockStatic; - - @Test - public void migrate_multiDeviceContactUpdateJob() throws Exception { - JobData testData = new JobData("MultiDeviceContactUpdateJob", "MultiDeviceContactUpdateJob", -1, -1, new JsonJobData.Builder().putBoolean("force_sync", false).putString("address", "+16101234567").serialize()); - mockRecipientResolve("+16101234567", 1); - - RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class)); - JobData converted = subject.migrate(testData); - JsonJobData data = JsonJobData.deserialize(converted.getData()); - - assertEquals("MultiDeviceContactUpdateJob", converted.getFactoryKey()); - assertEquals("MultiDeviceContactUpdateJob", converted.getQueueKey()); - assertFalse(data.getBoolean("force_sync")); - assertFalse(data.hasString("address")); - assertEquals("1", data.getString("recipient")); - - new MultiDeviceContactUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData()); - } - - @Test - public void migrate_multiDeviceViewOnceOpenJob() throws Exception { - OldSerializableSyncMessageId oldId = new OldSerializableSyncMessageId("+16101234567", 1); - JobData testData = new JobData("MultiDeviceRevealUpdateJob", null, -1, -1, new JsonJobData.Builder().putString("message_id", JsonUtils.toJson(oldId)).serialize()); - mockRecipientResolve("+16101234567", 1); - - RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class)); - JobData converted = subject.migrate(testData); - JsonJobData data = JsonJobData.deserialize(converted.getData()); - - assertEquals("MultiDeviceRevealUpdateJob", converted.getFactoryKey()); - assertNull(converted.getQueueKey()); - assertEquals(JsonUtils.toJson(new NewSerializableSyncMessageId("1", 1)), data.getString("message_id")); - - new MultiDeviceViewOnceOpenJob.Factory().create(mock(Job.Parameters.class), converted.getData()); - } - - @Test - public void migrate_sendDeliveryReceiptJob() throws Exception { - JobData testData = new JobData("SendDeliveryReceiptJob", null, -1, -1, new JsonJobData.Builder().putString("address", "+16101234567") - .putLong("message_id", 1) - .putLong("timestamp", 2) - .serialize()); - mockRecipientResolve("+16101234567", 1); - - RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class)); - JobData converted = subject.migrate(testData); - JsonJobData data = JsonJobData.deserialize(converted.getData()); - - assertEquals("SendDeliveryReceiptJob", converted.getFactoryKey()); - assertNull(converted.getQueueKey()); - assertEquals("1", data.getString("recipient")); - assertEquals(1, data.getLong("message_id")); - assertEquals(2, data.getLong("timestamp")); - - new SendDeliveryReceiptJob.Factory().create(mock(Job.Parameters.class), converted.getData()); - } - - @Test - public void migrate_multiDeviceVerifiedUpdateJob() throws Exception { - JobData testData = new JobData("MultiDeviceVerifiedUpdateJob", "__MULTI_DEVICE_VERIFIED_UPDATE__", -1, -1, new JsonJobData.Builder().putString("destination", "+16101234567") - .putString("identity_key", "abcd") - .putInt("verified_status", 1) - .putLong("timestamp", 123) - .serialize()); - mockRecipientResolve("+16101234567", 1); - - RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class)); - JobData converted = subject.migrate(testData); - JsonJobData data = JsonJobData.deserialize(converted.getData()); - - assertEquals("MultiDeviceVerifiedUpdateJob", converted.getFactoryKey()); - assertEquals("__MULTI_DEVICE_VERIFIED_UPDATE__", converted.getQueueKey()); - assertEquals("abcd", data.getString("identity_key")); - assertEquals(1, data.getInt("verified_status")); - assertEquals(123, data.getLong("timestamp")); - assertEquals("1", data.getString("destination")); - - new MultiDeviceVerifiedUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData()); - } - - @Test - public void migrate_pushGroupSendJob_null() throws Exception { - JobData testData = new JobData("PushGroupSendJob", "someGroupId", -1, -1, new JsonJobData.Builder().putString("filter_address", null) - .putLong("message_id", 123) - .serialize()); - mockRecipientResolve("someGroupId", 5); - - RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class)); - JobData converted = subject.migrate(testData); - JsonJobData data = JsonJobData.deserialize(converted.getData()); - - assertEquals("PushGroupSendJob", converted.getFactoryKey()); - assertEquals(RecipientId.from(5).toQueueKey(), converted.getQueueKey()); - assertNull(data.getString("filter_recipient")); - assertFalse(data.hasString("filter_address")); - - new PushGroupSendJob.Factory().create(mock(Job.Parameters.class), converted.getData()); - } - - @Test - public void migrate_pushGroupSendJob_nonNull() throws Exception { - JobData testData = new JobData("PushGroupSendJob", "someGroupId", -1, -1, new JsonJobData.Builder().putString("filter_address", "+16101234567") - .putLong("message_id", 123) - .serialize()); - mockRecipientResolve("+16101234567", 1); - mockRecipientResolve("someGroupId", 5); - - RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class)); - JobData converted = subject.migrate(testData); - JsonJobData data = JsonJobData.deserialize(converted.getData()); - - assertEquals("PushGroupSendJob", converted.getFactoryKey()); - assertEquals(RecipientId.from(5).toQueueKey(), converted.getQueueKey()); - assertEquals("1", data.getString("filter_recipient")); - assertFalse(data.hasString("filter_address")); - - new PushGroupSendJob.Factory().create(mock(Job.Parameters.class), converted.getData()); - } - - @Test - public void migrate_directoryRefreshJob_null() throws Exception { - JobData testData = new JobData("DirectoryRefreshJob", "DirectoryRefreshJob", -1, -1, new JsonJobData.Builder().putString("address", null).putBoolean("notify_of_new_users", true).serialize()); - - RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class)); - JobData converted = subject.migrate(testData); - JsonJobData data = JsonJobData.deserialize(converted.getData()); - - assertEquals("DirectoryRefreshJob", converted.getFactoryKey()); - assertEquals("DirectoryRefreshJob", converted.getQueueKey()); - assertNull(data.getString("recipient")); - assertTrue(data.getBoolean("notify_of_new_users")); - assertFalse(data.hasString("address")); - - new DirectoryRefreshJob.Factory().create(mock(Job.Parameters.class), converted.getData()); - } - - @Test - public void migrate_directoryRefreshJob_nonNull() throws Exception { - JobData testData = new JobData("DirectoryRefreshJob", "DirectoryRefreshJob", -1, -1, new JsonJobData.Builder().putString("address", "+16101234567").putBoolean("notify_of_new_users", true).serialize()); - mockRecipientResolve("+16101234567", 1); - - RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class)); - JobData converted = subject.migrate(testData); - JsonJobData data = JsonJobData.deserialize(converted.getData()); - - assertEquals("DirectoryRefreshJob", converted.getFactoryKey()); - assertEquals("DirectoryRefreshJob", converted.getQueueKey()); - assertTrue(data.getBoolean("notify_of_new_users")); - assertEquals("1", data.getString("recipient")); - assertFalse(data.hasString("address")); - - new DirectoryRefreshJob.Factory().create(mock(Job.Parameters.class), converted.getData()); - } - - @Test - public void migrate_retrieveProfileAvatarJob() throws Exception { - JobData testData = new JobData("RetrieveProfileAvatarJob", "RetrieveProfileAvatarJob+16101234567", -1, -1, new JsonJobData.Builder().putString("address", "+16101234567").putString("profile_avatar", "abc").serialize()); - mockRecipientResolve("+16101234567", 1); - - RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class)); - JobData converted = subject.migrate(testData); - JsonJobData data = JsonJobData.deserialize(converted.getData()); - - assertEquals("RetrieveProfileAvatarJob", converted.getFactoryKey()); - assertEquals("RetrieveProfileAvatarJob::" + RecipientId.from(1).toQueueKey(), converted.getQueueKey()); - assertEquals("1", data.getString("recipient")); - - - new RetrieveProfileAvatarJob.Factory().create(mock(Job.Parameters.class), converted.getData()); - } - - @Test - public void migrate_multiDeviceReadUpdateJob_empty() throws Exception { - JobData testData = new JobData("MultiDeviceReadUpdateJob", null, -1, -1, new JsonJobData.Builder().putStringArray("message_ids", new String[0]).serialize()); - - RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class)); - JobData converted = subject.migrate(testData); - JsonJobData data = JsonJobData.deserialize(converted.getData()); - - assertEquals("MultiDeviceReadUpdateJob", converted.getFactoryKey()); - assertNull(converted.getQueueKey()); - assertEquals(0, data.getStringArray("message_ids").length); - - new MultiDeviceReadUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData()); - } - - @Test - public void migrate_multiDeviceReadUpdateJob_twoIds() throws Exception { - OldSerializableSyncMessageId id1 = new OldSerializableSyncMessageId("+16101234567", 1); - OldSerializableSyncMessageId id2 = new OldSerializableSyncMessageId("+16101112222", 2); - - JobData testData = new JobData("MultiDeviceReadUpdateJob", null, -1, -1, new JsonJobData.Builder().putStringArray("message_ids", new String[]{ JsonUtils.toJson(id1), JsonUtils.toJson(id2) }).serialize()); - mockRecipientResolve("+16101234567", 1); - mockRecipientResolve("+16101112222", 2); - - RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class)); - JobData converted = subject.migrate(testData); - JsonJobData data = JsonJobData.deserialize(converted.getData()); - - assertEquals("MultiDeviceReadUpdateJob", converted.getFactoryKey()); - assertNull(converted.getQueueKey()); - - String[] updated = data.getStringArray("message_ids"); - assertEquals(2, updated.length); - - assertEquals(JsonUtils.toJson(new NewSerializableSyncMessageId("1", 1)), updated[0]); - assertEquals(JsonUtils.toJson(new NewSerializableSyncMessageId("2", 2)), updated[1]); - - new MultiDeviceReadUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData()); - } - - @Test - public void migrate_pushMediaSendJob() throws Exception { - JobData testData = new JobData("PushMediaSendJob", "+16101234567", -1, -1, new JsonJobData.Builder().putLong("message_id", 1).serialize()); - mockRecipientResolve("+16101234567", 1); - - RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class)); - JobData converted = subject.migrate(testData); - JsonJobData data = JsonJobData.deserialize(converted.getData()); - - assertEquals("PushMediaSendJob", converted.getFactoryKey()); - assertEquals(RecipientId.from(1).toQueueKey(), converted.getQueueKey()); - assertEquals(1, data.getLong("message_id")); - - new IndividualSendJob.Factory().create(mock(Job.Parameters.class), converted.getData()); - } - - private void mockRecipientResolve(String address, long recipientId) { - Recipient mockRecipient = mockRecipient(recipientId); - recipientMockedStatic.when(() -> Recipient.external(any(), eq(address))).thenReturn(mockRecipient); - } - - private Recipient mockRecipient(long id) { - Recipient recipient = mock(Recipient.class); - when(recipient.getId()).thenReturn(RecipientId.from(id)); - return recipient; - } - -} diff --git a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdJobMigrationTest.kt b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdJobMigrationTest.kt new file mode 100644 index 0000000000..c3d9692d9c --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/RecipientIdJobMigrationTest.kt @@ -0,0 +1,365 @@ +package org.thoughtcrime.securesms.jobmanager.migrations + +import android.app.Application +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.unmockkAll +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData +import org.thoughtcrime.securesms.jobmanager.JsonJobData +import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration.NewSerializableSyncMessageId +import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration.OldSerializableSyncMessageId +import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob +import org.thoughtcrime.securesms.jobs.IndividualSendJob +import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob +import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob +import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob +import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob +import org.thoughtcrime.securesms.jobs.PushGroupSendJob +import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob +import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob +import org.thoughtcrime.securesms.recipients.LiveRecipient +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.util.JsonUtils + +class RecipientIdJobMigrationTest { + @Before + fun setup() { + mockkObject(Recipient) + every { Recipient.live(any()) } returns mockk(relaxed = true) + } + + @After + fun cleanup() { + unmockkAll() + } + + @Test + fun migrate_multiDeviceContactUpdateJob() { + val testData = JobData( + factoryKey = "MultiDeviceContactUpdateJob", + queueKey = "MultiDeviceContactUpdateJob", + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder().putBoolean("force_sync", false).putString("address", "+16101234567").serialize() + ) + mockRecipientResolve("+16101234567", 1) + + val subject = RecipientIdJobMigration(mockk()) + val converted = subject.migrate(testData) + val data = JsonJobData.deserialize(converted.data) + + assertEquals("MultiDeviceContactUpdateJob", converted.factoryKey) + assertEquals("MultiDeviceContactUpdateJob", converted.queueKey) + assertFalse(data.getBoolean("force_sync")) + assertFalse(data.hasString("address")) + assertEquals("1", data.getString("recipient")) + + MultiDeviceContactUpdateJob.Factory().create(Job.Parameters.Builder().build(), converted.data) + } + + @Test + fun migrate_multiDeviceViewOnceOpenJob() { + val oldId = OldSerializableSyncMessageId("+16101234567", 1) + val testData = JobData( + factoryKey = "MultiDeviceRevealUpdateJob", + queueKey = null, + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder().putString("message_id", JsonUtils.toJson(oldId)).serialize() + ) + mockRecipientResolve("+16101234567", 1) + + val subject = RecipientIdJobMigration(mockk()) + val converted = subject.migrate(testData) + val data = JsonJobData.deserialize(converted.data) + + assertEquals("MultiDeviceRevealUpdateJob", converted.factoryKey) + assertNull(converted.queueKey) + assertEquals(JsonUtils.toJson(NewSerializableSyncMessageId("1", 1)), data.getString("message_id")) + + MultiDeviceViewOnceOpenJob.Factory().create(Job.Parameters.Builder().build(), converted.data) + } + + @Test + fun migrate_sendDeliveryReceiptJob() { + val testData = JobData( + factoryKey = "SendDeliveryReceiptJob", + queueKey = null, + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder().putString("address", "+16101234567") + .putLong("message_id", 1) + .putLong("timestamp", 2) + .serialize() + ) + mockRecipientResolve("+16101234567", 1) + + val subject = RecipientIdJobMigration(mockk()) + val converted = subject.migrate(testData) + val data = JsonJobData.deserialize(converted.data) + + assertEquals("SendDeliveryReceiptJob", converted.factoryKey) + assertNull(converted.queueKey) + assertEquals("1", data.getString("recipient")) + assertEquals(1, data.getLong("message_id")) + assertEquals(2, data.getLong("timestamp")) + + SendDeliveryReceiptJob.Factory().create(Job.Parameters.Builder().build(), converted.data) + } + + @Test + fun migrate_multiDeviceVerifiedUpdateJob() { + val testData = JobData( + factoryKey = "MultiDeviceVerifiedUpdateJob", + queueKey = "__MULTI_DEVICE_VERIFIED_UPDATE__", + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder().putString("destination", "+16101234567") + .putString("identity_key", "abcd") + .putInt("verified_status", 1) + .putLong("timestamp", 123) + .serialize() + ) + mockRecipientResolve("+16101234567", 1) + + val subject = RecipientIdJobMigration(mockk()) + val converted = subject.migrate(testData) + val data = JsonJobData.deserialize(converted.data) + + assertEquals("MultiDeviceVerifiedUpdateJob", converted.factoryKey) + assertEquals("__MULTI_DEVICE_VERIFIED_UPDATE__", converted.queueKey) + assertEquals("abcd", data.getString("identity_key")) + assertEquals(1, data.getInt("verified_status")) + assertEquals(123, data.getLong("timestamp")) + assertEquals("1", data.getString("destination")) + + MultiDeviceVerifiedUpdateJob.Factory().create(Job.Parameters.Builder().build(), converted.data) + } + + @Test + fun migrate_pushGroupSendJob_null() { + val testData = JobData( + factoryKey = "PushGroupSendJob", + queueKey = "someGroupId", + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder().putString("filter_address", null) + .putLong("message_id", 123) + .serialize() + ) + mockRecipientResolve("someGroupId", 5) + + val subject = RecipientIdJobMigration(mockk()) + val converted = subject.migrate(testData) + val data = JsonJobData.deserialize(converted.data) + + assertEquals("PushGroupSendJob", converted.factoryKey) + assertEquals(RecipientId.from(5).toQueueKey(), converted.queueKey) + assertNull(data.getString("filter_recipient")) + assertFalse(data.hasString("filter_address")) + + PushGroupSendJob.Factory().create(Job.Parameters.Builder().build(), converted.data) + } + + @Test + fun migrate_pushGroupSendJob_nonNull() { + val testData = JobData( + factoryKey = "PushGroupSendJob", + queueKey = "someGroupId", + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder().putString("filter_address", "+16101234567") + .putLong("message_id", 123) + .serialize() + ) + mockRecipientResolve("+16101234567", 1) + mockRecipientResolve("someGroupId", 5) + + val subject = RecipientIdJobMigration(mockk()) + val converted = subject.migrate(testData) + val data = JsonJobData.deserialize(converted.data) + + assertEquals("PushGroupSendJob", converted.factoryKey) + assertEquals(RecipientId.from(5).toQueueKey(), converted.queueKey) + assertEquals("1", data.getString("filter_recipient")) + assertFalse(data.hasString("filter_address")) + + PushGroupSendJob.Factory().create(Job.Parameters.Builder().build(), converted.data) + } + + @Test + fun migrate_directoryRefreshJob_null() { + val testData = JobData( + factoryKey = "DirectoryRefreshJob", + queueKey = "DirectoryRefreshJob", + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder() + .putString("address", null) + .putBoolean("notify_of_new_users", true) + .serialize() + ) + + val subject = RecipientIdJobMigration(mockk()) + val converted = subject.migrate(testData) + val data = JsonJobData.deserialize(converted.data) + + assertEquals("DirectoryRefreshJob", converted.factoryKey) + assertEquals("DirectoryRefreshJob", converted.queueKey) + assertNull(data.getString("recipient")) + assertTrue(data.getBoolean("notify_of_new_users")) + assertFalse(data.hasString("address")) + + DirectoryRefreshJob.Factory().create(Job.Parameters.Builder().build(), converted.data) + } + + @Test + fun migrate_directoryRefreshJob_nonNull() { + val testData = JobData( + factoryKey = "DirectoryRefreshJob", + queueKey = "DirectoryRefreshJob", + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder() + .putString("address", "+16101234567") + .putBoolean("notify_of_new_users", true) + .serialize() + ) + mockRecipientResolve("+16101234567", 1) + + val subject = RecipientIdJobMigration(mockk()) + val converted = subject.migrate(testData) + val data = JsonJobData.deserialize(converted.data) + + assertEquals("DirectoryRefreshJob", converted.factoryKey) + assertEquals("DirectoryRefreshJob", converted.queueKey) + assertTrue(data.getBoolean("notify_of_new_users")) + assertEquals("1", data.getString("recipient")) + assertFalse(data.hasString("address")) + + DirectoryRefreshJob.Factory().create(Job.Parameters.Builder().build(), converted.data) + } + + @Test + fun migrate_retrieveProfileAvatarJob() { + val testData = JobData( + factoryKey = "RetrieveProfileAvatarJob", + queueKey = "RetrieveProfileAvatarJob+16101234567", + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder() + .putString("address", "+16101234567") + .putString("profile_avatar", "abc") + .serialize() + ) + mockRecipientResolve("+16101234567", 1) + + val subject = RecipientIdJobMigration(mockk()) + val converted = subject.migrate(testData) + val data = JsonJobData.deserialize(converted.data) + + assertEquals("RetrieveProfileAvatarJob", converted.factoryKey) + assertEquals("RetrieveProfileAvatarJob::" + RecipientId.from(1).toQueueKey(), converted.queueKey) + assertEquals("1", data.getString("recipient")) + + RetrieveProfileAvatarJob.Factory().create(Job.Parameters.Builder().build(), converted.data) + } + + @Test + fun migrate_multiDeviceReadUpdateJob_empty() { + val testData = JobData( + factoryKey = "MultiDeviceReadUpdateJob", + queueKey = null, + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder() + .putStringArray("message_ids", arrayOfNulls(0)) + .serialize() + ) + + val subject = RecipientIdJobMigration(mockk()) + val converted = subject.migrate(testData) + val data = JsonJobData.deserialize(converted.data) + + assertEquals("MultiDeviceReadUpdateJob", converted.factoryKey) + assertNull(converted.queueKey) + assertEquals(0, data.getStringArray("message_ids").size) + + MultiDeviceReadUpdateJob.Factory().create(Job.Parameters.Builder().build(), converted.data) + } + + @Test + fun migrate_multiDeviceReadUpdateJob_twoIds() { + val id1 = OldSerializableSyncMessageId("+16101234567", 1) + val id2 = OldSerializableSyncMessageId("+16101112222", 2) + + val testData = JobData( + factoryKey = "MultiDeviceReadUpdateJob", + queueKey = null, + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder() + .putStringArray("message_ids", arrayOf(JsonUtils.toJson(id1), JsonUtils.toJson(id2))) + .serialize() + ) + mockRecipientResolve("+16101234567", 1) + mockRecipientResolve("+16101112222", 2) + + val subject = RecipientIdJobMigration(mockk()) + val converted = subject.migrate(testData) + val data = JsonJobData.deserialize(converted.data) + + assertEquals("MultiDeviceReadUpdateJob", converted.factoryKey) + assertNull(converted.queueKey) + + val updated = data.getStringArray("message_ids") + assertEquals(2, updated.size) + + assertEquals(JsonUtils.toJson(NewSerializableSyncMessageId("1", 1)), updated[0]) + assertEquals(JsonUtils.toJson(NewSerializableSyncMessageId("2", 2)), updated[1]) + + MultiDeviceReadUpdateJob.Factory().create(Job.Parameters.Builder().build(), converted.data) + } + + @Test + fun migrate_pushMediaSendJob() { + val testData = JobData( + factoryKey = "PushMediaSendJob", + queueKey = "+16101234567", + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder().putLong("message_id", 1).serialize() + ) + mockRecipientResolve("+16101234567", 1) + + val subject = RecipientIdJobMigration(mockk()) + val converted = subject.migrate(testData) + val data = JsonJobData.deserialize(converted.data) + + assertEquals("PushMediaSendJob", converted.factoryKey) + assertEquals(RecipientId.from(1).toQueueKey(), converted.queueKey) + assertEquals(1, data.getLong("message_id")) + + IndividualSendJob.Factory().create(Job.Parameters.Builder().build(), converted.data) + } + + private fun mockRecipientResolve(address: String, recipientId: Long) { + every { Recipient.external(any(), address) } returns mockRecipient(recipientId) + } + + private fun mockRecipient(id: Long): Recipient { + return mockk { + every { this@mockk.id } returns RecipientId.from(id) + } + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SendReadReceiptsJobMigrationTest.java b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SendReadReceiptsJobMigrationTest.java deleted file mode 100644 index 96a2fbb535..0000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SendReadReceiptsJobMigrationTest.java +++ /dev/null @@ -1,107 +0,0 @@ -package org.thoughtcrime.securesms.jobmanager.migrations; - -import org.junit.Test; -import org.thoughtcrime.securesms.database.MessageTable; -import org.thoughtcrime.securesms.jobmanager.JsonJobData; -import org.thoughtcrime.securesms.jobmanager.JobMigration; -import org.thoughtcrime.securesms.jobs.SendReadReceiptJob; -import org.thoughtcrime.securesms.recipients.RecipientId; - -import java.util.ArrayList; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class SendReadReceiptsJobMigrationTest { - - private final MessageTable mockDatabase = mock(MessageTable.class); - private final SendReadReceiptsJobMigration testSubject = new SendReadReceiptsJobMigration(mockDatabase); - - @Test - public void givenSendReadReceiptJobDataWithoutThreadIdAndThreadIdFound_whenIMigrate_thenIInsertThreadId() { - // GIVEN - SendReadReceiptJob job = new SendReadReceiptJob(1, RecipientId.from(2), new ArrayList<>(), new ArrayList<>()); - JobMigration.JobData jobData = new JobMigration.JobData(job.getFactoryKey(), - "asdf", - -1, - -1, - new JsonJobData.Builder() - .putString("recipient", RecipientId.from(2).serialize()) - .putLongArray("message_ids", new long[]{1, 2, 3, 4, 5}) - .putLong("timestamp", 292837649).serialize()); - when(mockDatabase.getThreadIdForMessage(anyLong())).thenReturn(1234L); - - // WHEN - JobMigration.JobData result = testSubject.migrate(jobData); - JsonJobData data = JsonJobData.deserialize(result.getData()); - - // THEN - assertEquals(1234L, data.getLong("thread")); - assertEquals(RecipientId.from(2).serialize(), data.getString("recipient")); - assertTrue(data.hasLongArray("message_ids")); - assertTrue(data.hasLong("timestamp")); - } - - @Test - public void givenSendReadReceiptJobDataWithoutThreadIdAndThreadIdNotFound_whenIMigrate_thenIGetAFailingJob() { - // GIVEN - SendReadReceiptJob job = new SendReadReceiptJob(1, RecipientId.from(2), new ArrayList<>(), new ArrayList<>()); - JobMigration.JobData jobData = new JobMigration.JobData(job.getFactoryKey(), - "asdf", - -1, - -1, - new JsonJobData.Builder() - .putString("recipient", RecipientId.from(2).serialize()) - .putLongArray("message_ids", new long[]{}) - .putLong("timestamp", 292837649).serialize()); - when(mockDatabase.getThreadIdForMessage(anyLong())).thenReturn(-1L); - - // WHEN - JobMigration.JobData result = testSubject.migrate(jobData); - - // THEN - assertEquals("FailingJob", result.getFactoryKey()); - } - - @Test - public void givenSendReadReceiptJobDataWithThreadId_whenIMigrate_thenIDoNotReplace() { - // GIVEN - SendReadReceiptJob job = new SendReadReceiptJob(1, RecipientId.from(2), new ArrayList<>(), new ArrayList<>()); - JobMigration.JobData jobData = new JobMigration.JobData(job.getFactoryKey(), "asdf", -1, -1, job.serialize()); - - // WHEN - JobMigration.JobData result = testSubject.migrate(jobData); - - // THEN - assertEquals(jobData, result); - } - - @Test - public void givenSomeOtherJobDataWithThreadId_whenIMigrate_thenIDoNotReplace() { - // GIVEN - JobMigration.JobData jobData = new JobMigration.JobData("SomeOtherJob", "asdf", -1, -1, new JsonJobData.Builder().putLong("thread", 1).serialize()); - - // WHEN - JobMigration.JobData result = testSubject.migrate(jobData); - - // THEN - assertEquals(jobData, result); - } - - @Test - public void givenSomeOtherJobDataWithoutThreadId_whenIMigrate_thenIDoNotReplace() { - // GIVEN - JobMigration.JobData jobData = new JobMigration.JobData("SomeOtherJob", "asdf", -1, -1, new JsonJobData.Builder().serialize()); - - // WHEN - JobMigration.JobData result = testSubject.migrate(jobData); - - // THEN - assertEquals(jobData, result); - } - - -} \ No newline at end of file diff --git a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SendReadReceiptsJobMigrationTest.kt b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SendReadReceiptsJobMigrationTest.kt new file mode 100644 index 0000000000..754fef32fe --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SendReadReceiptsJobMigrationTest.kt @@ -0,0 +1,104 @@ +package org.thoughtcrime.securesms.jobmanager.migrations + +import io.mockk.every +import io.mockk.mockk +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.thoughtcrime.securesms.database.MessageTable +import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData +import org.thoughtcrime.securesms.jobmanager.JsonJobData +import org.thoughtcrime.securesms.jobs.SendReadReceiptJob +import org.thoughtcrime.securesms.recipients.RecipientId + +class SendReadReceiptsJobMigrationTest { + private val mockDatabase = mockk() + private val testSubject = SendReadReceiptsJobMigration(mockDatabase) + + @Test + fun givenSendReadReceiptJobDataWithoutThreadIdAndThreadIdFound_whenIMigrate_thenIInsertThreadId() { + // GIVEN + val job = SendReadReceiptJob(1, RecipientId.from(2), ArrayList(), ArrayList()) + val jobData = JobData( + factoryKey = job.factoryKey, + queueKey = "asdf", + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder() + .putString("recipient", RecipientId.from(2).serialize()) + .putLongArray("message_ids", longArrayOf(1, 2, 3, 4, 5)) + .putLong("timestamp", 292837649).serialize() + ) + every { mockDatabase.getThreadIdForMessage(any()) } returns 1234L + + // WHEN + val result = testSubject.migrate(jobData) + val data = JsonJobData.deserialize(result.data) + + // THEN + assertEquals(1234L, data.getLong("thread")) + assertEquals(RecipientId.from(2).serialize(), data.getString("recipient")) + assertTrue(data.hasLongArray("message_ids")) + assertTrue(data.hasLong("timestamp")) + } + + @Test + fun givenSendReadReceiptJobDataWithoutThreadIdAndThreadIdNotFound_whenIMigrate_thenIGetAFailingJob() { + // GIVEN + val job = SendReadReceiptJob(1, RecipientId.from(2), ArrayList(), ArrayList()) + val jobData = JobData( + factoryKey = job.factoryKey, + queueKey = "asdf", + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder() + .putString("recipient", RecipientId.from(2).serialize()) + .putLongArray("message_ids", longArrayOf()) + .putLong("timestamp", 292837649).serialize() + ) + every { mockDatabase.getThreadIdForMessage(any()) } returns -1L + + // WHEN + val result = testSubject.migrate(jobData) + + // THEN + assertEquals("FailingJob", result.factoryKey) + } + + @Test + fun givenSendReadReceiptJobDataWithThreadId_whenIMigrate_thenIDoNotReplace() { + // GIVEN + val job = SendReadReceiptJob(1, RecipientId.from(2), ArrayList(), ArrayList()) + val jobData = JobData(job.factoryKey, "asdf", -1, -1, job.serialize()) + + // WHEN + val result = testSubject.migrate(jobData) + + // THEN + assertEquals(jobData, result) + } + + @Test + fun givenSomeOtherJobDataWithThreadId_whenIMigrate_thenIDoNotReplace() { + // GIVEN + val jobData = JobData("SomeOtherJob", "asdf", -1, -1, JsonJobData.Builder().putLong("thread", 1).serialize()) + + // WHEN + val result = testSubject.migrate(jobData) + + // THEN + assertEquals(jobData, result) + } + + @Test + fun givenSomeOtherJobDataWithoutThreadId_whenIMigrate_thenIDoNotReplace() { + // GIVEN + val jobData = JobData("SomeOtherJob", "asdf", -1, -1, JsonJobData.Builder().serialize()) + + // WHEN + val result = testSubject.migrate(jobData) + + // THEN + assertEquals(jobData, result) + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SenderKeyDistributionSendJobRecipientMigrationTest.java b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SenderKeyDistributionSendJobRecipientMigrationTest.java deleted file mode 100644 index 0ec33c0090..0000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SenderKeyDistributionSendJobRecipientMigrationTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.thoughtcrime.securesms.jobmanager.migrations; - -import org.junit.Test; -import org.thoughtcrime.securesms.database.GroupTable; -import org.thoughtcrime.securesms.database.model.GroupRecord; -import org.thoughtcrime.securesms.groups.GroupId; -import org.thoughtcrime.securesms.jobmanager.JsonJobData; -import org.thoughtcrime.securesms.jobmanager.JobMigration; -import org.thoughtcrime.securesms.jobs.FailingJob; -import org.thoughtcrime.securesms.jobs.SenderKeyDistributionSendJob; -import org.thoughtcrime.securesms.recipients.RecipientId; -import org.thoughtcrime.securesms.util.Util; - -import java.util.Optional; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class SenderKeyDistributionSendJobRecipientMigrationTest { - - private final GroupTable mockDatabase = mock(GroupTable.class); - private final SenderKeyDistributionSendJobRecipientMigration testSubject = new SenderKeyDistributionSendJobRecipientMigration(mockDatabase); - - private static final GroupId GROUP_ID = GroupId.pushOrThrow(Util.getSecretBytes(32)); - - @Test - public void normalMigration() { - // GIVEN - JobMigration.JobData jobData = new JobMigration.JobData(SenderKeyDistributionSendJob.KEY, - "asdf", - -1, - -1, - new JsonJobData.Builder() - .putString("recipient_id", RecipientId.from(1).serialize()) - .putBlobAsString("group_id", GROUP_ID.getDecodedId()) - .serialize()); - - GroupRecord mockGroup = mock(GroupRecord.class); - when(mockGroup.getRecipientId()).thenReturn(RecipientId.from(2)); - when(mockDatabase.getGroup(GROUP_ID)).thenReturn(Optional.of(mockGroup)); - - // WHEN - JobMigration.JobData result = testSubject.migrate(jobData); - JsonJobData data = JsonJobData.deserialize(result.getData()); - - // THEN - assertEquals(RecipientId.from(1).serialize(), data.getString("recipient_id")); - assertEquals(RecipientId.from(2).serialize(), data.getString("thread_recipient_id")); - } - - @Test - public void cannotFindGroup() { - // GIVEN - JobMigration.JobData jobData = new JobMigration.JobData(SenderKeyDistributionSendJob.KEY, - "asdf", - -1, - -1, - new JsonJobData.Builder() - .putString("recipient_id", RecipientId.from(1).serialize()) - .putBlobAsString("group_id", GROUP_ID.getDecodedId()) - .serialize()); - - // WHEN - JobMigration.JobData result = testSubject.migrate(jobData); - - // THEN - assertEquals(FailingJob.KEY, result.getFactoryKey()); - } - - @Test - public void missingGroupId() { - // GIVEN - JobMigration.JobData jobData = new JobMigration.JobData(SenderKeyDistributionSendJob.KEY, - "asdf", - -1, - -1, - new JsonJobData.Builder() - .putString("recipient_id", RecipientId.from(1).serialize()) - .serialize()); - - // WHEN - JobMigration.JobData result = testSubject.migrate(jobData); - - // THEN - assertEquals(FailingJob.KEY, result.getFactoryKey()); - } -} \ No newline at end of file diff --git a/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SenderKeyDistributionSendJobRecipientMigrationTest.kt b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SenderKeyDistributionSendJobRecipientMigrationTest.kt new file mode 100644 index 0000000000..a30abebf02 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/jobmanager/migrations/SenderKeyDistributionSendJobRecipientMigrationTest.kt @@ -0,0 +1,94 @@ +package org.thoughtcrime.securesms.jobmanager.migrations + +import io.mockk.every +import io.mockk.mockk +import org.junit.Assert.assertEquals +import org.junit.Test +import org.thoughtcrime.securesms.database.GroupTable +import org.thoughtcrime.securesms.database.model.GroupRecord +import org.thoughtcrime.securesms.groups.GroupId +import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData +import org.thoughtcrime.securesms.jobmanager.JsonJobData +import org.thoughtcrime.securesms.jobs.FailingJob +import org.thoughtcrime.securesms.jobs.SenderKeyDistributionSendJob +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.util.Util +import java.util.Optional + +class SenderKeyDistributionSendJobRecipientMigrationTest { + private val mockDatabase = mockk(relaxed = true) + private val testSubject = SenderKeyDistributionSendJobRecipientMigration(mockDatabase) + + @Test + fun normalMigration() { + // GIVEN + val jobData = JobData( + factoryKey = SenderKeyDistributionSendJob.KEY, + queueKey = "asdf", + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder() + .putString("recipient_id", RecipientId.from(1).serialize()) + .putBlobAsString("group_id", GROUP_ID.decodedId) + .serialize() + ) + + val mockGroup = mockk { + every { recipientId } returns RecipientId.from(2) + } + every { mockDatabase.getGroup(GROUP_ID) } returns Optional.of(mockGroup) + + // WHEN + val result = testSubject.migrate(jobData) + val data = JsonJobData.deserialize(result.data) + + // THEN + assertEquals(RecipientId.from(1).serialize(), data.getString("recipient_id")) + assertEquals(RecipientId.from(2).serialize(), data.getString("thread_recipient_id")) + } + + @Test + fun cannotFindGroup() { + // GIVEN + val jobData = JobData( + factoryKey = SenderKeyDistributionSendJob.KEY, + queueKey = "asdf", + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder() + .putString("recipient_id", RecipientId.from(1).serialize()) + .putBlobAsString("group_id", GROUP_ID.decodedId) + .serialize() + ) + + // WHEN + val result = testSubject.migrate(jobData) + + // THEN + assertEquals(FailingJob.KEY, result.factoryKey) + } + + @Test + fun missingGroupId() { + // GIVEN + val jobData = JobData( + factoryKey = SenderKeyDistributionSendJob.KEY, + queueKey = "asdf", + maxAttempts = -1, + lifespan = -1, + data = JsonJobData.Builder() + .putString("recipient_id", RecipientId.from(1).serialize()) + .serialize() + ) + + // WHEN + val result = testSubject.migrate(jobData) + + // THEN + assertEquals(FailingJob.KEY, result.factoryKey) + } + + companion object { + private val GROUP_ID: GroupId = GroupId.pushOrThrow(Util.getSecretBytes(32)) + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/payments/GeographicalRestrictionsTest.java b/app/src/test/java/org/thoughtcrime/securesms/payments/GeographicalRestrictionsTest.java deleted file mode 100644 index a346e8b985..0000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/payments/GeographicalRestrictionsTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.thoughtcrime.securesms.payments; - -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.thoughtcrime.securesms.util.RemoteConfig; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; - -public final class GeographicalRestrictionsTest { - - @Rule - public MockitoRule rule = MockitoJUnit.rule(); - - @Mock - private MockedStatic remoteConfigMockedStatic; - - @Test - public void e164Allowed_general() { - when(RemoteConfig.paymentsCountryBlocklist()).thenReturn(""); - assertTrue(GeographicalRestrictions.e164Allowed("+15551234567")); - - when(RemoteConfig.paymentsCountryBlocklist()).thenReturn("1"); - assertFalse(GeographicalRestrictions.e164Allowed("+15551234567")); - - when(RemoteConfig.paymentsCountryBlocklist()).thenReturn("1,44"); - assertFalse(GeographicalRestrictions.e164Allowed("+15551234567")); - assertFalse(GeographicalRestrictions.e164Allowed("+445551234567")); - assertTrue(GeographicalRestrictions.e164Allowed("+525551234567")); - - when(RemoteConfig.paymentsCountryBlocklist()).thenReturn("1 234,44"); - assertFalse(GeographicalRestrictions.e164Allowed("+12341234567")); - assertTrue(GeographicalRestrictions.e164Allowed("+15551234567")); - assertTrue(GeographicalRestrictions.e164Allowed("+525551234567")); - assertTrue(GeographicalRestrictions.e164Allowed("+2345551234567")); - } - - @Test - public void e164Allowed_nullNotAllowed() { - assertFalse(GeographicalRestrictions.e164Allowed(null)); - } -} diff --git a/app/src/test/java/org/thoughtcrime/securesms/payments/GeographicalRestrictionsTest.kt b/app/src/test/java/org/thoughtcrime/securesms/payments/GeographicalRestrictionsTest.kt new file mode 100644 index 0000000000..7c533adf68 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/payments/GeographicalRestrictionsTest.kt @@ -0,0 +1,37 @@ +package org.thoughtcrime.securesms.payments + +import io.mockk.every +import io.mockk.mockkStatic +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.thoughtcrime.securesms.util.RemoteConfig + +class GeographicalRestrictionsTest { + @Test + fun e164Allowed_general() { + mockkStatic(RemoteConfig::class) { + every { RemoteConfig.paymentsCountryBlocklist } returns "" + assertTrue(GeographicalRestrictions.e164Allowed("+15551234567")) + + every { RemoteConfig.paymentsCountryBlocklist } returns "1" + assertFalse(GeographicalRestrictions.e164Allowed("+15551234567")) + + every { RemoteConfig.paymentsCountryBlocklist } returns "1,44" + assertFalse(GeographicalRestrictions.e164Allowed("+15551234567")) + assertFalse(GeographicalRestrictions.e164Allowed("+445551234567")) + assertTrue(GeographicalRestrictions.e164Allowed("+525551234567")) + + every { RemoteConfig.paymentsCountryBlocklist } returns "1 234,44" + assertFalse(GeographicalRestrictions.e164Allowed("+12341234567")) + assertTrue(GeographicalRestrictions.e164Allowed("+15551234567")) + assertTrue(GeographicalRestrictions.e164Allowed("+525551234567")) + assertTrue(GeographicalRestrictions.e164Allowed("+2345551234567")) + } + } + + @Test + fun e164Allowed_nullNotAllowed() { + assertFalse(GeographicalRestrictions.e164Allowed(null)) + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/registration/fcm/PushChallengeRequestTest.java b/app/src/test/java/org/thoughtcrime/securesms/registration/fcm/PushChallengeRequestTest.java deleted file mode 100644 index 8af17977f5..0000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/registration/fcm/PushChallengeRequestTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2024 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.registration.fcm; - -import android.app.Application; -import android.os.AsyncTask; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; - -import java.io.IOException; -import java.util.Optional; - -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.lessThan; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -@RunWith(RobolectricTestRunner.class) -@Config(manifest = Config.NONE, application = Application.class) -public final class PushChallengeRequestTest { - - @Test - public void getPushChallengeBlocking_returns_absent_if_times_out() { - SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class); - - Optional challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 50L); - - assertFalse(challenge.isPresent()); - } - - @Test - public void getPushChallengeBlocking_waits_for_specified_period() { - SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class); - - long startTime = System.currentTimeMillis(); - PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 250L); - long duration = System.currentTimeMillis() - startTime; - - assertThat(duration, greaterThanOrEqualTo(250L)); - } - - @Test - public void getPushChallengeBlocking_completes_fast_if_posted_to_event_bus() throws IOException { - SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class); - doAnswer(invocation -> { - AsyncTask.execute(() -> PushChallengeRequest.postChallengeResponse("CHALLENGE")); - return null; - }).when(signal).requestRegistrationPushChallenge("session ID", "token"); - - long startTime = System.currentTimeMillis(); - Optional challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 500L); - long duration = System.currentTimeMillis() - startTime; - - assertThat(duration, lessThan(500L)); - verify(signal).requestRegistrationPushChallenge("session ID", "token"); - verifyNoMoreInteractions(signal); - - assertTrue(challenge.isPresent()); - assertEquals("CHALLENGE", challenge.get()); - } - - @Test - public void getPushChallengeBlocking_returns_fast_if_no_fcm_token_supplied() { - SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class); - - long startTime = System.currentTimeMillis(); - PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.empty(), 500L); - long duration = System.currentTimeMillis() - startTime; - - assertThat(duration, lessThan(500L)); - } - - @Test - public void getPushChallengeBlocking_returns_absent_if_no_fcm_token_supplied() { - SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class); - - Optional challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.empty(), 500L); - - verifyNoInteractions(signal); - assertFalse(challenge.isPresent()); - } - - @Test - public void getPushChallengeBlocking_returns_absent_if_any_IOException_is_thrown() throws IOException { - SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class); - - doThrow(new IOException()).when(signal).requestRegistrationPushChallenge(any(), any()); - - Optional challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 500L); - - assertFalse(challenge.isPresent()); - } -} diff --git a/app/src/test/java/org/thoughtcrime/securesms/registration/fcm/PushChallengeRequestTest.kt b/app/src/test/java/org/thoughtcrime/securesms/registration/fcm/PushChallengeRequestTest.kt new file mode 100644 index 0000000000..ba424fafd3 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/registration/fcm/PushChallengeRequestTest.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ +package org.thoughtcrime.securesms.registration.fcm + +import android.app.Application +import android.os.AsyncTask +import io.mockk.called +import io.mockk.confirmVerified +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.greaterThanOrEqualTo +import org.hamcrest.Matchers.lessThan +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.whispersystems.signalservice.api.SignalServiceAccountManager +import java.io.IOException +import java.util.Optional + +@RunWith(RobolectricTestRunner::class) +@Config(manifest = Config.NONE, application = Application::class) +class PushChallengeRequestTest { + @Test + fun pushChallengeBlocking_returns_absent_if_times_out() { + val signal = mockk(relaxUnitFun = true) + + val challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 50L) + + assertFalse(challenge.isPresent) + } + + @Test + fun pushChallengeBlocking_waits_for_specified_period() { + val signal = mockk(relaxUnitFun = true) + + val startTime = System.currentTimeMillis() + PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 250L) + val duration = System.currentTimeMillis() - startTime + + assertThat(duration, greaterThanOrEqualTo(250L)) + } + + @Test + fun pushChallengeBlocking_completes_fast_if_posted_to_event_bus() { + val signal = mockk { + every { + requestRegistrationPushChallenge("session ID", "token") + } answers { + AsyncTask.execute { PushChallengeRequest.postChallengeResponse("CHALLENGE") } + } + } + + val startTime = System.currentTimeMillis() + val challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 500L) + val duration = System.currentTimeMillis() - startTime + + assertThat(duration, lessThan(500L)) + verify { signal.requestRegistrationPushChallenge("session ID", "token") } + confirmVerified(signal) + + assertTrue(challenge.isPresent) + assertEquals("CHALLENGE", challenge.get()) + } + + @Test + fun pushChallengeBlocking_returns_fast_if_no_fcm_token_supplied() { + val signal = mockk() + + val startTime = System.currentTimeMillis() + PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.empty(), 500L) + val duration = System.currentTimeMillis() - startTime + + assertThat(duration, lessThan(500L)) + } + + @Test + fun pushChallengeBlocking_returns_absent_if_no_fcm_token_supplied() { + val signal = mockk() + + val challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.empty(), 500L) + + verify { signal wasNot called } + assertFalse(challenge.isPresent) + } + + @Test + fun pushChallengeBlocking_returns_absent_if_any_IOException_is_thrown() { + val signal = mockk { + every { requestRegistrationPushChallenge(any(), any()) } throws IOException() + } + + val challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 500L) + + assertFalse(challenge.isPresent) + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/registration/v2/PinHashKbsDataTest.java b/app/src/test/java/org/thoughtcrime/securesms/registration/v2/PinHashKbsDataTest.java deleted file mode 100644 index 5457bdfceb..0000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/registration/v2/PinHashKbsDataTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.thoughtcrime.securesms.registration.v2; - -import org.junit.Test; -import org.signal.core.util.StreamUtil; -import org.signal.libsignal.svr2.PinHash; -import org.thoughtcrime.securesms.registration.testdata.KbsTestVector; -import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException; -import org.whispersystems.signalservice.api.kbs.KbsData; -import org.whispersystems.signalservice.api.kbs.MasterKey; -import org.whispersystems.signalservice.api.kbs.PinHashUtil; -import org.whispersystems.signalservice.internal.util.JsonUtil; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.thoughtcrime.securesms.testutil.SecureRandomTestUtil.mockRandom; - -public final class PinHashKbsDataTest { - - @Test - public void vectors_createNewKbsData() throws IOException { - for (KbsTestVector vector : getKbsTestVectorList()) { - PinHash pinHash = fromArgon2Hash(vector.getArgon2Hash()); - - KbsData kbsData = PinHashUtil.createNewKbsData(pinHash, MasterKey.createNew(mockRandom(vector.getMasterKey()))); - - assertArrayEquals(vector.getMasterKey(), kbsData.getMasterKey().serialize()); - assertArrayEquals(vector.getIvAndCipher(), kbsData.getCipherText()); - assertArrayEquals(vector.getKbsAccessKey(), kbsData.getKbsAccessKey()); - assertEquals(vector.getRegistrationLock(), kbsData.getMasterKey().deriveRegistrationLock()); - } - } - - @Test - public void vectors_decryptKbsDataIVCipherText() throws IOException, InvalidCiphertextException { - for (KbsTestVector vector : getKbsTestVectorList()) { - PinHash hashedPin = fromArgon2Hash(vector.getArgon2Hash()); - - KbsData kbsData = PinHashUtil.decryptSvrDataIVCipherText(hashedPin, vector.getIvAndCipher()); - - assertArrayEquals(vector.getMasterKey(), kbsData.getMasterKey().serialize()); - assertArrayEquals(vector.getIvAndCipher(), kbsData.getCipherText()); - assertArrayEquals(vector.getKbsAccessKey(), kbsData.getKbsAccessKey()); - assertEquals(vector.getRegistrationLock(), kbsData.getMasterKey().deriveRegistrationLock()); - } - } - - private static KbsTestVector[] getKbsTestVectorList() throws IOException { - try (InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("data/kbs_vectors.json")) { - - KbsTestVector[] data = JsonUtil.fromJson(StreamUtil.readFullyAsString(resourceAsStream), KbsTestVector[].class); - - assertTrue(data.length > 0); - - return data; - } - } - - public static PinHash fromArgon2Hash(byte[] argon2Hash64) { - if (argon2Hash64.length != 64) throw new AssertionError(); - - byte[] K = Arrays.copyOfRange(argon2Hash64, 0, 32); - byte[] kbsAccessKey = Arrays.copyOfRange(argon2Hash64, 32, 64); - - PinHash mocked = mock(PinHash.class); - when(mocked.encryptionKey()).thenReturn(K); - when(mocked.accessKey()).thenReturn(kbsAccessKey); - - return mocked; - } -} diff --git a/app/src/test/java/org/thoughtcrime/securesms/registration/v2/PinHashKbsDataTest.kt b/app/src/test/java/org/thoughtcrime/securesms/registration/v2/PinHashKbsDataTest.kt new file mode 100644 index 0000000000..96f4783998 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/registration/v2/PinHashKbsDataTest.kt @@ -0,0 +1,72 @@ +package org.thoughtcrime.securesms.registration.v2 + +import io.mockk.every +import io.mockk.mockk +import org.junit.Assert.assertArrayEquals +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.signal.core.util.StreamUtil +import org.signal.libsignal.svr2.PinHash +import org.thoughtcrime.securesms.registration.v2.testdata.KbsTestVector +import org.thoughtcrime.securesms.testutil.SecureRandomTestUtil +import org.whispersystems.signalservice.api.kbs.MasterKey +import org.whispersystems.signalservice.api.kbs.PinHashUtil.createNewKbsData +import org.whispersystems.signalservice.api.kbs.PinHashUtil.decryptSvrDataIVCipherText +import org.whispersystems.signalservice.internal.util.JsonUtil + +class PinHashKbsDataTest { + @Test + fun vectors_createNewKbsData() { + for (vector in kbsTestVectorList) { + val pinHash = fromArgon2Hash(vector.argon2Hash) + + val kbsData = createNewKbsData(pinHash, MasterKey.createNew(SecureRandomTestUtil.mockRandom(vector.masterKey))) + + assertArrayEquals(vector.masterKey, kbsData.masterKey.serialize()) + assertArrayEquals(vector.ivAndCipher, kbsData.cipherText) + assertArrayEquals(vector.kbsAccessKey, kbsData.kbsAccessKey) + assertEquals(vector.registrationLock, kbsData.masterKey.deriveRegistrationLock()) + } + } + + @Test + fun vectors_decryptKbsDataIVCipherText() { + for (vector in kbsTestVectorList) { + val hashedPin = fromArgon2Hash(vector.argon2Hash) + + val kbsData = decryptSvrDataIVCipherText(hashedPin, vector.ivAndCipher) + + assertArrayEquals(vector.masterKey, kbsData.masterKey.serialize()) + assertArrayEquals(vector.ivAndCipher, kbsData.cipherText) + assertArrayEquals(vector.kbsAccessKey, kbsData.kbsAccessKey) + assertEquals(vector.registrationLock, kbsData.masterKey.deriveRegistrationLock()) + } + } + + companion object { + private val kbsTestVectorList: Array + get() { + ClassLoader.getSystemClassLoader().getResourceAsStream("data/kbs_vectors.json").use { resourceAsStream -> + val data: Array = JsonUtil.fromJson( + StreamUtil.readFullyAsString(resourceAsStream), + Array::class.java + ) + assertTrue(data.isNotEmpty()) + return data + } + } + + fun fromArgon2Hash(argon2Hash64: ByteArray): PinHash { + if (argon2Hash64.size != 64) throw AssertionError() + + val k = argon2Hash64.copyOfRange(0, 32) + val kbsAccessKey = argon2Hash64.copyOfRange(32, 64) + + return mockk { + every { encryptionKey() } returns k + every { accessKey() } returns kbsAccessKey + } + } + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/registration/v2/testdata/KbsTestVector.java b/app/src/test/java/org/thoughtcrime/securesms/registration/v2/testdata/KbsTestVector.java deleted file mode 100644 index 4e5826fcca..0000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/registration/v2/testdata/KbsTestVector.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.thoughtcrime.securesms.registration.testdata; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import org.thoughtcrime.securesms.testutil.HexDeserializer; - -public final class KbsTestVector { - - @JsonProperty("backup_id") - @JsonDeserialize(using = HexDeserializer.class) - private byte[] backupId; - - @JsonProperty("argon2_hash") - @JsonDeserialize(using = HexDeserializer.class) - private byte[] argon2Hash; - - @JsonProperty("pin") - private String pin; - - @JsonProperty("registration_lock") - private String registrationLock; - - @JsonProperty("master_key") - @JsonDeserialize(using = HexDeserializer.class) - private byte[] masterKey; - - @JsonProperty("kbs_access_key") - @JsonDeserialize(using = HexDeserializer.class) - private byte[] kbsAccessKey; - - @JsonProperty("iv_and_cipher") - @JsonDeserialize(using = HexDeserializer.class) - private byte[] ivAndCipher; - - public byte[] getBackupId() { - return backupId; - } - - public byte[] getArgon2Hash() { - return argon2Hash; - } - - public String getPin() { - return pin; - } - - public String getRegistrationLock() { - return registrationLock; - } - - public byte[] getMasterKey() { - return masterKey; - } - - public byte[] getKbsAccessKey() { - return kbsAccessKey; - } - - public byte[] getIvAndCipher() { - return ivAndCipher; - } -} \ No newline at end of file diff --git a/app/src/test/java/org/thoughtcrime/securesms/registration/v2/testdata/KbsTestVector.kt b/app/src/test/java/org/thoughtcrime/securesms/registration/v2/testdata/KbsTestVector.kt new file mode 100644 index 0000000000..de1f646180 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/registration/v2/testdata/KbsTestVector.kt @@ -0,0 +1,62 @@ +package org.thoughtcrime.securesms.registration.v2.testdata + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import org.thoughtcrime.securesms.testutil.HexDeserializer + +data class KbsTestVector( + @JsonProperty("backup_id") + @JsonDeserialize(using = HexDeserializer::class) + val backupId: ByteArray, + + @JsonProperty("argon2_hash") + @JsonDeserialize(using = HexDeserializer::class) + val argon2Hash: ByteArray, + + @JsonProperty("pin") + val pin: String? = null, + + @JsonProperty("registration_lock") + val registrationLock: String? = null, + + @JsonProperty("master_key") + @JsonDeserialize(using = HexDeserializer::class) + val masterKey: ByteArray, + + @JsonProperty("kbs_access_key") + @JsonDeserialize(using = HexDeserializer::class) + val kbsAccessKey: ByteArray, + + @JsonProperty("iv_and_cipher") + @JsonDeserialize(using = HexDeserializer::class) + val ivAndCipher: ByteArray +) { + // equals() and hashCode() are still recommended on data class because of ByteArray usage + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as KbsTestVector + + if (!backupId.contentEquals(other.backupId)) return false + if (!argon2Hash.contentEquals(other.argon2Hash)) return false + if (pin != other.pin) return false + if (registrationLock != other.registrationLock) return false + if (!masterKey.contentEquals(other.masterKey)) return false + if (!kbsAccessKey.contentEquals(other.kbsAccessKey)) return false + if (!ivAndCipher.contentEquals(other.ivAndCipher)) return false + + return true + } + + override fun hashCode(): Int { + var result = backupId.contentHashCode() + result = 31 * result + argon2Hash.contentHashCode() + result = 31 * result + (pin?.hashCode() ?: 0) + result = 31 * result + (registrationLock?.hashCode() ?: 0) + result = 31 * result + masterKey.contentHashCode() + result = 31 * result + kbsAccessKey.contentHashCode() + result = 31 * result + ivAndCipher.contentHashCode() + return result + } +} From bbd6643733ad93d1e29d362a9d3d8a2539893714 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 26 Nov 2024 16:28:48 -0500 Subject: [PATCH 38/56] Update most of the backup integration tests. --- .../assets/backupTests/chat_00.binproto | Bin 545 -> 582 bytes .../assets/backupTests/chat_01.binproto | Bin 620 -> 657 bytes .../assets/backupTests/chat_02.binproto | Bin 610 -> 647 bytes .../assets/backupTests/chat_03.binproto | Bin 593 -> 630 bytes .../assets/backupTests/chat_04.binproto | Bin 511 -> 548 bytes .../assets/backupTests/chat_05.binproto | Bin 607 -> 644 bytes .../assets/backupTests/chat_06.binproto | Bin 453 -> 490 bytes .../assets/backupTests/chat_07.binproto | Bin 461 -> 498 bytes .../assets/backupTests/chat_08.binproto | Bin 467 -> 504 bytes .../assets/backupTests/chat_09.binproto | Bin 447 -> 484 bytes .../assets/backupTests/chat_10.binproto | Bin 467 -> 504 bytes .../assets/backupTests/chat_11.binproto | Bin 461 -> 498 bytes .../assets/backupTests/chat_12.binproto | Bin 453 -> 490 bytes .../assets/backupTests/chat_13.binproto | Bin 461 -> 498 bytes .../assets/backupTests/chat_14.binproto | Bin 467 -> 504 bytes .../assets/backupTests/chat_15.binproto | Bin 447 -> 484 bytes .../assets/backupTests/chat_16.binproto | Bin 467 -> 504 bytes .../assets/backupTests/chat_17.binproto | Bin 461 -> 498 bytes .../assets/backupTests/chat_18.binproto | Bin 453 -> 490 bytes .../assets/backupTests/chat_19.binproto | Bin 461 -> 498 bytes .../assets/backupTests/chat_20.binproto | Bin 467 -> 504 bytes .../assets/backupTests/chat_21.binproto | Bin 447 -> 484 bytes .../assets/backupTests/chat_22.binproto | Bin 467 -> 504 bytes .../assets/backupTests/chat_23.binproto | Bin 461 -> 498 bytes .../assets/backupTests/chat_24.binproto | Bin 453 -> 490 bytes .../assets/backupTests/chat_25.binproto | Bin 461 -> 498 bytes .../assets/backupTests/chat_26.binproto | Bin 467 -> 504 bytes .../chat_item_contact_message_00.binproto | Bin 485 -> 522 bytes .../chat_item_contact_message_01.binproto | Bin 669 -> 706 bytes .../chat_item_contact_message_02.binproto | Bin 903 -> 940 bytes .../chat_item_contact_message_03.binproto | Bin 683 -> 720 bytes .../chat_item_contact_message_04.binproto | Bin 750 -> 787 bytes .../chat_item_contact_message_05.binproto | Bin 757 -> 794 bytes .../chat_item_contact_message_06.binproto | Bin 658 -> 695 bytes .../chat_item_contact_message_07.binproto | Bin 769 -> 806 bytes .../chat_item_contact_message_08.binproto | Bin 731 -> 768 bytes .../chat_item_contact_message_09.binproto | Bin 482 -> 519 bytes .../chat_item_contact_message_10.binproto | Bin 668 -> 705 bytes .../chat_item_contact_message_11.binproto | Bin 903 -> 940 bytes .../chat_item_contact_message_12.binproto | Bin 681 -> 718 bytes .../chat_item_contact_message_13.binproto | Bin 752 -> 789 bytes .../chat_item_contact_message_14.binproto | Bin 757 -> 794 bytes ...t_item_expiration_timer_update_00.binproto | Bin 455 -> 492 bytes ...t_item_expiration_timer_update_01.binproto | Bin 462 -> 499 bytes ...t_item_expiration_timer_update_02.binproto | Bin 462 -> 499 bytes .../chat_item_gift_badge_00.binproto | Bin 807 -> 844 bytes .../chat_item_gift_badge_01.binproto | Bin 805 -> 842 bytes .../chat_item_gift_badge_02.binproto | Bin 797 -> 834 bytes .../chat_item_gift_badge_03.binproto | Bin 471 -> 508 bytes .../chat_item_gift_badge_04.binproto | Bin 802 -> 839 bytes .../chat_item_gift_badge_05.binproto | Bin 799 -> 836 bytes .../chat_item_gift_badge_06.binproto | Bin 804 -> 841 bytes .../chat_item_gift_badge_07.binproto | Bin 471 -> 508 bytes .../chat_item_gift_badge_08.binproto | Bin 795 -> 832 bytes .../chat_item_gift_badge_09.binproto | Bin 806 -> 843 bytes .../chat_item_gift_badge_10.binproto | Bin 804 -> 841 bytes .../chat_item_gift_badge_11.binproto | Bin 462 -> 499 bytes .../chat_item_gift_badge_12.binproto | Bin 802 -> 839 bytes .../chat_item_gift_badge_13.binproto | Bin 806 -> 843 bytes .../chat_item_gift_badge_14.binproto | Bin 799 -> 836 bytes .../chat_item_group_call_update_00.binproto | Bin 741 -> 819 bytes .../chat_item_group_call_update_01.binproto | Bin 749 -> 827 bytes .../chat_item_group_call_update_02.binproto | Bin 751 -> 829 bytes .../chat_item_group_call_update_03.binproto | Bin 743 -> 821 bytes .../chat_item_group_call_update_04.binproto | Bin 753 -> 829 bytes .../chat_item_group_call_update_05.binproto | Bin 751 -> 827 bytes .../chat_item_group_call_update_06.binproto | Bin 743 -> 819 bytes .../chat_item_group_call_update_07.binproto | Bin 749 -> 827 bytes ...up_change_chat_multiple_update_00.binproto | Bin 990 -> 1143 bytes ...up_change_chat_multiple_update_01.binproto | Bin 1032 -> 1185 bytes ...up_change_chat_multiple_update_02.binproto | Bin 992 -> 1145 bytes ...up_change_chat_multiple_update_03.binproto | Bin 1100 -> 1253 bytes ...up_change_chat_multiple_update_04.binproto | Bin 996 -> 1149 bytes ...up_change_chat_multiple_update_05.binproto | Bin 996 -> 1149 bytes ...up_change_chat_multiple_update_06.binproto | Bin 1012 -> 1165 bytes ...up_change_chat_multiple_update_07.binproto | Bin 1046 -> 1199 bytes ...up_change_chat_multiple_update_08.binproto | Bin 996 -> 1149 bytes ..._item_group_change_chat_update_00.binproto | Bin 946 -> 1099 bytes ..._item_group_change_chat_update_01.binproto | Bin 946 -> 1099 bytes ..._item_group_change_chat_update_02.binproto | Bin 946 -> 1099 bytes ..._item_group_change_chat_update_03.binproto | Bin 946 -> 1099 bytes ..._item_group_change_chat_update_04.binproto | Bin 946 -> 1099 bytes ..._item_group_change_chat_update_05.binproto | Bin 958 -> 1111 bytes ..._item_group_change_chat_update_06.binproto | Bin 963 -> 1116 bytes ..._item_group_change_chat_update_07.binproto | Bin 959 -> 1112 bytes ..._item_group_change_chat_update_08.binproto | Bin 948 -> 1101 bytes ..._item_group_change_chat_update_09.binproto | Bin 946 -> 1099 bytes ..._item_group_change_chat_update_10.binproto | Bin 946 -> 1099 bytes ..._item_group_change_chat_update_11.binproto | Bin 1002 -> 1155 bytes ..._item_group_change_chat_update_12.binproto | Bin 976 -> 1129 bytes ..._item_group_change_chat_update_13.binproto | Bin 966 -> 1119 bytes ..._item_group_change_chat_update_14.binproto | Bin 948 -> 1101 bytes ..._item_group_change_chat_update_15.binproto | Bin 948 -> 1101 bytes ..._item_group_change_chat_update_16.binproto | Bin 948 -> 1101 bytes ..._item_group_change_chat_update_17.binproto | Bin 948 -> 1101 bytes ..._item_group_change_chat_update_18.binproto | Bin 948 -> 1101 bytes ..._item_group_change_chat_update_19.binproto | Bin 948 -> 1101 bytes ..._item_group_change_chat_update_20.binproto | Bin 948 -> 1101 bytes ..._item_group_change_chat_update_21.binproto | Bin 946 -> 1099 bytes ..._item_group_change_chat_update_22.binproto | Bin 966 -> 1119 bytes ..._item_group_change_chat_update_23.binproto | Bin 964 -> 1117 bytes ..._item_group_change_chat_update_24.binproto | Bin 946 -> 1099 bytes ..._item_group_change_chat_update_25.binproto | Bin 946 -> 1099 bytes ..._item_group_change_chat_update_26.binproto | Bin 964 -> 1117 bytes ..._item_group_change_chat_update_27.binproto | Bin 964 -> 1117 bytes ..._item_group_change_chat_update_28.binproto | Bin 946 -> 1099 bytes ..._item_group_change_chat_update_29.binproto | Bin 946 -> 1099 bytes ..._item_group_change_chat_update_30.binproto | Bin 946 -> 1099 bytes ..._item_group_change_chat_update_31.binproto | Bin 946 -> 1099 bytes ..._item_group_change_chat_update_32.binproto | Bin 948 -> 1101 bytes ..._item_group_change_chat_update_33.binproto | Bin 948 -> 1101 bytes ...at_item_individual_call_update_00.binproto | Bin 472 -> 509 bytes ...at_item_individual_call_update_01.binproto | Bin 480 -> 517 bytes ...at_item_individual_call_update_02.binproto | Bin 478 -> 515 bytes ...at_item_individual_call_update_03.binproto | Bin 472 -> 509 bytes ...at_item_learned_profile_update_00.binproto | Bin 461 -> 498 bytes ...at_item_learned_profile_update_01.binproto | Bin 461 -> 498 bytes ...at_item_learned_profile_update_02.binproto | Bin 468 -> 505 bytes ...at_item_learned_profile_update_03.binproto | Bin 466 -> 503 bytes ...at_item_learned_profile_update_04.binproto | Bin 463 -> 500 bytes ...chat_item_payment_notification_00.binproto | Bin 691 -> 728 bytes ...chat_item_payment_notification_01.binproto | Bin 764 -> 801 bytes ...chat_item_payment_notification_02.binproto | Bin 764 -> 801 bytes ...chat_item_payment_notification_03.binproto | Bin 570 -> 607 bytes ...chat_item_payment_notification_04.binproto | Bin 517 -> 554 bytes ...chat_item_payment_notification_05.binproto | Bin 576 -> 613 bytes ...chat_item_payment_notification_06.binproto | Bin 756 -> 793 bytes ...chat_item_payment_notification_07.binproto | Bin 754 -> 791 bytes ...chat_item_payment_notification_08.binproto | Bin 694 -> 731 bytes ...chat_item_payment_notification_09.binproto | Bin 581 -> 618 bytes ...chat_item_payment_notification_10.binproto | Bin 589 -> 626 bytes ...chat_item_payment_notification_11.binproto | Bin 561 -> 598 bytes ...chat_item_payment_notification_12.binproto | Bin 686 -> 723 bytes ...chat_item_payment_notification_13.binproto | Bin 765 -> 802 bytes ...chat_item_payment_notification_14.binproto | Bin 766 -> 803 bytes ...hat_item_profile_change_update_00.binproto | Bin 481 -> 518 bytes ...hat_item_profile_change_update_01.binproto | Bin 482 -> 519 bytes ...hat_item_profile_change_update_02.binproto | Bin 479 -> 516 bytes .../chat_item_remote_delete_00.binproto | Bin 471 -> 508 bytes .../chat_item_remote_delete_01.binproto | Bin 467 -> 504 bytes .../chat_item_remote_delete_02.binproto | Bin 459 -> 496 bytes .../chat_item_remote_delete_03.binproto | Bin 468 -> 505 bytes .../chat_item_remote_delete_04.binproto | Bin 466 -> 503 bytes .../chat_item_remote_delete_05.binproto | Bin 461 -> 498 bytes .../chat_item_remote_delete_06.binproto | Bin 466 -> 503 bytes .../chat_item_remote_delete_07.binproto | Bin 468 -> 505 bytes .../chat_item_remote_delete_08.binproto | Bin 459 -> 496 bytes .../chat_item_remote_delete_09.binproto | Bin 468 -> 505 bytes .../chat_item_remote_delete_10.binproto | Bin 466 -> 503 bytes .../chat_item_remote_delete_11.binproto | Bin 459 -> 496 bytes .../chat_item_remote_delete_12.binproto | Bin 466 -> 503 bytes .../chat_item_remote_delete_13.binproto | Bin 468 -> 505 bytes .../chat_item_remote_delete_14.binproto | Bin 461 -> 498 bytes ...item_session_switchover_update_00.binproto | Bin 461 -> 498 bytes ...item_session_switchover_update_01.binproto | Bin 462 -> 499 bytes .../chat_item_simple_updates_00.binproto | Bin 457 -> 494 bytes .../chat_item_simple_updates_01.binproto | Bin 457 -> 494 bytes .../chat_item_simple_updates_02.binproto | Bin 457 -> 494 bytes .../chat_item_simple_updates_03.binproto | Bin 457 -> 494 bytes .../chat_item_simple_updates_04.binproto | Bin 457 -> 494 bytes .../chat_item_simple_updates_05.binproto | Bin 466 -> 503 bytes .../chat_item_simple_updates_06.binproto | Bin 479 -> 516 bytes .../chat_item_simple_updates_07.binproto | Bin 457 -> 494 bytes .../chat_item_simple_updates_08.binproto | Bin 479 -> 516 bytes .../chat_item_simple_updates_09.binproto | Bin 479 -> 516 bytes .../chat_item_simple_updates_10.binproto | Bin 479 -> 516 bytes .../chat_item_simple_updates_11.binproto | Bin 479 -> 516 bytes .../chat_item_simple_updates_12.binproto | Bin 457 -> 494 bytes .../chat_item_simple_updates_13.binproto | Bin 457 -> 494 bytes .../chat_item_simple_updates_14.binproto | Bin 457 -> 494 bytes .../chat_item_simple_updates_15.binproto | Bin 457 -> 494 bytes ...tandard_message_formatted_text_00.binproto | Bin 838 -> 914 bytes ...tandard_message_formatted_text_01.binproto | Bin 854 -> 930 bytes ...tandard_message_formatted_text_02.binproto | Bin 858 -> 934 bytes ...tandard_message_formatted_text_03.binproto | Bin 886 -> 962 bytes ...tandard_message_formatted_text_04.binproto | Bin 918 -> 994 bytes ...tandard_message_formatted_text_05.binproto | Bin 890 -> 966 bytes ...tandard_message_formatted_text_06.binproto | Bin 854 -> 930 bytes ...tandard_message_formatted_text_07.binproto | Bin 886 -> 962 bytes ...tandard_message_formatted_text_08.binproto | Bin 842 -> 918 bytes ...tandard_message_formatted_text_09.binproto | Bin 886 -> 962 bytes ...tandard_message_formatted_text_10.binproto | Bin 918 -> 994 bytes ...tandard_message_formatted_text_11.binproto | Bin 898 -> 974 bytes ...tandard_message_formatted_text_12.binproto | Bin 854 -> 930 bytes ...tandard_message_formatted_text_13.binproto | Bin 870 -> 946 bytes ...tandard_message_formatted_text_14.binproto | Bin 850 -> 926 bytes ...tem_standard_message_long_text_00.binproto | Bin 610 -> 647 bytes ...tem_standard_message_long_text_01.binproto | Bin 658 -> 695 bytes ...tem_standard_message_long_text_02.binproto | Bin 670 -> 707 bytes ...tem_standard_message_long_text_03.binproto | Bin 607 -> 644 bytes ...tem_standard_message_long_text_04.binproto | Bin 616 -> 653 bytes ...tem_standard_message_long_text_05.binproto | Bin 645 -> 682 bytes ...tem_standard_message_long_text_06.binproto | Bin 588 -> 625 bytes ...tem_standard_message_long_text_07.binproto | Bin 562 -> 599 bytes ...tem_standard_message_long_text_08.binproto | Bin 649 -> 686 bytes ...tem_standard_message_long_text_09.binproto | Bin 636 -> 673 bytes ...tem_standard_message_long_text_10.binproto | Bin 649 -> 686 bytes ...tem_standard_message_long_text_11.binproto | Bin 649 -> 686 bytes ...tem_standard_message_long_text_12.binproto | Bin 592 -> 629 bytes ...tem_standard_message_long_text_13.binproto | Bin 624 -> 661 bytes ...tem_standard_message_long_text_14.binproto | Bin 635 -> 672 bytes ...chat_item_standard_message_sms_00.binproto | Bin 482 -> 519 bytes ...chat_item_standard_message_sms_01.binproto | Bin 582 -> 619 bytes ...chat_item_standard_message_sms_02.binproto | Bin 993 -> 1030 bytes ...chat_item_standard_message_sms_03.binproto | Bin 483 -> 520 bytes ...chat_item_standard_message_sms_04.binproto | Bin 577 -> 614 bytes ...chat_item_standard_message_sms_05.binproto | Bin 999 -> 1036 bytes ...chat_item_standard_message_sms_06.binproto | Bin 477 -> 514 bytes ...chat_item_standard_message_sms_07.binproto | Bin 583 -> 620 bytes ...chat_item_standard_message_sms_08.binproto | Bin 993 -> 1030 bytes ...chat_item_standard_message_sms_09.binproto | Bin 483 -> 520 bytes ...chat_item_standard_message_sms_10.binproto | Bin 577 -> 614 bytes ...chat_item_standard_message_sms_11.binproto | Bin 997 -> 1034 bytes ...chat_item_standard_message_sms_12.binproto | Bin 477 -> 514 bytes ...chat_item_standard_message_sms_13.binproto | Bin 583 -> 620 bytes ...chat_item_standard_message_sms_14.binproto | Bin 995 -> 1032 bytes ...rd_message_special_attachments_00.binproto | Bin 572 -> 609 bytes ...rd_message_special_attachments_01.binproto | Bin 585 -> 622 bytes ...rd_message_special_attachments_02.binproto | Bin 599 -> 636 bytes ...rd_message_special_attachments_03.binproto | Bin 569 -> 606 bytes ...rd_message_special_attachments_04.binproto | Bin 584 -> 621 bytes ...rd_message_special_attachments_05.binproto | Bin 601 -> 638 bytes ...rd_message_special_attachments_06.binproto | Bin 566 -> 603 bytes ...rd_message_special_attachments_07.binproto | Bin 586 -> 623 bytes ...rd_message_special_attachments_08.binproto | Bin 599 -> 636 bytes ...rd_message_special_attachments_09.binproto | Bin 569 -> 606 bytes ...rd_message_special_attachments_10.binproto | Bin 584 -> 621 bytes ...rd_message_special_attachments_11.binproto | Bin 599 -> 636 bytes ...rd_message_special_attachments_12.binproto | Bin 566 -> 603 bytes ...rd_message_special_attachments_13.binproto | Bin 586 -> 623 bytes ...rd_message_special_attachments_14.binproto | Bin 601 -> 638 bytes ...d_message_standard_attachments_00.binproto | Bin 571 -> 608 bytes ...d_message_standard_attachments_01.binproto | Bin 1049 -> 1086 bytes ...d_message_standard_attachments_02.binproto | Bin 1105 -> 1142 bytes ...d_message_standard_attachments_03.binproto | Bin 721 -> 758 bytes ...d_message_standard_attachments_04.binproto | Bin 924 -> 961 bytes ...d_message_standard_attachments_05.binproto | Bin 1194 -> 1231 bytes ...d_message_standard_attachments_06.binproto | Bin 681 -> 718 bytes ...d_message_standard_attachments_07.binproto | Bin 963 -> 1000 bytes ...d_message_standard_attachments_08.binproto | Bin 1168 -> 1205 bytes ...d_message_standard_attachments_09.binproto | Bin 652 -> 689 bytes ...d_message_standard_attachments_10.binproto | Bin 935 -> 972 bytes ...d_message_standard_attachments_11.binproto | Bin 1231 -> 1268 bytes ...d_message_standard_attachments_12.binproto | Bin 581 -> 618 bytes ...d_message_standard_attachments_13.binproto | Bin 880 -> 917 bytes ...d_message_standard_attachments_14.binproto | Bin 1277 -> 1314 bytes ...tem_standard_message_text_only_00.binproto | Bin 504 -> 541 bytes ...tem_standard_message_text_only_01.binproto | Bin 523 -> 560 bytes ...tem_standard_message_text_only_02.binproto | Bin 543 -> 580 bytes ...tem_standard_message_text_only_03.binproto | Bin 501 -> 538 bytes ...tem_standard_message_text_only_04.binproto | Bin 522 -> 559 bytes ...tem_standard_message_text_only_05.binproto | Bin 545 -> 582 bytes ...tem_standard_message_text_only_06.binproto | Bin 499 -> 536 bytes ...tem_standard_message_text_only_07.binproto | Bin 524 -> 561 bytes ...tem_standard_message_text_only_08.binproto | Bin 543 -> 580 bytes ...tem_standard_message_text_only_09.binproto | Bin 501 -> 538 bytes ...tem_standard_message_text_only_10.binproto | Bin 522 -> 559 bytes ...tem_standard_message_text_only_11.binproto | Bin 543 -> 580 bytes ...tem_standard_message_text_only_12.binproto | Bin 499 -> 536 bytes ...tem_standard_message_text_only_13.binproto | Bin 524 -> 561 bytes ...tem_standard_message_text_only_14.binproto | Bin 545 -> 582 bytes ...em_standard_message_with_edits_00.binproto | Bin 600 -> 637 bytes ...em_standard_message_with_edits_01.binproto | Bin 739 -> 776 bytes ...em_standard_message_with_edits_02.binproto | Bin 588 -> 625 bytes ...dard_message_with_link_preview_00.binproto | Bin 561 -> 598 bytes ...dard_message_with_link_preview_01.binproto | Bin 748 -> 785 bytes ...dard_message_with_link_preview_02.binproto | Bin 839 -> 876 bytes ...dard_message_with_link_preview_03.binproto | Bin 736 -> 773 bytes ...dard_message_with_link_preview_04.binproto | Bin 732 -> 769 bytes ...dard_message_with_link_preview_05.binproto | Bin 778 -> 815 bytes ...dard_message_with_link_preview_06.binproto | Bin 732 -> 769 bytes ...dard_message_with_link_preview_07.binproto | Bin 711 -> 748 bytes ...dard_message_with_link_preview_08.binproto | Bin 680 -> 717 bytes ...dard_message_with_link_preview_09.binproto | Bin 558 -> 595 bytes ...dard_message_with_link_preview_10.binproto | Bin 747 -> 784 bytes ...dard_message_with_link_preview_11.binproto | Bin 839 -> 876 bytes ...dard_message_with_link_preview_12.binproto | Bin 734 -> 771 bytes ...dard_message_with_link_preview_13.binproto | Bin 734 -> 771 bytes ...dard_message_with_link_preview_14.binproto | Bin 778 -> 815 bytes .../chat_item_sticker_message_00.binproto | Bin 630 -> 667 bytes .../chat_item_sticker_message_01.binproto | Bin 669 -> 706 bytes .../chat_item_sticker_message_02.binproto | Bin 698 -> 735 bytes .../chat_item_sticker_message_03.binproto | Bin 637 -> 674 bytes .../chat_item_sticker_message_04.binproto | Bin 635 -> 672 bytes .../chat_item_sticker_message_05.binproto | Bin 669 -> 706 bytes .../chat_item_sticker_message_06.binproto | Bin 615 -> 652 bytes .../chat_item_sticker_message_07.binproto | Bin 580 -> 617 bytes .../chat_item_sticker_message_08.binproto | Bin 660 -> 697 bytes .../chat_item_sticker_message_09.binproto | Bin 658 -> 695 bytes .../chat_item_sticker_message_10.binproto | Bin 686 -> 723 bytes .../chat_item_sticker_message_11.binproto | Bin 662 -> 699 bytes .../chat_item_sticker_message_12.binproto | Bin 609 -> 646 bytes .../chat_item_sticker_message_13.binproto | Bin 661 -> 698 bytes .../chat_item_sticker_message_14.binproto | Bin 655 -> 692 bytes .../chat_item_thread_merge_update_00.binproto | Bin 461 -> 498 bytes .../chat_item_thread_merge_update_01.binproto | Bin 462 -> 499 bytes .../chat_item_view_once_00.binproto | Bin 472 -> 509 bytes .../chat_item_view_once_01.binproto | Bin 568 -> 605 bytes .../chat_item_view_once_02.binproto | Bin 644 -> 681 bytes .../chat_item_view_once_03.binproto | Bin 620 -> 657 bytes .../chat_item_view_once_04.binproto | Bin 642 -> 679 bytes .../chat_item_view_once_05.binproto | Bin 561 -> 598 bytes .../chat_item_view_once_06.binproto | Bin 623 -> 660 bytes .../chat_item_view_once_07.binproto | Bin 552 -> 589 bytes .../chat_item_view_once_08.binproto | Bin 531 -> 568 bytes .../chat_item_view_once_09.binproto | Bin 469 -> 506 bytes .../chat_item_view_once_10.binproto | Bin 566 -> 603 bytes .../chat_item_view_once_11.binproto | Bin 644 -> 681 bytes .../chat_item_view_once_12.binproto | Bin 618 -> 655 bytes .../chat_item_view_once_13.binproto | Bin 644 -> 681 bytes .../chat_item_view_once_14.binproto | Bin 561 -> 598 bytes .../recipient_contacts_01.binproto | Bin 442 -> 482 bytes .../recipient_contacts_02.binproto | Bin 429 -> 469 bytes .../recipient_contacts_03.binproto | Bin 445 -> 483 bytes .../recipient_distribution_list_00.binproto | Bin 661 -> 777 bytes .../recipient_distribution_list_01.binproto | Bin 676 -> 792 bytes .../recipient_distribution_list_02.binproto | Bin 670 -> 786 bytes .../recipient_distribution_list_03.binproto | Bin 680 -> 796 bytes .../backupTests/recipient_groups_00.binproto | Bin 1086 -> 1278 bytes .../backupTests/recipient_groups_01.binproto | Bin 1270 -> 1462 bytes .../backupTests/recipient_groups_02.binproto | Bin 1228 -> 1420 bytes .../backupTests/recipient_groups_03.binproto | Bin 1217 -> 1409 bytes .../backup/v2/ArchiveImportExportTests.kt | 2 +- .../RecipientTableArchiveExtensions.kt | 49 +++++++++++------- .../v2/exporters/ChatArchiveExporter.kt | 4 +- .../v2/exporters/ChatItemArchiveExporter.kt | 22 ++++---- .../v2/exporters/ContactArchiveExporter.kt | 12 +++++ .../backup/v2/importer/ChatArchiveImporter.kt | 4 +- .../v2/importer/ChatItemArchiveImporter.kt | 9 ++-- .../v2/importer/ContactArchiveImporter.kt | 29 ++++++++++- .../v2/util/ArchiveConverterExtensions.kt | 4 +- app/src/main/protowire/Backup.proto | 45 +++++++++++----- 332 files changed, 125 insertions(+), 55 deletions(-) diff --git a/app/src/androidTest/assets/backupTests/chat_00.binproto b/app/src/androidTest/assets/backupTests/chat_00.binproto index 62ce43003c6b5a92df048a9daf7dcd4e02b14d15..3333623fb9dccaeda3c6d4fa2ab3e9db7cc69c0b 100644 GIT binary patch delta 61 zcmV-D0K)&F1jYogM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iCk=7U9 delta 23 ecmX@cvXEtiJ0p9NPyz>wP|V~2#;D0>87l!$AqLd| diff --git a/app/src/androidTest/assets/backupTests/chat_01.binproto b/app/src/androidTest/assets/backupTests/chat_01.binproto index 1683ae3f1c33d1a811487517e5fb15d93a12b73c..91db1eaf81d8e3df31f476037cb22d662d253263 100644 GIT binary patch delta 61 zcmV-D0K)(51d#=>M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)j2m;)F< delta 23 fcmbQp`i5nLJ0p9NPyz>wP|V~2#;D0>8IJ$}QRW9X diff --git a/app/src/androidTest/assets/backupTests/chat_02.binproto b/app/src/androidTest/assets/backupTests/chat_02.binproto index 88e5e5a9ca8c2aca1a41dae7be24446dc3b623f3..50d071621b358755a6fa7b0dcb2a45ac3dfb12fa 100644 GIT binary patch delta 61 zcmV-D0K)&`1cwE%M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)i@lSvpN delta 23 ecmZo?eZ;cCosm6BD1n1TC}wg1W7OocjJp6(BnKG) diff --git a/app/src/androidTest/assets/backupTests/chat_03.binproto b/app/src/androidTest/assets/backupTests/chat_03.binproto index 56b77af28b944f3126ee5dce8ef5f3a3c6611833..ef963f5b674f7fa1078125e38d974a459a8f5ba6 100644 GIT binary patch delta 61 zcmV-D0K)oi~5M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iysD>DB delta 23 fcmeyya*<_&J0p9NPyz>wP|V~2#;D0>8CL=TSdj;1 diff --git a/app/src/androidTest/assets/backupTests/chat_04.binproto b/app/src/androidTest/assets/backupTests/chat_04.binproto index 5227b732d30e3ccfac3495808ebe8045811f8a3c..6b54ae21dd48eff325b65190ded7268a2371b959 100644 GIT binary patch delta 61 zcmV-D0K)(O1Ed77M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h#pa>WD delta 23 ecmZ3&@}GHwJ0p9NPyz>wP|V~2#;D0>8N&folLqbp diff --git a/app/src/androidTest/assets/backupTests/chat_05.binproto b/app/src/androidTest/assets/backupTests/chat_05.binproto index b591f0439d94d086a09c8a846790d9a12519bb87..2e65ab153b6861e80e4f87579854746d9dfa7014 100644 GIT binary patch delta 61 zcmV-D0K)&@1cU{!M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)i=k=_^? delta 23 ecmZo+z0b12osm6BD1n1TC}wg1W7OocjN1WEod*yA diff --git a/app/src/androidTest/assets/backupTests/chat_06.binproto b/app/src/androidTest/assets/backupTests/chat_06.binproto index 049f678a20bcfc5e31f4008ab97ba48a3087af17..eda2f427cf76f18f95c1a6505ed630ee44e27174 100644 GIT binary patch delta 61 zcmV-D0K)&p1L^~?M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h5q2w3H delta 23 ecmaFGe3W^EJ0p9NPyz>wP|V~2#;D0>8D#-g%Lcyy diff --git a/app/src/androidTest/assets/backupTests/chat_07.binproto b/app/src/androidTest/assets/backupTests/chat_07.binproto index ad785ed2bf42842a475760fdf74243790f5c9485..b1cf4aba486229ec5387564687e0707330305397 100644 GIT binary patch delta 61 zcmV-D0K)&x1M&l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hDrK}g= delta 23 ecmeywe3p5GJ0p9NPyz>wP|V~2#;D0>8C3ySGX~cH diff --git a/app/src/androidTest/assets/backupTests/chat_08.binproto b/app/src/androidTest/assets/backupTests/chat_08.binproto index ebfed08c13650395f1a510d8de8c49218e3468e3..bced1ced770156bdf76ba0fc7813c30b27774837 100644 GIT binary patch delta 61 zcmV-D0K)&%1NZ~5M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hJsDc;r delta 23 ecmeyte3^NJJ0p9NPyz>wP|V~2#;D0>8MOgeK?dmn diff --git a/app/src/androidTest/assets/backupTests/chat_09.binproto b/app/src/androidTest/assets/backupTests/chat_09.binproto index 1ad160bf84bb41477b7ba2ae1a3d0961ee08888f..ff4dafe2c5729f0ecf5928be2482259d4b4d9a0d 100644 GIT binary patch delta 80 zcmV-W0I&bQ1LOm+M*#_j0TP1&2m}&;lTHCzI&vWe>UD*yPi^6mI`##hX|)Q{Y*nu( mdE*=2Sl-CG7G4%kIsgwP|V~2#;D0>8N~rqy#}oS diff --git a/app/src/androidTest/assets/backupTests/chat_10.binproto b/app/src/androidTest/assets/backupTests/chat_10.binproto index 9ff10ea74f1163d36b0476f5cc37150d92cc4977..d36b0265cc61322a16f015c075dc4ae0524edae6 100644 GIT binary patch delta 61 zcmV-D0K)&%1NZ~5M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hJsDc;r delta 23 ecmeyte3^NJJ0p9NPyz>wP|V~2#;D0>8MOgeK?dmn diff --git a/app/src/androidTest/assets/backupTests/chat_11.binproto b/app/src/androidTest/assets/backupTests/chat_11.binproto index a32ebcfbe3065d1b30f856149e453e51bf286f33..64cd9f2005abbcf1784e2cda2b3bdcd0d8187694 100644 GIT binary patch delta 61 zcmV-D0K)&x1M&l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hDrK}g= delta 23 ecmeywe3p5GJ0p9NPyz>wP|V~2#;D0>8C3ySGX~cH diff --git a/app/src/androidTest/assets/backupTests/chat_12.binproto b/app/src/androidTest/assets/backupTests/chat_12.binproto index 08d40530f5ad1e0b3819ae93111e551d7dcebd5a..1b0da8307eacea99a784427dca0354eb25682f62 100644 GIT binary patch delta 61 zcmV-D0K)&p1L^~?M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h5q2w3H delta 23 ecmaFGe3W^EJ0p9NPyz>wP|V~2#;D0>8D#-g%Lcyy diff --git a/app/src/androidTest/assets/backupTests/chat_13.binproto b/app/src/androidTest/assets/backupTests/chat_13.binproto index f18b1ed4d52758ea0a2148def317b29632857f1d..ae49a511b204a604d9b2c9ab442043912d40cdb5 100644 GIT binary patch delta 61 zcmV-D0K)&x1M&l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hDrK}g= delta 23 ecmeywe3p5GJ0p9NPyz>wP|V~2#;D0>8C3ySGX~cH diff --git a/app/src/androidTest/assets/backupTests/chat_14.binproto b/app/src/androidTest/assets/backupTests/chat_14.binproto index 5fcf98400e612c3654426162fba2aac29edf9392..9f2c775a65722786a6742679d4bca5d5c778982a 100644 GIT binary patch delta 61 zcmV-D0K)&%1NZ~5M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hJsDc;r delta 23 ecmeyte3^NJJ0p9NPyz>wP|V~2#;D0>8MOgeK?dmn diff --git a/app/src/androidTest/assets/backupTests/chat_15.binproto b/app/src/androidTest/assets/backupTests/chat_15.binproto index b0a2fdce53c00f89ea8e0db8168e7489c6547d60..f39638ede100c37a8892d506ddcbc746aa3408b6 100644 GIT binary patch delta 80 zcmV-W0I&bQ1LOm+M*#_j0TP1&2m}&;lTHCzI&vWe>UD*yPi^6mI`##hX|)Q{Y*nu( mdE*=2Sl-CG7G4%kIsgwP|V~2#;D0>8N~rqy#}oS diff --git a/app/src/androidTest/assets/backupTests/chat_16.binproto b/app/src/androidTest/assets/backupTests/chat_16.binproto index d618d5c413802db9f622ef391c38befee4e8669c..a2252fbfb9941898052f59f618508a340f5ba9bc 100644 GIT binary patch delta 61 zcmV-D0K)&%1NZ~5M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hJsDc;r delta 23 ecmeyte3^NJJ0p9NPyz>wP|V~2#;D0>8MOgeK?dmn diff --git a/app/src/androidTest/assets/backupTests/chat_17.binproto b/app/src/androidTest/assets/backupTests/chat_17.binproto index dba433590d2892764a493fa68a47b9127107b8ba..2612e19a4c36410bcc0f5f9be1369dd68e10dd75 100644 GIT binary patch delta 61 zcmV-D0K)&x1M&l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hDrK}g= delta 23 ecmeywe3p5GJ0p9NPyz>wP|V~2#;D0>8C3ySGX~cH diff --git a/app/src/androidTest/assets/backupTests/chat_18.binproto b/app/src/androidTest/assets/backupTests/chat_18.binproto index 1bfa7407e9e18b37f35f908b406b1c6e84930b9e..c9dec51c52717cc45a959ec5d91f22f77ea38ceb 100644 GIT binary patch delta 61 zcmV-D0K)&p1L^~?M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h5q2w3H delta 23 ecmaFGe3W^EJ0p9NPyz>wP|V~2#;D0>8D#-g%Lcyy diff --git a/app/src/androidTest/assets/backupTests/chat_19.binproto b/app/src/androidTest/assets/backupTests/chat_19.binproto index 3a0d16be3a7b92bcb031c2541f8141d48cc73eaf..b0a630ae97bcfa86cf7d8b26aa3c3515b280311b 100644 GIT binary patch delta 61 zcmV-D0K)&x1M&l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hDrK}g= delta 23 ecmeywe3p5GJ0p9NPyz>wP|V~2#;D0>8C3ySGX~cH diff --git a/app/src/androidTest/assets/backupTests/chat_20.binproto b/app/src/androidTest/assets/backupTests/chat_20.binproto index 5a21a991380edac684f729727fa0993f47d3aae0..737d5053a866e57e525f2864a80537119e1688ce 100644 GIT binary patch delta 61 zcmV-D0K)&%1NZ~5M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hJsDc;r delta 23 ecmeyte3^NJJ0p9NPyz>wP|V~2#;D0>8MOgeK?dmn diff --git a/app/src/androidTest/assets/backupTests/chat_21.binproto b/app/src/androidTest/assets/backupTests/chat_21.binproto index 256ccb98baedec7a1b98762ee427ddec93f31c28..7c030dcda33b6c8a1467e94d2dad7438393082e4 100644 GIT binary patch delta 80 zcmV-W0I&bQ1LOm+M*#_j0TP1&2m}&;lTHCzI&vWe>UD*yPi^6mI`##hX|)Q{Y*nu( mdE*=2Sl-CG7G4%kIsgl48 delta 23 ecmaFDyq|f4J0p9NPyz>wP|V~2#;D0>8N~rqy#}oS diff --git a/app/src/androidTest/assets/backupTests/chat_22.binproto b/app/src/androidTest/assets/backupTests/chat_22.binproto index 6a66382a41412bbb6ea666fa884f829bd60d223e..9e0b2919bb37714c68c83482678fe70e8d5e4607 100644 GIT binary patch delta 61 zcmV-D0K)&%1NZ~5M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hJsDc;r delta 23 ecmeyte3^NJJ0p9NPyz>wP|V~2#;D0>8MOgeK?dmn diff --git a/app/src/androidTest/assets/backupTests/chat_23.binproto b/app/src/androidTest/assets/backupTests/chat_23.binproto index 05d380fe4262414af825205f9452bc8b14dab8f0..d0797013a3e13d61a1f0194573e82c00c9f4dd85 100644 GIT binary patch delta 61 zcmV-D0K)&x1M&l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hDrK}g= delta 23 ecmeywe3p5GJ0p9NPyz>wP|V~2#;D0>8C3ySGX~cH diff --git a/app/src/androidTest/assets/backupTests/chat_24.binproto b/app/src/androidTest/assets/backupTests/chat_24.binproto index 595d9b9127d925d73b0d62ee82c9f8fe0ba90597..c41863fe4f19b663dbb78231aeb11b8ce0221496 100644 GIT binary patch delta 61 zcmV-D0K)&p1L^~?M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h5q2w3H delta 23 ecmaFGe3W^EJ0p9NPyz>wP|V~2#;D0>8D#-g%Lcyy diff --git a/app/src/androidTest/assets/backupTests/chat_25.binproto b/app/src/androidTest/assets/backupTests/chat_25.binproto index aa15046f0fe9732b884cc5f515c00b07494571ec..a533ca441f46a8025a785162438d1b16199ceca9 100644 GIT binary patch delta 61 zcmV-D0K)&x1M&l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hDrK}g= delta 23 ecmeywe3p5GJ0p9NPyz>wP|V~2#;D0>8C3ySGX~cH diff --git a/app/src/androidTest/assets/backupTests/chat_26.binproto b/app/src/androidTest/assets/backupTests/chat_26.binproto index 9161f4ed1cdd1935a3e4e96a6e1a9d41ec66be39..2e5715090b7fa02e789468e7e2146c04b625c96f 100644 GIT binary patch delta 61 zcmV-D0K)&%1NZ~5M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hJsDc;r delta 23 ecmeyte3^NJJ0p9NPyz>wP|V~2#;D0>8MOgeK?dmn diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_00.binproto index b3a98a44fc611eaca8bbc4518982efbb1d81f166..c0011f497c461b7d3f765a64b8396da8649c683f 100644 GIT binary patch delta 61 zcmV-D0K)&}1BwK&M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hblfDUD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jpkmwjx delta 23 fcmX@aI+t~WJ0p9NPyz>wP|V~2#;D0>8NUJmP!|VF diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_02.binproto index 9c3c29b7ecc285752a79692256f0bc93e4d5a7df..3df6778b3c3bb980a8236cd4ef83dfecbddc9364 100644 GIT binary patch delta 62 zcmV-E0Kxx<2doFMM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0^z0iY!qHvj+t delta 24 fcmZ3(-p;UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?B0j{MNp8x;= delta 24 fcmcb>x|(%^J0p9NPyz>wP|V~2#;DC_8JQUYSfd7( diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_04.binproto index 551652af558ac37b6449231eaa33f1df16cef8c8..9333b464c7d5afa2198999fc321a4e758dfece6b 100644 GIT binary patch delta 62 zcmV-E0Kxz61(OD_M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?^0kKpU=>Px# delta 24 fcmbQt_KtOfJ0p9NPyz>wP|V~2#;DC_8QmBGSxN@r diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_05.binproto index 2d93a5e6f9f0530e6e5f192d9ae7f70fce439bec..9daeeb8a3e302e6ce8fde5bae583472351ce37af 100644 GIT binary patch delta 62 zcmV-E0KxzD1)2u1M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@00kpUm{r~^~ delta 24 fcmbQm_LX&mJ0p9NPyz>wP|V~2#;DC_8GRW6TABv+ diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_06.binproto index 383b786ddcf136cfcc0151735577ae35313d4b54..4723d8f88c9f27354f96c57355a3e0355e1b03e6 100644 GIT binary patch delta 61 zcmV-D0K)&01-AvTM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jei?tXt delta 23 fcmdnaI*E0IJ0p9NPyz>wP|V~2#;D0>8D9YaPACT| diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_07.binproto index 02df535a22b85af23f441574bd66eaf82c00655f..821b22caede75cafb0583abb1efb6f5bd35be11c 100644 GIT binary patch delta 62 zcmV-E0Kxx(2BrqEM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@C0h`4aqW}N^ delta 24 fcmZ3+*2uQOosm6BD1n1TC}wg1W7OuejFF50QSSzt diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_08.binproto index f071168fcb404f549d7009cfaa94bf554a22c6a1..26a7f388e8f1edff11d74d68795bd20d8c58e8a3 100644 GIT binary patch delta 62 zcmV-E0Kxy;1%L*yM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?x0jMAsumAu6 delta 24 fcmZo*yUn`6osm6BD1n1TC}wg1W7OuejK+)rRk{YJ diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_09.binproto index 28f72983183c0756146ac15466e2ceb5b2513b07..9ae16e9ed7a20315a6bab4fa9b440b0c5a18e053 100644 GIT binary patch delta 61 zcmV-D0K)&`1BV2#M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hYl2aF; delta 23 ecmZo?dBnWIosm6BD1n1TC}wg1W7Oocj8*_odIpyO diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_10.binproto index 6f244fc05ec52d3b2f19746df9508daf9820d191..d02f99d23f0ff600572e1457af2d8ef5f4b33663 100644 GIT binary patch delta 61 zcmV-D0K)&A1;GWdM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jokZ%}K delta 23 fcmX@eI)`wP|V~2#;D0>8NUDkPwEFq diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_11.binproto index fab134cc7b0a18f77d4053a00d86acae2482b2f8..f2c486a450df5d6fac26adef8b753492592c8372 100644 GIT binary patch delta 62 zcmV-E0Kxx<2doFMM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0^z0iY!qHvj+t delta 24 fcmZ3(-p;UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?90j;eUnE(I) delta 24 fcmX@dx{`H+J0p9NPyz>wP|V~2#;DC_85tP?SVIPo diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_13.binproto index 41ba5e5fd7ba92b0796168ce2c385a4bd4ca7912..4eea60e590edff10c8a42c0dccd832bb7c2b475d 100644 GIT binary patch delta 62 zcmV-E0Kxz81(gP{M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?`0kTXN?*IS* delta 24 fcmbQr_JMVSJ0p9NPyz>wP|V~2#;DC_89f*QS*ix+ diff --git a/app/src/androidTest/assets/backupTests/chat_item_contact_message_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_contact_message_14.binproto index 1c5e6fe694965786a7a30217233370fec6800f91..430f9ec06135424c34b34dae5045c44b9a7eeda6 100644 GIT binary patch delta 62 zcmV-E0KxzD1)2u1M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@00kpUm{r~^~ delta 24 fcmbQm_LX&mJ0p9NPyz>wP|V~2#;DC_8GRW6TABv+ diff --git a/app/src/androidTest/assets/backupTests/chat_item_expiration_timer_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_expiration_timer_update_00.binproto index 6f36601965310e50d2df99496c5c7dcf96093b00..7f4ed81c00c8d8ab3ab63460224d1f2cc998a061 100644 GIT binary patch delta 61 zcmV-D0K)&r1MCB^M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h7qShDA delta 23 ecmaFEe4KfMJ0p9NPyz>wP|V~2#;D0>8RY?1B?iR+ diff --git a/app/src/androidTest/assets/backupTests/chat_item_expiration_timer_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_expiration_timer_update_01.binproto index 424421d52680cdc3bcb0f72ed7b21e0e87b39ca5..b7898c1ffb360c50ff6f05e3f7b24aba04b3da0a 100644 GIT binary patch delta 61 zcmV-D0K)&y1M>s0M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hErX?5S delta 23 ecmey&e2#g8J0p9NPyz>wP|V~2#;D0>8Pxz+Us0M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hErX?5S delta 23 ecmey&e2#g8J0p9NPyz>wP|V~2#;D0>8Pxz+UUD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@o0j@0=6#xJL delta 24 fcmX@Zww!H)J0p9NPyz>wP|V~2#;DC_8S5DVSq}#c diff --git a/app/src/androidTest/assets/backupTests/chat_item_gift_badge_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_gift_badge_01.binproto index baabfed1f2c707a504388dd1089adda878600b60..2f4661750470f3910a1e1c5134f57496c137c85b 100644 GIT binary patch delta 62 zcmV-E0KxyI2FeDoM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@m0j)I{4*&oF delta 24 fcmX@bwv=syJ0p9NPyz>wP|V~2#;DC_8EY8UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@e0jX6N_W%F@ delta 24 fcmX@aHkWOKJ0p9NPyz>wP|V~2#;DC_8A}-fS1$(c diff --git a/app/src/androidTest/assets/backupTests/chat_item_gift_badge_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_gift_badge_03.binproto index e94cf99eb2af5aeda9981ec95b0f7361e82c39ff..b221afb4eca07403bda24092bdea75f907da8cd0 100644 GIT binary patch delta 61 zcmV-D0K)&*1N;N9M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hNs%97d delta 23 ecmeyve4TlNJ0p9NPyz>wP|V~2#;D0>8TA2J^#=6- diff --git a/app/src/androidTest/assets/backupTests/chat_item_gift_badge_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_gift_badge_04.binproto index 996f0e71dfec0ff67795c2f866b13730e535ef25..ecc9412b4685f0dfbf5166ec3d0967122f35a181 100644 GIT binary patch delta 62 zcmV-E0KxyF2FC`lM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@j0jt3n1^@s6 delta 24 fcmX@kwuo(mJ0p9NPyz>wP|V~2#;DC_8LJopSRV%c diff --git a/app/src/androidTest/assets/backupTests/chat_item_gift_badge_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_gift_badge_05.binproto index af719c54935694b423964bedbea5d05a964fc546..a10e2a96d9650ce0547bf4584bcd63c58ce0f273 100644 GIT binary patch delta 62 zcmV-E0KxyC2E+!iM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@g0jfwP|V~2#;DC_8Os>~SC0nt diff --git a/app/src/androidTest/assets/backupTests/chat_item_gift_badge_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_gift_badge_06.binproto index 4bc59a61d9792764faa1525b681dbb7c73b0279e..9014984cfbf33bad36acdae422c24df6f19dd7b3 100644 GIT binary patch delta 62 zcmV-E0KxyH2FV7nM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@l0j#+g3;+NC delta 24 fcmX@fwuEhiJ0p9NPyz>wP|V~2#;DC_8EY5;Sbqlt diff --git a/app/src/androidTest/assets/backupTests/chat_item_gift_badge_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_gift_badge_07.binproto index 3254a715387c5208e5b8e118d7539baf6a4485c3..24e8d589f79c17b796beaa5fd477c5727baa404d 100644 GIT binary patch delta 61 zcmV-D0K)&*1N;N9M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hNs%97d delta 23 ecmeyve4TlNJ0p9NPyz>wP|V~2#;D0>8TA2J^#=6- diff --git a/app/src/androidTest/assets/backupTests/chat_item_gift_badge_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_gift_badge_08.binproto index 4ba4df27235fd49649713d47269439cb9d4ddd6a..3495ab52d71a1c16d95abc3f383419130dccd0b9 100644 GIT binary patch delta 62 zcmV-E0Kxy82EYceM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@c0jOOU@c;k- delta 24 fcmX@WHk)mOJ0p9NPyz>wP|V~2#;DC_8H*VKR?i0L diff --git a/app/src/androidTest/assets/backupTests/chat_item_gift_badge_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_gift_badge_09.binproto index c3652f7ed64bde1b051203f595ebb15f6272bb2a..6bdd809fc32a79408d0a505f4083af3025b33ea1 100644 GIT binary patch delta 62 zcmV-E0KxyJ2FnJpM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@n0j;qZ5&!@I delta 24 fcmX@jwv26qJ0p9NPyz>wP|V~2#;DC_8S5AUSlUD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@l0j#+g3;+NC delta 24 fcmX@fwuEhiJ0p9NPyz>wP|V~2#;DC_8EY5;Sbqlt diff --git a/app/src/androidTest/assets/backupTests/chat_item_gift_badge_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_gift_badge_11.binproto index 2fcddb3308cfb065896f803058f7e8d7670c0292..0f1d5a8e7f0237a4c6ca6fc958d14377411af35a 100644 GIT binary patch delta 61 zcmV-D0K)&y1M>s0M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hErX?5S delta 23 ecmey&e2#g8J0p9NPyz>wP|V~2#;D0>8Pxz+UUD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@j0jt3n1^@s6 delta 24 fcmX@kwuo(mJ0p9NPyz>wP|V~2#;DC_8LJopSRV%c diff --git a/app/src/androidTest/assets/backupTests/chat_item_gift_badge_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_gift_badge_13.binproto index 3e41c0f09e62da00cffe5b3fd5a68042fe10e0e5..e738dfe4ae29f105f279c28db10124c2defe2f3e 100644 GIT binary patch delta 62 zcmV-E0KxyJ2FnJpM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@n0j;qZ5&!@I delta 24 fcmX@jwv26qJ0p9NPyz>wP|V~2#;DC_8S5AUSlUD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@g0jfwP|V~2#;DC_8Os>~SC0nt diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_call_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_call_update_00.binproto index 96b787f609eed8e044016136614f80c501dca1f2..0c25c20bff1e32eb522e855780944d01fafbfd8c 100644 GIT binary patch delta 152 zcmaFLx|wZ*J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++Cbrp=0Ihl5pIgQhALqvK@J81Mg>NV`RkXgxL{XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++Cbrp=0Ihl6q>L_V(FbgnB++6&6>pIgQhAL?%Nsj*4bCyp1ed%(J0FwfX#^as? M$5t2^Fjz1G0GPZ#^#A|> delta 81 zcmV-X0IvVL2JHo~M*#<75?}}f5?qr|0a^(<0ALbc2n7;alh^@RlT`w_D=H!=2m=rS n7}=xiwXQW%0D2ih7YL8*oTZif($j1Z0w4q^XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++Cbrp=0Ihl6q>nZ7QFbgnB+*pIgQhALSnX^zjoXY5}%>HfM90cHsn1y+su O>zAy!U}V5x!3Y2rEkEu6 delta 83 zcmV-Z0IdJL2JZ#1M*#<75?}}f5?qr|0a^(<0ALbc2n7;alh^@RlT`w_Ei57`2m=rS p7}}%jwXQW%0D2oj83^y^;0~iD#1t_1dq^Qs`H~|Lq8v_6U diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_call_update_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_call_update_03.binproto index f22725c27b0782cac45578b04f5017ce8dfffcca..4ab92d7d2f1b7516e0d43d68c0e3cc2440772acf 100644 GIT binary patch delta 154 zcmaFPx|MB%J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++Cbrp=0Ihl5pIgQhAL4fVGafX76}#wMvcck2ac^UFkrA? F1OU=pIg9`R delta 75 zcmV-R0JQ(L2ImE^M*#<75?}}f5?qr|0a^(<0ALbc2n7;alh^@RlT`w_B_tvu2m=rS h7~7-kwXQW%0D2Qb5eNVf1Q-M$0Vv~)z{aRBI05>e6rlhB diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_call_update_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_call_update_04.binproto index 74ae4c7939969f5d764707787fd0e28d8afbfeed..3b6184db43ce027c4e40f2db3b05f0734b6a5cc3 100644 GIT binary patch delta 114 zcmey!x|eN(J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=0IhkGn0JY#R7ytkO delta 37 tcmdnX_K|giJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yCWkV;006&&3TFTS diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_call_update_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_call_update_05.binproto index 06ed75d3c2eae2345fa5550cc945256b99862ce2..48cb36a0e7fcbd60c179876d705247ed8d184095 100644 GIT binary patch delta 114 zcmaFQx|?l-J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=0IhmdT0JIt}5&!@I delta 37 tcmdnZ_MUZvJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yCWkUT0|3783Sj^M diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_call_update_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_call_update_06.binproto index 0c8722fed88e4591118fb73c50640cb37c8a53fc..412700cccc74db55cbc7125ba768b3ea68b347d3 100644 GIT binary patch delta 114 zcmaFPx|wZ*J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=0IhpPO0IdQp`Tzg` delta 37 tcmdnY_MCNtJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yCWkWJ0|30J3P}I} diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_call_update_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_call_update_07.binproto index 536fdefddb164bd35453d9ff3779a3b019562281..7460dae0e233f1fa0d19e88e1dc2f7ef85a1f536 100644 GIT binary patch delta 160 zcmaFMx|?l-J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++Cbrp=0Ihl6q>L_V(FbgnB++X~9>pIgQhAL?%Nsj*4bCyp1ed%(J0EYsL#^as? M$5t2^Fjz1G0GaMU0RR91 delta 81 zcmV-X0IvVL2JHo~M*#<75?}}f5?qr|0a^(<0ALbc2n7;alh^@RlT`w_D=H!=2m=rS n7~iAowXQW%0D2ih7YL8*oTZif($j1Z2p|L~XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb SxlG+pz6!6b*?gXfpAi7rEmXJw delta 66 zcmey)agTk2J0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Opxj%gh|L+y{EPrII}kwt diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_01.binproto index 0e750d5343e25803d511de2e927cf2059fa3afd4..bedc16d0b615c86586cf53ac007580757a06fe8a 100644 GIT binary patch delta 220 zcmeC+Sjf4-osqMRQK*@bgGH!*vL9oVUXdc}tJ2oh{<#k)T76@kpShLmQcmc4^~%T6 z_ag3{*d-Px=4ZtK(ZUMUvXL>u5U54>UQX`$_56QNAAR)dBwv)oq0Gz)HBQ5mYz; delta 66 zcmZ3;*}<{Fosm6BD1n1TC}wg1V-%+qLxNBo2dhxj27^oaO OQ0^^L#O4fUOGW?)V-LXq diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_02.binproto index 534337a0efe71ea6ab2dbec3e6869a7ddda21651..f1625ec4950530ceca100874e52338374f876d86 100644 GIT binary patch delta 220 zcmaFB{*z;aJ0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb SxlG+pz6!6b*?gW!kP!gfom9R6 delta 66 zcmey#@qm4UJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Opxj%gh|L+yf{Xw(Ll8#* diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_03.binproto index 113929678174271075bb45037c7ebd87486ef293..2c0c4766cd6e6e2776f6022291fcccfb05d7aded 100644 GIT binary patch delta 220 zcmX@Z@sx9eJ0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb SxlG+pz6!6b*?gX delta 66 zcmaFLd4^+yJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Opxj%gh|L+ywTu8Oa}bRH diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_04.binproto index e851a0a01022abf7454e6527f62a56544b68eabf..b5fba7cb8986b4890bf4d7f1eecf05262c2e3bc7 100644 GIT binary patch delta 220 zcmaFD{+DBeJ0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb SxlG+pz6!6b*?gW!lo0^qeN@T- delta 66 zcmey%@q~SYJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Opxj%gh|L+yqKp7GQxH=C diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_05.binproto index c0de83647931a1f146b443adb3e89736586fe258..4211210f00cc01c4b53da3fdff086a4dd87b9a65 100644 GIT binary patch delta 220 zcmaFD{+DBeJ0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb SxlG+pz6!6b*?gW!lo0^qeN@T- delta 66 zcmey%@q~SYJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Opxj%gh|L+yqKp7GQxH=C diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_06.binproto index 9613d1688a21873dabda4c5f390f3b42d911726d..b833c99e5e1da2d89ad5b07af6328397b86bb98f 100644 GIT binary patch delta 220 zcmeyu-pjecosqMRQK*@bgGH!*vL9oVUXdc}tJ2oh{<#k)T76@kpShLmQcmc4^~%T6 z_ag3{*d-Px=4ZtK(ZUMUvXL>u5U54>UQX`$_56QNAAR)dBwv)oq0Gz)HBL}{ delta 66 zcmeC>{KCG$osm6BD1n1TC}wg1V-%+qLxNBo2dhxj27^oaO OQ0^^L#O4fURYm|E)DQ#! diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_07.binproto index e9a76f2174b64c54a7eecb67ff054431d2a1de3b..d32662b484af823be1d7ff8bedb69ed038960a11 100644 GIT binary patch delta 220 zcmbQnv7U2-J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb SxlG+pz6!6b*?gYKoe=wP|V~2#wdO(h6JHF4pyNkE&&DxZtlsBEV7e-Gn#T*F{B73 daj*%+O-^Kr0rEhq*oC4dzh#QpoWbnQ2mpY65f%Ud diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_multiple_update_08.binproto index 17578455f714b783046c584313857c4562fdf9bf..47d2dd0ef01c3adeb48c8b4d086e65fa4f480ae6 100644 GIT binary patch delta 220 zcmaFD{+DBeJ0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb SxlG+pz6!6b*?gW!lo0^qeN@T- delta 66 zcmey%@q~SYJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Opxj%gh|L+yqKp7GQxH=C diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_00.binproto index 80ec8e85f0ccd3c5a15e0152d52bbb461be5430a..6dad124118a705cc797d8156b71b51caaceef0b9 100644 GIT binary patch delta 219 zcmdnQewt&0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuj{yx%5S#!2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_01.binproto index e2f546f15a1daa457e9426a5c394468c09abf0eb..cc08d139ac7428287d61e635077f1453b9ec338b 100644 GIT binary patch delta 219 zcmdnQewt&0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuj{yx%5S#!2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_02.binproto index 724d353d0b7332552c0576f7715f3e124befc5d7..b32b465b4d909ebff50f4356072fd1a7507c1188 100644 GIT binary patch delta 219 zcmdnQewt&0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuj{yx%5S#!2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_03.binproto index f5b456e95bd527ee5b43e17ab4875986c66ac7cd..2d97ee042b7a6ca4cc298e0d257112bd643b5f69 100644 GIT binary patch delta 219 zcmdnQewt&0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuj{yx%5S#!2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_04.binproto index b33d825c9d3c6316b274a6175921ba41f5def386..a3ac89cc843614ccdb1f6056e5e2417b98bf0afb 100644 GIT binary patch delta 219 zcmdnQewt&0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuj{yx%5S#!2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_05.binproto index 66bdbb63d7d89f1c948b36f7bda43ddbbbb0b932..a5701c5841ca8d42d4cec59513510d5a1050a5d9 100644 GIT binary patch delta 219 zcmdnTew|~3J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+ku?*S895WoNc diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_06.binproto index e8bccd38d0f2393e1f4681454db2068db493ee21..441b48188ffffbe20e1f59c9e15331ab5ed1648f 100644 GIT binary patch delta 219 zcmX@ieura&J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7q<3jkK$RYm{+ delta 65 zcmcb^ahQFBJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuUjP-~5YGSr diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_07.binproto index ea5ab2f54f70f1b8abbd29a530c8bb74334e561c..f328d247cca2ec63c6064722cade7feeed626022 100644 GIT binary patch delta 219 zcmdnbeuHC!J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7q<0{~D8RXP9w delta 65 zcmcb?v7dc|J0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+ku9{>~E5W@ff diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_08.binproto index 50c03903355988131d5c30d2b5b1058332c2ee24..4c120a71895f5250148cb4251e2ec921490132c8 100644 GIT binary patch delta 219 zcmdnOewJf{J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuPXP{45TXD8 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_09.binproto index f86713930725b38ce4b483e81f884864d6abcaab..3f85afb4c14acd4ea0bde77585e765fcef9f8546 100644 GIT binary patch delta 219 zcmdnQewt&0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuj{yx%5S#!2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_10.binproto index ba9f823da00da968909ec03a9cc7d08eed11b486..ea8680dcbc53fe6bf43fe35d80ae40635d984a79 100644 GIT binary patch delta 219 zcmdnQewt&0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuj{yx%5S#!2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_11.binproto index 96bae71a5a4a176e436692175668d39911235f33..b7b03db2fd299b2b5161063f5c4e982d3e41f323 100644 GIT binary patch delta 220 zcmaFG-pskdosqMRQK*@bgGH!*vL9oVUXdc}tJ2oh{<#k)T76@kpShLmQcmc4^~%T6 z_ag3{*d-Px=4ZtK(ZUMUvXL>u5U54>UQX`$_56QNAAR)dBwv)oq0Gz)HB27^oaO OQ0^^L#O4fUX+{7TtPkb@ diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_12.binproto index 0ac35d5b0d5495fab1ca8f6e1aee89fe45c68a5a..bd8e875e7a029c10807cdd933fa9a44a65394c6b 100644 GIT binary patch delta 220 zcmcb>{*q&ZJ0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb SxlG+pz6!6b*?gXfkr4pF9aN40 delta 66 zcmaFKae;kwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Opxj%gh|L+yjEn#*0}vMg diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_13.binproto index 908889f30ecd04e715e58d5958ecaf27dfd85f60..c1262a27facc4dfd13a5e595243a1ba73cce76ef 100644 GIT binary patch delta 219 zcmX@cexGB5J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+ku-vJk55ZC|! diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_14.binproto index d3ded9b3edd5da783f02e26755a5a14347628179..5cf1dbc271349dc86005ec7d9faeaf13cdae0eee 100644 GIT binary patch delta 219 zcmdnOewJf{J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuPXP{45TXD8 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_15.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_15.binproto index 6da2f58114b513e0945792be41b3bf0f80d4bc4f..a9b8a39c0eab377788933a0bc7eaf1861aaabc02 100644 GIT binary patch delta 219 zcmdnOewJf{J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuPXP{45TXD8 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_16.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_16.binproto index ee19b39d19ba53d04e850fdde510a28f79b772f4..4145d6376086f80fdcac36a349c5db7437f4d151 100644 GIT binary patch delta 219 zcmdnOewJf{J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuPXP{45TXD8 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_17.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_17.binproto index 3a984944c0732999c23a72cb04d241f811b11369..1a23337096fbba124037d668f69aa9dc8a97df7d 100644 GIT binary patch delta 219 zcmdnOewJf{J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuPXP{45TXD8 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_18.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_18.binproto index 4f37a1f216fa52ded85d032074fbff6252eeba85..07942f3b5a26474ba6cba13b2dd8fd388553c50f 100644 GIT binary patch delta 219 zcmdnOewJf{J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuPXP{45TXD8 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_19.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_19.binproto index 9754cd8d28db4c36deac843e668279a42035f161..9cdf2e86d70b1cf0db1406aa91c59bc0c82e06d8 100644 GIT binary patch delta 219 zcmdnOewJf{J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuPXP{45TXD8 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_20.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_20.binproto index 018c98dff4d302bbf4c24f6e31b9e42d6faaacf9..1ac7dd0143beb53bd3647f9d742b03ee687e535c 100644 GIT binary patch delta 219 zcmdnOewJf{J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuPXP{45TXD8 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_21.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_21.binproto index 5a956278c7ad446dc0acb25121155df46b0a3781..f8d7585dc6d1f372c9ce40a0edabc141d7f55d18 100644 GIT binary patch delta 219 zcmdnQewt&0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuj{yx%5S#!2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_22.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_22.binproto index 1e2a71c894573f22c6d127204e83db6084c20a9d..64bb7885be15851ade29bc39f60740d99b3033a1 100644 GIT binary patch delta 219 zcmX@cexGB5J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+ku-vJk55ZC|! diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_23.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_23.binproto index c3cf8bd64296f59401365b144e679173e10cf87b..265c3c167626dd2eb6e92652db018f75c57ccbed 100644 GIT binary patch delta 219 zcmX@YewSl|J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wdO(h6JHF4pyNkE&&DxZtltUEV7e-Gn#T*F{B73 caj*%+O-^Kr0rEhq*oC4dzh#P;oWcAR0EtZz`v3p{ diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_24.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_24.binproto index f2faf4eb1388d46c84765157940d127529f5c262..0adaaa6c8e9b042fca81ac023f1ded7bc4622a18 100644 GIT binary patch delta 219 zcmdnQewt&0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuj{yx%5S#!2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_25.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_25.binproto index 1c77bee33ca74f90854530a6c3093bf7ccb0d138..e08f302db1e8d00b046e05b845142f7e7681e9c4 100644 GIT binary patch delta 219 zcmdnQewt&0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuj{yx%5S#!2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_26.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_26.binproto index 7cf9ec21c18933a007b2f4b1ed5fa56c7489002b..6dbc5639c703490a0b391589c9e787a93c0b20ec 100644 GIT binary patch delta 219 zcmX@YewSl|J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuUjY_i5Yhku diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_27.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_27.binproto index a3b1fe8349158c0ded45d542cdc04b4849d2e246..e18c714be6e4e951e48d5775439c0008222d2269 100644 GIT binary patch delta 219 zcmX@YewSl|J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wdO(h6JHF4pyNkE&&DxZtltUEV7e-Gn#T*F{B73 caj*%+O-^Kr0rEhq*oC4dzh#P;oWcAR0EtZz`v3p{ diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_28.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_28.binproto index 06dd4f18d03b844eabab8e467a6b1ef8349029a3..2e9952afb31f108396569b08b482a431eb4876d1 100644 GIT binary patch delta 219 zcmdnQewt&0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuj{yx%5S#!2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_29.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_29.binproto index 4fe38766a16473eb52a49cc37118d7545723a309..61fcb8e752f3275f15a4df98424321d39743b6bf 100644 GIT binary patch delta 219 zcmdnQewt&0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuj{yx%5S#!2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_30.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_30.binproto index f0726b85a4f54f2a04ff678691d6a2c022676792..2fb193be3fd641f742dc9d25ec7d598ccc8625c6 100644 GIT binary patch delta 219 zcmdnQewt&0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuj{yx%5S#!2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_31.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_31.binproto index 49bb4169b26da74aa124765a9a0512356582094a..5b7b01bc2d90a07755d77592000a733c1688e98e 100644 GIT binary patch delta 219 zcmdnQewt&0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuj{yx%5S#!2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_32.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_32.binproto index 02893198bd4a8a3a7038cd24a5fa2a493906ff25..77ba470e87a801788d1cd5ea7a45484a9291ed5a 100644 GIT binary patch delta 219 zcmdnOewJf{J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuPXP{45TXD8 diff --git a/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_33.binproto b/app/src/androidTest/assets/backupTests/chat_item_group_change_chat_update_33.binproto index ac7aaf22b0955818fae55d9465738d6c897227ba..525588c42a340fb28d058f9c431ce7bbd23c5df8 100644 GIT binary patch delta 219 zcmdnOewJf{J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHD%0Nx?1laW+t1Bv(zxQ#`aNeT!%1d5sf#xBb RxlG+pz6!6bnS7qwP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Npxj%gh{+kuPXP{45TXD8 diff --git a/app/src/androidTest/assets/backupTests/chat_item_individual_call_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_individual_call_update_00.binproto index 07f249ce5388db7d190312a722516431e75446b3..77f461d9937cd2df325a9c0f3620175b2b56259e 100644 GIT binary patch delta 61 zcmV-D0K)&+1N{TAM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hOs^1s^ delta 23 ecmey%e1my|J0p9NPyz>wP|V~2#;D0>84UngA_n*X diff --git a/app/src/androidTest/assets/backupTests/chat_item_individual_call_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_individual_call_update_01.binproto index 93b5d1a9b8e2513f98c6366de692e4bf7e906b83..e829a586aa48877d05faeb6aab406ac346dae366 100644 GIT binary patch delta 61 zcmV-D0K)&^1BC>zM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hWkzp5_ delta 23 ecmZo=dBD8Eosm6BD1n1TC}wg1W7Oocj1~Y-AO?{D diff --git a/app/src/androidTest/assets/backupTests/chat_item_individual_call_update_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_individual_call_update_02.binproto index 76c409935018cd7b74b2ce1293af0f68cfbcb8b4..e49d9ce5b1009d2cf42c1644c9842900f8f5e32f 100644 GIT binary patch delta 61 zcmV-D0K)&?1A_#xM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hUkZ%{1 delta 23 ecmZo>xyQW0osm6BD1n1TC}wg1W7OocjAj5%#s-T3 diff --git a/app/src/androidTest/assets/backupTests/chat_item_individual_call_update_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_individual_call_update_03.binproto index 51cf94cc528b6442a45f274c12aa6436936129d6..69b4faef11ee3d2c75745cba75cc8606fa6af0a7 100644 GIT binary patch delta 61 zcmV-D0K)&+1N{TAM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hOs^1s^ delta 23 ecmey%e1my|J0p9NPyz>wP|V~2#;D0>84UngA_n*X diff --git a/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_00.binproto index 4ab29602c8d2312032a4cd3239e580121b81c61b..affe330ba35b50ac3ed0af6b53c017dee25e259a 100644 GIT binary patch delta 61 zcmV-D0K)&x1M&l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hDrK}g= delta 23 ecmeywe3p5GJ0p9NPyz>wP|V~2#;D0>8C3ySGX~cH diff --git a/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_01.binproto index 2ca2a723cc774446668cdf9a1c571c2cb17152b6..be5521c7a47273a411594aa2062437a8ec80be95 100644 GIT binary patch delta 61 zcmV-D0K)&x1M&l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hDrK}g= delta 23 ecmeywe3p5GJ0p9NPyz>wP|V~2#;D0>8C3ySGX~cH diff --git a/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_02.binproto index 0e1bae11d556679453c316b8f59c3b02fa59de17..0e97f77e387c14cd6290118a53d6cd9cbdc13796 100644 GIT binary patch delta 61 zcmV-D0K)&&1Nj56M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hKsQVZ7 delta 23 ecmey#e1&;~J0p9NPyz>wP|V~2#;D0>8Fc_yZU*cC diff --git a/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_03.binproto index 5462b30bc5a241c24ee74f1743b95b6875575410..7922671adb2a0eae2359a6d9cf64418068bd0e46 100644 GIT binary patch delta 61 zcmV-D0K)&$1NQ^4M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hIs0kPE delta 23 ecmey)e2IC3J0p9NPyz>wP|V~2#;D0>8MOdd6b9x1 diff --git a/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_learned_profile_update_04.binproto index 3a9dc1a8deb51f0403705a93c5d52ccf9498282d..7ed8b8afc5e1c5fdace799abb8c4af3fad5c8aca 100644 GIT binary patch delta 61 zcmV-D0K)&z1M~y1M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hFrk)q( delta 23 ecmeyue4crOJ0p9NPyz>wP|V~2#;D0>8Px$-jRxHS diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_00.binproto index fa173ee8e0f4a173edc46bc6cd3e00982c5c3496..09fe7cc429ab5decbdf758320f1515d62f9a5a83 100644 GIT binary patch delta 62 zcmV-E0KxyW1=t0!M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?J0kVY`w*UYD delta 24 fcmcb?x|wx@J0p9NPyz>wP|V~2#;DC_8Mzq&S|bLo diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_01.binproto index 38bd7999d97766ca709a72d674952d0160acde75..4473d22fdce5307581cfd63f51801d9cc01e7c55 100644 GIT binary patch delta 62 zcmV-E0KxzK1)&D8M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@70k|9(6951J delta 24 fcmZ3;_J?(YJ0p9NPyz>wP|V~2#;DC_8ABKWTj~c2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_02.binproto index f0ec663b8ff64a35d01cdf7a0273bf218ecaab12..10bfde1ee4f527dac71a43a6b483683e514cca28 100644 GIT binary patch delta 62 zcmV-E0KxzK1)&D8M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@70k|9(6951J delta 24 fcmZ3;_J?(YJ0p9NPyz>wP|V~2#;DC_8ABKWTj~c2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_03.binproto index a49fa0f43430497e2e4c252fdccb46100e671302..6c9744d7e72361d892f78709d7075b97cd3de336 100644 GIT binary patch delta 61 zcmV-D0K)&e1m6U(M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)ibou?Qn delta 23 fcmcc5vWsPdJ0p9NPyz>wP|V~2#;D0>87Bb%RE7s2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_04.binproto index 17ae913aa9c1343e38bda07531adc6edc565f878..479c82cc5e8838e352d73b21c6d4e81d75e401b0 100644 GIT binary patch delta 61 zcmV-D0K)%;1gZqEM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h*gqjzM delta 23 ecmZ3*(#o>Iosm6BD1n1TC}wg1W7OocjIjVqH3oqI diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_05.binproto index 72287321116f4cd524757c676e447e9eae8b2ec0..b2a310d3a33620b66c9f85c10bbceaf867359d47 100644 GIT binary patch delta 61 zcmV-D0K)&k1my&UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)ihpnVuS delta 23 fcmaFLa)4!nJ0p9NPyz>wP|V~2#;D0>8D{_hRg?!Z diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_06.binproto index 42a029b1d4ecf02fc94d6ede5973fc13fc263bba..874d5610f72677001b7a95a58c712efa0036f1fc 100644 GIT binary patch delta 62 zcmV-E0KxzC1(^o0M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?~0kk|9`v3p{ delta 24 fcmbQq_JwtWJ0p9NPyz>wP|V~2#;DC_8GRT5T51OJ diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_07.binproto index e6d2671e7e60a82daf098c712a7995816df32f28..9265e90e918ba2644f5114b8751a6ce0251ce4fb 100644 GIT binary patch delta 62 zcmV-E0KxzA1(yb}M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?|0kcFG^#A|> delta 24 fcmbQv_K9_aJ0p9NPyz>wP|V~2#;DC_8NC<*S_%g2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_08.binproto index 4c1d0aedb3d06a664f94b921cd0ddb14b283d9c0..3908bc87682d5ff1620bee0056baea24df5de2d3 100644 GIT binary patch delta 62 zcmV-E0KxyZ1=|I%M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?M0kioRzyJUM delta 24 fcmcc3x{Y;%J0p9NPyz>wP|V~2#;DC_8Tl9iTC)bX diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_09.binproto index c68a6b4d23582c0d7fe8422a2d1ff27cd7fd149b..8a3785a47cb52d84641371f8214222dc2a45a326 100644 GIT binary patch delta 61 zcmV-D0K)&p1nLB^M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)imqS_cr delta 23 fcmaFGa+GC*J0p9NPyz>wP|V~2#;D0>8Rr53R&@tK diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_10.binproto index be7e96772a52fbb1eea74a8bba3f13d7196c31fb..c87c0a08a99853678244ed81bdcf02a8593af46e 100644 GIT binary patch delta 61 zcmV-D0K)&x1o8y1M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iurlJ^P delta 23 fcmeywa+YO-J0p9NPyz>wP|V~2#;D0>8J7Y8SKS9$ diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_11.binproto index c43b417485147691b478972467fd86e9a26713ed..d876ee6d406b21b8d5b40c4f1614c59310ea2b19 100644 GIT binary patch delta 61 zcmV-D0K)&V1l9zwM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iSnPwOc delta 23 ecmcb{vXNzjJ0p9NPyz>wP|V~2#;D0>89M<|@dpI} diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_12.binproto index 58be016dc818006ea08ac10c7b39f53b30fa1d33..300a3107131b559ab37a5422b5a745806ddc2aaa 100644 GIT binary patch delta 62 zcmV-E0KxyR1=9tvM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?E0k9btr~m)} delta 24 fcmcc2x{h^&J0p9NPyz>wP|V~2#;DC_8QB;CSu+No diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_13.binproto index 89e82218d45a6c92cd92f10997cb9f6f07f54248..16e78a428003193cc72b56999dcad38be0cea155 100644 GIT binary patch delta 62 zcmV-E0KxzL1)>J9M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@80l1hL761SM delta 24 fcmZ3)_Lp^oJ0p9NPyz>wP|V~2#;DC_8ABNXTp9-r diff --git a/app/src/androidTest/assets/backupTests/chat_item_payment_notification_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_payment_notification_14.binproto index 8de36e3e1196dcb0c1409fa3f0e4650e1b537bea..0dce02ab17f7881d0bb1f91fd7d2e6e4f75133f4 100644 GIT binary patch delta 62 zcmV-E0KxzM1)~PAM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@90l5?y82|tP delta 24 fcmZ3?_K$UgJ0p9NPyz>wP|V~2#;DC_8N(O>TuKKJ diff --git a/app/src/androidTest/assets/backupTests/chat_item_profile_change_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_profile_change_update_00.binproto index 60478d0bdf11e0ebc7be028bf16af18b8687c2a6..6098f339925331389f8fa260c64a4cb72c1bffdd 100644 GIT binary patch delta 61 zcmV-D0K)&_1BL{!M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hXk=hrX delta 23 ecmZo;dC0uMosm6BD1n1TC}wg1W7OocjFtdTO$L+z diff --git a/app/src/androidTest/assets/backupTests/chat_item_profile_change_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_profile_change_update_01.binproto index 0acaaebae622696409361f5f42d660fdb9b4ccfe..762373b4f5a0aca065ea6eb5ab4279fd8c8b885f 100644 GIT binary patch delta 61 zcmV-D0K)&`1BV2#M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hYl2aF; delta 23 ecmZo?dBnWIosm6BD1n1TC}wg1W7Oocj8*_odIpyO diff --git a/app/src/androidTest/assets/backupTests/chat_item_profile_change_update_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_profile_change_update_02.binproto index 8608f0a590558bbc40278411348f69b199195f3d..0382248e50acf31b3cadc946299766af41b0899d 100644 GIT binary patch delta 61 zcmV-D0K)&@1B3*yM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hVkmwhe delta 23 ecmZo+xzD`8osm6BD1n1TC}wg1W7OocjOGAN^9GIp diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_00.binproto index 97e673912c2cd60c4c07d045054e1675c0e0a8cb..a7f8ea8217c799f6eefb40b73ba0cbfd4968187f 100644 GIT binary patch delta 61 zcmV-D0K)&*1N;N9M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hNs%97d delta 23 ecmeyve4TlNJ0p9NPyz>wP|V~2#;D0>8TA2J^#=6- diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_01.binproto index 71f4a3cfd60ea64524fa775dd2999350df6ab1cb..c917b48e910a2b1645fd711341e2bae2b8fa7e24 100644 GIT binary patch delta 61 zcmV-D0K)&%1NZ~5M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hJsDc;r delta 23 ecmeyte3^NJJ0p9NPyz>wP|V~2#;D0>8MOgeK?dmn diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_02.binproto index 2801aa1bc16e7a3a7e9e1ba5f80ecf55a5039bd8..33ea7946f47cb8297d96fc7c101e496293da241e 100644 GIT binary patch delta 61 zcmV-D0K)&v1MmZ|M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hBq`DW{ delta 23 ecmeyse42TKJ0p9NPyz>wP|V~2#;D0>8I=K6*#^-7 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_03.binproto index 8e2a03a4c9ad74c057a9722e88c9b544f25f466a..b43db4a9688456a657de63a59685a18f05976dd2 100644 GIT binary patch delta 61 zcmV-D0K)&&1Nj56M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hKsQVZ7 delta 23 ecmey#e1&;~J0p9NPyz>wP|V~2#;D0>8Fc_yZU*cC diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_04.binproto index f5a5a3d9ecd735332df41277954add73e7681665..5e9daa9c76c1f99e9abbbd365a8482654fd839f2 100644 GIT binary patch delta 61 zcmV-D0K)&$1NQ^4M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hIs0kPE delta 23 ecmey)e2IC3J0p9NPyz>wP|V~2#;D0>8MOdd6b9x1 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_05.binproto index a5367d2abbeac4ae88354192bf8d0b327adeee7d..183eec91a3d6fe83e1d54fd2e978aa847339e839 100644 GIT binary patch delta 61 zcmV-D0K)&x1M&l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hDrK}g= delta 23 ecmeywe3p5GJ0p9NPyz>wP|V~2#;D0>8C3ySGX~cH diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_06.binproto index 3f29c80895c54a2ada061f1e0029adb206e13d19..7e2d68eed134a5c6d75e8bf7a59dc6449c7c60d0 100644 GIT binary patch delta 61 zcmV-D0K)&$1NQ^4M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hIs0kPE delta 23 ecmey)e2IC3J0p9NPyz>wP|V~2#;D0>8MOdd6b9x1 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_07.binproto index 4ac9d6b2d1809773a43fa86b343ead9638820669..874359b59df110f6959d88fdc45f6b9b6bc3c788 100644 GIT binary patch delta 61 zcmV-D0K)&&1Nj56M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hKsQVZ7 delta 23 ecmey#e1&;~J0p9NPyz>wP|V~2#;D0>8Fc_yZU*cC diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_08.binproto index ec1cbd23826953d3c8f2af55c9b335e9c7a94446..76b05e252ec108e63d0c512f0c84fef675ff3580 100644 GIT binary patch delta 61 zcmV-D0K)&v1MmZ|M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hBq`DW{ delta 23 ecmeyse42TKJ0p9NPyz>wP|V~2#;D0>8I=K6*#^-7 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_09.binproto index 0d73b53b47a49ddd172a89f83dc53872ad23f790..646cf502451e1c1ec9d714d5db6b3c116649ef04 100644 GIT binary patch delta 61 zcmV-D0K)&&1Nj56M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hKsQVZ7 delta 23 ecmey#e1&;~J0p9NPyz>wP|V~2#;D0>8Fc_yZU*cC diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_10.binproto index cd573f98e93ca0f8c212aeb750947ce96c5bd59f..ef5a7cfc5d79c4b6c9727bdf401e56d8c366089b 100644 GIT binary patch delta 61 zcmV-D0K)&$1NQ^4M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hIs0kPE delta 23 ecmey)e2IC3J0p9NPyz>wP|V~2#;D0>8MOdd6b9x1 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_11.binproto index fd20e01dc5e5e02ccf2504bd18a3cd772b61aad1..309807da265c48779d9a20f14c72d4a512c808f3 100644 GIT binary patch delta 61 zcmV-D0K)&v1MmZ|M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hBq`DW{ delta 23 ecmeyse42TKJ0p9NPyz>wP|V~2#;D0>8I=K6*#^-7 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_12.binproto index 601dee89778ad55d6b5ce1dbf9ff7932df0473b5..fc83fe8f154b719fb6a73a9aafef5cfbcacf5e61 100644 GIT binary patch delta 61 zcmV-D0K)&$1NQ^4M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hIs0kPE delta 23 ecmey)e2IC3J0p9NPyz>wP|V~2#;D0>8MOdd6b9x1 diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_13.binproto index f363ea766c4e34d348b4d544b2aba076076136ff..6f590c74c61092970501c0beb37f301fa835c9a0 100644 GIT binary patch delta 61 zcmV-D0K)&&1Nj56M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hKsQVZ7 delta 23 ecmey#e1&;~J0p9NPyz>wP|V~2#;D0>8Fc_yZU*cC diff --git a/app/src/androidTest/assets/backupTests/chat_item_remote_delete_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_remote_delete_14.binproto index 82e239386df103d8685d87abcb739d99c585b2c5..66796fd42322ba7948cda7e648b20dc0e59e7dd9 100644 GIT binary patch delta 61 zcmV-D0K)&x1M&l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hDrK}g= delta 23 ecmeywe3p5GJ0p9NPyz>wP|V~2#;D0>8C3ySGX~cH diff --git a/app/src/androidTest/assets/backupTests/chat_item_session_switchover_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_session_switchover_update_00.binproto index e946b65ce02aa69d46a9d205b32f0ca5421da3c2..bfb521b96dd997d448e67fe79eef25a26cb4a607 100644 GIT binary patch delta 61 zcmV-D0K)&x1M&l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hDrK}g= delta 23 ecmeywe3p5GJ0p9NPyz>wP|V~2#;D0>8C3ySGX~cH diff --git a/app/src/androidTest/assets/backupTests/chat_item_session_switchover_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_session_switchover_update_01.binproto index 7a767b7ada156da565a1469c70feffd285582cff..fe059b39340f15e4eb6f55be968e23da361c6278 100644 GIT binary patch delta 61 zcmV-D0K)&y1M>s0M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hErX?5S delta 23 ecmey&e2#g8J0p9NPyz>wP|V~2#;D0>8Pxz+UUD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h9qsSN3 delta 23 ecmaFIe3E&CJ0p9NPyz>wP|V~2#;D0>85IFme+J6{ diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_01.binproto index 2d2ea28a45fd3fad5099f32f52852e9f6b55280c..f9447f26baa19ec4705a7e937c01cb56223cb09b 100644 GIT binary patch delta 61 zcmV-D0K)&t1MUN`M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h9qsSN3 delta 23 ecmaFIe3E&CJ0p9NPyz>wP|V~2#;D0>85IFme+J6{ diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_02.binproto index 15fad7532548019aea60148d9f2c5488fd21df41..1fcefbb773a19792434aa87d1bba3edd8c91083d 100644 GIT binary patch delta 61 zcmV-D0K)&t1MUN`M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h9qsSN3 delta 23 ecmaFIe3E&CJ0p9NPyz>wP|V~2#;D0>85IFme+J6{ diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_03.binproto index cd7b86b25be39e44a4fdbbd5e55822829205ea70..fcb5bdc8b5daa407bd15828c1c057762cecbd25d 100644 GIT binary patch delta 61 zcmV-D0K)&t1MUN`M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h9qsSN3 delta 23 ecmaFIe3E&CJ0p9NPyz>wP|V~2#;D0>85IFme+J6{ diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_04.binproto index 6a7954ec0a38f839f576fa6dccff0d92e28ea743..7ec90064a27ab7b9e2eee1f975bff379edc365d2 100644 GIT binary patch delta 61 zcmV-D0K)&t1MUN`M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h9qsSN3 delta 23 ecmaFIe3E&CJ0p9NPyz>wP|V~2#;D0>85IFme+J6{ diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_05.binproto index 98dda811c2924529743b547465d87c6df4f8a452..a42072992a0b620ee933d0d6d9ae17d9fdc13359 100644 GIT binary patch delta 61 zcmV-D0K)&$1NQ^4M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hIs0kPE delta 23 ecmey)e2IC3J0p9NPyz>wP|V~2#;D0>8MOdd6b9x1 diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_06.binproto index dc838e19dffd80962d6442fa3f47e5df94e4da04..f32ac27ef02ee77ac12880334c0c01aff652846a 100644 GIT binary patch delta 61 zcmV-D0K)&@1B3*yM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hVkmwhe delta 23 ecmZo+xzD`8osm6BD1n1TC}wg1W7OocjOGAN^9GIp diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_07.binproto index 8ec4f4a48bed6b1e20e8f4e030a94b1c3e483b44..392612462d4ab9e73225149903e51a531ea99582 100644 GIT binary patch delta 61 zcmV-D0K)&t1MUN`M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h9qsSN3 delta 23 ecmaFIe3E&CJ0p9NPyz>wP|V~2#;D0>85IFme+J6{ diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_08.binproto index b169625e91592bfe31fb588d01f860ee95e7bfec..c6c6f0e53886fd08691a77f7c7e71f75e02adabe 100644 GIT binary patch delta 61 zcmV-D0K)&@1B3*yM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hVkmwhe delta 23 ecmZo+xzD`8osm6BD1n1TC}wg1W7OocjOGAN^9GIp diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_09.binproto index d27e0180b47af2b1f5a613270994b57ad2ade9ff..17e1d37e21d8e6f95abec2244b2b1b3cb4c743dc 100644 GIT binary patch delta 61 zcmV-D0K)&@1B3*yM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hVkmwhe delta 23 ecmZo+xzD`8osm6BD1n1TC}wg1W7OocjOGAN^9GIp diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_10.binproto index b3645f24411d3602d98b19827466eb851017fa6d..8e1707dd7bad39073e8a15b013705d5fa54a7095 100644 GIT binary patch delta 61 zcmV-D0K)&@1B3*yM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hVkmwhe delta 23 ecmZo+xzD`8osm6BD1n1TC}wg1W7OocjOGAN^9GIp diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_11.binproto index fffb150caefa7e0c66e220463111d26ed87cc3ae..36ec62618adb971f07ce92d46c002490fdf35e5e 100644 GIT binary patch delta 61 zcmV-D0K)&@1B3*yM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hVkmwhe delta 23 ecmZo+xzD`8osm6BD1n1TC}wg1W7OocjOGAN^9GIp diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_12.binproto index f6e588656bd841be8f4b2053e71a4d387b8c1a2f..63d97ba707aae7ab6015d7adb5cd358cf0cfa55e 100644 GIT binary patch delta 61 zcmV-D0K)&t1MUN`M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h9qsSN3 delta 23 ecmaFIe3E&CJ0p9NPyz>wP|V~2#;D0>85IFme+J6{ diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_13.binproto index b846acc22c4769a2353f0bf8542ddf5f02d0a310..3d42e4cb0a82012f2bdc85c468bcd57837b38a41 100644 GIT binary patch delta 61 zcmV-D0K)&t1MUN`M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h9qsSN3 delta 23 ecmaFIe3E&CJ0p9NPyz>wP|V~2#;D0>85IFme+J6{ diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_14.binproto index 6ee47833dc1bb1c9030230337c98175f60e27d7b..0baf021afc5691e0ee9819023f722cca4b8cf80a 100644 GIT binary patch delta 61 zcmV-D0K)&t1MUN`M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h9qsSN3 delta 23 ecmaFIe3E&CJ0p9NPyz>wP|V~2#;D0>85IFme+J6{ diff --git a/app/src/androidTest/assets/backupTests/chat_item_simple_updates_15.binproto b/app/src/androidTest/assets/backupTests/chat_item_simple_updates_15.binproto index 3ae69328d56cfe6e78b32bbfd7517dffbb0f7f93..a8d34570ad01f351c125516e70115d2ee02e8dc4 100644 GIT binary patch delta 61 zcmV-D0K)&t1MUN`M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h9qsSN3 delta 23 ecmaFIe3E&CJ0p9NPyz>wP|V~2#;D0>85IFme+J6{ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_00.binproto index e872b57a1e49945631e476e82eae9f327699911b..49c69153aba6b810a085a9bd2d5642b376275dc8 100644 GIT binary patch delta 115 zcmX@cHi>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=8Ihkx30lwKTIsgCw delta 38 ucmbQlevEB{J0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yHit6VG6Dd-4he|> diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_01.binproto index c1a334604a66c0c81e511e64b0204138f08f540d..baaa9d6d8f26f44144109edbb33d26e4b2877776 100644 GIT binary patch delta 115 zcmcb{wupU$J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=8IhlMJ0nIrsY5)KL delta 38 ucmZ3)evNH|J0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yHit6#G6Dd>&I!2y diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_02.binproto index 91bed9d6e81dd487532631dade5b66c0b3fae36d..bdb8554ab748cbb67bd025274804ec3823056313 100644 GIT binary patch delta 115 zcmcb`wv2s)J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=8Ihg_(0npJdb^rhX delta 38 ucmZ3+ev55`J0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yHit3=G6Dd?{t3kZ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_03.binproto index c8b0078aa8695cda2026492bf9a6b8ff423ed641..401f665f1a3cfff20f75d39156cc75460b0e9f8c 100644 GIT binary patch delta 115 zcmeyyc8Gn0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=8Ihk@90qOZJ$^ZZW delta 38 ucmX@a{*7&eJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yHit6hG6De0P6`XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=8IhlGH0nMu}s{jB1 delta 38 ucmaFFK8<~YJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yHit6xG6Dd?@d^3> diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_05.binproto index f7058532bb940cbc15108ffccc54a988ddc86e3a..09b33e05809f4ecc6bdfc732bb008df0a434e5a5 100644 GIT binary patch delta 115 zcmeyxc8q<4J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=8IhhI>0qv14)&Kwi delta 38 ucmX@c{)=scJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yHit45G6De1ehMW3 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_06.binproto index a33295c2ebd920d7a4632911efeecbff83182352..bf052b538ede464e01736ffa00c5bb4b422abaef 100644 GIT binary patch delta 115 zcmcb{wupU$J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=8IhlMJ0nIrsY5)KL delta 38 ucmZ3)evNH|J0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yHit6#G6Dd>&I!2y diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_07.binproto index 96c7d49fd0f0e7184774655859621ef8301f7526..c00f9d564b35658249261965849f181de862aecb 100644 GIT binary patch delta 115 zcmeyyc8Gn0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=8Ihk@90qOZJ$^ZZW delta 38 ucmX@a{*7&eJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yHit6hG6De0P6`XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=8Ihh<80m5-EMgRZ+ delta 38 ucmbQneu`~_J0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yHit4fG6Dd;J_(fo diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_09.binproto index 0bc1ad22d95bb966ccd3d9a7c266acda739234ba..c5f489f9161c9f8e39f3f31907d619c60d40f852 100644 GIT binary patch delta 115 zcmeyyc8Gn0J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=8Ihk@90qOZJ$^ZZW delta 38 ucmX@a{*7&eJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yHit6hG6De0P6`XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=8IhlGH0nMu}s{jB1 delta 38 ucmaFFK8<~YJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yHit6xG6Dd?@d^3> diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_11.binproto index 26e6489cd9d7ec1c6d3f7e06fb83d0a47dcfafe3..014efd830ed8263a3a6474a30eb93e7dbaaffca7 100644 GIT binary patch delta 115 zcmZo-KgYhoosqMRQK*@bgGH!*vL9oVUXdc}tJ2oh{<#k)T76@kpShLmQcmc4^~%T6 z_ag3{*d-Px=4ZtK(ZUMUvXL=D6{tn`UQX`$_56QNAAR)dBwv)oq0Gz)HBXnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=8IhlMJ0nIrsY5)KL delta 38 ucmZ3)evNH|J0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yHit6#G6Dd>&I!2y diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_13.binproto index f38e9273e649290ca75fe470d4a8380d3396a710..5a9845bc00685caff632c05d2ac45e8ef018a739 100644 GIT binary patch delta 115 zcmaFHwuya%J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=8IhkS^0o$1_ng9R* delta 38 ucmdnQ{)}ydJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yHit6BG6Dd`jtS`i diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_formatted_text_14.binproto index dd4151f9571cd577deba1fa972a5ab9f5128443b..1a614fe9ecf178a02ba70be2d58766f109c5528f 100644 GIT binary patch delta 115 zcmcb_HjjOSJ0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY Q>y++Cbrp=8Ihi~e0m-2*UH||9 delta 38 ucmbQoeu-^^J0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yHit5KG6Dd=o(Zi0 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_00.binproto index 2e4fa99464090649ba1aa2091a7ac3f99a2212f2..0eae171bc0fbba659f078f98389573efae47b74d 100644 GIT binary patch delta 61 zcmV-D0K)&`1cwE%M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)i@lSvpN delta 23 ecmZo?eZ;cCosm6BD1n1TC}wg1W7OocjJp6(BnKG) diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_01.binproto index 8dd6c5505a10c6d0b24b86623d1f086ddad9b20f..38eb28264e5236358de184b9cf1f2a9139ba972e 100644 GIT binary patch delta 61 zcmV-D0K)&01-AvTM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jei?tXt delta 23 fcmdnaI*E0IJ0p9NPyz>wP|V~2#;D0>8D9YaPACT| diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_02.binproto index 6c9e277be128da67c8568f8672ea32c615f0ced5..62f49ae83227627bf85e7b3e7ed3f4037b2acfe0 100644 GIT binary patch delta 61 zcmV-D0K)&C1;YifM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jqkzp8D delta 23 fcmX@iI*)aOJ0p9NPyz>wP|V~2#;D0>8NUGlP(%k# diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_03.binproto index 42a1a7db4bb86c3708eb24a31efe148fc5769fa8..94a6a6bc72d2aaca91abdf38c2b3aeb23e988aac 100644 GIT binary patch delta 61 zcmV-D0K)&@1cU{!M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)i=k=_^? delta 23 ecmZo+z0b12osm6BD1n1TC}wg1W7OocjN1WEod*yA diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_04.binproto index 317a8476142c2219df099e09fe25106435819759..0a0fa4f61536bfbb98fa161d89b4385d3c873738 100644 GIT binary patch delta 61 zcmV-D0K)(11dRo-M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)i}mLC{2 delta 23 ecmeBWeZjK9osm6BD1n1TC}wg1W7Oocj0XTwG6yRF diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_05.binproto index 38cf510f7d19851b42e62fa3ed78ae8e25299f97..dd2baff89d0f649d4d3a7d28e3152e869f027c3a 100644 GIT binary patch delta 61 zcmV-D0K)%;1*!$GM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jRg^(Bw delta 23 ecmZ3*+RD1Yosm6BD1n1TC}wg1W7OocjCTP`-vUD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)itrYRU- delta 23 fcmey!a)xDtJ0p9NPyz>wP|V~2#;D0>8J7S6SFi_G diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_07.binproto index 3bd5b9b5c4f9a3cd1aa3de6dc7abcebe3091748f..5a073bfe5f7d3832229caa4e9d4a4db621771829 100644 GIT binary patch delta 61 zcmV-D0K)&W1lI(xM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iTnco-@ delta 23 ecmcc4vWaDbJ0p9NPyz>wP|V~2#;D0>8M^>e9tQ{j diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_08.binproto index 5ccc937e1033d89fccd2f6d7c92a5f350a4ebf14..da4659e0dd317c7590b073ad502082231e697736 100644 GIT binary patch delta 61 zcmV-D0K)%?1+E3KM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jVhjbVi delta 23 ecmZ3-+R3`Xosm6BD1n1TC}wg1W7Oocj1K`!lLrp~ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_09.binproto index 42e30f29f283579a7b8bf7b930bfe6f0d7752389..171e50341086a6eab32d3143bc755aab22e3f6d6 100644 GIT binary patch delta 61 zcmV-D0K)(L1fd16M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jIpOYAH delta 23 fcmZ3;`iEtMJ0p9NPyz>wP|V~2#;D0>8Lt2URJI3a diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_10.binproto index a075dfedeec85a7d4b51926cd5126c86b31999d4..86a053ace6e312de282fcf423783e14cb542083b 100644 GIT binary patch delta 61 zcmV-D0K)%?1+E3KM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jVhjbVi delta 23 ecmZ3-+R3`Xosm6BD1n1TC}wg1W7Oocj1K`!lLrp~ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_11.binproto index 41de10a6f98b91c5600abb6a66069c56a6b860ea..a5430b592bcb4c265b361db1bda8d6ae021f4331 100644 GIT binary patch delta 61 zcmV-D0K)%?1+E3KM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jVhjbVi delta 23 ecmZ3-+R3`Xosm6BD1n1TC}wg1W7Oocj1K`!lLrp~ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_12.binproto index b395250d757e9100ff92e2a9debdee225db2b6d7..8b21d5b6ce534368f08f28cbca6e3944dfaf2aea 100644 GIT binary patch delta 61 zcmV-D0K)&!1oZ^4M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)ixs0|ov delta 23 fcmey$a)D)oJ0p9NPyz>wP|V~2#;D0>8CL)RSY!uc diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_13.binproto index c1658dea2111336338314f237ec8f146ba3f46c4..60b00b94f1540594dbac901b15e942d7c41abdd1 100644 GIT binary patch delta 61 zcmV-D0K)(91eFD_M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)j6ndcZx delta 23 fcmbQr`hjJGJ0p9NPyz>wP|V~2#;D0>8BYKJQkn-t diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_long_text_14.binproto index b7da4fc72ce0ba0974ca7e578a62239f07909c31..ed16fbc7e6a0a6b34a9c3b87dab6fdf097d62850 100644 GIT binary patch delta 61 zcmV-D0K)(K1fT`5M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jHpBfl# delta 23 fcmZ3$`kQ5gJ0p9NPyz>wP|V~2#;D0>87~6>REY;< diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_00.binproto index f6b4e9230bf1feb5c2dbcf325cf28177dcc9af5b..ac7b71ac75c74775915b6563c0989e5fe78935b1 100644 GIT binary patch delta 61 zcmV-D0K)&`1BV2#M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hYl2aF; delta 23 ecmZo?dBnWIosm6BD1n1TC}wg1W7Oocj8*_odIpyO diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_01.binproto index 219a3aae57d8120aa6fb76181405957292944299..f9679560fed2a600bba23893e512123d06a21ec8 100644 GIT binary patch delta 61 zcmV-D0K)&q1nUH_M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)inqf;17 delta 23 fcmaFOa*SnzJ0p9NPyz>wP|V~2#;D0>8Rr22R-y+) diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_02.binproto index e9f492ea9b2ecd0194467f7bf91aff4071dd599e..6f52108e6ae58e2663ea1237a3d102413c06ec04 100644 GIT binary patch delta 62 zcmV-E0Kxy^2Zji+M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?%0;~BK#{d8T delta 24 fcmZqUc*wrNosm6BD1n1TC}wg1W7OuejFwCQS2G5_ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_03.binproto index 892e97bd8c59c1fce8e621c0c335b9588483102e..3699c22e123bf0c75822c815722d1e0c9733d2dd 100644 GIT binary patch delta 61 zcmV-D0K)&{1Be8$M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hZlFS#Q delta 23 ecmeBRdCa`Qosm6BD1n1TC}wg1W7OocjMe~8rv{n; diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_04.binproto index 6ab0ff9bac265b70551495ce43ad1b0ffe4cca36..9fe749da0412c38df861d6cced90c64d03fa8226 100644 GIT binary patch delta 61 zcmV-D0K)&l1m*;=M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iip!OI( delta 23 fcmaFHa*$<%J0p9NPyz>wP|V~2#;D0>8D|0jRlx@} diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_05.binproto index b5098949dd7f86ddeef4ef1f52b5c17a5499e721..969d45dfd3930f46f992007ae85f001f56ee25b5 100644 GIT binary patch delta 62 zcmV-E0Kxy~2aE`?M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?-01A+vwM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hTkMUD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)ioqs$mk delta 23 fcmaFEa-3y@J0p9NPyz>wP|V~2#;D0>8Rr84R?i1V diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_08.binproto index ccab74b8da3ba83fb9b861c53db17194e7b58780..55a4bc0f500757ca611f61622fb0adf44cb7fac4 100644 GIT binary patch delta 62 zcmV-E0Kxy^2Zji+M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?%0;~BK#{d8T delta 24 fcmZqUc*wrNosm6BD1n1TC}wg1W7OuejFwCQS2G5_ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_09.binproto index aa4507e4feac52914bbc8818745528cd7001aa04..b0d251c6fd207387354c4dbb38c83d3011c3808a 100644 GIT binary patch delta 61 zcmV-D0K)&{1Be8$M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hZlFS#Q delta 23 ecmeBRdCa`Qosm6BD1n1TC}wg1W7OocjMe~8rv{n; diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_10.binproto index 9cb2a641704062f4738f88372a455ec74134de63..c91f62427d8329eba7feab7b68c13b965f335eab 100644 GIT binary patch delta 61 zcmV-D0K)&l1m*;=M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iip!OI( delta 23 fcmaFHa*$<%J0p9NPyz>wP|V~2#;D0>8D|0jRlx@} diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_11.binproto index fc3a8c5c24baa9e0fed023d58fc51e7bb9b5f26b..2027c9b99be839dea70d0fd0c9972ccdb829c519 100644 GIT binary patch delta 62 zcmV-E0Kxy|2Z{)=M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?*01A+vwM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hTkMUD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)ioqs$mk delta 23 fcmaFEa-3y@J0p9NPyz>wP|V~2#;D0>8Rr84R?i1V diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_sms_14.binproto index bd83375f674703a310f171a16e40edde88fb2a31..9ddb002ee5d0e77e86be63af798144bffd327093 100644 GIT binary patch delta 62 zcmV-E0Kxy`2Z#u;M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?(0<7^D%>V!Z delta 24 fcmeC+c+9@Rosm6BD1n1TC}wg1W7OuejMhv5SCaUD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)ido|zag delta 23 fcmaFJvWI1ZJ0p9NPyz>wP|V~2#;D0>8K(dMRNx0D diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_01.binproto index 4a6ca736944c319b14fb905af52b33c2c7187134..6ef5b12f384bc1ba35b67e2800cfe0702714463f 100644 GIT binary patch delta 61 zcmV-D0K)&t1nvZ|M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iqq`nwd delta 23 fcmaFIa*}0(J0p9NPyz>wP|V~2#;D0>85aToS1AWg diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_02.binproto index 6fe2901d4f2bb99d9492c1fcea350868a3082d3d..a6d5189fef40680673dbdf1bcfe357bb9a4503f0 100644 GIT binary patch delta 61 zcmV-D0K)&*1pEZBM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)i&t6Ug> delta 23 fcmeyva-C&^J0p9NPyz>wP|V~2#;D0>8P@{UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iaoh}$A delta 23 fcmcb|vXfwP|V~2#;D0>87Be&R9Ocd diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_04.binproto index cf4e45f1d8b11f24c24fc89354740df3f672d5e4..e518f5fb7fd80011559e380ad1cbe3b07d1fd3b6 100644 GIT binary patch delta 61 zcmV-D0K)&s1nmT{M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)ipq(vB0 delta 23 fcmaFMa)M=pJ0p9NPyz>wP|V~2#;D0>85aNmR{RG_ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_05.binproto index 7c97b3c3b3b7056c785e95dab49ddcfd1f929c83..d5ff79febdbac19b5d863a7ad22b670a61c34de7 100644 GIT binary patch delta 61 zcmV-D0K)&-1pWlDM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)i)tWFq) delta 23 fcmeyza+76)J0p9NPyz>wP|V~2#;D0>88-p|S@{Qj diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_06.binproto index 016e4221b7dc876eb9b88e760de48a4550537189..f218f561bd45657ba0c5e25bcd1ccf8a82abf92e 100644 GIT binary patch delta 61 zcmV-D0K)&a1lt6#M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iXo5L6# delta 23 ecmcc3vW;bfJ0p9NPyz>wP|V~2#;D0>8T$ZJ(gzd( diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_07.binproto index 356638608779f29c7d99353dfb5c8864895aa69b..cc464f04d1448775771c4e3b03d422ed65016c6c 100644 GIT binary patch delta 61 zcmV-D0K)&u1n&f}M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)irr8gK^ delta 23 fcmaFQa*AbxJ0p9NPyz>wP|V~2#;D0>85aQnS5^m5 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_08.binproto index 53781c565219d0f8ca3d426945ceedf873ac405a..5a6ed591ae4fc242d4645aa6f480344a017740ae 100644 GIT binary patch delta 61 zcmV-D0K)&*1pEZBM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)i&t6Ug> delta 23 fcmeyva-C&^J0p9NPyz>wP|V~2#;D0>8P@{UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iaoh}$A delta 23 fcmcb|vXfwP|V~2#;D0>87Be&R9Ocd diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_10.binproto index 15a5f13e2b563e0390ec6d11ff4fed57778f2163..c4e3a11ecb6cead4aaf8737e4b36598b6376e460 100644 GIT binary patch delta 61 zcmV-D0K)&s1nmT{M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)ipq(vB0 delta 23 fcmaFMa)M=pJ0p9NPyz>wP|V~2#;D0>85aNmR{RG_ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_11.binproto index b84cb47a13e63cc7f2b9110fa3a93be05e89c6e7..b4caeed3660b449922e6ab4cf514e6bba5c16d5a 100644 GIT binary patch delta 61 zcmV-D0K)&*1pEZBM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)i&t6Ug> delta 23 fcmeyva-C&^J0p9NPyz>wP|V~2#;D0>8P@{UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iXo5L6# delta 23 ecmcc3vW;bfJ0p9NPyz>wP|V~2#;D0>8T$ZJ(gzd( diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_13.binproto index 1aa281915efb96d9668cef26dbe408698d6ef9d9..c8b6e8abea82e767b4f0d7ca03bc34cffe35d2ff 100644 GIT binary patch delta 61 zcmV-D0K)&u1n&f}M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)irr8gK^ delta 23 fcmaFQa*AbxJ0p9NPyz>wP|V~2#;D0>85aQnS5^m5 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_special_attachments_14.binproto index 29110404c720d6ab7ad08c6d5bc7c9dc2dbe8f86..ac00c6953b93b3bee21dab31fc1912cc0a5caaad 100644 GIT binary patch delta 61 zcmV-D0K)&-1pWlDM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)i)tWFq) delta 23 fcmeyza+76)J0p9NPyz>wP|V~2#;D0>88-p|S@{Qj diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_00.binproto index c13e785f92d30535fe59ee1bfa801573aa5e20b9..bd169feda7fa68a6e349ee71894119a479f374f2 100644 GIT binary patch delta 61 zcmV-D0K)&f1mFa)M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)ico*)=3 delta 23 fcmaFBvYTatJ0p9NPyz>wP|V~2#;D0>87Bh(RI>*o diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_01.binproto index 372c238cdc03604cefa9f556fc4aed86b431f594..1d60d5cd7702d574257200d91b3e95f82d8a6b51 100644 GIT binary patch delta 62 zcmV-E0Kxy62)+ogM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@a0;pCO@Bjb+ delta 24 fcmdnTF_UA1J0p9NPyz>wP|V~2#;DC_84H;JR>%hD diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_02.binproto index fc63977dbf9a6556ee245be04f6cd03f89f3cee8..cfaf7806360ef34398ac11e46c1ce820cfea5436 100644 GIT binary patch delta 62 zcmV-E0Kxy!2=)lDM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0^70>gF~m;e9( delta 24 gcmeyyagk$#J0p9NPyz>wP|V~2#;DC_8CNm^0AWZ6j{pDw diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_03.binproto index a82639ef29965601a3bd83aa0791c683e452c442..3a123251aad4f8d58308cc9a3b3531be35b41d60 100644 GIT binary patch delta 62 zcmV-E0Kxy!1@;B7M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?n0l^Iz5dZ)H delta 24 fcmeyydXaU5J0p9NPyz>wP|V~2#;DC_88sOJU&99o diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_04.binproto index a389cf9fc236fd9928c265360b34c67801c6a121..ffa5f9d535f8f218b6b2d866153f6b2605483be5 100644 GIT binary patch delta 62 zcmV-E0Kxy92f+uhM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0^|0jg0Lb^rhX delta 24 gcmX@eK8JmSJ0p9NPyz>wP|V~2#;DC_8NVUD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?A1F#DhrT_o{ delta 24 fcmX@lxr%dxJ0p9NPyz>wP|V~2#;DC_8JU;?StSOY diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_06.binproto index a752edfbe7b54b1b4f4081e82f31138b929d70a6..67e8cb3544e9ea6e0de8a8b841fc68c69c335140 100644 GIT binary patch delta 62 zcmV-E0KxyM1UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?90j;eUnE(I) delta 24 fcmX@dx{`H+J0p9NPyz>wP|V~2#;DC_85tP?SVIPo diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_07.binproto index 129f853d9beca18edea0d0189ff36ed04447591d..13768b33ca0253e6c1b9e57be36077045dffd234 100644 GIT binary patch delta 62 zcmV-E0Kxym2j~Z|M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?Z0=qUB>;M1& delta 24 fcmaFCewclOJ0p9NPyz>wP|V~2#;DC_8Ks#3U3>=P diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_08.binproto index 71d779e05f6eef5c71b4624b2611c20aca4f9771..22f4410a62e653c1094c94184716a0b078a6d12d 100644 GIT binary patch delta 62 zcmV-E0Kxx|3AG8ZM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0^+0;O^oR{#J2 delta 24 gcmdnWIe~M7J0p9NPyz>wP|V~2#;DC_8DB5~09Hf?P5=M^ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_09.binproto index b88cad67db87671bf3ca0eb98bba31ca6cee2ecb..d346d2712cdec7f035f02379cadde4a3d6e739f6 100644 GIT binary patch delta 61 zcmV-D0K)%_1+fLNM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jYh~F3? delta 23 ecmdnU+QYiRosm6BD1n1TC}wg1W7Oocj86bf8V48v diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_10.binproto index fcdc049789eeb59d1c48a71e5c4bd6aea6cd123c..3e3cd4edc3f6dbc542a7e91fca7efdaf8e5f99e2 100644 GIT binary patch delta 62 zcmV-E0KxyK2h0bsM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0_80k5SPmjD0& delta 24 gcmX@ZzMOr7J0p9NPyz>wP|V~2#;DC_8UHf^09rc-jsO4v diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_11.binproto index 95b78bcf4476481d0c2d39ec1137b588fcc19fd8..3eb2a1f2f55ebd08c73fa7a6a333744974b16622 100644 GIT binary patch delta 62 zcmV-E0Kxyy3G@lDM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?l1Htzg6#xJL delta 24 fcmeyud7g8FJ0p9NPyz>wP|V~2#;DC_8P%BqU=;@p diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_12.binproto index c79c65c28006311f0c0016e3a307897d522efa32..71072c6cc3e4eedf51b40541a96f40ffa79c387c 100644 GIT binary patch delta 61 zcmV-D0K)&p1nLB^M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)imqS_cr delta 23 fcmaFGa+GC*J0p9NPyz>wP|V~2#;D0>8Rr53R&@tK diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_13.binproto index 1dfbeeb4e84a6d7dc08d12609f50b761e8c4aef3..b1435c709977d24774c0a8d9be3baea7036146fc 100644 GIT binary patch delta 62 zcmV-E0Kxz829*b}M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0^c0kgyyaR2}S delta 24 gcmbQr{()_SJ0p9NPyz>wP|V~2#;DC_8BZ_*09w`uXaE2J diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_standard_attachments_14.binproto index 35546e5fa60406fe939dc31f3b2efe5b5cf99f2b..3334c445a32afbfd6cde6ef219ade4b91ce7deba 100644 GIT binary patch delta 62 zcmV-E0KxzL38D(HM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@81G;(`AOHXW delta 24 fcmZ3)^_O#lJ0p9NPyz>wP|V~2#;DC_8AF)?T+9a- diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_00.binproto index 379ba404acdd4e989cc88cf627db84b6291a9b25..24f78ea96aac20399b040c01a33778393004af43 100644 GIT binary patch delta 61 zcmV-D0K)(H1Dyo0M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)huoVgd` delta 23 ecmbQs@`HJUJ0p9NPyz>wP|V~2#;D0>83O=QSO(bu diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_01.binproto index 8046fd9401d5a5d62923328913e98f0db639f08f..e4d77d1d7286c8b3ad6770770b5e17355742e39c 100644 GIT binary patch delta 61 zcmV-D0K)%^1h53KM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h>hj161 delta 23 ecmdnM(#^8Losm6BD1n1TC}wg1W7OocjL85@Lk5!o diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_02.binproto index c6a3260d0971eadc22ce9743aed602f32ffab108..10aef471411c19e5d0db3d0e42af578e92ce6471 100644 GIT binary patch delta 61 zcmV-D0K)&D1jGceM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iAkmMKG delta 23 ecmX@YGM{CGJ0p9NPyz>wP|V~2#;D0>8Os4r#|F;; diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_03.binproto index f0b7bb5e422d41f149f5588e53dc88137df1f244..3b94a63adaf7d32418cf0cae639d2f339e7be488 100644 GIT binary patch delta 61 zcmV-D0K)(E1DXV|M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hrn@$(m delta 23 ecmbQm@|AgmJ0p9NPyz>wP|V~2#;D0>8GQj#(FV`} diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_04.binproto index 1c5a824c43cc07cefd9a96ef4764a2a16b96d9fc..6ab350b6c73e87e5a50fbc1070b28999b763d749 100644 GIT binary patch delta 61 zcmV-D0K)%@1g`|JM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h=hW8hl delta 23 ecmZ3_(#5jDosm6BD1n1TC}wg1W7Oocj7b1Y76y<2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_05.binproto index 9a9029117e6fca622d9fa5868a35a5b051b613df..48a0e9deb50bced0c6e2284f7d6491c572968b7f 100644 GIT binary patch delta 61 zcmV-D0K)&F1jYogM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iCk=7U9 delta 23 ecmX@cvXEtiJ0p9NPyz>wP|V~2#;D0>87l!$AqLd| diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_06.binproto index 351ab6734f9f3c4bbb81e23b3e8d97382f81342b..10685cc43d45323c905a0df7d1660e4a39c696fa 100644 GIT binary patch delta 61 zcmV-D0K)(C1DFJ`M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hpnp_vt delta 23 ecmbQi@|k&qJ0p9NPyz>wP|V~2#;D0>8NC5gcLvG; diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_07.binproto index 3ae6891f7be4c541cfcfa348c4d4f48c47d1db1a..62b8842f838bf7118f771136eefc45e8ff749f85 100644 GIT binary patch delta 61 zcmV-D0K)%_1hE9LM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h?hv^re delta 23 ecmdnU(!;XBosm6BD1n1TC}wg1W7Oocj41$2a0ZqD diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_08.binproto index ca8b9a2e6401d233519d8d94e21cb1dde085fb62..e5679b57f69504d1d173f768c2063ee20be078a8 100644 GIT binary patch delta 61 zcmV-D0K)&D1jGceM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iAkmMKG delta 23 ecmX@YGM{CGJ0p9NPyz>wP|V~2#;D0>8Os4r#|F;; diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_09.binproto index 08212908aa00bebae904e9b780b3db5b23477005..01dd4b045068bc5b79bc9859b473b0897569d299 100644 GIT binary patch delta 61 zcmV-D0K)(E1DXV|M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hrn@$(m delta 23 ecmbQm@|AgmJ0p9NPyz>wP|V~2#;D0>8GQj#(FV`} diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_10.binproto index ee9c681a6dce1f587d1fc43f102996eabac09b04..e044f703a8472f3e8ab3ea5f85c8d39b374a467c 100644 GIT binary patch delta 61 zcmV-D0K)%@1g`|JM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h=hW8hl delta 23 ecmZ3_(#5jDosm6BD1n1TC}wg1W7Oocj7b1Y76y<2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_11.binproto index a01ff4be222b044f306c0e864b9993420cd9f2cd..959ca920fef3372944ffbc4951a9923d231bc736 100644 GIT binary patch delta 61 zcmV-D0K)&D1jGceM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iAkmMKG delta 23 ecmX@YGM{CGJ0p9NPyz>wP|V~2#;D0>8Os4r#|F;; diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_12.binproto index 67dd7d9b52c2250ec2ee7a5a27b0af529df962f1..4c3002c84405083748c9a13b227dc2e5da11126f 100644 GIT binary patch delta 61 zcmV-D0K)(C1DFJ`M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hpnp_vt delta 23 ecmbQi@|k&qJ0p9NPyz>wP|V~2#;D0>8NC5gcLvG; diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_13.binproto index c036d3d437c901187721f34f63abae97fde07a0d..83479b593a28c35562708f08ffc42e3dd3300273 100644 GIT binary patch delta 61 zcmV-D0K)%_1hE9LM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h?hv^re delta 23 ecmdnU(!;XBosm6BD1n1TC}wg1W7Oocj41$2a0ZqD diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_text_only_14.binproto index 3df633ae1d20441da14e5ad8d4648d48d1da5213..0f814ebb1f6b6fc85e707cdfc309d5010cbff2f4 100644 GIT binary patch delta 61 zcmV-D0K)&F1jYogM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iCk=7U9 delta 23 ecmX@cvXEtiJ0p9NPyz>wP|V~2#;D0>87l!$AqLd| diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_edits_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_edits_00.binproto index b37187d1f9afafcffbe9002e28aed7e37bc1baff..9ae142e0852099904be735648dbb5ffbebf32d63 100644 GIT binary patch delta 61 zcmV-D0K)&+1pNfCM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)i(tJN5T delta 23 fcmey%a)V`qJ0p9NPyz>wP|V~2#;D0>88-j`SUD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?(0jvNQ$N&HU delta 24 fcmeBRd(67Qosm6BD1n1TC}wg1W7OuejMj_*S2_m2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_edits_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_edits_02.binproto index a15ad443f11a6d4fc33ae85db34c2850fc686fb1..043668637c745768516490304a5f9e0965039275 100644 GIT binary patch delta 61 zcmV-D0K)&w1n~s0M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)itrYRU- delta 23 fcmey!a)xDtJ0p9NPyz>wP|V~2#;D0>8J7S6SFi_G diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_00.binproto index c4284d821956a195b173e7c35b22e592a5f2b5f0..541fbedaf9372e873eb1524ea3e34519f825acc7 100644 GIT binary patch delta 61 zcmV-D0K)&V1l9zwM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iSnPwOc delta 23 ecmcb{vXNzjJ0p9NPyz>wP|V~2#;D0>89M<|@dpI} diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_01.binproto index fd201552e84ff59ce81d228cf82fbb3e74e069c3..6b713ee8e9b1ebf366501a82f27180bb615f26f9 100644 GIT binary patch delta 62 zcmV-E0Kxz41(61@M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0??0kB*b;{X5v delta 24 fcmbQp_J(zXJ0p9NPyz>wP|V~2#;DC_8C@6wSn3Aa diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_02.binproto index de8a6ac6610880026d674ce525369dc0a2367723..946e1af61143e5f1683d552c1fd85c057f540724 100644 GIT binary patch delta 62 zcmV-E0Kxyq2J8l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@|0llplbpQYW delta 24 gcmaFEcARa4J0p9NPyz>wP|V~2#;DC_8Rs(s0ACLWYybcN diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_03.binproto index b7463bcdc2296a09eba0c008ac9cc14e98fa09a1..7b9d2b844dece1f33ccfa5237a3ced35919d415e 100644 GIT binary patch delta 62 zcmV-E0Kxy@1%(E%M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?$0ji7_zW@LL delta 24 fcmZo=d%(KEosm6BD1n1TC}wg1W7Ouej24UlR;mWJ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_04.binproto index 834f3e8ed90d0ecde347255375cd582496af1b90..17a912329b148a177cbb01d2c9a5d471a084db1a 100644 GIT binary patch delta 62 zcmV-E0Kxy<1%U>zM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?y0jQi8vj6}9 delta 24 fcmZoUD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@L0iYolz5oCK delta 24 fcmZ3_*2T8Losm6BD1n1TC}wg1W7Ouej7f|DQ=bO4 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_06.binproto index c239ef9b05110faf8398c6070084e966fd8b5e85..efae0852f787b761e2d6cb655792a53d914bc6f2 100644 GIT binary patch delta 62 zcmV-E0Kxy<1%U>zM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?y0jQi8vj6}9 delta 24 fcmZoUD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?d0lYOA^8f$< delta 24 fcmaFEdYpBGJ0p9NPyz>wP|V~2#;DC_8RZ!PUE>Do diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_08.binproto index be6874e0dfd1088b95397b54ad65cae3a565cea8..b2545c0e47491db811d9e5a9e704599a0f1f69a3 100644 GIT binary patch delta 61 zcmV-D0K)&M1UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%kqo@G^m0TBw delta 23 ecmX@hx`K6sJ0p9NPyz>wP|V~2#;C<-85sdlM+SWW diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_09.binproto index 251f38faf82bb2637cd12bcff2606e5792519f2f..27360e484ba12cee375e97063b949bed204f79cd 100644 GIT binary patch delta 61 zcmV-D0K)&S1k(htM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iPm-`q6 delta 23 ecmcc2vW{hgJ0p9NPyz>wP|V~2#;D0>8QTC-Y6koO diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_10.binproto index 020f3040bc1d8cd8c5623987db996b29dc99a7c5..d282ac77567d4c5973d8cbda3133098822337a87 100644 GIT binary patch delta 62 zcmV-E0Kxz31&{`?M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?>0k7Z}-~a#s delta 24 fcmbQh_L_BrJ0p9NPyz>wP|V~2#;DC_8J!scSh@z+ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_11.binproto index d3fc5e3586a3d2f6f4f678959647397aa1cdcde3..da8255fe16b69b6cd636b698f9420f0bd5ee0c33 100644 GIT binary patch delta 62 zcmV-E0Kxyq2J8l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@|0llplbpQYW delta 24 gcmaFEcARa4J0p9NPyz>wP|V~2#;DC_8Rs(s0ACLWYybcN diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_12.binproto index c7300a58ae2df14e985bd74c361a38c808a7c290..8497af972cd4aee60b015c604beeda3188d624e9 100644 GIT binary patch delta 62 zcmV-E0Kxy>1%n2#M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?!0jZQ1xc~qF delta 24 fcmZo>yT`i0osm6BD1n1TC}wg1W7OuejAo1gR!Ro2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_13.binproto index aa8f8de2f74d0f8a029ee831dc24edcf529fcfd6..40b08be8c753261ea779dc07f4037ed6f72ce8f8 100644 GIT binary patch delta 62 zcmV-E0Kxy>1%n2#M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?!0jZQ1xc~qF delta 24 fcmZo>yT`i0osm6BD1n1TC}wg1W7OuejAo1gR!Ro2 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_link_preview_14.binproto index 2f566e187c166a9a0d926d96871c8e281d45111b..a57063abbd496c101acd3c3ee7e1d6fe80c8abe8 100644 GIT binary patch delta 62 zcmV-E0Kxx?2CoLNM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0@L0iYolz5oCK delta 24 fcmZ3_*2T8Losm6BD1n1TC}wg1W7Ouej7f|DQ=bO4 diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_00.binproto index 6a2436d991a11d6277d0e31d7e1b133128fece58..e11cda1566d854d4b4c820a89e1088c3fc1190dc 100644 GIT binary patch delta 61 zcmV-D0K)(F1e*o0M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jCoV^%c delta 23 fcmbQu`i*6SJ0p9NPyz>wP|V~2#;D0>8P5R#Q>X`3 diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_01.binproto index 890910f798547849fe026cf0181f03d75d3abf7d..1081212b778b019a702930dcf67aade0fad96fba 100644 GIT binary patch delta 61 zcmV-D0K)&B1;PceM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jpkmwjx delta 23 fcmX@aI+t~WJ0p9NPyz>wP|V~2#;D0>8NUJmP!|VF diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_02.binproto index a070a0ee6647fa51abcb98a530ce9d0768e81ece..643a2f603d6e53aad473c92a45c8aa7ca3ded463 100644 GIT binary patch delta 62 zcmV-E0Kxyd1>Xg*M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?Q0k!ED%m4rY delta 24 fcmcc5x{Gy#J0p9NPyz>wP|V~2#;DC_8HE@DTXP1( diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_03.binproto index b5c2ab89fe235bd6c1830b284289086f730956f6..66c4d0734cdd6cdf04499a896e4c2f50dd9e5d3f 100644 GIT binary patch delta 61 zcmV-D0K)(M1fm77M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jJpbQvu delta 23 fcmZ3)`j=&cJ0p9NPyz>wP|V~2#;D0>8Lt8WRO1I~ diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_04.binproto index cf1d237c3fc712db256bd19224b0fff56d77cb58..bf2daa0359df9d819391430513e2286f8e4056e8 100644 GIT binary patch delta 61 zcmV-D0K)(K1fT`5M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jHpBfl# delta 23 fcmZ3$`kQ5gJ0p9NPyz>wP|V~2#;D0>87~6>REY;< diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_05.binproto index 6b9934c70cce364651ab2d6bd9e03ba1b30fa0d0..80d9f3289abdbb0ebd2c9e61dc6c55e6350edc48 100644 GIT binary patch delta 61 zcmV-D0K)&B1;PceM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jpkmwjx delta 23 fcmX@aI+t~WJ0p9NPyz>wP|V~2#;D0>8NUJmP!|VF diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_06.binproto index 635aaa11722172b32d3d23543d44f920e3d6cb79..0d59bf1c3995fcea586bedcdb6cd5ff29da7ed77 100644 GIT binary patch delta 61 zcmV-D0K)(01dIi+M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)i|m8KXm delta 23 ecmeBSea^DMosm6BD1n1TC}wg1W7OocjQasm1qUbq diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_07.binproto index 06537a3347c7ebb3c4878d08ada18e8c05772a9c..7c88e4c9906a02fba7b8d10f9e8d2c56ac9010eb 100644 GIT binary patch delta 61 zcmV-D0K)&o1nC5@M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)ilqG1?E delta 23 fcmaFKa)f1rJ0p9NPyz>wP|V~2#;D0>8Rq~1R!9dv diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_08.binproto index 95fdfc085a8c5620415ab436f43aac285ce814ae..65048ab28346edf5e3fc8399efdf5832d405d460 100644 GIT binary patch delta 61 zcmV-D0K)&21-S*VM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jgjHehm delta 23 fcmdnVI)!zEJ0p9NPyz>wP|V~2#;D0>8Q%Z^PJ#z8 diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_09.binproto index 97cb3d6aac148ee8e4c09126032f17c8152a671a..761e3cb3169535ea9fdb0a625ce820aa0c984b81 100644 GIT binary patch delta 61 zcmV-D0K)&01-AvTM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jei?tXt delta 23 fcmdnaI*E0IJ0p9NPyz>wP|V~2#;D0>8D9YaPACT| diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_10.binproto index 0cfd92f4ee9ede302d27d05efa477f0ab049ab02..8f1298dead0c9ef9073673e6e779ffa15a644651 100644 GIT binary patch delta 62 zcmV-E0KxyR1=9tvM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( UdE*=2Sl-CG7G4%kv#0?E0k9btr~m)} delta 24 fcmcc2x{h^&J0p9NPyz>wP|V~2#;DC_8QB;CSu+No diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_11.binproto index 5ae1a6c4f75780f6fd7e02b79abea31cc3c8b5b1..9baba88c2cd29eaa70bd0eedc676e8b5cb230cef 100644 GIT binary patch delta 61 zcmV-D0K)&41-k{XM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jijhPrf delta 23 fcmdnZI*oOMJ0p9NPyz>wP|V~2#;D0>8Q%c_PTU7J diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_12.binproto index ccbbf33508720d5bb2903d70e5de843f9721d365..ed62f8f2615d56843cd9510ecb6a7a5c924a6bbd 100644 GIT binary patch delta 61 zcmV-D0K)&_1cn8$M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)i?lF%3* delta 23 ecmZo;eaN!Gosm6BD1n1TC}wg1W7Oocj5`5O_XidL diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_13.binproto index e1f68fd178eacbf8c12f7dbbb8882422153f35f9..b8bc87ee3af44b4bb477e44c778efa1e01459e85 100644 GIT binary patch delta 61 zcmV-D0K)&31-b>WM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jhjUX62 delta 23 fcmdnRI+b;UJ0p9NPyz>wP|V~2#;D0>8Q%f`POk?u diff --git a/app/src/androidTest/assets/backupTests/chat_item_sticker_message_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_sticker_message_14.binproto index f83e326a9271f5058c6ffd403df9befd706c10e9..91ea6f1d0f9168f0952e60ae527907c4a7b345ba 100644 GIT binary patch delta 61 zcmV-D0K)%|1+)dQM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jbib@zN delta 23 ecmdnO+RwVdosm6BD1n1TC}wg1W7OocjL!j0p$8!V diff --git a/app/src/androidTest/assets/backupTests/chat_item_thread_merge_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_thread_merge_update_00.binproto index 9b37f67be1ea166ea938f3581065933e1d1042f2..6342078dc1949a956442d83d1c4292cece639c70 100644 GIT binary patch delta 61 zcmV-D0K)&x1M&l~M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hDrK}g= delta 23 ecmeywe3p5GJ0p9NPyz>wP|V~2#;D0>8C3ySGX~cH diff --git a/app/src/androidTest/assets/backupTests/chat_item_thread_merge_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_thread_merge_update_01.binproto index a4afc1832a3a73d65dea49a61c12da25e290a6c9..8a7dbc776d085a735ceeeadfbca480fcc7d68e38 100644 GIT binary patch delta 61 zcmV-D0K)&y1M>s0M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hErX?5S delta 23 ecmey&e2#g8J0p9NPyz>wP|V~2#;D0>8Pxz+UUD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hOs^1s^ delta 23 ecmey%e1my|J0p9NPyz>wP|V~2#;D0>84UngA_n*X diff --git a/app/src/androidTest/assets/backupTests/chat_item_view_once_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_view_once_01.binproto index d5e79bfb0e739759a65beeb0e4cd204e320da3ff..7411df0e9a505d18176edeaced8fbe5197c4c126 100644 GIT binary patch delta 61 zcmV-D0K)&c1lUD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iZoV6Gu delta 23 fcmcc1vV&!VJ0p9NPyz>wP|V~2#;D0>87BY$R4fM? diff --git a/app/src/androidTest/assets/backupTests/chat_item_view_once_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_view_once_02.binproto index 2896e62004663f0ec05d62f303320b96f4686373..6ddd4a904408fbf9b2eba3364e115737df1c1084 100644 GIT binary patch delta 61 zcmV-D0K)%-1*rwFM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jQg%=nJ delta 23 ecmZ3<+QPcQosm6BD1n1TC}wg1W7OocjCTM_vIhVF diff --git a/app/src/androidTest/assets/backupTests/chat_item_view_once_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_view_once_03.binproto index 0c408c47be235822cfe2922199100a0805a94e52..0cc7b8fc4468b0963c76287471d428b408c5e0bf 100644 GIT binary patch delta 61 zcmV-D0K)(51d#=>M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)j2m;)F< delta 23 fcmbQp`i5nLJ0p9NPyz>wP|V~2#;D0>8IJ$}QRW9X diff --git a/app/src/androidTest/assets/backupTests/chat_item_view_once_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_view_once_04.binproto index 7c968909b4b0303636cf4339dcaa0ace045d0382..8e91f10bd982c8e2ccb992ba30f4318b8a2f70e0 100644 GIT binary patch delta 61 zcmV-D0K)%*1*ZkDM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jOge4dQ delta 23 ecmZ3^+QhoSosm6BD1n1TC}wg1W7OocjJE(wSO)q4 diff --git a/app/src/androidTest/assets/backupTests/chat_item_view_once_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_view_once_05.binproto index b5eeed931689064ad25c34e9beda345c3e80bb8f..ef8c1111e2ea63ac2b44157c36e2b233978a6983 100644 GIT binary patch delta 61 zcmV-D0K)&V1l9zwM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iSnPwOc delta 23 ecmcb{vXNzjJ0p9NPyz>wP|V~2#;D0>89M<|@dpI} diff --git a/app/src/androidTest/assets/backupTests/chat_item_view_once_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_view_once_06.binproto index 11f17cccbcbfd77efa20f9eaab7a3335d116fd78..b0fc494479e843093b7483f6b6b2df61d60b9fa2 100644 GIT binary patch delta 61 zcmV-D0K)(81e67^M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)j5nQjwP|V~2#;D0>8IJ=1Qf&u7 diff --git a/app/src/androidTest/assets/backupTests/chat_item_view_once_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_view_once_07.binproto index acf025da56c329b75f4ae7c279307d675293f02b..7a7657805f4ad1ce763f93bf537c863e1ebb8f4b 100644 GIT binary patch delta 61 zcmV-D0K)&M1kD7nM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iJl_eMR delta 23 ecmX@hvVvuUJ0p9NPyz>wP|V~2#;D0>85;mmTn6d@ diff --git a/app/src/androidTest/assets/backupTests/chat_item_view_once_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_view_once_08.binproto index 6c26d8597f8972af9bb92cfda6140b21e63ca6ed..beeb9ff538b1d7551a4ad27d2b6ea36833e85330 100644 GIT binary patch delta 61 zcmV-D0K)&11h@pSM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)h}i#Qjw delta 23 ecmdnNGMQzAJ0p9NPyz>wP|V~2#;D0>8M6URs|Kq8 diff --git a/app/src/androidTest/assets/backupTests/chat_item_view_once_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_view_once_09.binproto index 110f7e60f1f3b76c26dc53f2bbe1ee986232cc6b..826ad6b7da6f15c5269293691f88479b9e33c1d2 100644 GIT binary patch delta 61 zcmV-D0K)&(1NsB7M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)hLsdN|k delta 23 ecmeyxe3f~FJ0p9NPyz>wP|V~2#;D0>8Fc|zn+ERy diff --git a/app/src/androidTest/assets/backupTests/chat_item_view_once_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_view_once_10.binproto index c9bb749a8a8b1673d45f86bc888b5221916c3440..c02a26a81a8aacd361061891140b4ce3981a9366 100644 GIT binary patch delta 61 zcmV-D0K)&a1lt6#M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iXo5L6# delta 23 ecmcc3vW;bfJ0p9NPyz>wP|V~2#;D0>8T$ZJ(gzd( diff --git a/app/src/androidTest/assets/backupTests/chat_item_view_once_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_view_once_11.binproto index ec21e7feb5c9d8f7b143f81f15808ad6dafdbf48..c9a3bf51cf9c94c2a1a1e08da2497bf28f9ba5f4 100644 GIT binary patch delta 61 zcmV-D0K)%-1*rwFM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jQg%=nJ delta 23 ecmZ3<+QPcQosm6BD1n1TC}wg1W7OocjCTM_vIhVF diff --git a/app/src/androidTest/assets/backupTests/chat_item_view_once_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_view_once_12.binproto index bdaf8b458eecdbefb3c9abf466863f9fd5777f1c..c6a5d3456cdaefd5461ab8281eafde7abd87f107 100644 GIT binary patch delta 61 zcmV-D0K)(31dj!UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)j0mk}5` delta 23 ecmeBYeZ{iDosm6BD1n1TC}wg1W7OocjE4YGj0Z6Q diff --git a/app/src/androidTest/assets/backupTests/chat_item_view_once_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_view_once_13.binproto index de1fc3b8ee139cdcc4b1e1fce1c6db0a99f66959..e677417fc581d488c351d318795356be3c1fcb78 100644 GIT binary patch delta 61 zcmV-D0K)%-1*rwFM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)jQg%=nJ delta 23 ecmZ3<+QPcQosm6BD1n1TC}wg1W7OocjCTM_vIhVF diff --git a/app/src/androidTest/assets/backupTests/chat_item_view_once_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_view_once_14.binproto index 1e1a3ac6280820e08a587366767490d2dd1eab3c..2c8af978d1bd1751d95d7daa8b8170061a5fc122 100644 GIT binary patch delta 61 zcmV-D0K)&V1l9zwM*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)iSnPwOc delta 23 ecmcb{vXNzjJ0p9NPyz>wP|V~2#;D0>89M<|@dpI} diff --git a/app/src/androidTest/assets/backupTests/recipient_contacts_01.binproto b/app/src/androidTest/assets/backupTests/recipient_contacts_01.binproto index 89e2b60ff1ff5464382a38f7baeda98b1bfca182..28265026d6bca65800dd0603a46e838756e06a1c 100644 GIT binary patch delta 62 zcmV-E0Kxyd1L6a)M*#|%0TPx02m}(50h3MvYA13b1u^2GEc({@Ip~VQuwKQaD_!Q( UacJnda-Ay>!ddwI9C!gj0N~6T8vp$R>m511Ks delta 29 lcmcc0yq0-`J0p9lP%;OLQ2gXT#xPDNc8(j1UvFJ!3IKl73331c diff --git a/app/src/androidTest/assets/backupTests/recipient_contacts_03.binproto b/app/src/androidTest/assets/backupTests/recipient_contacts_03.binproto index c6f3014ab3cbbd12d75b3d8616d75273fc67c980..317398594589ff029ea763564e3f737fdc5878aa 100644 GIT binary patch delta 67 zcmV-J0KEUb1LFg*M*#|&0TP!12m}(60h3MvW-M|c1>WNJTpXHFp!H^YR;;0nL*-_K Z=5d|>`jADD+_EQ^A3_HRubs-Jt1)VY9UuSz delta 29 lcmaFNyq9@{J0p9kP%#IKQ2yjV#xzbRc8>LPPc2<-2mpj<326WT diff --git a/app/src/androidTest/assets/backupTests/recipient_distribution_list_00.binproto b/app/src/androidTest/assets/backupTests/recipient_distribution_list_00.binproto index bed4b89d72999b44af574c0390743f205a488d58..395a706c71ed8150926cddb52131fef4e4292078 100644 GIT binary patch delta 168 zcmbQr+R3&-i;=U9QK*@bgGH!*vLR!XUXdc}tJ2oh{<#k)T76@kpShLmQcmc4^~%T6 z_ag3{*d-Px=4ZtK(ZUMUGKDe15U54>UQX`$_56QNAAR)dBwv)oq0Gz)HBp9c diff --git a/app/src/androidTest/assets/backupTests/recipient_distribution_list_01.binproto b/app/src/androidTest/assets/backupTests/recipient_distribution_list_01.binproto index 6ecb6506b96d58c9720e65b7f4cc27e13555ea7f..79b3e13cb6b997320f2dd21942de4bc6d0abeba4 100644 GIT binary patch delta 168 zcmZ3&I)iP479(dHqfj#=2a8bsWJAU%y&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpT)yB+`hWXVxP|bwP|Rdw#wboJh6JHF4pyP4$-5aNfV>o;Bn~#AxXCU|F_Vun G1p@#y1`J04 diff --git a/app/src/androidTest/assets/backupTests/recipient_distribution_list_02.binproto b/app/src/androidTest/assets/backupTests/recipient_distribution_list_02.binproto index ec3db903c2c79ffd58c6af0bbbe2a260358adf50..0656bb5529e87c1e6336d637e514a32616a2f25a 100644 GIT binary patch delta 168 zcmbQoI*DzA79(dHqfj#=2a8bsWJAU%y&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpT)yB+`hWXVxP|bwP|Rdw#wboJh6JHF4pyP4$-5aNfV>o;Bn~#AxXCU|F_Vun G`2qkjs0=j# diff --git a/app/src/androidTest/assets/backupTests/recipient_distribution_list_03.binproto b/app/src/androidTest/assets/backupTests/recipient_distribution_list_03.binproto index abef3b83282e5f0d6f2f3da3e66b3156f1730fa8..520f164f80c81bacd2bd352a0e1babaf1e4d1a7b 100644 GIT binary patch delta 168 zcmZ3%I)`n879(dHqfj#=2a8bsWJAU%y&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpT)yB+`hWXVxP|bwP|Rdw#wboJh6JHF4pyP4$-5aNfV>o;Bn~#AxXCU|F_Vun Gg#!RJnha9_ diff --git a/app/src/androidTest/assets/backupTests/recipient_groups_00.binproto b/app/src/androidTest/assets/backupTests/recipient_groups_00.binproto index 752e53a0448d86543393648947f0917f55be95cb..91e20e643b74751345b3ebf91403128c2145f85d 100644 GIT binary patch delta 273 zcmdnT@sD$ZJ0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHDAlJuxYbr~dl7E=x-973*q@HG#UYuZQ MQNg&`fSHdG03v&4JOBUy delta 80 zcmeyzxsPLmJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Xpxj#~kQ_)Q2as3H9I^QWGan-WB54z$ diff --git a/app/src/androidTest/assets/backupTests/recipient_groups_01.binproto b/app/src/androidTest/assets/backupTests/recipient_groups_01.binproto index d6167d75a6a985bab69f5bdc2fec0f578ef2b3e1..b1fe6fa419403107c1e670efddeb79eefa86abfb 100644 GIT binary patch delta 273 zcmeyyxs7{+J0oWsqfj#=2a8bsWIx6zy&^@{SEa41{c|5qwED(6KXWVBrJT_9>XnbB z??v1@u}dsY%+HDeqJ-qnlKKkg@NxmqFLz$TqYNqI|eZ1hY z>y++CbrpmHDAlJuxYbr~dl7E=x-973*q@HG#UYuZQ MQNg&`fcY3B02Y^K=Kufz delta 80 zcmdnS{f%>jJ0p9NPyz>wP|V~2#wboJh6JHF4pyP4$u}4yfV>o;Bn~#AxXFo3F;F>n Xpxj#~kQ_)Q2as3H9I^QW^D#yMA5;`f diff --git a/app/src/androidTest/assets/backupTests/recipient_groups_02.binproto b/app/src/androidTest/assets/backupTests/recipient_groups_02.binproto index 1563b5e1718a13b4abfaf39f4c1e503c11aae458..bd3b40635d353cfb766ede42870a4d8ab0dd7201 100644 GIT binary patch delta 273 zcmX@Z*~7iTosqMRQK*@bgGH!*vL9oVUXdc}tJ2oh{<#k)T76@kpShLmQcmc4^~%T6 z_ag3{*d-Px=4ZtK(ZUMUvXL>u5U54>UQX`$_56QNAAR)dBwv)oq0Gz)HB27^oaO XQ0^@gNDicu1IR08j@bNxc@8501r!s` diff --git a/app/src/androidTest/assets/backupTests/recipient_groups_03.binproto b/app/src/androidTest/assets/backupTests/recipient_groups_03.binproto index 7606cb120c0632b8314e0ec083738ebedee842d5..3d70c9d1d9e34e80ef6858996f537a3c79e7d3d5 100644 GIT binary patch delta 273 zcmX@e*~q=YosqMRQK*@bgGH!*vL9oVUXdc}tJ2oh{<#k)T76@kpShLmQcmc4^~%T6 z_ag3{*d-Px=4ZtK(ZUMUvXL>u5U54>UQX`$_56QNAAR)dBwv)oq0Gz)HB27^oaO XQ0^@gNDicu1IR08j@bNxc_JeK{v{Kw diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt index 4f2ef39b58..39f605eb24 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt @@ -73,7 +73,7 @@ class ArchiveImportExportTests { // @Test fun chatItemExpirationTimerUpdate() { - runTests { it.startsWith("chat_item_expiration_timer_") } + runTests { it.startsWith("chat_item_expiration_timer_update_") } } // @Test diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableArchiveExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableArchiveExtensions.kt index c246b289e0..3b35bd9474 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableArchiveExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableArchiveExtensions.kt @@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.backup.v2.exporters.ContactArchiveExporter import org.thoughtcrime.securesms.backup.v2.exporters.GroupArchiveExporter import org.thoughtcrime.securesms.backup.v2.proto.AccountData import org.thoughtcrime.securesms.database.GroupTable +import org.thoughtcrime.securesms.database.IdentityTable import org.thoughtcrime.securesms.database.RecipientTable import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras import org.thoughtcrime.securesms.keyvalue.SignalStore @@ -30,26 +31,36 @@ import org.thoughtcrime.securesms.recipients.RecipientId fun RecipientTable.getContactsForBackup(selfId: Long): ContactArchiveExporter { val cursor = readableDatabase .select( - RecipientTable.ID, - RecipientTable.ACI_COLUMN, - RecipientTable.PNI_COLUMN, - RecipientTable.USERNAME, - RecipientTable.E164, - RecipientTable.BLOCKED, - RecipientTable.HIDDEN, - RecipientTable.REGISTERED, - RecipientTable.UNREGISTERED_TIMESTAMP, - RecipientTable.PROFILE_KEY, - RecipientTable.PROFILE_SHARING, - RecipientTable.PROFILE_GIVEN_NAME, - RecipientTable.PROFILE_FAMILY_NAME, - RecipientTable.PROFILE_JOINED_NAME, - RecipientTable.MUTE_UNTIL, - RecipientTable.CHAT_COLORS, - RecipientTable.CUSTOM_CHAT_COLORS_ID, - RecipientTable.EXTRAS + "${RecipientTable.TABLE_NAME}.${RecipientTable.ID}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.ACI_COLUMN}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.PNI_COLUMN}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.USERNAME}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.E164}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.BLOCKED}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.HIDDEN}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.REGISTERED}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.UNREGISTERED_TIMESTAMP}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_KEY}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_SHARING}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_GIVEN_NAME}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_FAMILY_NAME}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_JOINED_NAME}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.MUTE_UNTIL}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.CHAT_COLORS}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.CUSTOM_CHAT_COLORS_ID}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.EXTRAS}", + "${IdentityTable.TABLE_NAME}.${IdentityTable.IDENTITY_KEY}", + "${IdentityTable.TABLE_NAME}.${IdentityTable.VERIFIED}" + ) + .from( + """ + ${RecipientTable.TABLE_NAME} LEFT OUTER JOIN ${IdentityTable.TABLE_NAME} ON ( + ${RecipientTable.TABLE_NAME}.${RecipientTable.ACI_COLUMN} = ${IdentityTable.TABLE_NAME}.${IdentityTable.ADDRESS} OR ( + ${RecipientTable.TABLE_NAME}.${RecipientTable.ACI_COLUMN} IS NULL AND ${RecipientTable.TABLE_NAME}.${RecipientTable.PNI_COLUMN} = ${IdentityTable.TABLE_NAME}.${IdentityTable.ADDRESS} + ) + ) + """ ) - .from(RecipientTable.TABLE_NAME) .where( """ ${RecipientTable.TYPE} = ? AND ( diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatArchiveExporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatArchiveExporter.kt index e62e74b03c..e8fad00f8b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatArchiveExporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatArchiveExporter.kt @@ -48,9 +48,9 @@ class ChatArchiveExporter(private val cursor: Cursor, private val db: SignalData recipientId = cursor.requireLong(ThreadTable.RECIPIENT_ID), archived = cursor.requireBoolean(ThreadTable.ARCHIVED), pinnedOrder = cursor.requireInt(ThreadTable.PINNED), - expirationTimerMs = cursor.requireLong(RecipientTable.MESSAGE_EXPIRATION_TIME).seconds.inWholeMilliseconds, + expirationTimerMs = cursor.requireLong(RecipientTable.MESSAGE_EXPIRATION_TIME).seconds.inWholeMilliseconds.takeIf { it > 0 }, expireTimerVersion = cursor.requireInt(RecipientTable.MESSAGE_EXPIRATION_TIME_VERSION), - muteUntilMs = cursor.requireLong(RecipientTable.MUTE_UNTIL), + muteUntilMs = cursor.requireLong(RecipientTable.MUTE_UNTIL).takeIf { it > 0 }, markedUnread = ThreadTable.ReadStatus.deserialize(cursor.requireInt(ThreadTable.READ)) == ThreadTable.ReadStatus.FORCED_UNREAD, dontNotifyForMentionsIfMuted = RecipientTable.MentionSetting.DO_NOT_NOTIFY.id == cursor.requireInt(RecipientTable.MENTION_SETTING), style = ChatStyleConverter.constructRemoteChatStyle( diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt index d96302d163..18b620e478 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt @@ -220,8 +220,8 @@ class ChatItemArchiveExporter( MessageTypes.isExpirationTimerUpdate(record.type) -> { builder.updateMessage = ChatUpdateMessage(expirationTimerChange = ExpirationTimerChatUpdate(record.expiresIn)) - builder.expireStartDate = 0 - builder.expiresInMs = 0 + builder.expireStartDate = null + builder.expiresInMs = null } MessageTypes.isProfileChange(record.type) -> { @@ -394,8 +394,8 @@ private fun BackupMessageRecord.toBasicChatItemBuilder(selfRecipientId: Recipien chatId = record.threadId authorId = record.fromRecipientId dateSent = record.dateSent - expireStartDate = if (record.expireStarted > 0) record.expireStarted else 0 - expiresInMs = if (record.expiresIn > 0) record.expiresIn else 0 + expireStartDate = record.expireStarted.takeIf { it > 0 } + expiresInMs = record.expiresIn.takeIf { it > 0 } revisions = emptyList() sms = record.type.isSmsType() if (record.type.isDirectionlessType() || record.messageExtras?.gv2UpdateDescription != null) { @@ -405,32 +405,32 @@ private fun BackupMessageRecord.toBasicChatItemBuilder(selfRecipientId: Recipien sendStatus = record.toRemoteSendStatus(isGroupThread, groupReceipts, exportState) ) - if (expiresInMs > 0 && outgoing?.sendStatus?.all { it.pending == null && it.failed == null } == true) { + if (expiresInMs != null && outgoing?.sendStatus?.all { it.pending == null && it.failed == null } == true) { Log.w(TAG, "Outgoing expiring message was sent but the timer wasn't started! Fixing.") expireStartDate = record.dateReceived } } else { incoming = ChatItem.IncomingMessageDetails( - dateServerSent = max(record.dateServer, 0), + dateServerSent = record.dateServer.takeIf { it > 0 }, dateReceived = record.dateReceived, read = record.read, sealedSender = record.sealedSender ) - if (expiresInMs > 0 && incoming?.read == true && expireStartDate == 0L) { + if (expiresInMs != null && incoming?.read == true && expireStartDate == null) { Log.w(TAG, "Incoming expiring message was read but the timer wasn't started! Fixing.") expireStartDate = record.dateReceived } } } - if (!MessageTypes.isExpirationTimerUpdate(record.type) && builder.expiresInMs > 0 && builder.expireStartDate + builder.expiresInMs < backupStartTime + 1.days.inWholeMilliseconds) { + if (!MessageTypes.isExpirationTimerUpdate(record.type) && builder.expiresInMs != null && builder.expireStartDate != null && builder.expireStartDate!! + builder.expiresInMs!! < backupStartTime + 1.days.inWholeMilliseconds) { Log.w(TAG, "Message expires too soon! Must skip.") return null } - if (builder.expireStartDate > 0 && builder.expiresInMs == 0L) { - builder.expireStartDate = 0 + if (builder.expireStartDate != null && builder.expiresInMs == null) { + builder.expireStartDate = null } return builder @@ -801,7 +801,7 @@ private fun BackupMessageRecord.toRemoteQuote(mediaArchiveEnabled: Boolean, atta attachments = attachments?.toRemoteQuoteAttachments(mediaArchiveEnabled) ?: emptyList(), type = when (type) { QuoteModel.Type.NORMAL -> Quote.Type.NORMAL - QuoteModel.Type.GIFT_BADGE -> Quote.Type.GIFTBADGE + QuoteModel.Type.GIFT_BADGE -> Quote.Type.GIFT_BADGE } ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ContactArchiveExporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ContactArchiveExporter.kt index e7bd9b53fa..ee93df4213 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ContactArchiveExporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ContactArchiveExporter.kt @@ -9,6 +9,7 @@ import android.database.Cursor import okio.ByteString.Companion.toByteString import org.signal.core.util.Base64 import org.signal.core.util.logging.Log +import org.signal.core.util.optionalInt import org.signal.core.util.requireBoolean import org.signal.core.util.requireInt import org.signal.core.util.requireLong @@ -16,6 +17,7 @@ import org.signal.core.util.requireString import org.thoughtcrime.securesms.backup.v2.ArchiveRecipient import org.thoughtcrime.securesms.backup.v2.proto.Contact import org.thoughtcrime.securesms.backup.v2.proto.Self +import org.thoughtcrime.securesms.database.IdentityTable import org.thoughtcrime.securesms.database.RecipientTable import org.thoughtcrime.securesms.database.RecipientTableCursorUtil import org.thoughtcrime.securesms.recipients.Recipient @@ -71,6 +73,8 @@ class ContactArchiveExporter(private val cursor: Cursor, private val selfId: Lon .profileGivenName(cursor.requireString(RecipientTable.PROFILE_GIVEN_NAME)) .profileFamilyName(cursor.requireString(RecipientTable.PROFILE_FAMILY_NAME)) .hideStory(RecipientTableCursorUtil.getExtras(cursor)?.hideStory() ?: false) + .identityKey(cursor.requireString(IdentityTable.IDENTITY_KEY)?.let { Base64.decode(it).toByteString() }) + .identityState(cursor.optionalInt(IdentityTable.VERIFIED).map { IdentityTable.VerifiedStatus.forState(it) }.orElse(IdentityTable.VerifiedStatus.DEFAULT).toRemote()) val registeredState = RecipientTable.RegisteredState.fromId(cursor.requireInt(RecipientTable.REGISTERED)) if (registeredState == RecipientTable.RegisteredState.REGISTERED) { @@ -98,6 +102,14 @@ private fun Recipient.HiddenState.toRemote(): Contact.Visibility { } } +private fun IdentityTable.VerifiedStatus.toRemote(): Contact.IdentityState { + return when (this) { + IdentityTable.VerifiedStatus.DEFAULT -> Contact.IdentityState.DEFAULT + IdentityTable.VerifiedStatus.VERIFIED -> Contact.IdentityState.VERIFIED + IdentityTable.VerifiedStatus.UNVERIFIED -> Contact.IdentityState.UNVERIFIED + } +} + private fun String.e164ToLong(): Long? { val fixed = if (this.startsWith("+")) { this.substring(1) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatArchiveImporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatArchiveImporter.kt index d28db190ca..70a3b61840 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatArchiveImporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatArchiveImporter.kt @@ -55,8 +55,8 @@ object ChatArchiveImporter { RecipientTable.TABLE_NAME, contentValuesOf( RecipientTable.MENTION_SETTING to (if (chat.dontNotifyForMentionsIfMuted) RecipientTable.MentionSetting.DO_NOT_NOTIFY.id else RecipientTable.MentionSetting.ALWAYS_NOTIFY.id), - RecipientTable.MUTE_UNTIL to chat.muteUntilMs, - RecipientTable.MESSAGE_EXPIRATION_TIME to chat.expirationTimerMs.milliseconds.inWholeSeconds, + RecipientTable.MUTE_UNTIL to (chat.muteUntilMs ?: 0), + RecipientTable.MESSAGE_EXPIRATION_TIME to (chat.expirationTimerMs?.milliseconds?.inWholeSeconds ?: 0), RecipientTable.MESSAGE_EXPIRATION_TIME_VERSION to chat.expireTimerVersion, RecipientTable.CHAT_COLORS to chatColor?.serialize()?.encode(), RecipientTable.CUSTOM_CHAT_COLORS_ID to (chatColor?.id ?: ChatColors.Id.NotSet).longValue, diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt index 851a0b1aa7..8b4c1e8b87 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt @@ -456,8 +456,8 @@ class ChatItemArchiveImporter( contentValues.putNull(MessageTable.LATEST_REVISION_ID) contentValues.putNull(MessageTable.ORIGINAL_MESSAGE_ID) contentValues.put(MessageTable.REVISION_NUMBER, 0) - contentValues.put(MessageTable.EXPIRES_IN, this.expiresInMs) - contentValues.put(MessageTable.EXPIRE_STARTED, this.expireStartDate) + contentValues.put(MessageTable.EXPIRES_IN, this.expiresInMs ?: 0) + contentValues.put(MessageTable.EXPIRE_STARTED, this.expireStartDate ?: 0) when { this.outgoing != null -> { @@ -669,7 +669,7 @@ class ChatItemArchiveImporter( } updateMessage.expirationTimerChange != null -> { typeFlags = getAsLong(MessageTable.TYPE) or MessageTypes.EXPIRATION_TIMER_UPDATE_BIT - put(MessageTable.EXPIRES_IN, updateMessage.expirationTimerChange.expiresInMs.toLong()) + put(MessageTable.EXPIRES_IN, updateMessage.expirationTimerChange.expiresInMs) } updateMessage.profileChange != null -> { typeFlags = MessageTypes.PROFILE_CHANGE_TYPE @@ -895,7 +895,8 @@ class ChatItemArchiveImporter( return when (this) { Quote.Type.UNKNOWN -> QuoteModel.Type.NORMAL.code Quote.Type.NORMAL -> QuoteModel.Type.NORMAL.code - Quote.Type.GIFTBADGE -> QuoteModel.Type.GIFT_BADGE.code + Quote.Type.GIFT_BADGE -> QuoteModel.Type.GIFT_BADGE.code + Quote.Type.VIEW_ONCE -> QuoteModel.Type.NORMAL.code } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ContactArchiveImporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ContactArchiveImporter.kt index dd13ebcb95..0acffd8168 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ContactArchiveImporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ContactArchiveImporter.kt @@ -7,10 +7,13 @@ package org.thoughtcrime.securesms.backup.v2.importer import androidx.core.content.contentValuesOf import org.signal.core.util.Base64 +import org.signal.core.util.insertInto import org.signal.core.util.toInt import org.signal.core.util.update import org.thoughtcrime.securesms.backup.v2.proto.Contact +import org.thoughtcrime.securesms.database.IdentityTable import org.thoughtcrime.securesms.database.RecipientTable +import org.thoughtcrime.securesms.database.SQLiteDatabase import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras import org.thoughtcrime.securesms.dependencies.AppDependencies @@ -26,9 +29,12 @@ import org.whispersystems.signalservice.api.push.ServiceId.PNI */ object ContactArchiveImporter { fun import(contact: Contact): RecipientId { + val aci = ACI.parseOrNull(contact.aci?.toByteArray()) + val pni = PNI.parseOrNull(contact.pni?.toByteArray()) + val id = SignalDatabase.recipients.getAndPossiblyMergePnpVerified( - aci = ACI.parseOrNull(contact.aci?.toByteArray()), - pni = PNI.parseOrNull(contact.pni?.toByteArray()), + aci = aci, + pni = pni, e164 = contact.formattedE164 ) @@ -60,6 +66,17 @@ object ContactArchiveImporter { .where("${RecipientTable.ID} = ?", id) .run() + if (contact.identityKey != null && (aci != null || pni != null)) { + SignalDatabase.writableDatabase + .insertInto(IdentityTable.TABLE_NAME) + .values( + IdentityTable.ADDRESS to (aci ?: pni).toString(), + IdentityTable.IDENTITY_KEY to Base64.encodeWithPadding(contact.identityKey.toByteArray()), + IdentityTable.VERIFIED to contact.identityState.toLocal().toInt() + ) + .run(SQLiteDatabase.CONFLICT_REPLACE) + } + return id } } @@ -72,6 +89,14 @@ private fun Contact.Visibility.toLocal(): Recipient.HiddenState { } } +private fun Contact.IdentityState.toLocal(): IdentityTable.VerifiedStatus { + return when (this) { + Contact.IdentityState.DEFAULT -> IdentityTable.VerifiedStatus.DEFAULT + Contact.IdentityState.VERIFIED -> IdentityTable.VerifiedStatus.VERIFIED + Contact.IdentityState.UNVERIFIED -> IdentityTable.VerifiedStatus.UNVERIFIED + } +} + private fun Contact.toLocalExtras(): RecipientExtras { return RecipientExtras( hideStory = this.hideStory diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/util/ArchiveConverterExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/util/ArchiveConverterExtensions.kt index 2f12694a62..725113b815 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/util/ArchiveConverterExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/util/ArchiveConverterExtensions.kt @@ -61,7 +61,7 @@ fun FilePointer?.toLocalAttachment( isGif = gif, caption = Optional.ofNullable(this.caption), blurHash = Optional.ofNullable(this.blurHash), - uploadTimestamp = this.attachmentLocator.uploadTimestamp, + uploadTimestamp = this.attachmentLocator.uploadTimestamp ?: 0, uuid = UuidUtil.fromByteStringOrNull(uuid) ) return PointerAttachment.forPointer( @@ -164,7 +164,7 @@ fun DatabaseAttachment.toRemoteFilePointer(mediaArchiveEnabled: Boolean, content builder.attachmentLocator = FilePointer.AttachmentLocator( cdnKey = this.remoteLocation, cdnNumber = this.cdn.cdnNumber, - uploadTimestamp = this.uploadTimestamp, + uploadTimestamp = this.uploadTimestamp.takeIf { it > 0 }, key = Base64.decode(remoteKey).toByteString(), size = this.size.toInt(), digest = this.remoteDigest.toByteString() diff --git a/app/src/main/protowire/Backup.proto b/app/src/main/protowire/Backup.proto index a7ac5eb914..ca5917a295 100644 --- a/app/src/main/protowire/Backup.proto +++ b/app/src/main/protowire/Backup.proto @@ -86,6 +86,17 @@ message AccountData { bool manuallyCancelled = 3; } + message IAPSubscriberData { + bytes subscriberId = 1; + + oneof iapSubscriptionId { + // Identifies an Android Play Store IAP subscription. + string purchaseToken = 2; + // Identifies an iOS App Store IAP subscription. + uint64 originalTransactionId = 3; + } + } + bytes profileKey = 1; optional string username = 2; UsernameLink usernameLink = 3; @@ -93,8 +104,9 @@ message AccountData { string familyName = 5; string avatarUrlPath = 6; SubscriberData donationSubscriberData = 7; - SubscriberData backupsSubscriberData = 8; + reserved /*backupsSubscriberData*/ 8; // A deprecated format AccountSettings accountSettings = 9; + IAPSubscriberData backupsSubscriberData = 10; } message Recipient { @@ -110,6 +122,12 @@ message Recipient { } message Contact { + enum IdentityState { + DEFAULT = 0; + VERIFIED = 1; + UNVERIFIED = 2; + } + message Registered { } message NotRegistered { uint64 unregisteredTimestamp = 1; @@ -138,6 +156,8 @@ message Contact { optional string profileGivenName = 11; optional string profileFamilyName = 12; bool hideStory = 13; + optional bytes identityKey = 14; + IdentityState identityState = 15; } message Group { @@ -238,9 +258,9 @@ message Chat { uint64 id = 1; // generated id for reference only within this file uint64 recipientId = 2; bool archived = 3; - uint32 pinnedOrder = 4; // 0 = unpinned, otherwise chat is considered pinned and will be displayed in ascending order - uint64 expirationTimerMs = 5; // 0 = no expire timer. - uint64 muteUntilMs = 6; + optional uint32 pinnedOrder = 4; // will be displayed in ascending order + optional uint64 expirationTimerMs = 5; + optional uint64 muteUntilMs = 6; // INT64_MAX (2^63 - 1) = "always muted". bool markedUnread = 7; bool dontNotifyForMentionsIfMuted = 8; ChatStyle style = 9; @@ -283,6 +303,8 @@ message AdHocCall { } message DistributionListItem { + // distribution ids are UUIDv4s. "My Story" is represented + // by an all-0 UUID (00000000-0000-0000-0000-000000000000). bytes distributionId = 1; // distribution list ids are uuids oneof item { @@ -308,7 +330,7 @@ message DistributionList { message ChatItem { message IncomingMessageDetails { uint64 dateReceived = 1; - uint64 dateServerSent = 2; + optional uint64 dateServerSent = 2; bool read = 3; bool sealedSender = 4; } @@ -323,8 +345,8 @@ message ChatItem { uint64 chatId = 1; // conversation id uint64 authorId = 2; // recipient id uint64 dateSent = 3; - uint64 expireStartDate = 4; // timestamp of when expiration timer started ticking down - uint64 expiresInMs = 5; // how long timer of message is (ms) + optional uint64 expireStartDate = 4; // timestamp of when expiration timer started ticking down + optional uint64 expiresInMs = 5; // how long timer of message is (ms) repeated ChatItem revisions = 6; // ordered from oldest to newest bool sms = 7; @@ -614,7 +636,7 @@ message FilePointer { message AttachmentLocator { string cdnKey = 1; uint32 cdnNumber = 2; - uint64 uploadTimestamp = 3; + optional uint64 uploadTimestamp = 3; bytes key = 4; bytes digest = 5; uint32 size = 6; @@ -648,7 +670,8 @@ message Quote { enum Type { UNKNOWN = 0; NORMAL = 1; - GIFTBADGE = 2; + GIFT_BADGE = 2; + VIEW_ONCE = 3; } message QuotedAttachment { @@ -766,8 +789,7 @@ message GroupCall { optional uint64 ringerRecipientId = 3; optional uint64 startedCallRecipientId = 4; uint64 startedCallTimestamp = 5; - // The time the call ended. 0 indicates an unknown time. - uint64 endedCallTimestamp = 6; + optional uint64 endedCallTimestamp = 6; // The time the call ended. bool read = 7; } @@ -823,7 +845,6 @@ message SessionSwitchoverChatUpdate { message GroupChangeChatUpdate { message Update { - // Note: group expiration timer changes are represented as ExpirationTimerChatUpdate. oneof update { GenericGroupUpdate genericGroupUpdate = 1; GroupCreationUpdate groupCreationUpdate = 2; From a74ae00bb4ab327305412d58c69235ba193e1ead Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 2 Dec 2024 15:54:22 -0500 Subject: [PATCH 39/56] Add some more link+sync logs. --- .../linkdevice/LinkDeviceRepository.kt | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt index 5aa62a84a0..7c8566c6cc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt @@ -5,6 +5,7 @@ import org.signal.core.util.Base64 import org.signal.core.util.Stopwatch import org.signal.core.util.isNotNullOrBlank import org.signal.core.util.logging.Log +import org.signal.core.util.logging.logD import org.signal.core.util.logging.logI import org.signal.core.util.logging.logW import org.signal.libsignal.protocol.InvalidKeyException @@ -201,6 +202,7 @@ object LinkDeviceRepository { when (result) { is NetworkResult.Success -> { + Log.d(TAG, "[waitForDeviceToBeLinked] Sucessfully found device after waiting ${System.currentTimeMillis() - startTime} ms.") return result.result } is NetworkResult.ApplicationError -> { @@ -242,34 +244,35 @@ object LinkDeviceRepository { } catch (e: Exception) { return LinkUploadArchiveResult.BackupCreationFailure(e) } + Log.d(TAG, "[createAndUploadArchive] Successfully created backup.") stopwatch.split("create-backup") when (val result = ArchiveValidator.validate(tempBackupFile, ephemeralMessageBackupKey)) { ArchiveValidator.ValidationResult.Success -> { - Log.d(TAG, "Successfully passed validation.") + Log.d(TAG, "[createAndUploadArchive] Successfully passed validation.") } is ArchiveValidator.ValidationResult.ReadError -> { - Log.w(TAG, "Failed to read the file during validation!", result.exception) + Log.w(TAG, "[createAndUploadArchive] Failed to read the file during validation!", result.exception) return LinkUploadArchiveResult.BackupCreationFailure(result.exception) } is ArchiveValidator.ValidationResult.ValidationError -> { - Log.w(TAG, "The backup file fails validation!", result.exception) + Log.w(TAG, "[createAndUploadArchive] The backup file fails validation!", result.exception) return LinkUploadArchiveResult.BackupCreationFailure(result.exception) } } stopwatch.split("validate-backup") val uploadForm = when (val result = SignalNetwork.attachments.getAttachmentV4UploadForm()) { - is NetworkResult.Success -> result.result + is NetworkResult.Success -> result.result.logD(TAG, "[createAndUploadArchive] Successfully retrieved upload form.") is NetworkResult.ApplicationError -> throw result.throwable - is NetworkResult.NetworkError -> return LinkUploadArchiveResult.NetworkError(result.exception).logW(TAG, "Network error when fetching form.", result.exception) - is NetworkResult.StatusCodeError -> return LinkUploadArchiveResult.NetworkError(result.exception).logW(TAG, "Status code error when fetching form.", result.exception) + is NetworkResult.NetworkError -> return LinkUploadArchiveResult.NetworkError(result.exception).logW(TAG, "[createAndUploadArchive] Network error when fetching form.", result.exception) + is NetworkResult.StatusCodeError -> return LinkUploadArchiveResult.NetworkError(result.exception).logW(TAG, "[createAndUploadArchive] Status code error when fetching form.", result.exception) } when (val result = uploadArchive(tempBackupFile, uploadForm)) { - is NetworkResult.Success -> Log.i(TAG, "Successfully uploaded backup.") - is NetworkResult.NetworkError -> return LinkUploadArchiveResult.NetworkError(result.exception).logW(TAG, "Network error when uploading archive.", result.exception) - is NetworkResult.StatusCodeError -> return LinkUploadArchiveResult.NetworkError(result.exception).logW(TAG, "Status code error when uploading archive.", result.exception) + is NetworkResult.Success -> Log.i(TAG, "[createAndUploadArchive] Successfully uploaded backup.") + is NetworkResult.NetworkError -> return LinkUploadArchiveResult.NetworkError(result.exception).logW(TAG, "[createAndUploadArchive] Network error when uploading archive.", result.exception) + is NetworkResult.StatusCodeError -> return LinkUploadArchiveResult.NetworkError(result.exception).logW(TAG, "[createAndUploadArchive] Status code error when uploading archive.", result.exception) is NetworkResult.ApplicationError -> throw result.throwable } stopwatch.split("upload-backup") @@ -282,13 +285,13 @@ object LinkDeviceRepository { ) when (transferSetResult) { - is NetworkResult.Success -> Log.i(TAG, "Successfully set transfer archive.") + is NetworkResult.Success -> Log.i(TAG, "[createAndUploadArchive] Successfully set transfer archive.") is NetworkResult.ApplicationError -> throw transferSetResult.throwable - is NetworkResult.NetworkError -> return LinkUploadArchiveResult.NetworkError(transferSetResult.exception).logW(TAG, "Network error when setting transfer archive.", transferSetResult.exception) + is NetworkResult.NetworkError -> return LinkUploadArchiveResult.NetworkError(transferSetResult.exception).logW(TAG, "[createAndUploadArchive] Network error when setting transfer archive.", transferSetResult.exception) is NetworkResult.StatusCodeError -> { return when (transferSetResult.code) { - 422 -> LinkUploadArchiveResult.BadRequest(transferSetResult.exception).logW(TAG, "422 when setting transfer archive.", transferSetResult.exception) - else -> LinkUploadArchiveResult.NetworkError(transferSetResult.exception).logW(TAG, "Status code error when setting transfer archive.", transferSetResult.exception) + 422 -> LinkUploadArchiveResult.BadRequest(transferSetResult.exception).logW(TAG, "[createAndUploadArchive] 422 when setting transfer archive.", transferSetResult.exception) + else -> LinkUploadArchiveResult.NetworkError(transferSetResult.exception).logW(TAG, "[createAndUploadArchive] Status code error when setting transfer archive.", transferSetResult.exception) } } } From 10ae26c92499ba0e61afe3d6e69e1f0ecaba57f0 Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Mon, 2 Dec 2024 13:06:47 -0800 Subject: [PATCH 40/56] Use AEP when validating backup. --- .../org/thoughtcrime/securesms/backup/v2/BackupRepository.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index 4515e752a2..c0399536d0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -844,8 +844,8 @@ object BackupRepository { } fun validate(length: Long, inputStreamFactory: () -> InputStream, selfData: SelfData): ValidationResult { - val masterKey = SignalStore.svr.masterKey - val key = LibSignalMessageBackupKey(masterKey.serialize(), Aci.parseFromBinary(selfData.aci.toByteArray())) + val accountEntropyPool = SignalStore.account.accountEntropyPool.value + val key = LibSignalMessageBackupKey(accountEntropyPool, Aci.parseFromBinary(selfData.aci.toByteArray())) return MessageBackup.validate(key, MessageBackup.Purpose.REMOTE_BACKUP, inputStreamFactory, length) } From 86d78d2e5d7b30de7f45ffe136108cd185cb4428 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 2 Dec 2024 16:32:41 -0500 Subject: [PATCH 41/56] Fix incorrect ACI/PNI storage for revoked invites in group updates. --- .../v2/exporters/ChatItemArchiveExporter.kt | 1 - .../helpers/SignalDatabaseMigrations.kt | 6 +- .../V258_FixGroupRevokedInviteeUpdate.kt | 93 +++++++++++++++++++ .../migrations/ApplicationMigrations.java | 7 +- 4 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V258_FixGroupRevokedInviteeUpdate.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt index 18b620e478..645a6c86da 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt @@ -85,7 +85,6 @@ import org.whispersystems.signalservice.api.util.UuidUtil import org.whispersystems.signalservice.api.util.toByteArray import java.io.Closeable import java.io.IOException -import java.util.HashMap import java.util.LinkedList import java.util.Queue import java.util.concurrent.Callable diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index f543f6fba2..61ecaaca96 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -114,6 +114,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V254_AddChatFolderC import org.thoughtcrime.securesms.database.helpers.migration.V255_AddCallTableLogIndex import org.thoughtcrime.securesms.database.helpers.migration.V256_FixIncrementalDigestColumns import org.thoughtcrime.securesms.database.helpers.migration.V257_CreateBackupMediaSyncTable +import org.thoughtcrime.securesms.database.helpers.migration.V258_FixGroupRevokedInviteeUpdate /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -230,10 +231,11 @@ object SignalDatabaseMigrations { 254 to V254_AddChatFolderConstraint, 255 to V255_AddCallTableLogIndex, 256 to V256_FixIncrementalDigestColumns, - 257 to V257_CreateBackupMediaSyncTable + 257 to V257_CreateBackupMediaSyncTable, + 258 to V258_FixGroupRevokedInviteeUpdate ) - const val DATABASE_VERSION = 257 + const val DATABASE_VERSION = 258 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V258_FixGroupRevokedInviteeUpdate.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V258_FixGroupRevokedInviteeUpdate.kt new file mode 100644 index 0000000000..105b372a00 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V258_FixGroupRevokedInviteeUpdate.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import androidx.core.content.contentValuesOf +import net.zetetic.database.sqlcipher.SQLiteDatabase +import okio.IOException +import org.signal.core.util.forEach +import org.signal.core.util.logging.Log +import org.signal.core.util.requireBlob +import org.signal.core.util.requireLong +import org.thoughtcrime.securesms.backup.v2.proto.GroupInvitationRevokedUpdate +import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExtras +import org.whispersystems.signalservice.api.push.ServiceId + +/** + * Ensure we store ACIs only in the ACI fields and non-byte prefixed PNIs only in the PNI fields of + * [GroupInvitationRevokedUpdate.Invitee] in [GroupInvitationRevokedUpdate]. + */ +@Suppress("ClassName") +object V258_FixGroupRevokedInviteeUpdate : SignalDatabaseMigration { + + private val TAG = Log.tag(V258_FixGroupRevokedInviteeUpdate::class) + + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + val messageExtrasFixes = mutableListOf>() + + db.query("message", arrayOf("_id", "message_extras"), "message_extras IS NOT NULL AND type & 0x10000 != 0", null, null, null, null) + .forEach { cursor -> + val blob = cursor.requireBlob("message_extras")!! + + val messageExtras: MessageExtras? = try { + MessageExtras.ADAPTER.decode(blob) + } catch (e: IOException) { + Log.w(TAG, "Unable to decode message extras", e) + null + } + + if (messageExtras?.gv2UpdateDescription?.groupChangeUpdate?.updates?.any { it.groupInvitationRevokedUpdate != null } != true) { + return@forEach + } + + val groupUpdateDescription = messageExtras.gv2UpdateDescription + val groupUpdate = groupUpdateDescription.groupChangeUpdate!! + val updates = groupUpdate.updates.toMutableList() + + updates + .replaceAll { change -> + if (change.groupInvitationRevokedUpdate != null) { + val invitees = change.groupInvitationRevokedUpdate.invitees.toMutableList() + + invitees.replaceAll { invitee -> + val inviteeAciFieldServiceId = ServiceId.parseOrNull(invitee.inviteeAci) + val inviteePniFieldServiceId = ServiceId.parseOrNull(invitee.inviteePni) + + if (inviteeAciFieldServiceId is ServiceId.PNI) { + // We have an obvious PNI in the ACI field, move to PNI field without byte prefix + invitee.copy(inviteeAci = null, inviteePni = inviteeAciFieldServiceId.toByteStringWithoutPrefix()) + } else if (inviteePniFieldServiceId is ServiceId.PNI) { + // We have a byte-prefixed encoded PNI in the PNI field, update to remove byte prefix + invitee.copy(inviteePni = inviteePniFieldServiceId.toByteStringWithoutPrefix()) + } else { + // ACI field doesn't have an obvious PNI and PNI doesn't have a byte-prefixed PNI, no fix needed + invitee + } + } + + change.copy(groupInvitationRevokedUpdate = change.groupInvitationRevokedUpdate.copy(invitees = invitees)) + } else { + change + } + } + + val updatedMessageExtras = messageExtras.copy( + gv2UpdateDescription = groupUpdateDescription.copy( + groupChangeUpdate = groupUpdate.copy( + updates = updates + ) + ) + ) + + messageExtrasFixes += cursor.requireLong("_id") to updatedMessageExtras.encode() + } + + messageExtrasFixes.forEach { (id, extras) -> + db.update("message", contentValuesOf("message_extras" to extras), "_id = $id", null) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java index f646a448fc..8461f4aad7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java @@ -161,9 +161,10 @@ static final class Version { static final int SVR2_ENCLAVE_UPDATE_2 = 117; static final int WALLPAPER_MIGRATION_CLEANUP = 118; static final int AEP_INTRODUCTION = 119; + static final int GROUP_EXTRAS_DB_FIX = 120; } - public static final int CURRENT_VERSION = 119; + public static final int CURRENT_VERSION = 120; /** * This *must* be called after the {@link JobManager} has been instantiated, but *before* the call @@ -738,6 +739,10 @@ private static LinkedHashMap getMigrationJobs(@NonNull Co jobs.put(Version.AEP_INTRODUCTION, new AepMigrationJob()); } + if (lastSeenVersion < Version.GROUP_EXTRAS_DB_FIX) { + jobs.put(Version.GROUP_EXTRAS_DB_FIX, new DatabaseMigrationJob()); + } + return jobs; } From 49e57a3c6605d8f54aead2bfa547058265e53fb8 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Tue, 3 Dec 2024 09:49:49 -0400 Subject: [PATCH 42/56] Fix denial dialog for call links. --- .../java/org/thoughtcrime/securesms/WebRtcCallActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java index fb1d2907f1..ecb44a0612 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java @@ -456,7 +456,7 @@ private void initializePendingParticipantFragmentListener() { case DENY_ALL: new MaterialAlertDialogBuilder(this) .setTitle(getResources().getQuantityString(R.plurals.WebRtcCallActivity__deny_d_requests, recipientIds.size(), recipientIds.size())) - .setMessage(getResources().getQuantityString(R.plurals.WebRtcCallActivity__d_people_will_be_added_to_the_call, recipientIds.size(), recipientIds.size())) + .setMessage(getResources().getQuantityString(R.plurals.WebRtcCallActivity__d_people_will_not_be_added_to_the_call, recipientIds.size(), recipientIds.size())) .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(R.string.WebRtcCallActivity__deny_all, (dialog, which) -> { for (RecipientId id : recipientIds) { From e6c5080a07f5cd594d1ce84c170b8340ae7fdeeb Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 3 Dec 2024 11:16:51 -0500 Subject: [PATCH 43/56] Handle web socket closed unexpectedly errors more gracefully. --- .../java/org/thoughtcrime/securesms/ApplicationContext.java | 2 ++ .../internal/websocket/OkHttpWebSocketConnection.java | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index ca855cffb7..3f96244b83 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -362,6 +362,8 @@ private void initializeRx() { return; } + Log.e(TAG, "RxJava error handler invoked", e); + Thread.UncaughtExceptionHandler uncaughtExceptionHandler = Thread.currentThread().getUncaughtExceptionHandler(); if (uncaughtExceptionHandler == null) { uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection.java index f72b759d25..5780202a1f 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/OkHttpWebSocketConnection.java @@ -16,6 +16,7 @@ import org.whispersystems.signalservice.internal.util.Util; import java.io.IOException; +import java.net.SocketException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; @@ -353,7 +354,7 @@ private void cleanupAfterShutdown() { while (iterator.hasNext()) { Map.Entry entry = iterator.next(); - entry.getValue().onError(new IOException("Closed unexpectedly")); + entry.getValue().onError(new SocketException("Closed unexpectedly")); iterator.remove(); } From f44d157f9a8407f69dae8339bd9f345e4ffe06bf Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 3 Dec 2024 11:55:13 -0500 Subject: [PATCH 44/56] Inline the expireVersion capability. --- .../securesms/testing/SignalActivityRule.kt | 2 +- .../privacy/expire/ExpireTimerSettingsRepository.kt | 3 +-- .../InternalConversationSettingsFragment.kt | 2 +- .../thoughtcrime/securesms/database/RecipientTable.kt | 4 ++-- .../securesms/database/RecipientTableCursorUtil.kt | 1 - .../securesms/database/model/RecipientRecord.kt | 2 -- .../securesms/jobs/RefreshOwnProfileJob.java | 5 ----- .../securesms/logsubmit/LogSectionCapabilities.java | 1 - .../org/thoughtcrime/securesms/recipients/Recipient.kt | 3 --- .../securesms/recipients/RecipientUtil.java | 2 +- .../thoughtcrime/securesms/util/ExpirationTimerUtil.kt | 5 ++--- .../securesms/database/RecipientDatabaseTestUtils.kt | 1 - .../api/profiles/SignalServiceProfile.java | 10 +--------- 13 files changed, 9 insertions(+), 32 deletions(-) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/SignalActivityRule.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/SignalActivityRule.kt index 14a7802359..46933f55c8 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/SignalActivityRule.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/SignalActivityRule.kt @@ -141,7 +141,7 @@ class SignalActivityRule(private val othersCount: Int = 4, private val createGro val recipientId = RecipientId.from(SignalServiceAddress(aci, "+15555551%03d".format(i))) SignalDatabase.recipients.setProfileName(recipientId, ProfileName.fromParts("Buddy", "#$i")) SignalDatabase.recipients.setProfileKeyIfAbsent(recipientId, ProfileKeyUtil.createNew()) - SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, false, true, true)) + SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, false, true)) SignalDatabase.recipients.setProfileSharing(recipientId, true) SignalDatabase.recipients.markRegistered(recipientId, aci) val otherIdentity = IdentityKeyUtil.generateIdentityKeyPair() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsRepository.kt index 32c5fe90e6..91fe84d226 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsRepository.kt @@ -14,7 +14,6 @@ import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.sms.MessageSender import org.thoughtcrime.securesms.storage.StorageSyncHelper -import org.thoughtcrime.securesms.util.ExpirationTimerUtil import java.io.IOException private val TAG: String = Log.tag(ExpireTimerSettingsRepository::class.java) @@ -39,7 +38,7 @@ class ExpireTimerSettingsRepository(val context: Context) { consumer.invoke(Result.failure(e)) } } else { - val expireTimerVersion = ExpirationTimerUtil.setExpirationTimer(recipientId, newExpirationTime) + val expireTimerVersion = SignalDatabase.recipients.setExpireMessagesAndIncrementVersion(recipientId, newExpirationTime) val outgoingMessage = OutgoingMessage.expirationUpdateMessage(Recipient.resolved(recipientId), System.currentTimeMillis(), newExpirationTime * 1000L, expireTimerVersion) MessageSender.send(context, outgoingMessage, getThreadId(recipientId), MessageSender.SendType.SIGNAL, null, null) consumer.invoke(Result.success(newExpirationTime)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt index 39db36335c..c5a5778373 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt @@ -343,7 +343,7 @@ class InternalConversationSettingsFragment : DSLSettingsFragment( TextUtils.concat( colorize("DeleteSync", capabilities.deleteSync), ", ", - colorize("VersionedExpirationTimer", capabilities.versionedExpirationTimer) + colorize("SSREv2", capabilities.storageServiceEncryptionV2) ) } else { "Recipient not found!" diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt index 3e1d5549e6..8be110b94f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt @@ -419,7 +419,6 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da fun maskCapabilitiesToLong(capabilities: SignalServiceProfile.Capabilities): Long { var value: Long = 0 value = Bitmask.update(value, Capabilities.DELETE_SYNC, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isDeleteSync).serialize().toLong()) - value = Bitmask.update(value, Capabilities.VERSIONED_EXPIRATION_TIMER, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isVersionedExpirationTimer).serialize().toLong()) value = Bitmask.update(value, Capabilities.STORAGE_SERVICE_ENCRYPTION_V2, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isStorageServiceEncryptionV2).serialize().toLong()) return value } @@ -4713,7 +4712,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da // const val PNP = 7 // const val PAYMENT_ACTIVATION = 8 const val DELETE_SYNC = 9 - const val VERSIONED_EXPIRATION_TIMER = 10 + +// const val VERSIONED_EXPIRATION_TIMER = 10 const val STORAGE_SERVICE_ENCRYPTION_V2 = 11 // IMPORTANT: We cannot sore more than 32 capabilities in the bitmask. diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTableCursorUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTableCursorUtil.kt index d66dc1c3e6..2354515b16 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTableCursorUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTableCursorUtil.kt @@ -177,7 +177,6 @@ object RecipientTableCursorUtil { return RecipientRecord.Capabilities( rawBits = capabilities, deleteSync = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.DELETE_SYNC, Capabilities.BIT_LENGTH).toInt()), - versionedExpirationTimer = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.VERSIONED_EXPIRATION_TIMER, Capabilities.BIT_LENGTH).toInt()), storageServiceEncryptionV2 = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.STORAGE_SERVICE_ENCRYPTION_V2, Capabilities.BIT_LENGTH).toInt()) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientRecord.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientRecord.kt index 6816a39a1c..9a05d47c29 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientRecord.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientRecord.kt @@ -121,7 +121,6 @@ data class RecipientRecord( data class Capabilities( val rawBits: Long, val deleteSync: Recipient.Capability, - val versionedExpirationTimer: Recipient.Capability, val storageServiceEncryptionV2: Recipient.Capability ) { companion object { @@ -129,7 +128,6 @@ data class RecipientRecord( val UNKNOWN = Capabilities( rawBits = 0, deleteSync = Recipient.Capability.UNKNOWN, - versionedExpirationTimer = Recipient.Capability.UNKNOWN, storageServiceEncryptionV2 = Recipient.Capability.UNKNOWN ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java index 4cf87026b3..31e2bd65c9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java @@ -225,11 +225,6 @@ private void setProfileCapabilities(@Nullable SignalServiceProfile.Capabilities AppDependencies.getJobManager().add(new MultiDeviceProfileContentUpdateJob()); } - if (!selfSnapshot.getVersionedExpirationTimerCapability().isSupported() && capabilities.isVersionedExpirationTimer()) { - Log.d(TAG, "Transitioned to versioned expiration timer capable, notify linked devices in case we were the last one"); - AppDependencies.getJobManager().add(new MultiDeviceProfileContentUpdateJob()); - } - if (selfSnapshot.getStorageServiceEncryptionV2Capability() == Recipient.Capability.NOT_SUPPORTED && capabilities.isStorageServiceEncryptionV2()) { Log.i(TAG, "Transitioned to storageServiceEncryptionV2 capable. Notifying other devices and pushing to storage service with a recordIkm."); AppDependencies.getJobManager().add(new MultiDeviceProfileContentUpdateJob()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionCapabilities.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionCapabilities.java index 5c8d3e1b90..5246b1c804 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionCapabilities.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionCapabilities.java @@ -43,7 +43,6 @@ public final class LogSectionCapabilities implements LogSection { if (globalCapabilities != null) { builder.append("DeleteSync: ").append(globalCapabilities.getDeleteSync()).append("\n"); - builder.append("VersionedExpirationTimer: ").append(globalCapabilities.getVersionedExpirationTimer()).append("\n"); builder.append("StorageServiceEncryptionV2: ").append(globalCapabilities.getStorageServiceEncryptionV2()).append("\n"); builder.append("\n"); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt index 4dc9baa1d8..01e3ab0c25 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt @@ -318,9 +318,6 @@ class Recipient( /** The user's capability to handle synchronizing deletes across linked devices. */ val deleteSyncCapability: Capability = capabilities.deleteSync - /** The user's capability to handle tracking an expire timer version. */ - val versionedExpirationTimerCapability: Capability = capabilities.versionedExpirationTimer - /** The user's capability to handle the new storage record encryption scheme. */ val storageServiceEncryptionV2Capability: Capability get() = if (SignalStore.internal.forceSsre2Capability) Capability.SUPPORTED else capabilities.storageServiceEncryptionV2 diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java index 577221e9e8..d0b7e57e3b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java @@ -336,7 +336,7 @@ public static boolean shouldHaveProfileKey(@NonNull Recipient recipient) { } if (threadId == -1 || SignalDatabase.messages().canSetUniversalTimer(threadId)) { - int expireTimerVersion = ExpirationTimerUtil.setExpirationTimer(recipient.getId(), defaultTimer); + int expireTimerVersion = SignalDatabase.recipients().setExpireMessagesAndIncrementVersion(recipient.getId(), defaultTimer); OutgoingMessage outgoingMessage = OutgoingMessage.expirationUpdateMessage(recipient, System.currentTimeMillis(), defaultTimer * 1000L, expireTimerVersion); MessageSender.send(context, outgoingMessage, SignalDatabase.threads().getOrCreateThreadIdFor(recipient), MessageSender.SendType.SIGNAL, null, null); return expireTimerVersion; diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ExpirationTimerUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/ExpirationTimerUtil.kt index a018a4870d..7401b0bc97 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ExpirationTimerUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ExpirationTimerUtil.kt @@ -6,7 +6,6 @@ package org.thoughtcrime.securesms.util import org.thoughtcrime.securesms.database.SignalDatabase -import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId /** @@ -20,8 +19,8 @@ object ExpirationTimerUtil { @JvmStatic fun setExpirationTimer(recipientId: RecipientId, expirationTimeSeconds: Int): Int { - val selfCapable = Recipient.self().versionedExpirationTimerCapability == Recipient.Capability.SUPPORTED - val recipientCapable = Recipient.resolved(recipientId).let { it.versionedExpirationTimerCapability == Recipient.Capability.SUPPORTED || it.expireTimerVersion > 2 } + val selfCapable = true + val recipientCapable = true return if (selfCapable && recipientCapable) { SignalDatabase.recipients.setExpireMessagesAndIncrementVersion(recipientId, expirationTimeSeconds) diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/RecipientDatabaseTestUtils.kt b/app/src/test/java/org/thoughtcrime/securesms/database/RecipientDatabaseTestUtils.kt index d2a381643a..a26ae64cf6 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/database/RecipientDatabaseTestUtils.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/database/RecipientDatabaseTestUtils.kt @@ -127,7 +127,6 @@ object RecipientDatabaseTestUtils { capabilities = RecipientRecord.Capabilities( rawBits = capabilities, deleteSync = Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientTable.Capabilities.DELETE_SYNC, RecipientTable.Capabilities.BIT_LENGTH).toInt()), - versionedExpirationTimer = Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientTable.Capabilities.VERSIONED_EXPIRATION_TIMER, RecipientTable.Capabilities.BIT_LENGTH).toInt()), storageServiceEncryptionV2 = Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientTable.Capabilities.STORAGE_SERVICE_ENCRYPTION_V2, RecipientTable.Capabilities.BIT_LENGTH).toInt()) ), storageId = storageId, diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java index 2b689e26eb..a05879c4b8 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java @@ -195,19 +195,15 @@ public static class Capabilities { @JsonProperty private boolean deleteSync; - @JsonProperty - private boolean versionedExpirationTimer; - @JsonProperty("ssre2") private boolean storageServiceEncryptionV2; @JsonCreator public Capabilities() {} - public Capabilities(boolean storage, boolean deleteSync, boolean versionedExpirationTimer, boolean storageServiceEncryptionV2) { + public Capabilities(boolean storage, boolean deleteSync, boolean storageServiceEncryptionV2) { this.storage = storage; this.deleteSync = deleteSync; - this.versionedExpirationTimer = versionedExpirationTimer; this.storageServiceEncryptionV2 = storageServiceEncryptionV2; } @@ -219,10 +215,6 @@ public boolean isDeleteSync() { return deleteSync; } - public boolean isVersionedExpirationTimer() { - return versionedExpirationTimer; - } - public boolean isStorageServiceEncryptionV2() { return storageServiceEncryptionV2; } From a3af2373972aeed158072d8985751f3c320a53ad Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 3 Dec 2024 12:11:25 -0500 Subject: [PATCH 45/56] Inline the deleteSync capability. --- .../securesms/testing/SignalActivityRule.kt | 2 +- .../components/DeleteSyncEducationDialog.kt | 4 +--- .../app/storage/ManageStorageSettingsFragment.kt | 5 ++--- .../InternalConversationSettingsFragment.kt | 2 -- .../ConversationListFragment.java | 2 +- .../securesms/database/RecipientTable.kt | 4 +--- .../database/RecipientTableCursorUtil.kt | 1 - .../securesms/database/ThreadTable.kt | 4 ++-- .../securesms/database/model/RecipientRecord.kt | 2 -- .../securesms/jobs/MultiDeviceDeleteSyncJob.kt | 15 --------------- .../securesms/jobs/RefreshOwnProfileJob.java | 5 ----- .../securesms/jobs/TrimThreadJob.java | 2 +- .../logsubmit/LogSectionCapabilities.java | 1 - .../securesms/mediaoverview/MediaActions.java | 2 +- .../mediapreview/MediaPreviewRepository.kt | 3 +-- .../securesms/recipients/Recipient.kt | 3 --- .../securesms/util/AttachmentUtil.java | 4 +--- .../thoughtcrime/securesms/util/DeleteDialog.kt | 13 +++---------- .../database/RecipientDatabaseTestUtils.kt | 1 - .../api/profiles/SignalServiceProfile.java | 10 +--------- 20 files changed, 16 insertions(+), 69 deletions(-) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/SignalActivityRule.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/SignalActivityRule.kt index 46933f55c8..d2f21168c7 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/SignalActivityRule.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/SignalActivityRule.kt @@ -141,7 +141,7 @@ class SignalActivityRule(private val othersCount: Int = 4, private val createGro val recipientId = RecipientId.from(SignalServiceAddress(aci, "+15555551%03d".format(i))) SignalDatabase.recipients.setProfileName(recipientId, ProfileName.fromParts("Buddy", "#$i")) SignalDatabase.recipients.setProfileKeyIfAbsent(recipientId, ProfileKeyUtil.createNew()) - SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, false, true)) + SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true)) SignalDatabase.recipients.setProfileSharing(recipientId, true) SignalDatabase.recipients.markRegistered(recipientId, aci) val otherIdentity = IdentityKeyUtil.generateIdentityKeyPair() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/DeleteSyncEducationDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/components/DeleteSyncEducationDialog.kt index 077d80c913..60255378dd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/DeleteSyncEducationDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/DeleteSyncEducationDialog.kt @@ -31,7 +31,6 @@ import org.signal.core.ui.SignalPreview import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.thoughtcrime.securesms.recipients.Recipient /** * Show educational info about delete syncing to linked devices. This dialog uses a subject to convey when @@ -45,8 +44,7 @@ class DeleteSyncEducationDialog : ComposeBottomSheetDialogFragment() { @JvmStatic fun shouldShow(): Boolean { return SignalStore.account.hasLinkedDevices && - !SignalStore.uiHints.hasSeenDeleteSyncEducationSheet && - Recipient.self().deleteSyncCapability.isSupported + !SignalStore.uiHints.hasSeenDeleteSyncEducationSheet } @JvmStatic diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/ManageStorageSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/ManageStorageSettingsFragment.kt index a12608c1a9..d8b1a7395d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/ManageStorageSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/ManageStorageSettingsFragment.kt @@ -70,7 +70,6 @@ import org.thoughtcrime.securesms.keyvalue.KeepMessagesDuration import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity import org.thoughtcrime.securesms.preferences.widgets.StorageGraphView -import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.BottomSheetUtil import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.viewModel @@ -158,7 +157,7 @@ class ManageStorageSettingsFragment : ComposeFragment() { dialog("confirm-delete-chat-history") { Dialogs.SimpleAlertDialog( title = stringResource(id = R.string.preferences_storage__delete_message_history), - body = if (SignalStore.account.hasLinkedDevices && Recipient.self().deleteSyncCapability.isSupported) { + body = if (SignalStore.account.hasLinkedDevices) { stringResource(id = R.string.preferences_storage__this_will_delete_all_message_history_and_media_from_your_device_linked_device) } else { stringResource(id = R.string.preferences_storage__this_will_delete_all_message_history_and_media_from_your_device) @@ -174,7 +173,7 @@ class ManageStorageSettingsFragment : ComposeFragment() { dialog("double-confirm-delete-chat-history", dialogProperties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true)) { Dialogs.SimpleAlertDialog( title = stringResource(id = R.string.preferences_storage__are_you_sure_you_want_to_delete_all_message_history), - body = if (SignalStore.account.hasLinkedDevices && Recipient.self().deleteSyncCapability.isSupported) { + body = if (SignalStore.account.hasLinkedDevices) { stringResource(id = R.string.preferences_storage__all_message_history_will_be_permanently_removed_this_action_cannot_be_undone_linked_device) } else { stringResource(id = R.string.preferences_storage__all_message_history_will_be_permanently_removed_this_action_cannot_be_undone) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt index c5a5778373..b5b38b1650 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt @@ -341,8 +341,6 @@ class InternalConversationSettingsFragment : DSLSettingsFragment( return if (capabilities != null) { TextUtils.concat( - colorize("DeleteSync", capabilities.deleteSync), - ", ", colorize("SSREv2", capabilities.storageServiceEncryptionV2) ) } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index 6cddac7627..f31e2d5ffc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -1241,7 +1241,7 @@ private void handleDelete(@NonNull Collection ids) { alert.setTitle(context.getResources().getQuantityString(R.plurals.ConversationListFragment_delete_selected_conversations, conversationsCount, conversationsCount)); - if (SignalStore.account().hasLinkedDevices() && Recipient.self().getDeleteSyncCapability().isSupported()) { + if (SignalStore.account().hasLinkedDevices()) { alert.setMessage(context.getResources().getQuantityString(R.plurals.ConversationListFragment_this_will_permanently_delete_all_n_selected_conversations_linked_device, conversationsCount, conversationsCount)); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt index 8be110b94f..2a136a3501 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt @@ -418,7 +418,6 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da @JvmStatic fun maskCapabilitiesToLong(capabilities: SignalServiceProfile.Capabilities): Long { var value: Long = 0 - value = Bitmask.update(value, Capabilities.DELETE_SYNC, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isDeleteSync).serialize().toLong()) value = Bitmask.update(value, Capabilities.STORAGE_SERVICE_ENCRYPTION_V2, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isStorageServiceEncryptionV2).serialize().toLong()) return value } @@ -4711,8 +4710,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da // const val GIFT_BADGES = 6 // const val PNP = 7 // const val PAYMENT_ACTIVATION = 8 - const val DELETE_SYNC = 9 - +// const val DELETE_SYNC = 9 // const val VERSIONED_EXPIRATION_TIMER = 10 const val STORAGE_SERVICE_ENCRYPTION_V2 = 11 diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTableCursorUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTableCursorUtil.kt index 2354515b16..b47b96c142 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTableCursorUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTableCursorUtil.kt @@ -176,7 +176,6 @@ object RecipientTableCursorUtil { val capabilities = cursor.requireLong(RecipientTable.CAPABILITIES) return RecipientRecord.Capabilities( rawBits = capabilities, - deleteSync = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.DELETE_SYNC, Capabilities.BIT_LENGTH).toInt()), storageServiceEncryptionV2 = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.STORAGE_SERVICE_ENCRYPTION_V2, Capabilities.BIT_LENGTH).toInt()) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt index e9cce339af..ba7d1b1c10 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt @@ -329,7 +329,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa return } - val syncThreadTrimDeletes = SignalStore.settings.shouldSyncThreadTrimDeletes() && Recipient.self().deleteSyncCapability.isSupported + val syncThreadTrimDeletes = SignalStore.settings.shouldSyncThreadTrimDeletes() val threadTrimsToSync = mutableListOf() readableDatabase @@ -1267,7 +1267,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa val queries: List = SqlUtil.buildCollectionQuery(ID, selectedConversations) writableDatabase.withinTransaction { db -> - if (syncThreadDeletes && Recipient.self().deleteSyncCapability.isSupported) { + if (syncThreadDeletes) { for (threadId in selectedConversations) { val mostRecentMessages = messages.getMostRecentAddressableMessages(threadId, excludeExpiring = false) val mostRecentNonExpiring = if (mostRecentMessages.size == MessageTable.ADDRESSABLE_MESSAGE_LIMIT && mostRecentMessages.any { it.expiresIn > 0 }) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientRecord.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientRecord.kt index 9a05d47c29..82360f4579 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientRecord.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientRecord.kt @@ -120,14 +120,12 @@ data class RecipientRecord( data class Capabilities( val rawBits: Long, - val deleteSync: Recipient.Capability, val storageServiceEncryptionV2: Recipient.Capability ) { companion object { @JvmField val UNKNOWN = Capabilities( rawBits = 0, - deleteSync = Recipient.Capability.UNKNOWN, storageServiceEncryptionV2 = Recipient.Capability.UNKNOWN ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceDeleteSyncJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceDeleteSyncJob.kt index 275051bef1..e17efbeeea 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceDeleteSyncJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceDeleteSyncJob.kt @@ -61,11 +61,6 @@ class MultiDeviceDeleteSyncJob private constructor( return } - if (!Recipient.self().deleteSyncCapability.isSupported) { - Log.i(TAG, "Delete sync support not enabled.") - return - } - messageRecords.chunked(CHUNK_SIZE).forEach { chunk -> val deletes = createMessageDeletes(chunk) if (deletes.isNotEmpty()) { @@ -83,11 +78,6 @@ class MultiDeviceDeleteSyncJob private constructor( return } - if (!Recipient.self().deleteSyncCapability.isSupported) { - Log.i(TAG, "Delete sync support not enabled.") - return - } - val delete = createAttachmentDelete(message, attachment) if (delete != null) { AppDependencies.jobManager.add(MultiDeviceDeleteSyncJob(attachments = listOf(delete))) @@ -102,11 +92,6 @@ class MultiDeviceDeleteSyncJob private constructor( return } - if (!Recipient.self().deleteSyncCapability.isSupported) { - Log.i(TAG, "Delete sync support not enabled.") - return - } - threads.chunked(THREAD_CHUNK_SIZE).forEach { chunk -> val threadDeletes = createThreadDeletes(chunk, isFullDelete) if (threadDeletes.isNotEmpty()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java index 31e2bd65c9..c3ba0a0f6c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java @@ -220,11 +220,6 @@ private void setProfileCapabilities(@Nullable SignalServiceProfile.Capabilities SignalDatabase.recipients().setCapabilities(Recipient.self().getId(), capabilities); - if (!selfSnapshot.getDeleteSyncCapability().isSupported() && capabilities.isDeleteSync()) { - Log.d(TAG, "Transitioned to delete sync capable, notify linked devices in case we were the last one"); - AppDependencies.getJobManager().add(new MultiDeviceProfileContentUpdateJob()); - } - if (selfSnapshot.getStorageServiceEncryptionV2Capability() == Recipient.Capability.NOT_SUPPORTED && capabilities.isStorageServiceEncryptionV2()) { Log.i(TAG, "Transitioned to storageServiceEncryptionV2 capable. Notifying other devices and pushing to storage service with a recordIkm."); AppDependencies.getJobManager().add(new MultiDeviceProfileContentUpdateJob()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/TrimThreadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/TrimThreadJob.java index 1c9aeef5ae..813e6ae1f0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/TrimThreadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/TrimThreadJob.java @@ -78,7 +78,7 @@ public void onRun() { long trimBeforeDate = keepMessagesDuration != KeepMessagesDuration.FOREVER ? System.currentTimeMillis() - keepMessagesDuration.getDuration() : ThreadTable.NO_TRIM_BEFORE_DATE_SET; - SignalDatabase.threads().trimThread(threadId, SignalStore.settings().shouldSyncThreadTrimDeletes() && Recipient.self().getDeleteSyncCapability().isSupported(), trimLength, trimBeforeDate, false); + SignalDatabase.threads().trimThread(threadId, SignalStore.settings().shouldSyncThreadTrimDeletes(), trimLength, trimBeforeDate, false); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionCapabilities.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionCapabilities.java index 5246b1c804..a9bc32d437 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionCapabilities.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionCapabilities.java @@ -42,7 +42,6 @@ public final class LogSectionCapabilities implements LogSection { .append("-- Global").append("\n"); if (globalCapabilities != null) { - builder.append("DeleteSync: ").append(globalCapabilities.getDeleteSync()).append("\n"); builder.append("StorageServiceEncryptionV2: ").append(globalCapabilities.getStorageServiceEncryptionV2()).append("\n"); builder.append("\n"); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java index 9500fa43fc..239d858917 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java @@ -86,7 +86,7 @@ protected Void doInBackground(MediaTable.MediaRecord... records) { } } - if (Recipient.self().getDeleteSyncCapability().isSupported() && Util.hasItems(deletedMessageRecords)) { + if (Util.hasItems(deletedMessageRecords)) { MultiDeviceDeleteSyncJob.enqueueMessageDeletes(deletedMessageRecords); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt index 6ab08c0b8d..568449c5a2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt @@ -23,7 +23,6 @@ import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.jobs.MultiDeviceDeleteSyncJob import org.thoughtcrime.securesms.longmessage.resolveBody -import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.sms.MessageSender import org.thoughtcrime.securesms.util.AttachmentUtil @@ -98,7 +97,7 @@ class MediaPreviewRepository { fun localDelete(attachment: DatabaseAttachment): Completable { return Completable.fromRunnable { val deletedMessageRecord = AttachmentUtil.deleteAttachment(attachment) - if (deletedMessageRecord != null && Recipient.self().deleteSyncCapability.isSupported) { + if (deletedMessageRecord != null) { MultiDeviceDeleteSyncJob.enqueueMessageDeletes(setOf(deletedMessageRecord)) } }.subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt index 01e3ab0c25..113108e430 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.kt @@ -315,9 +315,6 @@ class Recipient( /** The notification channel, if both set and supported by the system. Otherwise null. */ val notificationChannel: String? = if (!NotificationChannels.supported()) null else notificationChannelValue - /** The user's capability to handle synchronizing deletes across linked devices. */ - val deleteSyncCapability: Capability = capabilities.deleteSync - /** The user's capability to handle the new storage record encryption scheme. */ val storageServiceEncryptionV2Capability: Capability get() = if (SignalStore.internal.forceSsre2Capability) Capability.SUPPORTED else capabilities.storageServiceEncryptionV2 diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/AttachmentUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/AttachmentUtil.java index bd7f86529f..18442b61a2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/AttachmentUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/AttachmentUtil.java @@ -104,9 +104,7 @@ public static boolean isAutoDownloadPermitted(@NonNull Context context, @Nullabl SignalDatabase.messages().deleteMessage(mmsId); } else { SignalDatabase.attachments().deleteAttachment(attachmentId); - if (Recipient.self().getDeleteSyncCapability().isSupported()) { - MultiDeviceDeleteSyncJob.enqueueAttachmentDelete(SignalDatabase.messages().getMessageRecordOrNull(mmsId), attachment); - } + MultiDeviceDeleteSyncJob.enqueueAttachmentDelete(SignalDatabase.messages().getMessageRecordOrNull(mmsId), attachment); } return deletedMessageRecord; diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/DeleteDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/util/DeleteDialog.kt index 79f1d0e526..a3b8a2cfd5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/DeleteDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/DeleteDialog.kt @@ -10,7 +10,6 @@ import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.jobs.MultiDeviceDeleteSyncJob import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.sms.MessageSender import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask @@ -46,10 +45,8 @@ object DeleteDialog { if (forceRemoteDelete) { builder.setPositiveButton(R.string.ConversationFragment_delete_for_everyone) { _, _ -> deleteForEveryone(messageRecords, emitter) } } else { - val deleteSyncEnabled = Recipient.self().deleteSyncCapability.isSupported - val positiveButton = if (isNoteToSelfDelete) { - if (deleteSyncEnabled) R.string.ConversationFragment_delete else R.string.ConversationFragment_delete_on_this_device + R.string.ConversationFragment_delete } else { R.string.ConversationFragment_delete_for_me } @@ -60,9 +57,7 @@ object DeleteDialog { }.executeOnExecutor(SignalExecutors.BOUNDED) } - val canDeleteForEveryoneInNoteToSelf = isNoteToSelfDelete && SignalStore.account.hasLinkedDevices && !deleteSyncEnabled - - if (MessageConstraintsUtil.isValidRemoteDeleteSend(messageRecords, System.currentTimeMillis()) && (!isNoteToSelfDelete || canDeleteForEveryoneInNoteToSelf)) { + if (MessageConstraintsUtil.isValidRemoteDeleteSend(messageRecords, System.currentTimeMillis())) { builder.setNeutralButton(if (isNoteToSelfDelete) R.string.ConversationFragment_delete_everywhere else R.string.ConversationFragment_delete_for_everyone) { _, _ -> handleDeleteForEveryone(context, messageRecords, emitter) } } } @@ -120,9 +115,7 @@ object DeleteDialog { } } - if (Recipient.self().deleteSyncCapability.isSupported) { - MultiDeviceDeleteSyncJob.enqueueMessageDeletes(messageRecords) - } + MultiDeviceDeleteSyncJob.enqueueMessageDeletes(messageRecords) return threadDeleted } diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/RecipientDatabaseTestUtils.kt b/app/src/test/java/org/thoughtcrime/securesms/database/RecipientDatabaseTestUtils.kt index a26ae64cf6..6cd528a12b 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/database/RecipientDatabaseTestUtils.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/database/RecipientDatabaseTestUtils.kt @@ -126,7 +126,6 @@ object RecipientDatabaseTestUtils { sealedSenderAccessMode = sealedSenderAccessMode, capabilities = RecipientRecord.Capabilities( rawBits = capabilities, - deleteSync = Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientTable.Capabilities.DELETE_SYNC, RecipientTable.Capabilities.BIT_LENGTH).toInt()), storageServiceEncryptionV2 = Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientTable.Capabilities.STORAGE_SERVICE_ENCRYPTION_V2, RecipientTable.Capabilities.BIT_LENGTH).toInt()) ), storageId = storageId, diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java index a05879c4b8..1e648824e0 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java @@ -192,18 +192,14 @@ public static class Capabilities { @JsonProperty private boolean storage; - @JsonProperty - private boolean deleteSync; - @JsonProperty("ssre2") private boolean storageServiceEncryptionV2; @JsonCreator public Capabilities() {} - public Capabilities(boolean storage, boolean deleteSync, boolean storageServiceEncryptionV2) { + public Capabilities(boolean storage, boolean storageServiceEncryptionV2) { this.storage = storage; - this.deleteSync = deleteSync; this.storageServiceEncryptionV2 = storageServiceEncryptionV2; } @@ -211,10 +207,6 @@ public boolean isStorage() { return storage; } - public boolean isDeleteSync() { - return deleteSync; - } - public boolean isStorageServiceEncryptionV2() { return storageServiceEncryptionV2; } From d6f8f8acb3c1cf33577b8291db7fd61bc40ccfcd Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 3 Dec 2024 12:17:06 -0500 Subject: [PATCH 46/56] Fix calls silent ringer bug. --- .../service/webrtc/IncomingCallActionProcessor.java | 6 ++++++ .../securesms/webrtc/audio/AudioManagerCommand.kt | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java index 4491faf7e4..510a732cad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java @@ -2,6 +2,7 @@ import android.net.Uri; import android.os.ResultReceiver; +import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -204,6 +205,11 @@ public IncomingCallActionProcessor(@NonNull WebRtcInteractor webRtcInteractor) { ringtone = SignalStore.settings().getCallRingtone(); } + if (TextUtils.isEmpty(ringtone.toString())) { + Log.i(TAG, "Ringtone is likely set to silent"); + ringtone = null; + } + webRtcInteractor.startIncomingRinger(ringtone, vibrateState == RecipientTable.VibrateState.ENABLED || (vibrateState == RecipientTable.VibrateState.DEFAULT && SignalStore.settings().isCallVibrateEnabled())); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt index 52306bad2e..8bc95e18ec 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt @@ -27,7 +27,7 @@ sealed class AudioManagerCommand : Parcelable { } } - class StartIncomingRinger(val ringtoneUri: Uri, val vibrate: Boolean) : AudioManagerCommand() { + class StartIncomingRinger(val ringtoneUri: Uri?, val vibrate: Boolean) : AudioManagerCommand() { override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeParcelable(ringtoneUri, flags) ParcelUtil.writeBoolean(parcel, vibrate) @@ -37,7 +37,7 @@ sealed class AudioManagerCommand : Parcelable { @JvmField val CREATOR: Parcelable.Creator = ParcelCheat { parcel -> StartIncomingRinger( - ringtoneUri = parcel.readParcelableCompat(Uri::class.java)!!, + ringtoneUri = parcel.readParcelableCompat(Uri::class.java), vibrate = ParcelUtil.readBoolean(parcel) ) } From c12d577e8b20b6e0a97084e688c70097aba5497c Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 3 Dec 2024 12:25:23 -0500 Subject: [PATCH 47/56] Fix GSE crash when attempting to send to non-GV2 groups. --- .../main/java/org/thoughtcrime/securesms/database/GroupTable.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt index 0f38d0e027..1a4b0388c3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt @@ -988,7 +988,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT fun getGroupSendFullToken(threadId: Long, recipientId: RecipientId): GroupSendFullToken? { val threadRecipient = SignalDatabase.threads.getRecipientForThreadId(threadId) - if (threadRecipient == null || !threadRecipient.isGroup) { + if (threadRecipient == null || !threadRecipient.isPushV2Group) { return null } From 548da6a09d5b30021368740afa2df420ae5cbb31 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 3 Dec 2024 09:59:08 -0500 Subject: [PATCH 48/56] Update to the latest backup tests. --- ...em_standard_message_with_quote_00.binproto | Bin 581 -> 618 bytes ...em_standard_message_with_quote_01.binproto | Bin 635 -> 835 bytes ...em_standard_message_with_quote_02.binproto | Bin 1151 -> 1355 bytes ...em_standard_message_with_quote_03.binproto | Bin 660 -> 850 bytes ...em_standard_message_with_quote_04.binproto | Bin 555 -> 592 bytes ...em_standard_message_with_quote_05.binproto | Bin 761 -> 931 bytes ...em_standard_message_with_quote_06.binproto | Bin 748 -> 895 bytes ...em_standard_message_with_quote_07.binproto | Bin 708 -> 815 bytes ...em_standard_message_with_quote_08.binproto | Bin 768 -> 918 bytes ...em_standard_message_with_quote_09.binproto | Bin 712 -> 912 bytes ...em_standard_message_with_quote_10.binproto | Bin 674 -> 881 bytes ...em_standard_message_with_quote_11.binproto | Bin 746 -> 934 bytes ...em_standard_message_with_quote_12.binproto | Bin 655 -> 818 bytes ...em_standard_message_with_quote_13.binproto | Bin 860 -> 897 bytes ...em_standard_message_with_quote_14.binproto | Bin 622 -> 643 bytes .../v2/exporters/ChatItemArchiveExporter.kt | 27 +++++++--- .../v2/importer/ChatItemArchiveImporter.kt | 51 ++++++++++-------- .../v2/util/ArchiveConverterExtensions.kt | 7 +-- .../securesms/database/AttachmentTable.kt | 20 +++---- 19 files changed, 60 insertions(+), 45 deletions(-) diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_00.binproto index 35dd4996f9f7b5e1162eca58832aecc1f5baaa8f..c7f2e71bc0fa35f69a3048c44fa2bc5ac456e141 100644 GIT binary patch delta 61 zcmV-D0K)&p1nLB^M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( TdE*=2Sl-CG7G4%klc)imqS_cr delta 23 fcmaFGa+GC*J0p9NPyz>wP|V~2#;D0>8Rr53R&@tK diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_01.binproto index bc98f8c817ada644a047e415f3f45244110bef19..fd7937a4914c31e373a497d53312c15126f16fa2 100644 GIT binary patch delta 301 zcmey(a+qy{J0oWsqfj#=2a8bsWIx6zXnbB z??v1@u}dsY%y04v#*X?oj7l#VIhX`kBvw3`x9y#o6CcODGye}CH56F%r{&^olc>{- zTx%IQ7zJ3Q*tl2{i&N5+<}h+`W#%TPr|M@Fq^3*tGIDh>Dj|tDw2FG1;*65wP|V~2#;D0>89SK0lsqO!G0Es?bMbL73b07Aaj_&8r=)2x f3ISPpnZ-Fqf?O;g=1*!6V3hd$`RTu91`3P-^YRrp diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_02.binproto index 564dfcb0b3a01354d10e06b48513e569a8e95aed..bfe56d3d79fbb3ce7934bffd0456d350e7355aa1 100644 GIT binary patch delta 277 zcmey*ahhv`J0oWsqfj#=2a8bsWIx6zXnbB z??v1@u}dsY%y07wMoXr^!%RvCm^hdO7$sIdnYZnonHL)u3kQo7L)0oJt}Toli~=lD zK#|1alr*Kqj9grqxryni`dJ03=~7b|x%wHEkVG8buz9Q#n|zu{J|UJ%!N@e#EGf}4 z)i}vG)gsL>&B($$&Co2#B+=N?%*ZS)O@LWSK)FlgRrImm*Z4K{Of#zyccfohL{(stdqjc11(O8hWG7E1cPi{0-U^Eis RV)-zCVy^&;L@z{$2>|SBU5Nky delta 98 zcmX@j^`B#dJ0p9NPyz>wP|V~2#;DC_87-MK=P)YGWaMBHV3b(-WZt%SW?pPuEF3IS y3{lODTznji0xVKMfyCmJw8__4qNG26e)?~jfdZqEAQ#Jr`4f8uSR}qcgqQ$jMIO%p diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_03.binproto index a47b22f21ecf628bffd00cb756ebb7abd3a89cc3..951fb99bfbd63b67048f1a7b0f15a7eb0614217d 100644 GIT binary patch delta 235 zcmbQjdWmg=J0oWsqfj#=2a8bsWIx6zXnbB z??v1@u}dsY%y04v#s=1ACZz_Z$=Xb^^$!`jW;1dy3a}_mV&vk=%uP&B)z2zOO_yqB zWWvh6ygF?H(6pRAMpnOp(Cp-yiA delta 67 zcmV-J0KEUw29yP`M*#<75?}}f5?qr|0a}yI0e~lKB54Q$5CIsfUD*yPi^6mI`##hX|)Q{Y*nu( cdE*=2Sl-CG7G4%klc)iE2#mn*$+0yc0fjys2LJ#7 delta 32 ocmcb>vYKUsJ0p9NPyz>wP|V~2#;D0>8LK%ye}4LJnSlZ$0IXmOvj6}9 diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_05.binproto index 150d2bceb20929d1564ee31d8a9efe254b010fba..931adcfeec7827fc863357741e8568e008d38a98 100644 GIT binary patch delta 262 zcmey#x|n@~J0oWsqfj#=2a8bsWIx6zXnbB z??v1@u}dsY%y04v#$ENZn3Se7aWDxmO00P@Z`(UFFAgp?4i+sY4#ueej9i_J9E<`i zN)3!$T$#Cv>8biz1*z#$MO+0+NPLG@QIAucQ8GTBzE-tCP8RmMaXxw}Cb9l{dD-6a zMWvqh`6)tnTwGZ$1pyVgJ{}={0!$M3w$1xD$5cr}py~x{h=^)4>-RG0tL(xm;aUQ^ z{b$RCPHZYTd2z4&(U^}31~Z=YI81I~ijwX*@c!gRQw2sNK`xdL^C$KSut@YmgqQ%E CY+OMA delta 90 zcmZ3?{*!fsJ0p9NPyz>wP|V~2#;D0>8Fwj7WmKBP$iXDQD6!_rylwByyg0bnI9Rlp qI2faXnbB z??v1@u}dsY%y04v##{B*7?mzFaxe)nO00b{Z`(UFFJ3Ng4iFASJyk!eAT?bonk!rhiSO`+&10RIkTVybhm)hDw}(?mqGy3$wzmMY z#I!B<8@r5@1Qs^TdZ_lwP|V~2#;D0>8E-LXDy2_e!z9h_&BeySD8Mq=irEzaB?Akh diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_07.binproto index 273da92115e2658ab60810a6b9d6ecee8ef5fca6..77a0d2527f6f15c32f02b990878235075ff80f75 100644 GIT binary patch delta 201 zcmX@Yx}I%=J0oWsqfj#=2a8bsWIx6zXnbB z??v1@u}dsY%y04v#vAn=j7qJH983a?66>DK+xE`Pi=T^^gGFHd+*36{Mz1nQ$2?A@LpFN_d=RkCO38^T{v^%TKiQ^9;8y_jA+D v&-c-_4tI>O$gh-Q&|nl|<6_CnEY2|!i&hUVtRNh!vbMu`?iCP}GD#>oNCL{wVJ$N+h|Okdh#c$1fvF{kT;iS$>d2)!O}ek-k;oP Ws=#O@$i?zu{={AZ7KvVn5EB5|XHC%n delta 88 zcmbQn-oUoOosm6BD1n1TC}wg1W7OocjE5B_Gb&AB27t4qF6MF?%B)&j|m;hfb8}0xA diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_09.binproto index ec5c815ff9c410836258fdbbe63a73746bfab1a7..423ad482ef4c461e80b37b71e12c6ed627afef23 100644 GIT binary patch delta 245 zcmX@XI)QzIJ0oWsqfj#=2a8bsWIx6zXnbB z??v1@u}dsY%y04v#x1P9OiEo$ljE6W>t8c+EoJ0j6kt)B!^p*znVXoNs-IPmnl9DL z$koNDge2n7D(Z2HGfKuM%_qYwEI-lG&okV<+|NxnKi@~!I@~eBBEOO=f=j{J$SBz$ z+0fWD$s*0zGRe%;(9GB{Db*r1F)78!FvVPeNlL)qb(zTG$*VZKHP2ss81?ov$nuiWRX$-M`B@eWlbe}b0Y0Hp=Kufz delta 67 zcmV-J0KET@2gn7mM*#<75?}}f5?qr|0a}yI0kkJ;B54Q$5CIslXnbB z??v1@u}dsY%y04v#vS#q7?qwgaxe)nN^E>GZ`(UFFJ3Ng4iAm0E^OMMlP<*+{E-${j7r2bg3zfT>XqnNFokz*gV#WMalTMyKANC=w|BW=;qtI ztJK*?m6#WJ_!-)kxEgcCaw!;@rkW)sTBaH&8K+w0rWvLgS(v98nkAVe8e5tfnWd!( zFiQz2cZs}8-#AU{`0U9FFB{E@lmt|t?V4w8*DtS8FScdzG`0U4zowpPW;NoD^h=AV s3QTg3DDbynl3>(e6k_9I$;&LxF%slr`7nP{1JG+d2i~9DXsW;n0K0Z%SpWb4 delta 68 zcmV-K0K5P32BHP9M*#<75?}}f5?qr|0a}yI0k{J}B0rOg0vjYF3I+%P5CkXz5(Wwc aZfSFDG7$;{@Sl=^5CItT^X2}gFdzYKOcQnh diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_11.binproto index 1c468c85ae7d30eadb1d52841824165f5af7e3a9..858a8d8686246a6561fe6ce2a2c7b287b0ae229c 100644 GIT binary patch delta 278 zcmaFGx{Q5;J0oWsqfj#=2a8bsWIx6zXnbB z??v1@u}dsY%y04v#-Nb<|d}6>SqE$E9GNYHDn1Y-pL3YG`3(Y+#;ZWRPN!Y?NqhXk?sXk(g{Gz#t{CPUy$B zyIMs;8zU|rnkw<=mXbhSaIoY)31#+6yoVMG*>)Y$m^$_3Pu9t$Oi|K32i~9DXsWwP|V~2#;D0>8P_UIW>lKM$iXDQD6#3uylwByyx6!{I9Qw* qqN=&rI2Z+3CL1zGNq_$Q^xrZA1x6!5E|w4TC-w@kNPK|^F#!OFdK`2B diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_12.binproto index 6882ec7e4ffc985aa9edfa95cc10098d91ba2923..7534d4dcd4ff3da580a575d5b6b811ebc27ca7ee 100644 GIT binary patch delta 230 zcmeBY-Nd%RosqMRQK*@bgGH!*vL9oVa*-nItJ2oh{<#k)T76@kpShLmQcmc4^~%T6 z_ag3{*d-Px<~Ml-V{QF2My1D$983a?5}TjQ+xE`PiyfTOi$I%Do9P2%Hm2>LgGis_;>~Bn3<*O)j3y`s`}*n_|^m)sTzle zd*&DC3R!XSc{n*bdV4s9BzhM3WqT_Lte0FCz|gC5gHxGduW-7{hlN@KqJk`j4M9pp Z0c%Rc{`v%T8l1VlvyWYZak3YaD*(dDO*{Yq delta 43 zcmdnQ*3Y`Zosm6BD1n1TC}wg1W7OocjJ3>}O6imRnWWjhx!5=u1z0A(WpV`o{v!(T diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_13.binproto index b22ce21ea08c3ffad15838f268b01f820102c736..27f36cc18f79fe55ffbbea9e81c165bf7f65f095 100644 GIT binary patch delta 71 zcmV-N0J#6$27w2#M*#_j0TP1&2m}&;lTHCzBXS`H>UD*yPi^6mI`##hX|)Q{Y*nu( ddE*=2Sl-CG7G4%kv#0^A0SJu1@5!+>AOV`Y96$g7 delta 33 pcmZoeaEuFosm6BD1n1TC}wg1W0aN^1BVnF2a^Cx0Hc(W1c)KQ=)}gsD8MMe7{wvP z#>J9YoRT(=QEB$%4~*hM&5T@pKuH!UsDuXNWC5lK>Cc~^{#$0Cz-T1M#qweP#9p8& IUm!wE086$Ur~m)} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt index 645a6c86da..500d55a381 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt @@ -80,6 +80,7 @@ import org.thoughtcrime.securesms.payments.FailureReason import org.thoughtcrime.securesms.payments.State import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.util.JsonUtils +import org.thoughtcrime.securesms.util.MediaUtil import org.whispersystems.signalservice.api.push.ServiceId.ACI import org.whispersystems.signalservice.api.util.UuidUtil import org.whispersystems.signalservice.api.util.toByteArray @@ -787,7 +788,18 @@ private fun BackupMessageRecord.toRemoteQuote(mediaArchiveEnabled: Boolean, atta return null } - val type = QuoteModel.Type.fromCode(this.quoteType) + val localType = QuoteModel.Type.fromCode(this.quoteType) + val remoteType = when (localType) { + QuoteModel.Type.NORMAL -> { + if (attachments?.any { it.contentType == MediaUtil.VIEW_ONCE } == true) { + Quote.Type.VIEW_ONCE + } else { + Quote.Type.NORMAL + } + } + QuoteModel.Type.GIFT_BADGE -> Quote.Type.GIFT_BADGE + } + return Quote( targetSentTimestamp = this.quoteTargetSentTimestamp.takeIf { !this.quoteMissing && it != MessageTable.QUOTE_TARGET_MISSING_ID }, authorId = this.quoteAuthor, @@ -797,11 +809,12 @@ private fun BackupMessageRecord.toRemoteQuote(mediaArchiveEnabled: Boolean, atta bodyRanges = this.quoteBodyRanges?.toRemoteBodyRanges() ?: emptyList() ) }, - attachments = attachments?.toRemoteQuoteAttachments(mediaArchiveEnabled) ?: emptyList(), - type = when (type) { - QuoteModel.Type.NORMAL -> Quote.Type.NORMAL - QuoteModel.Type.GIFT_BADGE -> Quote.Type.GIFT_BADGE - } + attachments = if (remoteType == Quote.Type.VIEW_ONCE) { + emptyList() + } else { + attachments?.toRemoteQuoteAttachments(mediaArchiveEnabled) ?: emptyList() + }, + type = remoteType ) } @@ -847,7 +860,7 @@ private fun List.toRemoteQuoteAttachments(mediaArchiveEnable mediaArchiveEnabled = mediaArchiveEnabled, flagOverride = MessageAttachment.Flag.NONE, contentTypeOverride = "image/jpeg" - ).takeUnless { it.pointer?.invalidAttachmentLocator != null } + ) ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt index 8b4c1e8b87..3c430baa45 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt @@ -18,6 +18,7 @@ import org.signal.core.util.toInt import org.signal.core.util.update import org.thoughtcrime.securesms.attachments.Attachment import org.thoughtcrime.securesms.attachments.PointerAttachment +import org.thoughtcrime.securesms.attachments.TombstoneAttachment import org.thoughtcrime.securesms.backup.v2.ImportState import org.thoughtcrime.securesms.backup.v2.proto.BodyRange import org.thoughtcrime.securesms.backup.v2.proto.ChatItem @@ -70,6 +71,7 @@ import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.stickers.StickerLocator import org.thoughtcrime.securesms.util.JsonUtils +import org.thoughtcrime.securesms.util.MediaUtil import org.whispersystems.signalservice.api.payments.Money import org.whispersystems.signalservice.api.push.ServiceId import org.whispersystems.signalservice.api.util.UuidUtil @@ -396,9 +398,7 @@ class ChatItemArchiveImporter( ) }?.let { listOf(it) } ?: emptyList() - val quoteAttachments: List = this.standardMessage.quote?.attachments?.mapNotNull { - it.toLocalAttachment() - } ?: emptyList() + val quoteAttachments: List = this.standardMessage.quote?.toLocalAttachments() ?: emptyList() val hasAttachments = attachments.isNotEmpty() || linkPreviewAttachments.isNotEmpty() || quoteAttachments.isNotEmpty() || longTextAttachments.isNotEmpty() @@ -980,26 +980,30 @@ class ChatItemArchiveImporter( ?: false } - private fun Quote.QuotedAttachment.toLocalAttachment(): Attachment? { - // TODO [backup] quote status not passed through? - val thumbnail = this.thumbnail?.toLocalAttachment() - - if (thumbnail != null) { - return thumbnail + private fun Quote.toLocalAttachments(): List { + if (this.type == Quote.Type.VIEW_ONCE) { + return listOf(TombstoneAttachment(contentType = MediaUtil.VIEW_ONCE, quote = true)) } - if (this.contentType == null) { - return null - } + return attachments.mapNotNull { attachment -> + val thumbnail = attachment.thumbnail?.toLocalAttachment(quote = true) - // TODO [backup] Need to do the normal ArchiveAttachment thing -- not sure why the conversion to a pointer - return PointerAttachment.forPointer( - quotedAttachment = DataMessage.Quote.QuotedAttachment( - contentType = this.contentType, - fileName = this.fileName, - thumbnail = null - ) - ).orNull() + if (thumbnail != null) { + return@mapNotNull thumbnail + } + + if (attachment.contentType == null) { + return@mapNotNull null + } + + return@mapNotNull PointerAttachment.forPointer( + quotedAttachment = DataMessage.Quote.QuotedAttachment( + contentType = attachment.contentType, + fileName = attachment.fileName, + thumbnail = null + ) + ).orNull() + } } private fun Sticker?.toLocalAttachment(): Attachment? { @@ -1030,16 +1034,17 @@ class ChatItemArchiveImporter( ) } - private fun MessageAttachment.toLocalAttachment(): Attachment? { + private fun MessageAttachment.toLocalAttachment(quote: Boolean = false, contentType: String? = pointer?.contentType): Attachment? { return pointer?.toLocalAttachment( importState = importState, voiceNote = flag == MessageAttachment.Flag.VOICE_MESSAGE, gif = flag == MessageAttachment.Flag.GIF, borderless = flag == MessageAttachment.Flag.BORDERLESS, wasDownloaded = wasDownloaded, - contentType = pointer.contentType, + contentType = contentType, fileName = pointer.fileName, - uuid = clientUuid + uuid = clientUuid, + quote = quote ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/util/ArchiveConverterExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/util/ArchiveConverterExtensions.kt index 725113b815..62350038bf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/util/ArchiveConverterExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/util/ArchiveConverterExtensions.kt @@ -38,7 +38,8 @@ fun FilePointer?.toLocalAttachment( stickerLocator: StickerLocator? = null, contentType: String? = this?.contentType, fileName: String? = this?.fileName, - uuid: ByteString? = null + uuid: ByteString? = null, + quote: Boolean = false ): Attachment? { if (this == null) return null @@ -82,7 +83,7 @@ fun FilePointer?.toLocalAttachment( voiceNote = voiceNote, borderless = borderless, gif = gif, - quote = false, + quote = quote, stickerLocator = stickerLocator, uuid = UuidUtil.fromByteStringOrNull(uuid) ) @@ -108,7 +109,7 @@ fun FilePointer?.toLocalAttachment( voiceNote = voiceNote, borderless = borderless, gif = gif, - quote = false, + quote = quote, stickerLocator = stickerLocator, uuid = UuidUtil.fromByteStringOrNull(uuid), fileName = fileName diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt index 317d98a2e0..06cff833ce 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -1519,14 +1519,10 @@ class AttachmentTable( val insertedAttachments: MutableMap = mutableMapOf() for (attachment in attachments) { - val attachmentId = if (attachment.uri != null) { - insertAttachmentWithData(mmsId, attachment, attachment.quote) - } else { - if (attachment is ArchivedAttachment) { - insertArchivedAttachment(mmsId, attachment, attachment.quote) - } else { - insertUndownloadedAttachment(mmsId, attachment, attachment.quote) - } + val attachmentId = when { + attachment.uri != null -> insertAttachmentWithData(mmsId, attachment, attachment.quote) + attachment is ArchivedAttachment -> insertArchivedAttachment(mmsId, attachment, attachment.quote) + else -> insertUndownloadedAttachment(mmsId, attachment, attachment.quote) } insertedAttachments[attachment] = attachmentId @@ -1535,10 +1531,10 @@ class AttachmentTable( try { for (attachment in quoteAttachment) { - val attachmentId = if (attachment.uri != null) { - insertAttachmentWithData(mmsId, attachment, true) - } else { - insertUndownloadedAttachment(mmsId, attachment, true) + val attachmentId = when { + attachment.uri != null -> insertAttachmentWithData(mmsId, attachment, true) + attachment is ArchivedAttachment -> insertArchivedAttachment(mmsId, attachment, true) + else -> insertUndownloadedAttachment(mmsId, attachment, true) } insertedAttachments[attachment] = attachmentId From 0952afbba138aed40b85c45e675c3d6d6caac0c6 Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Tue, 3 Dec 2024 18:23:14 -0500 Subject: [PATCH 49/56] Add call link strings. --- .../components/webrtc/PendingParticipantsBottomSheet.kt | 4 ++-- app/src/main/res/values/strings.xml | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PendingParticipantsBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PendingParticipantsBottomSheet.kt index 02423eeb8f..127968cff0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PendingParticipantsBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PendingParticipantsBottomSheet.kt @@ -219,7 +219,7 @@ private fun PendingParticipantsSheet( onClick = onDenyAll ) { Text( - text = "Deny all", + text = stringResource(id = R.string.PendingParticipantsBottomSheet__deny_all), color = MaterialTheme.colorScheme.onSurface ) } @@ -228,7 +228,7 @@ private fun PendingParticipantsSheet( Buttons.LargeTonal(onClick = onApproveAll) { Text( - text = "Approve all" + text = stringResource(id = R.string.PendingParticipantsBottomSheet__approve_all) ) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fe4257b44c..9246096b79 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7329,6 +7329,10 @@ Reject Approve + + Approve all + + Deny all Turn on full screen notifications? From 1315724d52697d548f349cc134c4e7e3d7ab5b7d Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Wed, 4 Dec 2024 11:41:50 -0500 Subject: [PATCH 50/56] Add sms provider failed specific messaging during registration. --- .../ui/entercode/EnterCodeFragment.kt | 21 +++++++++++++++---- .../phonenumber/EnterPhoneNumberFragment.kt | 2 +- .../ui/entercode/EnterCodeFragment.kt | 21 +++++++++++++++---- .../phonenumber/EnterPhoneNumberFragment.kt | 2 +- app/src/main/res/values/strings.xml | 2 ++ 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/entercode/EnterCodeFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/entercode/EnterCodeFragment.kt index ef7d93ac7b..ea93e3df63 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/entercode/EnterCodeFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/entercode/EnterCodeFragment.kt @@ -166,6 +166,7 @@ class EnterCodeFragment : LoggingFragment(R.layout.fragment_registration_enter_c is VerificationCodeRequestResult.RateLimited -> presentRateLimitedDialog() is VerificationCodeRequestResult.AttemptsExhausted -> presentAccountLocked() is VerificationCodeRequestResult.RegistrationLocked -> presentRegistrationLocked(result.timeRemaining) + is VerificationCodeRequestResult.ExternalServiceFailure -> presentSmsGenericError(result) else -> presentGenericError(result) } } @@ -231,16 +232,28 @@ class EnterCodeFragment : LoggingFragment(R.layout.fragment_registration_enter_c }) } + private fun presentSmsGenericError(requestResult: RegistrationResult) { + binding.keyboard.displayFailure().addListener( + object : AssertedSuccessListener() { + override fun onSuccess(result: Boolean?) { + Log.w(TAG, "Encountered sms provider error!", requestResult.getCause()) + MaterialAlertDialogBuilder(requireContext()).apply { + setMessage(R.string.RegistrationActivity_sms_provider_error) + setPositiveButton(android.R.string.ok) { _, _ -> fragmentViewModel.showKeyboard() } + show() + } + } + } + ) + } + private fun presentGenericError(requestResult: RegistrationResult) { binding.keyboard.displayFailure().addListener( object : AssertedSuccessListener() { override fun onSuccess(result: Boolean?) { Log.w(TAG, "Encountered unexpected error!", requestResult.getCause()) MaterialAlertDialogBuilder(requireContext()).apply { - null?.let { - setTitle(it) - } - setMessage(getString(R.string.RegistrationActivity_error_connecting_to_service)) + setMessage(R.string.RegistrationActivity_error_connecting_to_service) setPositiveButton(android.R.string.ok) { _, _ -> fragmentViewModel.showKeyboard() } show() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/phonenumber/EnterPhoneNumberFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/phonenumber/EnterPhoneNumberFragment.kt index 76f890226e..925569c2ea 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/phonenumber/EnterPhoneNumberFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/phonenumber/EnterPhoneNumberFragment.kt @@ -347,7 +347,7 @@ class EnterPhoneNumberFragment : LoggingFragment(R.layout.fragment_registration_ is VerificationCodeRequestResult.Success -> throw IllegalStateException("Session error handler called on successful response!") is VerificationCodeRequestResult.AttemptsExhausted -> presentRateLimitedDialog() is VerificationCodeRequestResult.ChallengeRequired -> handleChallenges(result.challenges) - is VerificationCodeRequestResult.ExternalServiceFailure -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_unable_to_connect_to_service), skipToNextScreen) + is VerificationCodeRequestResult.ExternalServiceFailure -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_sms_provider_error)) is VerificationCodeRequestResult.ImpossibleNumber -> { MaterialAlertDialogBuilder(requireContext()).apply { setMessage(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid, fragmentViewModel.phoneNumber?.toE164())) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/entercode/EnterCodeFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/entercode/EnterCodeFragment.kt index c2d1075feb..bd49f08465 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/entercode/EnterCodeFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/entercode/EnterCodeFragment.kt @@ -166,6 +166,7 @@ class EnterCodeFragment : LoggingFragment(R.layout.fragment_registration_enter_c is VerificationCodeRequestResult.RateLimited -> presentRateLimitedDialog() is VerificationCodeRequestResult.AttemptsExhausted -> presentAccountLocked() is VerificationCodeRequestResult.RegistrationLocked -> presentRegistrationLocked(result.timeRemaining) + is VerificationCodeRequestResult.ExternalServiceFailure -> presentSmsGenericError(result) else -> presentGenericError(result) } } @@ -231,16 +232,28 @@ class EnterCodeFragment : LoggingFragment(R.layout.fragment_registration_enter_c }) } + private fun presentSmsGenericError(requestResult: RegistrationResult) { + binding.keyboard.displayFailure().addListener( + object : AssertedSuccessListener() { + override fun onSuccess(result: Boolean?) { + Log.w(TAG, "Encountered sms provider error!", requestResult.getCause()) + MaterialAlertDialogBuilder(requireContext()).apply { + setMessage(R.string.RegistrationActivity_sms_provider_error) + setPositiveButton(android.R.string.ok) { _, _ -> fragmentViewModel.showKeyboard() } + show() + } + } + } + ) + } + private fun presentGenericError(requestResult: RegistrationResult) { binding.keyboard.displayFailure().addListener( object : AssertedSuccessListener() { override fun onSuccess(result: Boolean?) { Log.w(TAG, "Encountered unexpected error!", requestResult.getCause()) MaterialAlertDialogBuilder(requireContext()).apply { - null?.let { - setTitle(it) - } - setMessage(getString(R.string.RegistrationActivity_error_connecting_to_service)) + setMessage(R.string.RegistrationActivity_error_connecting_to_service) setPositiveButton(android.R.string.ok) { _, _ -> fragmentViewModel.showKeyboard() } show() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/phonenumber/EnterPhoneNumberFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/phonenumber/EnterPhoneNumberFragment.kt index ff051e521e..35e5e5b022 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/phonenumber/EnterPhoneNumberFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/phonenumber/EnterPhoneNumberFragment.kt @@ -358,7 +358,7 @@ class EnterPhoneNumberFragment : LoggingFragment(R.layout.fragment_registration_ is VerificationCodeRequestResult.Success -> throw IllegalStateException("Session error handler called on successful response!") is VerificationCodeRequestResult.AttemptsExhausted -> presentRateLimitedDialog() is VerificationCodeRequestResult.ChallengeRequired -> handleChallenges(result.challenges) - is VerificationCodeRequestResult.ExternalServiceFailure -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_unable_to_connect_to_service), skipToNextScreen) + is VerificationCodeRequestResult.ExternalServiceFailure -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_sms_provider_error)) is VerificationCodeRequestResult.ImpossibleNumber -> { MaterialAlertDialogBuilder(requireContext()).apply { setMessage(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid, fragmentViewModel.phoneNumber?.toE164())) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9246096b79..b6fbc94538 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2484,6 +2484,8 @@ You\'ve made too many attempts to register this number. Please try again in %s. Unable to connect to service. Please check network connection and try again. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. We couldn\'t send you a verification code via SMS. Try receiving your code via voice call instead. From 699ddb989060c8fb6b378dd6d3e1a6df5d126296 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 4 Dec 2024 14:05:34 -0400 Subject: [PATCH 51/56] Better progress messaging. --- .../remote/RemoteBackupsSettingsFragment.kt | 27 ++++++++++++++----- app/src/main/res/values/strings.xml | 2 ++ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt index 873dffc941..0e5296bca0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt @@ -559,7 +559,7 @@ private fun LazyListScope.appendBackupDetailsItems( } } else { item { - InProgressBackupRow(progress = backupProgress.completedAttachments.toInt(), totalProgress = backupProgress.totalAttachments.toInt()) + InProgressBackupRow(archiveUploadProgressState = backupProgress) } } @@ -909,9 +909,11 @@ private fun SubscriptionMismatchMissingGooglePlayCard( @Composable private fun InProgressBackupRow( - progress: Int?, - totalProgress: Int? + archiveUploadProgressState: ArchiveUploadProgressState ) { + val progress = archiveUploadProgressState.completedAttachments + val totalProgress = archiveUploadProgressState.totalAttachments + Row( modifier = Modifier .padding(horizontal = dimensionResource(id = CoreUiR.dimen.gutter)) @@ -920,7 +922,7 @@ private fun InProgressBackupRow( Column( modifier = Modifier.weight(1f) ) { - if (totalProgress == null || totalProgress == 0) { + if (totalProgress == 0L) { LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) } else { LinearProgressIndicator( @@ -929,8 +931,8 @@ private fun InProgressBackupRow( ) } - val inProgressText = if (totalProgress == null || totalProgress == 0) { - stringResource(R.string.RemoteBackupsSettingsFragment__processing_backup) + val inProgressText = if (totalProgress == 0L) { + getProgressStateMessage(archiveUploadProgressState.state) } else { stringResource(R.string.RemoteBackupsSettingsFragment__d_slash_d, progress ?: 0, totalProgress) } @@ -944,6 +946,17 @@ private fun InProgressBackupRow( } } +@Composable +private fun getProgressStateMessage(state: ArchiveUploadProgressState.State): String { + val stringId = when (state) { + ArchiveUploadProgressState.State.None, ArchiveUploadProgressState.State.BackingUpMessages -> R.string.RemoteBackupsSettingsFragment__processing_backup + ArchiveUploadProgressState.State.UploadingMessages -> R.string.RemoteBackupsSettingsFragment__uploading_messages + ArchiveUploadProgressState.State.UploadingAttachments -> R.string.RemoteBackupsSettingsFragment__processing_backup + } + + return stringResource(stringId) +} + @Composable private fun LastBackupRow( lastBackupTimestamp: Long, @@ -1337,7 +1350,7 @@ private fun LastBackupRowPreview() { @Composable private fun InProgressRowPreview() { Previews.Preview { - InProgressBackupRow(50, 100) + InProgressBackupRow(archiveUploadProgressState = ArchiveUploadProgressState()) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b6fbc94538..9961736f12 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7778,6 +7778,8 @@ Couldn\'t turn off and delete backups A network error occurred. Please check your internet connection and try again. + + Uploading messages… From 9b13248da6a1965a059a2f1a8b0456e898666151 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 4 Dec 2024 14:27:29 -0500 Subject: [PATCH 52/56] Update translations and other static files. --- app/src/main/res/values-af/strings.xml | 62 +- app/src/main/res/values-ar/strings.xml | 448 +++++++----- app/src/main/res/values-az/strings.xml | 84 ++- app/src/main/res/values-bg/strings.xml | 84 ++- app/src/main/res/values-bn/strings.xml | 86 ++- app/src/main/res/values-bs/strings.xml | 84 ++- app/src/main/res/values-ca/strings.xml | 86 ++- app/src/main/res/values-cs/strings.xml | 88 ++- app/src/main/res/values-da/strings.xml | 84 ++- app/src/main/res/values-de/strings.xml | 96 ++- app/src/main/res/values-el/strings.xml | 86 ++- app/src/main/res/values-es/strings.xml | 206 ++++-- app/src/main/res/values-et/strings.xml | 86 ++- app/src/main/res/values-eu/strings.xml | 62 +- app/src/main/res/values-fa/strings.xml | 86 ++- app/src/main/res/values-fi/strings.xml | 84 ++- app/src/main/res/values-fr/strings.xml | 814 +++++++++++---------- app/src/main/res/values-ga/strings.xml | 86 ++- app/src/main/res/values-gl/strings.xml | 84 ++- app/src/main/res/values-gu/strings.xml | 84 ++- app/src/main/res/values-hi/strings.xml | 84 ++- app/src/main/res/values-hr/strings.xml | 86 ++- app/src/main/res/values-hu/strings.xml | 86 ++- app/src/main/res/values-in/strings.xml | 84 ++- app/src/main/res/values-it/strings.xml | 84 ++- app/src/main/res/values-iw/strings.xml | 86 ++- app/src/main/res/values-ja/strings.xml | 86 ++- app/src/main/res/values-ka/strings.xml | 84 ++- app/src/main/res/values-kk/strings.xml | 86 ++- app/src/main/res/values-km/strings.xml | 86 ++- app/src/main/res/values-kn/strings.xml | 84 ++- app/src/main/res/values-ko/strings.xml | 86 ++- app/src/main/res/values-ky/strings.xml | 86 ++- app/src/main/res/values-lt/strings.xml | 86 ++- app/src/main/res/values-lv/strings.xml | 84 ++- app/src/main/res/values-mk/strings.xml | 86 ++- app/src/main/res/values-ml/strings.xml | 86 ++- app/src/main/res/values-mr/strings.xml | 86 ++- app/src/main/res/values-ms/strings.xml | 86 ++- app/src/main/res/values-my/strings.xml | 84 ++- app/src/main/res/values-nb/strings.xml | 84 ++- app/src/main/res/values-nl/strings.xml | 160 ++-- app/src/main/res/values-pa/strings.xml | 86 ++- app/src/main/res/values-pl/strings.xml | 84 ++- app/src/main/res/values-pt-rBR/strings.xml | 94 ++- app/src/main/res/values-pt/strings.xml | 84 ++- app/src/main/res/values-ro/strings.xml | 84 ++- app/src/main/res/values-ru/strings.xml | 86 ++- app/src/main/res/values-sk/strings.xml | 86 ++- app/src/main/res/values-sl/strings.xml | 84 ++- app/src/main/res/values-sq/strings.xml | 86 ++- app/src/main/res/values-sr/strings.xml | 62 +- app/src/main/res/values-sv/strings.xml | 84 ++- app/src/main/res/values-sw/strings.xml | 84 ++- app/src/main/res/values-ta/strings.xml | 86 ++- app/src/main/res/values-te/strings.xml | 84 ++- app/src/main/res/values-th/strings.xml | 86 ++- app/src/main/res/values-tl/strings.xml | 86 ++- app/src/main/res/values-tr/strings.xml | 84 ++- app/src/main/res/values-ug/strings.xml | 62 +- app/src/main/res/values-uk/strings.xml | 408 ++++++----- app/src/main/res/values-ur/strings.xml | 86 ++- app/src/main/res/values-vi/strings.xml | 86 ++- app/src/main/res/values-yue/strings.xml | 120 ++- app/src/main/res/values-zh-rCN/strings.xml | 84 ++- app/src/main/res/values-zh-rHK/strings.xml | 84 ++- app/src/main/res/values-zh-rTW/strings.xml | 84 ++- app/static-ips.gradle.kts | 2 +- 68 files changed, 5313 insertions(+), 1963 deletions(-) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index a22f88e0e1..d6e4e8bbd5 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -1011,6 +1011,20 @@ Probeer dit weer koppel Gaan voort sonder om oor te dra + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1038,11 +1052,11 @@ Dra boodskapgeskiedenis oor - Dra jou teksboodskappe en onlangse media na jou rekenaar oor + Transfer your text messages and recent media to your linked device Moenie oordra nie - Geen ou boodskappe of media sal na jou rekenaar oorgedra word nie + No old messages or media will be transferred to your linked device Ontkoppel \"%1$s\"? @@ -1355,16 +1369,18 @@ Al jou boodskappe Herwin van rugsteun af - - Slegs media wat in die afgelope %1$d dae gestuur of ontvang is, is ingesluit. Jou rugsteun sluit in: Laai rugsteun terug - + Jou laaste rugsteun is op %1$s om %2$s gemaak. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Herwin tans rugsteunbesonderhede… + + Skip restore Stel my in kennis van Vermeldings @@ -2464,6 +2480,8 @@ Jy het te veel keer probeer om hierdie nommer te registreer. Probeer asseblief weer oor %1$s. Kan nie aan diens koppel nie. Kontroleer netwerkverbinding en probeer weer. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Ons kon nie vir jou \'n verifiëringskode per SMS stuur nie. Probeer om eerder jou kode per stemoproep te ontvang. @@ -4311,6 +4329,8 @@ Herwin of dra oor Dra rekening oor Slaan oor + + Skip restore Klets rugsteune Dra rekening oor Dra rekening oor na nuwe Android-toestel @@ -7307,6 +7327,10 @@ Verwerp Keur goed + + Approve all + + Deny all Skakel volskerm-kennisgewings aan? @@ -7482,6 +7506,12 @@ Slaan herstel oor? As jy herstel oorslaan, kan die oorblywende media en aanhegsels in jou rugsteun afgelaai word op \'n later tyd wanneer stoorspasie beskikbaar word. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Slaan aflaai oor - "Jou laaste rugsteun kon nie voltooi word nie. Maak seker dat jou foon aan wi-fi gekoppel is en tik Rugsteun nou om weer te probeer." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7718,6 +7752,8 @@ Hernu Vind meer uit + + Processing backup… Jy het %1$s rugsteundata wat nie op hierdie toestel is nie. Jou rugsteun sal geskrap word wanneer jou intekening oor %2$d dag eindig. @@ -7738,6 +7774,8 @@ Kon nie rugsteune afskakel en skrap nie \'n Netwerkfout het voorgekom. Gaan asseblief jou internetverbinding na en probeer weer. + + Uploading messages… @@ -7952,6 +7990,18 @@ Reg so + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 8255a24255..00594fbe79 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1091,6 +1091,20 @@ حاول الربط مجدَّدًا المواصلة دون النقل + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1116,13 +1130,13 @@ - Transfer message history + نقل سجلّ الرسائل - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - لا ترسلْ + لا تنقل - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device أترغبُ بفصل الربط بـ \"%1$s\"؟ @@ -1507,16 +1521,18 @@ جميع الرسائل الخاصة بك الاستعادة مِن نسخة احتياطية - - فقط الوسائط المُرسلة أو المُستلمة في الأيام %1$d الماضية هي المُضمّنة. تتضمن النسخة الاحتياطية: استعادة نسخة احتياطية - + أُجريت آخر نسخة احتياطية في %1$s على الساعة %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. جارٍ إعداد تفاصيل النسخة الاحتياطية… + + Skip restore نبّهني عندما يذكر أحدهم اسمي @@ -2800,6 +2816,8 @@ قمتَ بمحاولاتٍ كثيرة للتسجيل بهذا الرقم. يُرجى المحاولة لاحقًا %1$s. لا يمكن الاتصال بالخدمة. يُرجى التأكُّد من الاتصال بالشبكة والمحاولة مرّة أخرى. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. تعذَّر إرسال رمز التحقُّق عبر رسالة نصية. حاوِل الحصول على رمزك عبر مكالمة صوتية بدلًا من ذلك. @@ -4755,6 +4773,8 @@ الاستعادة أو النقل نقل الحساب تخطّي + + Skip restore النسخ الاحتياطية للدردشات نقل الحساب نقل الحساب إلى جهاز أندرويد جديد @@ -5440,9 +5460,9 @@ - جدّد اشتراكك في نسخ سيجنال الاحتياطية + جدِّد اشتراكك في النسخ الاحتياطية على سيجنال - تعذّر إكمال النسخ الاحتياطي + تعذَّر إكمال النسخ الاحتياطي ادْعُ أصدقائك @@ -5560,7 +5580,7 @@ لوحة المفاتيح - أرسل بالضغط على إدخال + أرسِل بالضغط على إدخال مجلدات الدردشة @@ -5589,7 +5609,7 @@ - نظّم دردشاتك في مجلدات وقُم بالتبديل بينها بسرعة في قائمة الدردشة. + نظّم دردشاتك في مجلدات وقُم بالتبديل بينها بسرعة في قائمة الدردشة لديك. المجلدات @@ -5597,9 +5617,9 @@ جميع الدردشات - المجلدات المقترَحة + المجلدات المُقترَحة - غير مقروءة + الرسائل غير المقروءة الرسائل غير المقروءة من جميع الدردشات @@ -5619,7 +5639,7 @@ كتم الجميع - إلغاء كتم صوت للجميع + إلغاء كتم الصوت للجميع اعتبار كل الرسائل مقروءة @@ -5674,7 +5694,7 @@ عند التفعيل، فقط الدردشات التي تتضمّن رسائل غير مقروءة هي التي ستظهر في هذا المجلد. - تَضمين الدردشاتِ المَكتومة + تَضمين الدردشات المَكتومة إنشاء @@ -5682,9 +5702,9 @@ حفظ - أترغبُ بتجاهل التغييرات؟ + هل ترغبُ بتجاهل التغييرات؟ - سَتفقد التغييرات التي قمت بها في هذا المجلد. + سَتفقد التغييرات التي قمتَ بها في هذا المجلد. تجاهل @@ -6088,50 +6108,50 @@ يُمكن فقط للمُشرِفين إرسال الرسائل في هذه المجموعة. - لا يمكنك تحديد أكثر من 5 محادثات + لا يمكنك تحديد أكثر من 5 دردشات. - الإضافة إلى قصة المجموعة \"%1$s\" + إضافة إلى قصة المجموعة \"%1$s\" - إضافة للقصة + إضافة إلى القصة إضافة رسالة إضافة رد إرسال إلى - وسائط للمُشاهدة مرة واحدة + وسائط للمُشاهدة مرّة واحدة عنصر واحد أو أكثر كان كبيرًا جدًا عنصر واحد أو أكثر غير صالح تمَّ تحديد عدد كبير جدًا من العناصر - تم تحديد عرض الفيديو لمرة واحدة + تمَّ تحديد عرض الفيديو لمرّة واحدة - تم تحديد عرض الصورة لمرة واحدة + تمَّ تحديد عرض الصورة لمرّة واحدة - تم تحديد جودة الفيديو للجودة العالية + تمَّ تحديد جودة الفيديو للجودة العالية - تم تحديد جودة الفيديو للجودة القياسية + تمَّ تحديد جودة الفيديو للجودة القياسية - تم تحديد جودة الصورة للجودة العالية + تمَّ تحديد جودة الصورة للجودة العالية - تم تحديد جودة الصورة للجودة القياسية + تمَّ تحديد جودة الصورة للجودة القياسية - تم تحديد جودة %1$d عناصر للجودة العالية - تم تحديد جودة %1$d عنصر للجودة العالية - تم تحديد جودة %1$d عنصرين للجودة العالية - تم تحديد جودة %1$d عناصر للجودة العالية - تم تحديد جودة %1$d عنصرًا للجودة العالية - تم تحديد جودة %1$d عنصر للجودة العالية + تمَّ تحديد جودة %1$d عناصر للجودة العالية + تمَّ تحديد جودة %1$d عنصر للجودة العالية + تمَّ تحديد جودة %1$d عنصرين للجودة العالية + تمَّ تحديد جودة %1$d عناصر للجودة العالية + تمَّ تحديد جودة %1$d عنصرًا للجودة العالية + تمَّ تحديد جودة %1$d عنصر للجودة العالية - تم تحديد جودة %1$d عناصر للجودة القياسية - تم تحديد جودة %1$d عنصر للجودة القياسية - تم تحديد جودة %1$d عنصرين للجودة القياسية - تم تحديد جودة %1$d عناصر للجودة القياسية - تم تحديد جودة %1$d عنصرًا للجودة القياسية - تم تحديد جودة %1$d عنصر للجودة القياسية + تمَّ تحديد جودة %1$d عناصر للجودة القياسية + تمَّ تحديد جودة %1$d عنصر للجودة القياسية + تمَّ تحديد جودة %1$d عنصرين للجودة القياسية + تمَّ تحديد جودة %1$d عناصر للجودة القياسية + تمَّ تحديد جودة %1$d عنصرًا للجودة القياسية + تمَّ تحديد جودة %1$d عنصر للجودة القياسية إلغاء @@ -6158,18 +6178,18 @@ سوف تفقد كل التغييرات التي قمتَ بها على هذه الصورة. - تم العثور على %1$s + تمَّ العثور على %1$s ابدأ دردشة مع \"%1$s\" - توجّه للدردشة + توجَّه للدردشة - ربط الجهاز؟ + هل ترغبُ بربط الجهاز؟ - يبدو أنك تحاول ربط جهاز سيجنال. انقر على متابعة وامسح الرمز مرة أخرى لِربطه. + يبدو أنك تحاول ربط جهاز يستخدم سيجنال.انقر على \"متابعة\" وامسح الكود مرّة أخرى لِربطه. - مواصلة + متابعة شاراتي @@ -6186,11 +6206,11 @@ تبرّع الآن - يدعم %1$s سيجنال + %1$s يدعم سيجنال - يدعم %1$s سيجنال بتبرّع شهري. يُعتبر سيجنال تطبيقًا غير ربحيًا ولا يعتمد على أي إعلانات أو مُستثمرين، يدعمه فقط أشخاص مثلك. + %1$s يدعم سيجنال بتبرُّع شهري. يُعتبر سيجنال تطبيقًا غير ربحيًا ولا يعتمد على أي إعلانات أو مُستثمرين، يدعمه فقط أشخاص مثلك. - يدعم %1$s سيجنال بتبرّع. يُعتبر سيجنال تطبيقًا غير ربحيًا ولا يعتمد على أي إعلانات أو مستثمرين، يدعمه فقط أشخاص مثلك. + %1$s يدعم سيجنال بتبرُّع. يُعتبر سيجنال تطبيقًا غير ربحيًا ولا يعتمد على أي إعلانات أو مستثمرين، يدعمه فقط أشخاص مثلك. شارة @@ -6207,27 +6227,27 @@ %1$s /شهريًا - تجديد %1$s + يتجدَّد في %1$s سَتنتهي الصلاحية في %1$s سيجنال تطبيق مُختلف. - التراسل الخاص. بدون إعلانات، بدون مُتعقبات، بدون رقابة. + التراسل الخاص. بدون إعلانات، بدون مُتعقِّبات، بدون رقابة. يتم دعم سيجنال من خلال التبرعات، مِما يعني أن خصوصيتك هي محور كل ما نقوم به. أُنشِئ تطبيق سيجنال من أجلك؛ لا لأجل بياناتك ولا لأجل الربح. - إذا استطعت، يُرجى التبرّع اليوم للحفاظ على تطبيق سيجنال مُمتعًا وموثوقًا به ومتاحًا للجميع. + إذا استطعت، يُرجى التبرُّع اليوم للحفاظ على تطبيق سيجنال مُمتعًا وموثوقًا به ومتاحًا للجميع. شكرا لدعمك! - لقد حصلت على شارة مُتبرع من سيجنال! اعرضها في حسابك الشخصي لإظهار دعمك. + حصلتَ على شارة مُتبرِّع من سيجنال! اعرضها في حسابك الشخصي لإظهار دعمك. يُمكنك أيضًا كُن داعمًا شهريًا. عرض في الحساب الشخصي اصنع شارة مُميَّزة - واصل + متابعة عندما يكون لديك أكثر من شارة واحدة، يُمكنك اختيار واحدة لإظهارها ليراها الآخرين في حسابك الشخصي. احصل على شارات لحسابك الشخصي عن طريق دعم سيجنال. @@ -6239,7 +6259,7 @@ المزيد - دعمي + الدعم الخاص بي إدارة الاشتراك إيصالات التبرُّعات @@ -6250,44 +6270,44 @@ تبرَّع لصديق - تعذر تأكيد التبرع + تعذَّر تأكيد التبرُّع - تعذر تأكيد تبرعك بـ %1$s/شهريًا. تفقد تطبيق مصرفك لتأكيد عملية دفع iDEAL الخاصة بك. + تعذَّر تأكيد تبرُّعك بـ %1$s/شهريًا. تفقَّد تطبيق مصرفك لتأكيد عملية دفع iDEAL الخاصة بك. - تعذر تأكيد تبرعك بـ %1$s لمرة واحدة. تفقد تطبيق مصرفك لتأكيد عملية دفع iDEAL الخاصة بك. + تعذر تأكيد تبرعك بـ %1$s لمرّة واحدة. تفقد تطبيق مصرفك لتأكيد عملية دفع iDEAL الخاصة بك. أدخِل مبلغ مُخَصَّص الحد الأدنى الذي يُمكنك التبرُّع به هو %1$s %1$s/شهر - تجديد %1$s + يتجدَّد في %1$s تجري معالجة المعاملة… تعذَّر إضافة الشارة. %1$s يُرجى الاتصال بفريق الدعم. - عملية الدفع مُعلقة + عملية الدفع مُعلَّقة - تحويلك المصرفي بمبلغ %1$s مُعلق. عادة ما يستغرق إنجاز التحويلات المصرفية ما بين يوم واحد و14 يوم عمل. + تحويلك المصرفي بمبلغ %1$s مُعلَّق. عادة ما يستغرق إكمال التحويلات المصرفية ما بين يوم واحد و14 يوم عمل. - معرفة المزيد + اعرف المزيد تحديث سيجنال - لقد انتهت صلاحية هذا الإصدار من سيجنال. حدِّث الآن للاستمرار في استخدام تطبيق سيجنال. + انتهت صلاحية هذا الإصدار من سيجنال. حدِّث الآن للاستمرار في استخدام تطبيق سيجنال. تحديث التطبيق إلغاء - الجهاز غير مُسجل + الجهاز غير مُسجَّل لم يعد جهازك مُسجلاً. يُرجى إعادة التسجيل للاستمرار في استخدام تطبيق سيجنال على هذا الجهاز. - إعادة-التسجيل + إعادة التسجيل إلغاء @@ -6304,19 +6324,19 @@ أضِف دعمًا ليس الآن - لقد تم إلغاء تبرعك الشهري الدوري تلقائيًا، بسبب غياب نشاطك لفترة طويلة. لم تعد تظهر شارة %1$s في حسابك. + تمَّ إلغاء تبرُّعك الشهري الدوري تلقائيًا، بسبب غياب نشاطك لفترة طويلة. لم تعد تظهر شارة %1$s في حسابك. - لقد تم إلغاء تبرعك الشهري الدوري تلقائيًا، لأنه تعذر علينا معالجة عمليات الدفع الخاصة بك. لم تعد تظهر شارتك في حسابك. + تمَّ إلغاء تبرُّعك الشهري الدوري تلقائيًا، لأنه تعذَّر علينا معالجة عمليات الدفع الخاصة بك. لم تعد تظهر شارتك في حسابك. - تمّ إلغاء تبرعك الشهري الدوري. %1$s لم تعد %2$s شارتك تظهر على حسابك الشخصي. + تمَّ إلغاء تبرُّعك الشهري الدوري. %1$s لم تعد شارتك %2$s تظهر على حسابك الشخصي. يُمكنك الاستمرار في استخدام سيجنال، ولكن يُرجى التجديد الآن لدعم التطبيق وإعادة تفعيل شارتك. إعادة الاشتراك - انتقل الى Google Pay + انتقل إلى Google Pay - لقد تعذرت معالجة عمليات دفع اشتراكك - ‫إننا نواجه مشاكل في جمع دفوعات دعمك سيجنال. يُرجى التأكد من أن طريقة قيامك بالدفوعات حديثة. إن لم تكن كذلك، يُرجى تحديثها في Google Pay. سوف يحاول معالجة دفوعاتك لاحقا خلال بضع أيام. - لا تظهر هذا مجدداً + تعذَّرت معالجة عملية الدفع لاشتراكك + نواجه مشاكل في جمع مدفوعات دعمك لسيجنال. يُرجى التأكُّد من أن طريقة الدفع الخاصة بك حديثة. إن لم تكن كذلك، يُرجى تحديثها في Google Pay. سوف يحاول سيجنال معالجة مدفوعاتك لاحقًا خلال بضع أيام. + لا تُظهِر هذا مُجدَّدًا الاتصال بالدعم احصل على شارة %1$s @@ -6344,34 +6364,34 @@ حدث خطأ أثناء معالجة عملية الدفع هذه، يُرجى إعادة المُحاولة لاحقًا. - حدث خطأ أثناء معالجة التبرع + حدث خطأ أثناء معالجة التبرُّع - يُرجى محاولة القيام بطريقة أخرى للدفع أو الاتصال بالمصرف الذي لديك عنده حساب للمزيد من المعلومات. + جرِّب بطاقة أخرى للدفع أو تواصَل بالمصرف الذي تتعامل معه للمزيد من المعلومات. - معرفة المزيد + اعرف المزيد - حدث خطأ أثناء معالجة التبرع. %1$s - تعذرت معالجة تبرعك ولم يتم تحصيل رسوم منك. يُرجى إعادة المُحاولة. + حدث خطأ أثناء معالجة التبرُّع. %1$s + تعذَّرت معالجة تبرّعك ولم يتم تحصيل رسوم منك. يُرجى إعادة المُحاولة. ما زال قيد المعالجة - تعذّر إضافة الشارة + تعذَّرت إضافة الشارة - Something went wrong + حدث خطأ ما - Your backups subscription couldn\'t be displayed. Please contact support. + تعذَّر عرض اشتراك النسخ الاحتياطية الخاص بك. يُرجى الاتصال بفريق الدعم. - تعذّر التحقّق من الشارة + تعذَّر التحقُّق من الشارة - تعذّر التحقّق من استجابة الخادم. يرجى الاتصال بالدعم. + تعذَّر التحقُّق من استجابة الخادم. يُرجى الاتصال بفريق الدعم. تعذَّر التبرُّع - تمت مُعالجة عملية دفعك ولكن لم يتمكّن سيجنال من إرسال رسالة التبرّع. يُرجى الاتصال بفريق الدعم. - تعذّر إضافة شارتك إلى حسابك، ولكن يُمكن أن المبلغ قد خُصم من حسابك. يُرجى التواصل مع قسم الدعم. - لا يزال تبرعك قيد المعالجة. قد يستغرق الأمر بضع دقائق حسب اتصال شبكتك. + تمَّت مُعالجة عملية التبرُّع الخاصة بك ولكن لم يتمكّن سيجنال من إرسال رسالة التبرُّع. يُرجى الاتصال بفريق الدعم. + تعذَّرت إضافة شارتك إلى حسابك، ولكن يُمكن أن يكون المبلغ خُصِمَ من حسابك. يُرجى التواصل مع قسم الدعم. + لا يزال تبرُّعك قيد المعالجة. قد يستغرق الأمر بضع دقائق حسب اتصال شبكتك. فشل إلغاء الاشتراك - يتطلب إلغاء الاشتراك اتصالاً بالإنترنت. - لا يدعم جهازك Google Pay، لذلك لا يُمكنك الاشتراك لِربح شارة. ما زال يُمكنك دعم سيجنال بالتبرع على موقعنا الإلكتروني. - لقد حدث خطأ في الشبكة. يُرجى التحقق من اتصالك بالانترنت ثم المحاولة مرة أخرى. + يتطلب إلغاء الاشتراك اتصالًا بالإنترنت. + لا يدعم جهازك Google Pay، لذلك لا يُمكنك الاشتراك لِربح شارة. ما زال يُمكنك دعم سيجنال بالتبرُّع على موقعنا الإلكتروني. + حدث خطأ في الشبكة. يُرجى التحقُّق من اتصالك بالانترنت ثم المحاولة مرّة أخرى. إعادة المُحاولة تعذَّر إرسال التبرُّع @@ -6380,87 +6400,87 @@ تعذَّر إرسال تبرُّعك بسبب خطأ في الشبكة. تحقّق من اتصالك بالشبكة ثم حاوِل مرّة أخرى. - تعذرت معالجة تبرع iDEAL الخاص بك. يُرجى محاولة طريقة أخرى للدفع أو الاتصال بمصرفك للمزيد من المعلومات. + تعذَّرت معالجة تبرُّع iDEAL الخاص بك. جرِّب طريقة أخرى للدفع أو تواصَل بمصرفك للمزيد من المعلومات. - تبرّع نيابة عن %1$s + التبرُّع نيابةً عن %1$s تبرَّعَ %1$s لسيجنال نيابة عنك - قبول الهدية + استخدام - إظهار + عرض - جارٍ قبول الهدية… + جارٍ استخدام الهدية… - تمّ قبول الهدية + تمَّ استخدام الهدية - يُرجى محاولة القيام بطريقة أخرى للدفع أو الاتصال بالمصرف الذي لديك عنده حساب للمزيد من المعلومات. + يُرجى استخدام طريقة دفع أخرى أوالتواصل مع المصرف الذي تتعامل معه للمزيد من المعلومات. - يُرجى اللجوء إلى طريقة أخرى للدفع أو التواصل مع مصرفك للمزيد من المعلومات. وإذا كان الأمر يتعلق بمعاملة PayPal فيُرجى التواصل مع PayPal. + جرِّب طريقة أخرى للدفع أو تواصَل مع مصرفك للمزيد من المعلومات. وإذا كان الأمر يتعلق بمعاملة PayPal فيُرجى التواصل مع PayPal. - يُرجى التحقق من أن طريقة دفعك في Google Pay حديثة ثم المحاولة مرة أخرى. + يُرجى التحقُّق من أن طريقة الدفع الخاصة بك في Google Pay حديثة ثم حاوِل مرّة أخرى. - لمعرفة المزيد + اعرف المزيد - يُرجى التحقق من أن طريقة دفعك في Google Pay حديثة ثم المحاولة مرة أخرى. إذا استمر المشكل، يُرجى الاتصال بالمصرف الذي لديك عنده حساب. + تحقَّق من أن طريقة الدفع الخاصة بك في Google Pay حديثة ثم المحاولة مرّة أخرى. إذا استمرت المشكلة، يُرجى الاتصال بالمصرف الذي تتعامل معه. - لا تدعم بطاقتك هذا الصنف من المشتريات. يُرجى محاولة استخدام طريقة دفع أخرى. + لا تدعم بطاقتك هذا الصنف من المشتريات. جرِّب استخدام طريقة دفع أخرى. - لقد انتهت صلاحية بطاقتك. يُرجى تحديث طريقة دفعك في Google Pay ثم المحاولة مرة أخرى. + انتهت صلاحية بطاقتك. قُم بتحديث طريقة دفعك في Google Pay ثم المحاولة مرّة أخرى. انتقل الى Google Pay - حاول مجددا + حاوِل مُجدَّدًا - إن رقم بطاقتك غير صحيح. يُرجى تحديثها في Google Pay ثم المحاولة مرة أخرى. + رقم بطاقتك غير صحيح. قُم بتحديثها في Google Pay ثم حاوِل مرّة أخرى. - إن أرقام رمز التحقق لبطاقتك (CVC) غير صحيحة. يُرجى تحديثها في Google Pay ثم المحاولة مرة أخرى. + رقم رمز التحقُّق لبطاقتك (CVC) غير صحيح. يُرجى تحديثه في Google Pay ثم المحاولة مرّة أخرى. - لا تتوفر بطاقتك على الرصيد الكافي لإتمام عملية الشراء هذه. يُرجى محاولة استخدام طريقة أخرى للدفع. + لا يوجد رصيد كافي في بطاقتك لإتمام عملية الشراء هذه. جرِّب استخدام طريقة دفع أخرى. - إن شهر انتهاء صلاحية طريقة دفعك غير صحيحة. يُرجى تحديثها في Google Pay ثم المحاولة مرة أخرى. + شهر انتهاء الصلاحية على طريقة الدفع الخاصة بك غير صحيح. يُرجى تحديثه في Google Pay ثم المحاولة مرّة أخرى. - إن سنة انتهاء صلاحية طريقة دفعك غير صحيحة. يُرجى تحديثها في Google Pay ثم المحاولة مرة أخرى. + سنة انتهاء الصلاحية على طريقة الدفع الخاصة بك غير صحيحة. يُرجى تحديثها في Google Pay ثم المحاولة مرّة أخرى. - يُرجى محاولة إتمام الدفع مرة أخرى أو الاتصال بمصرفك للمزيد من المعلومات. + حاوِل إتمام الدفع مرّة أخرى أو اتصل بمصرفك للمزيد من المعلومات. - يُرجى المحاولة مرة أخرى أو الاتصال بالمصرف الذي لديك حساب عنده للمزيد من المعلومات. + حاوِل مرة أخرى أو تواصَل مع المصرف الذي تتعامل معه لمزيد من المعلومات. - تأكد من صحة تفاصيل بطاقتك وأعد المحاولة مرة أخرى. + تأكَّد من صحة تفاصيل بطاقتك وحاوِل مرّة أخرى. - تأكد من صحة تفاصيل بطاقتك وأعد المحاولة مرة أخرى. إذا استمر المُشكل، يُرجى الاتصال بالبنك الذي تتعامل معه. + تأكَّد من صحة تفاصيل بطاقتك وحاوِل مرّة أخرى. إذا استمرت المشكلة، تواصَل مع البنك الذي تتعامل معه. - انتهت صلاحية بطاقتك. تأكد من صحة تفاصيل بطاقتك وأعد المحاولة مرة أخرى. + انتهت صلاحية بطاقتك. تأكَّد من صحة تفاصيل بطاقتك وحاوِل مرّة أخرى. - أرقام رمز التحقق لبطاقتك (CVC) غير صحيحة. تأكد من صحة تفاصيل بطاقتك وأعد المحاولة مرة أخرى. + رقم رمز التحقُّق لبطاقتك (CVC) غير صحيح. تأكَّد من صحة تفاصيل بطاقتك وحاوِل مرّة أخرى. - شهر انتهاء الصلاحية الخاص ببطاقتك غير صحيح. تأكد من صحة تفاصيل بطاقتك وأعد المحاولة مرة أخرى. + شهر انتهاء الصلاحية الخاص ببطاقتك غير صحيح. تأكَّد من صحة تفاصيل بطاقتك وحاوِل مرّة أخرى. - سنة انتهاء الصلاحية الخاصة ببطاقتك غير صحيحة. تأكد من صحة تفاصيل بطاقتك وأعد المحاولة مرة أخرى. + سنة انتهاء الصلاحية الخاصة ببطاقتك غير صحيحة. تأكَّد من صحة تفاصيل بطاقتك وحاوِل مرّة أخرى. - أرقام بطاقتك غير صحيحة. تأكد من صحة تفاصيل بطاقتك وأعد المحاولة مرة أخرى. + رقم بطاقتك غير صحيح. تأكَّد من صحة تفاصيل بطاقتك وحاوِل مرّة أخرى. - لا يتوفر الحساب البنكي الذي أدخلته على مبالغ كافية لإتمام عملية الشراء هذه. يُرجى المحاولة مرة أخرى أو الاتصال بمصرفك للمزيد من المعلومات. + لا يحوي الحساب البنكي الذي أدخلته على مبالغ كافية لإتمام عملية الشراء هذه. حاوِل مرّة أخرى أو تواصَل مع المصرف الذي تتعامل معه لمزيد من المعلومات. - لقد أُلغي هذا التبرع من قِبل صاحب الحساب وتعذّر معالجتها. لم تُحصّل أي رسوم منك. + أُلغي هذا التبرُّع من قِبل صاحب الحساب وتعذَّر معالجته. لم يتم تحصيل أي رسوم منك. - حدث خطأ ما أثناء معالجة هذا التبرع، يُرجى المحاولة مجددًا. + حدث خطأ ما أثناء معالجة هذا التبرُّع. يُرجى المحاولة مُجدَّدًا. - تعذّر معالجة المعلومات المصرفية المُدلى بها، يُرجى الاتصال بمصرفك للمزيد من المعلومات. + تعذَّر معالجة المعلومات المصرفية المُقدَّمة. يُرجى الاتصال بمصرفك للمزيد من المعلومات. - تأكد من صحة تفاصيل بطاقتك وأعد المحاولة مرة أخرى. إذا استمر المُشكل، يُرجى الاتصال بالمصرف الذي تتعامل معه. + تأكَّد من صحة تفاصيل بطاقتك وأعِد المحاولة مرّة أخرى. إذا استمرت المشكلة، تواصَل مع المصرف الذي تتعامل معه. - تسمية هيئة إشعارك + تسمية ملف الإشعارات لديك - اسم الحساب الشخصي + اسم ملف الإشعارات %1$d/%2$d @@ -6470,26 +6490,26 @@ حفظ - تحرير هيئة إشعارك هذه + تعديل ملف الإشعارات هذا - توجد هيئة للإشعار بهذا الاسم سلفا + يوجد ملف إشعارات بهذا الاسم من قبل العمل فترة النوم - فترة السياقة + قيادة السيارة - فترة توقف + وقت الاستراحة فترة التركيز - إن الاسم ضروري + الاسم مطلوب الإشعارات المسموح بها - يُرجى إضافة الأفراد والمجموعات المراد تلقي اﻹشعارات والمكالمات من عندها عندما تكون هيئة إشعارك مُشغَّلة + قُم بإضافة الأفراد والمجموعات الذين ترغب بتلقي اﻹشعارات والمكالمات منهم عندما يكون ملف الإشعارات هذا لديك في حالة تشغيل. إضافة اﻷفراد أو المجموعات @@ -6503,26 +6523,26 @@ إضافة - يُرجى إنشاء حساب لاستلام الإشعارات والمكالمات فقط من اﻷفراد والمجموعات المراد تلقي أخبار عنها. + أنشِئ ملف لاستلام الإشعارات والمكالمات فقط من اﻷفراد والمجموعات المُراد تلقي الأخبار منها. - هيئات الإشعار + ملفات الإشعارات - هيئة إشعار جديدة + ملف إشعارات جديد - مُفعَّل + في حالة تشغيل - حذف هيئة الإشعار + حذف ملف الإشعارات - ‫لقد حُذف \"%1$s\". + حُذِف \"%1$s\". تراجع - حذف ملف التعريف نهائيًا؟ + هل ترغب بحذف ملف الإشعارات نهائيًا؟ حذف - تحرير نمط الإشعار + تحرير ملف الإشعارات كل يوم @@ -6545,7 +6565,7 @@ إضافة جدولة - يمكنك إضافة جدولة لتفعيل نمط الإشعار هذا تلقائيًا. + يمكنك إضافة جدولة لتفعيل ملف الإشعارات هذا تلقائيًا. الجدولة @@ -6580,18 +6600,18 @@ يجب أن تدوم الفترة المُجدوَلة ليوم واحد على اﻷقل. - لقد أُنشئَت الهيئة + أُنشِئ ملف الإشعارات تمّ - يُمكنك تشغيل حسابك الشخصي أو تعطيله يدويًا من خلال القائمة الموجودة فوق لائحة الدردشات. + يُمكنك تشغيل ملف الإشعارات أو تعطيله يدويًا من خلال القائمة الموجودة فوق لائحة الدردشات. - يمكنك إضافة جدولة في إعداداتك للاشتغال التلقائي لهيئة إشعارك. + أضِف جدولة في الإعدادات للأتمتة التلقائية لملف الإشعارات. - سوف تشتغل هيئة إشعارك وتتوقف تلقائيا بناءا على جدولتك. + سوف يعمل ملف الإشعارات ويتوقف تلقائيًا بناءًا على جدولتك. - هيئة إشعار جديدة + ملف إشعارات جديد لساعة واحدة @@ -6602,41 +6622,41 @@ مُشغَّلة حتى %1$s - تعذّر فتح المنتقي. + تعذّر فتح المُتصفِّح. لتفعيل الإشعارات، يحتاج سيجنال إلى الإذن لِعرضها. - شغّل + تشغيل - ملاحظات إصدار سيجنال والمستجدات + ملاحظات ومستجدات خاصة بإصدار سيجنال كل النشاطات الجميع - التكرار + مُتكرِّرة - مرة واحدة + لمرّة واحدة نيابة عن صديق - تقديم تبرع لصديق + التبرُّع لصديق - صنف التبرّع + نوع التبرُّع تاريخ الدفع - مشاركة الوصولات + مشاركة الوصل - إذا أعدت تثبيت سيجنال، فلن تصبح إيصالات التبرعات السابقة متاحة. + إذا أعدت تثبيت سيجنال، فلن تصبح إيصالات التبرُّعات السابقة متاحة. - إيصال تبرّع + إيصال التبرُّع المبلغ - نشكرك على دعم سيجنال. تمكننا مساهمتك من الالتزام بمهمة تطوير تقنية خصوصية مفتوحة المصدر تحمي حرية التعبير وتتيح اتصالا عالميا آمنا للملايين عبر العالم. إذا كنت من المقيمين بالولايات المتحدة الأمريكية، يُرجى الاحتفاظ بهذا التوصيل للإدلاء به عند مصلحة الضرائب. إن ‫مؤسسة تكنولوجيا سيجنال مؤسسة خيرية عمومية وغير ربحية، أُحدثَت بالولايات المتحدة اﻷمريكية، وهي معفية من الضرائب بموجب الفقرة 501c3 من قانون الإيرادات الداخلية. مُعرِّفنا الضريبي هو ‎.82-4506840‎ + نشكرك على دعم سيجنال. تُمكِّننا مساهمتك من الالتزام بمهمة تطوير تقنية خصوصية مفتوحة المصدر تحمي حرية التعبير وتتيح اتصالًا عالميًا آمنًا للملايين عبر العالم. إذا كُنتَ من المقيمين بالولايات المتحدة الأمريكية، يُرجى الاحتفاظ بهذا الوصل للإدلاء به عند مصلحة الضرائب. إن ‫مؤسسة تكنولوجيا سيجنال مؤسسة خيرية عمومية وغير ربحية، أُحدِثَت بالولايات المتحدة اﻷمريكية، وهي معفية من الضرائب بموجب الفقرة 501c3 من قانون الإيرادات الداخلية. مُعرِّفنا الضريبي هو ‎.82-4506840‎ لا توجد إيصالات @@ -6651,7 +6671,7 @@ 99+ - إعدادات خصوصية القِصة + إعدادات خصوصية القصة قصصي @@ -6667,23 +6687,23 @@ المشاركة… - توجّه للدردشة + توجَّه للدردشة - معلومات + المعلومات - يَجري الإرسال… + جاٍر الإرسال… - يجري إرسال %1$d… + جاٍر الإرسال %1$d… فشل الإرسال - تم الإرسال جزئيًا + تمَّ الإرسال جزئيًا - يُرجى اللمس لإعادة المحاولة + انقر لإعادة المحاولة - إخفاء القصة؟ + هل ترغبُ بإخفاء القصة؟ - لن تظهر مستجدات قِصص %1$s الجديدة في أعلى لائحة القصص بعد الآن. + لن تظهر تحديثات القصص الجديدة من %1$s في أعلى لائحة القصص بعد الآن. إخفاء @@ -6704,17 +6724,17 @@ قصص %1$s - حذف القصة؟ + هل ترغبُ بحذف القصة؟ ستُحذَف هذه القصة من عند جميع من استلمها بما فيهم أنت. - تعذّر الحفظ + تعذَّر الحفظ لا مشاهدة (%1$d) مشاهدة واحدة (%1$d) مشاهدتان (%1$d) - %1$d مشاهدات + %1$d مشاهداتٍ %1$d مشاهدة %1$d مشاهدة @@ -6724,7 +6744,7 @@ جواب واحد (%1$d) جوابان (%1$d) %1$d أجوبة - %1$d جوابا + %1$d جوابًا %1$d جواب @@ -6732,37 +6752,37 @@ إضافة - مشاهدات غير مشغلة + المشاهدات في حالة إيقاف - ‏%1$s‏%2$s‏ + %1$s %2$s أنت %1$s إلى %2$s - الرَّدّ + الرد - تم الإرسال جزئيًا. يُرجى النقر للتفاصيل + تمَّ الإرسال جزئيًا. انقر للتفاصيل. فشل الإرسال. يُرجى النقر لإعادة المحاولة - الردّ على المجموعة + الرد على المجموعة - لا مشاهدة حاليا + لا مشاهدات حاليًا فعِّل إيصالات القراءة لِرؤية من شاهد قِصصك. - الانتقال إلى الإعدادات + الانتقال إلى \"الإعدادات\" إزالة - إزالة المُشاهد؟ + هل ترغبُ بإزالة المُشاهِد؟ - %1$s سَيظل قادرًا على مشاهدة هذا المنشور، لكنه لن يتمكن من مشاهدة أية منشورات مُستقبلية تشاركها مع %2$s. + سَيظل %1$s قادرًا على مشاهدة هذا المنشور، لكنه لن يتمكن من مشاهدة أية منشورات مُستقبلية تشاركها مع %2$s. - إزالة المُشاهد؟ + هل ترغبُ بإزالة المُشاهِد؟ - لا جواب حاليا + لا ردود حاليًا لا تستطيع الردّ على هذه القصة لأنك لم تعد عضواً في هذه المجموعة. @@ -6772,11 +6792,11 @@ المشاهدات - الإجابات + الردود - تفاعل مع هذه القصة + تفاعَل مع هذه القصة - الردّ على %1$s + الرد على %1$s نسخ @@ -6804,28 +6824,28 @@ الكل إلا… - إخفاء قصتك عن أناس مُحددين + إخفاء قصتك عن أناس مُحدَّدين - %1$d شخصا مستبعدا - %1$d شخص واحد مستبعد - %1$d شخصان مستبعدان - %1$dأشخاص مستبعدين - %1$d أشخاص مستبعدين - %1$d أشخاص مستبعدين + %1$d شخصًا مُستبعَدًا + %1$d شخص واحد مُستبعَد + %1$d شخصان مُستبعدَان + %1$d أشخاص مُستبعدَين + %1$d أشخاص مُستبعدَين + %1$d أشخاص مُستبعدَين مشاركة فقط مع… - مشاركة مع الأشخاص المختارين فقط + مشاركة فقط مع أشخاص مُحدَّدين فقط لا أحد (%1$d) شخص واحد (%1$d) شخصان (%1$d) - %1$d أفراد - %1$d فردا - %1$d فرد + %1$d أشخاص + %1$d شخصًا + %1$d شخصٍ اِختر من يُمكنه رؤية قصتك. لن تؤثر التغييرات على القصص التي أرسلتها من قبل. @@ -7844,7 +7864,7 @@ تعذّر حفظ التغييرات. تحقّق من اتصالك بالإنترنت وحاول مرة أخرى. - Couldn\'t delete call link as it is currently in use. + تعذّر حذف رابط المكالمة لأنه قيد الاستخدام حاليًا. حذف الرابط؟ @@ -7943,6 +7963,10 @@ رفض الموافقة + + Approve all + + Deny all تفعيل إشعارات ملء الشاشة؟ @@ -8125,7 +8149,13 @@ أترغبُ بتخطي الاستعادة؟ - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + إذا تخطّيت الاستعادة، يُمكن تنزيل الوسائط والمرفقات المتبقية في نسختك الاحتياطية في وقتٍ لاحق عند توفُّر مساحة التخزين. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -8158,7 +8188,11 @@ تخطي التنزيل - "تعذَّر إتمام عملية النسخ الاحتياطي الأخيرة. تأكَّد من أن هاتفك مُتصل بشبكة الواي فاي وانقر على \"إنشاء نسخة احتياطية الآن\" للمحاولة من جديد." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -8202,7 +8236,7 @@ سيتم تفريغ الوسائط غير المستخدمة، لكن يُمكن تنزيلها من النسخة الاحتياطية في أي وقت. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + لا يمكن استخدام ميزة تحسين التخزين إلا مع المُستوى المدفوع لنسخ سيجنال الاحتياطية. اشتراك النسخ الاحتياطية الخاص بك لا يزال قيد المعالجة ولم يتم تفعيله بعد. يُرجى المحاولة لاحقًا. @@ -8370,6 +8404,8 @@ التجديد اعرف المزيد + + Processing backup… لديك %1$s من البيانات الاحتياطية غير متوفرة على هذا الجهاز. سيتم حذف نسختك الاحتياطية عند انتهاء اشتراكك خلال %2$d أيام. @@ -8398,6 +8434,8 @@ تعذَّر إيقاف التشغيل وحذف النسخ الاحتياطية حدث خطأ بالشبكة. يُرجى التحقُّق من اتصالك بالإنترنت والمحاولة مرّة أخرى. + + Uploading messages… @@ -8437,7 +8475,7 @@ مفتاح النسخة الاحتياطية الخاص بك - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + مفتاح النسخة الاحتياطية الخاص بك هو كود مُكوَّن من 64 رقمًا يَسمح لك باستعادة نسختك الاحتياطية عندما تُعيد تثبيت سيجنال. إذا نسيتَ مفتاحك، لن تتمكَّن من استرجاع نسختك الاحتياطية. لا يُمكن لسيجنال مُساعدتك على استرجاع نسختك الاحتياطية. @@ -8534,7 +8572,7 @@ هاتفي القديم ليس بحوزتي - Or you\'re reinstalling Signal on the same device + أو تقوم بإعادة تثبيت سيجنال على الجهاز نفسه. استعادة أو نقل الحساب @@ -8563,15 +8601,15 @@ أدخِل مفتاح النسخة الاحتياطية الخاص بك - Your backup key is a 64-character code required to recover your account and data. + مفتاح النسخة الاحتياطية الخاص بك هو كود مُكوَّن من 64 رقمًا، وهو مطلوب لاستعادة حسابك وبياناتك. لا ترغبُ باستخدام مفتاح النسخة الاحتياطية؟ مفتاح النسخة الاحتياطية - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + لا يُمكن استعادة النسخ الاحتياطية دون كود الاستعادة المُكوَّن من 64 رقمًا. إذا فقدت مفتاح النسخة الاحتياطية الخاص بك، فلا يُمكن لسيجنال مُساعدتك في استعادة نسختك الاحتياطية. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + إذا كان لديك جهازك القديم، يُمكنك عرض مفتاح النسخة الاحتياطية الخاص بك في \"الإعدادات\" > \"النسخ الاحتياطية\". ثم انقر على عرض مفتاح النسخة الاحتياطية. اعرف المزيد @@ -8620,6 +8658,18 @@ حسنًا + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 81d1de427a..e0a6691778 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -1011,6 +1011,20 @@ Yenidən əlaqələndirməyə çalışın Köçürmədən davam et + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Mesaj tarixçəsini köçürün - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Köçürmə - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\" ilə əlaqə kəsilsin? @@ -1355,16 +1369,18 @@ Bütün mesajlarınız Nüsxədən geri yükləyin - - Yalnız son %1$d gün ərzində göndərilən və qəbul edilən media fayllar əhatə olunub. Ehtiyat nüsxənizə bunlar daxildir: Nüsxəni geri yüklə - + Son ehtiyat nüsxəniz çıxarılıb: %1$s tarixi saat %2$s + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Ehtiyat nüsxə təfərrüatları əldə edilir… + + Skip restore Adım çəkiləndə bildir @@ -2464,6 +2480,8 @@ Bu nömrəni qeydiyyatdan keçirmək üçün həddindən çox cəhd etdiniz. %1$s dəqiqə sonra yenidən cəhd edin. Xidmətlə bağlantı qurula bilmir. Zəhmət olmasa şəbəkə bağlantınızı yoxlayıb yenidən sınayın. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Təsdiq kodunu sizə SMS vasitəsilə göndərə bilmirik. Bunun əvəzinə kodunuzu audio zənglə almağa çalışın. @@ -4311,6 +4329,8 @@ Bərpa et və ya köçür Hesabı köçür Ötür + + Skip restore Çat nüsxələri Hesabı köçür Hesabınızı yeni bir Android cihazına köçürün @@ -5823,9 +5843,9 @@ Hələ də emal olunur Nişan əlavə etmək mümkün olmadı - Something went wrong + Nəsə səhv getdi - Your backups subscription couldn\'t be displayed. Please contact support. + Ehtiyat nüsxə abunəliyinizi göstərmək mümkün olmadı. Dəstək xidməti ilə əlaqə saxlayın. Nişan təsdiqlənmədi @@ -7216,7 +7236,7 @@ Dəyişiklikləri yaddaşda saxlamaq mümkün olmadı. Şəbəkə bağlantınızı yoxlayıb yenidən cəhd edin. - Couldn\'t delete call link as it is currently in use. + Hazırda istifadədə olduğu üçünn zəng keçidini silmək mümkün olmadıd. Keçid silinsin? @@ -7307,6 +7327,10 @@ Rədd et Təsdiqlə + + Approve all + + Deny all Tam ekran bildirişləri aktivləşdirilsin? @@ -7481,7 +7505,13 @@ Bərpaetmə ötürülsün? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Bərpaetməni ötürsəniz, ehtiyat nüsxənizdə qalan media fayl və qoşmalar daha sonra cihaz yaddaşına kifayət qədər boş yer olduqda endirilə bilər. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Endirməni ötür - "Son ehtiyat nüsxənizi tamamlamaq mümkün olmadı. Telefonunuzun Wi-Fi-a qoşulduğundan əmin olun və yenidən cəhd etmək üçün indi \"Ehtiyat nüsxə\" seçiminə toxunun." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ İstifadə edilməyən media faylı yaddaşdan çıxarılacaq, amma ehtiyat nüsxənizdən istənilən vaxt yenidən endirilə bilər. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Yaddaş optimallaşdırması yalnız Signal ehtiyat nüsxələrinin ödənişli səviyyəsi ilə istifadə oluna bilər. Ehtiyat nüsxə abunəliyiniz hələ də emal olunur və aktiv deyil. Daha sonra yenidən cəhd edin. @@ -7718,6 +7752,8 @@ Yenilə Daha ətraflı + + Processing backup… Bu cihazda olmayan %1$s ehtiyat nüsxə verilənləriniz var. Abunəliyiniz %2$d gün içində bitdikdən sonra ehtiyat nüsxəniz silinəcək. @@ -7738,6 +7774,8 @@ Söndürmək və ehtiyat nüsxələri silmək mümkün olmadı Bir şəbəkə xətası baş verdi. İnternet bağlantınızı yoxlayıb, yenidən cəhd edin. + + Uploading messages… @@ -7777,7 +7815,7 @@ Ehtiyat nüsxə şifrəniz - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Ehtiyat nüsxə şifrəsi, Signal-ı təkrar quraşdırdıqda ehtiyat nüsxənizi bərpa etmənizə imkan verən 64 simvollu bir koddur. Şifrənizi unutmusunuzsa, ehtiyat nüsxəsini bərpa edə bilməyəcəksiniz. Signal ehtiyat nüsxənizi bərpa etmənizə kömək edə bilmir. @@ -7866,7 +7904,7 @@ Köhnə telefonum əlimdə deyil - Or you\'re reinstalling Signal on the same device + Yaxud da Signal-ı eyni cihazda yenidən quraşdırırsınız Hesabı köçür və ya bərpa et @@ -7895,15 +7933,15 @@ Ehtiyat nüsxə şifrəsini daxil edin - Your backup key is a 64-character code required to recover your account and data. + Ehtiyat nüsxə şifrəniz hesabınızı və verilənlərinizi bərpa etmək üçün tələb olunan 64 simvollu bir koddur. Ehtiyat nüsxə şifrəsi yoxdur? Ehtiyat nüsxə şifrəsi - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + 64 simvolluq bərpaetmə kodu olmadan ehtiyat nüsxələrin bərpa edilməsi mümkün deyil. Ehtiyat nüsxə şifrənizi itirsəniz, Signal ehtiyat nüsxənizin bərpasına kömək edə bilməz. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Köhnə cihazınız əlinizdədirsə, Parametrlər > Ehtiyat nüsxələr bölmələrinə keçərək ehtiyat nüsxə şifrənizə baxa bilərsiniz. Sonra isə \"Ehtiyat nüsxə şifrəsinə bax\" seçiminə klikəyin. Daha ətraflı @@ -7952,6 +7990,18 @@ Oldu + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index a71718de13..adad2e0a1c 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -1011,6 +1011,20 @@ Повторен опит за свързване Продължаване без прехвърляне + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Прехвърляне на историята на съобщенията - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Не прехвърляй - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Прекъсване на връзката с „%1$s“? @@ -1355,16 +1369,18 @@ Всички ваши съобщения Възстановяване от резервно копие - - Включена е само мултимедията, изпратена или получена през последните %1$d дни. Вашето резервно копие включва: Възстановяване на архивно копие - + Последното ви резервно копиране е направено на %1$s в %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Извличане на данни за резервно копие… + + Skip restore Уведоми ме за споменавания @@ -2464,6 +2480,8 @@ Направихте прекалено много опити за регистриране на този номер. Моля, опитайте отново след %1$s. Неуспешно свързване с услугата. Моля, проверете мрежовата връзка и опитайте отново. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Не можахме да ви изпратим код за потвърждаване чрез SMS. Опитайте вместо това да получите кода чрез гласово повикване. @@ -4311,6 +4329,8 @@ Възстановяване или трансфер Прехвърляне на акаунт Пропусни + + Skip restore Архив на чатовете Прехвърляне на акаунт Прехвърляне на акаунта на ново устройство с Android @@ -5823,9 +5843,9 @@ Все още се обработва Неуспешно добавяне на значка - Something went wrong + Нещо се обърка - Your backups subscription couldn\'t be displayed. Please contact support. + Вашият абонамент за резервни копия не можа да бъде показан. Моля, свържете се с поддръжката. Неуспешно валидиране на значка @@ -7216,7 +7236,7 @@ Неуспешно запазване на промените. Проверете мрежовата си връзка и опитайте отново. - Couldn\'t delete call link as it is currently in use. + Линкът за разговор не може да бъде изтрит, тъй като в момента се използва. Изтриване на линка? @@ -7307,6 +7327,10 @@ Отхвърли Одобряване + + Approve all + + Deny all Включване на известията на цял екран? @@ -7481,7 +7505,13 @@ Пропускане на възстановяването? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Ако пропуснете възстановяването, останалите прикачени файлове и мултимедия в резервното ви копие ще бъдат изтеглени по-нататък, когато има налично пространство за съхранение. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Пропускане на изтеглянето - "Последното ви резервно копиране не можа да бъде завършено. Уверете се, че телефонът ви е свързан към Wi-Fi и докоснете „Резервно копиране сега“, за да опитате отново." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Неизползваната мултимедия ще бъдат премахната, но може да бъде изтеглена от вашето резервно копие по всяко време. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Оптимизацията на съхранението може да се използва само с платената версия на Резервни копия на Signal. Абонаментът ви за резервни копия все още се обработва и още не е активен. Моля, опитайте отново по-късно. @@ -7718,6 +7752,8 @@ Подновяване Научете повече + + Processing backup… Имате %1$s резервно копие на данни, които не са на това устройство. Вашето резервно копие ще бъде изтрито, когато абонаментът ви приключи след %2$d ден. @@ -7738,6 +7774,8 @@ Неуспешно изключване и изтриване на резервните копия Възникна грешка с мрежата. Моля, проверете интернет връзката си и опитайте отново. + + Uploading messages… @@ -7777,7 +7815,7 @@ Вашият резервен ключ - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Вашият резервен ключ е код от 64 знака, който ви позволява да възстановите резервното копие, когато инсталирате Signal отново. Ако забравите своя ключ, няма да можете да възстановите резервното си копие. Signal не може да ви помогне да възстановите резервното си копие. @@ -7866,7 +7904,7 @@ Старият ми телефон не е подръка - Or you\'re reinstalling Signal on the same device + Или инсталирате Signal отново на същото устройство Възстановяване или прехвърляне на акаунт @@ -7895,15 +7933,15 @@ Въведете своя резервен ключ - Your backup key is a 64-character code required to recover your account and data. + Вашият резервен ключ е код от 64 знака, необходим за възстановяване на акаунта и данните ви. Нямате резервен ключ? Резервен ключ - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Резервните копия не могат да бъдат възстановени без техния код за възстановяване от 64 знака. Ако сте изгубили резервния ключ, Signal не може да ви помогне за възстановяване на резервното копие. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Ако старото ви устройство е достъпно, можете да видите резервния си ключ в „Настройки > Резервни копия“. След това докоснете „Преглед на резервен ключ“. Научете повече @@ -7952,6 +7990,18 @@ Добре + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 9804e1f067..87021b3d7a 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -1011,6 +1011,20 @@ আবারো লিংক করার চেষ্টা করুন ট্রান্সফার না করে চালিয়ে যান + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + মেসেজের ইতিহাস ট্রান্সফার করুন - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - স্থানান্তর করবেন না + ট্রান্সফার করবেন না - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\" বিযুক্ত করবেন? @@ -1355,16 +1369,18 @@ আপনার সকল মেসেজ ব্যাকআপ থেকে পুনরুদ্ধার করুন - - শুধুমাত্র গত %1$d দিনে পাঠানো বা প্রাপ্ত মিডিয়া অন্তর্ভুক্ত করা হয়েছে। আপনার ব্যাকআপের মধ্য়ে যা যা রয়েছে: ব্যাকঅাপ পুনরুদ্ধার - + আপনার সর্বশেষ ব্যাকআপ করা হয়েছিল %1$s তারিখে %2$s-টার সময়। + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. ব্যাকআপের তথ্য আনা হচ্ছে… + + Skip restore মেনশন করা হলে আমাকে অবহিত করুন @@ -2464,6 +2480,8 @@ আপনি এই নম্বরটি রেজিস্ট্রেশন করার জন্য ইতোমধ্যে অনেকবার চেষ্টা করে ফেলেছেন। অনুগ্রহ করে %1$s মিনিটের মধ্যে আবার চেষ্টা করুন। পরিষেবাতে সংযোগ করতে অক্ষম। নেটওয়ার্ক সংযোগ পরীক্ষা করুন এবং আবার চেষ্টা করুন। + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. আমরা আপনাকে SMS-এর মাধ্যমে যাচাইকরণ কোড পাঠাতে পারিনি। এর পরিবর্তে আপনি ভয়েস কলের মাধ্যমে আপনার কোড পাওয়ার চেষ্টা করুন। @@ -4311,6 +4329,8 @@ পুনর্বহাল বা ট্রান্সফার করুন অ্যাকাউন্ট স্থানান্তর করুন বাদ দিয়ে যান + + Skip restore চ্যাট ব্যাকআপ অ্যাকাউন্ট স্থানান্তর করুন একটি নতুন অ্যান্ড্রয়েড ডিভাইসে অ্যাকাউন্ট স্থানান্তর করুন @@ -5823,9 +5843,9 @@ এখনও প্রক্রিয়া করা হচ্ছে ব্যাজ যোগ করা যায়নি - Something went wrong + কিছু একটা ভুল হয়েছে - Your backups subscription couldn\'t be displayed. Please contact support. + আপনার ব্যাকআপ সাবস্ক্রিপশন প্রদর্শন করা যায়নি। অনুগ্রহ করে সহায়তা কেন্দ্রে যোগাযোগ করুন। ব্যাজ যাচাই সফল হয়নি @@ -7216,7 +7236,7 @@ পরিবর্তনগুলো সেভ করা যায়নি। আপনার নেটওয়ার্ক সংযোগ ঠিক আছে কি না দেখুন এবং আবার চেষ্টা করুন। - Couldn\'t delete call link as it is currently in use. + কল লিংকটি মুছে ফেলা যায়নি কারণ এটি এখন ব্যবহার করা হচ্ছে। লিংক মুছে ফেলবেন? @@ -7307,6 +7327,10 @@ বাতিল অনুমোদন + + Approve all + + Deny all পূর্ণ স্ক্রীন নোটিফিকেশন চালু করবেন? @@ -7481,7 +7505,13 @@ পুনর্বহাল এড়িয়ে যাবেন? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + যদি আপনি আপনার ব্যাকআপে থাকা অবশিষ্ট মিডিয়া এবং সংযুক্তিগুলো পুনর্বহাল করা এড়িয়ে যান তবে পরে যখন স্টোরেজ স্পেস পাওয়া যাবে তখন সেগুলো ডাউনলোড করা যাবে। + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ ডাউনলোড এড়িয়ে যান - "আপনার শেষ ব্যাকআপ সম্পন্ন করা যায়নি। আপনার ফোন ওয়াইফাই-এর সাথে সংযুক্ত আছে কিনা তা নিশ্চিত করুন এবং আবার চেষ্টা করতে এখনই ব্যাক আপ ট্যাপ করুন।" + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ অব্যবহৃত মিডিয়া অফলোড করা হবে, কিন্তু আপনার ব্যাকআপ থেকে যেকোনো সময় ডাউনলোড করা যাবে। - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + স্টোরেজ অপ্টিমাইজেশন শুধুমাত্র Signal ব্যাকআপের পেইড টিয়ারের ক্ষেত্রে ব্যবহার করা যেতে পারে। আপনার ব্যাকআপ সাবস্ক্রিপশন এখনো প্রক্রিয়া করা হচ্ছে এবং এখনো সক্রিয় নয়। অনুগ্রহ করে পরে আবার চেষ্টা করুন। @@ -7718,6 +7752,8 @@ নবায়ন করুন আরো জানুন + + Processing backup… আপনার কাছে %1$s-টি ব্যাকআপ ডেটা আছে যা এই ডিভাইসে নেই। আপনার সাবস্ক্রিপশন %2$d দিনের মধ্যে শেষ হলে আপনার ব্যাকআপ মুছে ফেলা হবে। @@ -7738,6 +7774,8 @@ ব্যাকআপ বন্ধ করা এবং মুছে ফেলা যায়নি নেটওয়ার্কে কোনো ত্রুটি ঘটেছে। আপনার ইন্টারনেট সংযোগ ঠিক আছে কি না দেখুন এবং আবার চেষ্টা করুন। + + Uploading messages… @@ -7777,7 +7815,7 @@ আপনার ব্যাকআপ \'কি\' - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + আপনার ব্যাকআপ \'কি\' একটি 64-ক্যারেক্টারের কোড যা আপনাকে Signal পুনরায় ইনস্টল করার সময় আপনার ব্যাকআপ পুনর্বহাল করতে সাহায্য করে। \'কি\' ভুলে গেলে, আপনি আপনার ব্যাকআপ পুনরুদ্ধার করতে পারবেন না। Signal আপনাকে আপনার ব্যাকআপ পুনরুদ্ধার করতে সাহায্য করতে পারবে না। @@ -7866,7 +7904,7 @@ আমার পুরানো ফোনটি নেই - Or you\'re reinstalling Signal on the same device + অথবা আপনি একই ডিভাইসে Signal পুনরায় ইনস্টল করছেন অ্যাকাউন্ট পুনর্বহাল বা ট্রান্সফার করুন @@ -7895,15 +7933,15 @@ আপনার ব্যাকআপ \'কি\' লিখুন - Your backup key is a 64-character code required to recover your account and data. + আপনার ব্যাকআপ \'কি\' একটি 64-ক্যারেক্টারের কোড যা আপনার অ্যাকাউন্ট ও ডেটা পুনর্বহাল করার সময় প্রয়োজন হবে। কোনো ব্যাকআপ \'কি\' নেই? ব্যাকআপ \'কি\' - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + ব্যাকআপগুলো তাদের 64-ক্যারেক্টারের পুনর্বহাল কোড ছাড়া পুনর্বহাল করা যাবে না। আপনি যদি আপনার ব্যাকআপ \'কি\' হারিয়ে ফেলেন তবে Signal আপনার ব্যাকআপ পুনর্বহাল করতে সাহায্য করতে সক্ষম হবে না। - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + আপনার পুরানো ডিভাইসটি থাকলে আপনি সেটিংস > ব্যাকআপে আপনার ব্যাকআপ \'কি\' দেখতে পারবেন। তারপর আপনার \"ব্যাকআপ \'কি\' দেখান\" ট্যাপ করুন। আরো জানুন @@ -7952,6 +7990,18 @@ ঠিক আছে + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index 7b860d54aa..f67fdc7973 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -1051,6 +1051,20 @@ Pokušajte se ponovo povezati Nastavite bez prijenosa + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1076,13 +1090,13 @@ - Transfer message history + Prijenos historije poruka - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Ne prenosi - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Prekini vezu \"%1$s\"? @@ -1431,16 +1445,18 @@ Sve vaše poruke Vrati iz rezervne kopije - - Uključeni su samo medijski sadržaji poslani ili primljeni u proteklih %1$d dan(a). Vaša sigurnosna kopija uključuje: Vrati podatke - + Vaša zadnja sigurnosna kopija je napravljena %1$s u %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Preuzimanje detalja sigurnosne kopije… + + Skip restore Obavijesti me kad me neko spomene @@ -2632,6 +2648,8 @@ Previše puta ste pokušali registrirati ovaj broj. Pokušajte ponovo za %1$s. Povezivanje nije uspjelo. Molimo provjerite internet-konekciju i pokušajte ponovo. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Nismo mogli da vam pošaljemo verifikacioni kôd putem SMS-a. Umjesto toga pokušajte primiti kôd putem glasovnog poziva. @@ -4533,6 +4551,8 @@ Vratite ili prenesite Prenesi račun Preskoči + + Skip restore Rezervne kopije chata Prenesi račun Prenesite račun na novi Android uređaj @@ -6089,9 +6109,9 @@ Još uvijek obrađujem Nije bilo moguće dodati značku - Something went wrong + Nešto nije u redu - Your backups subscription couldn\'t be displayed. Please contact support. + Vaša pretplata na sigurnosne kopije nije mogla biti prikazana. Molimo kontaktirajte podršku. Neuspjela potvrda značke @@ -7530,7 +7550,7 @@ Nije moguće spremiti promjene. Provjerite mrežnu vezu i pokušajte ponovo. - Couldn\'t delete call link as it is currently in use. + Nije moguće izbrisati link za poziv jer je trenutno u upotrebi. Izbrisati link? @@ -7625,6 +7645,10 @@ Odbaci Odobri + + Approve all + + Deny all Uključiti obavještenja preko cijelog ekrana? @@ -7803,7 +7827,13 @@ Preskočiti vraćanje? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Ako preskočite obnovu, preostali medijski sadržaj i prilozi u vašoj sigurnosnoj kopiji mogu se preuzeti kasnije kada prostor za pohranu postane dostupan. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7836,7 +7866,11 @@ Preskoči preuzimanje - "Vaša posljednja sigurnosna kopija nije mogla biti dovršena. Provjerite je li vaš telefon povezan na Wi-F i dodirnite Kreiraj sigurnosnu kopiju sada da pokušate ponovo." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7880,7 +7914,7 @@ Neiskorišteni medijski sadržaji će biti uklonjeni, ali se mogu preuzeti iz vaše sigurnosne kopije u bilo koje vrijeme. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Optimizacija pohrane može se koristiti samo s plaćenim nivoom sigurnosnih kopija Signala. Vaša pretplata na sigurnosne kopije se još uvijek obrađuje i još nije aktivna. Pokušajte ponovo kasnije. @@ -8044,6 +8078,8 @@ Obnovi Saznaj više + + Processing backup… Imate %1$s podatak sigurnosne kopije koji nije na ovom uređaju. Vaša sigurnosna kopija će biti izbrisana kada vaša pretplata završi za %2$d dan. @@ -8068,6 +8104,8 @@ Nije moguće isključiti i izbrisati sigurnosne kopije Desila se greška mreže. Provjerite internetsku vezu i pokušajte ponovo. + + Uploading messages… @@ -8107,7 +8145,7 @@ Vaš rezervni ključ - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Vaš rezervni ključ je 64-cifreni kod koji vam omogućava da vratite rezervnu kopiju kada ponovo instalirate Signal. Ako zaboravite ključ, nećete moći vratiti sigurnosnu kopiju. Signal vam ne može pomoći da vratite sigurnosnu kopiju. @@ -8200,7 +8238,7 @@ Nemam svoj stari telefon - Or you\'re reinstalling Signal on the same device + Ili ponovo instalirajte Signal na istom uređaju Vratite ili prenesite račun @@ -8229,15 +8267,15 @@ Unesite rezervni ključ - Your backup key is a 64-character code required to recover your account and data. + Vaš rezervni ključ je 64-cifreni kod potreban za oporavak vašeg računa i podataka. Nemate rezervni ključ? Rezervni ključ - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Sigurnosne kopije se ne mogu oporaviti bez njihovog koda za oporavak od 64 znaka. Ako ste izgubili rezervni ključ, Signal vam ne može pomoći da vratite rezervnu kopiju. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Ako imate svoj stari uređaj, možete vidjeti rezervni ključ u Postavkama > Sigurnosne kopije. Zatim dodirnite Prikaži rezervni ključ. Saznaj više @@ -8286,6 +8324,18 @@ U redu + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 73759814b1..ef33712afa 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -1011,6 +1011,20 @@ Intentar tornar a enllaçar Continuar sense transferir + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Transferir l\'historial de missatges - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - No el transfereixis + No transferir - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Voleu desenllaçar «%1$s»? @@ -1355,16 +1369,18 @@ Tots els teus missatges Restaura des d\'una còpia de seguretat - - Només s\'inclouen els arxius enviats o rebuts durant els darrers %1$d dies. La teva còpia de seguretat inclou: Restaura la còpia de seguretat - + La teva darrera còpia de seguretat es va fer el dia %1$s a les %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Obtenint detalls de la còpia de seguretat… + + Skip restore Notifica\'m les mencions @@ -2464,6 +2480,8 @@ Has fet massa intents per registrar aquest número. Si us plau, torna-ho a provar en %1$s. No es pot connectar al servei. Si us plau, comproveu la connexió de xarxa i torneu-ho a provar. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. No t\'hem pogut enviar un codi de verificació per SMS. Prova de rebre el teu codi mitjançant una trucada de veu. @@ -4311,6 +4329,8 @@ Restaurar o transferir Transfereix el compte Omet + + Skip restore Còpies de seguretat de xats Transfereix el compte Transfereix el compte a un dispositiu d\'Android nou @@ -5823,9 +5843,9 @@ Encara en procés No s\'ha pogut afegir la insígnia. - Something went wrong + Alguna cosa no ha anat bé - Your backups subscription couldn\'t be displayed. Please contact support. + La subscripció a còpies de seguretat no s\'ha pogut mostrar. Posa\'t en contacte amb el servei d\'assistència. No s\'ha pogut validar la insígnia. @@ -7216,7 +7236,7 @@ No s\'han pogut guardar els canvis. Comprova la teva connexió i torna-ho a intentar. - Couldn\'t delete call link as it is currently in use. + No s\'ha pogut eliminar l\'enllaç de trucada, ja que en aquest moment s\'està utilitzant. Eliminar enllaç? @@ -7307,6 +7327,10 @@ Rebutja Aprova-la + + Approve all + + Deny all Activar les notificacions de pantalla completa? @@ -7481,7 +7505,13 @@ Ometre la restauració? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Si omets la restauració, podràs descarregar la resta d\'arxius multimèdia de la teva còpia de seguretat quan s\'alliberi espai. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Ometre la descàrrega - "La teva darrera còpia de seguretat no s\'ha pogut completar. Assegura\'t que el teu telèfon estigui connectat a una xarxa wifi i toca \"Fer una còpia de seguretat ara\" per tornar-ho a provar." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Els arxius multimèdia no utilitzats es descarregaran, però es poden baixar de la còpia de seguretat en qualsevol moment. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + L\'optimització d\'emmagatzematge només es pot utilitzar amb el nivell de subscripció de pagament de Còpies de seguretat de Signal. La teva subscripció a còpies de seguretat s\'està processant i encara no està activa. Torna-ho a provar més tard. @@ -7718,6 +7752,8 @@ Renovar Més informació + + Processing backup… Hi ha %1$s de dades de la còpia de seguretat que no s\'han copiat en aquest dispositiu. La teva còpia de seguretat s\'eliminarà quan la teva subscripció finalitzi d\'aquí a %2$d dia. @@ -7738,6 +7774,8 @@ No s\'ha pogut desactivar i eliminar les còpies de seguretat S\'ha produït un error de xarxa. Comprova teva la connexió a Internet i torna-ho a provar. + + Uploading messages… @@ -7777,7 +7815,7 @@ La teva clau de còpia de seguretat - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + La teva clau de còpia de seguretat és un codi de 64 caràcters que et permetrà restaurar la còpia de seguretat quan tornis a instal·lar Signal. Si oblides la teva clau, no podràs restaurar la còpia de seguretat. Signal no et pot ajudar a recuperar la còpia de seguretat. @@ -7866,7 +7904,7 @@ No conservo el meu telèfon antic - Or you\'re reinstalling Signal on the same device + O estàs reinstal·lant Signal al mateix dispositiu Restaurar o transferir compte @@ -7895,15 +7933,15 @@ Introdueix la còpia de seguretat de la clau - Your backup key is a 64-character code required to recover your account and data. + La teva còpia de seguretat de la clau és un codi de 64 caràcters i és necessària per recuperar el teu compte i les teves dades. ¿No tens la teva clau? Còpia de seguretat de la clau - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Les còpies de seguretat no es poden recuperar sense el seu codi de recuperació de 64 caràcters. Si has perdut la teva clau, Signal no pot ajudar-te a restaurar la còpia de seguretat. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Si conserves el teu dispositiu antic, podràs veure la teva còpia de seguretat de la clau a Ajustos > Còpies de seguretat de Signal. Llavors, toca Veure la còpia de seguretat de la clau. Més informació @@ -7952,6 +7990,18 @@ D\'acord + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 5d4860891b..ef58165c4c 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -1051,6 +1051,20 @@ Zkuste znovu propojit Pokračovat bez přenosu + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1076,13 +1090,13 @@ - Transfer message history + Přenést historii zpráv - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Nepřevádět + Nepřenášet - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Zrušit propojení „%1$s“? @@ -1431,16 +1445,18 @@ Všechny vaše zprávy Obnovit ze zálohy - - Zahrnuta jsou pouze média odeslaná nebo přijatá v posledních %1$d dnech. Vaše záloha obsahuje: Obnovit zálohu - + Poslední záloha proběhla dne %1$s v %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Načítají se údaje o zálohování… + + Skip restore Upozornit mne na zmínky @@ -2632,6 +2648,8 @@ Učinili jste příliš mnoho pokusů o registraci tohoto čísla. Zkuste to prosím znovu za %1$s. Nelze se připojit k službě. Prosím zkontrolujte připojení k internetu a poté to zkuste znovu. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Nepodařilo se vám zaslat ověřovací kód prostřednictvím SMS. Zkuste místo toho kód získat prostřednictvím hlasového hovoru. @@ -4533,6 +4551,8 @@ Obnovení nebo přenos Přenést účet Přeskočit + + Skip restore Zálohy chatů Přenést účet Přenést účet na nové Android zařízení @@ -6089,9 +6109,9 @@ Stále zpracovávám Odznak se nepodařilo přidat - Something went wrong + Něco se pokazilo - Your backups subscription couldn\'t be displayed. Please contact support. + Vaše předplatné pro zálohování nelze zobrazit. Kontaktujte prosím podporu. Odznak se nepodařilo ověřit @@ -7530,7 +7550,7 @@ Změny se nepodařilo uložit. Zkontrolujte připojení k internetu a zkuste to znovu. - Couldn\'t delete call link as it is currently in use. + Odkaz na hovor nelze odstranit, protože je právě používán. Smazat odkaz? @@ -7625,6 +7645,10 @@ Odmítnout Schválit + + Approve all + + Deny all Zapnout oznámení na celou obrazovku? @@ -7803,7 +7827,13 @@ Přeskočit obnovení? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Pokud obnovu přeskočíte, můžete zbývající média a přílohy v záloze stáhnout později, až bude k dispozici úložný prostor. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7836,7 +7866,11 @@ Přeskočit stahování - "Poslední zálohu se nepodařilo dokončit. Ujistěte se, že je telefon připojen k síti Wi-Fi, klepněte na Zpět a zkuste to znovu." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7880,7 +7914,7 @@ Nevyužívaná média budou odložena, ale můžete si je kdykoli stáhnout ze zálohy. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Optimalizaci úložiště lze použít pouze s placenou verzí zálohování služby Signal. Vaše předplatné zálohování se stále zpracovává a není ještě aktivní. Zkuste to prosím znovu později. @@ -8044,6 +8078,8 @@ Obnovit Zjistit více + + Processing backup… Máte %1$s záložních dat, která nejsou uložena v tomto zařízení. Vaše záloha bude smazána, až vaše předplatné skončí za %2$d den. @@ -8068,6 +8104,8 @@ Nepodařilo se vypnout a odstranit zálohy Vyskytla se chyba sítě. Zkontrolujte prosím své internetové připojení a zkuste to znovu. + + Uploading messages… @@ -8107,7 +8145,7 @@ Váš záložní klíč - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Váš záložní klíč je 64místný kód, který vám umožní obnovit zálohu při opětovné instalaci aplikace Signal. Pokud svůj klíč zapomenete, nebude možné zálohu obnovit. Signal vám nemůže pomoci obnovit vaši zálohu. @@ -8200,7 +8238,7 @@ Nemám svůj starý telefon - Or you\'re reinstalling Signal on the same device + Nebo přeinstalováváte aplikaci Signal na stejném zařízení Obnovení nebo přenos účtu @@ -8229,15 +8267,15 @@ Zadejte svůj záložní klíč - Your backup key is a 64-character code required to recover your account and data. + Záložní klíč je 64místný kód potřebný k obnovení účtu a dat. Nemáte záložní klíč? Záložní klíč - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Bez 64místného kódu pro obnovení nelze zálohy obnovit. Pokud jste záložní klíč ztratili, nemůže vám služba Signal s obnovením zálohy pomoci. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Pokud máte staré zařízení, můžete si záložní klíč zobrazit v Nastavení > Zálohy. Poté klepněte na Zobrazit záložní klíč. Zjistit více @@ -8261,7 +8299,7 @@ Přenést účet - Váš účet bude přenesen do nového zařízení. Toto zařízení bude moci vidět vaše skupiny a kontakty, mít přístup k chatům a posílat zprávy vaším jménem. %1$s + Váš účet bude přenesen do nového zařízení. Toto zařízení bude moci zobrazit vaše skupiny a kontakty, mít přístup k chatům a posílat zprávy vaším jménem. %1$s Zjistit více @@ -8286,6 +8324,18 @@ V pořádku + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 869f55edb6..9f152a2ed9 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -1011,6 +1011,20 @@ Prøv at forbinde igen Fortsæt uden at overføre + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Overfør beskedhistorik - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Overfør ikke - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Frakobl \"%1$s\"? @@ -1355,16 +1369,18 @@ Alle dine beskeder Gendan fra sikkerhedskopi - - Inkluderer kun medier sendt eller modtaget i løbet af de seneste %1$d dage. Din sikkerhedskopi omfatter: Gendan sikkerhedskopi - + Din sidste sikkerhedskopi blev lavet %1$s kl. %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Henter sikkerhedskopieringsoplysninger… + + Skip restore Underret mig ved omtaler @@ -2464,6 +2480,8 @@ Du har forsøgt at registrere dette nummer for mange gange. Prøv igen om %1$s. Ikke muligt at få forbindelse til service. Tjek venligst din netværksforbindelse og prøv igen. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Vi kunne ikke sende dig en bekræftelseskode via sms. Prøv at modtage din kode via et taleopkald i stedet. @@ -4311,6 +4329,8 @@ Gendan eller overfør Overfør konto Spring over + + Skip restore Sikkerhedskopier af chats Overfør konto Overfør konto til en ny Android-enhed @@ -5823,9 +5843,9 @@ Behandler stadig Kunne ikke tilføje badge - Something went wrong + Noget gik galt - Your backups subscription couldn\'t be displayed. Please contact support. + Dit abonnement på sikkerhedskopiering kunne ikke vises. Kontakt support. Kunne ikke validere badge @@ -7216,7 +7236,7 @@ Kunne ikke gemme ændringer. Tjek din netværksforbindelse, og prøv igen. - Couldn\'t delete call link as it is currently in use. + Kunne ikke slette opkaldslink, da det er i brug. Slet link? @@ -7307,6 +7327,10 @@ Afvis Godkend + + Approve all + + Deny all Slå notifikationer på fuld skærm til? @@ -7481,7 +7505,13 @@ Vil du springe gendannelse over? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Hvis du springer gendannelse over, kan du downloade medierne og de vedhæftede filer i sikkerhedskopien senere, når der er tilgængelig lagerplads. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Spring download over - "Din sidste sikkerhedskopiering kunne ikke gennemføres. Tjek, om din telefon er tilsluttet et wi-wi-netværk, og tryk på Sikkerhedskopiér nu for at prøve igen." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Ubrugte medier fjernes, men kan downloades fra din sikkerhedskop når som helst. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Lageroptimering kan kun bruges, hvis du betaler for Signal-sikkerhedskopier. Dit abonnement på sikkerhedskopiering behandles og er endnu ikke aktivt. Prøv igen senere. @@ -7718,6 +7752,8 @@ Forny Få mere at vide + + Processing backup… Du har %1$s sikkerhedskopieret data, der ikke er på denne enhed. Din sikkerhedskopi slettes, når dit abonnement slutter om %2$d dag. @@ -7738,6 +7774,8 @@ Kunne ikke deaktivere og slette sikkerhedskopier Der opstod en netværksfejl. Tjek din internetforbindelse, og prøv igen. + + Uploading messages… @@ -7777,7 +7815,7 @@ Din sikkerhedskopinøgle - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Din sikkerhedskopinøgle er en kode på 64 tegn, som lader dig gendanne din sikkerhedskopi, når du geninstallerer Signal. Hvis du glemmer din nøgle, kan du ikke gendanne din sikkerhedskopi. Signal kan ikke hjælpe dig med at gendanne din sikkerhedskopi. @@ -7866,7 +7904,7 @@ Jeg har ikke min gamle telefon - Or you\'re reinstalling Signal on the same device + Eller hvis du geninstallerer Signal på samme enhed Gendan eller overfør konto @@ -7895,15 +7933,15 @@ Angiv din sikkerhedskopinøgle - Your backup key is a 64-character code required to recover your account and data. + Din sikkerhedskopinøgle er en 64-cifret kode, der bruges til at gendanne din konto og dine data. Har du ingen sikkerhedskopinøgle? Sikkerhedskopinøgle - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Sikkerhedskopier kan ikke gendannes uden gendannelseskoden på 64 tegn. Hvis du har mistet din sikkerhedskopinøgle, kan Signal ikke hjælpe med at gendanne din sikkerhedskopi. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Hvis du har din gamle enhed, kan du se din sikkerhedskopinøgle under Indstillinger > Sikkerhedskopier. Klik derefter Vis sikkerhedskopinøgle. Få mere at vide @@ -7952,6 +7990,18 @@ Okay + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index c3395c4ba4..5697313f53 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1011,6 +1011,20 @@ Kopplung erneut versuchen Ohne Übertragung fortfahren + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Nachrichtenverlauf übertragen - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Nicht überweisen + Nicht übertragen - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device »%1$s« entkoppeln? @@ -1355,16 +1369,18 @@ Alle deine Nachrichten Aus Sicherung wiederherstellen - - Enthält ausschließlich Medien, die in den letzten %1$d Tagen gesendet oder erhalten wurden. Deine Datensicherung umfasst: Sicherung wiederherstellen - + Deine letzte Datensicherung wurde am %1$s um %2$s durchgeführt. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Backup-Details abrufen… + + Skip restore Mich bei Erwähnungen benachrichtigen @@ -2458,12 +2474,14 @@ Fehler bei den Google-Play-Diensten Google-Play-Dienste werden aktualisiert oder sind vorübergehend nicht verfügbar. Bitte versuche es erneut. Nutzungsbedingungen und Datenschutzerklärung - Signal benötigt die Berechtigungen »Kontakte« und »Speicher«, um dir das Kontaktieren deiner Freunde und das Senden von Nachrichten zu erleichtern. Deine Kontakte werden mittels Signals vertraulicher Kontaktfindung auf die Signal-Server hochgeladen. Das heißt, sie sind Ende-zu-Ende-verschlüsselt und somit zu keiner Zeit dem Signal-Dienst bekannt. - Signal benötigt die Berechtigung »Kontakte«, um dir das Kontaktieren deiner Freunde zu erleichtern. Deine Kontakte werden mittels Signals vertraulicher Kontaktfindung auf die Signal-Server hochgeladen. Das heißt, sie sind Ende-zu-Ende-verschlüsselt und somit zu keiner Zeit dem Signal-Dienst bekannt. + Signal benötigt die Berechtigungen »Kontakte« und »Speicher«, um dir das Kontaktieren deiner Freunde und das Senden von Nachrichten zu erleichtern. Deine Kontakte werden mittels Signals vertraulicher Kontaktsuche auf die Signal-Server hochgeladen. Das heißt, sie sind Ende-zu-Ende-verschlüsselt und somit zu keiner Zeit dem Signal-Dienst bekannt. + Signal benötigt die Berechtigung »Kontakte«, um dir das Kontaktieren deiner Freunde zu erleichtern. Deine Kontakte werden mittels Signals vertraulicher Kontaktsuche auf die Signal-Server hochgeladen. Das heißt, sie sind Ende-zu-Ende-verschlüsselt und somit zu keiner Zeit dem Signal-Dienst bekannt. Du hast zu oft versucht, diese Telefonnummer zu registrieren. Bitte versuche es später erneut. Du hast zu oft versucht, diese Nummer zu registrieren. Bitte versuche es erneut in %1$s. Keine Verbindung zum Dienst möglich. Bitte überprüfe deine Netzverbindung und versuche es erneut. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Wir konnten dir keinen Verifizierungscode per SMS schicken. Versuche stattdessen, den Code per Sprachanruf zu erhalten. @@ -3642,7 +3660,7 @@ Aktivieren, falls das Gerät SMS/MMS über WLAN nutzt (nur bei aktivierter WLAN-Telefonie) Inkognito-Tastatur Lesebestätigungen - Bei deaktivierten Lesebestätigungen kannst du auch die Lesebestätigungen anderer nicht sehen + Bei deaktivierten Lesebestätigungen kannst du auch die Lesebestätigungen anderer nicht sehen. Tipp-Indikatoren Bei deaktivierten Tipp-Indikatoren kannst du nicht sehen, wenn andere gerade eine Nachricht eintippen. Tastatur auffordern, personalisiertes Lernen auszuschalten @@ -4149,11 +4167,11 @@ Signal hat derzeit technische Schwierigkeiten. Wir arbeiten daran, den Betrieb so schnell wie möglich wiederherzustellen. %1$d %% - Die private Kontaktsuche von Signal kann die Kontakte deines Mobiltelefons vorübergehend nicht verarbeiten. + Die vertrauliche Kontaktsuche von Signal kann die Kontakte deines Mobiltelefons vorübergehend nicht verarbeiten. Mehr erfahren - Die private Kontaktsuche von Signal kann die Kontakte deines Mobiltelefons nicht verarbeiten. + Die vertrauliche Kontaktsuche von Signal kann die Kontakte deines Mobiltelefons nicht verarbeiten. Mehr erfahren @@ -4311,6 +4329,8 @@ Wiederherstellen oder übertragen Konto übertragen Überspringen + + Skip restore Chat-Sicherungen Konto übertragen Konto auf ein neues Android-Gerät übertragen @@ -5823,9 +5843,9 @@ Wird noch verarbeitet Abzeichen konnte nicht hinzugefügt werden. - Something went wrong + Etwas ist schiefgelaufen - Your backups subscription couldn\'t be displayed. Please contact support. + Dein Backup-Abo konnte nicht angezeigt werden. Bitte kontaktiere den Support. Abzeichen konnte nicht validiert werden @@ -7216,7 +7236,7 @@ Die Änderungen konnten nicht gespeichert werden. Überprüfe deine Netzverbindung und versuche es erneut. - Couldn\'t delete call link as it is currently in use. + Anruflink konnte nicht gelöscht werden, da er gerade verwendet wird. Link löschen? @@ -7307,6 +7327,10 @@ Ablehnen Bestätigen + + Approve all + + Deny all Vollbildbenachrichtigungen aktivieren? @@ -7481,7 +7505,13 @@ Wiederherstellen überspringen? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Wenn du »Wiederherstellen« überspringst, kannst du die verbleibenden Medien und Anhänge in deinem Backup zu einem späteren Zeitpunkt herunterladen, sobald wieder Speicherplatz verfügbar ist. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Download überspringen - "Deine letzte Datensicherung konnte nicht abgeschlossen werden. Stelle sicher, dass dein Gerät mit dem WLAN verbunden ist und tippe auf »Jetzt sichern«, um es erneut zu versuchen." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Nicht verwendete Medien werden zwar entfernt, sie können aber jederzeit aus deiner Datensicherung heruntergeladen werden. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Die Speicheroptimierung kann nur mit der kostenpflichtigen Option für Signal Backups genutzt werden. Dein Backup-Abo wird momentan noch bearbeitet und ist daher noch nicht aktiv. Bitte versuche es später noch einmal. @@ -7718,6 +7752,8 @@ Erneuern Mehr erfahren + + Processing backup… Du verfügst über %1$s gesicherte Daten, die sich nicht auf diesem Gerät befinden. Deine Datensicherung wird gelöscht, wenn dein Abo in %2$d Tag endet. @@ -7738,6 +7774,8 @@ Datensicherungen konnten nicht ausgeschaltet und gelöscht werden. Ein Netzwerkfehler ist aufgetreten. Bitte überprüfe deine Internetverbindung und versuche es erneut. + + Uploading messages… @@ -7777,7 +7815,7 @@ Dein Datensicherungsschlüssel - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Dein Datensicherungsschlüssel ist ein 64-stelliger Code, mit dem du deine Datensicherung wiederherstellen kannst, wenn du Signal neu installierst. Ohne Schlüssel kannst du deine Datensicherung nicht wiederherstellen. Signal kann dir nicht helfen, deine Datensicherung wiederherzustellen. @@ -7866,7 +7904,7 @@ Ich habe mein altes Telefon nicht - Or you\'re reinstalling Signal on the same device + Oder du installierst Signal auf demselben Gerät erneut Konto wiederherstellen oder übertragen @@ -7895,15 +7933,15 @@ Gib deinen Datensicherungsschlüssel ein - Your backup key is a 64-character code required to recover your account and data. + Dein Datensicherungsschlüssel ist ein 64-stelliger Code, den du benötigst, um dein Konto und deine Daten wiederherzustellen. Kein Datensicherungsschlüssel? Datensicherungsschlüssel - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Datensicherungen können ohne den 64-stelligen Code nicht wiederhergestellt werden. Wenn du deinen Datensicherungsschlüssel verloren hast, kann dir Signal nicht helfen, dein Backup wiederherzustellen. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Auf deinem alten Gerät kannst du deinen Datensicherungsschlüssel in Einstellungen > Backups sehen. Tippe dann auf »Datensicherungsschlüssel anzeigen«. Mehr erfahren @@ -7952,6 +7990,18 @@ OK + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 096e151b0b..222e406df0 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -1011,6 +1011,20 @@ Δοκίμασε να συνδεθείς ξανά Συνέχεια χωρίς μεταφορά + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Μεταφορά ιστορικού μηνυμάτων - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Όχι μεταφορά + Να μη γίνει μεταφορά - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Αποσύνδεση της \"%1$s\"; @@ -1355,16 +1369,18 @@ Όλα τα μηνύματά σου Επαναφορά από αντίγραφο ασφαλείας - - Συμπεριλαμβάνονται μόνο τα πολυμέσα που εστάλησαν ή ελήφθησαν τις τελευταίες %1$d ημέρες. Τα αντίγραφα ασφαλείας περιλαμβάνουν: Επαναφορά αντίγραφου ασφαλείας - + Το τελευταίο σου αντίγραφο ασφαλείας έγινε στις %1$s στις %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Λήψη στοιχείων αντιγράφων ασφαλείας… + + Skip restore Να ειδοποιούμαι για αναφορές @@ -2464,6 +2480,8 @@ Έχεις προσπαθήσει πάρα πολλές φορές να εγγράψεις αυτόν τον αριθμό. Ξαναπροσπάθησε σε %1$s. Αδυναμία σύνδεσης στην υπηρεσία. Παρακαλώ έλεγξε τη σύνδεση στο δίκτυο και ξαναπροσπάθησε. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Δεν μπορέσαμε να σου στείλουμε κωδικό επαλήθευσης μέσω SMS. Δοκίμασε να λάβεις τον κωδικό μέσω κλήσης. @@ -4311,6 +4329,8 @@ Επαναφορά ή μεταφορά Μεταφορά λογαριασμού Παράλειψη + + Skip restore Αντίγραφα ασφαλείας συνομιλιών Μεταφορά λογαριασμού Μεταφορά λογαριασμού σε νέα συσκευή Android @@ -5823,9 +5843,9 @@ Ακόμα επεξεργάζεται Αδυναμία προσθήκης σήματος - Something went wrong + Κάτι πήγε στραβά - Your backups subscription couldn\'t be displayed. Please contact support. + Δεν ήταν δυνατή η εμφάνιση της συνδρομής σου για αντίγραφα ασφαλείας. Επικοινώνησε με την υποστήριξη. Αποτυχία επικύρωσης σήματος @@ -7216,7 +7236,7 @@ Αδυναμία αποθήκευσης αλλαγών. Έλεγξε τη σύνδεσή σου στο δίκτυο και δοκίμασε πάλι. - Couldn\'t delete call link as it is currently in use. + Δεν ήταν δυνατή η διαγραφή του συνδέσμου κλήσης καθώς χρησιμοποιείται αυτήν τη στιγμή. Διαγραφή συνδέσμου; @@ -7307,6 +7327,10 @@ Απόρριψη Έγκριση + + Approve all + + Deny all Ενεργοποίηση ειδοποιήσεων σε πλήρη οθόνη; @@ -7481,7 +7505,13 @@ Παράλειψη επαναφοράς; - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Αν παραλείψεις την επαναφορά, τα υπόλοιπα πολυμέσα και τα συνημμένα στο αντίγραφο ασφαλείας σου μπορούν να ληφθούν αργότερα, όταν θα υπάρχει διαθέσιμος χώρος αποθήκευσης. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Παράλειψη λήψης - "Δεν ήταν δυνατή η ολοκλήρωση του τελευταίου αντιγράφου ασφαλείας. Βεβαιώσου ότι η συσκευή σου είναι συνδεδεμένη σε Wi-Fi και πάτησε Δημιουργία αντιγράφων ασφαλείας τώρα για να προσπαθήσεις ξανά." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Ο φόρτος των αχρησιμοποίητων πολυμέσων θα μειωθεί, αλλά μπορούν να ληφθούν από το αντίγραφο ασφαλείας σου ανά πάσα στιγμή. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Η βελτιστοποίηση αποθηκευτικού χώρου μπορεί να χρησιμοποιηθεί μόνο με την πληρωμένη βαθμίδα των Αντιγράφων Ασφαλείας Signal. Η συνδρομή για αντίγραφα ασφαλείας βρίσκεται ακόμη υπό επεξεργασία και δεν είναι ακόμη ενεργή. Δοκίμασε αργότερα. @@ -7718,6 +7752,8 @@ Ανανέωση Μάθε περισσότερα + + Processing backup… Έχεις %1$s δεδομένα αντιγράφων ασφαλείας που δεν υπάρχουν σε αυτήν τη συσκευή. Το αντίγραφο ασφαλείας σου θα διαγραφεί όταν λήξει η συνδρομή σου σε %2$d ημέρα. @@ -7738,6 +7774,8 @@ Δεν ήταν δυνατή η απενεργοποίηση και η διαγραφή των αντιγράφων ασφαλείας Παρουσιάστηκε σφάλμα δικτύου. Έλεγξε τη σύνδεσή σου στο διαδίκτυο και δοκίμασε ξανά. + + Uploading messages… @@ -7777,7 +7815,7 @@ Το εφεδρικό κλειδί σου - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Το εφεδρικό κλειδί σου είναι ένας 64-ψήφιος κωδικός που σου επιτρέπει να επαναφέρεις το αντίγραφο ασφαλείας σου κατά την επανεγκατάσταση του Signal. Εάν ξεχάσεις τον κλειδί σου, δεν θα μπορέσεις να επαναφέρεις τα αντίγραφα ασφαλείας σου. Το Signal δεν μπορεί να σε βοηθήσει να ανακτήσεις τα αντίγραφα ασφαλείας σου. @@ -7866,7 +7904,7 @@ Δεν έχω το παλιό μου τηλέφωνο - Or you\'re reinstalling Signal on the same device + Ή επανεγκαθιστάς το Signal στην ίδια συσκευή Επαναφορά ή μεταφορά λογαριασμού @@ -7895,15 +7933,15 @@ Γράψε το εφεδρικό κλειδί σου - Your backup key is a 64-character code required to recover your account and data. + Το εφεδρικό κλειδί σου είναι ένας 64ψήφιος κωδικός που απαιτείται για την ανάκτηση του λογαριασμού και των δεδομένων σου. Δεν υπάρχει εφεδρικό κλειδί; Εφεδρικό κλειδί - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Τα αντίγραφα ασφαλείας δεν μπορούν να ανακτηθούν χωρίς τον 64ψήφιο κωδικό ανάκτησης. Εάν έχεις χάσει το εφεδρικό κλειδί σου, το Signal δεν μπορεί να βοηθήσει στην επαναφορά του αντιγράφου ασφαλείας σου. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Αν έχεις την παλιά σου συσκευή, μπορείς να δεις το εφεδρικό κλειδί σου στις Ρυθμίσεις > Αντίγραφα ασφαλείας Signal. Στη συνέχεια, πάτα Προβολή εφεδρικού κλειδιού. Μάθε περισσότερα @@ -7952,6 +7990,18 @@ Εντάξει + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 4a5fda64c0..821a856722 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -130,7 +130,7 @@ %1$s no ha activado Pagos - ¿Quieres enviarles una solicitud para activar Pagos? + ¿Quieres enviarle una solicitud para activar Pagos? Enviar solicitud @@ -617,17 +617,17 @@ Tu número de seguridad con %1$s ha cambiado Tu número de seguridad con %1$s ha cambiado probablemente porque ha reinstalado Signal o cambiado de dispositivo. Toca Verificar para confirmar el nuevo número de seguridad. Esta verificación es opcional. - ¿Vetar solicitudes? + ¿Bloquear solicitud? %1$s no podrá unirse ni solicitar unirse al grupo mediante enlace, pero se le puede añadir manualmente. - Vetar solicitudes + Bloquear solicitud Cancelar Bloqueado - Actualiza Signal + Actualizar Signal Registrarse de nuevo en Signal @@ -657,7 +657,7 @@ Denunciado como spam y bloqueado - Has aceptado la solicitud de mensaje de %1$s. Si ha sido un error, puedes elegir una de las siguientes acciones. + Has aceptado una solicitud de mensaje de %1$s. Si ha sido un error, puedes elegir una de las siguientes acciones. Consejos de seguridad @@ -758,7 +758,7 @@ %1$s en línea - Tu código QR y tu enlace se han restablecido y tu alias es %1$s + Tu código QR y enlace se han restablecido y tu alias es %1$s Mensaje de intercambio de claves @@ -795,7 +795,7 @@ - Los perfiles son visibles para las personas a las que envías mensajes, tus contactos y tus grupos. + Tu perfil es visible para las personas a las que les envías mensajes, tus contactos y tus grupos. ¿Quién puede encontrarme con mi número? @@ -904,15 +904,15 @@ Para hacer una foto, se necesita acceso a la cámara. - Para mostrar la galería, se necesita acceso al almacenamiento. + Para mostrar la galería, Signal necesita acceso al almacenamiento. Ahora - %1$d m + %1$d min Hoy Ayer - %1$s a las %2$s + %2$s del %1$s Mañana @@ -959,7 +959,7 @@ Sesión de chat reiniciada - Signal usa cifrado de extremo a extremo y puede necesitar reiniciar la sesión de un chat en determinados casos. Este reinicio no afecta a la seguridad, pero puede ocurrir que el último mensaje de esta persona antes del reinicio sea ilegible. Solicita que lo envíe de nuevo, si se da el caso. + Signal usa cifrado de extremo a extremo y es posible que en determinados casos deba reiniciarse la sesión de un chat. Este reinicio no afecta a la seguridad, pero puede ocurrir que no hayas recibido el último mensaje que esta persona te envió antes del reinicio. Si es así, pídele que vuelva a enviarte el mensaje. @@ -1011,6 +1011,20 @@ Intentar vincular de nuevo Continuar sin transferir + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Transferir historial de mensajes - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device No transferir - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device ¿Desvincular \"%1$s\"? @@ -1133,7 +1147,7 @@ ¿Qué son los nuevos grupos? Los nuevos grupos incluyen funciones como @menciones y admins, y añadiremos más en el futuro. - Se ha conservado todo el historial de mensajes y todos los archivos multimedia que tenías antes de la actualización. + Se han conservado todos los mensajes y archivos multimedia que tenías antes de la actualización. Deberás aceptar una invitación para volver a unirte a este grupo y no recibirás ningún mensaje del grupo hasta que aceptes. Esta persona tiene que aceptar la invitación para volver a unirse al grupo y no recibirá ningún mensaje del grupo hasta que acepte: @@ -1259,7 +1273,7 @@ No tienes invitaciones pendientes. Personas que otros participantes han invitado No hay invitaciones pendientes de otros participantes. - No se muestra la información de las personas invitadas por otros participantes. Su información se mostrarán a partir del momento en que decidan formar parte del grupo. También verán los mensajes del grupo a partir de ese momento. + No se muestra la información de las personas que otros participantes han invitado. Su información se mostrará a partir del momento en que decidan unirse al grupo. También verán los mensajes del grupo a partir de ese momento. Retirar invitación Retirar invitaciones @@ -1281,7 +1295,7 @@ Listo - No se puede añadir a esta persona a grupos antiguos. + No se puede añadir a esta persona a grupos del sistema antiguo. ¿Añadir a %1$s a \"%2$s\"? ¿Añadir %3$d participantes a \"%2$s\"? @@ -1294,7 +1308,7 @@ Crear grupo Crear Participantes - Puedes añadir o invitar a personas después de crear este grupo. + Después de crear este grupo, podrás añadir o invitar a quien quieras. Nombre del grupo (obligatorio) Nombre del grupo (opcional) Este campo es obligatorio. @@ -1355,16 +1369,18 @@ Todos tus mensajes Restaurar desde copia de seguridad - - Solo se incluyen los archivos multimedia enviados o recibidos en los últimos %1$d días. Tu copia de seguridad incluye: Restaurar copia - + Última copia de seguridad: %2$s del %1$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Obteniendo detalles de la copia de seguridad… + + Skip restore Notificarme cuando alguien me mencione @@ -1400,7 +1416,7 @@ ¿Eliminar alias? - "Esto eliminará tu alias y deshabilitará tu código QR y tu enlace. %1$s estará disponible para que otras personas lo usen. ¿Seguro que quieres hacer esto?" + "Se eliminará tu alias y deshabilitarán tu código QR y tu enlace. %1$s estará disponible para que otras personas lo usen. ¿Seguro que quieres continuar?" @@ -1412,7 +1428,7 @@ - No hay grupos en común + No tienes grupos en común %1$d grupo en común %1$d grupos en común @@ -1431,7 +1447,7 @@ Vibración Personalizar - Modificar sonido y vibración + Cambiar sonido y vibración Ajustes de llamada Tono de llamada Predeterminado @@ -1442,7 +1458,7 @@ Compartir Restablecer enlace Solicitar aprobación de admin - Alguien que sea admin del grupo debe aprobar a los nuevos participantes que se unan mediante enlace. + Alguien que sea admin del grupo debe aprobar a nuevos participantes que se unan mediante enlace. ¿Quieres restablecer el enlace al grupo? Si lo restableces, nadie podrá unirse al grupo usando el enlace actual. @@ -1599,7 +1615,7 @@ Archivo Audios Vídeo - Foto + Imagen @@ -1767,7 +1783,7 @@ Has aceptado la invitación al grupo. - %1$s ha aceptado la invitación al grupo. + %1$s ha aceptado una invitación al grupo. Has añadido a %1$s. %1$s ha añadido a %2$s. @@ -1871,7 +1887,7 @@ %1$s pertenece a %2$s - Has mandado una solicitud a %1$s para activar Pagos + Has enviado una solicitud a %1$s para que active Pagos %1$s quiere que actives Pagos. Envía pagos solamente a las personas en las que confíes. @@ -1959,7 +1975,7 @@ Bloquear Desbloquear - ¿Quieres compartir tu nombre y foto con %1$s y permitir que te mande un mensaje? En el pasado, eliminaste a esta persona. + ¿Compartir tu nombre y foto con %1$s y permitir que te envíe mensajes? Has eliminado a esta persona anteriormente. ¿Permitir que %1$s te envíe mensajes y compartir tu nombre y foto con esta persona? No sabrá que has visto sus mensajes hasta que aceptes. ¿Quieres que %1$s te envíe mensajes y compartir tu nombre y foto? Esta persona no sabrá que has visto sus mensajes hasta lx desbloquees. @@ -1967,10 +1983,10 @@ ¿Quieres permitir a %1$s chatear contigo? No recibirás ningún mensaje si no desbloqueas el chat. ¿Quieres recibir noticias de %1$s? No recibirás ninguna novedad hasta que desbloquees a esta persona. - ¿Quieres chatear con este grupo y compartir tu nombre y foto con sus participantes? + ¿Seguir chateando con este grupo y compartir tu nombre y foto con sus participantes? Este es un grupo antiguo y ya no puede usarse. Crea un nuevo grupo para activar funciones nuevas como @menciones y admins. Este grupo del sistema antiguo no es válido porque es demasiado grande. Se permite un máximo de %1$d participantes. - ¿Quieres seguir chateando con %1$s y compartir tu nombre y foto con esta persona? + ¿Seguir chateando con %1$s y compartir tu nombre y foto con esta persona? ¿Unirte a este grupo y compartir tu nombre y foto con sus participantes? No sabrán que has visto sus mensajes hasta que aceptes. ¿Unirte a este grupo y compartir tu nombre y foto con sus participantes? No podrás ver sus mensajes hasta que aceptes. ¿Unirte a este grupo? Sus participantes no sabrán que has visto sus mensajes hasta que aceptes. @@ -2439,7 +2455,7 @@ Selecciona tu país Debes especificar el código de país - Para registrarte introduce un número de teléfono válido. + Introduce un número de teléfono válido para registrarte. Número no válido El número que has indicado (%1$s) no es válido. @@ -2464,6 +2480,8 @@ Has hecho demasiados intentos para registrar este número. Inténtalo de nuevo en %1$s. No se ha podido conectar con el servidor. Comprueba la conexión de red e inténtalo de nuevo. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. No hemos podido enviarte el código de verificación por SMS. Intenta recibir tu código a través de una llamada. @@ -2494,7 +2512,7 @@ Número de teléfono Introduce tu número de teléfono para comenzar. - Introduce el código que hemos enviado a %1$s + Introduce el código que hemos enviado al %1$s Número de teléfono Código de país @@ -2504,7 +2522,7 @@ ¿Tienes problemas para registrarte? - • Asegúrate de que tu teléfono tenga señal para recibir tu SMS o llamada\n • Confirma que puedes recibir una llamada telefónica al número\n • Comprueba que has ingresado tu número de teléfono correctamente. + • Asegúrate de que tu teléfono tenga señal para recibir tu SMS o llamada\n • Confirma que puedes recibir llamadas a ese número\n • Comprueba que has ingresado tu número de teléfono correctamente. Para obtener más información, consulta %1$s o selecciona %2$s @@ -2638,7 +2656,7 @@ Este mensaje ha sido eliminado. Mensaje eliminado. - Has mandado una solicitud para activar Pagos + Has enviado una solicitud para activar Pagos %1$s quiere que actives Pagos @@ -2869,7 +2887,7 @@ Activa las notificaciones - Para recibir notificaciones de nuevos mensajes: + Para recibir notificaciones de mensajes nuevos: 1. Toca el botón \"Ajustes\" que aparece más abajo @@ -3146,7 +3164,7 @@ Habilitar notificaciones de llamadas Actualizar contacto - Vetar solicitud + Bloquear solicitud No tienes grupos en común. Revisa las solicitudes detenidamente. Ninguno de los participantes de este grupo está en tus contactos. Revisa las solicitudes detenidamente. Ver @@ -3200,7 +3218,7 @@ Cambios en el número de seguridad Aceptar - Llamar de todas formas + Llamar de todos modos Unirse a la llamada Continuar llamada Abandonar llamada @@ -3347,12 +3365,12 @@ Escribe algunas palabras sobre ti… %1$d/%2$d Habla con libertad - Cifrado + Cifrado y seguro Sé amable Amante del café - Libre para chatear + Libre para charlar Tomándome un descanso - Trabajando en cosas nuevas + Trabajando en algo nuevo Editar grupo @@ -3416,7 +3434,7 @@ Leído por No enviado Visto por - Omitido + No entregado No se ha podido enviar @@ -3808,7 +3826,7 @@ - ¿Activar el bloqueo de pagos para futuros envíos? + ¿Activar el bloqueo de pagos para futuras transacciones? Añade una capa adicional de seguridad y activa el bloqueo de pantalla o la huella dactilar de Android para transferir fondos. @@ -3816,7 +3834,7 @@ Ahora no - Actualización requerida + Actualización necesaria Se requiere una actualización para seguir enviando y recibiendo pagos, y para ver tu saldo de pago actualizado. @@ -4139,7 +4157,7 @@ Filtrar chats sin leer - Dejar de filtrar chats sin leer + Dejar de filtrar por chats no leídos Copiar al portapapeles @@ -4191,7 +4209,7 @@ Crear nuevo PIN Puedes cambiar tu PIN siempre que este dispositivo esté registrado. Crea tu PIN - Los PINs pueden ayudarte a restaurar tu cuenta y mantener tu información encriptada con Signal. + El PIN puede ayudarte a restaurar tu cuenta y mantener tu información cifrada en Signal. Elige un PIN más seguro @@ -4311,6 +4329,8 @@ Restaurar o transferir Transferir cuenta Omitir + + Skip restore Copias de seguridad de los chats Transferir cuenta Transferir cuenta a un dispositivo Android nuevo @@ -4370,7 +4390,7 @@ Quién puede ver mi número - Tu número de teléfono será visible para las personas y los grupos a los que envíes mensajes. + Tu número de teléfono será visible para las personas y los grupos a los que les envíes mensajes. Tu número de teléfono no será visible para nadie a menos que lo tenga guardado en los contactos de su teléfono. @@ -4380,7 +4400,7 @@ Cualquiera que tenga tu número de teléfono verá que estás en Signal y podrá iniciar chats contigo. - Nadie podrá ver que estás en Signal a menos que les envíes un mensaje o ya tengas un chat con esa persona. + Nadie podrá ver que estás en Signal a menos que le envíes un mensaje o ya tengas un chat con esa persona. "Para cambiar este ajuste, selecciona la opción \"Nadie\" en \"Quién puede ver mi número\"." @@ -4458,7 +4478,7 @@ Registrar tu cuenta de nuevo - Actualiza Signal + Actualizar Signal Eliminar todos los datos @@ -4818,7 +4838,7 @@ Deslizar para ver más fondos de pantalla Elige un fondo para todos los chats. Elige un fondo para \"%1$s\". - Signal necesita acceso al almacenamiento para mostrar la galería. + Para mostrar la galería, Signal necesita acceso al almacenamiento. @@ -4931,7 +4951,7 @@ %1$s te ha enviado %2$s - %1$d notificaciones de pagos nuevas + %1$d nuevas notificaciones de pago No se puede enviar el pago @@ -5476,7 +5496,7 @@ ¿Eliminar a %1$s? - No verás a esta persona en tus búsquedas. Recibirás una solicitud de mensaje si te envían un mensaje en el futuro. + No verás a esta persona en tus búsquedas. Recibirás una solicitud de mensaje si te escribe en algún momento. Se ha eliminado a %1$s @@ -5742,7 +5762,7 @@ Más información - Actualiza Signal + Actualizar Signal Esta versión de Signal ha caducado. Actualízala ahora para continuar usando Signal. @@ -5823,9 +5843,9 @@ Aún se está procesando Fallo al añadir insignia - Something went wrong + Se ha producido un error - Your backups subscription couldn\'t be displayed. Please contact support. + No se ha podido mostrar tu suscripción a las copias de seguridad. Contacta con el equipo de asistencia. Fallo al validar la insignia @@ -6567,9 +6587,9 @@ Abrir el menú contextual - %1$s · Persona verificada + %1$s · Identidad verificada - Persona verificada + Identidad verificada Cambios en el número de seguridad @@ -6665,7 +6685,7 @@ Quién puede ver esta historia - "Las personas que están en el grupo \"%1$s\" pueden ver y responder a esta historia. Puedes añadir o eliminar participantes en los ajustes del grupo." + "Las personas que están en el grupo \"%1$s\" podrán ver y responder a esta historia. Puedes añadir o eliminar participantes en los ajustes del grupo." Eliminar historia de grupo @@ -6912,7 +6932,7 @@ - Has enviado a %1$s + Enviado a %1$s %1$s te ha enviado @@ -6942,7 +6962,7 @@ - Filtrar por no leído + Filtrar por no leídos Desplazarse para filtrar @@ -7043,7 +7063,7 @@ No se ha podido eliminar el enlace de la llamada. Comprueba tu conexión a Internet e inténtalo de nuevo. - No se han podido eliminar todos los enlaces de las llamadas. Comprueba tu conexión a Internet e inténtalo de nuevo. + No se han podido eliminar todos los enlaces de llamada. Comprueba tu conexión e inténtalo de nuevo. Eliminar historial de llamadas @@ -7083,7 +7103,7 @@ Llama a alguien para empezar. - Los enlaces de llamada que hayas creado ya no funcionarán para las personas que los tengan. + Los enlaces de llamada que hayas creado ya no funcionarán para nadie. @@ -7216,7 +7236,7 @@ No se han podido guardar los cambios. Comprueba tu conexión de red e inténtalo de nuevo. - Couldn\'t delete call link as it is currently in use. + No se ha podido eliminar el enlace de llamada porque se está usando en este momento. ¿Eliminar enlace? @@ -7307,6 +7327,10 @@ Rechazar Aprobar + + Approve all + + Deny all ¿Activar las notificaciones de pantalla completa? @@ -7354,7 +7378,7 @@ Alias no válido - Número no válido + Número de teléfono no válido Invitar a Signal @@ -7422,7 +7446,7 @@ - Tu suscripción al plan de copias de seguridad ha caducado + Tu suscripción a las copias de seguridad ha caducado Tu plan de copia de seguridad ha caducado porque no se ha podido renovar con tu método de pago actual. Transcurrido %1$d día, se eliminarán los archivos multimedia de tu copia de seguridad. @@ -7441,7 +7465,7 @@ Para liberar espacio, elimina las aplicaciones o contenido con archivos grandes que no uses. - No se ha renovar tu suscripción al plan de copias de seguridad + No se ha renovado tu suscripción a las copias de seguridad Asegúrate de que tu método de pago esté actualizado. Toca \"Gestionar suscripción\" y, en Métodos de pago, selecciona \"Actualizar\". @@ -7481,7 +7505,13 @@ ¿Omitir restauración? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Si omites la restauración, podrás descargar el resto de los archivos adjuntos y multimedia de tu copia de seguridad cuando se libere espacio. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7500,7 +7530,7 @@ Sin conexión a Internet - El dispositivo tiene poca batería + Tu dispositivo tiene poca batería %1$s de %2$s @@ -7514,7 +7544,11 @@ Omitir descarga - "No se ha podido completar tu última copia de seguridad. Asegúrate de que tu teléfono esté conectado a una red Wi-Fi y toca \"Iniciar copia\" para volver a intentarlo." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Los archivos multimedia no utilizados se inhabilitarán, pero podrás descargarlos desde tu copia de seguridad en cualquier momento. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + La optimización de almacenamiento solo se puede utilizar con el nivel de pago de las copias de seguridad de Signal. Tu suscripción a las copias de seguridad se está procesando y todavía no está activa. Inténtalo de nuevo más tarde. @@ -7583,7 +7617,7 @@ Este archivo ya no está disponible - Para empezar a hacer una copia de seguridad de todos tus archivos, activa las copias de seguridad de Signal y elige tu suscripción. + Para empezar a hacer una copia de seguridad de todos tus archivos multimedia, activa las copias de seguridad de Signal y elige tu suscripción. Continuar @@ -7658,7 +7692,7 @@ Caduca el %1$s - Haz una copia de seguridad de tu historial de mensajes para no perder ningún dato cuando compras un teléfono nuevo o reinstalas Signal. + Haz una copia de seguridad de tu historial de mensajes para no perder ningún dato si compras un teléfono nuevo o reinstalas Signal. Otras formas de hacer copias de seguridad @@ -7718,6 +7752,8 @@ Renovar Más información + + Processing backup… Hay %1$s de datos de tu copia de seguridad que no están en este dispositivo. Tu copia de seguridad se eliminará cuando finalice tu suscripción en %2$d día. @@ -7738,6 +7774,8 @@ No se ha podido desactivar y eliminar la copia de seguridad Se ha producido un error de red. Comprueba tu conexión a Internet e inténtalo de nuevo. + + Uploading messages… @@ -7777,7 +7815,7 @@ La clave de tu copia de seguridad - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + La clave de tu copia de seguridad es un código de 64 caracteres que te permite restaurar tu copia de seguridad cuando vuelves a instalar Signal. Si olvidas tu clave, no podrás restaurar tu copia de seguridad. Signal no puede ayudarte a recuperar tu copia de seguridad. @@ -7866,7 +7904,7 @@ No tengo mi teléfono anterior - Or you\'re reinstalling Signal on the same device + O estás reinstalando Signal en el mismo dispositivo Restaurar o transferir cuenta @@ -7895,15 +7933,15 @@ Introduce la clave de tu copia de seguridad - Your backup key is a 64-character code required to recover your account and data. + La clave de tu copia de seguridad es un código de 64 caracteres que necesitarás para recuperar tu cuenta y tus datos. ¿No tienes tu clave? Clave de copia de seguridad - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Las copias de seguridad no se pueden recuperar sin el código de recuperación de 64 caracteres correspondiente. Si has perdido la clave de tu copia de seguridad, Signal no puede ayudarte a restaurar tu copia de seguridad. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Si tienes tu dispositivo anterior, puedes ver la clave de tu copia de seguridad en Ajustes > Copias de seguridad de Signal. Luego, toca Ver clave de copia de seguridad. Más información @@ -7927,7 +7965,7 @@ Transferir cuenta - Tu cuenta se transferirá a un nuevo dispositivo. En este dispositivo podrás acceder a tus grupos y contactos, acceder a todos tus chats y enviar mensajes desde tu perfil. %1$s + Tu cuenta se transferirá a un nuevo dispositivo. En este dispositivo podrás ver tus grupos y contactos, acceder a tus chats y enviar mensajes desde tu perfil. %1$s Más información @@ -7952,6 +7990,18 @@ Aceptar + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index c0dc4e02a3..9042a91127 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -1011,6 +1011,20 @@ Proovi uuesti linkida Jätka ilma andmeid üle kandmata + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Edasta sõnumiajalugu - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Ära kanna üle + Ära edasta - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Kas tühistada seadme „%1$s“ linkimine? @@ -1355,16 +1369,18 @@ Kõik su sõnumid Taasta varukoopiast - - Hõlmab ainult viimase %1$d päeva jooksul saadetud või saadud meediat. Sinu varukoopia sisaldab järgmist: Taasta varukoopia - + Sinu viimane varukoopia tehti %1$s kell %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Varundamidandmete hankimine … + + Skip restore Teavita mind mainimise korral @@ -2464,6 +2480,8 @@ Oled proovinud seda numbrit liiga palju kordi registreerida. Palun proovi uuesti %1$s pärast. Teenusega ei saadud ühendust. Palun kontrolli võrguühendust ja proovi uuesti. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Me ei saanud sulle SMS-i kaudu kinnituskoodi saata. Proovi koodi saamiseks parem häälkõnet kasutada. @@ -4311,6 +4329,8 @@ Taasta või edasta Kanna konto üle Jäta vahele + + Skip restore Vestluste varukoopiad Kanna konto üle Kanna konto üle uude Android-seadmesse @@ -5823,9 +5843,9 @@ Töötlemine käib Märgi lisamine ei õnnestunud - Something went wrong + Midagi läks valesti - Your backups subscription couldn\'t be displayed. Please contact support. + Sinu varukoopiate tellimust ei saanud kuvada. Palun võta ühendust kasutajatoega. Märgi valideerimine nurjus @@ -7216,7 +7236,7 @@ Muudatusi ei saanud salvestada. Kontrolli oma võrguühendust ja proovi uuesti. - Couldn\'t delete call link as it is currently in use. + Kõnelinki ei saanud kustutada, kuna see on praegu kasutusel. Kas kustutada link? @@ -7307,6 +7327,10 @@ Keeldu Nõustu + + Approve all + + Deny all Kas lülitada sisse täisekraani formaadis teavitused? @@ -7481,7 +7505,13 @@ Kas jätta taastamine vahele? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Kui jätad taastamise vahele, saad oma varukoopias olevat meediat ja manuseid alla laadida hiljem, kui salvestusruumi vabaneb. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Jäta allalaadimine vahele - "Sinu viimast varukoopiat ei saanud lõpule viia. Veendu, et sinu telefon on Wi-Figa ühendatud ning toksa Varunda nüüd, et uuesti proovida." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Kasutamata meedia laaditakse maha, kuid saad neid igal ajal oma varukoopiast alla laadida. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Salvestusruumi saab optimeerida ainult Signali tasulise varukoopiate tasemega. Sinu varukoopiate tellimus on töötlemisel ega pole veel aktiivne. Proovi hiljem uuesti. @@ -7718,6 +7752,8 @@ Uuenda Rohkem teavet + + Processing backup… Sul on %1$s varundatud andmeid, mis ei ole selles seadmes. Sinu varukoopia kustutatakse, kui su tellimus %2$d päeva pärast lõpeb. @@ -7738,6 +7774,8 @@ Varukoopiaid ei saanud välja lülitada ega kustutada Tekkis võrgu viga. Kontrolli oma internetiühendust ja proovi uuesti. + + Uploading messages… @@ -7777,7 +7815,7 @@ Sinu varukoopia võti - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Sinu varukoopia võti on 64-kohaline kood, mille abil saad varukoopia taastada, kui Signali uuesti paigaldad. Kui oma võtme unustad, ei ole sul võimalik oma varukoopiat taastada. Signal ei saa sul aidata varukoopiat taastada. @@ -7866,7 +7904,7 @@ Mul ei ole mu vana telefoni - Or you\'re reinstalling Signal on the same device + Või paigaldad Signalit uuesti samasse seadmesse Taasta või edasta konto @@ -7895,15 +7933,15 @@ Sisesta oma varukoopia võti - Your backup key is a 64-character code required to recover your account and data. + Sinu varukoopia võti on 64-kohaline kood, mida on vaja sinu andmete ja konto taastamiseks. Kas sul ei ole varukoopia võtit? Varukoopia võti - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Ilma 64-kohalise koodita ei saa varukoopiat taastada. Kui oled oma varukoopia võtme kaotanud, ei saa Signal sul aidata varukoopiat taastada. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Kui sul on vana seade alles, saad vaadata oma varukoopia võtit, kui valid Sätted > Varukoopiad. Seejärel vali Vaata varukoopia võtit. Rohkem teavet @@ -7952,6 +7990,18 @@ Olgu + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index d416da3774..d052962079 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -1011,6 +1011,20 @@ Saiatu berriro lotzen Egin aurrera transferitu gabe + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1038,11 +1052,11 @@ Transferitu mezu-historia - Transferitu testu-mezuak eta azkenaldiko multimedia-elementuak ordenagailura + Transfer your text messages and recent media to your linked device Ez transferitu - Ez da transferituko mezu zahar edo multimedia-elementu zaharrik ordenagailura + No old messages or media will be transferred to your linked device \"%1$s\" gailuari lotura kendu nahi diozu? @@ -1355,16 +1369,18 @@ Mezu guztiak Babeskopia berreskuratu - - Azken %1$d egunetan bidalitako edo jasotako multimedia-edukia bakarrik dauka. Babeskopian, hauek gordetzen dira: Berreskuratu babeskopia - + Azken babeskopia: %1$s (%2$s). + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Babeskopiaren xehetasunak lortzen… + + Skip restore Nahi dut aipamenak jakinaraztea @@ -2464,6 +2480,8 @@ Saiakera gehiegi egin dituzu zenbaki hau erregistratzeko. Saiatu berriro denbora hau igarotakoan: %1$s. Ezin da zerbitzura konektatu. Egiaztatu sarearen ezarpenak eta saiatu berriz. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Ezin izan dizugu egiaztapen-koderik bidali SMS bidez. Horren ordez, saiatu kodea ahots-dei bidez jasotzen. @@ -4311,6 +4329,8 @@ Leheneratu edo transferitu Transferitu kontua Saltatu + + Skip restore Txaten babeskopiak Transferitu kontua Transferitu kontua Android gailu berri batera @@ -7307,6 +7327,10 @@ Ez onartu Onartu + + Approve all + + Deny all Pantaila osoko jakinarazpenak aktibatu nahi dituzu? @@ -7482,6 +7506,12 @@ Leheneratzea saltatu nahi duzu? Leheneratze-prozesua saltatzen baduzu, babeskopiako multimedia-edukia eta eranskinak geroago deskarga ditzakezu, biltegian tokia egitean. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Saltatu deskarga - "Ezin izan da osatu azken babeskopia. Ziurtatu telefonoa wifira konektatuta dagoela eta, berriro saiatzeko, sakatu Egin babeskopia orain." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7718,6 +7752,8 @@ Berritu Informazio gehiago + + Processing backup… %1$s-ko babeskopia bat duzu gailu honetatik kanpo. Harpidetza %2$d egun barru amaitzean, babeskopia ezabatu egingo da. @@ -7738,6 +7774,8 @@ Ezin izan dira desaktibatu eta ezabatu babeskopiak Sareko errore bat gertatu da. Egiaztatu Internetera konektatuta zaudela eta saiatu berriro. + + Uploading messages… @@ -7952,6 +7990,18 @@ Ados + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index ad4ff4962f..2dc18e1791 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -1011,6 +1011,20 @@ دوباره اتصال را امتحان کنید ادامه بدون انتقال + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + انتقال تاریخچه پیام - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - انتقال ندادن + انتقال داده نشود - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device قطع ارتباط با «%1$s»؟ @@ -1355,16 +1369,18 @@ همه پیام‌های شما بازگردانی از پشتیبان - - فقط شامل رسانه ارسالی یا دریافتی در %1$d روز گذشته است. نسخه پشتیبان شما شامل موارد زیر است: بازگردانی پشتیبان - + آخرین پشتیبان‌گیری در %1$s در %2$s انجام شده است. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. در حال دریافت جزئیات نسخه پشتیبان… + + Skip restore برای اشاره‌ها من را آگاه کن @@ -2464,6 +2480,8 @@ دفعات تلاش‌تان برای ثبت این شماره بیش از حد مجاز بوده است. لطفاً بعد از %1$s دوباره تلاش کنید. اتصال به سرویس ممکن نیست. لطفاً اتصال شبکه را بررسی کرده و دوباره امتحان کنید. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. نتوانستیم کد تأییدی از طریق پیامک برایتان ارسال کنیم. به جای آن، دریافت کد از طریق تماس را امتحان کنید. @@ -4311,6 +4329,8 @@ بازیابی یا انتقال انتقال حساب کاربری رد کردن + + Skip restore پشتیبان‌های گفتگو انتقال حساب کاربری انتقال حساب کاربری به یک دستگاه اندروید‌ی جدید @@ -5823,9 +5843,9 @@ هنوز در حال پردازش نشان افزوده نشد - Something went wrong + خطایی رخ‌ داد - Your backups subscription couldn\'t be displayed. Please contact support. + اشتراک نسخه پشتیبان شما نمایش داده نشد. لطفاً با پشتیبانی تماس بگیرید. اعتبار نشان تایید نشد @@ -7216,7 +7236,7 @@ تغییرات ذخیره نشد. اتصال شبکه خود را بررسی کنید و دوباره امتحان کنید. - Couldn\'t delete call link as it is currently in use. + پیوند تماس حذف نشد، چون در حال استفاده است. پیوند حذف شود؟ @@ -7307,6 +7327,10 @@ رد کردن موافقت + + Approve all + + Deny all اعلان‌های تمام‌صفحه روشن شود؟ @@ -7481,7 +7505,13 @@ بازیابی رد شود؟ - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + اگر بازیابی رسانه‌ها و پیوست‌های باقیمانده را در پشتیبان‌گیری رد کنید، در آینده وقتی فضای ذخیره‌سازی کافی داشته باشید قابل دانلود است. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ رد کردن دانلود - "آخرین پشتیبان‌گیری شما انجام نشد. مطمئن شوید که تلفن‌تان به Wi-Fi وصل است و برای امتحان دوباره، روی «اکنون پشتیبان‌گیری شود» ضربه بزنید." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ رسانه‌های استفاده‌نشده پاک می‌شوند، اما می‌توانید هر زمانی که خواستید از نسخه پشتیبان خود دانلودشان کنید. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + بهینه‌سازی فضای ذخیره‌سازی فقط با نسخه پولی پشتیبان‌های سیگنال امکان‌پذیر است. اشتراک نسخه پشتیبان هنوز در حال پردازش است و فعال نیست. لطفاً بعداً دوباره امتحان کنید. @@ -7718,6 +7752,8 @@ تمدید اطلاعات بیشتر + + Processing backup… شما %1$s داده پشتیبان دارید که در این دستگاه وجود ندارد. پس از پایان اشتراکتان تا %2$d روز دیگر، نسخه پشتیبان شما حذف خواهد شد. @@ -7738,6 +7774,8 @@ پشتیبان‌گیری خاموش و حذف نشد خطای شبکه رخ داد. لطفاً اتصال اینترنت خود را بررسی و دوباره امتحان کنید. + + Uploading messages… @@ -7777,7 +7815,7 @@ کلید پشتیبان شما - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + رمز پشتیبان شما یک کد ۶۴ رقمی است که می‌توانید با آن هنگام نصب مجدد سیگنال، نسخه پشتیبان خود را بازیابی کنید. اگر کلید خود را فراموش کنید، نمی‌توانید نسخه پشتیبان خود را بازیابی کنید. سیگنال نمی‌تواند به شما کمک کند نسخه پشتیبان خود را بازیابی کنید. @@ -7866,7 +7904,7 @@ تلفن قدیمی‌ام را ندارم - Or you\'re reinstalling Signal on the same device + یا در حال نصب مجدد سیگنال روی همین دستگاه هستید بازیابی یا انتقال حساب @@ -7895,15 +7933,15 @@ رمز پشتیبان خود را وارد کنید - Your backup key is a 64-character code required to recover your account and data. + رمز پشتیبان شما یک کد ۶۴ نویسه‌ای است که برای بازیابی حساب و اطلاعاتتان الزامی است. رمز پشتیبان ندارید؟ رمز پشتیبان - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + نسخه پشتیبان بدون کد بازیابی ۶۴ نویسه‌ای قابل‌بازیابی نیست. اگر رمز پشتیبان خود را گم کرده‌اید، سیگنال نمی‌تواند به بازیابی نسخه پشتیبان شما کمک کند. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + اگر دستگاه قدیمی‌تان را دارید، می‌توانید رمز پشتیبان خود را در «تنظیمات > پشتیبان‌ها» مشاهده کنید. سپس روی «مشاهده رمز پشتیبان» ضربه بزنید. اطلاعات بیشتر @@ -7952,6 +7990,18 @@ تأیید + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index d7ea957b1a..6a989320f7 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -1011,6 +1011,20 @@ Yritä yhdistämistä uudelleen Jatka ilman siirtoa + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Siirrä viestihistoria - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Älä siirrä - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Poistetaanko %1$s? @@ -1355,16 +1369,18 @@ Kaikki viestisi Palauta varmuuskopiosta - - Mukana on vain viimeisten %1$d päivän aikana lähetetty tai vastaanotettu media. Varmuuskopio sisältää: Palauta varmuuskopio - + Viimeisin varmuuskopio tehtiin %1$s klo %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Haetaan varmuuskopion tietoja… + + Skip restore Ilmoita minulle maininnoista @@ -2464,6 +2480,8 @@ Olet yrittänyt rekisteröidä tämän numeron liian monta kertaa. Yritä uudelleen, kun on kulunut %1$s. Yhteyttä ei voitu muodostaa palveluun. Tarkista verkkoyhteytesi ja yritä uudelleen. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Emme voineet lähettää sinulle vahvistuskoodia tekstiviestillä. Yritä sen sijaan vastaanottaa koodi äänipuhelun välityksellä. @@ -4311,6 +4329,8 @@ Palauta tai siirrä Siirrä tili Ohita + + Skip restore Keskustelujen varmuuskopiot Siirrä tili Siirrä tili uuteen Android-laitteeseen @@ -5823,9 +5843,9 @@ Käsitellään edelleen Merkkiä ei voitu lisätä - Something went wrong + Jokin meni pieleen - Your backups subscription couldn\'t be displayed. Please contact support. + Varmuuskopioinnin tilaustasi ei voitu näyttää. Ota yhteys tukeen. Merkin vahvistaminen epäonnistui @@ -7216,7 +7236,7 @@ Muutoksia ei voitu tallentaa. Tarkista verkkoyhteys ja yritä uudelleen. - Couldn\'t delete call link as it is currently in use. + Puhelulinkkiä ei voitu poistaa, koska se on tällä hetkellä käytössä. Poistetaanko linkki? @@ -7307,6 +7327,10 @@ Hylkää Hyväksy + + Approve all + + Deny all Otetaanko koko näytön ilmoitukset käyttöön? @@ -7481,7 +7505,13 @@ Ohitetaanko palauttaminen? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Jos ohitat palauttamisen, varmuuskopion jäljellä olevat mediat ja liitteet voidaan ladata myöhemmin, kun tallennustilaa vapautuu. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Ohita lataus - "Viimeistä varmuuskopiointia ei voitu suorittaa loppuun. Varmista, että puhelimesi on yhdistetty Wi-Fi-verkkoon, ja yritä uudelleen napauttamalla Varmuuskopioi nyt." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Käyttämätön media poistetaan, mutta se voidaan ladata varmuuskopiosta milloin tahansa. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Tallennustilan optimointia voidaan käyttää vain Signalin maksullisen varmuuskopiointitilauksen yhteydessä. Varmuuskopiotilaustasi käsitellään, eikä se ole vielä aktiivinen. Yritä myöhemmin uudelleen. @@ -7718,6 +7752,8 @@ Uusi Lue lisää + + Processing backup… Sinulla on %1$s varmuuskopiodataa, jota ei ole tällä laitteella. Varmuuskopiosi poistetaan, kun tilauksesi päättyy %2$d päivän kuluttua. @@ -7738,6 +7774,8 @@ Varmuuskopioiden poiskytkentä ja poistaminen ei onnistunut Tapahtui verkkovirhe. Tarkista verkkoyhteytesi ja yritä uudelleen. + + Uploading messages… @@ -7777,7 +7815,7 @@ Varmuuskopion avain - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Varmuuskopion avain on 64 merkkiä sisältävä koodi, jonka avulla voit palauttaa varmuuskopion, kun asennat Signalin uudelleen. Jos unohdat avaimen, et voi palauttaa varmuuskopiota. Signal ei voi auttaa sinua palauttamaan varmuuskopiota. @@ -7866,7 +7904,7 @@ Minulla ei ole vanhaa puhelintani - Or you\'re reinstalling Signal on the same device + Tai jos asennat Signalia uudelleen samalle laitteelle Palauta tai siirrä tili @@ -7895,15 +7933,15 @@ Anna varmuuskopion avain - Your backup key is a 64-character code required to recover your account and data. + Varmuuskopion avain on 64 merkkiä sisältävä koodi, jota tarvitaan tilisi ja tietosi palauttamiseen. Eikö sinulla ole varmuuskopion avainta? Varmuuskopion avain - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Varmuuskopioita ei voi palauttaa ilman 64 merkkiä sisältävää palautuskoodia. Jos olet kadottanut varmuuskopion avaimen, Signal ei voi palauttaa varmuuskopiotasi. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Jos vanha laitteesi on tallella, löydät varmuuskopion avaimen kohdasta Asetukset > Varmuuskopiot. Napauta sitten Näytä varmuuskopion avain. Lue lisää @@ -7952,6 +7990,18 @@ Selvä + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 02550db7b6..d6ea56372c 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -48,8 +48,8 @@ Mise à jour de Signal… - Vous n’avez pas encore défini de phrase de passe. - Désactiver la phrase de passe ? + Vous n\'avez pas encore défini de phrase secrète. + Désactiver la phrase secrète ? Signal et les notifications de message seront accessibles en permanence. Désactiver Erreur de connexion au serveur. @@ -560,7 +560,7 @@ - Application Contacts introuvable. + L\'application Contacts est introuvable. Supprimer le message sélectionné ? Supprimer les messages sélectionnés ? @@ -754,14 +754,14 @@ Profil de notification - Activez ou désactivez vos profils de notification ici + Activez ou désactivez vos profils de notification ici. %1$s activé Votre code QR et votre lien ont été réinitialisés. Votre nom d’utilisateur est désormais %1$s - Message d’échange de clés + Message d\'échange de clés Conversations archivées (%1$d) @@ -787,15 +787,15 @@ Profil - Erreur de définition de la photo de profil - Problème de définition du profil + Impossible de définir la photo de profil + Problème de configuration du profil Configurez votre profil Vos contacts, vos groupes et les personnes avec qui vous échangez des messages peuvent voir votre profil et les modifications que vous y apportez. Choisir une photo de profil - Les profils sont visibles par vos interlocuteurs, vos contacts et vos groupes. + Votre profil sera accessible à vos contacts, à vos groupes et aux utilisateurs à qui vous écrivez. Qui peut me trouver grâce à mon numéro de téléphone ? @@ -808,24 +808,24 @@ Personne ne saura que vous utilisez Signal, hormis les utilisateurs à qui vous envoyez un message ou avec qui vous avez déjà démarré une conversation. - Restaurer d’une sauvegarde ? + Restaurer à partir d\'une sauvegarde ? Restaurez vos messages et médias depuis une sauvegarde locale. Si vous ne les restaurez pas maintenant, vous ne pourrez pas le faire plus tard. - Restaurez à partir de l’icône de sauvegarde + Icône \"Restaurer à partir d\'une sauvegarde\" Choisir une sauvegarde En savoir plus - Aucun navigateur de fichiers n’est proposé + Aucun explorateur de fichiers disponible - La restauration est terminée. - Pour continuer à utiliser les sauvegardes, veuillez choisir un dossier. Les nouvelles sauvegardes seront enregistrées dans cet emplacement. - Sélectionner un dossier + Restauration terminée. + Pour continuer d\'utiliser les sauvegardes, choisissez un dossier. Nous pourrons ainsi y enregistrer vos prochaines sauvegardes. + Choisir un dossier Plus tard - Sauvegarde non trouvée. + Sauvegarde introuvable. Impossible de lire la sauvegarde. - L’extension de la sauvegarde est incorrecte. + L\'extension de la sauvegarde est incorrecte. @@ -874,14 +874,14 @@ Sauvegarde des conversations - Les sauvegardes sont chiffrées avec une phrase de passe et stockées sur votre appareil. + Les sauvegardes sont chiffrées au moyen d\'une phrase secrète et stockées sur votre appareil. Créer une sauvegarde Dernière sauvegarde : %1$s Dossier de sauvegarde Heure de sauvegarde - Confirmer la phrase de passe de la sauvegarde - Essayez la phrase de passe de la sauvegarde et confirmez qu’elle correspond + Confirmer la phrase secrète de sauvegarde + Essayez la phrase secrète de sauvegarde pour vérifier qu\'elle est correcte Activer Désactiver "Pour restaurer une sauvegarde, réinstallez Signal. Ouvrez l\'appli, touchez \"Restaurer la sauvegarde\", puis recherchez votre fichier de sauvegarde. %1$s" @@ -889,9 +889,9 @@ En cours… Vérification de la sauvegarde… - %1$d jusqu’à présent… + %1$d jusqu\'ici… - %1$s%% jusqu’à présent… + %1$s %% jusqu\'ici… Pour créer des sauvegardes, Signal doit accéder au stockage externe, mais vous lui en avez interdit l\'accès. Ouvrez l\'application Paramètres de votre appareil, puis touchez Applications > Signal > Autorisations et activez l\'option \"Stockage\". Définir l’heure de sauvegarde @@ -903,13 +903,13 @@ Aucun - L’autorisation d’accès à l’appareil photo est requise pour prendre une photo. - L’autorisation Stockage est exigée pour afficher votre galerie. + Pour prendre une photo, autorisez Signal à accéder à l\'appareil photo. + Pour afficher la galerie, autorisez Signal à accéder au stockage. - À l’instant - %1$d min - Aujourd’hui + À l\'instant + %1$d min + Aujourd\'hui Hier %1$s à %2$s @@ -1011,6 +1011,20 @@ Réessayer Continuer sans transférer + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,40 +1050,40 @@ - Transfer message history + Transférer tous les messages - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Ne pas transférer + Ne pas les transférer - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Dissocier \"%1$s\" ? - En annulant le lien avec cet appareil, il ne pourra plus ni envoyer ni recevoir de messages. - Échec de connexion au réseau + Si vous dissociez cet appareil, il ne pourra plus ni envoyer, ni recevoir de messages. + Impossible de se connecter au réseau Réessayer - Annulation du lien avec l’appareil… - Annulation du lien avec l’appareil + Dissociation de l\'appareil… + Dissociation de l\'appareil Échec du réseau. Appareil sans nom Associé le %1$s Dernière activité %1$s - Aujourd’hui + Aujourd\'hui - Fichier sans nom + Fichier sans titre - Optimiser pour palier l’absence des Services Play + Optimiser pour le fonctionnement sans services Google Play Cet appareil ne prend pas en charge les services Google Play. Pour désactiver les optimisations système de la batterie qui empêchent Signal de récupérer les messages lorsque l\'application est inactive, appuyez ici. Votre version de Signal est arrivée à expiration. Pour échanger des messages, passez à la nouvelle version. - Mettre Signal à jour maintenant + Passez à la dernière version @@ -1079,12 +1093,12 @@ Afficher - Échec permanent de communication de Signal. - Signal n’a pas réussi à s’inscrire auprès des services Google Play. Les appels et messages Signal ont été désactivés. Veuillez essayer de vous réinscrire dans Paramètres > Avancé. + Échec permanent de la connexion + Connexion aux services Google Play impossible. Les appels et messages Signal ont été désactivés. Nous vous invitons à vous réinscrire sous Paramètres > Avancé. - Erreur de récupération du GIF à pleine résolution + Une erreur s\'est produite lors de la récupération du GIF haute résolution @@ -1094,22 +1108,22 @@ %1$s a été ajouté à \"%2$s\". Ajouter au groupe Ajouter aux groupes - Cette personne ne peut pas être ajoutée à des groupes hérités. + Vous ne pouvez pas ajouter cet utilisateur à un groupe hérité. Ajouter Ajouter à un groupe - Choisir un nouvel administrateur - Terminé + Choisir un nouvel admin + OK Vous avez quitté \"%1$s\". Vous - Quiconque + Tout le monde Tous les membres - Les administrateurs + Seulement les admins Personne @@ -1131,8 +1145,8 @@ Ces utilisateurs ne peuvent pas être ajoutés automatiquement par vous à ce groupe.\n\nDes invitations ont été envoyées et ces utilisateurs ne verront aucun message du groupe tant que les invitations n’auront pas été acceptées. - Que sont les Nouveaux groupes ? - Les Nouveaux groupes offrent des fonctions telles que les @mentions et les administrateurs de groupes et en offriront d’autres dans le futur. + Les \"nouveaux groupes\", qu\'est-ce que c\'est ? + Les nouveaux groupes proposent des fonctionnalités comme les @mentions et les admins – et en compteront bien d\'autres bientôt. Tout l’historique des messages et tous les médias d’avant la conversion ont été conservés. Pour réintégrer ce groupe, vous devrez accepter une invitation. Vous ne recevrez aucun message du groupe tant que vous ne l’aurez pas acceptée. @@ -1145,12 +1159,12 @@ - Convertir en Nouveau groupe + Passer aux nouveaux groupes Convertir ce groupe - Les Nouveaux groupes offrent des fonctions telles que les @mentions et les administrateurs de groupes et en offriront d’autres dans le futur. + Les nouveaux groupes proposent des fonctionnalités comme les @mentions et les admins – et en compteront bien d\'autres bientôt. Tout l’historique des messages et tous les médias d’avant la conversion seront conservés. Une erreur réseau s’est produite. Veuillez réessayer plus tard. - Échec de conversion + Impossible de convertir le groupe Pour réintégrer le groupe, cette personne devra accepter une invitation. Elle ne recevra aucun message du groupe tant qu’elle ne l’aura pas acceptée: Pour réintégrer le groupe, ces personnes devront accepter une invitation. Elles ne recevront aucun message du groupe tant qu’elles ne l’auront pas acceptée : @@ -1185,24 +1199,24 @@ Ajouter des membres - Échec d’ajout du membre. Veuillez réessayer plus tard. - Échec d’ajout des membres. Veuillez réessayer plus tard. + Impossible d\'ajouter ce membre pour le moment. Veuillez réessayer plus tard. + Impossible d\'ajouter des membres pour le moment. Veuillez réessayer plus tard. - Impossible d’ajouter un membre. - Impossible d’ajouter des membres. + Impossible d\'ajouter un membre. + Impossible d\'ajouter des membres. Quitter le groupe ? - Vous ne pourrez plus ni recevoir ni envoyer de messages dans ce groupe. + Vous ne pourrez plus échanger de messages avec ce groupe. Quitter - Choisir un nouvel administrateur - Avant de quitter le groupe, vous devez choisir au moins un nouvel administrateur pour ce groupe. - Choisir un administrateur + Choisir un nouvel admin + Avant de quitter le groupe, vous devez choisir au moins un nouvel admin. + Choisir un admin - Aucun aperçu du lien n’est proposé + Aucun aperçu de lien n\'est disponible Ce lien de groupe est inactif %1$s · %2$s @@ -1256,9 +1270,9 @@ Demandes Invitations Personnes que vous avez invitées - Vous n’avez aucune invitation en attente. - Invitations par d’autres membres du groupe - Il n’y a aucune invitation par d’autres membres du groupe en attente. + Vous n\'avez aucune invitation en attente. + Invitations envoyées par d\'autres membres + Aucune invitation envoyée par d\'autres membres n\'est en attente. Nous n’affichons pas les infos relatives aux personnes invitées par des membres du groupe. Ces infos ne sont communiquées au groupe que si les invités choisissent de le rejoindre. Ces derniers n’ont accès à aucun des messages publiés dans le groupe tant qu’ils ne l’ont pas rejoint. Annuler l\'invitation @@ -1268,22 +1282,22 @@ Annuler %1$d invitations - Erreur de révocation de l\'invitation - Erreur de révocation des invitations + Une erreur s\'est produite lors de l\'annulation de l\'invitation + Une erreur s\'est produite lors de l\'annulation des invitations - Demandes de membres en attente - Il n’y a aucune demande de membre à afficher. + Demandes d\'adhésion en attente + Aucune demande d\'adhésion à afficher. Ces utilisateurs tentent de rejoindre le groupe via le lien du groupe. "%1$s a été ajouté." "%1$s a été refusé." - Terminé - Cette personne ne peut pas être ajoutée à des groupes hérités. + OK + Vous ne pouvez pas ajouter cet utilisateur à un groupe hérité. - Ajouter \"%1$s\" à \"%2$s\" ? + Ajouter %1$s à \"%2$s\" ? Ajouter %3$d membres à \"%2$s\" ? Ajouter @@ -1294,14 +1308,14 @@ Créer un groupe Créer Membres - Vous pouvez ajouter ou inviter des amis après avoir créé ce groupe. - Nom du groupe (requis) + Après avoir créé ce groupe, vous pourrez y ajouter ou y inviter des amis. + Nom du groupe (obligatoire) Nom du groupe (facultatif) Ce champ est obligatoire. - Échec de création du groupe. - Réessayez plus tard. + Impossible de créer le groupe. + Veuillez réessayer plus tard. Retirer - Contact texto + Contact SMS Retirer %1$s de ce groupe ? Le contact sélectionné ne peut pas utiliser les groupes Signal. Les échanges de ce groupe se feront donc par MMS. Vous seul pourrez afficher le nom personnalisé du groupe et ses photos. @@ -1321,7 +1335,7 @@ Un des membres que vous avez ajoutés ne peut utiliser les Nouveaux groupes et doit installer la dernière version de Signal. Un des membres que vous avez ajoutés ne peut utiliser les groupes de diffusion et doit installer la dernière version de Signal. Échec de la mise à jour du groupe. - Vous n’êtes pas un membre du groupe + Vous ne faites pas partie de ce groupe Échec de la mise à jour du groupe. Veuillez réessayer plus tard. Échec de la mise à jour du groupe en raison d’une erreur réseau. Veuillez réessayer plus tard. @@ -1330,11 +1344,11 @@ Ce groupe est un groupe hérité. Des fonctionnalités telles que l’administration des groupes ne sont proposées que pour les nouveaux groupes. - Ce groupe est un groupe hérité. Pour accéder aux nouvelles fonctions telles que les @mentions et l’administration, + Ce groupe est un groupe hérité. Pour accéder à de nouvelles fonctionnalités, telles que les @mentions et les admins, Ce groupe hérité ne peut pas être converti en Nouveau groupe, car il comporte un trop grand nombre de membres. Un groupe ne peut compter plus de %1$d membres. - convertissez ce groupe + passez aux nouveaux groupes. Ce groupe est un groupe de messages multimédias non sécurisé. Pour converser en toute confidentialité, invitez vos contacts sur Signal. - Inviter maintenant + Inviter plus Présenter le groupe en quelques mots… @@ -1355,19 +1369,21 @@ tous vos messages Restaurer depuis une sauvegarde - - Elle ne contient que les médias envoyés ou reçus %1$d jours avant cette date. Votre sauvegarde contient : Restaurer la sauvegarde - + Dernière sauvegarde effectuée le %1$s à %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Récupération des infos de la sauvegarde… + + Skip restore - Me signaler les mentions + Me prévenir quand on me mentionne Recevoir des notifications lorsqu\'on vous mentionne dans des conversations mises en sourdine ? Toujours me prévenir Ne pas me prévenir @@ -1378,7 +1394,7 @@ Vos amis peuvent désormais vous contacter grâce à votre nom d’utilisateur facultatif, ce qui vous évite de partager votre numéro de téléphone. Nom de profil - Nom d’utilisateur + Nom d\'utilisateur À propos Impossible de définir la photo de profil Macarons @@ -1427,14 +1443,14 @@ Notifications personnalisées Messages Utiliser des notifications personnalisées - Son de notification - Vibrer + Son des notifications + Vibreur Personnaliser - Modifier le son et les vibrations + Modifier les paramètres de son et de vibreur Paramètres des appels Sonnerie - Valeur par défaut + Par défaut Inconnu @@ -1458,20 +1474,20 @@ - Vous êtes déjà membre + Vous faites déjà partie de ce groupe Rejoindre Demander à rejoindre le groupe Impossible de rejoindre le groupe. Veuillez réessayer plus tard. - Une erreur réseau est survenue. + Une erreur réseau s\'est produite. Ce lien de groupe est inactif Impossible de rejoindre le groupe. Impossible de rejoindre le groupe via le lien, car un administrateur vous a retiré du groupe. - Ce lien de groupe n’est plus valide. + Ce lien de groupe n\'est plus valide. - Erreur de lien + Le lien a généré une erreur Impossible de rejoindre le groupe via ce lien pour le moment. Veuillez réessayer plus tard. @@ -1486,19 +1502,19 @@ Pour utiliser les liens de groupe, installez la dernière version de Signal. La version de Signal que vous utilisez ne prend pas en charge ce lien de groupe. Pour rejoindre le groupe via ce lien, installez la dernière version de Signal. Installez la dernière version de Signal - Le lien de groupe est invalide + Le lien de groupe n\'est pas valide Inviter des amis Partagez un lien avec vos amis pour les inviter à rejoindre ce groupe en un clin d’œil. - Activer et partager le lien + Activer le lien et le partager Partager le lien - Impossible d’activer le lien de groupe. Veuillez réessayer plus tard - Une erreur réseau est survenue. - Vous n’avez pas le droit d’activer le lien du groupe. Veuillez demander à un administrateur. - Vous n’êtes actuellement pas membre du groupe. + Impossible d\'activer le lien de groupe. Veuillez réessayer plus tard + Une erreur réseau s\'est produite. + Vous n\'êtes pas autorisé à activer le lien du groupe. Veuillez demander à un admin. + Vous ne faites actuellement pas partie de ce groupe. Ajouter %1$s au groupe ? @@ -1509,10 +1525,10 @@ Refuser - Rendre les visages flous - Nouveau : Rendez les visages flous ou dessinez n’importe où pour les rendre flous. - Dessinez n’importe où pour rendre flou. - Dessinez pour rendre d’autres visages et zones flous + Flouter les visages + Nouveauté : floutez les visages ou n\'importe quelle zone de vos photos. + Dessinez sur n\'importe quelle zone pour la flouter. + Dessinez sur n\'importe quelle zone ou sur d\'autres visages pour les flouter. Appuyer longuement : enregistrer. Relâcher : envoyer. @@ -1532,16 +1548,16 @@ Annuler Envoi… - L’invitation a été envoyée. + Invitation envoyée. Inviter sur Signal - Envoyer un texto (%1$d) + Envoyer un SMS (%1$d) Envoyer %1$d invitation par SMS ? Envoyer %1$d invitations par SMS ? Et si on passait à Signal ? %1$s - Vous semblez n’avoir aucune appli vers laquelle partager. + Aucune appli disponible pour le partage. En savoir plus @@ -1549,7 +1565,7 @@ Lire la suite - Impossible de trouver le message. + Message introuvable. Message de %1$s Votre message @@ -1578,8 +1594,8 @@ Le plus ancien Espace de stockage utilisé Utilisation totale de l\'espace de stockage - Vue en grille - Vue en liste + Affichage en grille + Affichage en liste Sélectionné Tout sélectionner @@ -1623,28 +1639,28 @@ Réponses - Établissement de l’appel Signal + Établissement de l\'appel Signal Démarrage du service d\'appel Signal - Arrêt du service d’appel de Signal - Annuler l’appel + Arrêt du service d\'appel Signal + Annuler l\'appel Activer les notifications ? - Ne manquez jamais un message de vos contacts ni de vos groupes. + Ne manquez pas les messages de vos contacts et de vos groupes. Activer Plus tard Message multimédia - Téléchargement du message multimédia - Erreur de téléchargement du message multimédia. Touchez pour réessayer + Téléchargement du MMS + Une erreur s\'est produite lors du téléchargement du MMS. Appuyez pour réessayer. - Ouvrir l’appareil photo + Ouvrir l\'appareil photo - L’appareil photo n’est pas disponible + Appareil photo non disponible. Tous les médias @@ -1652,9 +1668,9 @@ Inconnu - Vous avez reçu un message chiffré avec une ancienne version de Signal qui n’est plus prise en charge. Veuillez demander à l’expéditeur de mettre Signal à jour vers la version la plus récente et de renvoyer le message. - Vous avez quitté le groupe - Vous avez mis le groupe à jour. + Vous avez reçu un message chiffré avec une ancienne version de Signal. Cette version n\'est plus prise en charge. Demandez à l\'expéditeur de mettre à jour Signal et de renvoyer son message. + Vous avez quitté le groupe. + Vous avez mis à jour le groupe. Le groupe a été mis à jour. Appel vocal sortant @@ -1703,8 +1719,8 @@ Vous avez créé le groupe. - Le groupe a été mis à jour. - Invitez des amis à ce groupe grâce à un lien de groupe + Groupe mis à jour. + Invitez des amis à rejoindre ce groupe via un lien de groupe Vous avez ajouté %1$s. @@ -1720,31 +1736,31 @@ Vous avez quitté le groupe. %1$s a quitté le groupe. Vous ne faites plus partie du groupe. - %1$s n’est plus dans le groupe. + %1$s ne fait plus partie du groupe. - Vous avez nommé %1$s en tant qu’administrateur. - %1$s a nommé %2$s en tant qu’administrateur. - %1$s vous a nommé administrateur. + Vous avez accordé des droits d\'admin à %1$s. + %1$s a accordé des droits d\'admin à %2$s. + %1$s vous a accordé des droits d\'admin. Vous avez révoqué les privilèges d\'admin de %1$s. %1$s a révoqué vos privilèges d\'admin. %1$s a révoqué les privilèges d\'admin de %2$s. - %1$s est désormais administrateur. - Vous êtes désormais administrateur. - %1$s n’est plus administrateur. - Vous n’êtes plus administrateur. + %1$s est maintenant admin. + Vous êtes maintenant admin. + %1$s n\'est plus admin. + Vous n\'êtes plus admin. - Vous avez invité %1$s au groupe. - %1$s vous a invité au groupe. + Vous avez invité %1$s à rejoindre le groupe. + %1$s vous invite à rejoindre le groupe. - %1$s a invité 1 personne au groupe. - %1$s a invité %2$d personnes au groupe. + %1$s a invité 1 personne à rejoindre le groupe. + %1$s a invité %2$d personnes à rejoindre le groupe. - Vous avez été invité au groupe. + Vous avez reçu une invitation à rejoindre le groupe. - 1 personne a été invitée au groupe. - %1$d personnes ont été invitées au groupe. + 1 personne a été invitée à rejoindre le groupe. + %1$d personnes ont été invitées à rejoindre le groupe. @@ -1756,8 +1772,8 @@ %1$s a annulé une invitation au groupe. %1$s a annulé %2$d invitations au groupe. - Quelqu’un a décliné une invitation au groupe. - Vous avez décliné l’invitation au groupe. + Un utilisateur a refusé une invitation à rejoindre le groupe. + Vous avez refusé l\'invitation à rejoindre le groupe. %1$s a annulé votre invitation au groupe. Un admin a annulé votre invitation au groupe. @@ -1766,8 +1782,8 @@ - Vous avez accepté l’invitation du groupe. - %1$s a accepté une invitation au groupe. + Vous avez accepté l\'invitation à rejoindre le groupe. + %1$s a accepté une invitation à rejoindre le groupe. Vous avez ajouté le membre invité %1$s. %1$s a ajouté le membre invité %2$s. @@ -1787,9 +1803,9 @@ La photo du groupe a été changée. - Vous avez défini l\'autorisation de modifier les informations du groupe sur \"%1$s\". - %1$s a défini l\'autorisation de modifier les informations du groupe sur \"%2$s\". - L\'autorisation de modifier les informations du groupe a été définie sur \"%1$s\". + Vous avez défini l\'autorisation de modifier les infos du groupe sur \"%1$s\". + %1$s a défini l\'autorisation de modifier les infos du groupe sur \"%2$s\". + L\'autorisation de modifier les infos du groupe a été définie sur \"%1$s\". Vous avez défini l\'autorisation d\'ajouter des membres sur \"%1$s\". @@ -1801,25 +1817,25 @@ Vous avez modifié les paramètres du groupe : seuls les admins peuvent maintenant envoyer des messages. %1$s a modifié les paramètres du groupe : tous les membres peuvent maintenant envoyer des messages. %1$s a modifié les paramètres du groupe : seuls les admins peuvent maintenant envoyer des messages. - Les paramètres du groupe ont été changés afin d’autoriser tous les membres à envoyer des messages. - Les paramètres du groupe ont été changés pour n’autoriser que les administrateurs à envoyer des messages. + Modification des paramètres du groupe : tous les membres peuvent envoyer des messages. + Modification des paramètres du groupe : seuls les admins peuvent envoyer des messages. - Vous avez activé le lien du groupe et l’approbation d’un administrateur est désactivée. - Vous avez activé le lien du groupe et l’approbation d’un administrateur est activée. + Vous avez activé le lien du groupe sans exiger l\'approbation des nouveaux membres par un admin. + Vous avez activé le lien du groupe et exigé l\'approbation des nouveaux membres par un admin. Vous avez désactivé le lien du groupe. - %1$s a activé le lien du groupe et l’approbation d’un administrateur est désactivée. - %1$s a activé le lien du groupe et l’approbation d’un administrateur est activée. + %1$s a activé le lien du groupe sans exiger l\'approbation des nouveaux membres par un admin. + %1$s a activé le lien du groupe et exigé l\'approbation des nouveaux membres par un admin. %1$s a désactivé le lien du groupe. - Le lien du groupe a été activé et l’approbation d’un administrateur est désactivée. - Le lien du groupe a été activé et l’approbation d’un administrateur est activée. + Le lien du groupe a été activé sans exiger l\'approbation des nouveaux membres par un admin. + Le lien du groupe est activé et exige l\'approbation des nouveaux membres par un admin. Le lien du groupe a été désactivé. - Vous avez désactivé l’approbation d’un administrateur pour le lien du groupe. - %1$s a désactivé l’approbation d’un administrateur pour le lien du groupe. - L’approbation d’un administrateur pour le lien du groupe a été désactivée. - Vous avez activé l’approbation d’un administrateur pour le lien du groupe. - %1$s a activé l’approbation d’un administrateur pour le lien du groupe. - L’approbation d’un administrateur a été activée pour le lien du groupe. + Vous avez désactivé l\'approbation des adhésions via le lien du groupe. + %1$s a désactivé l\'approbation des adhésions via le lien du groupe. + L\'approbation des adhésions via le lien du groupe a été désactivée. + Vous avez activé l\'approbation des adhésions via le lien du groupe. + %1$s a activé l\'approbation des adhésions via le lien du groupe. + L\'approbation des adhésions via le lien du groupe a été activée. Vous avez réinitialisé le lien du groupe. @@ -1860,8 +1876,8 @@ Vous avez confirmé le numéro de sécurité associé à %1$s depuis un autre appareil. Vous avez marqué le numéro de sécurité associé à %1$s comme non confirmé. Vous avez marqué le numéro de sécurité associé à %1$s comme non confirmé depuis un autre appareil. - Un message de %1$s n’a pas pu être remis - %1$s a changé de numéro de téléphone + Impossible de distribuer un message de %1$s. + %1$s a changé de numéro de téléphone. Vous aimez cette nouvelle fonctionnalité ? Faites un don ponctuel pour soutenir Signal. @@ -1954,15 +1970,15 @@ Accepter - Poursuivre + Continuer Supprimer Bloquer Débloquer - Autoriser %1$s à vous envoyer des messages et partager vos nom et photo avec ce contact ? Vous avez précédemment supprimé ce contact. - Autoriser %1$s à échanger des messages et partager votre nom et votre photo avec ce contact ? Ce contact ne saura pas que vous avez vu ses messages tant que vous n’aurez pas accepté. + Partager votre nom et votre photo avec %1$s et l\'autoriser à échanger des messages avec vous ? Il s\'agit d\'un contact que vous aviez supprimé. + Partager votre nom et votre photo avec %1$s et l\'autoriser à échanger des messages avec vous ? Cet utilisateur ne saura pas que vous avez vu ses messages tant que vous n\'aurez pas accepté. - Autoriser %1$s à vous envoyer des messages et partager vos nom et photo avec ce contact ? Vous ne recevrez aucun message tant que vous ne l’aurez pas débloqué. + Partager votre nom et votre photo avec %1$s et l\'autoriser à échanger des messages avec vous ? Vous ne recevrez aucun message de ce contact tant que vous ne l\'aurez pas débloqué. Autoriser %1$s à vous envoyer un message ? Vous ne recevrez aucun message tant que vous ne l’aurez pas débloqué. @@ -1996,31 +2012,31 @@ - %1$d groupe supplémentaire - %1$d groupes supplémentaires + %1$d autre groupe + %1$d autres groupes Signaler - Les phrases de passe ne correspondent pas. - L’ancienne phrase de passe est erronée. - Saisissez la nouvelle phrase de passe. + Les phrases secrètes ne correspondent pas. + Ancienne phrase secrète incorrecte. + Saisissez la nouvelle phrase secrète. Associer cet appareil ? Poursuivre - Il pourra + Cet appareil pourra - • Lire tous vos messages \n• Envoyer des messages en votre nom + • Lire tous vos messages \n• Envoyer des messages via votre profil Association de l’appareil Association du nouvel appareil… - L’appareil a été approuvé. - Aucun appareil n’a été trouvé. + Appareil approuvé. + Appareil introuvable. Erreur réseau. - Le code QR est invalide + Code QR incorrect. Vous avez déjà associé un trop grand nombre d’appareils. Veuillez en supprimer quelques-uns. Le code QR utilisé pour associer l’appareil n’est pas un code valide. @@ -2028,21 +2044,21 @@ Il semble que vous tentiez d’associer un appareil Signal via un lecteur tiers. Pour votre sécurité, veuillez rescanner le code à partir de Signal. Pour scanner un code QR, Signal doit accéder à l\'appareil photo, mais vous lui en avez interdit l\'accès. Ouvrez l\'application Paramètres de votre appareil, puis touchez Applications > Signal > Autorisations et activez \"Appareil photo\". - Impossible de lire un code QR sans l’autorisation Appareil photo + Impossible de scanner un code QR sans l\'autorisation \"Appareil photo\". - Mettre Signal à jour maintenant - Cette version de Signal expirera aujourd’hui. Passez à la version la plus récente. + Passez à la dernière version de Signal + Cette version de Signal arrive à expiration aujourd\'hui. Passez à dernière la version. - Cette version de Signal expirera demain. Passez à la version la plus récente. - Cette version de Signal expirera dans %1$d jours. Passez à la version la plus récente. + Cette version de Signal arrivera à expiration demain. Passez à dernière la version. + Cette version de Signal arrivera à expiration dans %1$d jours. Passez à dernière la version. - Saisissez la phrase de passe + Saisissez la phrase secrète Icône de Signal - Envoyer la phrase de passe - La phrase de passe est erronée. + Envoyer la phrase secrète + Phrase secrète incorrecte. Déverrouiller Signal Signal pour Android – Écran de verrouillage @@ -2051,25 +2067,25 @@ Carte - Marquer - Accepter l’adresse + Placer un repère + Accepter l\'adresse - La version des services Google Play installée ne fonctionne pas correctement. Veuillez réinstaller les Services Google Play et réessayer. + La version des services Google Play installée sur votre appareil ne fonctionne pas correctement. Veuillez réinstaller les services Google Play et réessayer. Code PIN incorrect. Ne pas saisir de code PIN ? Besoin d’aide ? - Votre code PIN est un code numérique ou alphanumérique que vous avez créé et qui comporte %1$d caractères ou plus.\n\n Si vous l’avez oublié, vous pouvez en créer un nouveau. Vous pourrez vous inscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés, tels que les informations de profil. - Si vous ne vous souvenez pas de votre code PIN, vous pouvez en créer un nouveau. Vous pourrez vous inscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés, tels que les informations de profil. + Votre PIN est un code numérique ou alphanumérique d\'au moins %1$d caractères, que vous avez créé.\n\n Si vous l\'avez oublié, vous pouvez en créer un nouveau. Vous pourrez vous réinscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés, tels que les informations de profil. + Si vous ne vous souvenez pas de votre code PIN, vous pouvez en créer un nouveau. Vous pourrez vous réinscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés, tels que les informations de profil. Créer un nouveau PIN - Contacter l’assistance + Contacter l\'assistance Annuler Ignorer - Il vous reste %1$d essai. Si vous épuisez le nombre limite d’essais, vous pouvez créer un nouveau PIN. Vous pouvez vous inscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés tels que les informations de votre profil. - Il vous reste %1$d essais. Si vous épuisez le nombre limite d’essais, vous pouvez créer un nouveau PIN. Vous pouvez vous inscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés tels que les informations de votre profil. + Il vous reste %1$d tentative. Si vous dépassez le nombre de tentatives autorisé, vous pouvez créer un nouveau code PIN. Vous pourrez vous réinscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés, tels que les informations de profil. + Il vous reste %1$d tentative. Si vous dépassez le nombre de tentatives autorisé, vous pouvez créer un nouveau code PIN. Vous pourrez vous réinscrire et utiliser votre compte, mais vous perdrez certains paramètres enregistrés, tels que les informations de profil. Inscription à Signal – Besoin d’aide : code PIN sous Android @@ -2097,13 +2113,13 @@ Notez cette appli - Si vous aimez cette appli, veuillez prendre quelques instants pour nous aider en la notant. - Notez Signal maintenant. + Vous aimez cette appli ? N\'hésitez pas à prendre quelques instants pour la noter. Vous contribuerez ainsi à soutenir Signal. + Notez Signal. Non merci Plus tard - Tous · %1$d + Toutes · %1$d +%1$d @@ -2112,9 +2128,9 @@ Vous - Confirmer pour continuer à communiquer - Pour aider à prévenir le spam sur Signal, veuillez procéder à la confirmation. - Après la confirmation, vous pourrez continuer à communiquer. Tout les messages en attente seront automatiquement envoyés. + Confirmer pour continuer à échanger des messages + Pour nous aider à empêcher le spam sur Signal, merci de confirmer le reCAPTCHA. + Vous pourrez reprendre vos échanges sur Signal une fois le reCAPTCHA confirmé. Tous vos messages en attente seront automatiquement envoyés. Vous @@ -2134,11 +2150,11 @@ Fin de l’appel… Sonnerie… Occupé - Le destinataire n’est pas disponible + La personne que vous appelez n\'est pas disponible Erreur réseau. Veuillez vérifier votre connexion réseau et réessayer. - Le numéro n’est pas inscrit. - Le numéro que vous avez composé ne prend pas en charge les communications vocales sécurisées. + Numéro non inscrit + Attention : le numéro composé ne prend pas en charge les communications vocales sécurisées. J’ai compris @@ -2156,8 +2172,8 @@ Votre caméra est désactivée - Touchez ici pour activer votre vidéo - Pour appeler %1$s, Signal a besoin d’accéder à votre appareil photo + Touchez l\'écran pour activer la vidéo + Pour appeler %1$s, Signal doit accéder à l\'appareil photo Signal %1$s Appel… @@ -2238,12 +2254,12 @@ Appel vidéo Signal Lancer un appel Rejoindre l’appel - L’appel est complet - Le nombre maximal de %1$d participants a été atteint pour cet appel. Veuillez réessayer plus tard. - La vidéo est désactivée + L\'appel est complet + Cet appel a atteint le nombre maximal de %1$d participants. Veuillez réessayer plus tard. + Votre vidéo est désactivée Reconnexion… Connexion… - L’application est déconnectée + Déconnecté Lien de l’appel Signal @@ -2253,25 +2269,25 @@ En attente d’admission… - Signal sonnera chez %1$s - Signal sonnera chez %1$s et %2$s + Signal va appeler %1$s + Signal va appeler %1$s et %2$s - Signal sonnera chez %1$s, %2$s et %3$d autre - Signal sonnera chez %1$s, %2$s et %3$d autres + Signal va appeler %1$s, %2$s et %3$d autre contact + Signal va appeler %1$s, %2$s et %3$d autres contacts - %1$s sera notifié - %1$s et %2$s seront notifiés + %1$s recevra une notification + %1$s et %2$s recevront une notification - %1$s, %2$s et %3$d autre sera notifié - %1$s, %2$s et %3$d autres seront notifiés + %1$s, %2$s et %3$d autre recevront une notification + %1$s, %2$s et %3$d autres recevront une notification - Sonne chez %1$s - Sonne chez %1$s et %2$s + appelle %1$s + appelle %1$s et %2$s - Sonne chez %1$s, %2$s et %3$d autre - Sonne chez %1$s, %2$s et %3$d autres + appelle %1$s, %2$s et %3$d autre contact + appelle %1$s, %2$s et %3$d autres contacts %1$s vous appelle @@ -2282,11 +2298,11 @@ %1$s vous appelle avec %2$s, %3$s et %4$d autres - Il n’y a personne d’autre ici - %1$s est dans cet appel + Aucun autre participant + %1$s participe à cet appel - %1$s sont dans cet appel - %1$s et %2$s font partie de cet appel + Participants : %1$s + %1$s et %2$s participent à cet appel %1$s, %2$s et %3$d autre personne font partie de cet appel @@ -2302,7 +2318,7 @@ Activer ou désactiver la sourdine - Actions supplémentaires + Autres actions Raccrocher @@ -2415,14 +2431,14 @@ %1$s est bloqué - Plus de précisions + Plus d\'infos Vous ne recevez ni l’audio, ni la vidéo de cet utilisateur et il ne reçoit pas les vôtres. Impossible de recevoir l’audio et la vidéo de %1$s Impossible de recevoir l’audio et la vidéo de %1$s Cette personne n’a peut-être pas confirmé le changement de votre numéro de sécurité, il y a peut-être un problème avec son appareil ou elle vous a bloqué. - Balayez pour visualiser le partage d’écran + Balayez pour afficher le partage d\'écran Serveur proxy @@ -2432,45 +2448,47 @@ Vous êtes bien connecté au proxy. - Échec de l’envoi + Échec de l\'envoi Procéder à la confirmation - Sélectionner votre pays - Vous devez indiquer votre indicatif de pays + Sélectionnez votre pays + Indiquez l\'indicatif de votre pays Veuillez saisir un numéro de téléphone valide pour vous inscrire. - Le numéro est invalide - Le numéro que vous avez indiqué (%1$s) n’est pas valide. + Numéro incorrect + Le numéro indiqué (%1$s) n\'est pas valide. Le numéro de téléphone ci-dessous est-il correct ? - Vérification supplémentaire requise + Encore une petite vérification… - Un code de vérification sera envoyé à ce numéro. Votre opération pourrait vous facturer des frais supplémentaires. - Vous recevrez un appel afin de confirmer ce numéro. + Nous allons vous envoyer un code de vérification à ce numéro. Des frais d\'opérateur peuvent s\'appliquer. + Nous vous appellerons à ce numéro pour le confirmer. Modifier le numéro - Absence des services Google Play - Cet appareil ne dispose pas des services Google Play. Vous pouvez quand même utiliser Signal, mais cette configuration pourrait entraîner une fiabilité ou des performances moindres.\n\nSi vous n’êtes pas un utilisateur expérimenté, que vous n’utilisez pas une image Android de remplacement ou si vous pensez voir cela par erreur, veuillez contacter support@signal.org pour obtenir une aide au dépannage. + Services Google Play indisponibles + Les services Google Play ne sont pas disponibles sur cet appareil. Vous pouvez quand même utiliser Signal, mais cette configuration risque d\'entraîner une perte de fiabilité ou de performances.\n\nSi vous n\'êtes pas un utilisateur de niveau avancé, que vous n\'utilisez pas une ROM Android personnalisée ou si vous pensez que ce message s\'affiche par erreur, veuillez contacter support@signal.org pour obtenir de l\'aide. Je comprends - Erreur des Services Google Play + Erreur des services Google Play Les services Google Play sont en cours de mise à jour ou temporairement indisponibles. Veuillez réessayer. Conditions générales et règles de confidentialité Pour vous permettre de contacter vos amis et d\'envoyer des messages, Signal a besoin des autorisations \"Contacts\" et \"Médias\". Vos contacts sont importés grâce au service de découverte confidentielle de Signal : autrement dit, ils sont chiffrés de bout en bout et nous n\'y avons jamais accès. Pour vous permettre de contacter vos amis, Signal a besoin de l\'autorisation \"Contacts\". Vos contacts sont importés grâce au service de découverte confidentielle de Signal : autrement dit, ils sont chiffrés de bout en bout et nous n\'y avons jamais accès. - Vous avez fait trop d’essais pour inscrire ce numéro. Veuillez réessayer plus tard. + Vous avez dépassé le nombre de tentatives autorisées pour inscrire ce numéro. Veuillez réessayer plus tard. Vous avez atteint le nombre maximal de tentatives d’inscription de ce numéro. Veuillez réessayer dans %1$s. Impossible de se connecter au service. Veuillez vérifier la connexion réseau et réessayer. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Nous n’avons pas pu vous envoyer un code de vérification par SMS. Demandez plutôt de recevoir votre code par appel vocal. Demande de code de vérification impossible. Vérifiez votre connexion réseau puis réessayez. - Format de numéro non standard + Format de numéro inhabituel - Le numéro que vous avez saisi (%1$s) semble avoir un format atypique.\n\nSerait-ce plutôt %2$s ? + Le format du numéro saisi (%1$s) est inhabituel.\n\nNe s\'agit-il pas plutôt du numéro suivant : %2$s ? Signal pour Android – Format de numéro de téléphone Appel demandé @@ -2479,25 +2497,25 @@ Code de vérification demandé - Vous êtes maintenant à %1$d étape d’envoyer un journal de débogage. - Vous êtes maintenant à %1$d étapes d’envoyer un journal de débogage. + Plus qu\'une étape avant d\'envoyer votre journal de débogage. + Plus que %1$d avant d\'envoyer votre journal de débogage. - Nous devons confirmer que vous êtes un être humain. + Nous devons vérifier que vous êtes un être humain. Appel vocal Annuler Suivant - Poursuivre + Continuer La confidentialité dans votre poche.\nPour que chaque message reflète qui vous êtes. Numéro de téléphone Saisissez votre numéro de téléphone pour commencer. - Saisissez le code que nous avons envoyé au %1$s + Saisissez le code que nous vous avons envoyé au %1$s Numéro de téléphone - Indicatif de pays + Indicatif du pays Appeler Code de vérification Renvoyer le code @@ -2520,8 +2538,8 @@ Désactiver - Visualiser la photo - Visualiser la vidéo + Afficher la photo + Regarder la vidéo Vu Médias @@ -2582,12 +2600,12 @@ Stickers installés Stickers reçus - Séries des artistes de Signal + Stickers d\'artistes pour Signal Aucun sticker n’est installé Les stickers des messages reçus s’affichent ici Sans titre - Inconnu + Auteur inconnu Sans titre @@ -2600,17 +2618,17 @@ Modifier - Terminé + OK Enregistrer - Échec de la sauvegarde + Enregistrement impossible - Sauvegarde terminée + Enregistrement terminé Toucher une ligne pour la supprimer Envoyer - Échec d’envoi des journaux - C’est réussi ! + Impossible d\'envoyer les journaux + Merci ! Copiez cette URL et ajoutez-la à votre rapport d’erreur ou à votre e-mail de demande d’assistance :\n\n%1$s Partager Ce journal sera publiquement disponible sur le web, où les contributeurs pourront le consulter. Vous pouvez l’examiner avant de l’importer. @@ -2619,22 +2637,22 @@ Filtre : Infos sur l\'appareil : - Version d’Android : + Version d\'Android : Blocage d’inscription : - Le groupe a été mis à jour - A quitté le groupe - La session sécurisée a été réinitialisée. + Mise à jour du groupe + a quitté le groupe + Réinitialisation de la session sécurisée. Brouillon : Message multimédia Sticker - Photo éphémère - Vidéo éphémère - Média éphémère + Photo à vue unique + Vidéo à vue unique + Média à vue unique Ce message a été supprimé. Vous avez supprimé ce message. @@ -2647,12 +2665,12 @@ %1$s accepte désormais les paiements %1$s est sur Signal. Messages éphémères désactivés - L’expiration des messages éphémères a été définie à %1$s - Le numéro de sécurité a changé + Délai avant disparition des messages éphémères défini sur %1$s + Changement de numéro de sécurité Le numéro de sécurité associé à %1$s a changé. Vous avez confirmé le numéro de sécurité - Vous avez marqué comme non confirmé - Le message n’a pas pu être traité + Contact marqué comme non confirmé + Impossible de traiter le message Problème de remise Invitation par message @@ -2706,17 +2724,17 @@ Ajouter un nom d\'utilisateur Choisissez votre nom d\'utilisateur - Nom d’utilisateur + Nom d\'utilisateur Supprimer Le nom d’utilisateur a été effacé avec succès. - Une erreur réseau est survenue. + Une erreur réseau s\'est produite. Vous avez dépassé le nombre de tentatives autorisées. Veuillez réessayer ultérieurement. - Ce nom d’utilisateur existe déjà. - Les noms d’utilisateur ne peuvent inclure que des caractères de A à Z, 0 à 9 et des tirets bas. - Un nom d’utilisateur ne peut pas commencer par un chiffre. - Le nom d’utilisateur est invalide. - Un nom d’utilisateur doit comporter entre %1$d et %2$d caractères. + Ce nom d\'utilisateur est déjà pris. + Les noms d\'utilisateur ne peuvent comporter que des caractères de A à Z, 0 à 9 et des tirets bas. + Un nom d\'utilisateur ne peut pas commencer par un chiffre. + Ce nom d\'utilisateur est incorrect. + Les noms d\'utilisateur doivent comporter entre %1$d et %2$d caractères. Les noms d’utilisateur sont toujours associés à une série de chiffres. @@ -2725,7 +2743,7 @@ Ignorer - Terminé + OK Nom d’utilisateur indisponible, veuillez essayer un autre numéro. @@ -2750,18 +2768,18 @@ - Copier ou partager un lien d\'utilisateur + Copier ou partager le lien associé à votre nom d\'utilisateur - Votre contact utilise une version plus récente de Signal dont le format de code QR est incompatible. Veuillez mettre Signal à jour pour comparer. - Le code QR lu n’est pas un code de confirmation du numéro de sécurité correctement formaté. Veuillez réessayer de le lire. - Partager le numéro de sécurité par… + Votre contact utilise une version plus récente de Signal. Le format de son code QR est incompatible avec votre version. Installez une mise à jour Signal pour confirmer son numéro de sécurité. + Le format du code QR que vous avez scanné ne correspond pas à celui d\'un code de confirmation du numéro de sécurité. Veuillez le scanner de nouveau. + Partager le numéro de sécurité via… Notre numéro de sécurité Signal : - Vous semblez n’avoir aucune appli vers laquelle partager. - Aucun numéro de sécurité à comparer n’a été trouvé dans le presse-papiers + Aucune appli disponible pour le partage. + Aucun numéro de sécurité à comparer dans le presse-papiers Pour scanner un code QR, Signal doit accéder à l\'appareil photo, mais vous lui en avez interdit l\'accès. Ouvrez l\'application Paramètres de votre appareil, puis touchez Applications > Signal > Autorisations et activez \"Appareil photo\". - Impossible de lire le code QR sans l’autorisation Appareil photo - Vous devez d’abord échanger des messages afin d’afficher le numéro de sécurité de %1$s. + Impossible de scanner le code QR sans l\'autorisation \"Appareil photo\". + Pour afficher le numéro de sécurité de %1$s, commencez par échanger des messages avec ce contact. Un numéro de sécurité sera créé pour cette personne après un premier échange de messages. @@ -2778,18 +2796,18 @@ - Message chiffré pour une session non existante + Message chiffré pour une session inexistante - Mauvais message multimédia chiffré - Le message multimédia est chiffré pour une session inexistante + Mauvais MMS chiffré + Message MMS chiffré pour une session inexistante Mettre les notifications en sourdine - Touchez pour ouvrir - Signal est déverrouillée + Toucher l\'écran pour ouvrir. + Signal est déverrouillé Verrouiller Signal @@ -2797,7 +2815,7 @@ Type de média non pris en charge Brouillon Pour enregistrer des données sur un stockage externe, Signal doit accéder à l\'application Stockage, mais vous lui en avez interdit l\'accès. Ouvrez l\'application Paramètres de votre appareil, puis touchez Applications > Signal > Autorisations et activez l\'option \"Stockage\". - Impossible d’enregistrer sur l\'espace de stockage sans autorisation + Enregistrement sur le stockage externe impossible sans autorisation d\'accès Supprimer le message ? Ce message sera irrémédiablement supprimé. %1$s à %2$s @@ -2805,12 +2823,12 @@ Vous à %1$s %1$s à vous - Le média n’est plus disponible. + Ce média n\'est plus disponible. Impossible d’ouvrir le fichier. - Le message n’a pas été trouvé. - Impossible de trouver une appli qui peut partager ce média. + Message introuvable. + Nous n\'avons trouvé aucune appli permettant de partager ce média. Fermer Erreur du média @@ -2833,20 +2851,20 @@ Le plus récent de : %1$s Message verrouillé - Échec de remise du message. + Impossible de remettre ce message. Impossible d’envoyer la story Vous à %1$s - Échec de remise du message. - Erreur de remise du message. - Remise des messages en attente. - Confirmez pour continuer à communiquer sur Signal. + Nous n\'avons pas pu remettre ce message. + Une erreur s\'est produite lors de la remise du message. + Remise du message en attente. + Pour continuer d\'échanger des messages sur Signal, merci de confirmer. Tout marquer comme lu Marquer comme lu Désactiver ces notifications - Photo éphémère - Vidéo éphémère + Photo à vue unique + Vidéo à vue unique Répondre Message Signal %1$s %2$s @@ -2857,7 +2875,7 @@ a réagi par %1$s à votre GIF. a réagi par %1$s à votre fichier. a réagi par %1$s à votre fichier audio. - a réagi par %1$s à votre média éphémère. + a réagi par %1$s à votre média à vue unique. a réagi par %1$s à votre paiement. a réagi par %1$s à votre sticker. @@ -2884,14 +2902,14 @@ Échecs Sauvegardes État de verrouillage - Mises à jour de l’appli + Mises à jour de l\'application Autre Conversations Inconnu Notes vocales Un contact a rejoint Signal - Aucune activité n’est disponible pour ouvrir les paramètres du canal de notification. + Aucune application n\'est disponible pour ouvrir les paramètres du canal de notification. Connexion en arrière-plan @@ -2905,23 +2923,23 @@ Impossible d’envoyer une réponse rapide lorsque Signal est verrouillé. - Un problème d’envoi du message est survenu. + Un problème s\'est produit lors de l\'envoi du message. Contenu sauvegardé - Recherche + Rechercher Rechercher les conversations non lues Rechercher des conversations, des contacts et des messages Fermer - Réinitialiser + Supprimer - Le raccourci est invalide + Raccourci non valide Signal @@ -2953,12 +2971,12 @@ Lire la vidéo - A une légende. + Comporte une légende -   élément -   éléments + %1$d élément + %1$d éléments Compression en cours… @@ -3004,7 +3022,7 @@ Répondre - Répondre sans vidéo + Répondre sans la vidéo @@ -3022,13 +3040,13 @@ Casque filaire (USB) - Répondre à l’appel - Refuser l’appel + Répondre à l\'appel + Refuser l\'appel - Ancienne phrase de passe - Nouvelle phrase de passe - Répéter la nouvelle phrase de passe + Ancienne phrase secrète + Nouvelle phrase secrète + Ressaisir la nouvelle phrase secrète Inviter sur Signal @@ -3058,14 +3076,14 @@ Pour afficher vos contacts, Signal doit accéder à l\'application Contacts, mais vous lui en avez interdit l\'accès. Ouvrez l\'application Paramètres de votre appareil, puis touchez Applications > Signal > Autorisations et activez l\'option \"Contacts\". - Erreur de récupération des contacts, veuillez vérifier votre connexion réseau - Le nom d’utilisateur est introuvable + Erreur lors de la récupération des contacts. Veuillez vérifier votre connexion réseau. + Nom d\'utilisateur introuvable "\"%1$s\" ne correspond à aucun utilisateur Signal. Veuillez vérifier le nom d\'utilisateur et réessayer." - Vous n’avez pas besoin de vous ajouter au groupe - La taille maximale du groupe est atteinte - Les groupes Signal ne peuvent pas comporter plus de %1$d membres. - La limite de membres recommandée est atteinte - Les groupes Signal fonctionnent mieux avec %1$d membres ou moins. Ajouter davantage de membres entraînera des retards d’envoi et de réception des messages. + Inutile de vous ajouter au groupe + Le groupe a atteint sa taille maximale + Les groupes Signal ne peuvent pas accueillir plus de %1$d membres. + Votre groupe a atteint la taille maximale recommandée + La performance des groupes Signal est optimale avec un maximum de %1$d membres. Si vous dépassez cette limite, l\'envoi et la réception des messages risquent de s\'en trouver ralentis. %1$d membre %1$d membres @@ -3173,7 +3191,7 @@ Vidéo Photo GIF - Média éphémère + Média à vue unique Sticker Vous Le message original est introuvable @@ -3416,7 +3434,7 @@ Lu par N’a pas été envoyé Vu par - Non délivré + Non remis Échec d’envoi @@ -3425,9 +3443,9 @@ Voir l’historique des modifications - Créer une phrase de passe + Créer une phrase secrète Sélectionner des contacts - Changer la phrase de passe + Modifier la phrase secrète Confirmer le numéro de sécurité Aperçu du média Infos du message @@ -3539,14 +3557,14 @@ Les conversations mises en sourdine restent archivées, même lorsque vous recevez un nouveau message. Générer des aperçus de lien Récupérer des aperçus de lien depuis les sites web lorsque vous envoyez un message. - Changer la phrase de passe - Changer votre phrase de passe + Modifier la phrase secrète + Modifiez votre phrase secrète - Activer le verrouillage de l\'écran avec phrase de passe - Verrouiller l’écran et les notifications avec une phrase de passe + Verrouillage de l\'écran avec une phrase secrète + Utilisez une phrase secrète pour verrouiller l\'écran et les notifications Sécurité de l’écran - Verrouiller Signal automatiquement après un certain délai d\'inactivité - Phrase de passe du délai d’inactivité + Activez le verrouillage automatique de Signal passé un certain délai d\'inactivité + Déverrouillage par phrase secrète passé le délai d\'inactivité Délai d’inactivité Notifications Couleur de la DEL @@ -3645,8 +3663,8 @@ Si vous désactivez les confirmations de lecture, vous ne recevrez pas de confirmations de lecture de la part de vos contacts. Indicateurs de saisie Si les indicateurs de saisie sont désactivés, vous ne verrez pas les indicateurs de saisie des autres utilisateurs. - Demander au clavier de désactiver l’apprentissage personnalisé. - Ce paramètre n’est pas une garantie et votre clavier peut l’ignorer. + Désactiver l\'apprentissage personnalisé du clavier. + Ce paramètre ne garantit pas que votre clavier accepte le mode \"Incognito\" de Signal, qui peut être ignoré par certains claviers. Via les données mobiles Via le Wi-Fi @@ -3672,9 +3690,9 @@ Cela supprimera définitivement tout l’historique des messages et tous les fichiers de cet appareil et des appareils associés. Voulez-vous vraiment supprimer tout l’historique des messages ? - Tout l’historique des messages sera irrémédiablement effacé. Cette action ne peut être annulée. + Tous vos messages seront définitivement supprimés. Cette action est irréversible. - Vous allez définitivement supprimer tout l’historique de vos messages de tous vos appareils. Cette action est irréversible. + Vous allez définitivement supprimer tous vos messages de tous vos appareils. Cette action est irréversible. Tout supprimer maintenant Pour toujours @@ -3844,7 +3862,7 @@ Appels par téléphone - Facilitez l’étape d’inscription et activez des fonctions d’appel supplémentaires. + Facilitez-vous l\'inscription et activez de nouvelles fonctionnalités d\'appel. Espace de stockage @@ -3926,7 +3944,7 @@ Veuillez vérifier l\'adresse de portefeuille vers laquelle vous tentez d\'effectuer un virement et réessayer. Vous ne pouvez pas effectuer de virements vers l\'adresse de votre propre portefeuille Signal. Saisissez l\'adresse de portefeuille associée à votre autre compte. Celui-ci doit être hébergé sur une plateforme de change compatible. Pour lire un code QR, Signal a besoin d’accéder à l’appareil photo. - Signal a besoin de l\'autorisation Appareil photo pour scanner un code QR. Accédez à Paramètres > Autorisations et activez \"Appareil photo\". + Signal a besoin de l\'autorisation \"Appareil photo\" pour scanner un code QR. Accédez à Paramètres > Autorisations et activez \"Appareil photo\". Pour lire un code QR, Signal a besoin d’accéder à l’appareil photo. Paramètres @@ -3960,7 +3978,7 @@ Note - Sauvegarder la note + Enregistrer la note Confirmer le paiement @@ -4137,7 +4155,7 @@ Tout marquer comme lu Inviter des amis - Filtrer les conversations non lues + Filtrer par conversations non lues Supprimer le filtre Non lu @@ -4300,27 +4318,29 @@ Poursuivre Plus tard Migration de la base de données de Signal - Phrase de passe de la sauvegarde - Les sauvegardes sont enregistrées dans l\'espace de stockage externe et chiffrées avec la phrase de passe ci-dessous. Vous devez avoir cette phrase de passe afin de restaurer la sauvegarde. - Cette phrase de passe est requise afin de restaurer une sauvegarde. + Phrase secrète de sauvegarde + Les sauvegardes sont enregistrées dans un espace de stockage externe et chiffrées au moyen de la phrase secrète ci-dessous. Cette phrase secrète vous sera indispensable pour restaurer vos sauvegardes. + Cette phrase secrète est indispensable pour restaurer une sauvegarde. Dossier - J’ai noté cette phrase de passe. Sans elle, je ne pourrai pas restaurer une sauvegarde. + J\'ai bien noté ma phrase secrète. Sans elle, il me sera impossible de restaurer mes sauvegardes. Restaurer une sauvegarde Transférer ou restaurer le compte Restaurer ou transférer le compte Transfert de compte Ignorer + + Skip restore Sauvegarde des conversations Transfert de compte Transférer le compte vers un nouvel appareil Android - Saisissez la phrase de passe de la sauvegarde + Saisissez la phrase secrète de sauvegarde Restaurer Import des données de versions ultérieures impossible La sauvegarde contient des données mal formées - La phrase de passe de la sauvegarde est erronée + Phrase secrète de sauvegarde incorrecte Confirmation… %1$d messages jusqu’à présent… Restaurer de la sauvegarde ? @@ -4337,10 +4357,10 @@ Sélectionner un dossier A été copié dans le presse-papiers Aucun sélecteur de fichiers n’est proposé. - Saisissez la phrase de passe de la sauvegarde pour la confirmer + Saisissez la phrase secrète de sauvegarde pour la confirmer Confirmer - Vous avez saisi avec succès la phrase de passe de votre sauvegarde - La phrase de passe était erronée + Vous avez bien saisi votre phrase secrète de sauvegarde. + Phrase secrète incorrecte Création de la sauvegarde de Signal… Vérification de la sauvegarde Signal… @@ -4602,7 +4622,7 @@ Terminez l’inscription sur votre nouvel appareil Votre compte Signal a été transféré sur votre nouvel appareil, mais vous devez terminer l’inscription dessus pour continuer. Signal sera inactif sur cet appareil. - Terminé + OK Annuler et activer cette appareil @@ -4718,13 +4738,13 @@ %1$s, %2$s et %3$d personnes ont rejoint l’appel - %1$s a quitté l’appel - %1$s et %2$s ont quitté l’appel - %1$s, %2$s et %3$s ont quitté l’appel + %1$s a quitté l\'appel + %1$s et %2$s ont quitté l\'appel + %1$s, %2$s et %3$s ont quitté l\'appel - %1$s, %2$s et %3$d personne ont quitté l’appel - %1$s, %2$s et %3$d personnes ont quitté l’appel + %1$s, %2$s et %3$d autre ont quitté l\'appel + %1$s, %2$s et %3$d autres ont quitté l\'appel Vous @@ -4815,7 +4835,7 @@ Aperçu Définir le fond d’écran - Balayez pour prévisualiser d’autres fonds d’écran + Balayez pour afficher d\'autres fonds d\'écran Définir le fond d’écran pour toutes les conversations Définir le fond d’écran pour %1$s L’autorisation Stockage est exigée pour afficher votre galerie. @@ -4882,7 +4902,7 @@ Entrer manuellement Coller depuis le presse-papiers - Continuer sans sauvegarder ? + Continuer sans enregistrer ? Votre phrase de récupération vous permet de rétablir votre solde dans le pire des cas. Nous vous recommandons fortement de la sauvegarder. @@ -4919,7 +4939,7 @@ Saisissez les mots suivants de votre phrase de récupération. Mot %1$d Revoir la phrase - Terminé + OK Phrase de récupération confirmée @@ -5208,7 +5228,7 @@ Types de conversations - Terminé + OK Messages @@ -5239,12 +5259,12 @@ Messages éphémères Sécurité de l\'appli - Bloquer les captures d’écran dans la liste des récents et dans l’appli + Interdire les captures d\'écran dans Signal et dans la liste des applications ouvertes récemment. Messages et appels Signal : \"Toujours relayer les appels\" et \"Expéditeur scellé\" Délai avant disparition Définissez le délai après lequel les messages éphémères disparaissent des nouvelles conversations que vous avez initiées. - Exiger le verrouillage d\'écran Android ou l\'empreinte digitale pour effectuer des paiements + Exiger le verrouillage d\'écran Android ou l\'empreinte digitale pour effectuer des paiements. Impossible d’activer le verrouillage des paiements @@ -5523,7 +5543,7 @@ Aperçu - Terminé + OK Texte Couleur @@ -5607,7 +5627,7 @@ Écrire du texte Ajouter un sticker Flouter - Fini de modifier + Modification terminée Tout enlever Annuler Passer du marqueur au surligneur @@ -5823,9 +5843,9 @@ En cours de traitement Impossible d\'ajouter le macaron. - Something went wrong + Une erreur s\'est produite - Your backups subscription couldn\'t be displayed. Please contact support. + Impossible d\'afficher votre abonnement de sauvegarde. Veuillez contacter l\'assistance. Validation du macaron impossible. @@ -6050,7 +6070,7 @@ Profil créé - Terminé + OK Vous pouvez activer ou désactiver votre profil manuellement à partir du menu de la liste des conversations. @@ -6172,7 +6192,7 @@ Cette story sera supprimée pour vous et tous ses destinataires. - Échec de la sauvegarde + Enregistrement impossible %1$d vue @@ -6314,7 +6334,7 @@ Ajouter du texte - L’ajout de texte est terminé + OK Texte @@ -6333,7 +6353,7 @@ Ne partager qu’avec… - Terminé + OK Supprimer cette story de groupe ? @@ -6596,7 +6616,7 @@ Plus de destinataires à afficher - Terminé + OK Modification du numéro de sécurité @@ -6866,7 +6886,7 @@ En savoir plus - Terminé + OK Le don n’a pas pu être traité @@ -6882,7 +6902,7 @@ Nous avons bien reçu votre virement. Vous pouvez afficher ce macaron sur votre profil pour montrer que vous soutenez Signal. - Terminé + OK Annulation en cours… @@ -6942,7 +6962,7 @@ - Filtrer les messages non lus + Filtre \"Conversations non lues\" Faites glisser vers le bas pour filtrer @@ -7064,7 +7084,7 @@ Lancer un nouvel appel - Appel manqué filtré + Filtre \"Appels manqués\" Tout sélectionner @@ -7160,7 +7180,7 @@ Partager le lien - Terminé + OK Impossible de partager le lien de l’appel. @@ -7216,7 +7236,7 @@ Impossible d’enregistrer les modifications. Veuillez vérifier votre connexion réseau puis réessayez. - Couldn\'t delete call link as it is currently in use. + Impossible de supprimer le lien d\'appel, car il est en cours d\'utilisation. Supprimer le lien ? @@ -7247,7 +7267,7 @@ Réinitialiser - Terminé + OK Code @@ -7263,7 +7283,7 @@ Impossible de trouver cet utilisateur. - Une erreur réseau est survenue. Veuillez réessayer. + Une erreur réseau s\'est produite. Veuillez réessayer. Vous n’avez pas accès au réseau. Votre lien n’a pas été réinitialisé. Réessayez plus tard. @@ -7307,6 +7327,10 @@ Refuser Approuver + + Approve all + + Deny all Activer les notifications en plein écran ? @@ -7481,7 +7505,13 @@ Ignorer la restauration ? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Si vous choisissez d\'ignorer la restauration, vous pourrez télécharger ultérieurement les pièces jointes et médias stockés dans votre sauvegarde, lorsque l\'espace de stockage sera suffisant. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Ignorer le téléchargement - "Impossible de terminer votre dernière sauvegarde. Vérifiez que votre téléphone est connecté au Wi-Fi, puis appuyez sur \"Sauvegarder maintenant\" pour réessayer." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Signal déchargera les médias inutilisés mais vous pourrez les télécharger à partir de votre sauvegarde à tout moment. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + L\'optimisation de l\'espace de stockage n\'est disponible qu\'avec la version payante du forfait de sauvegarde Signal. Votre abonnement de sauvegarde est en cours de traitement et n\'est pas encore actif. Veuillez réessayer plus tard. @@ -7718,6 +7752,8 @@ Renouveler En savoir plus + + Processing backup… %1$s de données sauvegardées ne sont pas enregistrées sur cet appareil. Votre abonnement prend fin dans %2$d jour. Passé ce délai, votre sauvegarde sera supprimée. @@ -7738,6 +7774,8 @@ Impossible de désactiver et de supprimer les sauvegardes Une erreur réseau s\'est produite. Vérifiez votre connexion Internet et réessayez. + + Uploading messages… @@ -7777,7 +7815,7 @@ Votre clé de sauvegarde - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Votre clé de sauvegarde est un code de 64 caractères alphanumériques. Elle vous permet de restaurer votre sauvegarde si vous réinstallez Signal. Si vous oubliez votre clé, vous ne pourrez pas restaurer votre sauvegarde et Signal ne pourra pas vous aider à la récupérer. @@ -7866,7 +7904,7 @@ Vous n\'avez pas accès à votre ancien téléphone - Or you\'re reinstalling Signal on the same device + ou vous réinstallez Signal sur le même appareil Restaurer ou transférer le compte @@ -7895,15 +7933,15 @@ Saisir la clé de sauvegarde - Your backup key is a 64-character code required to recover your account and data. + Votre clé de sauvegarde est un code de 64 caractères alphanumériques qui vous permet de récupérer votre compte et vos données. Clé de sauvegarde introuvable ? Clé de sauvegarde - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Vous ne pouvez pas restaurer vos sauvegardes sans votre code à 64 caractères. Si vous l\'avez perdu, Signal ne peut pas vous aider à les restaurer. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Si vous avez toujours accès à votre ancien appareil, vous pouvez retrouver votre clé sous Paramètres > Sauvegardes > Afficher la clé de sauvegarde. En savoir plus @@ -7952,6 +7990,18 @@ OK + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index 7739e857df..209b12d17e 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -1071,6 +1071,20 @@ Triail nascadh arís Lean leis gan aistriú + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1096,13 +1110,13 @@ - Transfer message history + Aistrigh stair na dteachtaireachtaí - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Ná aistrigh é + Ná haistrigh - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Dínasc \"%1$s\"? @@ -1469,16 +1483,18 @@ Do theachtaireachtaí uile Aischuir ó chúltaca - - Ní áirítear ach meáin a seoladh nó a fuarthas le %1$d lá anuas. Áirítear i do chúltaca: Aischuir cúltaca - + Rinneadh do chúltaca deiridh %1$s ag %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Sonraí cúltaca á bhfáil… + + Skip restore Cuir Tráchtanna in iúl dom @@ -2716,6 +2732,8 @@ Tá an iomarca iarrachtaí déanta agat leis an uimhir sin a chlárú. Triail arís i gceann %1$s. Ní féidir ceangal a bhunú leis an tseirbhís. Deimhnigh go bhfuil tú ceangailte leis an líonra agus bain triail eile as. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Níorbh fhéidir linn cód fíoraithe a sheoladh trí SMS. Triail do chód a fháil trí ghuthghlao ina ionad. @@ -4644,6 +4662,8 @@ Aischuir nó aistrigh Cuntas a Aistriú Léim thar seo + + Skip restore Cúltacaí na gcomhráite Cuntas a Aistriú Aistrigh cuntas chuig gléas nua Android @@ -6222,9 +6242,9 @@ Still Processing Níor fhéidir suaitheantas a chur leis - Something went wrong + Chuaigh rud éigin amú - Your backups subscription couldn\'t be displayed. Please contact support. + Níorbh fhéidir do shíntiús cúltacaí a thaispeáint. Déan teagmháil leis an bhfoireann tacaíochta. Theip ar bhailíochtú an tsuaitheantais @@ -7687,7 +7707,7 @@ Níorbh fhéidir athruithe a shábháil. Seiceáil do nasc líonra agus triail arís. - Couldn\'t delete call link as it is currently in use. + Níorbh fhéidir an nasc glao a scriosadh toisc go bhfuil sé in úsáid faoi láthair. Scrios an nasc? @@ -7784,6 +7804,10 @@ Diúltaigh Ceadaigh í + + Approve all + + Deny all Cas air fógraí lánscáileáin? @@ -7964,7 +7988,13 @@ Scipeáil aischur? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Má scipeálann tú aischur beidh tú in ann na meáin agus ceangaltáin i do chúltaca a íoslódáil níos déanaí nuair a chuirfear spás stórála ar fáil. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7997,7 +8027,11 @@ Scipeáil íoslódáil - "Níorbh fhéidir do chúltaca deiridh a chur i gcrích. Cinntigh go bhfuil do ghuthán nasctha le Wi-Fi agus tapáil Cúltacaigh anois lena thriail arís." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -8041,7 +8075,7 @@ Bainfear meáin nár úsáideadh, ach is féidir iad a íoslódáil ó do chúltaca am ar bith. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Ní féidir optamú stórais a úsáid ach leis an leibhéal íoctha de Chúltacaí Signal. Tá do shíntiús cúltacaí á phróiseáil, níl sé gníomhach fós. Triail arís níos déanaí. @@ -8207,6 +8241,8 @@ Athnuaigh Tuilleadh faisnéise + + Processing backup… Tá %1$s de shonraí cúltaca agat nach bhfuil ar an ngléas seo. Scriosfar do chúltaca nuair a thiocfaidh deireadh le do shíntiús i gceann %2$d lae. @@ -8233,6 +8269,8 @@ Níorbh fhéidir cúltacaí a chasadh as agus a scriosadh Tharla earráid leis an líonra. Seiceáil do nasc Idirlín agus triail arís. + + Uploading messages… @@ -8272,7 +8310,7 @@ D\'eochair chúltaca - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Cód 64 carachtar is ea d\'eochair chúltaca a ligeann duit do chúltaca a aischur nuair a athshuiteálann tú Signal. Má dhéanann tú dearmad ar d\'eochair ní bheidh tú in ann do chúltaca a aischur. Ní féidir le Signal cabhrú leat do chúltaca a aischur. @@ -8367,7 +8405,7 @@ Níl mo sheanghuthán agam - Or you\'re reinstalling Signal on the same device + Nó tá tú ag athshuiteáil Signal ar an ngléas céanna Cuntas a aischur nó a aistriú @@ -8396,15 +8434,15 @@ Cuir isteach d\'eochair chúltaca - Your backup key is a 64-character code required to recover your account and data. + Cód 64 carachtar is ea d\'eochair chúltaca a éilítear chun do chuntas agus sonraí a athshlánú. Níl eochair chúltaca agat? Eochair chúltaca - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Ní féidir cúltacaí a athshlánú gan a gcód athshlánaithe 64 carachtar. Má tá d\'eochair chúltaca caillte agat ní féidir le Signal cabhrú leat le haischur do chúltaca. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Má tá do sheanghléas agat, is féidir leat féachaint ar d\'eochair chúltaca i Socruithe > Cúltacaí. Ansin tapáil Féach ar eochair chúltaca. Tuilleadh faisnéise @@ -8453,6 +8491,18 @@ Cgl + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index a953df4f3a..ebb21e154b 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -1011,6 +1011,20 @@ Vincular de novo Continuar sen transferencia + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Transferir historial de mensaxes - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Non transferir - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Desvincular «%1$s»? @@ -1355,16 +1369,18 @@ Todas as túas mensaxes Restaurar desde copia de seguranza - - Só se inclúe o contido enviado ou recibido nos últimos %1$d días. A túa copia de seguranza inclúe: Restaurar copia de seguranza - + A túa última copia de seguranza foi o %1$s ás %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Buscando información da copia de seguranza… + + Skip restore Notificarme as mencións @@ -2464,6 +2480,8 @@ Levas demasiados intentos para rexistrar este número. Téntao de novo en %1$s. Non é posible contactar co servizo. Comproba a túa conexión de rede e téntao de novo. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Non puidemos enviarche un código de verificación por SMS. Intenta recibir o código por chamada de voz. @@ -4311,6 +4329,8 @@ Restaurar ou transferir Transferir conta Omitir + + Skip restore Copias de seguranza das conversas Transferir conta Transferir a conta un novo dispositivo Android @@ -5823,9 +5843,9 @@ Procesando Non se puido engadir a insignia - Something went wrong + Vaia, algo foi mal - Your backups subscription couldn\'t be displayed. Please contact support. + Non foi posible mostrar a túa subscrición ás copias de seguranza. Contacta co centro de axuda. Erro ao validar a insignia @@ -7216,7 +7236,7 @@ Erro ao gardar os cambios. Comproba a túa conexión a Internet e inténtao de novo. - Couldn\'t delete call link as it is currently in use. + Non se puido eliminar a ligazón de chamada porque está en uso. Borrar ligazón? @@ -7307,6 +7327,10 @@ Rexeitar Aprobar + + Approve all + + Deny all Activar as notificacións de pantalla completa? @@ -7481,7 +7505,13 @@ Saltar restauración? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Se non completas a restauración, os arquivos e anexos restantes da túa copia de seguridade estarán dispoñibles para descargar máis tarde, cando se libere espazo de almacenamento. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Omitir descarga - "A túa última copia de seguranza non se completou. Comproba que o teu teléfono estea conectado a unha rede wifi e preme «Facer copia agora»." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Eliminaranse os arquivos non utilizados, pero sempre poderás descargalos de novo desde a túa copia de seguranza. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Só se pode acceder á optimización de almacenamento co nivel de pago das copias de seguranza de Signal. A túa subscrición ás copias de seguranza aínda está en proceso e non está activa. Inténtao de novo máis tarde. @@ -7718,6 +7752,8 @@ Renovar Máis información + + Processing backup… Tes %1$s de datos de copia de seguranza que non están neste dispositivo. A túa copia eliminarase cando a túa subscrición remate en %2$d día. @@ -7738,6 +7774,8 @@ Non se puido desactivar nin eliminar as copias de seguranza Produciuse un erro de rede. Comproba a túa conexión a Internet e inténtao de novo. + + Uploading messages… @@ -7777,7 +7815,7 @@ A túa clave de seguranza - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + A túa clave de seguranza é un código de 64 caracteres co que podes restaurar a túa copia de seguranza cando volves instalar Signal. Se perdes a túa clave, non poderás restablecer a copia e Signal non poderá axudarche a recuperala. @@ -7866,7 +7904,7 @@ Non teño o meu antigo teléfono - Or you\'re reinstalling Signal on the same device + Ou estás a reinstalar Signal no mesmo dispositivo Restaurar ou transferir conta @@ -7895,15 +7933,15 @@ Escribe a túa clave de seguranza - Your backup key is a 64-character code required to recover your account and data. + A túa clave de seguranza é un código de 64 caracteres esencial para recuperar a túa conta e os datos. Non tes a clave de seguranza? Clave de seguranza - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + As copias de seguranza non poden recuperarse sen o código de 64 caracteres. Se perdiches a clave de seguranza, Signal non restaurará a túa copia de seguranza. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Se tes o antigo dispositivo podes consultar a clave de seguranza en Configuración > Copias de seguranza. Despois selecciona «Ver clave de seguranza». Máis información @@ -7952,6 +7990,18 @@ De acordo + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-gu/strings.xml b/app/src/main/res/values-gu/strings.xml index 0985ae2e4b..0f26be14f4 100644 --- a/app/src/main/res/values-gu/strings.xml +++ b/app/src/main/res/values-gu/strings.xml @@ -1011,6 +1011,20 @@ ફરીથી લિંક કરવાનો પ્રયાસ કરો ટ્રાન્સફર કર્યા વિના ચાલુ રાખો + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + મેસેજની હિસ્ટ્રી ટ્રાન્સફર કરો - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device ટ્રાન્સફર કરશો નહીં - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\"ને અનલિંક કરીએ? @@ -1355,16 +1369,18 @@ તમારા બધા મેસેજ બેકઅપમાંથી રિસ્ટોર કરો - - ફક્ત પાછલા %1$d દિવસમાં મોકલેલા કે મેળવેલા મીડિયા શામેલ છે. તમારા બેકઅપમાં શામેલ છે: બેકઅપ રિસ્ટોર કરો - + તમારું છેલ્લું બેકઅપ %1$sના રોજ %2$s વાગ્યે લેવામાં આવ્યું હતું. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. બેકઅપ વિગતો મેળવી રહ્યાં છીએ… + + Skip restore મને ઉલ્લેખો માટે સૂચિત કરો @@ -2464,6 +2480,8 @@ આ નંબરને રજિસ્ટર કરવા માટે તમે ઘણા બધા પ્રયત્નો કરી લીધા છે. કૃપા કરીને %1$sમાં ફરી પ્રયાસ કરો. સેવાથી કનેક્ટ કરવામાં અસમર્થ. કૃપા કરીને નેટવર્ક કનેક્શન તપાસો અને ફરીથી પ્રયાસ કરો. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. અમે તમને SMS દ્વારા ચકાસણી કોડ મોકલી શક્યાં નથી. તેના બદલે વૉઇસ કૉલ દ્વારા તમારો કોડ મેળવવાનો પ્રયાસ કરો. @@ -4311,6 +4329,8 @@ રિસ્ટોર અથવા ટ્રાન્સફર કરો એકાઉન્ટ ટ્રાન્સફર કરો અવગણો + + Skip restore ચેટ બૅકઅપ એકાઉન્ટ ટ્રાન્સફર કરો નવા Android ડિવાઇસમાં એકાઉન્ટ ટ્રાન્સફર કરો @@ -5823,9 +5843,9 @@ હજી પ્રક્રિયા ચાલુ છે બૅજ ઉમેરી ન શક્યા - Something went wrong + કંઈક ખોટું થયું - Your backups subscription couldn\'t be displayed. Please contact support. + તમારું બેકઅપ સબ્સ્ક્રિપ્શન પ્રદર્શિત કરી શકાયું નથી. કૃપા કરીને સપોર્ટનો સંપર્ક કરો. બૅજને માન્ય કરવાનું નિષ્ફળ @@ -7216,7 +7236,7 @@ ફેરફારો સેવ શક્યા નથી. તમારું નેટવર્ક કનેક્શન તપાસો અને ફરી પ્રયાસ કરો. - Couldn\'t delete call link as it is currently in use. + કૉલ લિંક ડિલીટ કરી શકાઈ નથી કારણ કે તે હાલમાં ઉપયોગમાં છે. લિંક ડિલીટ કરવી છે? @@ -7307,6 +7327,10 @@ અસ્વીકાર મંજૂર + + Approve all + + Deny all ફૂલ સ્ક્રીન નોટિફિકેશન ચાલુ કરવા છે? @@ -7481,7 +7505,13 @@ રિસ્ટોર કરવાનું સ્કિપ કરવું છે? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + જો તમે રિસ્ટોર કરવાનું છોડી દો છો તો તમારા બેકઅપમાંના બાકીના મીડિયા અને અટેચમેન્ટ જ્યારે સ્ટોરેજ સ્પેસ ઉપલબ્ધ થાય ત્યારે પછીના સમયે ડાઉનલોડ કરી શકાય છે. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ ડાઉનલોડ કરવાનું છોડો - "તમારું છેલ્લું બેકઅપ પૂર્ણ કરી શકાયું નથી. ખાતરી કરો કે તમારો ફોન Wi-Fiથી કનેક્ટ થયેલો છે અને ફરી પ્રયાસ કરવા માટે હમણાં બેકઅપ લો પર ટેપ કરો." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ ન વપરાયેલ મીડિયાને ઑફલોડ કરવામાં આવશે, પરંતુ તમારા બેકઅપમાંથી ગમે ત્યારે ડાઉનલોડ કરી શકાય છે. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + સ્ટોરેજ ઑપ્ટિમાઇઝેશનનો ઉપયોગ માત્ર Signal બેકઅપના પેઇડ ટીયર સાથે જ થઈ શકે છે. તમારું બેકઅપ સબ્સ્ક્રિપ્શન હજી પ્રક્રિયામાં છે અને હજી સક્રિય નથી. કૃપા કરીને પછી ફરી પ્રયાસ કરો. @@ -7718,6 +7752,8 @@ રિન્યૂ કરો વધુ જાણો + + Processing backup… તમારી પાસે %1$s બેકઅપ ડેટા છે જે આ ડિવાઇસ પર નથી. જ્યારે તમારું સબ્સ્ક્રિપ્શન %2$d દિવસમાં સમાપ્ત થશે ત્યારે તમારું બેકઅપ ડિલીટ કરવામાં આવશે. @@ -7738,6 +7774,8 @@ બેકઅપ બંધ અને ડિલીટ ન કરી શક્યા નેટવર્કમાં ભૂલ આવી. કૃપા કરીને તમારું ઇન્ટરનેટ કનેક્શન તપાસો અને ફરી પ્રયાસ કરો. + + Uploading messages… @@ -7777,7 +7815,7 @@ તમારી બેકઅપ કી - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + તમારી બેકઅપ કી એ 64-અક્ષરનો કોડ છે જે તમને Signalને ફરીથી ઇન્સ્ટોલ કરવા પર તમારા બેકઅપને રિસ્ટોર કરવા દે છે. જો તમે તમારી કી ભૂલી જાઓ, તો તમે તમારા બેકઅપને રિસ્ટોર કરી શકશો નહીં. Signal તમને તમારા બેકઅપને રિસ્ટોર કરવામાં મદદ કરી શકશે નહીં. @@ -7866,7 +7904,7 @@ મારી પાસે મારો જૂનો ફોન નથી - Or you\'re reinstalling Signal on the same device + અથવા તમે તે જ ડિવાઇસ પર Signal ફરીથી ઇન્સ્ટોલ કરી રહ્યાં છો એકાઉન્ટ રિસ્ટોર અથવા ટ્રાન્સફર કરો @@ -7895,15 +7933,15 @@ તમારી બેકઅપ કી દાખલ કરો - Your backup key is a 64-character code required to recover your account and data. + તમારી બેકઅપ કી એ તમારા એકાઉન્ટ અને ડેટાને રિકવર કરવા માટેનો જરૂરી 64-અક્ષરનો કોડ છે. કોઈ બેકઅપ કી નથી? બેકઅપ કી - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + બેકઅપ તેમના 64-અક્ષરના રિકવરી કોડ વિના રિકવર કરી શકાતા નથી. જો તમે તમારી બેકઅપ કી ગુમાવી દીધી હોય તો Signal તમારા બેકઅપને રિસ્ટોર કરવામાં મદદ કરી શકશે નહીં. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + જો તમારી પાસે તમારું જૂનું ડિવાઇસ હોય તો તમે સેટિંગ્સ > બેકઅપમાં તમારી બેકઅપ કી જોઈ શકો છો. પછી બેકઅપ કી જુઓ પર ટેપ કરો. વધુ જાણો @@ -7952,6 +7990,18 @@ બરાબર + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 62523208d1..2fffea760b 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -1011,6 +1011,20 @@ फिर से लिंक करने की कोशिश करें बिना ट्रांसफ़र किए जारी रखें + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + संदेश ट्रांसफ़र करने का इतिहास - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device ट्रांसफ़र न करें - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\" को अनलिंक करें? @@ -1355,16 +1369,18 @@ आपके सभी संदेश बैकअप से रीस्टोर करें - - सिर्फ़ उस मीडिया को शामिल किया गया है जो पिछले %1$d दिनों में भेजा या मिला है। आपके बैकअप में शामिल है: बैकअप पुनर्स्थापित करें - + आपका पिछला बैकअप %1$s को %2$s पर बनाया गया था। + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. बैकअप जानकारी फ़ेच की जा रही है… + + Skip restore मेंछन किए जाने पर मुझे सूचित करें @@ -2464,6 +2480,8 @@ आपने इस नंबर को पंजीकृत करने के बहुत सारे प्रयास किए। कृपया %1$s में पुन: प्रयास करें। सेवा से कनेक्ट करने में असमर्थ। कृपया नेटवर्क कनेक्शन की जांच करें और पुनः प्रयास करें। + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. हम आपको SMS द्वारा सत्यापन कोड नहीं भेज सकते। इसके बजाय वॉयस कॉल द्वारा अपना कोड प्राप्त करने का प्रयास करें। @@ -4311,6 +4329,8 @@ रीस्टोर या ट्रांसफ़र करें खाता ट्रांसफ़र करें छोड़ दे + + Skip restore बैकअप चैट करें खाता ट्रांसफ़र करें खाते को किसी नए Android डिवाइस पर ट्रांसफ़र करें @@ -5823,9 +5843,9 @@ अभी भी प्रोसेस कर रहे बैज नहीं जोड़ा जा सका - Something went wrong + कुछ गड़बड़ी हुई - Your backups subscription couldn\'t be displayed. Please contact support. + आपका बैकअप सब्सक्रिप्शन दिखाया नहीं जा सका। कृपया सपोर्ट से संपर्क करें। बैज वैलिडेट नहीं किया जा सका @@ -7216,7 +7236,7 @@ बदलाव सहेजे नहीं जा सके। अपने नेटवर्क कनेक्शन की जांच करके पुनः प्रयास करें। - Couldn\'t delete call link as it is currently in use. + कॉल लिंक को डिलीट नहीं किया जा सका, क्योंकि यह अभी इस्तेमाल हो रहा है। लिंक हटाएं? @@ -7307,6 +7327,10 @@ अस्वीकार करें स्वीकार करें + + Approve all + + Deny all फुल स्क्रीन अधिसूचना चालू करें? @@ -7481,7 +7505,13 @@ रीस्टोर करना छोड़ें? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + अगर आप बाकी के मीडिया और अटैचमेंट को अपने बैकअप में स्टोर नहीं करते हैं, तो स्टोरेज स्पेस उपलब्ध होने पर उन्हें बाद में डाउनलोड किया जा सकता है। + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ डाउनलोड छोड़ें - "आपका आखिरी बैकअप पूरा नहीं किया जा सका। पक्का करें कि आपका फ़ोन वाई-फ़ाई से कनेक्ट हैं और फिर से कोशिश करने के लिए \'अभी बैकअप करें\' पर टैप करें।" + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ इस्तेमाल न किया गया मीडिया ऑफ़लोड हो जाएगा, लेकिन इसे आपके बैकअप से कभी भी डाउनलोड किया जा सकता है। - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + स्टोरेज ऑप्टिमाइज़ेशन को सिर्फ़ Signal बैकअप के भुगतान वाले टियर के साथ इस्तेमाल किया जा सकता है। आपका बैकअप सब्सक्रिप्शन अब भी प्रोसेस हो रहा है और अभी ऐक्टिव नहीं है। कृपया बाद में कोशिश करें। @@ -7718,6 +7752,8 @@ रिन्यू करें अधिक जानें + + Processing backup… आपका %1$s डेटा इस डिवाइस पर मौजूद नहीं है। %2$d दिन में आपका सब्सक्रिप्शन खत्म होने पर, आपका बैकअप डिलीट कर दिया जाएगा। @@ -7738,6 +7774,8 @@ बैकअप को बंद और डिलीट नहीं किया जा सका नेटवर्क में कोई गड़बड़ी हुई। कृपया अपने इंटरनेट कनेक्शन की जाँच करके फिर से कोशिश करें। + + Uploading messages… @@ -7777,7 +7815,7 @@ आपकी बैकअप की - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + आपकी बैकअप की 64 वर्णों का कोड होती है, जो आपको Signal फिर से इंस्टॉल करते समय अपना बैकअप रीस्टोर करने में मदद करती है। अगर आप अपनी की भूल जाते हैं, तो आप अपना बैकअप रीस्टोर नहीं कर पाएँगे। Signal आपके बैकअप को रिकवर करने में मदद नहीं कर सकता। @@ -7866,7 +7904,7 @@ मेरे पास मेरा पुराना फ़ोन नहीं है - Or you\'re reinstalling Signal on the same device + या आप उसी डिवाइस पर Signal को फिर से इंस्टॉल कर रहे हैं अकाउंट ट्रांसफ़र या रीस्टोर करें @@ -7895,15 +7933,15 @@ अपनी बैकअप की दर्ज करें - Your backup key is a 64-character code required to recover your account and data. + आपकी बैकअप की 64 वर्णों वाला एक कोड होती है, जो आपके अकाउंट और डेटा को रिकवर करने के लिए ज़रूरी होती है। आपकी बैकअप की नहीं है? आपकी बैकअप की - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + बैकअप की को उनके 64 वर्णों वाले रिकवरी कोड के बिना रिकवर नहीं किया जा सकता। अगर आपकी बैकअप की खो गई है, तो Signal आपका बैकअप रीस्टोर करने में मदद नहीं कर सकता। - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + अगर आपके पास अपना पुराना डिवाइस है, तो आप अपनी बैकअप की को सेटिंग्स > बैकअप में देख सकते हैं। फिर \'बैकअप की देखें\' पर टैप करें। अधिक जानें @@ -7952,6 +7990,18 @@ ठीक है + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 8d1d4abc85..34d3292a8b 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -1051,6 +1051,20 @@ Pokušaj ponovno Nastavi bez prijenosa + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1076,13 +1090,13 @@ - Transfer message history + Prijenos povijesti poruka - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Ne prenosi + Preskoči prijenos - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Poništi vezu s \"%1$s\"? @@ -1431,16 +1445,18 @@ Sve vaše poruke Vraćanje iz sigurnosne kopije - - Uključeni su samo medijski zapisi koji su poslani ili primljeni u zadnjih %1$d dana. Vaša sigurnosna kopija uključuje: Vrati iz sigurnosne kopije - + Posljednje sigurnosno kopiranje obavljeno je %1$s u %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Učitavanje podataka o sigurnosnoj kopiji… + + Skip restore Obavijesti me samo za Spominjanja @@ -2632,6 +2648,8 @@ Previše neuspjelih pokušaja registracije ovog broja telefona. Pokušajte ponovno za %1$s. Povezivanje s uslugom nije moguće. Provjerite mrežnu vezu i pokušajte ponovo. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Slanje koda za provjeru putem SMS-a nije uspjelo. Pokušajte primiti svoj kôd putem glasovnog poziva. @@ -4533,6 +4551,8 @@ Vrati ili prenesi Prijenos računa Preskoči + + Skip restore Sigurnosne kopije razgovora Prijenos računa Prijenos računa na novi Android uređaj @@ -6089,9 +6109,9 @@ Obrada u tijeku Nije moguće dodati značku - Something went wrong + Nešto nije u redu - Your backups subscription couldn\'t be displayed. Please contact support. + Došlo je do pogreške u prikazivanju vaše pretplate na sigurnosno kopiranje. Molimo, kontaktirajte podršku. Provjera značke nije uspjela @@ -7530,7 +7550,7 @@ Spremanje promjena nije uspjelo. Provjerite mrežnu vezu i pokušajte ponovno. - Couldn\'t delete call link as it is currently in use. + Brisanje poveznice na poziv nije uspjelo jer je trenutno u uporabi. Izbrisati poveznicu? @@ -7625,6 +7645,10 @@ Odbij Odobri + + Approve all + + Deny all Uključiti obavijesti preko cijelog zaslona? @@ -7803,7 +7827,13 @@ Preskočiti vraćanje datoteka? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Ako preskočite ovaj korak, moći ćete preuzeti medijske zapise i privitke spremljene u sigurnosnoj kopiji kada na vašem uređaju bude dostupno dovoljno prostora za pohranu. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7836,7 +7866,11 @@ Preskoči preuzimanje - "Posljednje sigurnosno kopiranje podataka nije uspjelo. Provjerite je li vaš uređaj spojen na Wi-Fi i pokušajte ponovno dodirom na \"Izradi sigurnosnu kopiju sada\"." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7880,7 +7914,7 @@ Nekorišteni medijski zapisi bit će uklonjeni, ali moguće ih je vratiti iz sigurnosne kopije u bilo kojem trenutku. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Optimizacija pohrane dostupna je samo uz plaćenu pretplatu na sigurnosno kopiranje Signala. Vaša pretplata se trenutno obrađuje i još nije aktivna. Pokušajte ponovno kasnije. @@ -8044,6 +8078,8 @@ Obnovi Saznajte više + + Processing backup… Imate %1$s sigurnosne kopije podataka koji nisu na ovom uređaju. Vaša sigurnosna kopija bit će izbrisana kada vaša pretplata istekne za %2$d dan. @@ -8068,6 +8104,8 @@ Nije bilo moguće isključiti i izbrisati sigurnosnu kopiju Došlo je do pogreške mreže. Provjerite internetsku vezu i pokušajte ponovno. + + Uploading messages… @@ -8107,7 +8145,7 @@ Vaš ključ za sigurnosnu kopiju - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Ključ za sigurnosnu kopiju je 64-znamenkasti kôd koji vam omogućuje vraćanje sigurnosne kopije kada ponovno instalirate Signal. Ako zaboravite svoj ključ, neće biti moguće vratiti podatke iz sigurnosne kopije. Signal vam ne može pomoći vratiti sigurnosnu kopiju. @@ -8200,7 +8238,7 @@ Nemam svoj stari telefon - Or you\'re reinstalling Signal on the same device + Ili ako želite ponovno instalirati Signal na isti uređaj Vratite ili prenesite račun @@ -8229,15 +8267,15 @@ Unesite svoj ključ za sigurnosnu kopiju - Your backup key is a 64-character code required to recover your account and data. + Ključ za sigurnosnu kopiju je 64-znamenkasti kôd koji je potreban za oporavak vašeg računa i podataka. Nemate ključ za sigurnosnu kopiju? Ključ za sigurnosnu kopiju - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Sigurnosne kopije nije moguće vratiti bez 64-znamenkastog koda za oporavak. Ako ste zaboravili svoj ključ, nećete moći vratiti podatke iz sigurnosne kopije. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Ako imate svoj stari uređaj, svoj ključ za sigurnosnu kopiju možete pregledati u Postavkama > Sigurnosne kopije. Zatim dodirnite Prikaži ključ za sigurnosnu kopiju. Saznajte više @@ -8286,6 +8324,18 @@ U redu + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 90defd698d..185010ed9f 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -1011,6 +1011,20 @@ Próbáld meg újra összekapcsolni Folytatás átvitel nélkül + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Üzenetelőzmények áthelyezése - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Ne utalja át + Nem kerül áthelyezésre - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Társítás megszüntetése ezzel: \"%1$s\"? @@ -1355,16 +1369,18 @@ Az összes üzeneted Visszaállítás biztonsági mentésből - - Csak az elmúlt %1$d napban küldött vagy fogadott médiafájlokat tartalmazza. A biztonsági másolat a következőket tartalmazza: Biztonsági mentés visszaállítása - + Az utolsó biztonsági mentés dátuma: %1$s, %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Biztonsági mentés részleteinek lekérése… + + Skip restore Értesítsen említés esetén @@ -2464,6 +2480,8 @@ Túl sokszor próbáltad regisztrálni ezt a számot. Kérjük, próbáld újra %1$s múlva. Nem lehet kapcsolódni szolgáltatáshoz. Kérlek ellenőrizd a hálózati kapcsolatot és próbáld újra! + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Nem sikerült ellenőrző kódot küldenünk SMS-ben. Próbáld meg inkább hanghívással fogadni a kódot. @@ -4311,6 +4329,8 @@ Visszaállítás vagy átvitel Fiók átvitele Kihagyás + + Skip restore Csevegések biztonsági mentése Fiók átvitele Fiók átvitele egy másik Android eszközre @@ -5823,9 +5843,9 @@ Még dolgozunk Jelvény hozzáadása sikertelen. - Something went wrong + Hiba történt! - Your backups subscription couldn\'t be displayed. Please contact support. + A biztonsági mentésre vonatkozó előfizetést nem lehet megjeleníteni. Kérjük, vedd fel a kapcsolatot az Ügyfélszolgálattal! Nem sikerült ellenőrizni a jelvényhez való jogosultságodat. @@ -7216,7 +7236,7 @@ Nem sikerült elmenteni a változtatásokat. Ellenőrizd a hálózati kapcsolatot, és próbáld újra! - Couldn\'t delete call link as it is currently in use. + Nem sikerült törölni a híváslinket, mivel jelenleg használatban van. Hivatkozás törlése? @@ -7307,6 +7327,10 @@ Elutasít Jóváhagyás + + Approve all + + Deny all Bekapcsolod a teljes képernyős értesítéseket? @@ -7481,7 +7505,13 @@ Visszaállítás kihagyása? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Ha kihagyod a visszaállítást, a biztonsági másolatban lévő fennmaradó médiafájlok és mellékletek letölthetők később, amikor tárhely szabadul fel. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Letöltés kihagyása - "Az utolsó biztonsági mentést nem lehetett befejezni. Győződj meg róla, hogy a telefonod csatlakozik a wifihez, és az újrapróbálkozáshoz koppints a Biztonsági mentés most elemre." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ A fel nem használt médiafájlokat a rendszer eltávolítja, de bármikor letöltheted őket a biztonsági másolatból. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + A tárhely-optimalizálás csak a díj ellenében igénybe vehető Signal biztonsági mentésekkel használható. Biztonsági mentésre vonatkozó előfizetésed még feldolgozás alatt áll, és még nem aktív. Kérjük, próbáld újra később. @@ -7718,6 +7752,8 @@ Megújítás Tudj meg többet + + Processing backup… %1$s biztonsági mentési adatod van, amely nem található ezen az eszközön. A biztonsági másolatot töröljük, ha az előfizetésed %2$d napon belül lejár. @@ -7738,6 +7774,8 @@ Nem sikerült kikapcsolni és törölni a biztonsági másolatokat Hálózati hiba történt. Ellenőrizd az internetkapcsolatot, és próbáld újra. + + Uploading messages… @@ -7777,7 +7815,7 @@ A biztonsági kulcsod - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + A biztonsági kulcs egy 64 számjegyű kód, amely lehetővé teszi a biztonsági másolat visszaállítását a Signal újratelepítésekor. Ha elfelejted a kulcsod, nem tudod visszaállítani a biztonsági másolatot. A Signal nem tud segíteni a biztonsági másolat helyreállításában. @@ -7866,7 +7904,7 @@ Nincs nálam a régi telefonom - Or you\'re reinstalling Signal on the same device + Vagy telepítsd újra a Signalt ugyanarra az eszközre Fiók visszaállítása vagy átvitele @@ -7895,15 +7933,15 @@ Add meg a biztonsági kulcsot - Your backup key is a 64-character code required to recover your account and data. + A biztonsági kulcs egy 64 karakterből álló kód, amely a fiók és az adatok helyreállításához szükséges. Nincs biztonsági kulcsod? Biztonsági kulcs - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + A biztonsági másolatokat nem lehet visszaállítani a 64 karakterből álló helyreállítási kód nélkül. Ha elvesztetted a biztonsági kulcsodat, a Signal nem tud segíteni a biztonsági másolat visszaállításában. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Ha rendelkezel a régi eszközöddel, tekintsd meg a biztonsági kulcsot a Beállítások > Csevegések menüpontban. Ezután koppints a Biztonsági kulcs megtekintése lehetőségre. Tudj meg többet @@ -7952,6 +7990,18 @@ OK + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 65eee5508a..ae851a4b73 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -991,6 +991,20 @@ Coba tautkan lagi Lanjutkan tanpa mentransfer + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1016,13 +1030,13 @@ - Transfer message history + Transfer riwayat pesan - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Jangan transfer - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Putus penautan \"%1$s\"? @@ -1317,16 +1331,18 @@ Semua pesan Anda Pulihkan dari cadangan - - Hanya media yang dikirim atau diterima dalam %1$d hari terakhir yang disertakan. Cadangan Anda mencakup: Pulihkan cadangan - + Pencadangan terakhir Anda dibuat pada %1$s pukul %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Mengambil detail cadangan … + + Skip restore Beritahu saya ketika ada penyebutan @@ -2380,6 +2396,8 @@ Anda terlalu sering mencoba mendaftar dengan nomor ini. Mohon coba lagi dalam %1$s. Tidak bisa terhubung ke layanan. Mohon cek koneksi jaringan dan coba lagi. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Kami tidak dapat mengirimi Anda kode verifikasi via SMS. Coba terima kode via panggilan suara. @@ -4200,6 +4218,8 @@ Pulihkan atau transfer Transfer akun Lewati + + Skip restore Cadangan obrolan Transfer akun Transfer akun ke perangkat Android baru @@ -5690,9 +5710,9 @@ Masih memproses Tidak dapat menambahkan lencana - Something went wrong + Ada yang tidak beres - Your backups subscription couldn\'t be displayed. Please contact support. + Langganan cadangan Anda tidak dapat ditampilkan. Silakan hubungi tim dukungan. Gagal untuk memvalidasi lencana @@ -7059,7 +7079,7 @@ Tidak dapat menyimpan perubahan. Periksa koneksi internet Anda dan coba lagi. - Couldn\'t delete call link as it is currently in use. + Tidak dapat menghapus tautan panggilan karena saat ini sedang digunakan. Hapus tautan? @@ -7148,6 +7168,10 @@ Tolak Terima + + Approve all + + Deny all Aktifkan notifikasi layar penuh? @@ -7320,7 +7344,13 @@ Lewati pemulihan? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Jika Anda melewati pemulihan, media dan lampiran yang masih ada di cadangan dapat diunduh lain waktu ketika ruang penyimpanan telah tersedia. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7353,7 +7383,11 @@ Lewati pengunduhan - "Pencadangan terakhir Anda tidak dapat diselesaikan. Pastikan ponsel terhubung ke Wi-Fi dan ketuk Cadangkan sekarang untuk coba lagi." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7397,7 +7431,7 @@ Media yang tidak digunakan akan disisihkan, tetapi dapat diunduh dari cadangan Anda kapan saja. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Pengoptimalan penyimpanan hanya dapat digunakan dengan Cadangan Signal berbayar. Langganan cadangan Anda masih diproses dan belum aktif. Coba lagi nanti. @@ -7555,6 +7589,8 @@ Perpanjang Pelajari selengkapnya + + Processing backup… Anda punya data cadangan sebesar %1$s yang tidak ada di perangkat ini. Cadangan data akan dihapus saat langganan Anda berakhir dalam %2$d hari. @@ -7573,6 +7609,8 @@ Tidak dapat menonaktifkan dan menghapus cadangan Terjadi kesalahan jaringan. Periksa koneksi internet Anda dan coba lagi. + + Uploading messages… @@ -7612,7 +7650,7 @@ Kunci cadangan Anda - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Kunci cadangan adalah kode 64 karakter yang memungkinkan Anda memulihkan data cadangan saat menginstal ulang Signal. Jika lupa kunci tersebut, Anda tidak akan dapat memulihkan data cadangan. Signal tidak dapat membantu memulihkan data cadangan Anda. @@ -7699,7 +7737,7 @@ Ponsel lama saya sudah tidak ada - Or you\'re reinstalling Signal on the same device + Atau Anda menginstal ulang Signal di perangkat yang sama Pulihkan atau transfer akun @@ -7728,15 +7766,15 @@ Masukkan kunci cadangan Anda - Your backup key is a 64-character code required to recover your account and data. + Kunci cadangan adalah kode 64 karakter yang diperlukan untuk memulihkan akun dan data Anda. Tidak punya kunci cadangan? Kunci cadangan - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Cadangan tidak dapat dipulihkan tanpa kode pemulihan 64 karakter. Jika Anda kehilangan kunci cadangan, Signal tidak dapat membantu memulihkan cadangan Anda. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Jika perangkat lama masih ada, Anda dapat melihat kunci cadangan di Pengaturan > Cadangan. Lalu ketuk Lihat kunci cadangan. Pelajari selengkapnya @@ -7785,6 +7823,18 @@ Oke + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index bf50a7fea0..4dda630d53 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1011,6 +1011,20 @@ Riprova il collegamento Prosegui senza il trasferimento + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Trasferisci la cronologia messaggi - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Non trasferire - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Scollegare “%1$s”? @@ -1355,16 +1369,18 @@ Tutti i tuoi messaggi Ripristina da backup - - Sono inclusi solo i media inviati o ricevuti negli ultimi %1$d giorni. Il tuo backup include: Ripristina backup - + Il tuo ultimo backup è stato eseguito alle ore %2$s del giorno %1$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Recupero dettagli del backup… + + Skip restore Notificami per le menzioni @@ -2464,6 +2480,8 @@ Hai fatto troppi tentativi per registrarti con questo numero. Per favore riprova tra %1$s. Impossibile connettersi al servizio. Per favore controlla la connessione a internet e riprova + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Non è stato possibile inviare il codice di verifica via SMS. Prova a richiedere l\'invio del codice tramite chiamata vocale. @@ -4311,6 +4329,8 @@ Ripristina o trasferisci Trasferisci account Salta + + Skip restore Backup delle chat Trasferisci account Trasferisci l\'account su un nuovo dispositivo Android @@ -5823,9 +5843,9 @@ Ancora in fase di elaborazione Non è stato possibile aggiungere il badge - Something went wrong + Qualcosa non ha funzionato! - Your backups subscription couldn\'t be displayed. Please contact support. + Non è stato possibile mostrare il tuo tipo di abbonamento. Ti consigliamo di contattare l\'assistenza. Validazione del badge non riuscita @@ -7216,7 +7236,7 @@ Impossibile salvare le modifiche. Controlla la tua connessione di rete e riprova. - Couldn\'t delete call link as it is currently in use. + Impossibile eliminare il link chiamata perché al momento è in uso. Vuoi eliminare il link? @@ -7307,6 +7327,10 @@ Rifiuta Approva + + Approve all + + Deny all Attivare le notifiche a schermo intero? @@ -7481,7 +7505,13 @@ Saltare il ripristino? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Se scegli di saltare il ripristino, potrai scaricare i media e gli allegati rimanenti nel tuo backup in un secondo momento quando avrai più spazio di archiviazione. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Salta download - "Non è stato possibile completare il tuo ultimo backup. Assicurati che il tuo dispositivo sia collegato a una rete Wi-Fi e tocca su \"Fai ora il backup\" per riprovare." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ I media inutilizzati verranno spostati, ma potrai scaricarli in qualsiasi momento dal tuo backup. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + L\'ottimizzazione dello spazio di archiviazione è una funzione disponibile solo con i Backup di Signal a pagamento. Il tuo abbonamento per il backup è in fase di elaborazione e per questo non è ancora attivo. Riprova più tardi. @@ -7718,6 +7752,8 @@ Rinnova Scopri di più + + Processing backup… Hai %1$s di dati di backup non presenti su questo dispositivo. Il tuo backup verrà eliminato quando il tuo abbonamento terminerà tra %2$d giorno. @@ -7738,6 +7774,8 @@ Impossibile disattivare ed eliminare i backup Si è verificato un errore di rete. Controlla la tua connessione Internet e riprova. + + Uploading messages… @@ -7777,7 +7815,7 @@ La tua chiave di backup - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + La chiave di backup è un codice di 64 caratteri che ti consente di ripristinare il tuo backup quando installi di nuovo Signal. Se dimentichi la tua chiave, non potrai ripristinare il backup. Ricorda che Signal non può aiutarti a recuperare il backup. @@ -7866,7 +7904,7 @@ Non ho il mio vecchio telefono - Or you\'re reinstalling Signal on the same device + Oppure stai reinstallando Signal sullo stesso dispositivo Ripristina o trasferisci il tuo account @@ -7895,15 +7933,15 @@ Inserisci la tua chiave di backup - Your backup key is a 64-character code required to recover your account and data. + La chiave di backup è un codice di 64 caratteri che ti consente di ripristinare il tuo account e i relativi dati. Non hai una chiave di backup? Chiave di backup - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Non puoi ripristinare un backup senza l\'apposito codice di 64 caratteri. Se hai perso la tua chiave di backup, Signal non può aiutarti a recuperare il backup. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Se hai ancora il tuo dispositivo precedente, puoi vedere la chiave di backup su Impostazioni > Backup. Poi tocca su \"Vedi chiave di backup\". Scopri di più @@ -7952,6 +7990,18 @@ Ok + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 9bfef570d8..96d3e9c9c2 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -1051,6 +1051,20 @@ לנסות לקשר שוב להמשיך בלי להעביר + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1076,13 +1090,13 @@ - Transfer message history + העברת היסטוריית הודעות - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - אל תעביר + לא להעביר - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device לבטל קישור \"%1$s\"? @@ -1431,16 +1445,18 @@ כל ההודעות שלך שחזר מגיבוי - - רק מדיה שנשלחה או התקבלה ב–%1$d הימים האחרונים כלולה. הגיבוי שלך כולל את: שחזר גיבוי - + הגיבוי האחרון שלך התבצע ב–%1$s בשעה %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. טוענים פרטי גיבוי… + + Skip restore יידע אותי לגבי אזכורים @@ -2632,6 +2648,8 @@ עשית יותר מדי ניסיונות להירשם עם המספר הזה. יש לנסות שוב עוד %1$s. לא היה ניתן להתחבר אל השירות. אנא בדוק את חיבור האינטרנט ונסה שוב. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. לא הצלחנו לשלוח לך קוד אימות דרך SMS. כדאי לנסות לקבל את הקוד שלך דרך שיחה קולית במקום. @@ -4533,6 +4551,8 @@ שחזור או העברה העבר חשבון דלג + + Skip restore גיבויי צ׳אטים העבר חשבון העבר חשבון אל מכשיר Android חדש @@ -6089,9 +6109,9 @@ עדין מעבד לא הייתה אפשרות להוסיף תג - Something went wrong + משהו השתבש - Your backups subscription couldn\'t be displayed. Please contact support. + לא ניתן היה להציג את מנוי הגיבויים שלך. יש ליצור קשר עם התמיכה. אימות התג נכשל @@ -7530,7 +7550,7 @@ לא הצלחנו לשמור את השינויים. כדאי לבדוק את חיבור הרשת שלך ולנסות שוב. - Couldn\'t delete call link as it is currently in use. + לא ניתן היה למחוק את לינק השיחה כי הוא נמצא כעת בשימוש. למחוק לינק? @@ -7625,6 +7645,10 @@ דחה אשר + + Approve all + + Deny all להפעיל התראות במסך מלא? @@ -7803,7 +7827,13 @@ לדלג על שחזור? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + בחירה בדילוג על שחזור תגרום לכך שתהיה לך אפשרות להוריד את המדיה והקבצים המצורפים הנותרים בגיבוי שלך במועד מאוחר יותר, כשיתפנה שטח אחסון. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7836,7 +7866,11 @@ דילוג על הורדה - "לא ניתן היה להשלים את הגיבוי האחרון שלך. חשוב לוודא שהטלפון שלך מחובר ל–Wi–Fi וללחוץ ״לגבות כעת״ כדי לנסות שוב." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7880,7 +7914,7 @@ מדיה שאינה בשימוש תוסר, אך ניתן להוריד אותה מהגיבוי שלך בכל עת. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + ניתן להשתמש באופטימיזציה של אחסון רק עם אפשרות הגיבוי בתשלום של Signal. מנוי הגיבויים שלך נמצא בעיבוד ואינו פעיל עדיין. יש לנסות שוב מאוחר יותר. @@ -8044,6 +8078,8 @@ חידוש למידע נוסף + + Processing backup… יש לך %1$s של גיבוי נתונים שאינם במכשיר הזה. הגיבוי שלך יימחק כשהמנוי של יסתיים בעוד יום %2$d. @@ -8068,6 +8104,8 @@ לא היה ניתן לכבות ולמחוק את הגיבויים אירעה שגיאת רשת. יש לבדוק את חיבור האינטרנט שלך ולנסות שוב. + + Uploading messages… @@ -8107,7 +8145,7 @@ מפתח הגיבוי שלך - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + מפתח הגיבוי שלך הוא קוד בן 64 ספרות שמאפשר לך לשחזר את הגיבוי שלך בעת התקנה מחדש של Signal. אם שכחת את המפתח שלך, לא תהיה לך אפשרות לשחזר את הגיבוי. Signal לא תוכל לעזור לך לשחזר את הגיבוי שלך. @@ -8200,7 +8238,7 @@ אין לי את הטלפון הישן שלי - Or you\'re reinstalling Signal on the same device + או שמדובר בהתקנה חוזרת של Signal על אותו מכשיר שחזור או העברת חשבון @@ -8229,15 +8267,15 @@ הזנת מפתח הגיבוי שלך - Your backup key is a 64-character code required to recover your account and data. + מפתח הגיבוי שלך הוא קוד בן 64 ספרות שמאפשר לך לשחזר את החשבון והנתונים שלך. אין לך מפתח גיבוי? מפתח גיבוי - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + לא ניתן לשחזר גיבויים ללא קוד השחזור בן 64 הספרות שלהם. אם איבדת את מפתח הגיבוי, Signal לא תוכל לעזור לשחזר את הגיבוי שלך. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + אם יש לך את המכשיר הישן שלך, יש לך אפשרות לראות את מפתח הגיבוי באמצעות כניסה להגדרות > גיבויים. לאחר מכן יש ללחוץ על ״הצגת מפתח גיבוי״. למידע נוסף @@ -8286,6 +8324,18 @@ בסדר + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 77b5f2cf30..f0b09ac9ad 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -991,6 +991,20 @@ もう一度リンクする 転送せずに続ける + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1016,13 +1030,13 @@ - Transfer message history + メッセージ履歴を移行する - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - 振り替えない + 移行しない - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device 「%1$s」のリンクを解除しますか? @@ -1317,16 +1331,18 @@ すべてのメッセージ バックアップから復元 - - 過去 %1$d 日間に送受信されたメディアのみが対象です。 バックアップの内容 バックアップを復元する - + 最終バックアップは%1$sの%2$sです。 + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. バックアップの詳細を取得中… + + Skip restore メンションの通知 @@ -2380,6 +2396,8 @@ この番号の登録試行回数が多すぎます。%1$s分後にもう一度お試しください。. サービスに接続できませんでした。ネットワークの接続を確認してから、再度試してください。 + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. SMSで認証コードを送信できませんでした。音声通話でコードを受信してみてください。 @@ -4200,6 +4218,8 @@ 復元または移行する アカウントの移行 スキップする + + Skip restore チャットのバックアップ アカウントの移行 新しいAndroid端末にアカウントを移行します。 @@ -5690,9 +5710,9 @@ 処理を継続しています バッジを追加できませんでした - Something went wrong + エラーが発生しました - Your backups subscription couldn\'t be displayed. Please contact support. + バックアップサブスクリプションを表示できませんでした。サポートにお問い合わせください。 バッジを確認できませんでした @@ -7059,7 +7079,7 @@ 変更内容を保存できませんでした。ネットワーク接続を確認して再度試してください。 - Couldn\'t delete call link as it is currently in use. + 通話リンクは現在使用中のため消去できませんでした。 リンクを消去しますか? @@ -7148,6 +7168,10 @@ 拒否する 承認する + + Approve all + + Deny all 全画面通知をオンにしますか? @@ -7320,7 +7344,13 @@ 復元をスキップしますか? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + 復元をスキップした場合、バックアップ内の残りのメディアと添付ファイルは、ストレージ容量が確保できたときにダウンロードできます。 + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7353,7 +7383,11 @@ ダウンロードをスキップする - "前回のバックアップは完了していません。ご利用中のスマートフォンがWi-Fiに接続されている事をご確認の上、「今すぐバックアップを実行する」をタップしてもう一度お試しください。" + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7397,7 +7431,7 @@ 未使用のメディアはオフロードされますが、バックアップからいつでもダウンロードできます。 - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + ストレージの最適化は、Signalバックアップの有料版でのみご利用いただけます。バックアップサブスクリプションはまだ処理中で、アクティブになっていません。あとでもう一度試してください。 @@ -7555,6 +7589,8 @@ 更新する 詳しく見る + + Processing backup… この端末上にないバックアップデータが %1$s あります。サブスクリプションが %2$d 日後に終了すると、バックアップは消去されます。 @@ -7573,6 +7609,8 @@ バックアップの無効化と消去ができませんでした ネットワークエラーが発生しました。インターネット接続を確認してもう一度試してください。 + + Uploading messages… @@ -7612,7 +7650,7 @@ あなたのバックアップキー - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + バックアップキーは64文字のコードで、Signalを再インストールした際には、このコードを使ってバックアップを復元することができます。 キーを忘れた場合、バックアップの復元はできません。Signalはバックアップの復元をサポートできません。 @@ -7699,7 +7737,7 @@ 以前利用していた端末がない場合 - Or you\'re reinstalling Signal on the same device + または、Signalを同じ端末に再インストールする場合 アカウントの復元または移行 @@ -7728,15 +7766,15 @@ バックアップキーを入力してください - Your backup key is a 64-character code required to recover your account and data. + バックアップキーは、アカウントとデータを復元するために必要な64文字のコードです。 バックアップキーをお持ちでない場合 バックアップキー - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + バックアップは、64文字のバックアップキーがないと復元できません。また、バックアップキーを紛失した場合、Signalはバックアップの復元対応はできません。 - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + 以前利用していた端末をお持ちの場合は、設定 >「バックアップ」でバックアップキーを確認できます。「バックアップキーを表示する」が表示されたらタップします。 詳しく見る @@ -7785,6 +7823,18 @@ OK + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 547e2f6fc4..32ee43e997 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -1011,6 +1011,20 @@ სცადე თავიდან მიბმა გადატანის გარეშე გაგრძელება + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + მიმოწერის ისტორიის გადატანა - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device არ გადაიტანო - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device გსურს, \"%1$s\" გათიშო? @@ -1355,16 +1369,18 @@ შენი ყველა შეტყობინება სარეზერვო კოპიიდან აღდგენა - - შედის მხოლოდ ბოლო %1$d დღის განმავლობაში გაგზავნილი ან მიღებული მედია ფაილები. შენს სარეზერვო კოპირებაში შედის: სარეზერვო კოპიების აღდგენა - + შენი ბოლო სარეზერვო კოპია შეიქმნა %1$s-ში %2$s-ზე. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. მიმდინარეობს სათადარიგო ასლების დეტალების მიღება… + + Skip restore შემატყობინეთ, როცა მომნიშნავენ @@ -2464,6 +2480,8 @@ ამ ნომრის დარეგისტრირება ზედმეტად ბევრჯერ სცადე. გთხოვთ, %1$s-ში თავიდან სცადო. სერვისთან დაკავშირება შეუძლებელია. გთხოვთ, შეამოწმოთ ქსელის კავშირი და ხელახლა სცადოთ. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. SMS-ით ვერიფიკაციის კოდი ვერ გამოგიგზავნეთ. სცადე კოდი ხმოვანი ზარით მიიღო. @@ -4311,6 +4329,8 @@ აღდგენა ან გადატანა მონაცემების გადატანა გამოტოვება + + Skip restore ჩატის სარეზერვო კოპიები მონაცემების გადატანა გადაიტანე მონაცემები Android-ის ახალ მოწყობილობაში @@ -5823,9 +5843,9 @@ ისევ ვამუშავებთ ემბლემის დამატება ვერ მოხერხდა - Something went wrong + რაღაც პრობლემა შეიქმნა - Your backups subscription couldn\'t be displayed. Please contact support. + შენი სათადარიგო ასლების გამოწერის ჩვენება ვერ მოხერხდა. გთხოვთ, მხარდაჭერის გუნდს მიმართო. ემბლემის ვალიდაცია ვერ მოხერხდა @@ -7216,7 +7236,7 @@ ცვლილებების შენახვა ვერ მოხერხდა. შეამოწმე შენი ინტერნეტ-კავშირი და თავიდან სცადე. - Couldn\'t delete call link as it is currently in use. + ზარის ბმულის წაშლა ვერ მოხერხდა, რადგან ის ამ მომენტში გამოყენებაშია. გსურს, ბმული წაშალო? @@ -7307,6 +7327,10 @@ უარყოფა დადასტურება + + Approve all + + Deny all გსურს შეტყობინებები სრულ ეკრანზე ჩართო? @@ -7481,7 +7505,13 @@ გსურს აღდგენის გამოტოვება? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + თუ აღდგენას გამოტოვებ, შეიძლება დარჩენილი მედია ფაილები და დანართები შენს სათადარიგო ასლებში მოგვიანებით ჩამოიტვირთოს, როცა მეხსიერებაში ადგილი გათავისუფლება. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ გადმოწერის გამოტოვება - "შენი ბოლო სათადარიგო ასლის შექმნა ვერ მოხერხდა. დარწმუნდი, რომ შენი მობილური Wi-Fi-სთან დაკავშირებულია და თავიდან საცდელად სათადარიგო ასლის ახლა შექმნას დააჭირე." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ გამოუყენებელი მედია ფაილები გაქრება, მაგრამ მათი სარეზერვო კოპიების გადმოწერა ნებისმიერ დროს შეგიძლია. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + მეხსიერების ოპტიმიზაციის გამოყენება მხოლოდ Signal-ის სათადარიგო ასლების ფასიან დონეზე შეიძლება. შენი სათადარიგო ასლების გამოწერა ჯერ კიდევ მუშავდება და ჯერ აქტიური არაა. გთხოვთ, მოგვიანებით სცადო. @@ -7718,6 +7752,8 @@ განახლება გაიგე მეტი + + Processing backup… სათადარიგო ასლის %1$s მონაცემი გაქვს, რომელიც ამ მოწყობილობაზე არაა. შენი სათადარიგო ასლი %2$d დღეში წაიშლება, როცა შენს გამოწერას ვადა გაუვა. @@ -7738,6 +7774,8 @@ სათადარიგო ასლების გამორთვა და წაშლა ვერ მოხერხდა დაფიქსირდა კავშირის ხარვეზი. გთხოვთ, შენი ინტერნეტ კავშირი შეამოწმო და თავიდან სცადო. + + Uploading messages… @@ -7777,7 +7815,7 @@ შენი სათადარიგო გასაღები - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + შენი სათადარიგო ასლების გასაღები 64-რიცხვიანი კოდია, რომელიც საშუალებას გაძლევს, შენი ჩანაწერები აღადგინო, თუ Signal-ს თავიდან გადმოიწერ. თუ შენი პინ-კოდი დაგავიწყდება, სათადარიგო ჩანაწერების აღდგენას ვეღარ შეძლებ. Signal-ი შენი სათადარიგო ჩანაწერების აღდგენაში ვერ დაგეხმარება. @@ -7866,7 +7904,7 @@ ჩემი ძველი მობილური არ მაქვს - Or you\'re reinstalling Signal on the same device + ან Signal-ს თავიდან იწერ იმავე მოწყობილობაზე აღდგენა ან ანგარიშის გადატანა @@ -7895,15 +7933,15 @@ შეიყვანე შენი სათადარიგო ასლების გასაღები - Your backup key is a 64-character code required to recover your account and data. + შენი სათადარიგო ასლების გასაღები 64 ციფრიანი კოდია, რომელიც შენი ანგარიშისა და მონაცემების აღსადგენადაა საჭირო. არ გაქვს სათადარიგო ასლების გასაღები? სათადარიგო ასლების გასაღები - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + სათადარიგო ასლების აღდგენა მათი აღდგენის 64 ციფრიანი კოდის გარეშე შეუძლებელია. თუ შენი სათადარიგო ასლების გასაღები დაკარგე, Signal შენი სათადარიგო ასლების აღდგენაში დახმარებას ვერ შეძლებს. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + თუ ძველი მოწყობილობა გაქვს, შეგიძლია, შენი სათადარიგო ასლების გასაღები აქ ნახო: პარამეტრები > სათადარიგო ასლები. შემდეგ სათადარიგო ასლების გასაღების ნახვას დააჭირე. გაიგე მეტი @@ -7952,6 +7990,18 @@ კარგი + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 5718ca29e7..b615ae112c 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -1011,6 +1011,20 @@ Қайта байланыстырып көру Тасымалдамай жалғастыру + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Хабарлар тарихын тасымалдау - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Аудармау + Тасымалдамау - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\" құрылғысын ажырату керек пе? @@ -1355,16 +1369,18 @@ Барлық хат Резервтік көшірмеден қалпына келтіру - - Соңғы %1$d күнде жіберілген немесе қабылданған мультимедиа ғана кіреді. Резервтік көшірме: Сақтық көшірмені қалпына келтіру - + Соңғы резервтік көшірме %1$s күні сағат%2$s жасалды. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Сақтық көшірме мәліметтері алынып жатыр… + + Skip restore Менің атым аталғанда хабарландыру алғым келеді @@ -2464,6 +2480,8 @@ Осы нөмірді тіркеу үшін сіз тым көп әрекет жасадыңыз. %1$s өткен соң, қайталап көріңіз. Қызметке жалғану мүмкін емес. Желіге жалғанғаныңызды тексеріңіз де, тағы бір рет байқап көріңіз. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Верификация кодын SMS арқылы жібере алмадық. Кодты дауыстық қоңырау арқылы алып көріңіз. @@ -4311,6 +4329,8 @@ Қалпына келтіру немесе тасымалдау Аккаунтты тасымалдау Өткізіп жіберу + + Skip restore Чаттың резервтік көшірмелері Аккаунтты тасымалдау Аккаунтты жаңа Android құрылғысына тасымалдау @@ -5823,9 +5843,9 @@ Әлі өңделуде Таңба қосу мүмкін емес - Something went wrong + Бірдеңе дұрыс болмады - Your backups subscription couldn\'t be displayed. Please contact support. + Сақтық көшірмелер жазылымы көрсетілмеді. Қолдау көрсету орталығына хабарласыңыз. Таңбаның жарамдылығын тексеру мүмкін болмады @@ -7216,7 +7236,7 @@ Өзгерістер сақталмады. Желі байланысын тексеріп, қайталап көріңіз. - Couldn\'t delete call link as it is currently in use. + Қоңырау сілтемесі жойылмады, себебі ол қазір қолданылып жатыр. Сілтемені жою керек пе? @@ -7307,6 +7327,10 @@ Бас тарту Мақұлдау + + Approve all + + Deny all Толық экран хабарландыруларын қосу керек пе? @@ -7481,7 +7505,13 @@ Қалпына келтіруді өткізіп жіберу керек пе? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Қалпына келтіру процесін өткізіп жіберсеңіз, жад көлемі жеткілікті болғанда, сақтық көшірмедегі қалған мультимедианы және тіркемелерді кейінірек жүктеп алуға болады. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Жүктеп алуды өткізіп жіберу - "Соңғы сақтық көшірме жасалмады. Телефоныңыз Wi-Fi желісіне қосылып тұрғанын тексеріп, \"Сақтық көшірмені қазір жасау\" түймесін түртіңіз де, қайталап көріңіз." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Қолданылмаған мультимедиа алып тасталады, бірақ оны резервтік көшірмеден кез келген уақытта жүктеп алуға болады. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Жадты оңтайландыру үшін Signal сақтық көшірмелерінің ақылы деңгейі керек. Сақтық көшірмелер жазылымы әлі өңделіп жатыр және әзірге белсенді емес. Кейінірек қайталап көріңіз. @@ -7718,6 +7752,8 @@ Жаңарту Толық ақпарат + + Processing backup… Сізде %1$s сақтық көшірме деректері бар, олар бұл құрылғыда емес. %2$d күннен кейін жазылым мерзімі аяқталғанда, сақтық көшірмеңіз жойылады. @@ -7738,6 +7774,8 @@ Сақтық көшірмелер өшірілмеді және жойылмады Желі қатесі пайда болды. Интернет байланысын тексеріп, қайталап көріңіз. + + Uploading messages… @@ -7777,7 +7815,7 @@ Резервтік кілтіңіз - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Сақтық кілтіңіз – 64 таңбалы код. Signal қолданбасын қайта орнатқан кезде, ол сақтық көшірмеңізді қалпына келтіруге мүмкіндік береді. Кілтіңізді ұмытып қалсаңыз, резервтік көшірмені қалпына келтіре алмайсыз. Signal резервтік көшірмені қалпына келтіруге көмектесе алмайды. @@ -7866,7 +7904,7 @@ Ескі телефоным жоқ - Or you\'re reinstalling Signal on the same device + Signal қолданбасын бір құрылғыға қайта орнатып жатырсыз Аккаунтты қалпына келтіру немесе тасымалдау @@ -7895,15 +7933,15 @@ Сақтық кілтті енгізіңіз - Your backup key is a 64-character code required to recover your account and data. + Сақтық кілт – аккаунтыңыз бен деректеріңізді қалпына келтіруге қажетті 64 таңбалы код. Сақтық кілт жоқ па? Сақтық кілт - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Сақтық көшірмелер 64 таңбалы кодсыз қалпына келтірілмейді. Сақтық кілтіңізді жоғалтып алсаңыз, Signal сақтық көшірмеңізді қалпына келтіруге көмектесе алмайды. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Ескі құрылғыңыз болса, сақтық кілтіңізді көру үшін \"Параметрлер > Сақтық көшірмелер\" тармағына өтіңіз. Содан кейін \"Сақтық кілтті көру\" түймесін түртіңіз. Толық ақпарат @@ -7952,6 +7990,18 @@ Жарайды + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-km/strings.xml b/app/src/main/res/values-km/strings.xml index ee02bc0a15..7962ae9ce2 100644 --- a/app/src/main/res/values-km/strings.xml +++ b/app/src/main/res/values-km/strings.xml @@ -991,6 +991,20 @@ សាកល្បងភ្ជាប់ម្តងទៀត បន្តដោយមិនចាំបាច់ផ្ទេរ + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1016,13 +1030,13 @@ - Transfer message history + ប្រវត្តិផ្ទេរសារ - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - កុំផ្ទេរចេញ + កុំផ្ទេរ - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device ផ្តាច់ \"%1$s\" ឬ? @@ -1317,16 +1331,18 @@ សារទាំងអស់របស់អ្នក ស្តារចេញពីការបម្រុងទុក - - មានតែមេឌៀដែលបានផ្ញើ ឬទទួលបានក្នុងរយៈពេល %1$d ថ្ងៃចុងក្រោយប៉ុណ្ណោះដែលត្រូវបានរួមបញ្ចូល។ ការបម្រុងទុករបស់អ្នករួមមាន៖ ស្តារការបម្រុងទុក - + ការបម្រុងទុកចុងក្រោយរបស់អ្នកត្រូវបានធ្វើឡើងនៅ %1$s ម៉ោង %2$s។ + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. កំពុងទាញយកព័ត៌មានលម្អិតនៃការបម្រុងទុក… + + Skip restore ជូនដំណឹងខ្ញុំសម្រាប់ការហៅឈ្មោះ @@ -2380,6 +2396,8 @@ អ្នកបានព្យាយាមជាច្រើនដងពេកក្នុងការចុះឈ្មោះលេខនេះ។ សូមព្យាយាមម្តងទៀតក្នុងរយៈពេល %1$s។ មិនអាចតភ្ជាប់សេវាបាន។ សូមពិនិត្យការតភ្ជាប់បណ្តាញ ហើយព្យាយាមម្តងទៀត។ + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. យើងមិនអាចផ្ញើលេខកូដផ្ទៀងផ្ទាត់តាមរយៈសារជាអក្សរទេ។ សាកល្បងទទួលលេខកូដរបស់អ្នកតាមរយៈការហៅជាសំឡេងជំនួសវិញ។ @@ -4200,6 +4218,8 @@ ស្តារ ឬផ្ទេរ បញ្ជូនគណនី រំលង + + Skip restore ការបម្រុងទុកការជជែក បញ្ជូនគណនី បញ្ជូនគណនី ទៅកាន់ឧបករណ៍ Android ថ្មីមួយ @@ -5690,9 +5710,9 @@ កំពុងដំណើការ មិនអាចដាក់ស្លាកបាន - Something went wrong + មានអ្វីមួយខុសប្រក្រតី - Your backups subscription couldn\'t be displayed. Please contact support. + ការជាវការបម្រុងទុករបស់អ្នកមិនអាចបង្ហាញបានទេ។ សូមទាក់ទងទៅកាន់ផ្នែកជំនួយ។ មិនអាចបញ្ជាក់ស្លាកបាន @@ -7059,7 +7079,7 @@ មិនអាចរក្សាទុកការផ្លាស់ប្តូរ។ សូមពិនិត្យមើលសេវាអ៊ីនធឺណិតរបស់អ្នក រួចព្យាយាមម្តងទៀត។ - Couldn\'t delete call link as it is currently in use. + មិនអាចលុបតំណហៅទូរសព្ទបានទេ ដោយសារវាកំពុងត្រូវបានគេប្រើ។ លុបតំណឬ? @@ -7148,6 +7168,10 @@ ច្រានចោល អនុញាតិ + + Approve all + + Deny all បើកការជូនដំណឹងពេញអេក្រង់ឬ? @@ -7320,7 +7344,13 @@ រំលងការស្តារឡើងវិញ? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + ប្រសិនបើអ្នករំលងការស្ដារឡើងវិញ នោះមេឌៀ និងឯកសារភ្ជាប់ដែលនៅសល់នៅក្នុងការបម្រុងទុករបស់អ្នកអាចទាញយកបាននៅពេលក្រោយ នៅពេលដែលមានទំហំផ្ទុក។ + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7353,7 +7383,11 @@ រំលងការទាញយក - "ការបម្រុងទុកចុងក្រោយរបស់អ្នកមិនអាចបញ្ចប់បានទេ។ ត្រូវប្រាកដថាទូរសព្ទរបស់អ្នកបានភ្ជាប់ Wi-Fi រួចចុចបម្រុងទុកឥឡូវនេះ ដើម្បីព្យាយាមម្តងទៀត។" + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7397,7 +7431,7 @@ មេឌៀដែលមិនបានប្រើនឹងត្រូវបានបិទ ប៉ុន្តែអាចទាញយកពីការបម្រុងទុករបស់អ្នកបានគ្រប់ពេល។ - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + ការបង្កើនប្រសិទ្ធភាពទំហំផ្ទុកអាចប្រើបានតែជាមួយការបម្រុងទុក Signal នៅក្នុងកម្រិតបង់ប្រាក់ប៉ុណ្ណោះ។ ការជាវការបម្រុងទុករបស់អ្នកកំពុងដំណើរការនៅឡើយ ហើយមិនទាន់សកម្មទេ។ សូមព្យាយាមម្តងទៀតនៅពេលក្រោយ។ @@ -7555,6 +7589,8 @@ បន្ត ស្វែងយល់បន្ថែម + + Processing backup… អ្នកមានទិន្នន័យបម្រុងទុក %1$s ដែលមិនមាននៅលើឧបករណ៍នេះទេ។ ការបម្រុងទុករបស់អ្នកនឹងត្រូវបានលុប នៅពេលដែលការជាវរបស់អ្នកបញ្ចប់ក្នុងរយៈពេល %2$d ថ្ងៃទៀត។ @@ -7573,6 +7609,8 @@ មិនអាចបិទ និងលុបការបម្រុងទុកទេ មានបញ្ហាបណ្ដាញកើតឡើង។ សូមពិនិត្យមើលសេវាអ៊ីនធឺណិតរបស់អ្នក រួចព្យាយាមម្តងទៀត។ + + Uploading messages… @@ -7612,7 +7650,7 @@ សោបម្រុងទុករបស់អ្នក - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + សោបម្រុងទុករបស់អ្នកគឺជាលេខកូដមាន 64 តួអក្សរដែលអាចឱ្យអ្នកស្តារការបម្រុងទុករបស់អ្នកមកវិញនៅពេលអ្នកដំឡើង Signal ឡើងវិញ។ ប្រសិនបើអ្នកភ្លេចសោរបស់អ្នក អ្នកនឹងមិនអាចស្ដារការបម្រុងទុករបស់អ្នកបានទេ។ Signal មិនអាចជួយអ្នកក្នុងការស្តារការបម្រុងទុករបស់អ្នកបានទេ។ @@ -7699,7 +7737,7 @@ ខ្ញុំមិនមានទូរសព្ទចាស់ទេ - Or you\'re reinstalling Signal on the same device + ឬអ្នកកំពុងដំឡើង Signal ឡើងវិញនៅលើឧបករណ៍តែមួយ ស្តារ ឬផ្ទេរគណនី @@ -7728,15 +7766,15 @@ បញ្ចូលសោបម្រុងទុករបស់អ្នក - Your backup key is a 64-character code required to recover your account and data. + សោបម្រុងទុករបស់អ្នកគឺជាលេខកូដមាន 64 តួអក្សរដែលត្រូវការចាំបាច់ដើម្បីស្តារយកគណនី និងទិន្នន័យរបស់អ្នកមកវិញ។ គ្មានសោបម្រុងទុកទេ? សោបម្រុងទុក - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + ការបម្រុងទុកមិនអាចស្តារបានទេបើវាគ្មានលេខកូដស្តារដែលមាន 64 តួអក្សរនោះទេ។ ប្រសិនបើអ្នកបាត់សោបម្រុងទុករបស់អ្នក Signal មិនអាចជួយស្ដារការបម្រុងទុករបស់អ្នកបានទេ។ - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + ប្រសិនបើអ្នកមានឧបករណ៍ចាស់របស់អ្នក អ្នកអាចមើលសោបម្រុងទុករបស់អ្នកនៅក្នុងការកំណត់ > ការបម្រុងទុក។ បន្ទាប់មកចុច មើលសោបម្រុងទុក។ ស្វែងយល់បន្ថែម @@ -7785,6 +7823,18 @@ យល់ព្រម + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index 664d22569e..ce685515e3 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -1011,6 +1011,20 @@ ಮತ್ತೊಮ್ಮೆ ಲಿಂಕ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ ವರ್ಗಾವಣೆ ಮಾಡದೆಯೇ ಮುಂದುವರೆಸಿ + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + ಮೆಸೇಜ್ ಇತಿಹಾಸವನ್ನು ವರ್ಗಾಯಿಸಿ - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device ವರ್ಗಾವಣೆ ಮಾಡಬೇಡಿ - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\" ಅನ್ ಲಿಂಕ್ ಮಾಡಬೇಕೇ? @@ -1355,16 +1369,18 @@ ನಿಮ್ಮ ಎಲ್ಲಾ ಮೆಸೇಜ್‌ಗಳು ಬ್ಯಾಕಪ್‌ನಿಂದ ರಿಸ್ಟೋರ್ ಮಾಡಿ - - ಕಳೆದ %1$d ದಿನಗಳಲ್ಲಿ ಕಳುಹಿಸಿದ ಅಥವಾ ಸ್ವೀಕರಿಸಿದ ಮೀಡಿಯಾವನ್ನು ಮಾತ್ರ ಸೇರಿಸಲಾಗಿದೆ. ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಇವುಗಳನ್ನು ಒಳಗೊಂಡಿರುತ್ತದೆ: ಬ್ಯಾಕಪ್ ಮರುಸ್ಥಾಪಿಸಿ - + ನಿಮ್ಮ ಕೊನೆಯ ಬ್ಯಾಕಪ್ ಅನ್ನು %1$s ರಂದು %2$s ಕ್ಕೆ ಮಾಡಲಾಗಿದೆ. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. ಬ್ಯಾಕಪ್ ವಿವರಗಳನ್ನು ಪಡೆಯಲಾಗುತ್ತಿದೆ… + + Skip restore \@ಉಲ್ಲೇಖಗಳು ಬಂದರೆ ಎಚ್ಚರಿಸು @@ -2464,6 +2480,8 @@ ಈ ಸಂಖ್ಯೆಯನ್ನು ನೋಂದಾಯಿಸಲು ನೀವು ತುಂಬಾ ಪ್ರಯತ್ನಗಳನ್ನು ಮಾಡಿದ್ದೀರಿ. ದಯವಿಟ್ಟು %1$s ನಲ್ಲಿ ಮತ್ತೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಿ. ಸೇವೆಗೆ ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ದಯವಿಟ್ಟು ನೆಟ್‌ವರ್ಕ್ ಸಂಪರ್ಕವನ್ನು ಪರಿಶೀಲಿಸಿ ಮತ್ತು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. SMS ಮೂಲಕ ನಿಮಗೆ ದೃಢೀಕರಣ ಕೋಡ್ ಅನ್ನು ಕಳುಹಿಸಲು ನಮಗೆ ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ಬದಲಾಗಿ ಧ್ವನಿ ಕರೆ ಮೂಲಕ ನಿಮ್ಮ ಕೋಡ್ ಸ್ವೀಕರಿಸಲು ಪ್ರಯತ್ನಿಸಿ. @@ -4311,6 +4329,8 @@ ರಿಸ್ಟೋರ್ ಮಾಡಿ ಅಥವಾ ವರ್ಗಾಯಿಸಿ ಖಾತೆ ವರ್ಗಾಯಿಸಿ ಬಿಟ್ಟು ಮುಂದುವರಿ + + Skip restore ಚಾಟ್ ಬ್ಯಾಕಪ್‌ಗಳು ಖಾತೆ ವರ್ಗಾಯಿಸಿ ಹೊಸ ಆಂಡ್ರಾಯ್ಡ್ ಸಾಧನಕ್ಕೆ ಖಾತೆ ವರ್ಗಾವಣೆ ಮಾಡಿ @@ -5823,9 +5843,9 @@ ಇನ್ನೂ ಪ್ರಕ್ರಿಯೆಯಲ್ಲಿದೆ ಬ್ಯಾಡ್ಜ್ ಸೇರಿಸಲು ಆಗಲಿಲ್ಲ - Something went wrong + ಏನೋ ತಪ್ಪಾಗಿದೆ - Your backups subscription couldn\'t be displayed. Please contact support. + ನಿಮ್ಮ ಬ್ಯಾಕಪ್‌ಗಳ ಚಂದಾದಾರಿಕೆಯನ್ನು ಪ್ರದರ್ಶಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ದಯವಿಟ್ಟು ಬೆಂಬಲವನ್ನು ಸಂಪರ್ಕಿಸಿ. ಬ್ಯಾಡ್ಜ್ ಅನ್ನು ಮೌಲ್ಯೀಕರಿಸಲು ವಿಫಲವಾಗಿದೆ @@ -7216,7 +7236,7 @@ ಬದಲಾವಣೆಗಳನ್ನು ಉಳಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ನಿಮ್ಮ ನೆಟ್‌ವರ್ಕ್ ಸಂಪರ್ಕವನ್ನು ಪರಿಶೀಲಿಸಿ ಹಾಗೂ ಇನ್ನೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಿ. - Couldn\'t delete call link as it is currently in use. + ಕಾಲ್ ಲಿಂಕ್ ಪ್ರಸ್ತುತ ಬಳಕೆಯಲ್ಲಿರುವುದರಿಂದ ಅದನ್ನು ಅಳಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ಲಿಂಕ್ ಅಳಿಸಬೇಕೇ? @@ -7307,6 +7327,10 @@ ತಿರಸ್ಕರಿಸಿ ಅನುಮತಿಸಿ + + Approve all + + Deny all ಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆನ್ ಮಾಡಬೇಕೇ? @@ -7481,7 +7505,13 @@ ರಿಸ್ಟೋರ್ ಸ್ಕಿಪ್ ಮಾಡಬೇಕೇ? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + ನಿಮ್ಮ ಬ್ಯಾಕಪ್‌ನಲ್ಲಿ ಉಳಿದಿರುವ ಮೀಡಿಯಾ ಮತ್ತು ಅಟ್ಯಾಚ್‌ಮೆಂಟ್‌ಗಳನ್ನು ರಿಸ್ಟೋರ್ ಮಾಡುವುದನ್ನು ನೀವು ಸ್ಕಿಪ್ ಮಾಡಿದರೆ, ನಂತರದ ಸಮಯದಲ್ಲಿ ಸಂಗ್ರಹಣೆ ಸ್ಥಳಾವಕಾಶ ಲಭ್ಯವಾದಾಗ ಡೌನ್‌ಲೋಡ್ ಮಾಡಬಹುದು. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ ಡೌನ್ ಲೋಡ್ ಸ್ಕಿಪ್ ಮಾಡಿ - "ನಿಮ್ಮ ಕೊನೆಯ ಬ್ಯಾಕಪ್ ಪೂರ್ತಿಯಾಗಿಲ್ಲ. ನಿಮ್ಮ ಫೋನ್ ವೈ- ಫೈ ಸಂಪರ್ಕ ಹೊಂದಿರುವ ಬಗ್ಗೆ ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಮತ್ತು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಲು ಬ್ಯಾಕಪ್ ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ ಬಳಕೆಯಾಗದ ಮೀಡಿಯಾವನ್ನು ಆಫ್‌ಲೋಡ್ ಮಾಡಲಾಗುತ್ತದೆ, ಆದರೆ ನಿಮ್ಮ ಬ್ಯಾಕಪ್‌ನಿಂದ ಯಾವಾಗ ಬೇಕಾದರೂ ಡೌನ್‌ಲೋಡ್ ಮಾಡಬಹುದು. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Signal ಬ್ಯಾಕಪ್‌ಗಳ ಪಾವತಿಸಿದ ಶ್ರೇಣಿಯೊಂದಿಗೆ ಮಾತ್ರ ಸಂಗ್ರಹಣೆ ಆಪ್ಟಿಮೈಸೇಶನ್ ಅನ್ನು ಬಳಸಬಹುದು. ನಿಮ್ಮ ಬ್ಯಾಕಪ್‌ಗಳ ಚಂದಾದಾರಿಕೆಯು ಇನ್ನೂ ಪ್ರಕ್ರಿಯೆಯಲ್ಲಿದೆ ಮತ್ತು ಇನ್ನೂ ಸಕ್ರಿಯವಾಗಿಲ್ಲ. ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ. @@ -7718,6 +7752,8 @@ ನವೀಕರಿಸಿ ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ + + Processing backup… ನಿಮ್ಮ %1$s ಬ್ಯಾಕಪ್ ಡೇಟಾ ಈ ಸಾಧನದಲ್ಲಿ ಇಲ್ಲ. ನಿಮ್ಮ ಚಂದಾದಾರಿಕೆ ಕೊನೆಯಾಗುವ %2$d ದಿನದ ಮೊದಲು ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಅನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ. @@ -7738,6 +7774,8 @@ ಬ್ಯಾಕಪ್ ರದ್ದು ಮಾಡಲು ಮತ್ತು ಅಳಿಸಿ ಹಾಕಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ ನೆಟ್ ವರ್ಕ್ ದೋಷ ಕಾಣಿಸಿದೆ. ನಿಮ್ಮ ಇಂಟರ್ ನೆಟ್ ಸಂಪರ್ಕವನ್ನು ಪರಿಶೀಲಿಸಿ ಮತ್ತು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ. + + Uploading messages… @@ -7777,7 +7815,7 @@ ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಕೀ - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಕೀ 64-ಅಕ್ಷರಗಳ ಕೋಡ್ ಆಗಿದ್ದು, ನೀವು Signal ಅನ್ನು ಮರು-ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿದಾಗ ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಅನ್ನು ರಿಸ್ಟೋರ್ ಮಾಡಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ. ನಿಮ್ಮ ಕೀಯನ್ನು ನೀವು ಮರೆತರೆ, ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಅನ್ನು ರಿಸ್ಟೋರ್ ಮಾಡಲು ನಿಮಗೆ ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ. ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಅನ್ನು ರಿಕವರ್ ಮಾಡಲು Signal ನಿಮಗೆ ಸಹಾಯ ಮಾಡಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ. @@ -7866,7 +7904,7 @@ ನನ್ನ ಬಳಿ ನನ್ನ ಹಳೆಯ ಫೋನ್ ಇಲ್ಲ - Or you\'re reinstalling Signal on the same device + ಅಥವಾ ನೀವು ಅದೇ ಸಾಧನದಲ್ಲಿ Signal ಅನ್ನು ಮರು-ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಖಾತೆಯನ್ನು ರಿಸ್ಟೋರ್ ಮಾಡಿ ಅಥವಾ ವರ್ಗಾಯಿಸಿ @@ -7895,15 +7933,15 @@ ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಕೀ ನಮೂದಿಸಿ - Your backup key is a 64-character code required to recover your account and data. + ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಕೀ, ನಿಮ್ಮ ಖಾತೆ ಮತ್ತು ಡೇಟಾವನ್ನು ಮರುಪಡೆಯಲು ಅಗತ್ಯವಿರುವ 64-ಅಕ್ಷರಗಳ ಕೋಡ್ ಆಗಿದೆ. ಬ್ಯಾಕಪ್ ಕೀ ಇಲ್ಲವೇ? ಬ್ಯಾಕಪ್ ಕೀ - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + 64-ಅಕ್ಷರಗಳ ರಿಕವರಿ ಕೋಡ್ ಇಲ್ಲದೆ ಬ್ಯಾಕಪ್‌ಗಳನ್ನು ರಿಕವರ್ ಮಾಡಲಾಗುವುದಿಲ್ಲ. ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಕೀಯನ್ನು ನೀವು ಕಳೆದುಕೊಂಡಿದ್ದರೆ ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಅನ್ನು ರಿಸ್ಟೋರ್ ಮಾಡಲು Signal ಸಹಾಯ ಮಾಡಲಾಗುವುದಿಲ್ಲ. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + ನಿಮ್ಮ ಬಳಿ ನಿಮ್ಮ ಹಳೆಯ ಸಾಧನವಿದ್ದರೆ ನೀವು ಸೆಟ್ಟಿಂಗ್‌ಗಳು > ಬ್ಯಾಕಪ್‌ಗಳು ಎಂಬುದರಲ್ಲಿ ನಿಮ್ಮ ಬ್ಯಾಕಪ್ ಕೀಯನ್ನು ವೀಕ್ಷಿಸಬಹುದು. ನಂತರ ಬ್ಯಾಕಪ್ ಕೀ ವೀಕ್ಷಿಸಿ ಎಂಬುದನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ. ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ @@ -7952,6 +7990,18 @@ ಸರಿ + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 1d18b95736..38f7fede77 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -991,6 +991,20 @@ 다시 연결 시도 전송하지 않고 계속 + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1016,13 +1030,13 @@ - Transfer message history + 메시지 기록 전송 - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - 이체하지 마세요 + 전송 안 함 - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\" 기기의 연결을 해제하시겠습니까? @@ -1317,16 +1331,18 @@ 모든 메시지 백업에서 복원 - - 지난 %1$d일간 보내거나 받은 미디어만 포함합니다. 백업 내용: 백업 복원 - + 마지막 백업이 완료된 시간은 %1$s %2$s입니다. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. 백업 세부 정보를 가져오는 중… + + Skip restore 멘션 알림 @@ -2380,6 +2396,8 @@ 이 번호를 등록하려고 너무 많이 시도했습니다. %1$s 후에 다시 시도해 주세요. 서비스에 연결할 수 없습니다. 네트워크 연결을 확인 후 다시 시도해 주세요. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. SMS를 통해 확인 코드를 보낼 수 없습니다. 음성 통화로 대신 받아 보세요. @@ -4200,6 +4218,8 @@ 복원 또는 전송 계정 이전 건너뛰기 + + Skip restore 대화 백업 계정 이전 새 Android 장치로 계정 이전 @@ -5690,9 +5710,9 @@ 아직 처리 중 배지를 추가할 수 없습니다. - Something went wrong + 오류가 발생했습니다 - Your backups subscription couldn\'t be displayed. Please contact support. + 백업 구독을 표시할 수 없습니다. 고객 지원팀에 문의하세요. 배지를 확인하지 못했습니다. @@ -7059,7 +7079,7 @@ 변경 내용을 저장할 수 없습니다. 네트워크 연결을 확인하고 다시 시도하세요. - Couldn\'t delete call link as it is currently in use. + 현재 통화 링크를 사용 중이므로 삭제할 수 없습니다. 링크를 삭제할까요? @@ -7148,6 +7168,10 @@ 거절 승인 + + Approve all + + Deny all 전체 화면 알림을 켜시겠습니까? @@ -7320,7 +7344,13 @@ 복원을 건너뛰시겠어요? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + 복원을 건너뛸 경우 나중에 저장 공간을 사용할 수 있을 때 백업의 남은 미디어와 첨부 파일을 다운로드할 수 있습니다. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7353,7 +7383,11 @@ 다운로드 건너뛰기 - "마지막 백업을 완료하지 못했습니다. 휴대폰이 Wi-Fi에 연결되어 있는지 확인하고 지금 백업을 탭하여 다시 시도하세요." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7397,7 +7431,7 @@ 사용하지 않는 미디어를 오프로드하지만, 언제든지 백업에서 다운로드할 수 있습니다. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + 저장 공간 최적화는 유료 Signal 백업으로만 사용할 수 있습니다. 백업 구독이 아직 처리 중이며 활성화되지 않았습니다. 나중에 다시 시도해 주세요. @@ -7555,6 +7589,8 @@ 갱신 자세히 알아보기 + + Processing backup… 이 기기에 있지 않은 백업 데이터가 %1$s 있습니다. %2$d일 후 구독이 종료되면 백업이 삭제됩니다. @@ -7573,6 +7609,8 @@ 백업을 비활성화하고 삭제하지 못함 네트워크 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도하세요. + + Uploading messages… @@ -7612,7 +7650,7 @@ 내 백업 키 - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + 백업 키는 Signal을 다시 설치할 경우 백업을 복원하는 데 사용할 수 있는 64자리 코드입니다. 키를 잊어버리면 백업을 복원할 수 없습니다. Signal은 백업을 복원하도록 도와드릴 수 없습니다. @@ -7699,7 +7737,7 @@ 이전 휴대폰이 없음 - Or you\'re reinstalling Signal on the same device + 또는 동일한 기기에 Signal을 다시 설치하는 중입니다 계정 복원 또는 전송 @@ -7728,15 +7766,15 @@ 백업 키 입력 - Your backup key is a 64-character code required to recover your account and data. + 백업 키는 계정과 데이터를 복원하는 데 필요한 64자리 코드입니다. 백업 키가 없으신가요? 백업 키 - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + 64자리 복원 코드 없이는 백업을 복원할 수 없습니다. 백업 키를 분실한 경우 Signal에서 백업을 복원할 수 없습니다. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + 이전 기기를 가지고 있는 경우 백업 키를 확인할 수 있습니다. 설정 > 백업으로 이동한 다음 백업 키 보기를 탭하세요. 자세히 알아보기 @@ -7785,6 +7823,18 @@ 확인 + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-ky/strings.xml b/app/src/main/res/values-ky/strings.xml index e1604a7035..abcf2b8d45 100644 --- a/app/src/main/res/values-ky/strings.xml +++ b/app/src/main/res/values-ky/strings.xml @@ -991,6 +991,20 @@ Кайра байланышып көрүңүз Билдирүүлөрдү өткөрбөй улантуу + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1016,13 +1030,13 @@ - Transfer message history + Билдирүүлөр таржымалын өткөрүү - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Которбоо + Өтпөсүн - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\" ажыратасызбы? @@ -1317,16 +1331,18 @@ Бардык билдирүүлөрүңүз Камдык көчүрмөдөн калыбына келтирүү - - Соңку %1$d күндө жөнөтүлгөн/алынган медиа файлдар гана камтылды. Камдык көчүрмөлөрүңүздө эмнелер камтылган: Резервдик копияны эски калыбына келтирүү - + Камдык көчүрмө акыркы жолу качан сакталган: %1$s, %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Камдык көчүрмөнүн чоо-жайы алынууда… + + Skip restore Мени айтып өткөндөр тууралуу билип турайын @@ -2380,6 +2396,8 @@ Бул номерди каттаганга өтө көп жолу аракет кылдыңыз. %1$s кийин кайталап көрүңүз. Сервиске туташылбай жатат. Тармактык туташууну текшерип, дагы бир жолу аракет кылып көрүңүз. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Ырастоо кодун SMS аркылуу жөнөтө албайбыз. Кодду аудио чалуу аркылуу алганга аракет кылып көрүңүз. @@ -4200,6 +4218,8 @@ Калыбына келтирүү же өткөрүү Аккаунтту которуу Өткөрүп жиберүү + + Skip restore Маектердин камдык көчүрмөсү Аккаунтту которуу Аккаунтту жаңы Android түзмөгүнө өткөрүү @@ -5690,9 +5710,9 @@ Дагы эле иштетилүүдө Төшбелги кошулган жок. - Something went wrong + Бир жерден ката кетти - Your backups subscription couldn\'t be displayed. Please contact support. + Камдык көчүрмөгө жазылууңузду көрсөтүү мүмкүн эмес. Кардарларды колдоо кызматына кайрылыңыз. Төшбелгинин жарактуулугу текшерилбей калды @@ -7059,7 +7079,7 @@ Өзгөрүүлөр сакталган жок. Байланышыңызды текшерип, кайталап көрүңүз. - Couldn\'t delete call link as it is currently in use. + Чалуу шилтемеси колдонулуп жаткандыктан, аны өчүрүү мүмкүн эмес. Шилтемени өчүрөсүзбү? @@ -7148,6 +7168,10 @@ Четке кагуу Ырастоо + + Approve all + + Deny all Толук экран билдирмелерин күйгүзөсүзбү? @@ -7320,7 +7344,13 @@ Калыбына келтирүү өткөрүп жиберилсинби? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Өткөрүп жиберсеңиз, камдык көчүрмөлөрдөгү калган медиа файлдар менен тиркемелерди кийинчерээк жетиштүү орун болгондо жүктөп алсаңыз болот. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7353,7 +7383,11 @@ Өткөрүп жиберүү - "Акыркы камдык көчүрмөңүз аягына чейин чыккан жок. Телефонуңуздун Wi-Fi\'га туташып турганын текшерип, \"Камдык көчүрмөнү сактоо\" дегенди басып кайталап көрүңүз." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7397,7 +7431,7 @@ Колдонулбаган медиа файлдар чыгарылып, аларды камдык көчүрмөлөрдөн каалаган убакта жүктөп алсаңыз болот. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Сактоо мейкиндигин кеңейтүү үчүн Signal\'дын камдык көчүрмөлөрдү сактоо кызматынын акы алынуучу деңгээлине көтөрүлүү керек. Камдык көчүрмөгө жазылууңуз каралып жатат. Бир аздан кийин кайталап көрүңүз. @@ -7555,6 +7589,8 @@ Жаңыртуу Кененирээк маалымат + + Processing backup… %1$s камдык көчүрмөңүз бул түзмөктө эмес. Жазылууңуздун мөөнөтү %2$d күндөн кийин аяктаганда, камдык көчүрмөңүз өчүп калат. @@ -7573,6 +7609,8 @@ Камдык көчүрмөлөрдү токтотуп, өчүрүү мүмкүн болгон жок Тармакта ката кетти. Интернет байланышыңызды текшерип, кайталап көрүңүз. + + Uploading messages… @@ -7612,7 +7650,7 @@ Камдык көчүрмөнүн ачкычы - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Камдык көчүрмөңүздүн ачкычы 64 орундуу код болуп, Signal\'ды кайра орнотконуңузда сакталган нерселерди калыбына келтиргенге жардам берет. PIN кодуңузду унутуп калсаңыз, камдык көчүрмөлөрүңүздү калыбына келтире албай каласыз. Signal да жардам бере албайт. @@ -7699,7 +7737,7 @@ Эски телефонум жок - Or you\'re reinstalling Signal on the same device + Же Signal\'ды ошол эле телефонго кайра орнотуңуз Аккаунтту калыбына келтирүү же өткөрүү @@ -7728,15 +7766,15 @@ Камдык көчүрмөнүн ачкычын киргизиңиз - Your backup key is a 64-character code required to recover your account and data. + Ал аккаунтуңузду жана андагы нерселерди калыбына келтирүүчү 64 орундуу код. Камдык көчүрмөнүн ачкычы жокпу? Камдык көчүрмөнүн ачкычы - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Камдык көчүрмөлөрүңүздү 64 орундуу кодсуз калыбына келтире албайсыз. Ачкычыңызды жоготуп алсаңыз, Signal камдык көчүрмөлөрүңүздү калыбына келтире албайт. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Эски түзмөгүңүз болсо, камдык көчүрмөнүн ачкычын Параметрлер > Камдык көчүрмөлөр бөлүмүнөн көрө аласыз. Андан соң Камдык көчүрмөнүн ачкычын көрүү дегенди басыңыз. Кененирээк маалымат @@ -7785,6 +7823,18 @@ Макул + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index a16eb063cb..55a86cc7e5 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -1051,6 +1051,20 @@ Bandykite susieti dar kartą Tęsti neperkėlus + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1076,13 +1090,13 @@ - Transfer message history + Perkelti žinučių istoriją - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Nepervesti + Neperkelti - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Atsieti „%1$s“? @@ -1431,16 +1445,18 @@ visas jūsų žinutes. Atkurti iš atsarginės kopijos - - Įtraukiami tik per pastarąsias %1$d d. išsiųsti arba gauti įrašai. Jūsų atsarginė kopija apima: Atkurti atsarginę kopiją - + Paskutinė atsarginė kopija buvo sukurta %1$s, %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Gaunama informacija apie atsarginę kopiją… + + Skip restore Pranešti apie paminėjimus @@ -2632,6 +2648,8 @@ Atlikai per daug bandymų užregistruoti šį numerį. Pabandyk už %1$s. Nepavyksta prisijungti prie paslaugos. Patikrinkite tinklo ryšį ir bandykite dar kartą. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Mums nepavyko tau išsiųsti patvirtinimo kodo SMS žinute. Pabandyk gauti savo kodą balso skambučiu. @@ -4533,6 +4551,8 @@ Atkurti arba perkelti Perkelti paskyrą Praleisti + + Skip restore Pokalbių atsarginės kopijos Perkelti paskyrą Perkelti paskyrą į naująjį „Android“ įrenginį @@ -6089,9 +6109,9 @@ Vis dar apdorojama Nepavyko pridėti ženklelio - Something went wrong + Kažkas nutiko - Your backups subscription couldn\'t be displayed. Please contact support. + Nepavyko parodyti jūsų atsarginių kopijų prenumeratos. Susisiekite su aptarnavimo komanda. Nepavyko patikrinti ženklelio @@ -7530,7 +7550,7 @@ Nepavyko išsaugoti pakeitimų. Patikrink tinklo ryšį ir bandyk dar kartą. - Couldn\'t delete call link as it is currently in use. + Nepavyko ištrinti skambučio nuorodos, nes ji šiuo metu naudojama. Ištrinti nuorodą? @@ -7625,6 +7645,10 @@ Atmesti Patvirtinti + + Approve all + + Deny all Įjungti viso ekrano pranešimus? @@ -7803,7 +7827,13 @@ Praleisti atkūrimą? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Jei pasirinksite praleisti atkūrimą, likusius įrašus ir priedus iš atsarginės kopijos bus galima atsisiųsti vėliau, kai bus atlaisvinta vietos saugykloje. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7836,7 +7866,11 @@ Praleisti atsisiuntimą - "Paskutinės atsarginės kopijos sukurti nepavyko. Norėdami pabandyti dar kartą įsitikinkite, kad telefonas prijungtas prie „Wi-Fi“, ir bakstelėkite „Kurti atsarginę kopiją dabar“." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7880,7 +7914,7 @@ Nepanaudoti įrašai bus išimti, tačiau juos bet kada galėsite atsisiųsti iš atsarginės kopijos. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Saugyklos optimizavimą galima naudoti tik įsigijus mokamą „Signal“ atsarginių kopijų planą. Jūsų atsarginių kopijų prenumerata vis dar apdorojama ir dar neaktyvi. Bandykite dar kartą vėliau. @@ -8044,6 +8078,8 @@ Atnaujinti Sužinoti daugiau + + Processing backup… Turite %1$s atsarginės kopijos duomenų, kurių nėra šiame įrenginyje. Atsarginė kopija bus ištrinta, kai prenumerata baigsis po %2$d d. @@ -8068,6 +8104,8 @@ Nepavyko išjungti ir ištrinti atsarginių kopijų Įvyko tinklo klaida. Patikrinkite interneto ryšį ir bandykite dar kartą. + + Uploading messages… @@ -8107,7 +8145,7 @@ Jūsų atsarginės kopijos raktas - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Atsarginis raktas yra 64 simbolių kodas, leidžiantis atkurti atsarginę kopiją iš naujo įdiegus „Signal“. Pamiršę savo raktą atsarginės kopijos atkurti negalėsite. „Signal“ negali padėti jums atkurti atsarginės kopijos. @@ -8200,7 +8238,7 @@ Neturiu savo senojo telefono - Or you\'re reinstalling Signal on the same device + Arba iš naujo įdiegiate „Signal“ tame pačiame įrenginyje Atkurti arba perkelti paskyrą @@ -8229,15 +8267,15 @@ Įveskite savo atsarginės kopijos raktą - Your backup key is a 64-character code required to recover your account and data. + Jūsų atsarginės kopijos raktas yra 64 simbolių kodas, reikalingas norint atkurti paskyrą ir duomenis. Neturite atsarginės kopijos rakto? Atsarginės kopijos raktas - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Atsarginių kopijų negalima atkurti be 64 simbolių atkūrimo kodo. Jei prarasite savo atsarginės kopijos raktą, „Signal“ negalės jums padėti atkurti atsarginės kopijos. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Jei turite senąjį įrenginį, atsarginės kopijos raktą rasite skiltyje Nustatymai > Atsarginės kopijos. Tada bakstelėkite „Peržiūrėti atsarginės kopijos raktą“. Sužinoti daugiau @@ -8286,6 +8324,18 @@ Gerai + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index a515553c49..258ef18e3b 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -1031,6 +1031,20 @@ Mēģināt saistīt vēlreiz Turpināt bez pārsūtīšanas + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1056,13 +1070,13 @@ - Transfer message history + Pārsūtīt ziņu vēsturi - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Nepārsūtīt - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Vai atsaistīt \"%1$s\"? @@ -1393,16 +1407,18 @@ Visas jūsu ziņas Atjaunot no rezerves kopijas - - Tiek iekļauta tikai pēdējās %1$d dienās sūtītā vai saņemtā multivide. Jūsu rezerves kopija ietver: Atjaunot rezerves kopiju - + Pēdējā rezerves kopija tika izveidota: %1$s plkst. %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Meklē rezerves kopijas informāciju… + + Skip restore Paziņot man par Pieminējumiem @@ -2548,6 +2564,8 @@ Pārāk daudz šī numura reģistrēšanas mēģinājumu. Mēģiniet vēlreiz pēc %1$s. Nevar izveidot savienojumu ar pakalpojumu. Lūdzu, pārbaudiet tīkla savienojumu un mēģiniet vēlreiz. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Mēs nevarējām nosūtīt jums verifikācijas kodu īsziņā. Tā vietā mēģiniet saņemt kodu, izmantojot balss zvanu. @@ -4422,6 +4440,8 @@ Atjaunot vai pārsūtīt Nodot kontu Izlaist + + Skip restore Sarunu rezerves kopijas Pārnest kontu Pārnest kontu uz citu Android ierīci @@ -5956,9 +5976,9 @@ Vēl apstrādā Nevarēja pievienot nozīmīti - Something went wrong + Kaut kas nogāja greizi - Your backups subscription couldn\'t be displayed. Please contact support. + Neizdevās parādīt rezerves kopiju abonementu. Lūdzu, sazinieties ar atbalsta dienestu. Nevarēja pārbaudīt nozīmīti @@ -7373,7 +7393,7 @@ Neizdevās saglabāt izmaiņas. Pārbaudiet tīkla savienojumu un mēģiniet vēlreiz. - Couldn\'t delete call link as it is currently in use. + Zvana saiti nevarēja dzēst, jo tā pašlaik tiek izmantota. Dzēst saiti? @@ -7466,6 +7486,10 @@ Noraidīt Apstiprināt + + Approve all + + Deny all Vai ieslēgt pilnekrāna paziņojumus? @@ -7642,7 +7666,13 @@ Vai izlaist atjaunošanu? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Ja izlaidīsiet atjaunošanu, rezerves kopijā atlikušo multividi un pielikumus varēs lejupielādēt vēlāk, kad ierīcē būs pieejams vairāk krātuves. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7675,7 +7705,11 @@ Izlaist lejupielādi - "Neizdevās pabeigt pēdējās rezerves kopijas izveidi. Lai mēģinātu vēlreiz, pārliecinieties, ka tālrunī ir izveidots savienojums ar Wi-Fi un pieskarieties pie \"Izveidot rezerves kopiju tūlīt\"." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7719,7 +7753,7 @@ Neizmantotās multivides lejupielāde tiks atcelta, taču to jebkurā brīdī var lejupielādēt no rezerves kopijas. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Krātuves optimizāciju var izmantot tikai Signal rezerves kopiju maksas plāna īpašnieki. Jūsu rezerves kopiju abonements joprojām tiek apstrādāts un vēl nav aktīvs. Lūdzu, vēlāk mēģiniet vēlreiz. @@ -7881,6 +7915,8 @@ Atjaunot Uzzināt vairāk + + Processing backup… Jums ir %1$s rezerves kopijas datu, kas nav šajā ierīcē. Rezerves kopija tiks dzēsta, kad abonements beigsies pēc %2$d dienām. @@ -7903,6 +7939,8 @@ Neizdevās izslēgt un dzēst rezerves kopijas Radās tīkla kļūda. Pārbaudiet interneta savienojumu un mēģiniet vēlreiz. + + Uploading messages… @@ -7942,7 +7980,7 @@ Rezerves kopijas atslēga - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Rezerves kopijas atslēga ir 64 rakstzīmju kods, kas ļauj atjaunot rezerves kopiju, atkārtoti instalējot Signal. Ja aizmirsīsiet atslēgu, nevarēsiet atjaunot rezerves kopiju. Signal nevar palīdzēt atgūt rezerves kopiju. @@ -8033,7 +8071,7 @@ Man nav iepriekšējā tālruņa - Or you\'re reinstalling Signal on the same device + Vai arī jūs atkārtoti instalējat Signal tajā pašā ierīcē Atjaunojiet vai pārsūtiet kontu @@ -8062,15 +8100,15 @@ Ievadiet rezerves kopijas atslēgu - Your backup key is a 64-character code required to recover your account and data. + Rezerves kopijas atslēga ir 64 rakstzīmju kods, kas nepieciešams konta un datu atgūšanai. Vai jums nav rezerves kopijas atslēgas? Rezerves kopijas atslēga - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Rezerves kopijas nevar atgūt bez 64 rakstzīmju atgūšanas koda. Ja rezerves kopijas atslēga ir pazaudēta, Signal nevar palīdzēt atgūt rezerves kopiju. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Ja iepriekšējā ierīce ir jūsu rīcībā, rezerves kopijas atslēgu varat skatīt sadaļā Iestatījumi > Rezerves kopijas. Pēc tam pieskarieties pie Skatīt rezerves kopijas atslēgu. Uzzināt vairāk @@ -8119,6 +8157,18 @@ Labi + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 689a63eba4..5c8555cdf5 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -1011,6 +1011,20 @@ Обидете се да поврзете повторно Продолжете без пренос + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Пренесете ја историјата на пораки - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Не префрлувај + Не префрлајте - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Да се прекине поврзувањето со „%1$s“? @@ -1355,16 +1369,18 @@ Сите ваши пораки Врати од резервна копија - - Вклучени се само медиумските датотеки кои биле испратени или примени во последните %1$d дена. Вашата резервна копија ги вклучува: Обнови последна копија - + Вашата последна резервна копија беше направена на %1$s во %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Се преземаат деталите за резервната копија… + + Skip restore Извести кога некој ќе ме спомне @@ -2464,6 +2480,8 @@ Направивте премногу обиди за регистрација на овој број. Ве молиме обидете се повторно за %1$s. Не може да се поврзе со услугата. Ве молиме проверете ја Вашата интернет конекција и обидете се повторно. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Не можевме да испратиме код за потврда преку SMS. Наместо тоа, обидете се да го добиете кодот преку гласовен повик. @@ -4311,6 +4329,8 @@ Вратете или пренесете Префрли сметка Прескокни + + Skip restore Резервни копии на разговори Префрли сметка Префрли сметка на друг Android уред @@ -5823,9 +5843,9 @@ Сѐ уште се обработува Не може да се додаде беџ - Something went wrong + Нешто не е во ред - Your backups subscription couldn\'t be displayed. Please contact support. + Вашата претплата на резервни копии не можеше да се прикаже. Ве молиме контактирајте го тимот за поддршка. Неуспешно потврдување на беџот @@ -7216,7 +7236,7 @@ Промените не можеа да се зачуваат. Проверете ја вашата мрежна поврзаност и обидете се повторно. - Couldn\'t delete call link as it is currently in use. + Не може да се избрише линкот за повик бидејќи моментално се користи. Да се избрише линкот? @@ -7307,6 +7327,10 @@ Одбиј Дозволи + + Approve all + + Deny all Сакате да се вклучат известувања на цел екран? @@ -7481,7 +7505,13 @@ Да се прескокне враќањето? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Ако го прескокнете враќањето, преостанатите медиумски датотеки и прилози во вашата резервна копија ќе може да се преземат подоцна кога ќе се ослободи простор за складирање. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Прескокнете го преземањето - "Не можеше да се заврши вашата последна резервна копија. Проверете дали вашиот телефон e поврзан на Wi-Fi, допрете на „Направи резервна копија сега“ и обидете се повторно." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Медиумските датотеки кои не се користат ќе бидат отстранети, но ќе може да се преземат од вашата резервна копија во кое било време. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Оптимизацијата на складирање може да се користи само со платена претплата на Signal резервни копии. Вашта претплата на резервни копии сè уште се обработува и сè уште не е активна. Обидете се повторно подоцна. @@ -7718,6 +7752,8 @@ Обновете Дознајте повеќе + + Processing backup… Имате %1$s податоци од резервни копии кои не се на овој уред. Вашата резервна копија ќе биде избришана кога вашата претплата ќе заврши за %2$d ден. @@ -7738,6 +7774,8 @@ Не можеше да се исклучи и да се избришат резервните копии Се јави мрежна грешка. Проверете ја интернет врската и обидете се повторно. + + Uploading messages… @@ -7777,7 +7815,7 @@ Вашиот клуч за резервни копии - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Вашиот клуч за резервни копии е код од 64 знаци кој ви помага да ги вратите податоците од резервната копија кога ќе ја реинсталирате Signal апликацијата. Ако го заборавите вашиот клуч, нема да можете да ги вратите податоците од резервната копија. Signal нема да може да ви помогне да ги вратите податоците од резервната копија. @@ -7866,7 +7904,7 @@ Го немам мојот стар телефон - Or you\'re reinstalling Signal on the same device + Или ако го реинсталирате Signal на истиот уред Вратете или пренесете ја вашата корисничка сметка @@ -7895,15 +7933,15 @@ Внесете го вашиот клуч за резервни копии - Your backup key is a 64-character code required to recover your account and data. + Вашиот клуч за резервни копии е код од 64 знаци кој ви помага да си ги вратите корисничката сметка и податоците. Немате клуч за резервни копии? Клуч за резервни копии - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Резервните копии не може да се вратат без нивниот код за враќање од 64 знаци. Ако го имате изгубено клучот за резервни копии, Signal не може да ви помогне да си ја вратите резервната копија. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Ако го имате вашиот стар уред можете да го видите клучот за резервни копии во Поставувања > Резервни копии. Потоа допрете на „Види го клучот за резервни копии“. Дознајте повеќе @@ -7952,6 +7990,18 @@ Во ред + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 4974a3f6ac..9505d3f352 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -1011,6 +1011,20 @@ വീണ്ടും ലിങ്ക് ചെയ്യാൻ ശ്രമിക്കുക കൈമാറാതെ തുടരുക + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + സന്ദേശ ചരിത്രം കൈമാറുക - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - ട്രാൻസ്ഫർ ചെയ്യരുത് + കൈമാറരുത് - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\" അൺലിങ്ക് ചെയ്യണോ? @@ -1355,16 +1369,18 @@ നിങ്ങളുടെ എല്ലാ സന്ദേശങ്ങളും ബാക്കപ്പിൽ നിന്ന് പുനഃസ്ഥാപിക്കുക - - കഴിഞ്ഞ %1$d ദിവസങ്ങളിൽ അയച്ചതോ സ്വീകരിച്ചതോ ആയ മീഡിയ മാത്രമേ ഉൾപ്പെടുത്തിയിട്ടുള്ളൂ. നിങ്ങളുടെ ബാക്കപ്പിൽ ഇവ ഉൾപ്പെടുന്നു: ബാക്കപ്പ് വീണ്ടെടുക്കൂ - + നിങ്ങളുടെ അവസാന ബാക്കപ്പ് %1$s, %2$s -ന് ചെയ്തു. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. ബാക്കപ്പ് വിശദാംശങ്ങൾ ലഭ്യമാക്കുന്നു… + + Skip restore സൂചനകൾ എന്നെ അറിയിക്കുക @@ -2464,6 +2480,8 @@ ഈ നമ്പർ രജിസ്റ്റർ ചെയ്യാൻ നിങ്ങൾ നിരവധി ശ്രമങ്ങൾ നടത്തി. %1$s സമയത്തിന് ശേഷം വീണ്ടും ശ്രമിക്കുക. സേവനത്തിലേക്ക് കണക്റ്റുചെയ്യാനായില്ല. നെറ്റ്‌വർക്ക് കണക്ഷൻ പരിശോധിച്ച് വീണ്ടും ശ്രമിക്കുക. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. ഞങ്ങൾക്ക് SMS-ലൂടെ പരിശോധിച്ചുറപ്പിക്കൽ കോഡ് അയയ്ക്കാനായില്ല. വോയ്‌സ് കോളിലൂടെ കോഡ് നേടാൻ ശ്രമിക്കൂ. @@ -4311,6 +4329,8 @@ പുനഃസ്ഥാപിക്കുക അല്ലെങ്കിൽ കൈമാറ്റം ചെയ്യുക അക്കൗണ്ട് മാറ്റിസ്ഥാപിക്കുക ഒഴിവാക്കുക + + Skip restore ചാറ്റ് ബാക്കപ്പുകൾ അക്കൗണ്ട് മാറ്റിസ്ഥാപിക്കുക അക്കൌണ്ട് ഒരു പുതിയ ആൻഡ്രോയിഡ് ഉപകരണത്തിലേക്ക് മാറ്റിസ്ഥാപിക്കുക @@ -5823,9 +5843,9 @@ പ്രോസസ്സ് ചെയ്യുന്നു ബാഡ്‌ജ് ചേർക്കാനായില്ല - Something went wrong + എന്തോ കുഴപ്പം സംഭവിച്ചു - Your backups subscription couldn\'t be displayed. Please contact support. + നിങ്ങളുടെ ബാക്കപ്പ് സബ്സ്ക്രിപ്ഷൻ പ്രദർശിപ്പിക്കാൻ കഴിഞ്ഞില്ല. പിന്തുണയുമായി ബന്ധപ്പെടൂ. ബാഡ്‍ജ് വാലിഡേറ്റ് ചെയ്യാനായില്ല @@ -7216,7 +7236,7 @@ മാറ്റങ്ങൾ സംരക്ഷിക്കാനായില്ല. നിങ്ങളുടെ നെറ്റ്‌വർക്ക് കണക്ഷൻ പരിശോധിച്ച ശേഷം വീണ്ടും ശ്രമിക്കുക. - Couldn\'t delete call link as it is currently in use. + കോൾ ലിങ്ക് നിലവിൽ ഉപയോഗത്തിലുള്ളതിനാൽ അത് ഇല്ലാതാക്കാൻ കഴിഞ്ഞില്ല. ലിങ്ക് ഇല്ലാതാക്കണോ? @@ -7307,6 +7327,10 @@ നിരസിക്കുക അംഗീകരിക്കുക + + Approve all + + Deny all പൂർണ്ണ സ്ക്രീൻ അറിയിപ്പുകൾ ഓണാക്കണോ? @@ -7481,7 +7505,13 @@ പുനഃസ്ഥാപിക്കൽ ഒഴിവാക്കണോ? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + നിങ്ങൾ പുനഃസ്ഥാപിക്കുന്നത് ഒഴിവാക്കുകയാണെങ്കിൽ, നിങ്ങളുടെ ബാക്കപ്പിലെ ശേഷിക്കുന്ന മീഡിയയും അറ്റാച്ച്‌മെൻ്റുകളും പിന്നീട് സ്റ്റോറേജ് ഇടം ലഭ്യമാകുമ്പോൾ ഡൗൺലോഡ് ചെയ്യാവുന്നതാണ്. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ ഡൗൺലോഡ് ഒഴിവാക്കുക - "നിങ്ങളുടെ അവസാന ബാക്കപ്പ് പൂർത്തിയാക്കാനായില്ല. നിങ്ങളുടെ ഫോൺ Wi-Fi-ലേക്ക് കണക്‌റ്റ് ചെയ്‌തിട്ടുണ്ടെന്ന് ഉറപ്പുവരുത്തുക, വീണ്ടും ശ്രമിക്കാൻ ഇപ്പോൾ ബാക്കപ്പ് ടാപ്പ് ചെയ്യുക." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ ഉപയോഗിക്കാത്ത മീഡിയ ഓഫ്‌ലോഡ് ചെയ്യപ്പെടും, എന്നാൽ നിങ്ങളുടെ ബാക്കപ്പിൽ നിന്ന് എപ്പോൾ വേണമെങ്കിലും ഡൗൺലോഡ് ചെയ്യാം. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + പണമടച്ചുള്ള Signal ബാക്കപ്പുകളിൽ മാത്രമേ സ്റ്റോറേജ് ഒപ്റ്റിമൈസേഷൻ ഉപയോഗിക്കാൻ കഴിയൂ. നിങ്ങളുടെ ബാക്കപ്പ് സബ്സ്ക്രിപ്ഷൻ ഇപ്പോഴും പ്രോസസ്സ് ചെയ്യുകയാണ്, ഇതുവരെ സജീവമായിട്ടില്ല. പിന്നീട് വീണ്ടും ശ്രമിക്കൂ. @@ -7718,6 +7752,8 @@ പുതുക്കുക കൂടുതലറിയുക + + Processing backup… ഈ ഉപകരണത്തിൽ ഇല്ലാത്ത %1$s ബാക്കപ്പ് ഡാറ്റ നിങ്ങളുടെ പക്കലുണ്ട്. നിങ്ങളുടെ സബ്സ്ക്രിപ്ഷൻ %2$d ദിവസത്തിനുള്ളിൽ അവസാനിക്കുമ്പോൾ നിങ്ങളുടെ ബാക്കപ്പ് ഇല്ലാതാക്കപ്പെടും. @@ -7738,6 +7774,8 @@ ബാക്കപ്പുകൾ ഓഫാക്കാനും ഇല്ലാതാക്കാനും കഴിഞ്ഞില്ല ഒരു നെറ്റ്‌വർക്ക് പിശക് സംഭവിച്ചു. നിങ്ങളുടെ ഇൻ്റർനെറ്റ് കണക്ഷൻ പരിശോധിച്ച് വീണ്ടും ശ്രമിക്കുക. + + Uploading messages… @@ -7777,7 +7815,7 @@ നിങ്ങളുടെ ബാക്കപ്പ് കീ - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + നിങ്ങൾ Signal വീണ്ടും ഇൻസ്റ്റാൾ ചെയ്യുമ്പോൾ ബാക്കപ്പ് പുനഃസ്ഥാപിക്കാൻ നിങ്ങളെ അനുവദിക്കുന്ന 64 അക്ക പ്രതീക കോഡാണ് നിങ്ങളുടെ ബാക്കപ്പ് കീ. നിങ്ങളുടെ കീ മറന്നുപോയാല്‍, നിങ്ങളുടെ ബാക്കപ്പ് പുനഃസ്ഥാപിക്കാൻ നിങ്ങൾക്ക് കഴിയില്ല. നിങ്ങളുടെ ബാക്കപ്പ് വീണ്ടെടുക്കാൻ Signal-ന് നിങ്ങളെ സഹായിക്കാനാകില്ല. @@ -7866,7 +7904,7 @@ എൻ്റെ കയ്യിൽ പഴയ ഫോൺ ഇല്ല - Or you\'re reinstalling Signal on the same device + അല്ലെങ്കിൽ നിങ്ങൾ അതേ ഉപകരണത്തിൽ Signal വീണ്ടും ഇൻസ്റ്റാൾ ചെയ്യുകയാണ് അക്കൗണ്ട് പുനഃസ്ഥാപിക്കുക അല്ലെങ്കിൽ കൈമാറുക @@ -7895,15 +7933,15 @@ നിങ്ങളുടെ ബാക്കപ്പ് കീ നൽകുക - Your backup key is a 64-character code required to recover your account and data. + നിങ്ങളുടെ അക്കൗണ്ടും ഡാറ്റയും വീണ്ടെടുക്കാൻ ആവശ്യമായ 64 പ്രതീക കോഡാണ് നിങ്ങളുടെ ബാക്കപ്പ് കീ. ബാക്കപ്പ് കീ ഇല്ലേ? ബാക്കപ്പ് കീ - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + 64 പ്രതീകങ്ങളുള്ള വീണ്ടെടുക്കൽ കോഡ് ഇല്ലാതെ ബാക്കപ്പുകൾ വീണ്ടെടുക്കാൻ കഴിയില്ല. നിങ്ങളുടെ ബാക്കപ്പ് കീ നഷ്‌ടപ്പെട്ടാൽ, നിങ്ങളുടെ ബാക്കപ്പ് പുനഃസ്ഥാപിക്കാൻ Signal-ന് സഹായിക്കാനാകില്ല. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + നിങ്ങളുടെ പഴയ ഉപകരണം ഉണ്ടെങ്കിൽ, ക്രമീകരണങ്ങൾ > ബാക്കപ്പുകൾ എന്നതിൽ നിങ്ങളുടെ ബാക്കപ്പ് കീ കാണാനാകും. തുടർന്ന് ബാക്കപ്പ് കീ കാണുക എന്നതിൽ ടാപ്പ് ചെയ്യുക. കൂടുതലറിയുക @@ -7952,6 +7990,18 @@ ശരി + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml index 415ffb84bc..e550b7837f 100644 --- a/app/src/main/res/values-mr/strings.xml +++ b/app/src/main/res/values-mr/strings.xml @@ -1011,6 +1011,20 @@ पुन्हा लिंक करून पाहा हस्तांतरित न करता पुढे जा + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + संदेश इतिहास हस्तांतरित करा - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - स्थानांतरित करू नका + हस्तांतरित करू नका - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\" अनलिंक करायचे? @@ -1355,16 +1369,18 @@ आपले सर्व संदेश बॅकअपवरून पुनर्स्थापित करा - - मागील %1$d दिवसांमध्ये पाठविलेला किंवा प्राप्त मिडीया समाविष्ट केलेला आहे. आपल्या बॅकअपमध्ये हे समाविष्ट आहे: बॅकअप पुनर्स्थापित करा - + आपला शेवटचा बॅकअप %1$s रोजी %2$s वाजता घेतला. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. बॅकअप तपशील आणत आहे… + + Skip restore उल्लेखा साठी मला सूचित करा @@ -2464,6 +2480,8 @@ आपण या क्रमांकांची नोंदणी करण्यास असंख्य प्रयत्न केले आहेत. कृपया %1$s मध्ये पुन्हा प्रयत्न करा. सेवेसोबत कनेक्ट करण्यात अक्षम. कृपया नेटवर्क कनेक्शन तपासा आणि पुन्हा प्रयत्न करा. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. आम्ही SMS मार्फत आपणाला सत्यापन कोड पाठवू शकत नाही. त्याऐवजी व्हॉइस कॉल मार्फत आपला कोड प्राप्त करण्याचा प्रयत्न करा. @@ -4311,6 +4329,8 @@ परत मिळवा किंवा हस्तांतरण करा खाते स्थानांतरित करा वगळा + + Skip restore चॅट बॅकअप्स खाते स्थानांतरित करा नवीन Android डिव्हाइसवर खाते स्थानांतरित करा @@ -5823,9 +5843,9 @@ अजूनही प्रक्रिया करत आहे बॅज जोडता आला नाही - Something went wrong + काहीतरी चुकले - Your backups subscription couldn\'t be displayed. Please contact support. + आपले बॅकअप सदस्यत्व दाखवता आले नाही. कृपया सपोर्टशी संंपर्क साधा. बॅज प्रमाणित करणे अयशस्वी झाले @@ -7216,7 +7236,7 @@ बदल जतन करू शकत नाही. आपले नेटवर्क कनेक्शन तपासा आणि पुन्हा प्रयत्न करा. - Couldn\'t delete call link as it is currently in use. + कॉल लिंक हटवता आली नाही कारण ती सध्या वापरात आहे. लिंक हटवायची? @@ -7307,6 +7327,10 @@ नकार द्या स्वीकार करा + + Approve all + + Deny all पूर्ण स्क्रिन अधिसूचना सुरू करावयाची? @@ -7481,7 +7505,13 @@ परत मिळवण्याची क्रिया वगळायची? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + जर आपण पूर्ववत करायचे टाळलेत तर आपल्या बॅकअपमधील उरलेला मिडीया आणि अटॅचमेंट्स नंतर साठवणीसाठी जागा मिळाल्यावर डाऊनलोड करता येतात. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ डाउनलोड वगळा - "आपला शेवटचा बॅकअप पूर्ण होऊ शकला नाही. आपला फोन Wi-Fi शी कनेक्ट झाला असल्याची खात्री करा आणि पुन्हा प्रयत्न करण्यासाठी आत्ता बॅकअप घ्या वर टॅप करा." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ न वापरलेला मिडीया ऑफलोड केला जाईल, परंतु त्याला आपल्या बॅकअप मधून कधीही डाउनलोड केले जाऊ शकते. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + साठवण सुधारणा फक्त Signal बॅकअप्सच्या सशुल्क टीअरसह वापरता येते. आपले बॅकअप सदस्यत्व अजूनही प्रक्रियेअंतर्गत आहे आणि अद्याप सक्रिय झालेले नाही. कृपया नंतर पुन्हा प्रयत्न करा. @@ -7718,6 +7752,8 @@ रीन्यू करा अधिक जाणून घ्या + + Processing backup… आपल्याकडे %1$s बॅकअप डेटा आहे पण तो या डिव्हाईसवर नाही. आपले सदस्यत्व %2$d दिवसाने संपल्यावर आपला बॅकअप हटवला जाईल. @@ -7738,6 +7774,8 @@ बॅकअप्स बंद करता आणि हटवता आले नाहीत नेटवर्कमध्ये त्रुटी उद्भवली. कृपया आपले इंटरनेट कनेक्शन तपासा आणि पुन्हा प्रयत्न करा. + + Uploading messages… @@ -7777,7 +7815,7 @@ आपली बॅकअप की - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + आपली बॅकअप की हा एक 64-अंकी कोड असून तो आपण जेव्हा Signal पुन्हा-इन्स्टॉल करता तेव्हा आपणाला आपला बॅकअप पुर्नस्थापित करू देतो. आपण आपली पिन विसरल्यास आपण आपला बॅकअप पुर्नस्थापित करू शकणार नाही. Signal आपणाला आपला बॅकअप पुर्नस्थापित करण्यास मदत करू शकणार नाही. @@ -7866,7 +7904,7 @@ माझ्याकडे माझा जुना फोन नाही - Or you\'re reinstalling Signal on the same device + किंवा आपण त्याच डिव्हाईसवर Signal पुन्हा इन्स्टॉल करत आहात अकाऊंट परत मिळवा किंवा हस्तांतरित करा @@ -7895,15 +7933,15 @@ आपली बॅकअप की प्रविष्ट करा - Your backup key is a 64-character code required to recover your account and data. + आपली बॅकअप की एक ६४ अंकी कोड असतो जो आपला अकाऊंट आणि माहिती परत मिळवण्यासाठी आवश्यक असतो. बॅकअप की नाही? बॅकअप की - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + बॅकअप्स त्यांच्या ६४ अंकी पुनःप्राप्ती कोडशिवाय परत मिळवता येत नाहीत. जर आपली बॅकअप की हरवली असेल तर Signal आपला बॅकअप परत मिळवण्यास मदत करू शकत नाही. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + जर आपल्याकडे आपला जुना डिव्हाईस असेल तर आपण आपली बॅकअप की सेटिंग्ज > बॅकअप्स मध्ये दृश्य करू शकता. मग बॅकअप की दृश्य वर टॅप करा. अधिक जाणून घ्या @@ -7952,6 +7990,18 @@ ठीक आहे + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index 5f1abacfaa..9943746f94 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -991,6 +991,20 @@ Cuba pautkan sekali lagi Teruskan tanpa memindahkan + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1016,13 +1030,13 @@ - Transfer message history + Pindahkan sejarah mesej - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Jangan pindah + Jangan pindahkan - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Nyahpaut \"%1$s\"? @@ -1317,16 +1331,18 @@ Semua mesej anda Pulihkan dari sandaran - - Hanya media yang dihantar atau diterima dalam %1$d hari yang lalu disertakan. Sandaran anda termasuk: Memulihkan sandaran - + Sandaran terakhir anda telah dibuat pada %1$s pada %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Mengambil butiran sandaran… + + Skip restore Maklumkan saya untuk Sebutan @@ -2380,6 +2396,8 @@ Anda telah membuat terlalu banyak percubaan untuk mendaftar nombor ini. Sila cuba lagi kemudian dalam masa %1$s. Perkhidmatan tidak dapat disambungkan. Sila semak sambungan rangkaian dan cuba lagi. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Kami tidak dapat menghantar kod pengesahan kepada anda melalui SMS. Cuba terima kod anda melalui panggilan suara. @@ -4200,6 +4218,8 @@ Pulihkan atau pindahkan Pindahkan akaun Langkau + + Skip restore Sandaran sembang Pindahkan akaun Pindahkan akaun ke peranti Android baharu @@ -5690,9 +5710,9 @@ Masih sedang diproses Tidak dapat menambah lencana - Something went wrong + Ada yang tidak kena - Your backups subscription couldn\'t be displayed. Please contact support. + Langganan sandaran anda tidak dapat dipaparkan. Sila hubungi sokongan. Gagal mengesahkan lencana @@ -7059,7 +7079,7 @@ Tidak dapat menyimpan perubahan. Periksa sambungan rangkaian anda dan cuba semula. - Couldn\'t delete call link as it is currently in use. + Tidak dapat memadamkan pautan panggilan kerana ia sedang digunakan. Padam pautan? @@ -7148,6 +7168,10 @@ Tolak Luluskan + + Approve all + + Deny all Hidupkan pemberitahuan skrin penuh? @@ -7320,7 +7344,13 @@ Langkau pemulihan? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Jika anda melangkau pemulihan, media dan lampiran yang masih ada dalam sandaran anda boleh dimuat turun kemudian apabila ruang storan tersedia. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7353,7 +7383,11 @@ Langkau muat turun - "Sandaran terakhir anda tidak dapat diselesaikan. Pastikan telefon anda disambungkan ke Wi-Fi dan ketik Sandarkan sekarang untuk mencuba lagi." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7397,7 +7431,7 @@ Media yang tidak digunakan akan dipindahkan, tetapi boleh dimuat turun dari sandaran anda pada bila-bila masa. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Pengoptimuman storan hanya boleh digunakan dengan Sandaran Signal berbayar. Langganan sandaran anda masih diproses dan belum lagi aktif. Sila cuba lagi nanti. @@ -7555,6 +7589,8 @@ Perbaharui Ketahui lebih lanjut + + Processing backup… Anda mempunyai %1$s data sandaran yang tiada pada peranti ini. Sandaran anda akan dipadam apabila langganan anda berakhir dalam %2$d hari. @@ -7573,6 +7609,8 @@ Tidak dapat mematikan dan memadamkan sandaran Ralat rangkaian telah berlaku. Sila semak sambungan internet anda dan cuba lagi. + + Uploading messages… @@ -7612,7 +7650,7 @@ Kunci sandaran anda - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Kunci sandaran anda ialah kod 64 digit yang boleh memulihkan sandaran apabila anda memasang semula Signal. Jika terlupa PIN, anda tidak akan dapat memulihkan sandaran. Signal tidak dapat membantu memulihkan sandaran anda. @@ -7699,7 +7737,7 @@ Saya tidak mempunyai telefon lama saya - Or you\'re reinstalling Signal on the same device + Atau anda pasang semula Signal pada peranti yang sama Pulihkan atau pindahkan akaun @@ -7728,15 +7766,15 @@ Masukkan kunci sandaran anda - Your backup key is a 64-character code required to recover your account and data. + Kunci sandaran anda ialah kod 64 digit yang diperlukan untuk memulihkan akaun dan data anda. Tiada kunci sandaran? Kunci sandaran - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Sandaran tidak boleh dipulihkan tanpa kod pemulihan 64 aksara. Jika anda kehilangan kunci sandaran, Signal tidak dapat membantu memulihkan sandaran anda. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Jika anda mempunyai peranti lama anda, anda boleh melihat kunci sandaran anda dalam Tetapan > Sandaran. Kemudian ketik Lihat kunci sandaran. Ketahui lebih lanjut @@ -7785,6 +7823,18 @@ Okey + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index 2cd1a5eb50..13a805cb31 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -991,6 +991,20 @@ ထပ်၍ချိတ်ကြည့်ပါ မလွှဲပြောင်းဘဲ ဆက်လက်လုပ်ဆောင်မည် + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1016,13 +1030,13 @@ - Transfer message history + မက်ဆေ့ချ်မှတ်တမ်းကို လွှဲပြောင်းရန် - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device မလွှဲပြောင်းပါ - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\" ချိတ်ဆက်မှုကို ဖြုတ်မည်လား။ @@ -1317,16 +1331,18 @@ သင်၏ မက်ဆေ့ချ်များအားလုံး ဘက်ခ်အပ်မှ ပြန်လည်ရယူမည် - - လွန်ခဲ့သော %1$d ရက်အတွင်း ပေးပို့ထားသည့် သို့မဟုတ် လက်ခံရရှိသည့် မီဒီယာများသာ ပါဝင်ပါသည်။ သင့် ဘက်ခ်အပ်တွင် အောက်ပါတို့ပါဝင်သည်- ဘက်ခ်အပ်မှ ပြန်လည်ရယူမည် - + သင်၏နောက်ဆုံး ဘက်ခ်အပ်ကို %1$s တွင် %2$s အချိန်က ပြုလုပ်ခဲ့သည်။ + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. ဘက်ခ်အပ်အသေးစိတ်ကို ရယူနေသည်… + + Skip restore မန်းရှင်းများရှိလျှင် ကျွန်ုပ်ကိုအသိပေးပါ @@ -2380,6 +2396,8 @@ ဤနံပါတ်ကို မှတ်ပုံတင်ရန် ကြိုးပမ်းမှုများစွာ ဆောင်ရွက်ခဲ့ပါသည်။ %1$s အကြာတွင် ထပ်ကြိုးစားကြည့်ပါ။ ဝန်ဆောင်မှုနှင့် ဆက်သွယ်၍ မရနိုင်ပါ။ အင်တာနက်ချိတ်ဆက်မှုအား နောက်တစ်ကြိမ် စစ်ဆေး၍ ပြန်လည်ကြိုးစားကြည့်ပါ။ + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. SMS မှတစ်ဆင့် သင့်ထံ အတည်ပြုကုဒ် ပို့၍ မရနိုင်ခဲ့ပါ။ ၎င်းအစား အော်ဒီယိုကောလ်မှတစ်ဆင့် သင့်ကုဒ်ကို လက်ခံကြည့်ပါ။ @@ -4200,6 +4218,8 @@ ပြန်ယူရန် သို့မဟုတ် လွှဲပြောင်းရန် အကောင့်လွှဲမယ် ကျော်ပါ + + Skip restore ချက်(တ်) အရန်သိမ်းဆည်းမှုများ အကောင့်လွှဲမယ် သင့် Android စက်အသစ်ပေါ်သို့ အကောင့်လွှဲပြောင်းမယ် @@ -5690,9 +5710,9 @@ စီမံလုပ်ဆောင်နေဆဲ ဘဲ့ဂျ် ပေါင်းထည့်၍ မရနိုင်ပါ - Something went wrong + တစ်ခုခုမှားယွင်းနေသည် - Your backups subscription couldn\'t be displayed. Please contact support. + သင်၏ ဘက်ခ်အပ် ပုံမှန်လှူဒါန်းငွေကို ပြသ၍မရပါ။ အကူအညီရယူရန် ဆက်သွယ်ပါ။ ဘဲ့ဂျ်ကို တရားဝင်အောင်ပြုလုပ်ရန် မအောင်မြင်ပါ @@ -7059,7 +7079,7 @@ အပြောင်းအလဲများကို သိမ်းဆည်း၍မရပါ။ သင့်ကွန်ရက် ချိတ်ဆက်မှုကို စစ်ဆေးပြီး ထပ်ကြိုးစားပါ။ - Couldn\'t delete call link as it is currently in use. + လက်ရှိအသုံးပြုနေသည့် Signal ဖြင့်ဖုန်းခေါ်ဆိုရန်လင့်ကို ဖျက်၍မရပါ။ လင့်ခ်ကို ဖျက်မည်လား။ @@ -7148,6 +7168,10 @@ ငြင်းပယ်သည် ခွင့်ပြုမယ် + + Approve all + + Deny all မျက်နှာပြင်အပြည့် အသိပေးချက်များကို ဖွင့်မည်လား။ @@ -7320,7 +7344,13 @@ ပြန်လည်ရယူခြင်းကို ကျော်လိုပါသလား။ - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + သင့် ဘက်ခ်အပ်တွင် ကျန်ရှိသည့်မီဒီယာနှင့် ပူးတွဲဖိုင်များအား ပြန်လည်ရယူခြင်းကို ကျော်လိုက်ပါက နောင်တစ်ချိန် သင့်စက်တွင် နေရာလွတ်ရှိသောအခါ ဘက်ခ်အပ်ကိုဒေါင်းလုဒ်ပြုလုပ်နိုင်သည်။ + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7353,7 +7383,11 @@ ဒေါင်းလုဒ်ကို ကျော်လိုက်မည် - "သင်၏နောက်ဆုံး ဘက်ခ်အပ်ကို မပြီးမြောက်နိုင်ပါ။ သင့်ဖုန်းကို Wi-Fi နှင့် ချိတ်ဆက်ထားကြောင်းသေချာစေပြီး ထပ်၍စမ်းကြည့်ရန် ယခုဘက်ခ်အပ်လုပ်မည်ကို နှိပ်ပါ။" + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7397,7 +7431,7 @@ အသုံးမပြုသော မီဒီယာကို ဒေါင်းလုဒ်လုပ်မည်မဟုတ်သော်လည်း သင်၏ဘက်ခ်အပ်မှ အချိန်မရွေးဒေါင်းလုဒ်လုပ်နိုင်ပါသည်။ - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + သိုလှောင်မှု အကောင်းဆုံးချိန်ညှိခြင်းကို Signal ဘက်ခ်အပ်၏ အခပေးအဆင့်ဖြင့်သာ အသုံးပြုနိုင်သည်။ သင်၏ ပုံမှန်လှူဒါန်းမှုသည် လုပ်ဆောင်ဆဲဖြစ်ပြီး အသက်မဝင်သေးပါ။ နောက်မှ ထပ်ကြိုးစားကြည့်ပါ။ @@ -7555,6 +7589,8 @@ သက်တမ်းတိုးရန် ပိုမိုလေ့လာရန် + + Processing backup… သင့်တွင် ဤစက်တွင်မရှိသော ဘက်ခ်အပ်ဒေတာ %1$s ရှိသည်။ သင်၏ပုံမှန်လှူဒါန်းမှု %2$d ရက်အတွင်း ကုန်ဆုံးသည့်အခါ သင့် ဘက်ခ်အပ်ကို ဖျက်လိုက်ပါမည်။ @@ -7573,6 +7609,8 @@ ဘက်ခ်အပ်များကို ပိတ်ပြီး ဖျက်၍မရပါ ကွန်ရက်ချို့ယွင်းချက်တစ်ခု ဖြစ်နေပါသည်။ သင့်အင်တာနက်ချိတ်ဆက်မှုကို စစ်ဆေးပြီး ထပ်ကြိုးစားပါ။ + + Uploading messages… @@ -7612,7 +7650,7 @@ သင့် ဘက်ခ်အပ်ကီး - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + သင်၏ ဘက်ခ်အပ်ကီးသည် Signal ကို ပြန်လည်ထည့်သွင်းသောအခါတွင် သင့် ဘက်ခ်အပ်ကို ပြန်လည်ရယူနိုင်သည့် စာလုံး 64 လုံးကုဒ်ဖြစ်သည်။ သင့် ကီးကိုမေ့သွားပါက သင်၏ဘက်ခ်အပ်ကို ပြန်လည်ရယူနိုင်တော့မည် မဟုတ်ပါ။ Signal သည် သင်၏ဘက်ခ်အပ်ကို ပြန်လည်ရယူရန် မကူညီနိုင်ပါ။ @@ -7699,7 +7737,7 @@ ကျွန်ုပ်တွင် ဖုန်းအဟောင်းမရှိပါ - Or you\'re reinstalling Signal on the same device + သို့မဟုတ် သင်သည် ထိုစက်တွင်ပင် Signal ကို ပြန်လည်ထည့်သွင်းနေသည် အကောင့်ကို ပြန်ယူရန် သို့မဟုတ် လွှဲပြောင်းရန် @@ -7728,15 +7766,15 @@ သင့် ဘက်ခ်အပ်ကီးကို ထည့်ပါ။ - Your backup key is a 64-character code required to recover your account and data. + သင့် ဘက်ခ်အပ်ကီးသည် သင့်အကောင့်နှင့် ဒေတာကို ပြန်လည်ရယူရန်လိုအပ်သော စာလုံး 64 လုံးပါ ကုဒ်တစ်ခုဖြစ်သည်။ ဘက်ခ်အပ်ကီး မရှိဘူးလား။ သင့် ဘက်ခ်အပ်ကီး - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + စာလုံး 64 လုံးရှိသော ပြန်လည်ရယူရေးကုဒ်မပါဘဲ ဘက်ခ်အပ်များကို ပြန်လည်ရယူ၍မရပါ။ သင့် ဘက်ခ်အပ်ကီး ပျောက်ဆုံးသွားပါက သင်၏ဘက်ခ်အပ်ကို ပြန်လည်ရယူနိုင်တော့မည် မဟုတ်ပါ။ - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + သင့်စက်အဟောင်းရှိလျှင် ဘက်ခ်အပ်ကီးကို ဆက်တင် > ဘက်ခ်အပ် တွင် ကြည့်ရှုနိုင်သည်။ ထို့နောက် ဘက်ခ်အပ်ကီးကိုကြည့်ရန် ကိုနှိပ်ပါ။ ပိုမိုလေ့လာရန် @@ -7785,6 +7823,18 @@ အိုကေ + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 6abc76d8d1..5f4e77a978 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -1011,6 +1011,20 @@ Koble til på nytt Gå videre uten overføring + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Overfør meldingslogg - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Ikke overfør - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Vil du koble fra «%1$s»? @@ -1355,16 +1369,18 @@ alle meldingene dine Gjenopprett fra sikkerhetskopi - - Dette inkluderer kun mediefiler du har sendt eller mottatt de siste %1$d dagene. Sikkerhetskopien inneholder: Gjenopprett sikkerhetskopi - + Den siste sikkerhetskopieringen ble gjort kl. %2$s den %1$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Henter informasjon om sikkerhetskopien … + + Skip restore Varsle meg om omtaler @@ -2464,6 +2480,8 @@ Du har gjort for mange forsøk på å registrere dette nummeret. Prøv igjen om %1$s minutter. Klarte ikke å koble til tjenesten. Kontroller at du har en stabil nettilkobling og prøv på nytt. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Vi kunne ikke sende deg bekreftelseskoden via SMS. Motta koden via et taleanrop i stedet. @@ -4311,6 +4329,8 @@ Gjenopprett eller overfør Overfør konto Hopp over + + Skip restore Sikkerhetskopi av samtaler Overfør konto Overfør konto til ny Android-enhet @@ -5823,9 +5843,9 @@ Under behandling Kunne ikke legge til merket - Something went wrong + Noe gikk galt - Your backups subscription couldn\'t be displayed. Please contact support. + Abonnementet på sikkerhetskopiering kan ikke vises. Ta kontakt med brukerstøtten vår. Kunne ikke bekrefte merket @@ -7216,7 +7236,7 @@ Kunne ikke lagre endringene. Sjekk internettilkoblingen og prøv igjen. - Couldn\'t delete call link as it is currently in use. + Anropslenken kunne ikke slettes fordi den er i bruk. Vil du slette lenken? @@ -7307,6 +7327,10 @@ Avvis Godkjenn + + Approve all + + Deny all Vil du slå på varsler i fullskjerm? @@ -7481,7 +7505,13 @@ Vil du hoppe over gjenoppretting? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Hvis du velger å hoppe over dette trinnet, kan du laste ned resten av mediefilene og vedleggene i sikkerhetskopien din senere når du har nok lagringsplass. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Hopp over - "Den siste sikkerhetskopien kunne ikke gjennomføres. Sørg for at enheten er koblet til internett, og trykk på «Sikkerhetskopier nå» for å prøve igjen." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Ubrukte mediefiler fjernes, men kan lastes ned fra sikkerhetskopien når som helst. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Optimalisering av lagring kan kun brukes med betalingsabonnement på sikkerhetskopiering fra Signal. Abonnementet ditt er ikke aktivert ennå. Prøv igjen senere. @@ -7718,6 +7752,8 @@ Forny Les mer + + Processing backup… Du har %1$s med sikkerhetskopierte data som ikke er lagret på denne enheten. Disse dataene slettes når abonnementet ditt utløper om %2$d dag. @@ -7738,6 +7774,8 @@ Sikkerhetskopiering kunne ikke slås av og slettes Det oppsto en nettverksfeil. Sjekk internettilkoblingen og prøv igjen. + + Uploading messages… @@ -7777,7 +7815,7 @@ Sikkerhetskode - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Sikkerhetskoden din består av 64 tegn. Du trenger den for å gjenopprette sikkerhetskopien din når du installerer Signal på nytt. Hvis du glemmer koden, vil du ikke kunne gjenopprette sikkerhetskopien. Signal kan ikke hjelpe deg med dette. @@ -7866,7 +7904,7 @@ Jeg har ikke den gamle enheten - Or you\'re reinstalling Signal on the same device + Eller du skal installere Signal på nytt på samme enhet Gjenopprette eller overføre en konto @@ -7895,15 +7933,15 @@ Skriv inn sikkerhetskoden - Your backup key is a 64-character code required to recover your account and data. + Sikkerhetskoden består av 64 tegn og kreves for å gjenopprette kontoen og dataene dine. Mangler du koden? Sikkerhetskode - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Det er ikke mulig å gjenopprette sikkerhetskopier uten sikkerhetskoden på 64 tegn. Dersom du mister den, kan ikke Signal hjelpe deg med å gjenopprette sikkerhetskopien. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Hvis du har den gamle enheten din, finner du sikkerhetskoden ved å gå til Innstillinger > Sikkerhetskopiering. Der trykker du på «Se sikkerhetskode». Les mer @@ -7952,6 +7990,18 @@ Greit + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index ee64f53c1e..925ce9360b 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -696,7 +696,7 @@ Hiermee wordt de geselecteerde chat permanent verwijderd. - Hiermee worden de geselecteerde chats permanent verwijderd. + Hiermee worden alle %1$d geselecteerde chats permanent verwijderd. @@ -926,7 +926,7 @@ Geplande berichten - Wanneer je een gepland bericht wilt versturen, moet je ervoor zorgen dat je apparaat aan staat en verbonden is met internet op het geplande moment. Als dit niet het geval is, wordt je bericht verstuurd zodra je apparaat weer verbonden is. + Wanneer je een gepland bericht wilt verzenden, moet je ervoor zorgen dat je apparaat aan staat en verbonden is met internet op het geplande moment. Als dit niet het geval is, wordt je bericht verzonden zodra je apparaat weer verbonden is. Oké @@ -1011,6 +1011,20 @@ Probeer opnieuw te koppelen Doorgaan zonder over te zetten + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,17 +1050,17 @@ - Transfer message history + Chatgeschiedenis overzetten - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Niet overschrijven + Niet overzetten - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device ‘%1$s’ ontkoppelen? - Door dit apparaat te ontkoppelen kan het niet langer berichten versturen en ontvangen. + Door dit apparaat te ontkoppelen kan het niet langer berichten verzenden en ontvangen. Netwerkfout Opnieuw proberen @@ -1355,16 +1369,18 @@ Al je berichten Gegevens vanuit een back-up terugzetten - - Uitsluitend media die in de afgelopen %1$d dagen zijn verzonden of ontvangen, zijn inbegrepen. Je back-up omvat: Back-upgegevens terugzetten - + De laatste back-up is gemaakt op %1$s om %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Back-upgegevens aan het ophalen… + + Skip restore Meldingen bij vermeldingen @@ -1625,7 +1641,7 @@ Signal-oproep aan het opzetten - Signal-oproepen starten + Signal-oproep aan het starten Signal-oproep wordt gestopt Oproep annuleren @@ -2013,7 +2029,7 @@ Het kan dan - • Al je berichten lezen \n• Berichten uit jouw naam verzenden + • Al je berichten lezen \n• Berichten uit jouw naam versturen Apparaat aan het koppelen Nieuw apparaat wordt gekoppeld… @@ -2267,11 +2283,11 @@ %1$s, %2$s en %3$d anderen zullen een melding krijgen - %1$s aan het bellen - %1$s en %2$s aan het bellen + Belt naar %1$s + Belt naar %1$s en %2$s - %1$s, %2$s en %3$d ander aan het bellen - %1$s, %2$s en %3$d anderen aan het bellen + Belt naar %1$s, %2$s en %3$d + Belt naar %1$s, %2$s en %3$d %1$s belt jou @@ -2464,6 +2480,8 @@ Je hebt te vaak geprobeerd dit telefoonnummer te registreren. Probeer het opnieuw over %1$s. Het lukt niet om te verbinden met Signals servers. Controleer je internetverbinding en probeer het opnieuw. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. We konden je geen verificatiecode sturen via sms. Probeer in plaats daarvan je code via een spraakoproep te ontvangen. @@ -2835,7 +2853,7 @@ Nieuw bericht, ontgrendel Signal om het weer te geven Berichtaflevering mislukt. - Verhaal kan niet worden verstuurd + Verhaal kan niet worden verzonden Jij naar %1$s Het bericht afleveren is mislukt. @@ -3132,7 +3150,7 @@ - Foto van contact + Afbeelding van contact Aan het laden @@ -3614,7 +3632,7 @@ Annuleren - Wijzig het app-pictogram en de naam in ’%1$s’ + Wijzig het app-pictogram en de naam in ‘%1$s’ Signal moet worden gesloten om het app-pictogram en de naam te wijzigen. Meldingen geven altijd het standaard Signal-pictogram en de standaard app-naam weer. @@ -3645,7 +3663,7 @@ Als leesbevestigingen zijn uitgeschakeld, kun je ook niet zien of anderen jouw berichten hebben gelezen. Typindicatoren Als typindicatoren zijn uitgeschakeld, kun je ook niet zien wanneer anderen aan het typen zijn. - Probeer gepersonaliseerd leren door het toetsenbord te voorkomen + Probeer gepersonaliseerd leren door het toetsenbord te voorkomen. Let op: Signal kan niet garanderen dat gepersonaliseerd leren door het toetsenbord ook daadwerkelijk wordt voorkomen, omdat dit alleen werkt als je toetsenbord-app daaraan meewerkt. Bij mobiele dataverbinding @@ -3707,7 +3725,7 @@ Beltoon Tekstgrootte voor berichten - Dringendheid instellen + Prioriteit instellen Notificatieproblemen oplossen @@ -4198,7 +4216,7 @@ De pincodes komen niet overeen, probeer het opnieuw. Voer de zojuist aangemaakte pincode opnieuw in. - Bevestig je pincode. + Bevestig je pincode Pincode aanmaken mislukt Je pincode is niet opgeslagen. We zullen je later vragen een pincode aan te maken. Pincode aangemaakt. @@ -4311,6 +4329,8 @@ Herstellen of overzetten Account overzetten Overslaan + + Skip restore Back-up van chats Account overzetten Alle gegevens overzetten naar een nieuw Android-apparaat @@ -4357,7 +4377,7 @@ Bel me (%1$02d:%2$02d) - Code opnieuw versturen (%1$02d:%2$02d) + Stuur code opnieuw (%1$02d:%2$02d) Contact opnemen met Signal Support Signal-registratie - Verificatiecode voor Android Onjuiste code @@ -4621,7 +4641,7 @@ Uit de groep verwijderen De beheerdersbevoegdheden van %1$s intrekken? - "‘%1$s’ zal de groepsinformatie en het groepslidmaatschap kunnen bewerken." + "%1$s zal de groepsinformatie en het groepslidmaatschap kunnen bewerken." %1$s uit deze groep verwijderen? @@ -4747,7 +4767,7 @@ Het telefoonnummer dat je hebt ingevuld komt niet overeen met het telefoonnummer van je Signal-account. Weet je zeker dat je je account wilt verwijderen? Hiermee verwijder je je Signal-account en reset je de app. De app wordt gesloten nadat dit proces is voltooid. - Het wissen van lokale gegevens is mislukt. Je kunt de lokale gegevens zelf wissen via het ‘apps’-menu in de Android-instellingenapp. + Het wissen van lokale gegevens is mislukt. Je kunt de lokale gegevens zelf wissen via het ‘apps’-menu in de Android-Instellingen-app. App-instellingen openen Groepen aan het verlaten… @@ -4979,7 +4999,7 @@ Account Bevestig regelmatig je Signal-pincode om deze beter te onthouden. Na verloop van tijd ontvang je minder vaak herinneringen. - Vereis je Signal-pincode om je telefoonnummer opnieuw bij Signal te kunnen registeren + Vereis je Signal-pincode om je telefoonnummer opnieuw bij Signal te kunnen registreren Telefoonnummer wijzigen Jouw accountgegevens @@ -5783,7 +5803,7 @@ Google Pay openen Signal kan je maandelijkse donatie niet verwerken - Het lukt Signal niet om je maandelijkse donatie af te schrijven. Controleer of je betalingsmethode nog steeds juist is. Werk je betalingsinformatie bij in Google Pay als dat nodig is. Signal zal je donatie binnen een paar dagen opnieuw proberen af te schrijven. + Het lukt Signal niet om je maandelijkse donatie af te schrijven. Controleer of je betaalmethode nog steeds juist is. Werk je betaalgegevens bij in Google Pay als dat nodig is. Signal zal je donatie binnen een paar dagen opnieuw proberen af te schrijven. Dit niet opnieuw weergeven Neem contact op met ondersteuning @@ -5814,7 +5834,7 @@ Fout bij het verwerken van donatie - Probeer het via een andere betalingsmethode of neem contact op met je bank voor meer informatie. + Probeer het via een andere betaalmethode of neem contact op met je bank voor meer informatie. Meer lezen @@ -5823,9 +5843,9 @@ Wordt nog verwerkt Kan badge niet toevoegen - Something went wrong + Er ging iets mis - Your backups subscription couldn\'t be displayed. Please contact support. + Je back-upabonnement kon niet worden weergegeven. Neem contact op met ondersteuning. Kon badge niet valideren @@ -5848,7 +5868,7 @@ Je donatie kon niet worden verstuurd vanwege een netwerkfout. Controleer je internetverbinding en probeer het opnieuw. - Je donatie met iDEAL kan niet worden verwerkt. Probeer het via een andere betalingsmethode of neem contact op met je bank voor meer informatie. + Je donatie met iDEAL kan niet worden verwerkt. Probeer het via een andere betaalmethode of neem contact op met je bank voor meer informatie. Donatie namens %1$s @@ -5865,33 +5885,33 @@ - Probeer het via een andere betalingsmethode of neem contact op met je bank voor meer informatie. + Probeer het via een andere betaalmethode of neem contact op met je bank voor meer informatie. - Probeer het via een andere betalingsmethode of neem contact op met je bank voor meer informatie. Als dit een PayPal-transactie was, neem dan contact op met PayPal. + Probeer het via een andere betaalmethode of neem contact op met je bank voor meer informatie. Als dit een PayPal-transactie was, neem dan contact op met PayPal. - Controleer of je betalingsmethode nog steeds juist is. Werk je betalingsinformatie bij in Google Pay als dat nodig is. Probeer het daarna opnieuw. + Controleer of je betaalmethode nog steeds juist is. Werk je betaalgegevens bij in Google Pay als dat nodig is. Probeer het daarna opnieuw. Meer informatie - Controleer of je betalingsmethode nog steeds juist is. Werk je betalingsinformatie bij in Google Pay als dat nodig is. Probeer het daarna opnieuw. Neem contact op met je bank als dit probleem aanhoudt. + Controleer of je betaalmethode nog steeds juist is. Werk je betaalgegevens bij in Google Pay als dat nodig is. Probeer het daarna opnieuw. Neem contact op met je bank als dit probleem aanhoudt. - Je betaalkaart ondersteunt dit type betaling niet. Probeer het via een andere betalingsmethode opnieuw. + Je betaalkaart ondersteunt dit type betaling niet. Probeer het via een andere betaalmethode opnieuw. - Je betaalkaart is verlopen. Werk eerst je betalingsinformatie bij in Google Pay en probeer het daarna opnieuw. + Je betaalkaart is verlopen. Werk eerst je betaalgegevens bij in Google Pay en probeer het daarna opnieuw. Google Pay openen Opnieuw proberen - Je betaalkaartnummer is niet juist. Werk eerst je betalingsinformatie bij in Google Pay en probeer het daarna opnieuw. + Je betaalkaartnummer is niet juist. Werk eerst je betaalgegevens bij in Google Pay en probeer het daarna opnieuw. - Het CVC-nummer van je betaalkaart is onjuist. Werk eerst je betalingsinformatie bij in Google Pay en probeer het daarna opnieuw. + Het CVC-nummer van je betaalkaart is onjuist. Werk eerst je betaalgegevens bij in Google Pay en probeer het daarna opnieuw. - Er staat onvoldoende krediet op je betaalkaart om te doneren. Probeer het via een andere betalingsmethode opnieuw. + Er staat onvoldoende krediet op je betaalkaart om te doneren. Probeer het via een andere betaalmethode opnieuw. - De maand waarop je betalingsmethode verloopt is onjuist. Werk eerst je betalingsinformatie bij in Google Pay en probeer het daarna opnieuw. + De maand waarop je betaalmethode verloopt is onjuist. Werk eerst je betaalgegevens bij in Google Pay en probeer het daarna opnieuw. - Het jaar waarop je betalingsmethode verloopt is onjuist. Werk eerst je betalingsinformatie bij in Google Pay en probeer het daarna opnieuw. + Het jaar waarop je betaalmethode verloopt is onjuist. Werk eerst je betaalgegevens bij in Google Pay en probeer het daarna opnieuw. Probeer de betaling opnieuw te voltooien of neem contact op met je bank voor meer informatie. @@ -6590,7 +6610,7 @@ Uit verhaal verwijderen - Toch verzenden + Toch versturen Contacten beoordelen @@ -6871,7 +6891,7 @@ Donatie kan niet worden verwerkt - Er zijn problemen met het verwerken van je bankoverschrijving. Er is geen geld afgeschreven. Probeer het via een andere betalingsmethode of neem contact op met je bank voor meer informatie. + Er zijn problemen met het verwerken van je bankoverschrijving. Er is geen geld afgeschreven. Probeer het via een andere betaalmethode of neem contact op met je bank voor meer informatie. Opnieuw proberen @@ -7216,7 +7236,7 @@ Kan wijzigingen niet opslaan. Controleer je internetverbinding en probeer het opnieuw. - Couldn\'t delete call link as it is currently in use. + Oproeplink kon niet worden verwijderd, omdat deze op dit moment in gebruik is. Link verwijderen? @@ -7297,7 +7317,7 @@ - Verzoeken om deel te nemen aan deze oproep + Deelnameverzoeken %1$d wachtende persoon @@ -7307,6 +7327,10 @@ Weigeren Goedkeuren + + Approve all + + Deny all Meldingen op volledig scherm inschakelen? @@ -7443,7 +7467,7 @@ Je back-upabonnement kon niet worden verlengd - Controleer of je betalingsinformatie nog steeds klopt. Tik op Abonnement beheren en tik onder Betaalmethoden op Bijwerken. + Controleer of je betaalmethode nog steeds juist is. Tik op Abonnement beheren en tik onder Betaalmethoden op Bijwerken. Back-up kon niet worden voltooid @@ -7481,7 +7505,13 @@ Herstel overslaan? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Als je herstellen overslaat, kunnen de resterende media en bijlagen in je back-up op een later moment worden gedownload wanneer er opslagruimte vrijkomt. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Downloaden overslaan - "De laatste back-up kon niet worden voltooid. Zorg ervoor dat je telefoon is verbonden met wifi. Tik op ‘Nu back-up maken’ en probeer het opnieuw." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Ongebruikte media worden verwijderd, maar kunnen altijd opnieuw gedownload worden vanuit je back-up. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Opslagoptimalisatie kan alleen worden gebruikt met een betaald abonnement op Signal back-ups. Je back-upabonnement wordt nog verwerkt en is nog niet actief. Probeer het later opnieuw. @@ -7718,6 +7752,8 @@ Vernieuwen Meer lezen + + Processing backup… Je hebt %1$s aan back-upgegevens die niet op dit apparaat staan. Je back-up wordt verwijderd over %2$d dag wanneer je abonnement afloopt. @@ -7738,6 +7774,8 @@ Back-ups konden niet worden uitgeschakeld en verwijderd Er is een netwerkfout opgetreden. Controleer je internetverbinding en probeer het opnieuw. + + Uploading messages… @@ -7777,7 +7815,7 @@ Je back-upsleutel - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Je back-upsleutel is een code van 64 tekens waarmee je je back-up kunt herstellen wanneer je Signal opnieuw installeert. Als je je sleutel vergeet, kun je je back-up niet herstellen. Signal kan je niet helpen je back-up te herstellen. @@ -7866,7 +7904,7 @@ Ik heb mijn oude telefoon niet - Or you\'re reinstalling Signal on the same device + Of je installeert Signal opnieuw op hetzelfde apparaat Account herstellen of overzetten @@ -7895,15 +7933,15 @@ Voer je back-upsleutel in - Your backup key is a 64-character code required to recover your account and data. + Je back-upsleutel is een code van 64 tekens die je nodig hebt om je account en gegevens te herstellen. Geen back-upsleutel? Back-upsleutel - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Back-ups kunnen niet worden hersteld zonder de herstelcode van 64 tekens. Als je de back-upsleutel kwijt bent, kan Signal je niet helpen je back-up te herstellen. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Op je oude apparaat kun je de back-upsleutel bekijken in Instellingen > Back-ups. Tik vervolgens op Back-upsleutel weergeven. Meer lezen @@ -7952,6 +7990,18 @@ Oké + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index e0ea601610..f54b14d4b6 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -1011,6 +1011,20 @@ ਦੁਬਾਰਾ ਲਿੰਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਟ੍ਰਾਂਸਫਰ ਕੀਤੇ ਬਿਨਾਂ ਜਾਰੀ ਰੱਖੋ + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + ਪੁਰਾਣੇ ਸੁਨੇਹੇ ਟ੍ਰਾਂਸਫਰ ਕਰੋ - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - ਟਰਾਂਸਫ਼ਰ ਨਾ ਕਰੋ + ਟ੍ਰਾਂਸਫਰ ਨਾ ਕਰੋ - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device ਕੀ \"%1$s\" ਨੂੰ ਅਨਲਿੰਕ ਕਰਨਾ ਹੈ? @@ -1355,16 +1369,18 @@ ਤੁਹਾਡੇ ਸਾਰੇ ਸੁਨੇਹੇ ਬੈਕਅੱਪ ਤੋਂ ਰੀਸਟੋਰ ਕਰੋ - - ਸਿਰਫ਼ ਪਿਛਲੇ %1$d ਦਿਨਾਂ ਵਿੱਚ ਭੇਜਿਆ ਜਾਂ ਪ੍ਰਾਪਤ ਹੋਇਆ ਮੀਡੀਆ ਸ਼ਾਮਲ ਹੈ। ਤੁਹਾਡੇ ਬੈਕਅੱਪ ਵਿੱਚ ਸ਼ਾਮਲ ਹੈ: ਬੈਕਅੱਪ ਰੀਸਟੋਰ ਕਰੋ - + ਤੁਹਾਡਾ ਆਖਰੀ ਬੈਕਅੱਪ %1$s ਨੂੰ %2$s ਲਿਆ ਗਿਆ ਸੀ। + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. ਬੈਕਅੱਪ ਦੇ ਵੇਰਵੇ ਪ੍ਰਾਪਤ ਕੀਤੇ ਜਾ ਰਹੇ ਹਨ… + + Skip restore ਹਵਾਲਿਆਂ ਲਈ ਮੈਨੂੰ ਸੂਚਿਤ ਕਰੋ @@ -2464,6 +2480,8 @@ ਤੁਸੀਂ ਇਸ ਨੰਬਰ ਨੂੰ ਰਜਿਸਟਰ ਕਰਨ ਲਈ ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ ਕਰ ਚੁੱਕੇ ਹੋ। ਕਿਰਪਾ ਕਰਕੇ %1$s ਬਾਅਦ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ। ਸੇਵਾ ਨਾਲ ਜੁੜਨ ਵਿੱਚ ਅਸਮਰੱਥ। ਕਿਰਪਾ ਕਰਕੇ ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨ ਦੀ ਜਾਂਚ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ। + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. ਅਸੀਂ ਤੁਹਾਨੂੰ SMS ਰਾਹੀਂ ਤਸਦੀਕ ਕੋਡ ਨਹੀਂ ਭੇਜ ਸਕੇ। ਇਸਦੀ ਬਜਾਏ ਵੌਇਸ ਕਾਲ ਰਾਹੀਂ ਆਪਣਾ ਕੋਡ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ। @@ -4311,6 +4329,8 @@ ਰੀਸਟੋਰ ਜਾਂ ਟ੍ਰਾਂਸਫਰ ਕਰੋ ਖਾਤਾ ਟ੍ਰਾਂਸਫਰ ਕਰੋ ਛੱਡੋ + + Skip restore ਚੈਟ ਬੈਕਅਪ ਖਾਤਾ ਟ੍ਰਾਂਸਫਰ ਕਰੋ ਖਾਤੇ ਨੂੰ ਨਵੇਂ Android ਡਿਵਾਈਸ ਉੱਤੇ ਟ੍ਰਾਂਸਫਰ ਕਰੋ @@ -5823,9 +5843,9 @@ ਹਾਲੇ ਕਾਰਵਾਈ ਜਾਰੀ ਹੈ ਬੈਜ ਸ਼ਾਮਲ ਨਹੀਂ ਕਰ ਸਕੇ - Something went wrong + ਕੁਝ ਗਲਤ ਵਾਪਰ ਗਿਆ ਹੈ - Your backups subscription couldn\'t be displayed. Please contact support. + ਤੁਹਾਡੀ ਬੈਕਅੱਪ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਨੂੰ ਦਿਖਾ ਨਹੀਂ ਸਕੇ। ਕਿਰਪਾ ਕਰਕੇ ਸਹਾਇਤਾ ਟੀਮ ਨਾਲ ਸੰਪਰਕ ਕਰੋ। ਬੈਜ ਨੂੰ ਪ੍ਰਮਾਣਿਤ ਕਰਨ ਵਿੱਚ ਅਸਫਲ ਰਹੇ @@ -7216,7 +7236,7 @@ ਤਬਦੀਲੀਆਂ ਨੂੰ ਸੁਰੱਖਿਅਤ ਨਹੀਂ ਕਰ ਸਕੇ। ਆਪਣੇ ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨ ਦੀ ਜਾਂਚ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ। - Couldn\'t delete call link as it is currently in use. + ਕਾਲ ਦਾ ਲਿੰਕ ਮਿਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਿਆ ਕਿਉਂਕਿ ਉਹ ਲਿੰਕ ਇਸ ਵੇਲੇ ਵਰਤਿਆ ਜਾ ਰਿਹਾ ਹੈ। ਕੀ ਲਿੰਕ ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ? @@ -7307,6 +7327,10 @@ ਰੱਦ ਕਰੋ ਮਨਜ਼ੂਰ ਕਰੋ + + Approve all + + Deny all ਕੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਉੱਤੇ ਦਿਖਣ ਵਾਲੀਆਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਚਾਲੂ ਕਰਨਾ ਹੈ? @@ -7481,7 +7505,13 @@ ਕੀ ਰੀਸਟੋਰ ਕਰਨਾ ਛੱਡਣਾ ਹੈ? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + ਜੇਕਰ ਤੁਸੀਂ ਰੀਸਟੋਰ ਕਰਨਾ ਛੱਡ ਦਿੰਦੇ ਹੋ, ਤਾਂ ਅਗਲੀ ਵਾਰ ਜਦੋਂ ਸਟੋਰੇਜ ਦੀ ਥਾਂ ਉਪਲਬਧ ਹੋਣ \'ਤੇ ਤੁਹਾਡੇ ਬੈਕਅੱਪ ਵਿੱਚ ਬਾਕੀ ਬਚਿਆ ਮੀਡੀਆ ਅਤੇ ਅਟੈਚਮੈਂਟਾਂ ਨੂੰ ਬਾਅਦ ਵਿੱਚ ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ। + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ ਡਾਊਨਲੋਡ ਛੱਡੋ - "ਤੁਹਾਡਾ ਪਿਛਲਾ ਬੈਕਅੱਪ ਪੂਰਾ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡਾ ਫ਼ੋਨ Wi-F ਨਾਲ ਕਨੈਕਟ ਹੈ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਲਈ ਹੁਣੇ ਬੈਕਅੱਪ ਲਓ \'ਤੇ ਟੈਪ ਕਰੋ।" + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ ਵਰਤਿਆ ਨਾ ਜਾਣ ਵਾਲਾ ਮੀਡੀਆ ਔਫਲੋਡ ਕਰ ਦਿੱਤਾ ਜਾਵੇਗਾ, ਪਰ ਉਹ ਮੀਡੀਆ ਕਿਸੇ ਵੀ ਸਮੇਂ ਤੁਹਾਡੇ ਬੈਕਅੱਪ ਵਿੱਚੋਂ ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ। - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + ਸਟੋਰੇਜ ਅਨੁਕੂਲਤਾ ਦੀ ਵਰਤੋਂ ਸਿਰਫ਼ Signal ਬੈਕਅੱਪ ਦੇ ਭੁਗਤਾਨਸ਼ੁਦਾ ਟੀਅਰ ਨਾਲ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ। ਤੁਹਾਡੀ ਬੈਕਅੱਪ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਉੱਤੇ ਹਾਲੇ ਕਾਰਵਾਈ ਜਾਰੀ ਹੈ ਅਤੇ ਹਾਲੇ ਕਿਰਿਆਸ਼ੀਲ ਨਹੀਂ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ। @@ -7718,6 +7752,8 @@ ਰੀਨਿਊ ਕਰੋ ਹੋਰ ਜਾਣੋ + + Processing backup… ਤੁਹਾਡੇ ਕੋਲ %1$s ਬੈਕਅੱਪ ਡਾਟਾ ਹੈ ਜੋ ਇਸ ਡਿਵਾਈਸ \'ਤੇ ਨਹੀਂ ਹੈ। %2$d ਦਿਨ ਵਿੱਚ ਤੁਹਾਡੀ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਦੇ ਸਮਾਪਤ ਹੋਣ \'ਤੇ ਤੁਹਾਡਾ ਬੈਕਅੱਪ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ। @@ -7738,6 +7774,8 @@ ਬੈਕਅੱਪ ਨੂੰ ਬੰਦ ਕੀਤਾ ਅਤੇ ਮਿਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਿਆ ਨੈੱਟਵਰਕ ਵਿੱਚ ਕੋਈ ਗੜਬੜ ਆ ਗਈ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨ ਦੀ ਜਾਂਚ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ। + + Uploading messages… @@ -7777,7 +7815,7 @@ ਤੁਹਾਡੀ ਬੈਕਅੱਪ ਕੁੰਜੀ - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + ਤੁਹਾਡੀ ਬੈਕਅੱਪ ਕੁੰਜੀ 64-ਅੱਖਰਾਂ ਵਾਲਾ ਕੋਡ ਹੈ ਜਿਸ ਦੀ ਮਦਦ ਨਾਲ ਤੁਸੀਂ Signal ਨੂੰ ਮੁੜ-ਇੰਸਟਾਲ ਕਰਨ ਤੋਂ ਬਾਅਦ ਆਪਣਾ ਬੈਕਅੱਪ ਰੀਸਟੋਰ ਕਰ ਸਕਦੇ ਹੋ। ਜੇਕਰ ਤੁਸੀਂ ਆਪਣੀ ਕੁੰਜੀ ਭੁੱਲ ਜਾਂਦੇ ਹੋ, ਤਾਂ ਤੁਸੀਂ ਆਪਣਾ ਬੈਕਅੱਪ ਰੀਸਟੋਰ ਨਹੀਂ ਕਰ ਸਕੋਗੇ। Signal ਤੁਹਾਡੇ ਬੈਕਅੱਪ ਨੂੰ ਰਿਕਵਰ ਕਰਨ ਵਿੱਚ ਤੁਹਾਡੀ ਮਦਦ ਨਹੀਂ ਕਰ ਸਕੇਗਾ। @@ -7866,7 +7904,7 @@ ਮੇਰੇ ਕੋਲ ਮੇਰਾ ਪੁਰਾਣਾ ਫ਼ੋਨ ਨਹੀਂ ਹੈ - Or you\'re reinstalling Signal on the same device + ਜਾਂ ਤੁਸੀਂ ਉਸੇ ਡਿਵਾਈਸ \'ਤੇ Signal ਨੂੰ ਮੁੜ ਇੰਸਟਾਲ ਕਰ ਰਹੇ ਹੋ ਖਾਤਾ ਰੀਸਟੋਰ ਕਰੋ ਜਾਂ ਟ੍ਰਾਂਸਫਰ ਕਰੋ @@ -7895,15 +7933,15 @@ ਆਪਣੀ ਬੈਕਅੱਪ ਕੁੰਜੀ ਦਰਜ ਕਰੋ - Your backup key is a 64-character code required to recover your account and data. + ਤੁਹਾਡੀ ਬੈਕਅੱਪ ਕੁੰਜੀ ਤੁਹਾਡੇ ਖਾਤੇ ਅਤੇ ਡਾਟਾ ਨੂੰ ਰਿਕਵਰ ਕਰਨ ਲਈ ਲੋੜੀਂਦਾ 64-ਅੱਖਰਾਂ ਵਾਲਾ ਕੋਡ ਹੈ। ਤੁਹਾਡੇ ਕੋਲ ਬੈਕਅੱਪ ਕੁੰਜੀ ਨਹੀਂ ਹੈ? ਬੈਕਅੱਪ ਕੁੰਜੀ - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + 64-ਅੰਕਾਂ ਦੇ ਰਿਕਵਰੀ ਕੋਡ ਤੋਂ ਬਿਨਾਂ ਬੈਕਅੱਪ ਰਿਕਵਰ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ ਹਨ। ਜੇਕਰ ਤੁਸੀਂ ਆਪਣੀ ਬੈਕਅੱਪ ਕੁੰਜੀ ਗੁਆ ਦਿੱਤੀ ਹੈ ਤਾਂ Signal ਤੁਹਾਡੇ ਬੈਕਅੱਪ ਨੂੰ ਰੀਸਟੋਰ ਕਰਨ ਵਿੱਚ ਮਦਦ ਨਹੀਂ ਕਰ ਸਕਦਾ। - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + ਜੇਕਰ ਤੁਹਾਡੇ ਕੋਲ ਤੁਹਾਡਾ ਪੁਰਾਣਾ ਡਿਵਾਈਸ ਹੈ ਤਾਂ ਤੁਸੀਂ ਸੈਟਿੰਗਾਂ > ਬੈਕਅੱਪ ਵਿੱਚ ਆਪਣੀ ਬੈਕਅੱਪ ਕੁੰਜੀ ਦੇਖ ਸਕਦੇ ਹੋ। ਫਿਰ ਬੈਕਅੱਪ ਕੁੰਜੀ ਦੇਖੋ \'ਤੇ ਟੈਪ ਕਰੋ। ਹੋਰ ਜਾਣੋ @@ -7952,6 +7990,18 @@ ਠੀਕ ਹੈ + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index eb4ccf5db2..841e95ece2 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -1051,6 +1051,20 @@ Spróbuj połączyć ponownie Kontynuuj bez przenoszenia + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1076,13 +1090,13 @@ - Transfer message history + Przenieś historię wiadomości - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Nie przenoś - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Odłączyć „%1$s”? @@ -1431,16 +1445,18 @@ Wszystkie wiadomości Przywróć z kopii zapasowej - - Uwzględniane są tylko multimedia wysłane lub odebrane w ciągu ostatnich %1$d dni. Kopia zapasowa obejmuje: Przywróć kopię zapasową - + Ostatnia kopia zapasowa została utworzona w dniu %1$s o godz. %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Pobieranie szczegółów kopii zapasowej… + + Skip restore Powiadom mnie o wzmiankach @@ -2632,6 +2648,8 @@ Zbyt dużo nieudanych prób rejestracji tego numeru. Spróbuj ponownie za %1$s. Nie można połączyć się z serwisem. Proszę sprawdzić połączenie internetowe i spróbować ponownie. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Nie udało nam się wysłać Ci kodu weryfikacyjnego przez SMS. Spróbuj go zażądać za pomocą połączenia głosowego. @@ -4533,6 +4551,8 @@ Przywróć lub przenieś Przenieś konto Pomiń + + Skip restore Kopia zapasowa czatów Przenieś konto Przenieś konto na nowe urządzenie z systemem Android @@ -6089,9 +6109,9 @@ Trwa przetwarzanie Nie udało się dodać odznaki - Something went wrong + Coś poszło nie tak - Your backups subscription couldn\'t be displayed. Please contact support. + Nie udało się wyświetlić usługi tworzenia kopii zapasowych. Skontaktuj się z pomocą techniczną. Nie udało się zweryfikować odznaki @@ -7530,7 +7550,7 @@ Nie udało się zapisać zmian. Sprawdź połączenie z internetem i spróbuj ponownie. - Couldn\'t delete call link as it is currently in use. + Nie udało się usunąć linku do połączenia, ponieważ jest on obecnie używany. Usunąć link? @@ -7625,6 +7645,10 @@ Odrzuć Zaakceptuj + + Approve all + + Deny all Włączyć powiadomienia pełnoekranowe? @@ -7803,7 +7827,13 @@ Pominąć przywracanie? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Jeśli pominiesz przywracanie, pozostałe multimedia i załączniki z kopii zapasowej będzie można pobrać później, gdy zwolni się miejsce na dysku. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7836,7 +7866,11 @@ Pomiń pobieranie - "Nie udało się ukończyć tworzenia ostatniej kopii zapasowej. Upewnij się, że urządzenie jest podłączone do Wi-Fi, i ponownie wybierz opcję Utwórz kopię zapasową teraz." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7880,7 +7914,7 @@ Nieużywane multimedia zostaną usunięte, ale w dowolnym momencie można je będzie pobrać z kopii zapasowej. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Optymalizacja pamięci jest dostępna tylko w ramach płatnej usługi kopii zapasowych Signal. Twoja usługa kopii zapasowych jest jeszcze przetwarzana i nieaktywna. Spróbuj ponownie później. @@ -8044,6 +8078,8 @@ Odnów Dowiedz się więcej + + Processing backup… Masz %1$s danych kopii zapasowej, których nie ma na tym urządzeniu. Twoja kopia zapasowa zostanie usunięta wraz z wygaśnięciem subskrypcji za %2$d dzień. @@ -8068,6 +8104,8 @@ Nie udało się wyłączyć i usunąć kopii zapasowych. Błąd sieci. Sprawdź połączenie z internetem i spróbuj ponownie. + + Uploading messages… @@ -8107,7 +8145,7 @@ Twój kod kopii zapasowej - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Kod kopii zapasowej składający się z 64 znaków umożliwi Ci przywrócenie kopii zapasowej po ponownej instalacji aplikacji Signal. Jeśli go zapomnisz, stracisz możliwość przywrócenia kopii zapasowej. Signal nie będzie mógł pomóc w odzyskaniu kopii zapasowej. @@ -8200,7 +8238,7 @@ Nie mam swojego starego telefonu - Or you\'re reinstalling Signal on the same device + Wybierz tę opcję również wtedy, gdy ponownie instalujesz Signal na tym samym urządzeniu Przywróć lub przenieś konto @@ -8229,15 +8267,15 @@ Wprowadź kod kopii zapasowej - Your backup key is a 64-character code required to recover your account and data. + Kod kopii zapasowej to sekwencja 64 znaków potrzebna do przywrócenia konta i danych. Nie masz kodu kopii zapasowej? Kod kopii zapasowej - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Bez kodu składającego się z 64 znaków nie odzyskasz kopii zapasowej. Jeśli nie pamiętasz kodu kopii zapasowej, nie będziemy w stanie przywrócić kopii zapasowej Twojego konta Signal. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Jeśli masz dostęp do swojego starego urządzenia, kod kopii zapasowej znajdziesz w sekcji Ustawienia > Kopie zapasowe. Następnie wybierz opcję Wyświetl kod kopii zapasowej. Dowiedz się więcej @@ -8286,6 +8324,18 @@ OK + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 9e3ec2c537..e133135e4b 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -1011,6 +1011,20 @@ Tente vincular novamente Continuar sem transferir + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Transferir histórico de mensagens - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Não transferir - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Desvincular \"%1$s\"? @@ -1355,16 +1369,18 @@ Todas as mensagens Restaurar do backup - - Somente arquivos de mídia enviados ou recebidos nos últimos %1$d dias serão incluídos. Seu backup inclui: Restaurar backup - + Seu último backup foi feito em %1$s à(s) %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Buscando detalhes do backup… + + Skip restore Notifique-me ao ser mencionado @@ -2003,7 +2019,7 @@ Denunciar… - Frases-chave não correspondem! + As frases de recuperação não coincidem! Frase de recuperação anterior incorreta! Insira uma nova frase de recuperação! @@ -2464,6 +2480,8 @@ Você tentou cadastrar esse número muitas vezes. Tente novamente em %1$s. Não é possível conectar ao serviço. Favor verificar a conexão à rede e tentar novamente. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Não foi possível enviar um código de verificação por SMS. Tente receber seu código por chamada de voz. @@ -4311,6 +4329,8 @@ Restaurar ou transferir Transferir conta Ignorar + + Skip restore Backups de chat Transferir conta Transferir conta para um novo dispositivo Android @@ -4340,7 +4360,7 @@ Digite sua frase de recuperação de backup para verificação Verificar Você digitou com sucesso sua frase de recuperação de backup - A senha está incorreta + A frase de recuperação está incorreta Criando backup do Signal… Verificando backup do Signal… @@ -4911,16 +4931,16 @@ Senha de recuperação inválida Verifique se você digitou a sua senha corretamente e tente novamente. Copiar para a área de transferência? - Se você optar por guardar a sua senha de recuperação digitalmente, certifique-se de que ela estará armazenada com segurança em um lugar de sua confiança. + Se você optar por guardar a sua frase de recuperação digitalmente, certifique-se de que ela estará armazenada com segurança em um lugar de sua confiança. Copiar Confirmar senha de recuperação Digite as seguintes palavras da sua senha de recuperação. Palavra %1$d - Ver a senha novamente + Ver frase de recuperação novamente Pronto - Senha de recuperação confirmada + Frase de recuperação confirmada Digite a senha de recuperação @@ -5823,9 +5843,9 @@ Ainda em processamento Não foi possível adicionar o selo - Something went wrong + Algo deu errado - Your backups subscription couldn\'t be displayed. Please contact support. + Não foi possível exibir sua assinatura de backups. Entre em contato com o suporte. Falha na validação do selo @@ -7216,7 +7236,7 @@ Não foi possível salvar as alterações. Verifique sua conexão de rede e tente novamente. - Couldn\'t delete call link as it is currently in use. + Não foi possível apagar o link da chamada, pois está em uso no momento. Excluir link? @@ -7307,6 +7327,10 @@ Rejeitar Aprovar + + Approve all + + Deny all Ativar notificações em tela cheia? @@ -7481,7 +7505,13 @@ Pular restauração? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Se você pular essa etapa, poderá baixar a mídia e os anexos restantes no seu backup posteriormente, quando houver espaço de armazenamento disponível. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Pular download - "Não foi possível concluir seu último backup. Confira se o seu telefone está conectado à rede Wi-Fi e toque em \"Fazer backup agora\" para tentar novamente." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Arquivos de mídia não utilizados serão descarregados, mas poderão ser baixados do seu backup a qualquer momento. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + A otimização de armazenamento só pode ser usada com o plano pago dos Backups do Signal. Sua assinatura de backups está sendo processada e ainda não está ativa. Tente novamente mais tarde. @@ -7718,6 +7752,8 @@ Renovar Saiba mais + + Processing backup… Você tem %1$s de dados de backup que não estão neste dispositivo. Seu backup será apagado quando sua assinatura terminar em %2$d dia. @@ -7738,6 +7774,8 @@ Não foi possível desativar e apagar backups Ocorreu um erro na rede. Verifique sua conexão à Internet e tente novamente. + + Uploading messages… @@ -7777,7 +7815,7 @@ Sua chave de backup - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Sua chave de backup é um código de 64 dígitos que permite restaurar seu backup ao reinstalar o Signal. Se você esquecer sua chave, não será possível restaurar o backup. O Signal não tem como ajudar na recuperação do seu backup. @@ -7866,7 +7904,7 @@ Não tenho meu telefone antigo - Or you\'re reinstalling Signal on the same device + Ou se você está reinstalando o Signal no mesmo dispositivo Restaurar ou transferir conta @@ -7895,15 +7933,15 @@ Digite sua chave de backup - Your backup key is a 64-character code required to recover your account and data. + Sua chave de backup é um código de 64 dígitos que você deve usar para recuperar sua conta e dados. Não tem uma chave de backup? Chave de backup - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Os backups não podem ser recuperados sem o código de recuperação de 64 dígitos. Se perder sua chave de backup, você não poderá restaurar seu backup pelo Signal. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Se tiver seu dispositivo antigo com você, poderá ver sua chave de backup em Configurações > Backups. Em seguida, é só tocar em Ver chave de backup. Saiba mais @@ -7952,6 +7990,18 @@ Ok + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 99da0dd50f..2168d3fc37 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -1011,6 +1011,20 @@ Experimente vincular outra vez Continuar sem transferir + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Transferir histórico de mensagens - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Não transferir - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Desassociar \"%1$s\"? @@ -1355,16 +1369,18 @@ Todas as suas mensagens Restaurar a partir da cópia de segurança - - Só estão incluídos ficheiros multimédia enviados ou recebidos nos últimos %1$d. A sua cópia de segurança inclui: Restaurar cópia de segurança - + A sua última cópia de segurança foi feita a %1$s às %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. A obter detalhes da cópia de segurança… + + Skip restore Notificar-me quando for \'Mencionado\' @@ -2464,6 +2480,8 @@ Fez demasiadas tentativas para registar este número. Tente mais tarde dentro de %1$s. Não foi possível ligar ao serviço. Por favor, verifique a sua ligação à rede e tente novamente. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Não lhe conseguimos enviar um código de verificação via SMS. Experimente receber o código via chamada de voz. @@ -4311,6 +4329,8 @@ Restaurar ou transferir Transferir conta Saltar + + Skip restore Cópias de segurança dos chats Transferir conta Transferir conta para um dispositivo Android novo @@ -5823,9 +5843,9 @@ Ainda a processar Não foi possível adicionar o crachá - Something went wrong + Ocorreu algo de errado - Your backups subscription couldn\'t be displayed. Please contact support. + Não foi possível exibir a sua subscrição das cópias de segurança. Entre em contacto com a equipa de suporte. Falha ao validar o crachá @@ -7216,7 +7236,7 @@ Não foi possível guardar as alterações. Verifique a sua ligação à internet e tente novamente. - Couldn\'t delete call link as it is currently in use. + Não foi possível eliminar o link de chamada dado que este está a ser utilizado. Eliminar link? @@ -7307,6 +7327,10 @@ Rejeitar Aprovar + + Approve all + + Deny all Ativar as notificações de ecrã inteiro? @@ -7481,7 +7505,13 @@ Ignorar restauro? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Se saltar o restauro, os restantes ficheiros multimédia e anexos da cópia de segurança podem ser transferidos mais tarde, quando o espaço de armazenamento estiver disponível. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Saltar transferência - "A sua última cópia de segurança não pôde ser completada. Certifique-se de que o seu telemóvel está ligado a uma rede Wi-Fi e toca em \"Fazer cópia de segurança\" para tentar outra vez." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Os ficheiros não utilizados serão movidos, mas podem ser descarregados a partir da sua cópia de segurança a qualquer altura. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + A otimização do armazenamento só pode ser usada com a categoria paga das Cópias de Segurança do Signal. A sua subscrição das cópias de segurança está a ser processada e ainda não está ativa. Tente novamente mais tarde. @@ -7718,6 +7752,8 @@ Renovar Saber mais + + Processing backup… Tem %1$s de dados de cópia de segurança que não estão neste dispositivo. A sua cópia de segurança será eliminada quando a sua subscrição terminar dentro de %2$d dia. @@ -7738,6 +7774,8 @@ Não foi possível desativar e eliminar as cópias de segurança Ocorreu um erro de rede. Verifique a sua ligação à internet e tente outra vez. + + Uploading messages… @@ -7777,7 +7815,7 @@ A sua chave da cópia de segurança - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + A sua chave da cópia de segurança é um código de 64 caracteres que lhe permite restaurar a sua cópia de segurança ao reinstalar o Signal. Se esquecer a sua chave, não poderá restaurar a cópia de segurança. O Signal não o pode ajudar a restaurar a sua cópia de segurança. @@ -7866,7 +7904,7 @@ Não tenho o meu telemóvel antigo - Or you\'re reinstalling Signal on the same device + Ou está a reinstalar o Signal no mesmo dispositivo Restaure ou transfira a sua conta @@ -7895,15 +7933,15 @@ Introduza a sua chave da cópia de segurança - Your backup key is a 64-character code required to recover your account and data. + A sua chave da cópia de segurança é um código de 64 caracteres necessário para recuperar a sua conta e os seus dados. Não tem chave da cópia de segurança? Chave da cópia de segurança - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + As cópias de segurança não podem ser recuperadas sem o seu código de recuperação de 64 caracteres. Se tiver perdido a sua chave da cópia de segurança, o Signal não pode ajudar a restaurar a sua cópia de segurança. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Se tiver acesso ao seu dispositivo antigo, pode ver a sua chave da cópia de segurança em Definições > Cópias de segurança. Depois, toque em \"Ver chave da cópia de segurança\". Saber mais @@ -7952,6 +7990,18 @@ Ok + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index ed717439bf..ba135ffa3c 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -1031,6 +1031,20 @@ Încearcă din nou să asociezi Continuă fără transfer + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1056,13 +1070,13 @@ - Transfer message history + Transferă istoricul mesajelor - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Nu transfera - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Disociez \"%1$s\"? @@ -1393,16 +1407,18 @@ Toate mesajele tale Restaurare din backup - - Sunt incluse numai fișierele trimise sau primite în ultimele %1$d de zile. Backup-ul tău include: Restaurare backup - + Ultima copie de rezervă a fost făcută pe %1$s la %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Se preiau detaliile backup-ului… + + Skip restore Anunță-mă pentru Mențiuni @@ -2548,6 +2564,8 @@ Ai făcut prea multe încercări pentru înregistrarea acestui număr. Încearcă din nou în %1$s. Nu s-a putut realiza conexiunea la serviciu. Te rugăm verifică conexiunea la rețea și încearcă din nou. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Nu ți-am putut trimite un cod de verificare prin SMS. Încearcă să primești codul prin apel vocal. @@ -4422,6 +4440,8 @@ Restaurează sau transferă Transfer cont Omite + + Skip restore Backup-uri pt. conversații Transfer cont Transfer cont pe un nou dispozitiv Android @@ -5956,9 +5976,9 @@ În curs de procesare Nu s-a putut adăuga insigna - Something went wrong + Ceva nu a funcționat - Your backups subscription couldn\'t be displayed. Please contact support. + Abonamentul pentru copii de rezervă nu a putut fi afișat. Contactează serviciul de asistență. Validarea insignei a eșuat @@ -7373,7 +7393,7 @@ Nu s-au putut salva schimbările. Verifică-ți conexiunea la rețea și încearcă din nou. - Couldn\'t delete call link as it is currently in use. + Nu s-a putut elimina linkul apelului deoarece este în uz momentan. Ștergi linkul? @@ -7466,6 +7486,10 @@ Respinge Aprobă + + Approve all + + Deny all Activezi notificările pe ecran complet? @@ -7642,7 +7666,13 @@ Sari peste restaurare? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Dacă omiți restaurarea, fișierele media rămase și atașamentele din backup pot fi descărcate mai târziu, când spațiul de stocare devine disponibil. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7675,7 +7705,11 @@ Omite descărcarea - "Ultimul backup nu a putut fi finalizat. Asigură-te că telefonul este conectat la Wi-Fi și atinge Back-up acum pentru a încerca din nou." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7719,7 +7753,7 @@ Fișierele neutilizate vor fi mutate, dar pot fi descărcate oricând din backup. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Optimizarea stocării poate fi utilizată numai cu nivelul plătit de Backup-uri Signal. Abonamentul pentru copiile de rezervă este încă în procesare și nu este încă activ. Încearcă mai târziu. @@ -7881,6 +7915,8 @@ Reînnoiește Află mai multe + + Processing backup… Ai %1$s de date de back-up care nu se află pe acest dispozitiv. Copia de rezervă va fi ștearsă când abonamentul se încheie în %2$d zi. @@ -7903,6 +7939,8 @@ Nu s-a putut dezactiva și nu s-au putut șterge copiile de rezervă A apărut o eroare de rețea. Verifică conexiunea la internet și încearcă din nou. + + Uploading messages… @@ -7942,7 +7980,7 @@ Codul tău de rezervă - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Codul de rezervă este un cod din 64 de caractere care îți permite să îți restaurezi backup-ul atunci când reinstalezi Signal. Dacă uiți codul, nu vei putea să îți restaurezi copia de rezervă. Signal nu te poate ajuta să îți recuperezi backup-ul. @@ -8033,7 +8071,7 @@ Nu am telefonul meu vechi - Or you\'re reinstalling Signal on the same device + Sau reinstalează Signal pe același dispozitiv Restaurează sau transferă contul @@ -8062,15 +8100,15 @@ Introdu codul de rezervă - Your backup key is a 64-character code required to recover your account and data. + Codul de rezervă este un cod format din 64 de caractere, necesar pentru a-ți recupera contul și datele. Nu ai cod de rezervă? Codul de rezervă - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Copiile de rezervă nu pot fi recuperate fără codul lor de recuperare de 64 de caractere. Dacă ți-ai pierdut codul de rezervă, Signal nu te poate ajuta să-ți restabilești backup-ul. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Dacă ai vechiul dispozitiv, poți vizualiza codul de rezervă în Setări > Backup-uri. Apoi atinge Vezi codul de rezervă. Află mai multe @@ -8119,6 +8157,18 @@ Okay + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 1ca1529de0..975bb92169 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1051,6 +1051,20 @@ Попробуйте связать ещё раз Продолжить без переноса + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1076,13 +1090,13 @@ - Transfer message history + Перенести историю сообщений - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Не переводить + Не переносить - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Отвязать «%1$s»? @@ -1431,16 +1445,18 @@ Все ваши сообщения Восстановить из резервной копии - - Учитываются только медиафайлы, отправленные или полученные за последние %1$d дней. Резервное копирование включает: Восстановить резервную копию - + Последняя резервная копия была создана %1$s в %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Получение сведений о резервном копировании… + + Skip restore Уведомлять меня об упоминаниях @@ -2632,6 +2648,8 @@ Вы сделали слишком много попыток зарегистрировать этот номер. Пожалуйста, попробуйте ещё раз через %1$s. Не удается подключиться к сервису. Пожалуйста, проверьте подключение к сети и повторите попытку. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Мы не смогли отправить вам код проверки по SMS. Попробуйте получить код с помощью голосового вызова. @@ -4533,6 +4551,8 @@ Восстановить или перенести Перенести учётную запись Пропустить + + Skip restore Резервные копии чатов Перенести учётную запись Перенести учётную запись на новое Android-устройство @@ -6089,9 +6109,9 @@ Продолжается обработка Не удалось добавить значок - Something went wrong + Что-то пошло не так - Your backups subscription couldn\'t be displayed. Please contact support. + Не удалось отобразить вашу подписку на резервное копирование. Пожалуйста, свяжитесь с поддержкой. Не удалось проверить значок @@ -7530,7 +7550,7 @@ Не удалось сохранить изменения. Проверьте ваше подключение к интернету и попробуйте ещё раз. - Couldn\'t delete call link as it is currently in use. + Не удалось удалить ссылку на звонок, так как она используется в настоящее время. Удалить ссылку? @@ -7625,6 +7645,10 @@ Отклонить Принять + + Approve all + + Deny all Включить уведомления во весь экран? @@ -7803,7 +7827,13 @@ Пропустить восстановление? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Если вы пропустите восстановление, оставшиеся медиафайлы и вложения из резервной копии могут быть загружены позднее, когда освободится место для хранения. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7836,7 +7866,11 @@ Пропустить загрузку - "Не удалось завершить последнее резервное копирование. Убедитесь, что телефон подключён к сети Wi-Fi, и нажмите «Создать резервную копию сейчас», чтобы повторить попытку." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7880,7 +7914,7 @@ Неиспользуемые медиафайлы будут выгружены, но их можно будет загрузить из резервной копии в любое время. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Оптимизация хранилища доступна только с платным уровнем резервного копирования Signal. Ваша подписка на резервное копирование всё ещё обрабатывается и поэтому не активна. Пожалуйста, повторите попытку позднее. @@ -8044,6 +8078,8 @@ Продлить Узнать больше + + Processing backup… У вас есть %1$s резервных копий, которых нет на этом устройстве. Резервные копии будут удалены, когда ваша подписка закончится через %2$d день. @@ -8068,6 +8104,8 @@ Не удалось отключить и удалить резервные копии Произошла ошибка сети. Пожалуйста, проверьте подключение к интернету и повторите попытку. + + Uploading messages… @@ -8107,7 +8145,7 @@ Ваш резервный ключ - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Резервный ключ — это 64-значный код, который позволяет восстановить резервную копию при повторной установке Signal. Если вы забудете свой ключ, вы не сможете восстановить резервную копию. Signal не может помочь восстановить резервную копию. @@ -8200,7 +8238,7 @@ У меня нет старого телефона - Or you\'re reinstalling Signal on the same device + Либо вы повторно устанавливаете Signal на том же устройстве Восстановить или перенести учётную запись @@ -8229,15 +8267,15 @@ Введите резервный ключ - Your backup key is a 64-character code required to recover your account and data. + Резервный ключ представляет собой 64-значный код, необходимый для восстановления вашей учётной записи и данных. Нет резервного ключа? Резервный ключ - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Резервные копии не могут быть восстановлены без 64-значного кода восстановления. Если вы потеряли свой резервный ключ, Signal не сможет помочь восстановить резервную копию. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Если у вас есть старое устройство, вы можете просмотреть свой резервный ключ в Настройках > Резервное копирование. Затем нажмите «Просмотреть резервный ключ». Узнать больше @@ -8286,6 +8324,18 @@ Хорошо + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index fd16b3032a..9c8eb734b3 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -1051,6 +1051,20 @@ Skúsiť prepojiť znova Pokračovať bez prenosu + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1076,13 +1090,13 @@ - Transfer message history + Preniesť históriu správ - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Nepreviesť + Nepreniesť - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Odpojiť „%1$s“? @@ -1431,16 +1445,18 @@ Všetky vaše správy Obnoviť zo zálohy - - Zahrnuté sú iba médiá odoslané alebo prijaté za posledných %1$d dní. Vaša záloha zahŕňa: Obnoviť zálohu - + Vaša posledná záloha bola vytvorená %1$s o %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Načítavajú sa podrobnosti o zálohe… + + Skip restore Upozorni ma na Spomenutia @@ -2632,6 +2648,8 @@ Urobili ste príliš veľa pokusov o zaregistrovanie tohto čísla. Skúste to znova o %1$s. Nepodarilo sa pripojiť k službe. Prosím, overte Vaše sieťové pripojenie a skúste to znovu. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Nepodarilo sa nám poslať vám overovací kód prostredníctvom SMS. Skúste prijať kód prostredníctvom hlasového hovoru. @@ -4533,6 +4551,8 @@ Obnoviť alebo preniesť Preniesť účet Preskočiť + + Skip restore Zálohy četov Preniesť účet Preniesť účet do nového Android zariadenia @@ -6089,9 +6109,9 @@ Stále sa spracováva Odznak sa nepodarilo pridať - Something went wrong + Niekde sa stala chyba - Your backups subscription couldn\'t be displayed. Please contact support. + Vaše predplatné záloh sa nepodarilo zobraziť. Kontaktujte náš tím podpory. Nepodarilo sa overiť odznak @@ -7530,7 +7550,7 @@ Zmeny sa nepodarilo uložiť. Skontrolujte svoje pripojenie a skúste to znova. - Couldn\'t delete call link as it is currently in use. + Odkaz na hovor sa nepodarilo odstrániť, pretože sa momentálne používa. Vymazať odkaz? @@ -7625,6 +7645,10 @@ Odmietnuť Prijať + + Approve all + + Deny all Chcete zapnúť upozornenia na celú obrazovku? @@ -7803,7 +7827,13 @@ Preskočiť obnovenie? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Ak preskočíte obnovenie, zostávajúce médiá a prílohy vo vašej zálohe si môžete stiahnuť neskôr, keď bude k dispozícii úložný priestor. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7836,7 +7866,11 @@ Preskočiť sťahovanie - "Vašu poslednú zálohu sa nepodarilo dokončiť. Uistite sa, že váš telefón je pripojený k sieti Wi-Fi a skúste to znova ťuknutím na možnosť Zálohovať teraz." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7880,7 +7914,7 @@ Nepoužívané médiá budú dočasne vymazané, môžete si ich však kedykoľvek stiahnuť zo zálohy. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Optimalizáciu úložiska je možné využiť iba v prípade, ak používate platenú verziu Záloh Signal. Vaše predplatné záloh sa stále spracováva a ešte nie je aktívne. Skúste to znova neskôr. @@ -8044,6 +8078,8 @@ Obnoviť Zistiť viac + + Processing backup… Máte %1$s zálohovaných údajov, ktoré nie sú v tomto zariadení. Vaša záloha bude vymazaná, keď skončí platnosť vášho predplatného, to znamená o %2$d deň. @@ -8068,6 +8104,8 @@ Nepodarilo sa vypnúť a vymazať zálohy Vyskytla sa chyba siete. Skontrolujte internetové pripojenie a skúste to znova. + + Uploading messages… @@ -8107,7 +8145,7 @@ Váš záložný kľúč - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Váš záložný kľúč je 64-miestny kód, ktorý vám umožňuje obnoviť zálohu, keď znovu nainštalujete Signal. Ak zabudnete svoj kľúč, nebudete môcť obnoviť zálohu. Signal vám nemôže pomôcť obnoviť zálohu. @@ -8200,7 +8238,7 @@ Nemám svoj starý telefón - Or you\'re reinstalling Signal on the same device + Alebo znova inštalujete Signal na rovnakom zariadení Obnoviť alebo preniesť účet @@ -8229,15 +8267,15 @@ Zadajte záložný kľúč - Your backup key is a 64-character code required to recover your account and data. + Váš záložný kľúč je 64-miestny kód potrebný na obnovenie vášho účtu a údajov. Nemáte záložný kľúč? Záložný kľúč - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Zálohy nie je možné obnoviť bez ich 64-miestneho kódu na obnovenie. V prípade straty záložného kľúča vám Signal nemôže pomôcť obnoviť zálohu. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Ak máte svoje staré zariadenie, záložný kľúč si môžete pozrieť v časti Nastavenia > Zálohy Signal. Potom ťuknite na Zobraziť záložný kľúč. Zistiť viac @@ -8286,6 +8324,18 @@ OK + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 6cc361e9d9..2bc54cd06e 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -1051,6 +1051,20 @@ Poskusite znova povezati Nadaljuj brez prenosa + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1076,13 +1090,13 @@ - Transfer message history + Prenos zgodovine sporočil - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Ne prenesi - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Želite prekiniti povezavo z napravo \"%1$s\"? @@ -1431,16 +1445,18 @@ Vsa vaša sporočila Obnovi iz varnostne kopije - - Vključeni so samo mediji, poslani ali prejeti v zadnjih %1$d dneh. Vaša varnostna kopija vključuje: Obnovi iz varnostne kopije - + Vaša zadnja varnostna kopija je bila izdelana dne %1$s ob %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Pridobivanje podatkov o varnostni kopiji … + + Skip restore Obveščaj me o omembah @@ -2632,6 +2648,8 @@ Preveč neuspešnih poskusov registracije številke. Prosimo, poskusite znova čez %1$s. Povezava s storitvijo ni mogoča. Preverite omrežje in poskusite znova. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Nismo vam mogli poslati potrditvene kode prek SMS-a. Namesto tega poskusite prejeti kodo prek zvočnega klica. @@ -4533,6 +4551,8 @@ Obnovi ali prenesi Prenos računa Preskoči + + Skip restore Varnostno kopiranje klepetov Prenos računa Prenos računa na novo androidno napravo @@ -6089,9 +6109,9 @@ Obdelava še vedno poteka Nismo mogli dodati značke. - Something went wrong + Nekaj je šlo narobe - Your backups subscription couldn\'t be displayed. Please contact support. + Vaše naročnine na varnostno kopiranje ni bilo mogoče prikazati. Obrnite se na podporo uporabnikom. Značke ni bilo mogoče potrditi @@ -7530,7 +7550,7 @@ Sprememb ni bilo mogoče shraniti. Preverite omrežno povezavo in poskusite znova. - Couldn\'t delete call link as it is currently in use. + Klicne povezave ni bilo mogoče izbrisati, saj je trenutno v uporabi. Želite izbrisati povezavo? @@ -7625,6 +7645,10 @@ Zavrni Potrdi + + Approve all + + Deny all Želite vklopiti celozaslonska obvestila? @@ -7803,7 +7827,13 @@ Želite preskočiti obnovitev? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Če obnovitev preskočite, lahko preostale medije in priponke iz varnostne kopije prenesete pozneje, ko bo na voljo prostor za shranjevanje. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7836,7 +7866,11 @@ Preskoči prenos - "Zadnje varnostne kopije ni bilo mogoče dokončati. Prepričajte se, da je vaš telefon povezan na Wi-Fi, in tapnite Izdelaj varnostno kopijo, da poskusite znova." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7880,7 +7914,7 @@ Neuporabljeni mediji bodo preneseni, vendar jih lahko kadarkoli snamete iz varnostne kopije. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Optimizacijo shranjevanja je mogoče uporabljati samo s plačljivo ravnjo Varnostne kopije Signal. Vaša naročnina na varnostno kopiranje se še vedno obdeluje in še ni aktivna. Poskusite pozneje. @@ -8044,6 +8078,8 @@ Obnovi Preberite več + + Processing backup… Imate %1$s varnostne kopije, ki ni v tej napravi. Vaša varnostna kopija bo izbrisana, ko se vaša naročnina konča (čez %2$d dan). @@ -8068,6 +8104,8 @@ Ni bilo mogoče izklopiti in izbrisati varnostnih kopij Zgodila se je napaka v omrežju. Preverite internetno povezavo in poskusite znova. + + Uploading messages… @@ -8107,7 +8145,7 @@ Vaš varnostni ključ - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Ključ varnostne kopije je 64-mestna koda, s katero lahko obnovite varnostno kopijo, ko znova namestite program Signal. Če pozabite svoj ključ, ne boste mogli obnoviti varnostne kopije. Signal vam ne more pomagati obnoviti varnostne kopije. @@ -8200,7 +8238,7 @@ Nimam starega telefona - Or you\'re reinstalling Signal on the same device + Ali pa ponovno nameščate Signal v isto napravo Obnovitev ali prenos računa @@ -8229,15 +8267,15 @@ Vnesite varnostni ključ - Your backup key is a 64-character code required to recover your account and data. + Varnostni ključ je 64-mestna koda, ki je potrebna za obnovitev računa in podatkov. Nimate varnostnega ključa? Varnostni ključ - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Varnostnih kopij ni mogoče obnoviti brez njihove 64-mestne obnovitvene kode. Če ste izgubili varnostni ključ, vam Signal ne more pomagati obnoviti varnostne kopije. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Če še imate staro napravo, si lahko varnostni ključ ogledate v Nastavitve > Varnostne kopije. Nato tapnite Ogled varnostnega ključa. Preberite več @@ -8286,6 +8324,18 @@ Okej + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index cbd4b25045..cf48dddd78 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -1011,6 +1011,20 @@ Provo të lidhesh përsëri Vazhdo pa transferuar + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Transfero historikun e mesazheve - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Mos shpërngul + Mos transfero - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Të shkëputet \"%1$s\"? @@ -1355,16 +1369,18 @@ Të gjitha mesazhet Riktheni prej kopjeruajtje - - Përfshihet vetëm media e dërguar ose marrë në %1$d ditët e fundit. Kopjeruajtja përfshin: Riktheje kopjeruajtjen - + Kopjeruajtja jote e fundit është bërë më %1$s në %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Duke marrë detajet e kopjeruajtjes… + + Skip restore Njoftomë për Përmendje @@ -2464,6 +2480,8 @@ Ke bërë shumë përpjekje për të regjistruar këtë numër. Të lutem provoje përsëri për %1$s. S\\’arrihet të bëhet lidhja te shërbimi. Ju lutemi, kontrolloni lidhjen me rrjetin dhe riprovoni. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Nuk mundëm të të dërgonim një kod verifikimi me SMS. Në vend të kësaj, provo të marrësh kodin nëpërmjet thirrjes zanore. @@ -4311,6 +4329,8 @@ Rikthe ose transfero Shpërngulni llogari Anashkaloje + + Skip restore Kopjeruajtje bisedash Shpërngulni llogari Shpërngulni llogari te një pajisje Android e re @@ -5823,9 +5843,9 @@ Ende në përpunim Distinktivi nuk mundi të shtohej - Something went wrong + Diçka shkoi keq - Your backups subscription couldn\'t be displayed. Please contact support. + Abonimi i kopjeruajtjeve nuk mund të shfaqej. Të lutem, kontakto ndihmën. Vërtetimi i distinktivit dështoi @@ -7216,7 +7236,7 @@ Ndryshimet nuk mund të ruheshin. Kontrollo lidhjen e rrjetit dhe provo sërish. - Couldn\'t delete call link as it is currently in use. + Lidhja e thirrjes nuk mund të fshihej pasi është aktualisht në përdorim. Të fshihet lidhja? @@ -7307,6 +7327,10 @@ Mos e Prano Miratoje + + Approve all + + Deny all Të aktivizohen njoftimet me ekran të plotë? @@ -7481,7 +7505,13 @@ Të kapërcehet rikthimi? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Nëse kapërcen rikthimin, mediat dhe bashkëngjitjet e mbetura në kopjeruajtje mund të shkarkohen më vonë kur hapësira ruajtëse të jetë e disponueshme. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Kapërce shkarkimin - "Kopjeruajtja e fundit nuk mund të kryhej. Sigurohu që telefoni të jetë i lidhur me Wi-Fi dhe kliko \"Kopjeruaj tani\" për të provuar përsëri." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Media e papërdorur do të shkarkohet, por mund të shkarkohet nga kopjeruajtja në çdo kohë. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Optimizimi i hapësirës ruajtëse mund të përdoret vetëm me nivelin e paguar të kopjeruajtjeve të Signal. Abonimi i kopjeruajtjeve është ende në përpunim dhe nuk është ende aktiv. Të lutem, provo sërish më vonë. @@ -7718,6 +7752,8 @@ Rinovo Mëso më shumë + + Processing backup… Ti ke %1$s të dhëna kopjeruajtjeje që nuk janë në këtë pajisje. Kopjeruajtja do të fshihet kur abonimi të përfundojë për %2$d ditë. @@ -7738,6 +7774,8 @@ Kopjeruajtjet nuk mund të çaktivizohen dhe fshihen Ndodhi një gabim në rrjet. Të lutem, kontrollo lidhjen e internetit dhe riprovo. + + Uploading messages… @@ -7777,7 +7815,7 @@ Kodi yt i kopjeruajtjes - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Kodi i kopjeruajtjes është 64-shifror dhe të lejon ta rikthesh kopjeruajtjen kur riinstalon Signal. Nëse harron kodin, nuk do të jesh në gjendje të rikthesh kopjeruajtjen. Signal nuk mund të të ndihmojë të rikuperosh kopjeruajtjen. @@ -7866,7 +7904,7 @@ Nuk e kam telefonin e vjetër - Or you\'re reinstalling Signal on the same device + Ose je duke e riinstaluar Signal në të njëjtën pajisje Rikthe ose transfero llogarinë @@ -7895,15 +7933,15 @@ Vendos kodin e kopjeruajtjes - Your backup key is a 64-character code required to recover your account and data. + Kodi i kopjeruajtjes është kod 64-shifror që kërkohet për të rikthyer llogarinë dhe të dhënat e tua.<sub></sub> Nuk ke kod kopjeruajtjeje? Kodi i kopjeruajtjes - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Kopjeruajtjet nuk mund të rikthehen pa kodin e tyre të rikuperimit prej 64 karakteresh. Nëse e ke humbur kodin e kopjeruajtjes, Signal nuk mund të ndihmojë në rikthimin e kopjeruajtjes. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Nëse ke pajisjen e vjetër, mund ta shikosh kodin e kopjeruajtjes te Parametrat > Kopjeruajtjet. Më pas kliko \"Shiko kodin e kopjeruajtjes\". Mëso më shumë @@ -7952,6 +7990,18 @@ OK + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 6898f54936..16b269fb8a 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -1011,6 +1011,20 @@ Повежи поново Настави без преноса + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1038,11 +1052,11 @@ Пренеси историју порука - Пренесите текстуалне поруке и недавне медије на рачунар + Transfer your text messages and recent media to your linked device Немој да пренесеш - Старе поруке или медији неће бити пренесени на рачунар + No old messages or media will be transferred to your linked device Прекините везу са уређајем „%1$s“? @@ -1355,16 +1369,18 @@ све ваше поруке Враћање садржаја из резервне копије - - Укључени су само медији послати или примљени у последњих %1$d дана. Резервна копија обухвата: Врати резервну копију - + Последња резервна копија направљена је %1$s у %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Преузимамо детаље о резервној копији… + + Skip restore Обавести ме за помињања @@ -2464,6 +2480,8 @@ Превише пута сте покушали да региструјете овај број. Пробајте поново за %1$s. Повезивање са сервисом није успело. Проверите да ли сте повезани на интернет и пробајте поново. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Нисмо успели да вам пошаљемо шифру за верификацију. Можете да затражите шифру преко гласовног позива. @@ -4311,6 +4329,8 @@ Врати или пренеси Пренесите налог Прескочи + + Skip restore Резерве ћаскања Пренесите налог Пренесите налог на нови Android уређај @@ -7307,6 +7327,10 @@ Одбиј Одобри + + Approve all + + Deny all Желите ли да укључите обавештења преко целог екрана? @@ -7482,6 +7506,12 @@ Желите ли да прескочите враћање? Ако прескочите враћање преосталих медија и прилога у резервној копији, можете их преузети касније када меморијски простор постане доступан. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Прескочи преузимање - "Последње креирање резервне копије није успело. Проверите да ли вам је телефон повезан на Wi-Fi, а затим додирните „Направи резервну копију“ да пробате поново." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7718,6 +7752,8 @@ Обновите Сазнајте више + + Processing backup… Имате %1$s резервне копије података, који нису на овом уређају. Ваша резервна копија ће бити избрисана када се претплата заврши за %2$d дан. @@ -7738,6 +7774,8 @@ Искључивање и брисање резервне копије није успело Дошло је до грешке у мрежи. Проверите да ли сте повезани на интернет и пробајте поново. + + Uploading messages… @@ -7952,6 +7990,18 @@ У реду + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index b5b87dbb4f..60a74338cd 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -1011,6 +1011,20 @@ Försök att länka igen Fortsätt utan att överföra + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Överför meddelandehistorik - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Överför inte - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Sluta länka \"%1$s\"? @@ -1355,16 +1369,18 @@ Alla dina meddelanden Återställ från säkerhetskopia - - Endast media som skickats eller tagits emot under de senaste %1$d dagarna ingår. Din säkerhetskopia innehåller: Återställ säkerhetskopia - + Din senaste säkerhetskopia gjordes den %1$s kl %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Hämtar säkerhetskopieringsdetaljer … + + Skip restore Meddela mig om omnämnanden @@ -2464,6 +2480,8 @@ Du har gjort för många försök att registrera detta nummer. Försök igen om %1$s. Det går inte att ansluta till tjänsten. Kontrollera nätverksanslutning och försök igen. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Vi kunde inte skicka en verifieringskod via sms. Försök att ta emot din kod via röstsamtal istället. @@ -4311,6 +4329,8 @@ Återställ eller överför Överför konto Hoppa över + + Skip restore Säkerhetskopior av chattar Överför konto Överför konto till en ny Android-enhet @@ -5823,9 +5843,9 @@ Bearbetar fortfarande Det gick inte att lägga till märket - Something went wrong + Något gick fel - Your backups subscription couldn\'t be displayed. Please contact support. + Ditt abonnemang för säkerhetskopiering kunde inte visas. Kontakta supporten. Det gick inte att validera märke @@ -7216,7 +7236,7 @@ Det gick inte att spara ändringar. Kontrollera nätverksanslutningen och försök igen. - Couldn\'t delete call link as it is currently in use. + Det gick inte att ta bort samtalslänken eftersom den används för närvarande. Radera länk? @@ -7307,6 +7327,10 @@ Avvisa Godkänn + + Approve all + + Deny all Aktivera helskärmsaviseringar? @@ -7481,7 +7505,13 @@ Hoppa över återställning? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Om du hoppar över återställning kan återstående media och bilagor i din säkerhetskopia laddas ner vid ett senare tillfälle när lagringsutrymme blir tillgängligt. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Hoppa över nedladdning - "Din senaste säkerhetskopiering kunde inte slutföras. Se till att din telefon är ansluten till wifi och tryck på Säkerhetskopiera nu för att försöka igen." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Oanvända mediafiler kommer att avlastas, men kan laddas ner från din säkerhetskopia när som helst. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Lagringsoptimering kan endast användas med den betalda nivån av Säkerhetskopiering av Signal. Ditt abonnemang för säkerhetskopiering bearbetas fortfarande och är inte aktivt ännu. Försök igen senare. @@ -7718,6 +7752,8 @@ Förnya Läs mer + + Processing backup… Du har %1$s av säkerhetskopierade data som inte finns på den här enheten. Din säkerhetskopia kommer att tas bort när ditt abonnemang löper ut om %2$d dag. @@ -7738,6 +7774,8 @@ Det gick inte att stänga av och ta bort säkerhetskopior Ett nätverksfel har inträffat. Kontrollera din internetanslutning och försök igen. + + Uploading messages… @@ -7777,7 +7815,7 @@ Din säkerhetskopieringsnyckel - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Din säkerhetskopieringsnyckel är en kod på 64 tecken som låter dig återställa din säkerhetskopia när du installerar om Signal. Om du glömmer din nyckel kommer du inte att kunna återställa din säkerhetskopia. Signal kan inte hjälpa dig att återställa din säkerhetskopia. @@ -7866,7 +7904,7 @@ Jag har inte min gamla telefon - Or you\'re reinstalling Signal on the same device + Eller så installerar du om Signal på samma enhet Återställ eller överför konto @@ -7895,15 +7933,15 @@ Ange din säkerhetskopieringsnyckel - Your backup key is a 64-character code required to recover your account and data. + Din säkerhetskopieringsnyckel är en kod på 64 tecken som krävs för att återställa ditt konto och dina data. Ingen säkerhetskopieringsnyckel? Säkerhetskopieringsnyckel - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Säkerhetskopior kan inte återställas utan deras återställningskod på 64 tecken. Om du har tappat bort din säkerhetskopieringsnyckel kan Signal inte hjälpa till att återställa din säkerhetskopia. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Om du har din gamla enhet kan du visa din säkerhetskopieringsnyckel i Inställningar > Säkerhetskopior. Tryck sedan på Visa säkerhetskopieringsnyckel. Läs mer @@ -7952,6 +7990,18 @@ Okej + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index 53a3f5b4d5..13873776b1 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -1011,6 +1011,20 @@ Jaribu kuunganisha tena Endelea bila kuhamisha + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Hamisha historia ya jumbe - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Usihamishe - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Ungependa kutenganisha \"%1$s\"? @@ -1355,16 +1369,18 @@ Jumbe zako zote Rejesha kutoka nakala hifadhi - - Video na picha zilizotumwa au kupokewa ndani ya siku %1$d zilizopita ndio zitajumuishwa. Hifadhi nakala yako inajumuisha: rejesha upya nakalahifadhi - + Chelezo yako ya mwisho ilifanywa tarehe %1$s saa %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Inachukua maelezo ya hifadhi… + + Skip restore Niarifu Ninapotajwa @@ -2464,6 +2480,8 @@ Umefanya majaribio mengi ya kusajili nambari hii. Tafadhali jaribu tena baada ya %1$s. Imeshindwa kuunganisha kwenye huduma. Tafadhali angalia muunganisho wako wa mtandao na ujaribu tena. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Tumeshindwa kukutumia msimbo wa kuthibitisha kupitia SMS. Badala yake, jaribu kupokea msimbo wako wa kuthibitisha kupitia simu ya kawaida. @@ -4311,6 +4329,8 @@ Rejesha au hamisha Hamisha akaunti Ruka + + Skip restore Nakalahifadhi ya Gumzo Hamisha akaunti Hamishia akaunti kwenye kifaa kipya cha Android @@ -5823,9 +5843,9 @@ Bado inachakata Imeshindwa kuongeza beji - Something went wrong + Kuna hitilafu imetokea - Your backups subscription couldn\'t be displayed. Please contact support. + Usajili wako wa nakala ya uhifadhi ihaijaonyeshwa. Tafadhali wasiliana na msaada. Imeshindwa kuhalalisha beji @@ -7216,7 +7236,7 @@ Imeshindwa kuhifadhi mabadiliko. Angalia muunganiko wako wa mtandao na ujaribu tena. - Couldn\'t delete call link as it is currently in use. + Imeshindwa kufuta viungo vyote vya simu kwani vinatumika kwa sasa. Futa kiungo? @@ -7307,6 +7327,10 @@ Ghairi Idhinisha + + Approve all + + Deny all Washa arifa za skrini nzima? @@ -7481,7 +7505,13 @@ Ruka hatua ya kurejesha? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Ukiruka kurejesha video, picha na viambatisho vilivyosalia kwenye hifadhi nakala yako vinaweza kupakuliwa baadaye nafasi ya kuhifadhi itakapopatikana. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ Ruka upakuaji - "Uhifadhi nakala wako wa mwisho haukukamilika. Hakikisha kuwa simu yako imeunganishwa kwenye Wi-Fi na uguse Hifadhi nakala sasa ili ujaribu tena." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Picha na video ambazo hazijatumika zitahamishwa, lakini zinaweza kupakuliwa kutoka kwenye hifadhi nakala yako wakati wowote. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Uboreshaji wa nafasi ya hifadhi inaweza tu kutumiwa na walipaji wa huduma ya Hifadhi Nakala zaa Signal. Usajili wako wa nakala ya uhifadhi bado unachakata na haujaanza kutumika. Tafadhali jaribu tena baadae. @@ -7718,6 +7752,8 @@ Sasisha Jifunze zaidi + + Processing backup… Una %1$s ya data ya hifadhi ambayo haiko kwenye kifaa hiki. Hifadhi yako itafutwa usajili wako ukiisha ndani ya siku %2$d. @@ -7738,6 +7774,8 @@ Imeshinda kuzima na kufuta hifadhi nakala Hitilafu ya kimtandao imetokea. Tafadhali angalia muunganisho wako wa intaneti kisha ujaribu tena. + + Uploading messages… @@ -7777,7 +7815,7 @@ Funguo wako wa ziada - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Ufunguo mbadala wako ni code ya herufi 64 zinazokuwezesha kurejesha hifadhi nakala yako unaposakinisha tena Signal. Ukisahau ufunguo wako, hutaweza kurejesha nakala yako. Signal haiwezi kukusaidia kurejesha hifadhi nakala yako. @@ -7866,7 +7904,7 @@ Sina simu yangu ya zamani - Or you\'re reinstalling Signal on the same device + Au unasakinisha tena Signal kwenye kifaa kile kile Rejesha au hamisha akaunti @@ -7895,15 +7933,15 @@ Ingiza ufunguo mbadala wako - Your backup key is a 64-character code required to recover your account and data. + Ufunguo mbadala wako ni code ya herufi 64 inayohitajika kurejesha akaunti na data yako. Hakuna ufunguo mbadala? Ufunguo mbadala - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Hifadhi nakala haiwezi kurejeshwa bila code yake yenye herufi 64. Iwapo umepoteza ufunguo mbadala wako, Signal haiwezi kukusaidia kurejesha akaunti na data. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Ikiwa una kifaa chako cha zamani, unaweza kuangalia ufunguo mbadala wako kwenye Mipangilio > Hifadhi Nakala. Kisha gusa Tazama ufunguo mbadala. Jifunze zaidi @@ -7952,6 +7990,18 @@ Sawa + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 9da3bfa86e..12f2b84cf0 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -1011,6 +1011,20 @@ மீண்டும் இணைக்க முயற்சிக்கவும் இடமாற்றாமல் தொடர்க + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + செய்தி வரலாற்றை இடமாற்றுக - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - வேண்டாம் பரிமாற்றம் + இடமாற்றம் செய்ய வேண்டாம் - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\" ஐ இணைப்பு நீக்குவதா? @@ -1355,16 +1369,18 @@ உங்களின் அனைத்து செய்திகள் மீட்டமை இருந்து காப்புப்பிரதி - - கடந்த %1$d நாட்களில் நீங்கள் அனுப்பிய அல்லது பெற்ற மீடியா மட்டுமே சேர்க்கப்பட்டுள்ளது. உங்கள் காப்புப்பிரதியில் உள்ளடங்குவன: காப்புப்பதிவு பயனர் தரவு மீட்டமை - + உங்களின் கடைசி காப்புப் பிரதி %1$sஅன்று %2$s மணிக்கு செய்யப்பட்டது. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. காப்புப்பிரதி விவரங்களைப் பெறுகிறது… + + Skip restore என் பெயர் குறிப்புகளை எனக்கு அறிவி @@ -2464,6 +2480,8 @@ இந்த எண்ணை பதிவு செய்ய நீங்கள் பல முயற்சிகளை செய்துள்ளீர்கள். %1$s இல் மீண்டும் முயற்சிக்கவும். சேவையுடன் இணைக்க முடியவில்லை. பிணைய இணைப்பைச் சரிபார்த்து மீண்டும் முயற்சிக்கவும். + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. SMS மூலம் சரிபார்ப்புக் குறியீட்டை உங்களுக்கு அனுப்ப முடியவில்லை. அதற்குப் பதிலாக குரல் அழைப்பு மூலம் உங்கள் குறியீட்டைப் பெற முயலவும். @@ -4311,6 +4329,8 @@ மீட்டெடு அல்லது இடமாற்று Signal கணக்கை நகர்த்தவும் தவிர் + + Skip restore சாட் காப்புப்பிரதிகள் Signal கணக்கை நகர்த்தவும் கணக்கை புதிய சாதனத்திற்கு நகர்த்தவும் @@ -5823,9 +5843,9 @@ இன்னும் செயலாக்கப்படுகிறது பேட்ஜை சேர்க்க முடியவில்லை - Something went wrong + ஏதோ தவறு நடந்துவிட்டது - Your backups subscription couldn\'t be displayed. Please contact support. + உங்கள் காப்புப்பிரதியின் சந்தாவைக் காட்ட முடியவில்லை. ஆதரவு மையத்தைத் தொடர்பு கொள்க. பேட்ஜை சரிபார்ப்பது தோல்வியடைந்தது @@ -7216,7 +7236,7 @@ மாற்றங்களைச் சேமிக்க முடியவில்லை. உங்கள் நெட்வொர்க் இணைப்பைச் சரிபார்த்து, மீண்டும் முயலவும். - Couldn\'t delete call link as it is currently in use. + அழைப்பு இணைப்பு தற்போது பயன்பாட்டில் உள்ளதால் அதை அழிக்க முடியவில்லை. இணைப்பை நீக்க வேண்டுமா? @@ -7307,6 +7327,10 @@ நிராகரி ஒப்புதல் + + Approve all + + Deny all முழுத் திரை அறிவிப்புகளை இயக்க வேண்டுமா? @@ -7481,7 +7505,13 @@ மீட்டமைப்பதைத் தவிர்ப்பதா? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + மீட்டமைப்பதைத் தவிர்த்தால், உங்கள் காப்புப்பிரதியில் மீதமுள்ள ஊடகம் மற்றும் இணைப்புகள் சேமிப்பகத்தில் இடம் கிடைக்கும்போது பதிவிறக்கம் செய்யப்படும். + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ பதிவிறக்கத்தைத் தவிர் - "உங்களின் கடைசிக் காப்புப்பிரதி முழுமையடைவில்லை. உங்கள் ஃபோன் வைஃபை உடன் இணைக்கப்பட்டுள்ளத்தை உறுதி செய்து, மீண்டும் முயற்சி செய்ய இப்போதே காப்புப் பிரதியெடு என்பதை அழுத்தவும்." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ பயன்படுத்தப்படாத மீடியா ஆஃப்லோட் செய்யப்படும், ஆனால் அவற்றை எப்போது வேண்டுமானாலும் உங்கள் காப்புப்பிரதியிலிருந்து பதிவிறக்கம் செய்யலாம். - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + சிக்னல் காப்புப்பிரதிகளின் கட்டண அடுக்குடன் மட்டுமே சேமிப்பக உகந்ததாக்கலைப் பயன்படுத்த முடியும். உங்கள் காப்புப்பிரதி சந்தா இன்னும் செயலாக்கத்தில் உள்ளது ஆனால் இன்னும் செயல்படத் தொடங்கவில்லை. பின்னர் மீண்டும் முயலவும். @@ -7718,6 +7752,8 @@ புதுப்பி மேலும் அறிக + + Processing backup… இந்தச் சாதனத்தில் இல்லாத %1$s காப்புப் பிரதி தரவு உங்களிடம் உள்ளது. உங்கள் சந்தா %2$d நாளில் முடிவடையும் போது உங்கள் காப்புப் பிரதி அழிக்கப்படும். @@ -7738,6 +7774,8 @@ காப்புப்பிரதிகளை ஆஃப் செய்ய மற்றும் அழிக்க முடியவில்லை நெட்வொர்க் பிழை ஏற்பட்டது. உங்கள் இணைய இணைப்பைச் சரிபார்த்து மீண்டும் முயற்சிக்கவும். + + Uploading messages… @@ -7777,7 +7815,7 @@ உங்கள் காப்புப்பிரதி குறியீடு - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + உங்கள் காப்புப்பிரதி குறியீடானது 64 இலக்கக் குறியீடாகும், இது சிக்னலை நீங்கள் மீண்டும் நிறுவும்போது உங்கள் காப்புப்பிரதியை மீட்டமைக்க உதவுகிறது. உங்கள் குறியீட்டை மறந்துவிட்டால், உங்கள் காப்புப்பிரதியை மீட்டெடுக்க முடியாது. உங்கள் காப்புப்பிரதியை மீட்டெடுக்க சிக்னல் உங்களுக்கு உதவாது. @@ -7866,7 +7904,7 @@ என்னிடம் என் பழைய ஃபோன் இல்லை - Or you\'re reinstalling Signal on the same device + அல்லது அதே சாதனத்தில் நீங்கள் சிக்னலை மீண்டும் நிறுவுகிறீர்கள் கணக்கை மீட்டெடுக்கவும் அல்லது இடமாற்றவும் @@ -7895,15 +7933,15 @@ உங்கக் காப்புப்பிரதி குறியீட்டை உள்ளிடவும் - Your backup key is a 64-character code required to recover your account and data. + உங்கள் காப்புப்பிரதி குறியீடு என்பது உங்கள் கணக்கையும் தரவையும் மீட்டெடுக்க உதவும் 64 இலக்கக் குறியீடாகும். காப்புப்பிரதி குறியீடு இல்லையா? காப்புப்பிரதி குறியீடு - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + 64-இலக்கம் கொண்ட மீட்புக் குறியீடு இல்லாமல் காப்புப் பிரதிகளை மீட்டெடுக்க முடியாது. உங்கள் காப்புப்பிரதி குறியீட்டை நீங்கள் இழந்திருந்தால், உங்கள் காப்புப் பிரதியை மீட்டெடுக்க சிக்னலால் உதவ இயலாது. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + உங்களிடம் பழைய சாதனம் இருந்தால், அமைப்புகள் > காப்புப்பிரதிகள் என்பதில் இருக்கும் உங்கள் காப்புப்பிரதி குறியீட்டைப் பார்க்க முடியும். பின்னர் காப்புப்பிரதி குறியீட்டைக் காண்க என்பதைத் தட்டுங்கள். மேலும் அறிக @@ -7952,6 +7990,18 @@ சரி + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index 902ca658d5..1e327a9f15 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -1011,6 +1011,20 @@ మళ్ళీ లింక్ చేయడానికి ప్రయత్నించండి బదిలీ చేయకుండానే కొనసాగించండి + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + సందేశ చరిత్రను బదిలీ చేయండి - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device బదిలీ చేయవద్దు - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\"ని అన్‌లింక్ చేసేదా? @@ -1355,16 +1369,18 @@ మీ సందేశాలు అన్ని బ్యాకప్ నుంచి పునరుద్ధరించండి - - గత %1$d రోజుల్లో పంపిన లేదా అందుకున్న మీడియా మాత్రమే చేర్చబడింది. మీ బ్యాకప్‌లలో ఇవి ఉంటాయి: ప్రత్యామ్నాయ పునరుద్ధరించండి - + మీ చివరి బ్యాకప్ %1$s న %2$s కు చేయబడింది. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. బ్యాకప్ వివరాలను పొందుతోంది… + + Skip restore నాకు ప్రస్తావనల తెలియజేయండి @@ -2464,6 +2480,8 @@ ఈ నంబర్‌ను నమోదు చేయడానికి మీరు చాలా ఎక్కువ ప్రయత్నాలు చేశారు. దయచేసి %1$sలో మళ్ళీ ప్రయత్నించండి. కనెక్ట్ చేయడం సాధ్యం కాలేదు. దయచేసి నెట్వర్క్ కనెక్షన్ను తనిఖీ చేసి మళ్ళీ ప్రయత్నించండి. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. SMS ద్వారా ధృవీకరణ కోడ్‌ను మేము పంపలేకపోయాము. బదులుగా స్వర కాల్ ద్వారా మీ కోడ్‌ను అందుకోవడానికి ప్రయత్నించండి. @@ -4311,6 +4329,8 @@ పునరుద్ధరించండి లేదా బదిలీ చేయండి ఖాతాను బదిలీ చేయడం వదిలివేయి + + Skip restore చాట్ బ్యాకప్‌లు ఖాతాను బదిలీ చేయడం ఖాతాను కొత్త Android పరికరానికి బదిలీ చేయండి @@ -5823,9 +5843,9 @@ ఇంకా ప్రాసెసింగ్ చేస్తోంది బ్యాడ్జిని జోడించలేక పోయాను - Something went wrong + ఏదో తప్పు జరిగింది - Your backups subscription couldn\'t be displayed. Please contact support. + మీ బ్యాకప్‌ల సబ్స్క్రిప్షన్ ప్రదర్శించబడలేదు. దయచేసి మద్దతును సంప్రదించండి. బ్యాడ్జిని చెల్లుబాటు చేయడంలో విఫలమైంది @@ -7216,7 +7236,7 @@ మార్పులను భద్రపరచలేకపోయాము. మీ నెట్‌వర్క్ కనెక్షన్ తనిఖీ చేయండి మరియు మళ్ళీ ప్రయత్నించండి. - Couldn\'t delete call link as it is currently in use. + కాల్ లింక్ ప్రస్తుతం వాడుకలో ఉన్నందున దాన్ని తొలగించడం సాధ్యపడలేదు. లింక్‌ను తొలగించేదా? @@ -7307,6 +7327,10 @@ తిరస్కరించు ఆమోదించడానికి + + Approve all + + Deny all పూర్తి స్క్రీన్ నోటిఫికేషన్లను ఆన్ చేసేదా? @@ -7481,7 +7505,13 @@ పునరుద్ధరించడాన్ని దాటవేసేదా? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + ఒకవేళ మీరు పునరుద్ధరించడాన్ని దాటవేస్తే, మీ బ్యాకప్‌లోని మిగిలిన మీడియా మరియు జోడింపులను నిల్వ స్థలం అందుబాటులోకి వచ్చిన తర్వాత డౌన్లోడ్ చేసుకోవచ్చు. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ డౌన్‌లోడ్‌ను దాటవేయండి - "మీ చివరి బ్యాకప్‌ను పూర్తిచేయడం సాధ్యం కాలేదు. మీ ఫోన్ Wi-Fi కు కనెక్ట్ చేయబడిందని నిర్ధారించుకొని మళ్ళీ ప్రయత్నించడానికి ఇప్పుడే బ్యాకప్ చేయండిని తట్టండి." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ ఉపయోగించని మీడియా ఆఫ్‌లోడ్ చేయబడుతుంది, అయితే మీ బ్యాకప్ నుండి ఎప్పుడైనా దానిని డౌన్‌లోడ్ చేసుకోవచ్చు. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + నిల్వ అనుకూలీకరణ Signal బ్యాకప్ల చెల్లింపు శ్రేణితో మాత్రమే ఉపయోగించడానికి సాధ్యం అవుతుంది. మీ బ్యాకప్‌ల సబ్స్క్రిప్షన్ ఇప్పటికీ ప్రాసెస్ చేయబడుతోంది మరియు ఇంకా సక్రియంగా లేదు. దయచేసి తర్వాత మళ్లీ ప్రయత్నించండి. @@ -7718,6 +7752,8 @@ పునరుద్ధరించండి మరింత తెలుసుకోండి + + Processing backup… మీ వద్ద ఈ పరికరంలో లేని %1$s బ్యాకప్ డేటా ఉంది. %2$d రోజులో మీ సబ్స్క్రిప్షన్ ముగిసినప్పుడు మీ బ్యాకప్ తొలగించబడుతుంది. @@ -7738,6 +7774,8 @@ బ్యాకప్‌లను ఆఫ్ చేయడం మరియు తొలగించడం సాధ్యపడలేదు నెట్‌వర్క్ లోపం ఏర్పడింది. దయచేసి మీ ఇంటర్నెట్ కనెక్షన్‌ను తనిఖీ చేసుకొని మళ్ళీ ప్రయత్నించండి. + + Uploading messages… @@ -7777,7 +7815,7 @@ మీ బ్యాకప్ కీ - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + మీ బ్యాకప్ కీ అనేది 64-అంకెల కోడ్, ఇది మీరు Signal ను మళ్ళీ ఇన్‌స్టాల్ చేసినప్పుడు మీ బ్యాకప్‌ను పునరుద్ధరించడానికి మిమ్మల్ని అనుమతిస్తుంది. ఒకవేళ మీరు మీ కీని మర్చిపోతే, మీరు మీ బ్యాకప్‌ను పునరుద్ధరించలేరు. మీ బ్యాకప్‌ను తిరిగి పొందడంలో Signal మీకు సహాయం చేయలేదు. @@ -7866,7 +7904,7 @@ నా దగ్గర నా పాత ఫోన్ లేదు - Or you\'re reinstalling Signal on the same device + లేదా మీరు అదే పరికరంలో Signal ను మళ్ళీ ఇన్‌స్టాల్ చేస్తున్నారు ఖాతాను పునరుద్ధరించండి లేదా బదిలీ చేయండి @@ -7895,15 +7933,15 @@ మీ బ్యాకప్ కీని ఎంటర్ చేయండి - Your backup key is a 64-character code required to recover your account and data. + మీ బ్యాకప్ కీ అనేది 64-అంకెల కోడ్, ఇది మీ ఖాతా మరియు డేటాను పునరుద్ధరించడానికి అవసరం. బ్యాకప్ కీ లేదా? బ్యాకప్ కీ - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + బ్యాకప్‌లను వాటి 64-అంకెల పునరుద్ధరణ కోడ్ లేకుండా తిరిగి పొందడం సాధ్యం కాదు. ఒకవేళ మీరు మీ బ్యాకప్ కీని పోగొట్టుకున్నట్లయితే, మీ బ్యాకప్‌ను పునరుద్ధరించడంలో Signal సహాయం చేయలేదు. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + ఒకవేళ మీరు మీ పాత పరికరాన్ని కలిగి ఉంటే, మీరు మీ బ్యాకప్ కీని సెట్టింగ్‌లు > బ్యాకప్‌లలో వీక్షించవచ్చు. ఆపై బ్యాకప్ కీని వీక్షించండిని తట్టండి. మరింత తెలుసుకోండి @@ -7952,6 +7990,18 @@ సరే + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 7300d8c014..ad26391101 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -991,6 +991,20 @@ ลองเชื่อมโยงอีกครั้ง ดำเนินการต่อโดยไม่ถ่ายโอน + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1016,13 +1030,13 @@ - Transfer message history + ถ่ายโอนประวัติการส่งข้อความ - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - ไม่โอน + ไม่ถ่ายโอน - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device เลิกเชื่อมโยง \"%1$s\" หรือไม่ @@ -1317,16 +1331,18 @@ ข้อความทั้งหมด กู้คืนจากข้อมูลสำรอง - - โดยข้อมูลสำรองจะรวมไฟล์สื่อที่ส่งหรือได้รับภายใน %1$d วันที่ผ่านมาเท่านั้น ข้อมูลสำรองของคุณประกอบด้วย: กู้คืนจากข้อมูลสำรอง - + คุณสำรองข้อมูลครั้งล่าสุดเมื่อวันที่ %1$s เวลา %2$s + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. กำลังเรียกดูข้อมูลสำรอง… + + Skip restore แจ้งให้ฉันรู้สำหรับการกล่าวถึง @@ -2380,6 +2396,8 @@ คุณพยายามลงทะเบียนด้วยหมายเลขนี้หลายครั้งเกินไป โปรดลองอีกครั้งใน %1$s ไม่สามารถเชื่อมต่อบริการได้ โปรดตรวจสอบการเชื่อมต่อเครือข่ายและลองอีกครั้ง + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. เราไม่สามารถส่งรหัสยืนยันผ่านทาง SMS ให้คุณได้ ลองเปลี่ยนไปรับรหัสผ่านทางการโทรแทน @@ -4200,6 +4218,8 @@ กู้คืนหรือถ่ายโอน ถ่ายโอนบัญชี ข้าม + + Skip restore ข้อมูลสำรองของแชท ถ่ายโอนบัญชี ถ่ายโอนบัญชีไปยังอุปกรณ์ Android เครื่องใหม่ @@ -5690,9 +5710,9 @@ ยังดำเนินการอยู่ เพิ่มโล่ไม่ได้ - Something went wrong + เกิดข้อผิดพลาดบางอย่าง - Your backups subscription couldn\'t be displayed. Please contact support. + ไม่สามารถแสดงแพ็กเกจสำรองข้อมูลของคุณ กรุณาติดต่อฝ่ายสนับสนุน การตรวจยืนยันโล่ล้มเหลว @@ -7059,7 +7079,7 @@ ไม่สามารถบันทึกการเปลี่ยนแปลงได้ โปรดตรวจสอบการเชื่อมต่อเครือข่ายแล้วลองอีกครั้ง - Couldn\'t delete call link as it is currently in use. + ไม่สามารถลบลิงก์การโทรได้เนื่องจากมีการใช้งานลิงก์อยู่ในขณะนี้ ลบลิงก์ใช่หรือไม่ @@ -7148,6 +7168,10 @@ ปฏิเสธ อนุมัติ + + Approve all + + Deny all เปิดการแจ้งเตือนแบบเต็มหน้าจอหรือไม่ @@ -7320,7 +7344,13 @@ ข้ามการกู้คืนใช่หรือไม่ - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + หากข้ามการกู้คืน คุณจะสามารถดาวน์โหลดไฟล์สื่อและไฟล์แนบที่หลงเหลืออยู่ในข้อมูลสำรองได้ในภายหลังเมื่ออุปกรณ์มีพื้นที่จัดเก็บเพียงพอ + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7353,7 +7383,11 @@ ข้ามการดาวน์โหลด - "การสำรองข้อมูลครั้งล่าสุดของคุณไม่เสร็จสมบูรณ์ โปรดตรวจสอบว่าโทรศัพท์เชื่อมต่อกับ Wi-Fi แล้วแตะสำรองข้อมูลเดี๋ยวนี้เพื่อลองอีกครั้ง" + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7397,7 +7431,7 @@ ไฟล์สื่อที่ไม่ได้ใช้งานจะถูกออฟโหลด แต่คุณสามารถดาวน์โหลดไฟล์กลับมาจากข้อมูลสำรองได้ทุกเมื่อ - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + ฟีเจอร์การเพิ่มประสิทธิภาพพื้นที่จัดเก็บจะสามารถใช้งานได้ในกรณีที่มีการชำระค่าแพ็กเกจสำรองข้อมูลของ Signal การสมัครแพ็กเกจสำรองข้อมูลของคุณยังอยู่ระหว่างดำเนินการและยังไม่ได้เปิดใช้งาน โปรดลองอีกครั้งในภายหลัง @@ -7555,6 +7589,8 @@ ต่ออายุ เรียนรู้เพิ่มเติม + + Processing backup… คุณมีข้อมูลสำรอง %1$s ที่ยังไม่ได้บันทึกไว้บนอุปกรณ์เครื่องนี้ โดยข้อมูลสำรองของคุณจะถูกลบเมื่อแพ็กเกจหมดอายุในอีก %2$d วัน @@ -7573,6 +7609,8 @@ ไม่สามารถปิดใช้งานและลบข้อมูลสำรอง เกิดข้อผิดพลาดด้านเครือข่าย ตรวจสอบการเชื่อมต่ออินเทอร์เน็ตแล้วลองอีกครั้ง + + Uploading messages… @@ -7612,7 +7650,7 @@ กุญแจสำรองของคุณ - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + กุญแจสำรองคือรหัส 64 ตัวที่จะทำให้คุณสามารถกู้คืนข้อมูลสำรองในกรณีที่ติดตั้ง Signal ใหม่อีกครั้ง หากลืมกุญแจของตัวเอง คุณจะไม่สามารถกู้คืนข้อมูลสำรอง และ Signal จะไม่สามารถช่วยกู้ข้อมูลสำรองให้คุณได้ @@ -7699,7 +7737,7 @@ ฉันไม่มีโทรศัพท์เครื่องเก่า - Or you\'re reinstalling Signal on the same device + หรือติดตั้ง Signal ใหม่บนอุปกรณ์เครื่องเดียวกันนั้น กู้คืนหรือถ่ายโอนบัญชี @@ -7728,15 +7766,15 @@ ใส่กุญแจสำรองของคุณ - Your backup key is a 64-character code required to recover your account and data. + กุญแจสำรองคือรหัส 64 ตัวที่คุณต้องใช้ในการกู้คืนบัญชีและข้อมูล ไม่มีกุญแจสำรองใช่หรือไม่ กุญแจสำรอง - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + ไม่สามารถกู้คืนข้อมูลสำรองในกรณีที่ไม่มีรหัสกู้คืน 64 ตัว Signal จะไม่สามารถช่วยกู้คืนข้อมูลสำรองของคุณหากคุณทำกุญแจสำรองหาย - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + โดยหากคุณยังมีอุปกรณ์เครื่องเก่า สามารถเปิดดูกุญแจสำรองได้ในการตั้งค่า > ข้อมูลสำรอง จากนั้นแตะที่ดูกุญแจสำรอง เรียนรู้เพิ่มเติม @@ -7785,6 +7823,18 @@ ตกลง + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 955a6fddc3..601d30ef54 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -1011,6 +1011,20 @@ Subukang i-link ulit Magpatuloy nang hindi nililipat + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Ilipat ang message history - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - \'Wag i-transfer + \'Wag ilipat - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Gusto mo bang i-unlink ang \"%1$s\"? @@ -1355,16 +1369,18 @@ Lahat ng iyong messages Mag-restore mula sa backup - - Ang media na ipinadala o natanggap sa nakaraang %1$d araw lang ang kasama dito. Kasama sa backup mo ang: Mag-restore ng backup - + Huli kang nag-backup noong %1$s nang %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Kinukuha ang backup details… + + Skip restore I-notify ako sa Mentions @@ -2464,6 +2480,8 @@ Masyadong maraming beses mo nang sinubukang i-register ang number na ito. Subukan ulit pagkatapos ng %1$s. Hindi makakonekta sa serbisyo. Pakisuri ang koneksyon ng network at subukang muli. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Hindi kami makapag-send sa \'yo ng verification code via SMS. Subukang kuhanin ang iyong code via voice call. @@ -4311,6 +4329,8 @@ I-restore o ilipat Transfer account Laktawan + + Skip restore Chat backups Transfer account I-transfer ang account sa bagong Android device @@ -5823,9 +5843,9 @@ Still processing Hindi ma-add ang badge - Something went wrong + Nagkaroon ng problema - Your backups subscription couldn\'t be displayed. Please contact support. + Hindi maipakita ang backups subscription mo. Paki contact ang support. Hindi na-validate ang badge @@ -7216,7 +7236,7 @@ Hindi ma-save ang changes. I-check ang iyong internet connection at subukan ulit. - Couldn\'t delete call link as it is currently in use. + Hindi mabura ang call link dahil kasalukuyan itong ginagamit. Gusto mo bang burahin ang link? @@ -7307,6 +7327,10 @@ Tanggihan Approve + + Approve all + + Deny all Gusto mo bang i-on ang full screen notifications? @@ -7481,7 +7505,13 @@ I-skip ang pag-restore? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Kapag pinili mong i-skip ang pag-restore, ang natitirang media at attachments sa backup mo ay maaaring i-download sa susunod kapag may available nang storage space. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ I-skip ang download - "Hindi makumpleto ang huling backup mo. Siguraduhing connected sa Wi-Fi ang phone mo at i-tap ang Back up now para subukan ulit." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Mawawala ang unused media, ngunit maaari itong ma-download anumang oras mula sa backup mo. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Magagamit lamang ang storage optimization sa paid tier ng Signal Backups. Pinoproseso pa ang backups subscription mo at hindi pa active. Subukan ulit mamaya. @@ -7718,6 +7752,8 @@ Mag-renew Matuto pa + + Processing backup… Mayroon kang %1$s ng backup data na wala sa device na ito. Mabubura ang backup mo kapag natapos na ang subscription mo pagkatapos ng %2$d araw. @@ -7738,6 +7774,8 @@ Hindi ma-off at mabura ang backups Nagkaroon ng network error. I-check ang internet connection mo at subukan ulit. + + Uploading messages… @@ -7777,7 +7815,7 @@ Ang backup key mo - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Ang backup key mo ay isang 64-digit code na magagamit mo sa pag-restore ng iyong backup kapag nag-install ka ulit ng Signal. Kapag nakalimutan mo ang iyong PIN, hindi mo maaaring ma-restore ang backup mo. Hindi ka matutulungan ng Signal sa pag-recover ng backup mo. @@ -7866,7 +7904,7 @@ Wala sa akin ang lumang phone ko - Or you\'re reinstalling Signal on the same device + O nag-iinstall ka ulit ng Signal sa parehong device I-restore o ilipat ang account @@ -7895,15 +7933,15 @@ Ilagay ang backup key mo - Your backup key is a 64-character code required to recover your account and data. + Ang backup key mo ay isang 64-digit code na kinakailangan para ma-recover ang iyong account at data. Wala kang backup key? Backup key - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Hindi mare-recover ang backups kapag wala ang kanilang 64-digit recovery code. Kapag nawala mo ang iyong backup key, hindi ka matutulungan ng Signal na i-restore ang backup mo. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Kapag nasa \'yo ang lumang device mo, pwede mong tignan ang iyong backup key sa Settings > Backups. Pagkatapos, i-tap ang View backup key. Matuto pa @@ -7952,6 +7990,18 @@ Okay + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 9312c3e230..19e5aaa79c 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -1011,6 +1011,20 @@ Tekrar bağlanmayı dene Aktarmadan devam et + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + Mesaj geçmişini aktar - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device Aktarma - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\" bağlantısı kaldırılsın mı? @@ -1355,16 +1369,18 @@ Mesajlarının tamamı Yedekten geri yükle - - Yalnızca son %1$d günde gönderilen veya alınan medya dahildir. Yedeklemen şunlardan oluşur: Yedeği geri yükle - + Son yedeklemen %1$s tarihinde %2$s saatinde yapılmıştır. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Yedekleme ayrıntıları getiriliyor… + + Skip restore Bahsedilmeleri bana bildir @@ -2464,6 +2480,8 @@ Bu numarayı kaydetmek için çok fazla girişimde bulundun. Lütfen %1$s içinde tekrar dene. Hizmete bağlanılamadı. Lütfen ağ bağlantınızı kontrol edip tekrar deneyin. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Sana SMS yoluyla bir doğrulama kodu gönderemedik. Bunun yerine kodunu sesli aramayla almayı dene. @@ -4311,6 +4329,8 @@ Geri yükle veya aktar Hesabı aktar Atla + + Skip restore Sohbet yedekleri Hesabı aktar Hesabı yeni bir Android cihaza aktar @@ -5823,9 +5843,9 @@ İşlem sürüyor Rozet eklenemedi - Something went wrong + Bir şeyler ters gitti - Your backups subscription couldn\'t be displayed. Please contact support. + Yedekleme aboneliğin görüntülenemedi. Lütfen destek ekibiyle iletişime geç. Rozet doğrulanamadı @@ -7216,7 +7236,7 @@ Değişiklikler kaydedilemedi. Ağ bağlantını kontrol edip tekrar dene. - Couldn\'t delete call link as it is currently in use. + Şu anda kullanımda olduğu için arama bağlantısı silinemedi. Bağlantı silinsin mi? @@ -7307,6 +7327,10 @@ Reddet Onayla + + Approve all + + Deny all Tam ekran bildirimleri açılsın mı? @@ -7481,7 +7505,13 @@ Geri yükleme atlansın mı? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Geri yüklemeyi atlarsan yedeklemende kalan medya ve eklentiler daha sonra depolama alanı kullanılabilir hale geldiğinde indirilebilir. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ İndirmeyi atla - "Son yedeklemen tamamlanamadı. Telefonunun Wi-Fi\'ye bağlı olduğundan emin ol ve tekrar denemek için Şimdi yedekle\'ye dokun." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ Kullanılmayan medya boşaltılır ancak istediğin zaman yedeklemenden indirilebilir. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Depolama optimizasyonu yalnızca Signal Yedeklemelerin ücretli katmanı ile kullanılabilir. Yedekleme aboneliğin hala işleniyor ve henüz etkin değil. Lütfen daha sonra tekrar dene. @@ -7718,6 +7752,8 @@ Yenile Daha fazlasını öğren + + Processing backup… Bu cihazda olmayan %1$s yedekleme verin var. Aboneliğin %2$d gün içinde sona erdiğinde, yedeklemen silinir. @@ -7738,6 +7774,8 @@ Yedeklemeler kapatılamadı ve silinemedi Bir ağ hatası oluştu. Lütfen internet bağlantını kontrol et ve tekrar dene. + + Uploading messages… @@ -7777,7 +7815,7 @@ Yedekleme anahtarın - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Yedekleme anahtarın, Signal\'i yeniden kurduğunda yedeklemeni geri yüklemeni sağlayan 64 karakterli bir koddur. Anahtarını unutursan yedeklemeni geri yükleyemezsin. Signal, yedeklemeni kurtarmana yardımcı olamaz. @@ -7866,7 +7904,7 @@ Eski telefonum yanımda değil - Or you\'re reinstalling Signal on the same device + Ya da Signal\'i aynı cihaza yeniden yüklüyorsun Hesabı geri yükle veya aktar @@ -7895,15 +7933,15 @@ Yedekleme anahtarını gir - Your backup key is a 64-character code required to recover your account and data. + Yedekleme anahtarın, hesabını ve verilerini kurtarmak için gereken 64 karakterli bir koddur. Yedekleme anahtarın yok mu? Yedekleme anahtarı - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Yedekleme anahtarları, 64 karakterli kurtarma kodu olmadan kurtarılamaz. Yedekleme anahtarını kaybettiysen Signal yedeklemeni geri yüklemene yardımcı olamaz. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Eski cihazın varsa yedekleme anahtarını Ayarlar > Yedeklemeler bölümünden görüntüleyebilirsin. Ardından Yedekleme anahtarını görüntüle tuşuna dokun. Daha fazlasını öğren @@ -7952,6 +7990,18 @@ Tamam + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index c3f7556801..01e6af1668 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -991,6 +991,20 @@ قايتا ئۇلاشنى سىناڭ يۆتكىمەي تۇرۇپلا داۋاملاشتۇرۇڭ + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1018,11 +1032,11 @@ ئۇچۇر تارىخىنى يۆتكەش - تېكىست ئۇچۇرلىرىڭىزنى ۋە يېقىنقى مېدىيانى ئۈستەل يۈزىڭىزگە يۆتكەڭ + Transfer your text messages and recent media to your linked device يۆتكىمەڭ - ھېچقانداق كونا ئۇچۇرلار ياكى مېدىيا ئۈستەل يۈزىگە يۆتكەلمەيدۇ + No old messages or media will be transferred to your linked device «%1$s» نى ئاجرىتامسىز؟ @@ -1317,16 +1331,18 @@ بارلىق ئۇچۇرلىرىڭىز زاپاستىن ئەسلىگە كەلتۈرىدۇ - - پەقەت ئالدىنقى %1$d كۈنلەردە ئەۋەتىلگەن ياكى تاپشۇرۇۋېلىنغان مېدىيالار بار. زاپاسلىغان مەلۇماتلار تۆۋەندىكىلەرنى ئۆز ئىچىگە ئالىدۇ: زاپاسنى ئەسلىگە كەلتۈرۈش - + ئاخىرقى قېتىملىق زاپاسلاش %1$s %2$s دە ئېلىپ بېرىلغان. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. زاپاسلاش تەپسىلاتلىرىنى قوبۇللاش… + + Skip restore ئاتالسام ئەسكەرتسۇن @@ -2380,6 +2396,8 @@ سىز بۇ نومۇرنى تىزىملىتىشنى بەك كۆپ قېتىم سىنىدىڭىز. «%1$s» تىن كېيىن قايتا سىناڭ. مۇلازىمەتكە ئۇلانغىلى بولمىدى. تور ئۇلىنىشىنى تەكشۈرۈپ قايتا سىناڭ. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. سىزگە SMS ئارقىلىق دەلىللەش كودىنى يوللىيالمىدۇق. دەلىللەش كودىنى ئاۋازلىق چاقىرىق ئارقىلىق تاپشۇرۇۋېلىڭ. @@ -4200,6 +4218,8 @@ ئەسلىگە كەلتۈرۈش ياكى يۆتكەش ھېساباتنى يۆتكە ئاتلا + + Skip restore پاراڭ زاپاسلانمىسى ھېساباتنى يۆتكە يېڭى بىر Android ئۈسكىنىسگە ھېساباتنى يۆتكە @@ -7148,6 +7168,10 @@ رەت قىل تەستىقلا + + Approve all + + Deny all پۈتۈن ئېكران ئۇقتۇرۇشىنى ئاچامسىز؟ @@ -7321,6 +7345,12 @@ ئەسلىگە كەلتۈرۈشتى ئاتلامسىز؟ ئەگەر زاپاس مېدىيا ۋە قوشۇمچە ھۆججەتلەرنى ئەسلىگە كەلتۈرۈشتىن ئاتلاپ ئۆتۈپ كەتسىڭىز ، كىيىن ساقلاش بوشلۇقى چىققاندا چۈشۈرگىلى بولىدۇ. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7353,7 +7383,11 @@ چۈشۈرۈشتىن ۋاز كېچىش - "ئاخىرقى زاپاسلىشىڭىزنى تاماملىيالمىدى. تېلېفونىڭىزنىڭ Wi-Fi غا ئۇلانغانلىقىنى جەزملەشتۈرۈڭ ھەمدە «ھازىر زاپاسلاڭ»نى چېكىپ قايتا سىناڭ." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7555,6 +7589,8 @@ يېڭىلاش تەپسىلاتى + + Processing backup… بۇ ئۈسكۈنىدە %1$s زاپاسلىغان مەلۇماتلىرىڭىز يوق. مۇشتەرىلىكىڭىز %2$d كۈندە ئاخىرلاشقاندا زاپاسلىغان مەلۇماتلىرىڭىز ئۆچۈرۈلىدۇ. @@ -7573,6 +7609,8 @@ تورسىز زاپاسلاشنى توختاتقىلى ۋە ئۆچۈرگىلى بولمىدى تور خاتالىقى كۆرۈلدى. تور ئۇلىنىشىڭىزنى تەكشۈرۈپ قايتا سىناڭ. + + Uploading messages… @@ -7785,6 +7823,18 @@ جەزملە + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index a6339f42d2..b7a33a7a96 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -405,7 +405,7 @@ Не надіслано, торкніться для інформації - Надіслано частково, торкніться для інформації + Надіслано не всім, торкніться для інформації Не вдалося надіслати Користувач %1$s покинув групу. Надсилання зупинено @@ -758,10 +758,10 @@ Прочитати - Непрочитаний - Непрочитані - Непрочитані - Непрочитані + Не читати + Не читати + Не читати + Не читати Закріпити Відкріпити @@ -1051,6 +1051,20 @@ Спробувати знову Продовжити без перенесення + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1076,13 +1090,13 @@ - Transfer message history + Перенести історію повідомлень - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Не переказувати + Не переносити - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Відв\'язати «%1$s»? @@ -1431,16 +1445,18 @@ Усі ваші повідомлення Відновлення з резервної копії - - Вона містить лише медіафайли, надіслані та отримані за минулі %1$d днів. Ваша резервна копія містить: Відновити з резервної копії - + Дата і час створення останньої резервної копії: %1$s, %2$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Отримання інформації про резервну копію… + + Skip restore Сповіщати про згадки @@ -2632,6 +2648,8 @@ Ви зробили забагато спроб зареєструвати цей номер. Будь ласка, повторіть через %1$s. Неможливо з\'єднатися з сервісом. Перевірте з\'єднання з мережею і спробуйте знову. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Ми не змогли надіслати вам код підтвердження в SMS. Код можна також отримати, прийнявши аудіовиклик. @@ -3018,8 +3036,8 @@ Під час доставлення повідомлення сталася помилка. Доставлення повідомлення зупинено. Пройдіть перевірку, щоб продовжувати спілкуватись у Signal. - Позначити все як прочитане - Прочитано + Позначити всі як прочитані + Прочитати Вимкнути ці сповіщення Одноразове фото Одноразове відео @@ -3089,7 +3107,7 @@ Пошук - Пошук непрочитаних чатів + Пошук у непрочитаних чатах Пошук чатів, контактів і повідомлень @@ -3986,7 +4004,7 @@ Вимкнути платежі Кодова фраза Допомога - Комісія за поглинення криптовалюти + Комісія за округлення койнів Надісланий платіж Отриманий платіж Опрацювання платежу @@ -4110,7 +4128,7 @@ Кому: Відправник Деталі транзакції, зокрема сума платежу і час транзакції, є частиною особового рахунку MobileCoin. - Плата за округлення койнів + Комісія за округлення койнів «Плата за округлення койнів» стягується, коли ваші койни неможливо згрупувати для здійснення трансакції. Округлення дасть вам змогу продовжити надсилати платежі. Інша інформація про цю трансакцію відсутня @@ -4533,6 +4551,8 @@ Відновлення або перенесення акаунту Перенести акаунт Пропустити + + Skip restore Резервні копії чатів Перенести акаунт Перенесіть акаунт на новий пристрій з ОС Android @@ -5168,19 +5188,19 @@ Неправильне слово - %1$s переказав вам %2$s - %1$d нове сповіщення про платіж + Користувач %1$s надіслав вам %2$s + Нових сповіщень про платежі: %1$d - Неможливо відправити платіж - Для відправки платежу цьому користувачу, йому потрібно схвалити ваш запит на листування. Надішліть йому повідомлення для створення цього запиту. + Неможливо надіслати платіж + Щоб ви могли надіслати цьому користувачеві платіж, він повинен схвалити ваш запит на повідомлення. Надішліть йому повідомлення для створення такого запиту. Надіслати повідомлення - У вас немає спільних груп з цією особою. Уважно розглядайте запити перед тим, як їх прийняти, щоб уникнути небажаних повідомлень. + У вас немає спільних груп із цією людиною. Щоб уникнути небажаних повідомлень, не приймайте запитів без уважної перевірки відправника. Жоден із ваших контактів або людей, з якими ви спілкуєтесь, не входить до цієї групи. Двічі перевірте всі запити на контакт, перш ніж їх прийняти, щоб уникнути небажаних повідомлень. - Про запити на листування - Добре + Про запити на повідомлення + Зрозуміло Таким буде колір чату. Цей колір бачите лише ви. @@ -5269,55 +5289,55 @@ Ваш номер телефону змінено на %1$s - Добре + Зрозуміло - Змінити номер + Зміна номеру Ваш старий номер Старий номер телефону Ваш новий номер Новий номер телефону - Наданий номер телефону не належить до вашого акаунту. - Потрібно вказати країну вашого старого номеру телефону - Потрібно вказати ваш старий номер телефону - Потрібно вказати країну вашого нового номеру телефону - Потрібно вказати ваш новий номер телефону + Ви ввели не той номер телефону, на який зареєстровано ваш акаунт. + Ви мусите зазначити країну старого номера телефону + Ви мусите зазначити старий номер телефону + Ви мусите зазначити країну нового номера телефону + Ви мусите зазначити новий номер телефону Змінити номер - Перевірка %1$s - Потрібно ввести капчу + Триває перевірка %1$s + Необхідно пройти перевірку CAPTCHA Змінити номер - Ви збираєтеся змінити свій номер телефону з %1$s на %2$s.\n\nПерш ніж продовжити, переконайтеся, що вказаний нижче номер правильний. - Редагувати номер + Ви за крок від зміни свого номера телефону з %1$s на %2$s.\n\nПерш ніж завершити процедуру зміни, переконайтеся, що цей номер правильний: + Змінити номер - Зміна номеру в Signal – потрібна допомога щодо PIN-коду для Android (PIN-код v2) + Зміна номеру в Signal: Потрібна допомога з PIN-кодом для Android (PIN-код v2) PIN-коди не збігаються - PIN-код, пов’язаний з вашим новим номером, відрізняється від PIN-коду, пов’язаного зі старим. Бажаєте зберегти свій старий PIN-код чи оновити його? - Залишити старий PIN + PIN-код, пов\'язаний з вашим новим номером, відрізняється від PIN-коду, пов\'язаного зі старим. Використовувати старий чи оновити PIN-код? + Лишити старий PIN-код Оновити PIN-код - Залишити старий PIN? + Лишити старий PIN-код? - Схоже, ви намагалися змінити свій номер, але ми не змогли визначити, чи це вдалося.\n\nПовторна перевірка… + Здається, ви спробували змінити свій номер, але ми не змогли визначити, чи це вдалося.\n\n Перевіряємо ще раз… - Зміна статусу підтверджена + Зміну підтверджено - Ваш номер підтверджено як %1$s. Якщо це не ваш новий номер, перезапустіть процес зміни номера. + Ваш підтверджений номер — %1$s. Якщо це не ваш новий номер, почніть процедуру зміни номера спочатку. - Зміна статусу не підтверджена + Підтвердження зміни не отримано - Нам не вдалося визначити статус вашого запиту на зміну номера.\n\n(Помилка: %1$s) + Нам не вдалося визначити результат вашого запиту на зміну номера.\n\n(Помилка: %1$s) Повторити - Покинути + Вийти Надіслати журнал налагодження @@ -5496,15 +5516,15 @@ Не вдалось увімкнути захист платежів - Щоб активувати захист платежів, потрібно спочатку увімкнути блокування екрана чи розблокування відбитком пальця в налаштуваннях телефону. + Щоб активувати захист платежів, потрібно спочатку увімкнути блокування екрана або розблокування відбитком пальця в налаштуваннях телефона. - Не вдалося перейти до налаштувань системи + Не вдалось увійти в системні налаштування - Показувати значок статусу + Показувати значок стану Показувати піктограму в інформації про повідомлення, доставлене від засекреченого відправника. @@ -5553,9 +5573,9 @@ Авто - Використовуйте власні кольори + Використовувати власні кольори Колір чату - Редагувати + Змінити Створити копію Видалити Видалити колір @@ -5575,7 +5595,7 @@ Зберегти - Редагувати колір + Змінити колір Цей колір використовується в %1$d чаті. Бажаєте зберегти зміни для усіх чатів? Цей колір використовується в %1$d чаті. Бажаєте зберегти зміни для усіх чатів? @@ -5599,12 +5619,12 @@ Торкніться, щоб замінити емоджі Скинути Зберегти - Автоматично припасувати колір до шпалер - Потягніть для зміни напрямку градієнту + Колір автоматично узгоджується зі шпалерами + Потягніть для зміни напрямку градієнта Додати фото профілю - Оберіть вид та колір, або налаштуйте під себе ініціали. + Оберіть вигляд і колір або підлаштуйте під себе ініціали. Не зараз Додати фото @@ -5619,13 +5639,13 @@ Емоджі Відкрийте пошук емоджі Відкрити пошук стікерів - Відкрийте програму пошуку GIF + Відкрити пошук GIF Стікери - Backspace - GIF-файли + Клавіша повернення + GIF-зображенння Пошук емоджі Повернутися до емоджі - Очистити пошук введених даних + Видалити дані, введені в поле пошуку Пошук на GIPHY @@ -5640,7 +5660,7 @@ Тільки адміністратори можуть додавати історії в цій групі - Програму з контактами не знайдено + Застосунок для керування контактами не знайдено Почати відеовиклик Почати аудіовиклик @@ -5702,12 +5722,12 @@ Власні сповіщення - Нещодавно використані + Недавно використані - .5x + 0,5x 1x - 1.5x + 1,5x 2x @@ -5770,46 +5790,46 @@ Вибрати фото Фото - Опис + Текст Зберегти - Витерти фото профілю - Помилка збереження фото профілю + Прибрати зображення + Не вдалося зберегти зображення Попередній вигляд Готово - Опис + Текст Колір - Вибрати колір + Виберіть колір Поділитися Перейти вгору - Переслати до + Куди переслати - Поділитися із + Поділитися з Додати повідомлення Швидше пересилання Відео буде поділено на 30-секундні фрагменти та опубліковано як історії. Відео в історіях не може тривати понад 30 с. - Переслані повідомлення тепер відправляються миттєво. + Тепер у разі пересилання надсилання повідомлень стається миттєво. - Відправити повідомлення %1$d - Відправити повідомлень %1$d - Відправити повідомлень %1$d - Відправити повідомлень %1$d + Надіслати %1$d повідомлення + Надіслати %1$d повідомлення + Надіслати %1$d повідомлень + Надіслати %1$d повідомлення Повідомлення надіслано - Повідомлення надіслані - Повідомлення надіслані - Повідомлення надіслані + Повідомлення надіслано + Повідомлення надіслано + Повідомлення надіслано Не вдалося надіслати повідомлення @@ -5818,10 +5838,10 @@ Не вдалося надіслати повідомлення - Не вдалось переслати повідомлення тому що воно більше недоступне. - Не вдалось переслати повідомлення тому що вони більше недоступні. - Не вдалось переслати повідомлення тому що вони більше недоступні. - Не вдалось переслати повідомлення тому що вони більше недоступні. + Не вдалося переслати повідомлення, бо воно вже недоступне. + Не вдалося переслати повідомлення, бо вони вже недоступні. + Не вдалося переслати повідомлення, бо вони вже недоступні. + Не вдалося переслати повідомлення, бо вони вже недоступні. Надсилати повідомлення цій групі можуть тільки адміністратори. @@ -5838,9 +5858,9 @@ Додати відповідь Надіслати Одноразовий медіафайл - Один або більше елементів були завеликими + Принаймні один файл був завеликий Принаймні один елемент був неприпустимий - Обрано забагато елементів + Вибрано забагато файлів Відео буде показано один раз @@ -5876,8 +5896,8 @@ Завершити редагування Очистити все Скасувати зміни - Перемкнутись між маркером та маркером для виділення тексту - Перемкнутись між стилями тексту + Перемкнутися між фломастером і маркером + Перемкнутися між стилями тексту Обрані @@ -5885,7 +5905,7 @@ Надіслати Натисніть для вилучення - Нажміть щоб обрати + Торкніться, щоб вибрати Відхилити Відхилити зміни? @@ -5954,7 +5974,7 @@ Будь ласка, підтримайте Signal донатом, якщо можете, щоб він лишався цікавим, надійним і доступним для всіх. - Дякуємо за вашу підтримку! + Дякуємо за підтримку! Ви отримали значок спонсора від Signal! Відобразіть його в профілі, щоб розповісти про свою підтримку. Ви також можете @@ -5970,7 +5990,7 @@ Підтримати Signal - Більше + Докладніше Моя підтримка @@ -5996,7 +6016,7 @@ %1$s/місяць Наступний платіж %1$s - Обробка транзакції… + Опрацювання трансакції… Не вдалося додати значок. %1$s Будь ласка, зверніться до служби підтримки. @@ -6034,7 +6054,7 @@ Ви можете повторно активувати Значок підтримки ще на 30 днів, зробивши одноразовий внесок. Ви можете і надалі користуватися Signal, але ми були би дуже вдячні, якщо б ви зробили місячний донат. Так ви допоможете нам підтримувати застосунок для вас. - Стати прихильником + Стати благодійником Сприяти розвитку Не зараз @@ -6046,11 +6066,11 @@ Ви можете продовжувати використовувати Signal. Якщо ви бажаєте підтримати застосунок і повторно активувати свій значок, ви можете поновити передплату. Поновити підписку - До Google Pay + Відкрити Google Pay Неможливо опрацювати платіж за підпискою - У нас виникли проблеми зі списанням платежу Підтримувача Signal. Упевніться, що дані платіжного засобу вірні. Якщо ні — оновіть їх у Google Pay. Signal ще раз спробує здійснити списання коштів за декілька днів. - Не показувати знов + Нам не вдалось опрацювати ваш благодійний внесок. Перевірте дійсність вашого платіжного засобу. За потреби оновіть платіжну інформацію в Google Pay. Через кілька днів Signal спробує стягнути платіж ще раз. + Більше не показувати Звернутися в службу підтримки Отримати значок %1$s @@ -6086,16 +6106,16 @@ Помилка обробки доната. %1$s Не вдалося обробити донат. Кошти не було стягнуто. Спробуйте ще раз. - Ще обробляється + Опрацювання триває Не вдалося додати значок - Something went wrong + Сталася помилка - Your backups subscription couldn\'t be displayed. Please contact support. + Не вдалося показати передплату на резервне копіювання. Будь ласка, зверніться до служби підтримки. Не вдалося перевірити значок - Не вдалося перевірити відповідь сервера. Будь ласка, зв\'яжіться зі службою підтримки. + Не вдалося перевірити відповідь сервера. Зверніться в службу підтримки. Не вдалося задонатити @@ -6105,7 +6125,7 @@ Не вдалося скасувати підписку Для скасування підписки потрібне з\'єднання з інтернетом. Ваш пристрій не підтримує Google Pay, тому ви не можете оформити підписку, щоб отримати значок. Але ви маєте змогу підтримати Signal, зробивши донат на нашому вебсайті. - Помилка мережі. Перевірте з\'єднання та спробуйте ще. + Мережева помилка. Перевірте з\'єднання та спробуйте знов. Повторити Неможливо надіслати донат @@ -6125,43 +6145,43 @@ Переглянути - Активуємо… + Активація… - Придбано + Активовано - Спробуйте інший спосіб платежу, або зв\'яжіться зі своїм банком для додаткової інформації. + Використайте інший платіжний засіб або зв\'яжіться зі своїм банком для додаткової інформації. Спробуйте інший спосіб оплати або зв\'яжіться зі своїм банком для додаткової інформації. Якщо транзакцію було здійснено через PayPal, зверніться до PayPal. - Упевніться, що дані вашого способу платежу оновлено у Google Pay та спробуйте знов. + Перевірте, чи в Google Pay введено вашу актуальну платіжну інформацію, і повторіть спробу. - Дізнатися більше + Докладніше - Упевніться, що дані вашого способу платежу оновлено у Google Pay та спробуйте знов. Якщо проблема не зникає, зв\'яжіться зі своїм банком. + Перевірте, чи в Google Pay введено вашу актуальну платіжну інформацію, і повторіть спробу. Якщо проблема не зникне, зв\'яжіться зі своїм банком. - Ваша картка не підтримує цей тип покупки. Спробуйте інший спосіб платежу. + Ваша картка не підтримує такий тип покупки. Спробуйте інший платіжний засіб. - Строк дії вашої картки скінчився. Оновіть спосіб платежу у Google Pay та спробуйте знов. + Ваша картка недійсна. Оновіть платіжний засіб у Google Pay і повторіть спробу. - До Google Pay + Відкрити Google Pay Спробуйте ще - Номер картки невірний. Оновіть його у Google Pay та спробуйте знов. + Номер картки неправильний. Оновіть його у Google Pay і повторіть спробу. - Код CVC вашої картки невірний. Оновіть його у Google Pay та спробуйте знов. + Ви ввели неправильний CVC-код. Оновіть його у Google Pay і повторіть спробу. - На картці недостатньо коштів для здійснення цієї покупки. Спробуйте інший спосіб платежу. + На картці недостатньо коштів для здійснення цієї покупки. Спробуйте інший платіжний засіб. - Місяць закінчення строку дії картки невірний. Оновіть його у Google Pay та спробуйте знов. + Зазначено неправильний місяць у строку дії вашого платіжного засобу. Оновіть його у Google Pay і повторіть спробу. - Рік закінчення строку дії картки невірний. Оновіть його у Google Pay та спробуйте знов. + Зазначено неправильний рік у строку дії вашого платіжного засобу. Оновіть його у Google Pay і повторіть спробу. Спробуйте повторити платіж або зв\'яжіться зі своїм банком для отримання додаткової інформації. - Спробуйте знов, або зв\'яжіться зі своїм банком для додаткової інформації. + Повторіть спробу або зв\'яжіться зі своїм банком для додаткової інформації. @@ -6204,9 +6224,9 @@ Зберегти - Редагувати цей профіль + Внести зміни в цей профіль - Профіль з таким ім\'ям вже існує + Профіль з такою назвою вже існує Робота @@ -6218,12 +6238,12 @@ Зосередженість - Має мати ім\'я + Дати назву обов\'язково Від кого надходять сповіщення - Додайте людей та групи, від яких бажаєте отримувати сповіщення, коли цей профіль увімкнено + Додайте людей і групи, від яких бажаєте отримувати сповіщення, коли цей профіль увімкнено Додати людей або групи @@ -6256,7 +6276,7 @@ Видалити - Редагувати профіль сповіщень + Внести зміни в профіль сповіщень Щодня @@ -6272,14 +6292,14 @@ Сповіщати про всі згадки - Розклад + Графік Показати решту - Додати розклад + Додайте графік - Створити розклад для автоматичного вмикання цього профілю сповіщень. + Створіть графік, за яким цей профіль сповіщень вмикатиметься автоматично. Розклад @@ -6311,7 +6331,7 @@ Далі - Розклад має мати хоча б один день + У графіку має бути хоча б один день Профіль створено @@ -6320,9 +6340,9 @@ Ви можете вмикати та вимикати профіль вручну через меню у списку чатів. - Додайте розклад у налаштуваннях для автоматизації свого профілю. + Додайте графік у налаштуваннях, щоб ваш профіль вмикався автоматично. - Профіль вмикатиметься та вимикатиметься автоматично за розкладом. + Профіль вмикатиметься й вимикатиметься автоматично за графіком. Новий профіль @@ -6331,12 +6351,12 @@ До %1$s - Дивитись налаштування + Відкрити налаштування - До %1$s + Увімкнено до %1$s - Не вдалося відкрити вибір. + Не вдалося відкрити список. Щоб увімкнути сповіщення, Signal потребує дозволу на їхні відображення. @@ -6360,7 +6380,7 @@ Тип внеску - Дата оплати + Дата платежу Поділитися @@ -6399,7 +6419,7 @@ Переслати - Поділитись… + Поділитися… До чату @@ -6407,13 +6427,13 @@ Надсилання… - Надсилаємо %1$d… + Надсилання %1$d… - Не вдалося відправити + Не вдалося надіслати Надіслано частково - Натисніть, щоб повторити + Торкніться, щоб повторити Приховати історію? @@ -6427,9 +6447,9 @@ %1$d перегляд - %1$d переглядів + %1$d перегляди %1$d переглядів - %1$d переглядів + %1$d перегляду Переслати @@ -6444,9 +6464,9 @@ %1$d перегляд - %1$d переглядів + %1$d перегляди %1$d переглядів - %1$d переглядів + %1$d перегляду @@ -6460,7 +6480,7 @@ Додати - Перегляди не відображаються + Показ переглядів вимкнено %1$s %2$s @@ -6476,11 +6496,11 @@ Відповідь групі - Ще немає переглядів + Переглядів ще немає Увімкніть відмітки про перегляд, щоб знати, хто дивився ваші історії. - Перейдіть до налаштувань + Відкрити налаштування Вилучити @@ -6544,10 +6564,10 @@ Показувати тільки обраним людям - %1$d особи - %1$d осіб - %1$d осіб - %1$d осіб + %1$d людина + %1$d людини + %1$d людей + %1$d людини Виберіть, хто зможе переглядати вашу історію. Зміни не вплинуть на вже надіслані історії. @@ -6587,18 +6607,18 @@ Назва історії - Торкніться для додавання тексту + Торкніться, щоб додати текст Додати текст Це весь текст - Опис + Текст Камера - Надрукувати або вставити URL + Введіть або вставте URL-адресу Поділіться посиланням з глядачами вашої історії @@ -6633,7 +6653,7 @@ Не вдалося надіслати історію. Перевірте з\'єднання з мережею і спробуйте знову. - Відправити + Надіслати Вимкнути і видалити @@ -6660,7 +6680,7 @@ Створити - Це необхідне поле. + Це обов\'язкове поле. Історія з такою назвою вже існує. @@ -6676,13 +6696,13 @@ Для користувачів уже створеної групи - Обрати групи + Вибрати групи Скопійовано в буфер обміну Читати далі - Надсилаємо відповідь… + Надсилання відповіді… Ця історія вже недоступна. @@ -6690,7 +6710,7 @@ Немає з\'єднання з інтернетом - Не вдалось завантажити вміст + Не вдалося завантажити вміст Історію надіслано @@ -6714,7 +6734,7 @@ - Дивитись далі + Далі Перейти за посиланням @@ -6748,11 +6768,11 @@ Додати повідомлення - Підтверджуємо одержувача… + Перевіряємо одержувача… %1$s донатить від вашого імені - Дякуємо за вашу підтримку! + Дякуємо за підтримку! Користувач %1$s задонатив Signal від вашого імені! Покажіть у своєму профілі, що підтримуєте Signal. @@ -6770,7 +6790,7 @@ Термін дії значка минув, тож він більше не відображається в профілі. - Щоб продовжувати підтримувати технологію, створену для вас, будь ласка, обміркуйте можливість стати щомісячним Прихильником. + Щоб і надалі підтримувати створені для вас технології, ви можете щомісяця здійснювати благодійний внесок. Зробити щомісячний донат @@ -6854,18 +6874,18 @@ Залишилося %1$d хвилини - Строк дії закінчився + Уже недійсний - Натисніть, щоб перейти + Торкніться, щоб перейти далі - Проведіть вгору, щоб пропустити + Потягніть угору, щоб пропустити - Проведіть праворуч, щоб вийти + Потягніть праворуч, щоб вийти Зрозуміло - Відкрийте контекстне меню + Відкрити контекстне меню %1$s · Перевірено @@ -6877,9 +6897,9 @@ Перевірка коду безпеки - Перевірка коду безпеки завершена + Перевірку коду безпеки завершено - Усі довірені контакти було переглянуто. Натисніть «відправити», щоб продовжити. + Усі контакти було переглянуто. Натисніть «Надіслати», щоб продовжити. Можливо, %1$d ваш контакт перевстановив Signal або змінив пристрій. Ви можете переглянути код безпеки або надіслати без перегляду. @@ -6894,19 +6914,19 @@ Усе одно надіслати - Переглянути довірені контакти + Переглянути контакти - Більше немає одержувачів + Одержувачів більше немає Готово Зміна коду безпеки - %1$d одержувач міг заново встановити Signal або змінити пристрій. Натисніть на одержувача, щоб підтвердити новий код безпеки. Це необов\'язково. - %1$d одержувача могли заново встановити Signal або змінити пристрої. Натисніть на одержувача, щоб підтвердити новий код безпеки. Це необов\'язково. - %1$d одержувачів могли заново встановити Signal або змінити пристрої. Натисніть на одержувача, щоб підтвердити новий код безпеки. Це необов\'язково. - %1$d одержувача могли заново встановити Signal або змінити пристрої. Натисніть на одержувача, щоб підтвердити новий код безпеки. Це необов\'язково. + Можливо, %1$d одержувач заново встановив Signal або змінив пристрій. Торкніться імені одержувача, щоб підтвердити новий код безпеки. Це необов\'язково. + Можливо, %1$d одержувачі заново встановили Signal або змінили пристрої. Торкніться імені одержувача, щоб підтвердити новий код безпеки. Це необов\'язково. + Можливо, %1$d одержувачів заново встановили Signal або змінили пристрої. Торкніться імені одержувача, щоб підтвердити новий код безпеки. Це необов\'язково. + Можливо, %1$d одержувача заново встановили Signal або змінили пристрої. Торкніться імені одержувача, щоб підтвердити новий код безпеки. Це необов\'язково. Контакти @@ -6927,7 +6947,7 @@ Обрані глядачі - Надіслано + Надіслано: Отримано @@ -6974,7 +6994,7 @@ Вилучити групову історію - Меню переповнення + Розкривне меню 1 @@ -7530,7 +7550,7 @@ Не вдалося зберегти зміни. Перевірте підключення до мережі і спробуйте знову. - Couldn\'t delete call link as it is currently in use. + Посилання зараз використовується, тому його не вдалося видалити. Видалити посилання? @@ -7625,6 +7645,10 @@ Відхилити Прийняти + + Approve all + + Deny all Увімкнути повноекранні сповіщення? @@ -7803,7 +7827,13 @@ Пропустити відновлення? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Якщо не відновити дані з резервної копії зараз, решту медіафайлів і вкладень можна буде завантажити пізніше, коли на пристрої буде вільне місце. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7836,7 +7866,11 @@ Не завантажувати - "Не вдалося завершити створення останньої резервної копії. Перевірте з\'єднання телефона з Wi-Fi і натисніть «Створити копію зараз»." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7880,7 +7914,7 @@ Пам\'ять буде звільнено від медіафайлів, які не використовуються; їх можна в будь-який час вивантажити з резервної копії. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Оптимізація пам\'яті доступна тим, хто користується платним резервним копіюванням від Signal. Опрацювання вашої передплати ще триває, тому вона поки що неактивна. Будь ласка, спробуйте пізніше. @@ -8044,6 +8078,8 @@ Поновити Докладніше + + Processing backup… %1$s даних вашої резервної копії зберігається не на цьому пристрої. Резервну копію буде видалено через %2$d день, коли скінчиться ваша передплата. @@ -8068,6 +8104,8 @@ Не вдалося вимкнути й видалити резервну копію Виникла мережева помилка. Перевірте з\'єднання з інтернетом і вимкніть резервне копіювання ще раз. + + Uploading messages… @@ -8107,7 +8145,7 @@ Ключ від резервної копії - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Ключ від резервної копії — це код із 64 символів, який дає змогу відновити дані з резервної копії в разі перевстановлення Signal. Якщо ви забудете цей ключ, то не зможете відновити дані з резервної копії. Signal не зможе допомогти вам з відновленням даних. @@ -8200,7 +8238,7 @@ У мене не лишилося старого телефона - Or you\'re reinstalling Signal on the same device + Або ви перевстановлюєте Signal на цьому ж пристрої Відновлення або перенесення акаунту @@ -8229,15 +8267,15 @@ Введіть ключ від резервної копії - Your backup key is a 64-character code required to recover your account and data. + Ключ від резервної копії — це код із 64 символів, який дає змогу відновити акаунт і пов\'язані з ним дані. Не зберегли ключа? Ключ від резервної копії - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Резервну копію неможливо відновити без відповідного ключа з 64 символів. Якщо ви втратили доступ до свого ключа, Signal не допоможе вам відновити резервну копію. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Якщо у вас лишився старий пристрій, ключ від резервної копії можна переглянути, відкривши налаштування > Резервне копіювання. Потім виберіть «Переглянути ключ від резервної копії». Докладніше @@ -8286,6 +8324,18 @@ Зрозуміло + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index 95c0e77b52..a7197c1306 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -1011,6 +1011,20 @@ دوبارہ لنک کرنے کی کوشش کریں منتقلی کے بغیر جاری رکھیں + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1036,13 +1050,13 @@ - Transfer message history + میسج ہسٹری ٹرانسفر کریں - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - منتقلی نہ کریں + ٹرانسفر نہیں کریں - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device \"%1$s\" غیر مربوط کریں؟ @@ -1355,16 +1369,18 @@ آپ کے تمام میسجز بیک اپ سے بحال کریں - - صرف گزشتہ%1$d دنوں میں بھیجا گیا یا موصول کیا گیا میڈیا شامل ہے۔ آپ کے بیک اپ میں شامل ہوتا ہے: بیک اپ بحال کریں - + آپ کا آخری بیک اپ %2$s بجے %1$s کو کیا گیا۔ + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. بیک اپ کی تفصیلات حاصل کی جا رہی ہیں… + + Skip restore جب مجھے کوئی ذکر کرتا ہے تو مجھے مطلع کریں @@ -2464,6 +2480,8 @@ آپ اس نمبر کو رجسٹر کرنے کے لیے بہت مرتبہ کوششیں کر چکے ہیں۔ براہ کرم %1$s میں دوبارہ کوشش کریں۔ سروس سے رابطے کے قابل نہیں۔ براہ کرم نیٹ ورک کنکشن چیک کریں اور دوبارہ کوشش کریں۔ + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. ہم آپ کو SMS کے ذریعے تصدیقی کوڈ بھیجنے سے قاصر ہیں۔ اس کے بجائے وائس کال کے ذریعے اپنا کوڈ حاصل کرنے کی کوشش کریں۔ @@ -4311,6 +4329,8 @@ ری اسٹور کریں یا ٹرانسفر کریں اکاؤنٹ منتقل کریں چھوڑ دو + + Skip restore چیٹ کے بیک اپس اکاؤنٹ منتقل کریں اکاؤنٹ کو کسی نئے Android ڈیوائس میں منتقل کریں @@ -5823,9 +5843,9 @@ تاحال زیر عمل ہے بیج شامل نہیں کر سکے - Something went wrong + کچھ غلط ہو گیا - Your backups subscription couldn\'t be displayed. Please contact support. + آپ کی بیک اپس کی سبسکرپشن کو ڈسپلے نہیں کیا جا سکا۔ براہ کرم سپورٹ سے رابطہ کریں۔ بیج کی تصدیق میں ناکامی ہوئی @@ -7216,7 +7236,7 @@ تبدیلیاں محفوظ نہیں کر سکے۔ اپنا نیٹ ورک کنکشن چیک کریں اور دوبارہ کوشش کریں۔ - Couldn\'t delete call link as it is currently in use. + کال لنک کو ڈیلیٹ نہیں کیا جا سکا کیونکہ یہ فی الحال استعمال میں ہے۔ لنک حذف کریں؟ @@ -7307,6 +7327,10 @@ مسترد کریں منظور کریں + + Approve all + + Deny all پوری اسکرین کی اطلاعات کو آن کریں؟ @@ -7481,7 +7505,13 @@ بحال کرنا چھوڑ دیں؟ - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + اگر آپ بحالی کے عمل کو چھوڑ دیتے ہیں، تو آپ کے بیک اپ میں موجود باقی میڈیا اور منسلکات کو بعد میں اس وقت ڈاؤن لوڈ کیا جا سکتا ہے جب اسٹوریج کی جگہ دستیاب ہو۔ + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7514,7 +7544,11 @@ ڈاؤن لوڈ چھوڑ دیں - "آپ کا آخری بیک اپ مکمل نہیں ہو سکا۔ یقینی بنائیں کہ آپ کا فون Wi-Fi سے منسلک ہے اور دوبارہ کوشش کرنے کے لیے ابھی بیک اپ کریں پر ٹیپ کریں۔" + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7558,7 +7592,7 @@ غیر استعمال شدہ میڈیا کو آف لوڈ کر دیا جائے گا، لیکن آپ کے بیک اپ سے کسی بھی وقت ڈاؤن لوڈ کیا جا سکتا ہے۔ - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + اسٹوریج کی کارکردگی کو صرف Signal بیک اپس کے ادا شدہ درجے کے ساتھ ہی استعمال کیا جا سکتا ہے۔ آپ کی بیک اپس کی سبسکرپشن ابھی بھی زیرِ عمل ہے اور ابھی فعال نہیں ہوئی۔ براہ کرم کچھ دیر بعد دوبارہ کوشش کریں۔ @@ -7718,6 +7752,8 @@ تجدید کریں مزید جانیں + + Processing backup… آپ کے بیک اپ کا %1$s ڈیٹا ہے جو اس ڈیوائس پر نہیں ہے۔ جب آپ کی سبسکرپشن %2$d دن میں ختم ہو جاتی ہے تو آپ کا بیک اپ حذف کر دیا جائے گا۔ @@ -7738,6 +7774,8 @@ بیک اپس کو غیر فعال اور حذف نہیں کیا جا سکا نیٹ ورک میں کوئی نقص واقع ہوا ہے۔ براہ کرم اپنا انٹرنیٹ کنیکشن چیک کریں اور دوبارہ کوشش کریں۔ + + Uploading messages… @@ -7777,7 +7815,7 @@ آپ کی بیک اپ کیی - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + آپ کی بیک اپ کی کیی 64 حروف پر مشتمل ایک کوڈ ہے جو آپ کو Signal کو دوبارہ انسٹال کرنے پر اپنے بیک اپ کو بحال کرنے میں مدد دیتا ہے۔ اگر آپ اپنی کیی بھول جاتے ہیں، تو آپ اپنے بیک اپ کو بحال نہیں کر سکیں گے۔ Signal آپ کے بیک اپ کو بحال کرنے میں مدد نہیں کر سکتا۔ @@ -7866,7 +7904,7 @@ میرے پاس اپنا پرانا فون موجود نہیں ہے - Or you\'re reinstalling Signal on the same device + یا آپ اسی ڈیوائس پر Signal کو دوبارہ انسٹال کر رہے ہیں ری اسٹور کریں یا اکاؤنٹ ٹرانسفر کریں @@ -7895,15 +7933,15 @@ اپنی بیک اپ کیی درج کریں - Your backup key is a 64-character code required to recover your account and data. + آپ کی بیک اپ کیی 64 حروف پر مشتمل ایک کوڈ ہے جو آپ کے اکاؤنٹ اور ڈیٹا کو بحال کرنے کے لیے درکار ہوتی ہے۔ بیک اپ کیی نہیں ہے؟ بیک اپ کیی - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + بیک اپس کو ان کے 64 حروف پر مشتمل بحالی کے کوڈ کے بغیر بحال نہیں کیا جا سکتا۔ اگر آپ سے اپنی بیک اپ کیی گم ہو گئی ہے، تو Signal آپ کے بیک اپ کو بحال کرنے میں مدد نہیں کر سکتا۔ - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + اگر آپ کے پاس اپنی پرانی ڈیوائس موجود ہے تو آپ سیٹنگز > بیک اپس میں اپنی بیک اپ کیی دیکھ سکتے ہیں۔ اس کے بعد بیک اپ کیی دیکھیں پر ٹیپ کریں۔ مزید جانیں @@ -7952,6 +7990,18 @@ ٹھیک ہے + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 18eab028af..7732c913f5 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -991,6 +991,20 @@ Thử liên kết lại Tiếp tục và không chuyển + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1016,13 +1030,13 @@ - Transfer message history + Chuyển lịch sử tin nhắn - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Không truyền tải + Không chuyển - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device Ngừng kết nối \"%1$s\"? @@ -1317,16 +1331,18 @@ Tất cả tin nhắn Khôi phục từ bản sao lưu - - Chỉ tập tin đa phương tiện được gửi và nhận trong %1$d ngày qua được bao gồm. Bản sao lưu bao gồm: Khôi phục từ bản sao lưu - + Bản sao lưu gần nhất của bạn là vào lúc %2$s ngày %1$s. + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. Đang thu thập thông tin bản sao lưu… + + Skip restore Thông báo khi tôi được nhắc tên @@ -2380,6 +2396,8 @@ Bạn đã thử đăng ký số này quá nhiều lần. Vui lòng thử lại trong %1$s. Không thể kết nối với dịch vụ. Vui lòng kiểm tra đuờng truyền mạng và thử lại. + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. Chúng tôi không thể gửi cho bạn mã xác minh thông qua tin nhắn SMS. Thử nhận mã của bạn qua cuộc gọi thoại. @@ -4200,6 +4218,8 @@ Khôi phục hoặc chuyển Chuyển tài khoản Bỏ qua + + Skip restore Sao lưu tin nhắn Chuyển tài khoản Chuyển tài khoản sang một thiết bị Android mới @@ -5690,9 +5710,9 @@ Vẫn đang xữ lý Không thể thêm huy hiệu - Something went wrong + Đã xảy ra lỗi - Your backups subscription couldn\'t be displayed. Please contact support. + Không thể hiển thị gói đăng ký sao lưu của bạn. Vui lòng liên hệ bộ phận hỗ trợ. Xác nhận huy hiệu không thành công @@ -7059,7 +7079,7 @@ Không thể lưu các thay đổi. Kiểm tra kết nối mạng của bạn và thử lại. - Couldn\'t delete call link as it is currently in use. + Không thể xóa đường dẫn cuộc gọi vì đường dẫn đang được sử dụng. Xóa liên kết? @@ -7148,6 +7168,10 @@ Từ chối Đồng ý + + Approve all + + Deny all Bật thông báo toàn màn hình? @@ -7320,7 +7344,13 @@ Bỏ qua khôi phục? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + Nếu bỏ qua việc khôi phục, các tập tin đa phương tiện và tập tin đính kèm còn lại trong bản sao lưu của bạn có thể được tải xuống sau khi có dung lượng trống. + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7353,7 +7383,11 @@ Bỏ qua không tải - "Quá trình sao lưu gần nhất của bạn không thể hoàn tất. Đảm bảo điện thoại của bạn đã kết nối Wi-Fi và nhấn Sao lưu ngay để thử lại." + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7397,7 +7431,7 @@ Tập tin đa phương tiện không được sử dụng sẽ được gỡ bỏ, nhưng có thể được tải xuống từ bản sao lưu của bạn bất kỳ lúc nào. - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + Tối ưu hóa lưu trữ là tính năng của gói trả phí Sao lưu Signal. Gói đăng ký sao lưu của bạn vẫn đang được xử lý và hiện chưa sẵn sàng. Vui lòng thử lại sau. @@ -7555,6 +7589,8 @@ Gia hạn Tìm hiểu thêm + + Processing backup… Bạn có %1$s dữ liệu sao lưu không có trên thiết bị này. Dữ liệu sao lưu của bạn sẽ được xóa khi gói đăng ký kết thúc trong %2$d ngày nữa. @@ -7573,6 +7609,8 @@ Không thể tắt và xóa bản sao lưu Đã xảy ra lỗi mạng. Vui lòng kiểm tra kết nối internet của bạn và thử lại. + + Uploading messages… @@ -7612,7 +7650,7 @@ Mã khóa sao lưu - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + Mã khóa sao lưu của bạn là mã có 64 ký tự có tác dụng giúp bạn khôi phục bản sao lưu khi cài lại Signal. Nếu quên mã khóa, bạn sẽ không thể khôi phục bản sao lưu. Signal không thể giúp bạn khôi phục bản sao lưu. @@ -7699,7 +7737,7 @@ Tôi không có điện thoại cũ - Or you\'re reinstalling Signal on the same device + Hoặc bạn đang cài lại Signal trên cùng thiết bị Khôi phục hoặc chuyển tài khoản @@ -7728,15 +7766,15 @@ Nhập mã khóa sao lưu - Your backup key is a 64-character code required to recover your account and data. + Yêu cầu có mã khóa sao lưu có 64 ký tự của bạn để khôi phục tài khoản và dữ liệu. Không có mã khóa sao lưu? Mã khóa sao lưu - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + Không thể khôi phục bản sao lưu nếu không có mã khóa sao lưu 64 ký tự. Nếu bạn đã để mất mã khóa sao lưu của mình, Signal không thể hỗ trợ bạn khôi phục bản sao lưu. - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + Nếu có thiết bị cũ của mình, bạn có thể xem mã khóa sao lưu trong Cài đặt > Sao lưu. Sau đó nhấn Xem mã khóa sao lưu. Tìm hiểu thêm @@ -7785,6 +7823,18 @@ OK + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-yue/strings.xml b/app/src/main/res/values-yue/strings.xml index 300a14b1fd..f782a2345a 100644 --- a/app/src/main/res/values-yue/strings.xml +++ b/app/src/main/res/values-yue/strings.xml @@ -48,17 +48,17 @@ Signal 更新緊… - 您仲未設定密碼喎! + 你仲未設定密碼喎! 係咪要停用密碼? - 之後 Signal 同埋所有訊息通知就唔會再上鎖。 + 咁樣以後 Signal 同埋訊息通知就唔會再上鎖㗎喇。 停用 連唔到伺服器! - 註冊鎖需要用到 PIN 碼。停用 PIN 碼嘅話,就要先停用註冊鎖。 + 註冊鎖需要用到 PIN 碼。想停用 PIN 碼嘅話,就要先停用註冊鎖。 整好咗 PIN 碼。 停用咗 PIN 碼。 記低付款恢復口訣 記低口訣 - 喺您可以停用您嘅 PIN 碼之前,您必須先記低您嘅付款恢復口訣,以確保您可以恢復您嘅付款帳戶。 + 喺停用 PIN 碼之前,你必須先記低付款恢復口訣,確保可以恢復付款帳戶。 @@ -72,7 +72,7 @@ - 退後一格 + Backspace 鍵 (相片) @@ -107,10 +107,10 @@ 搵唔到任何相或者影片。Signal 只可以用你揀咗嘅相片同影片。 - 搵唔到俾您揀多媒體嘅 app。 - Signal 要攞「儲存裝置」權限,先可以加入相片、影片或者聲音做附件,但權限已被永久拒絕。請到呢個 app 嘅設定功能表,揀選「權限」,然後啟用「儲存裝置」。 - Signal 要攞「聯絡人」權限,先可以加入聯絡人資訊做附件,但權限已被永久拒絕。請到呢個 app 嘅設定功能表,揀選「權限」,然後啟用「聯絡人」。 - Signal 要攞「位置」權限,先可以加入位置,但權限已被永久拒絕。請到呢個 app 嘅設定功能表,揀選「權限」,然後啟用「位置」。 + 搵唔到俾你揀多媒體嘅 app。 + Signal 要有「儲存空間」嘅權限,先可以附加相片、影片或者語音檔案,但權限已經被永久拒絕。請去 app 嘅設定選單度,揀選「權限」,然後啟用「儲存空間」。 + Signal 要有「聯絡人」嘅權限,先可以附加聯絡人資訊,但權限已經被永久拒絕。請去 app 嘅設定選單度,揀選「權限」,然後啟用「聯絡人」。 + Signal 要有「位置」嘅權限,先可以附加位置,但權限已經被永久拒絕。請去 app 嘅設定選單度,揀選「權限」,然後啟用「位置」。 允許存取你嘅位置 @@ -153,7 +153,7 @@ 被封鎖嘅用戶唔可以打俾你或者傳送訊息俾你。 冇用戶被封鎖 係咪要封鎖用戶? - 「%1$s」之後就無得再同您通話或者寫訊息畀您。 + 「%1$s」之後就無得再打俾你或者傳送訊息俾你。 封鎖 @@ -201,19 +201,19 @@ 係咪要封鎖同退出 %1$s? 係咪要封鎖 %1$s? - 您無得再接收呢個谷嘅訊息或者任何新消息,而且呢個谷嘅成員亦都無得再摻返您入個谷度個囉喎。 - 呢個谷嘅成員無得再摻返您入個谷度㗎喇。 - 呢個谷嘅成員可以再摻返您入個谷度㗎喇。 + 你冇得再接收呢個谷嘅訊息或者最新消息,而且呢個谷嘅成員亦都冇得再摻返你入個谷度㗎喇喎。 + 呢個谷嘅成員冇得再摻返你入個谷度㗎喇。 + 呢個谷嘅成員可以再摻返你入個谷度㗎喇。 - 您哋可以再寫訊息或者通話,您個名同幅相亦都會分享畀佢。 + 你可以同對方互傳訊息或者通話,而你個名同幅相亦都會分享俾佢。 - 您將能夠同佢互傳訊息。 + 你可以同對方互傳訊息。 被封鎖嘅人唔可以打俾你或者傳送訊息俾你。 被封鎖嘅人唔可以傳送訊息俾你。 - 封鎖接收 Signal 嘅更新資訊同最新消息。 + 封鎖接收 Signal 嘅最新消息同資訊。 - 恢復接收 Signal 出爐線報。 + 恢復接收 Signal 嘅最新消息同資訊。 係咪要解除封鎖 %1$s? 封鎖 封鎖同退出 @@ -991,6 +991,20 @@ 再試下連結 繼續但係唔轉移 + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1016,13 +1030,13 @@ - Transfer message history + 轉移訊息紀錄 - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device - Don\'t transfer + 唔好轉移 - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device 係咪要解除連結「%1$s」呀? @@ -1317,16 +1331,18 @@ 你嘅所有訊息 用備份還原 - - 只包括過去 %1$d 日內收發嘅媒體。 你嘅備份包括: 還原備份 - + 上次備份時間係 %1$s 嘅 %2$s。 + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. 攞緊備份詳細資料… + + Skip restore 點名講起我嘅時候通知 @@ -2380,6 +2396,8 @@ 你已經嘗試用呢個手機號碼註冊太多次喇,請你喺 %1$s 後再試。 無法連線到服務。請檢查下網絡連線,然後再試下啦。 + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. 我哋冇辦法透過短訊傳送驗證碼俾你。試吓改用語音通話接收驗證碼啦。 @@ -4200,6 +4218,8 @@ 還原或者轉移 轉移帳戶 飛過 + + Skip restore 傾偈備份 過帳戶落新機 將帳戶轉移去一部新嘅 Android 機度 @@ -5690,9 +5710,9 @@ 仲處理緊 加唔到個襟章 - Something went wrong + 系統出現問題 - Your backups subscription couldn\'t be displayed. Please contact support. + 顯示唔到你嘅備份課金計劃。請聯絡支援團隊。 無法驗證徵章 @@ -7059,7 +7079,7 @@ 儲存唔到變更。請檢查你嘅網絡連線,然後再試多次啦。 - Couldn\'t delete call link as it is currently in use. + 刪除唔到通話連結,因為有人用緊。 係咪要刪除連結呀? @@ -7148,6 +7168,10 @@ 拒絕接聽 批准 + + Approve all + + Deny all 係咪要開啟全螢幕通知? @@ -7320,7 +7344,13 @@ 係咪要跳過還原? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + 如果你跳過還原,備份入面剩低嘅媒體同附件可以之後喺儲存空間夠位嗰陣下載。 + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7353,7 +7383,11 @@ 跳過下載 - "完成唔到你上次嘅備份。請確保你部手機已經連咗 Wi-Fi,然後㩒「即刻備份」再試。" + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7397,7 +7431,7 @@ 未用嘅媒體會卸載,但可以隨時喺備份度下載。 - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + 淨係得有課金用 Signal 備份嘅帳戶先用到「優化儲存空間」功能。你嘅備份課金計劃仲處理緊,未用得住。請你遲啲再試過啦。 @@ -7555,6 +7589,8 @@ 續期 了解詳情 + + Processing backup… 你有 %1$s 嘅備份資料唔喺呢部機度。當你嘅課金計劃喺 %2$d 日之後夠期嗰陣,你嘅備份就會刪除。 @@ -7573,6 +7609,8 @@ 閂唔到同刪除唔到備份 網絡有問題。請檢查你嘅互聯網連線,然後再試多次。 + + Uploading messages… @@ -7612,7 +7650,7 @@ 你嘅備份金鑰 - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + 你嘅備份金鑰係一個 64 個字元嘅代碼,方便你喺重新安裝 Signal 之後還原備份。 如果你唔記得咗個金鑰,就冇辦法還原備份喇。Signal 冇辦法幫你還原備份。 @@ -7699,7 +7737,7 @@ 部舊手機唔喺我手上 - Or you\'re reinstalling Signal on the same device + 或者如果你喺同一部機度重新安裝緊 Signal 還原或者轉移帳戶 @@ -7728,15 +7766,15 @@ 輸入你嘅備份金鑰 - Your backup key is a 64-character code required to recover your account and data. + 你嘅備份金鑰係一組用嚟恢復帳戶同資料嘅 64 個字元代碼。 冇備份金鑰? 備份金鑰 - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + 冇咗 64 個字元嘅恢復代碼,就冇得還原備份㗎喇。如果你唔見咗個備份金鑰,Signal 都冇辦法幫你還原備份。 - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + 如果你有部舊機喺手,就可以喺「設定」 > 「備份」入面睇吓你嘅備份金鑰。然後㩒一下「睇吓備份金鑰」。 了解詳情 @@ -7785,6 +7823,18 @@ 確定 + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index e16953b27e..297938d5ef 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -991,6 +991,20 @@ 尝试重新链接 继续但不传输 + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1016,13 +1030,13 @@ - Transfer message history + 转移消息历史记录 - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device 不转移 - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device 确定解绑 \"%1$s\"? @@ -1317,16 +1331,18 @@ 您的所有消息 从备份还原 - - 备份中只包含过去 %1$d 天发送或接收的媒体。 您的备份包括: 还原备份 - + 您上一次备份的时间是 %1$s %2$s。 + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. 正在获取备份详情… + + Skip restore 提到我时通知我 @@ -2380,6 +2396,8 @@ 您尝试注册此号码的次数过多,请在 %1$s 后再试。 无法连接到 Signal 服务,请检查网络连接并重试。 + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. 我们无法通过短信给您发送验证码。请尝试通过语音通话进行接收。 @@ -4200,6 +4218,8 @@ 恢复或转移 转移帐户 跳过 + + Skip restore 聊天备份 转移帐户 转移帐户至新的 Android 设备 @@ -5690,9 +5710,9 @@ 仍在处理中 无法添加徽章 - Something went wrong + 出错了 - Your backups subscription couldn\'t be displayed. Please contact support. + 您的备份套餐无法显示。请联系支持团队。 无法验证徽章 @@ -7059,7 +7079,7 @@ 无法保存更改,请检查您的互联网连接并重试。 - Couldn\'t delete call link as it is currently in use. + 无法删除通话链接,因为它当前正在使用中。 要删除链接吗? @@ -7148,6 +7168,10 @@ 拒绝 批准 + + Approve all + + Deny all 要开启全屏通知吗? @@ -7320,7 +7344,13 @@ 要跳过恢复吗? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + 如果跳过恢复备份中剩余的媒体和附件,您后续可以在存储空间可用时下载它们。 + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7353,7 +7383,11 @@ 跳过下载 - "您的上次备份无法完成。请确保您的手机已连接到 Wi-Fi,然后点击“立即备份”以重试。" + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7397,7 +7431,7 @@ 未使用的媒体将会被卸载,但您可以随时在备份中下载这些媒体。 - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + 储存优化功能仅在 Signal 备份付费套餐中可用。您的备份套餐仍在处理中,目前尚未生效。请稍后重试。 @@ -7555,6 +7589,8 @@ 续订 了解详情 + + Processing backup… 您有 %1$s 的备份不在此设备上。您的套餐将于 %2$d 天后到期,届时您的备份将会被删除。 @@ -7573,6 +7609,8 @@ 无法关闭和删除备份 出现了一个网络错误。请检查您的互联网连接并重试。 + + Uploading messages… @@ -7612,7 +7650,7 @@ 您的备份密钥 - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + 您的备份密钥是一个 64 个字符的代码,可让您在重新安装 Signal 时恢复备份。 如果您忘记了自己的密钥,您将无法恢复备份。Signal 无法帮助您恢复备份。 @@ -7699,7 +7737,7 @@ 我无法用旧手机 - Or you\'re reinstalling Signal on the same device + 您可以在同一个设备上重新安装 Signal 恢复或转移账户 @@ -7728,15 +7766,15 @@ 输入您的备份密钥 - Your backup key is a 64-character code required to recover your account and data. + 您的备份密钥是一个 64 个字符的代码,它是恢复账户和数据的必要密钥。 没有备份密钥? 备份密钥 - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + 如果没有 64 个字符的恢复代码,备份将无法恢复。如果您丢失了自己的备份密钥,Signal 将无法帮助您恢复备份。 - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + 如果您的旧设备可用,您可以在“设置 > 备份”中查看您的备份密钥。点击“查看备份密钥”即可查看。 了解详情 @@ -7785,6 +7823,18 @@ 好的 + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 452dda1de1..701bf75aca 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -991,6 +991,20 @@ 嘗試再次連結 不轉移並繼續 + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1016,13 +1030,13 @@ - Transfer message history + 轉移訊息紀錄 - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device 不要轉移 - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device 要解除與「%1$s」的連結嗎? @@ -1317,16 +1331,18 @@ 所有你的訊息 從備份還原 - - 只包含過去 %1$d 天內發送或接收的媒體。 你的備份包括: 備份還原 - + 你上次備份是在 %1$s 的 %2$s 進行的。 + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. 正在擷取備份詳細資訊… + + Skip restore 通知我有關提及 @@ -2380,6 +2396,8 @@ 你已多次嘗試註冊此電話號碼。請在 %1$s後再試。 無法連上服務。請檢查網絡連線,並稍後再試。 + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. 我們無法透過 SMS 短訊傳送驗證碼給你。請嘗試透過語音通話接收驗證碼。 @@ -4200,6 +4218,8 @@ 還原或轉移 轉移帳戶 略過 + + Skip restore 聊天備份 轉移帳戶 轉移帳戶至新的 Android 裝置 @@ -5690,9 +5710,9 @@ 仍在處理中 無法新增徽章 - Something went wrong + 發生一些錯誤 - Your backups subscription couldn\'t be displayed. Please contact support. + 無法顯示你的備份定期贊助。請聯絡支援團隊。 驗證徽章失敗 @@ -7059,7 +7079,7 @@ 無法儲存變更。請檢查你的網路連線,然後再試一次。 - Couldn\'t delete call link as it is currently in use. + 無法刪除通話連結,因為它目前正在使用中。 要刪除連結嗎? @@ -7148,6 +7168,10 @@ 拒絕 核准 + + Approve all + + Deny all 要開啟全螢幕通知? @@ -7320,7 +7344,13 @@ 要跳過還原嗎? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + 如果你跳過還原,備份內尚餘的媒體和附件可以稍後在儲存空間可用時下載。 + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7353,7 +7383,11 @@ 跳過下載 - "無法完成你上次的備份。確保你的手機已連接到 Wi-Fi,然後點按「立即備份」以再試一次。" + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7397,7 +7431,7 @@ 未使用的媒體將被卸載,但可以隨時從備份中下載。 - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + 儲存最佳化只可供 Signal 備份付費級別使用。你的備份定期贊助仍在處理中,且尚未啟用。請稍後再試。 @@ -7555,6 +7589,8 @@ 續期 了解更多 + + Processing backup… 你有 %1$s 的備份資料不在此裝置上。當你的定期贊助在 %2$d 天後結束時,你的備份將被刪除。 @@ -7573,6 +7609,8 @@ 無法關閉並刪除備份 發生網路錯誤。請檢查你的網路連線,然後重試。 + + Uploading messages… @@ -7612,7 +7650,7 @@ 你的備份金鑰 - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + 你的備份金鑰是一組 64 個字元的代碼,可讓你在重新安裝 Signal 時還原備份。 如果你忘記了金鑰,你將會無法還原備份。Signal 無法幫助你還原備份。 @@ -7699,7 +7737,7 @@ 我沒有我的舊手機 - Or you\'re reinstalling Signal on the same device + 或者你正在同一裝置上重新安裝 Signal 還原或轉移帳戶 @@ -7728,15 +7766,15 @@ 輸入你的備份金鑰 - Your backup key is a 64-character code required to recover your account and data. + 你的備份金鑰是恢復你帳戶和資料所需的一組 64 個字元的代碼。 沒有備份金鑰? 備份金鑰 - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + 如果沒有 64 個字元的恢復代碼,則無法還原備份。如果你遺失了備份金鑰,Signal 將會無法幫助還原備份。 - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + 如果你有舊裝置,則可以在「設定」 >「備份」中檢視備份金鑰。然後輕按「檢視備份金鑰」。 了解更多 @@ -7785,6 +7823,18 @@ 確定 + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index e2efee5aeb..db8c440d31 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -991,6 +991,20 @@ 嘗試再次連結 不轉移並繼續 + + Edit name + + + + Edit device name + + Device name + + Save + + Device name updated + + Unable to change device name. Try again later. @@ -1016,13 +1030,13 @@ - Transfer message history + 轉移訊息紀錄 - Transfer your text messages and recent media to your desktop + Transfer your text messages and recent media to your linked device 不要轉移 - No old messages or media will be transferred to your desktop + No old messages or media will be transferred to your linked device 解除和「%1$s」的連結? @@ -1317,16 +1331,18 @@ 所有你的訊息 從備份還原 - - 只包含過去 %1$d 天內發送或接收的媒體。 你的備份包括: 備份還原 - + 你上次備份是在 %1$s 的 %2$s 進行的。 + + Your last backup was made on %1$s at %2$s. Your backup size is %3$s. 正在擷取備份詳細資訊… + + Skip restore 通知我提及。 @@ -2380,6 +2396,8 @@ 你已多次嘗試註冊此電話號碼。請在 %1$s後再試。 無法連接服務。請檢查網路連線並再試一次。 + + Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. 我們無法透過 SMS 短訊傳送驗證碼給你。請嘗試透過語音通話接收驗證碼。 @@ -4200,6 +4218,8 @@ 還原或轉移 轉移帳號 略過 + + Skip restore 聊天備份 轉移帳號 將帳號轉移到新的Android裝置 @@ -5690,9 +5710,9 @@ 仍在處理中 無法新增徽章 - Something went wrong + 發生一些錯誤 - Your backups subscription couldn\'t be displayed. Please contact support. + 無法顯示你的備份定期贊助。請聯絡支援團隊。 驗證徽章失敗 @@ -7059,7 +7079,7 @@ 無法儲存變更。請檢查你的網路連線,然後再試一次。 - Couldn\'t delete call link as it is currently in use. + 無法刪除通話連結,因為它目前正在使用中。 要刪除連結嗎? @@ -7148,6 +7168,10 @@ 拒絕 核准 + + Approve all + + Deny all 要開啟全螢幕通知? @@ -7320,7 +7344,13 @@ 要跳過還原嗎? - If you skip restore the remaining media and attachments in your backup can be downloaded at a later time when storage space becomes available. + 如果你跳過還原,備份內尚餘的媒體和附件可以稍後在儲存空間可用時下載。 + + Backup failed + + An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + + Check for update @@ -7353,7 +7383,11 @@ 跳過下載 - "無法完成你上次的備份。確保你的手機已連接到 Wi-Fi,然後點按「立即備份」以再試一次。" + Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + + Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + + Learn more @@ -7397,7 +7431,7 @@ 未使用的媒體將被卸載,但可以隨時從備份中下載。 - Storage optimization can only be used with the paid tier of Signal Backups. Your backups subscription is still processing and not yet active. Please try again later. + 儲存最佳化只可供 Signal 備份付費級別使用。你的備份定期贊助仍在處理中,且尚未啟用。請稍後再試。 @@ -7555,6 +7589,8 @@ 續期 了解更多 + + Processing backup… 你有 %1$s 的備份資料不在此裝置上。當你的定期贊助在 %2$d 天後結束時,你的備份將被刪除。 @@ -7573,6 +7609,8 @@ 無法關閉並刪除備份 發生網路錯誤。請檢查你的網路連線,然後重試。 + + Uploading messages… @@ -7612,7 +7650,7 @@ 你的備份金鑰 - Your backup key is a 64-character code that lets you restore your backup when you re-install Signal. + 你的備份金鑰是一組 64 個字元的代碼,可讓你在重新安裝 Signal 時還原備份。 如果你忘記了金鑰,你將會無法還原備份。Signal 無法幫助你還原備份。 @@ -7699,7 +7737,7 @@ 我沒有我的舊手機 - Or you\'re reinstalling Signal on the same device + 或者你正在同一裝置上重新安裝 Signal 還原或轉移帳戶 @@ -7728,15 +7766,15 @@ 輸入你的備份金鑰 - Your backup key is a 64-character code required to recover your account and data. + 你的備份金鑰是恢復你帳戶和資料所需的一組 64 個字元的代碼。 沒有備份金鑰? 備份金鑰 - Backups can\'t be recovered without their 64-character recovery code. If you\'ve lost your backup key Signal can\'t help restore your backup. + 如果沒有 64 個字元的恢復代碼,則無法還原備份。如果你遺失了備份金鑰,Signal 將會無法幫助還原備份。 - If you have your old device you can view your backup key in Settings > Backups. Then tap View backup key. + 如果你有舊裝置,則可以在「設定」 >「備份」中檢視備份金鑰。然後輕按「檢視備份金鑰」。 了解更多 @@ -7785,6 +7823,18 @@ 好的 + + No Backup to Restore + + Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + + Open Signal on your old device + + Tap Settings > Backups + + Enable backups and wait until your backup is complete + + Skip restore diff --git a/app/static-ips.gradle.kts b/app/static-ips.gradle.kts index b7b3629070..7660d58d97 100644 --- a/app/static-ips.gradle.kts +++ b/app/static-ips.gradle.kts @@ -1,5 +1,5 @@ rootProject.extra["service_ips"] = """new String[]{"13.248.212.111","76.223.92.165"}""" -rootProject.extra["storage_ips"] = """new String[]{"142.251.40.211"}""" +rootProject.extra["storage_ips"] = """new String[]{"142.251.40.147"}""" rootProject.extra["cdn_ips"] = """new String[]{"18.161.21.122","18.161.21.4","18.161.21.66","18.161.21.70"}""" rootProject.extra["cdn2_ips"] = """new String[]{"104.18.37.148","172.64.150.108"}""" rootProject.extra["cdn3_ips"] = """new String[]{"104.18.37.148","172.64.150.108"}""" From 6824f09631be7e68a1105e42ea692b8e3781a27d Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 4 Dec 2024 14:28:03 -0500 Subject: [PATCH 53/56] Bump version to 7.27.0 --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a4eecba1fc..862722f96a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,8 +21,8 @@ plugins { apply(from = "static-ips.gradle.kts") -val canonicalVersionCode = 1489 -val canonicalVersionName = "7.26.1" +val canonicalVersionCode = 1490 +val canonicalVersionName = "7.27.0" val currentHotfixVersion = 0 val maxHotfixVersions = 100 From 014218782f7677b39c3e70252178ac0422234781 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 6 Dec 2024 15:59:46 -0500 Subject: [PATCH 54/56] Fix issue with using registration recovery password. --- ...nternalStorageServicePlaygroundFragment.kt | 10 +-- .../securesms/jobs/StorageForcePushJob.kt | 2 +- .../jobs/StorageRotateManifestJob.kt | 2 +- .../securesms/jobs/StorageSyncJob.kt | 2 +- .../keyvalue/StorageServiceValues.kt | 25 +------ .../securesms/keyvalue/SvrValues.kt | 29 +++++++- .../securesms/pin/SvrRepository.kt | 6 +- .../data/RegistrationRepository.kt | 2 - .../registration/ui/RegistrationViewModel.kt | 68 +++++++++++++----- .../ui/RegistrationViewModel.kt | 72 +++++++++++++------ 10 files changed, 144 insertions(+), 74 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/storage/InternalStorageServicePlaygroundFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/storage/InternalStorageServicePlaygroundFragment.kt index bfe6b5b300..7079480167 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/storage/InternalStorageServicePlaygroundFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/storage/InternalStorageServicePlaygroundFragment.kt @@ -54,11 +54,11 @@ import org.thoughtcrime.securesms.jobs.StorageForcePushJob import org.thoughtcrime.securesms.jobs.StorageSyncJob import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.util.Util +import org.whispersystems.signalservice.api.kbs.MasterKey import org.whispersystems.signalservice.api.storage.RecordIkm import org.whispersystems.signalservice.api.storage.SignalStorageManifest import org.whispersystems.signalservice.api.storage.SignalStorageRecord import org.whispersystems.signalservice.api.storage.StorageId -import org.whispersystems.signalservice.api.storage.StorageKey import org.whispersystems.signalservice.internal.storage.protos.ContactRecord import org.whispersystems.signalservice.internal.storage.protos.StorageRecord @@ -171,12 +171,12 @@ fun ToolScreen( SignalStore.storageService.manifest = SignalStorageManifest.EMPTY } - ActionRow("Set initial storage key", "Initializes it to something random. Will cause a decryption failure.") { - SignalStore.storageService.storageKeyForInitialDataRestore = StorageKey(Util.getSecretBytes(32)) + ActionRow("Set initial master key", "Initializes it to something random. Will cause a decryption failure.") { + SignalStore.svr.masterKeyForInitialDataRestore = MasterKey(Util.getSecretBytes(32)) } - ActionRow("Clear initial storage key", "Sets it to null.") { - SignalStore.storageService.storageKeyForInitialDataRestore = null + ActionRow("Clear initial master key", "Sets it to null.") { + SignalStore.svr.masterKeyForInitialDataRestore = null } Rows.ToggleRow( diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.kt index 8c9a2d55bd..eb7e646704 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.kt @@ -124,7 +124,7 @@ class StorageForcePushJob private constructor(parameters: Parameters) : BaseJob( Log.i(TAG, "Force push succeeded. Updating local manifest version to: $newVersion") SignalStore.storageService.manifest = manifest - SignalStore.storageService.storageKeyForInitialDataRestore = null + SignalStore.svr.masterKeyForInitialDataRestore = null SignalDatabase.recipients.applyStorageIdUpdates(newContactStorageIds) SignalDatabase.recipients.applyStorageIdUpdates(Collections.singletonMap(Recipient.self().id, accountRecord.id)) SignalDatabase.unknownStorageIds.deleteAll() diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageRotateManifestJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageRotateManifestJob.kt index 35aa134442..e2eee3ca5a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageRotateManifestJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageRotateManifestJob.kt @@ -89,7 +89,7 @@ class StorageRotateManifestJob private constructor(parameters: Parameters) : Job return when (val result = repository.writeUnchangedManifest(storageServiceKey, manifestWithNewVersion)) { StorageServiceRepository.WriteStorageRecordsResult.Success -> { Log.i(TAG, "Successfully rotated the manifest as version ${manifestWithNewVersion.version}.${manifestWithNewVersion.sourceDeviceId}. Clearing restore key.") - SignalStore.storageService.storageKeyForInitialDataRestore = null + SignalStore.svr.masterKeyForInitialDataRestore = null Result.success() } StorageServiceRepository.WriteStorageRecordsResult.ConflictError -> { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.kt index 9ad64348b4..6ad7183e64 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.kt @@ -369,7 +369,7 @@ class StorageSyncJob private constructor(parameters: Parameters) : BaseJob(param Log.i(TAG, "Saved new manifest. Now at version: ${remoteWriteOperation.manifest.versionString}") SignalStore.storageService.manifest = remoteWriteOperation.manifest - SignalStore.storageService.storageKeyForInitialDataRestore = null + SignalStore.svr.masterKeyForInitialDataRestore = null stopwatch.split("remote-write") diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/StorageServiceValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/StorageServiceValues.kt index 9cb485d2a3..480898b1cf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/StorageServiceValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/StorageServiceValues.kt @@ -15,7 +15,6 @@ class StorageServiceValues internal constructor(store: KeyValueStore) : SignalSt // TODO [linked-device] No need to track this separately -- we'd get the AEP from the primary private const val SYNC_STORAGE_KEY = "storage.syncStorageKey" - private const val INITIAL_RESTORE_STORAGE_KEY = "storage.initialRestoreStorageKey" } public override fun onFirstEverAppLaunch() = Unit @@ -63,26 +62,8 @@ class StorageServiceValues internal constructor(store: KeyValueStore) : SignalSt /** * The [StorageKey] that should be used for our initial storage service data restore. * The presence of this value indicates that it hasn't been used yet. - * Once there has been *any* write to storage service, this value needs to be cleared. + * Once there has been *any* write to storage service, [SvrValues.masterKeyForInitialDataRestore] needs to be cleared. */ - @get:Synchronized - @set:Synchronized - var storageKeyForInitialDataRestore: StorageKey? - get() { - return getBlob(INITIAL_RESTORE_STORAGE_KEY, null)?.let { StorageKey(it) } - } - set(value) { - if (value != storageKeyForInitialDataRestore) { - if (value == storageKey) { - Log.w(TAG, "The key already matches the one derived from the AEP! All good, no need to store it.") - store.beginWrite().putBlob(INITIAL_RESTORE_STORAGE_KEY, null).commit() - } else if (value != null) { - Log.w(TAG, "Setting initial restore key!", Throwable()) - store.beginWrite().putBlob(INITIAL_RESTORE_STORAGE_KEY, value.serialize()).commit() - } else { - Log.w(TAG, "Clearing initial restore key!", Throwable()) - store.beginWrite().putBlob(INITIAL_RESTORE_STORAGE_KEY, null).commit() - } - } - } + val storageKeyForInitialDataRestore: StorageKey? + get() = SignalStore.svr.masterKeyForInitialDataRestore?.deriveStorageServiceKey() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SvrValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SvrValues.kt index 8e53ddf820..0428633caa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SvrValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SvrValues.kt @@ -20,6 +20,7 @@ class SvrValues internal constructor(store: KeyValueStore) : SignalStoreValues(s private const val SVR_LAST_AUTH_REFRESH_TIMESTAMP = "kbs.kbs_auth_tokens.last_refresh_timestamp" private const val SVR3_AUTH_TOKENS = "kbs.svr3_auth_tokens" private const val RESTORED_VIA_ACCOUNT_ENTROPY_KEY = "kbs.restore_via_account_entropy_pool" + private const val INITIAL_RESTORE_MASTER_KEY = "kbs.initialRestoreMasterKey" } public override fun onFirstEverAppLaunch() = Unit @@ -83,6 +84,32 @@ class SvrValues internal constructor(store: KeyValueStore) : SignalStoreValues(s val masterKey: MasterKey get() = SignalStore.account.accountEntropyPool.deriveMasterKey() + /** + * The [MasterKey] that should be used for our initial syncs with storage service + recovery password. + * The presence of this value indicates that it hasn't been used yet for storage service. + * Once there has been *any* write to storage service, this value needs to be cleared. + */ + @get:Synchronized + @set:Synchronized + var masterKeyForInitialDataRestore: MasterKey? + get() { + return getBlob(INITIAL_RESTORE_MASTER_KEY, null)?.let { MasterKey(it) } + } + set(value) { + if (value != masterKeyForInitialDataRestore) { + if (value == masterKey) { + Log.w(TAG, "The master key already matches the one derived from the AEP! All good, no need to store it.") + store.beginWrite().putBlob(INITIAL_RESTORE_MASTER_KEY, null).commit() + } else if (value != null) { + Log.w(TAG, "Setting initial restore master key!", Throwable()) + store.beginWrite().putBlob(INITIAL_RESTORE_MASTER_KEY, value.serialize()).commit() + } else { + Log.w(TAG, "Clearing initial restore master key!", Throwable()) + store.beginWrite().putBlob(INITIAL_RESTORE_MASTER_KEY, null).commit() + } + } + } + @get:Synchronized val pinBackedMasterKey: MasterKey? /** Returns null if master key is not backed up by a pin. */ @@ -102,7 +129,7 @@ class SvrValues internal constructor(store: KeyValueStore) : SignalStoreValues(s val recoveryPassword: String? get() { return if (hasOptedInWithAccess()) { - masterKey.deriveRegistrationRecoveryPassword() + masterKeyForInitialDataRestore?.deriveRegistrationRecoveryPassword() ?: masterKey.deriveRegistrationRecoveryPassword() } else { null } diff --git a/app/src/main/java/org/thoughtcrime/securesms/pin/SvrRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/pin/SvrRepository.kt index a850469c59..8b6699dae0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pin/SvrRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/pin/SvrRepository.kt @@ -168,7 +168,7 @@ object SvrRepository { SignalStore.registration.localRegistrationMetadata = metadata.copy(masterKey = response.masterKey.serialize().toByteString(), pin = userPin) } - SignalStore.storageService.storageKeyForInitialDataRestore = response.masterKey.deriveStorageServiceKey() + SignalStore.svr.masterKeyForInitialDataRestore = response.masterKey SignalStore.svr.setPin(userPin) SignalStore.svr.isRegistrationLockEnabled = false SignalStore.pin.resetPinReminders() @@ -321,14 +321,14 @@ object SvrRepository { Log.i(TAG, "[onRegistrationComplete] ReRegistration Skip SMS", true) } - SignalStore.storageService.storageKeyForInitialDataRestore = masterKey.deriveStorageServiceKey() + SignalStore.svr.masterKeyForInitialDataRestore = masterKey SignalStore.svr.setPin(userPin) SignalStore.pin.resetPinReminders() AppDependencies.jobManager.add(ResetSvrGuessCountJob()) } else if (masterKey != null) { Log.i(TAG, "[onRegistrationComplete] ReRegistered with key without pin") - SignalStore.storageService.storageKeyForInitialDataRestore = masterKey.deriveStorageServiceKey() + SignalStore.svr.masterKeyForInitialDataRestore = masterKey } else if (hasPinToRestore) { Log.i(TAG, "[onRegistrationComplete] Has a PIN to restore.", true) SignalStore.svr.clearRegistrationLockAndPin() diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/data/RegistrationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/data/RegistrationRepository.kt index dae2a1adb3..1008cd3359 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/data/RegistrationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/data/RegistrationRepository.kt @@ -275,8 +275,6 @@ object RegistrationRepository { withContext(Dispatchers.IO) { val credentialSet = SvrAuthCredentialSet(svr2Credentials = svr2Credentials, svr3Credentials = svr3Credentials) val masterKey = SvrRepository.restoreMasterKeyPreRegistration(credentialSet, pin) - SignalStore.storageService.storageKeyForInitialDataRestore = masterKey.deriveStorageServiceKey() - SignalStore.svr.setPin(pin) return@withContext masterKey } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/RegistrationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/RegistrationViewModel.kt index c92453be32..b847fb669f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/RegistrationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/RegistrationViewModel.kt @@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.updateAndGet import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.signal.core.util.Base64 import org.signal.core.util.Stopwatch import org.signal.core.util.isNotNullOrBlank import org.signal.core.util.logging.Log @@ -66,9 +67,12 @@ import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.dualsim.MccMncProducer import org.whispersystems.signalservice.api.SvrNoDataException import org.whispersystems.signalservice.api.kbs.MasterKey +import org.whispersystems.signalservice.api.svr.Svr3Credentials +import org.whispersystems.signalservice.internal.push.AuthCredentials import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataJson import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse import java.io.IOException +import java.nio.charset.StandardCharsets import java.util.concurrent.TimeUnit import kotlin.jvm.optionals.getOrNull import kotlin.time.Duration.Companion.minutes @@ -615,31 +619,18 @@ class RegistrationViewModel : ViewModel() { fun verifyReRegisterWithPin(context: Context, pin: String, wrongPinHandler: () -> Unit) { setInProgress(true) - // Local recovery password - if (RegistrationRepository.canUseLocalRecoveryPassword()) { - if (RegistrationRepository.doesPinMatchLocalHash(pin)) { - Log.d(TAG, "Found recovery password, attempting to re-register.") - viewModelScope.launch(context = coroutineExceptionHandler) { - verifyReRegisterInternal(context, pin, SignalStore.svr.masterKey) - setInProgress(false) - } - } else { - Log.d(TAG, "Entered PIN did not match local PIN hash.") - wrongPinHandler() - setInProgress(false) - } - return - } - // remote recovery password - val svr2Credentials = store.value.svr2AuthCredentials - val svr3Credentials = store.value.svr3AuthCredentials + val svr2Credentials = store.value.svr2AuthCredentials ?: SignalStore.svr.svr2AuthTokens.toSvrCredentials() + val svr3Credentials = store.value.svr3AuthCredentials ?: SignalStore.svr.svr3AuthTokens.toSvrCredentials()?.let { Svr3Credentials(it.username(), it.password(), null) } if (svr2Credentials != null || svr3Credentials != null) { Log.d(TAG, "Found SVR auth credentials, fetching recovery password from SVR (svr2: ${svr2Credentials != null}, svr3: ${svr3Credentials != null}).") viewModelScope.launch(context = coroutineExceptionHandler) { try { val masterKey = RegistrationRepository.fetchMasterKeyFromSvrRemote(pin, svr2Credentials, svr3Credentials) + SignalStore.svr.masterKeyForInitialDataRestore = masterKey + SignalStore.svr.setPin(pin) + setRecoveryPassword(masterKey.deriveRegistrationRecoveryPassword()) updateSvrTriesRemaining(10) verifyReRegisterInternal(context, pin, masterKey) @@ -657,6 +648,22 @@ class RegistrationViewModel : ViewModel() { return } + // Local recovery password + if (RegistrationRepository.canUseLocalRecoveryPassword()) { + if (RegistrationRepository.doesPinMatchLocalHash(pin)) { + Log.d(TAG, "Found recovery password, attempting to re-register.") + viewModelScope.launch(context = coroutineExceptionHandler) { + verifyReRegisterInternal(context, pin, SignalStore.svr.masterKey) + setInProgress(false) + } + } else { + Log.d(TAG, "Entered PIN did not match local PIN hash.") + wrongPinHandler() + setInProgress(false) + } + return + } + Log.w(TAG, "Could not get credentials to skip SMS registration, aborting!") store.update { it.copy(canSkipSms = false, inProgress = false) @@ -917,6 +924,31 @@ class RegistrationViewModel : ViewModel() { setInProgress(false) } + /** Converts the basic-auth creds we have locally into username:password pairs that are suitable for handing off to the service. */ + private fun List.toSvrCredentials(): AuthCredentials? { + return this + .asSequence() + .filterNotNull() + .map { it.replace("Basic ", "").trim() } + .mapNotNull { + try { + Base64.decode(it) + } catch (e: IOException) { + Log.w(TAG, "Encountered error trying to decode a token!", e) + null + } + } + .map { String(it, StandardCharsets.ISO_8859_1) } + .mapNotNull { + val colonIndex = it.indexOf(":") + if (colonIndex < 0) { + return@mapNotNull null + } + AuthCredentials.create(it.substring(0, colonIndex), it.substring(colonIndex + 1)) + } + .firstOrNull() + } + companion object { private val TAG = Log.tag(RegistrationViewModel::class.java) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/RegistrationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/RegistrationViewModel.kt index 4e6dcc2029..76b4d9cb3b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/RegistrationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registrationv3/ui/RegistrationViewModel.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.updateAndGet import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.signal.core.util.Base64 import org.signal.core.util.Stopwatch import org.signal.core.util.isNotNullOrBlank import org.signal.core.util.logging.Log @@ -71,8 +72,11 @@ import org.thoughtcrime.securesms.util.dualsim.MccMncProducer import org.whispersystems.signalservice.api.AccountEntropyPool import org.whispersystems.signalservice.api.SvrNoDataException import org.whispersystems.signalservice.api.kbs.MasterKey +import org.whispersystems.signalservice.api.svr.Svr3Credentials +import org.whispersystems.signalservice.internal.push.AuthCredentials import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse import java.io.IOException +import java.nio.charset.StandardCharsets import java.util.concurrent.TimeUnit import kotlin.jvm.optionals.getOrNull import kotlin.time.Duration.Companion.minutes @@ -621,33 +625,18 @@ class RegistrationViewModel : ViewModel() { fun verifyReRegisterWithPin(context: Context, pin: String, wrongPinHandler: () -> Unit) { setInProgress(true) - // Local recovery password - if (RegistrationRepository.canUseLocalRecoveryPassword()) { - if (RegistrationRepository.doesPinMatchLocalHash(pin)) { - Log.d(TAG, "Found recovery password, attempting to re-register.") - viewModelScope.launch(context = coroutineExceptionHandler) { - val masterKey = SignalStore.svr.masterKey - setRecoveryPassword(masterKey.deriveRegistrationRecoveryPassword()) - verifyReRegisterInternal(context, pin, masterKey) - setInProgress(false) - } - } else { - Log.d(TAG, "Entered PIN did not match local PIN hash.") - wrongPinHandler() - setInProgress(false) - } - return - } - // remote recovery password - val svr2Credentials = store.value.svr2AuthCredentials - val svr3Credentials = store.value.svr3AuthCredentials + val svr2Credentials = store.value.svr2AuthCredentials ?: SignalStore.svr.svr2AuthTokens.toSvrCredentials() + val svr3Credentials = store.value.svr3AuthCredentials ?: SignalStore.svr.svr3AuthTokens.toSvrCredentials()?.let { Svr3Credentials(it.username(), it.password(), null) } if (svr2Credentials != null || svr3Credentials != null) { Log.d(TAG, "Found SVR auth credentials, fetching recovery password from SVR (svr2: ${svr2Credentials != null}, svr3: ${svr3Credentials != null}).") viewModelScope.launch(context = coroutineExceptionHandler) { try { val masterKey = RegistrationRepository.fetchMasterKeyFromSvrRemote(pin, svr2Credentials, svr3Credentials) + SignalStore.svr.masterKeyForInitialDataRestore = masterKey + SignalStore.svr.setPin(pin) + setRecoveryPassword(masterKey.deriveRegistrationRecoveryPassword()) updateSvrTriesRemaining(10) verifyReRegisterInternal(context, pin, masterKey) @@ -665,6 +654,24 @@ class RegistrationViewModel : ViewModel() { return } + // Local recovery password + if (RegistrationRepository.canUseLocalRecoveryPassword()) { + if (RegistrationRepository.doesPinMatchLocalHash(pin)) { + Log.d(TAG, "Found recovery password, attempting to re-register.") + viewModelScope.launch(context = coroutineExceptionHandler) { + val masterKey = SignalStore.svr.masterKey + setRecoveryPassword(masterKey.deriveRegistrationRecoveryPassword()) + verifyReRegisterInternal(context, pin, masterKey) + setInProgress(false) + } + } else { + Log.d(TAG, "Entered PIN did not match local PIN hash.") + wrongPinHandler() + setInProgress(false) + } + return + } + Log.w(TAG, "Could not get credentials to skip SMS registration, aborting!") store.update { it.copy(canSkipSms = false, inProgress = false) @@ -946,6 +953,31 @@ class RegistrationViewModel : ViewModel() { } } + /** Converts the basic-auth creds we have locally into username:password pairs that are suitable for handing off to the service. */ + private fun List.toSvrCredentials(): AuthCredentials? { + return this + .asSequence() + .filterNotNull() + .map { it.replace("Basic ", "").trim() } + .mapNotNull { + try { + Base64.decode(it) + } catch (e: IOException) { + Log.w(TAG, "Encountered error trying to decode a token!", e) + null + } + } + .map { String(it, StandardCharsets.ISO_8859_1) } + .mapNotNull { + val colonIndex = it.indexOf(":") + if (colonIndex < 0) { + return@mapNotNull null + } + AuthCredentials.create(it.substring(0, colonIndex), it.substring(colonIndex + 1)) + } + .firstOrNull() + } + companion object { private val TAG = Log.tag(RegistrationViewModel::class.java) From 073c4f7ae1e140820bd704275db1b4ec1ac617fa Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 6 Dec 2024 16:07:47 -0500 Subject: [PATCH 55/56] Update translations and other static files. --- app/src/main/res/values-af/strings.xml | 22 +-- app/src/main/res/values-ar/strings.xml | 188 ++++++++++----------- app/src/main/res/values-az/strings.xml | 22 +-- app/src/main/res/values-bg/strings.xml | 22 +-- app/src/main/res/values-bn/strings.xml | 22 +-- app/src/main/res/values-bs/strings.xml | 22 +-- app/src/main/res/values-ca/strings.xml | 22 +-- app/src/main/res/values-cs/strings.xml | 22 +-- app/src/main/res/values-da/strings.xml | 22 +-- app/src/main/res/values-de/strings.xml | 22 +-- app/src/main/res/values-el/strings.xml | 22 +-- app/src/main/res/values-es/strings.xml | 22 +-- app/src/main/res/values-et/strings.xml | 22 +-- app/src/main/res/values-eu/strings.xml | 22 +-- app/src/main/res/values-fa/strings.xml | 22 +-- app/src/main/res/values-fi/strings.xml | 22 +-- app/src/main/res/values-fr/strings.xml | 130 +++++++------- app/src/main/res/values-ga/strings.xml | 22 +-- app/src/main/res/values-gl/strings.xml | 22 +-- app/src/main/res/values-gu/strings.xml | 22 +-- app/src/main/res/values-hi/strings.xml | 22 +-- app/src/main/res/values-hr/strings.xml | 22 +-- app/src/main/res/values-hu/strings.xml | 22 +-- app/src/main/res/values-in/strings.xml | 22 +-- app/src/main/res/values-it/strings.xml | 22 +-- app/src/main/res/values-iw/strings.xml | 22 +-- app/src/main/res/values-ja/strings.xml | 22 +-- app/src/main/res/values-ka/strings.xml | 22 +-- app/src/main/res/values-kk/strings.xml | 22 +-- app/src/main/res/values-km/strings.xml | 22 +-- app/src/main/res/values-kn/strings.xml | 22 +-- app/src/main/res/values-ko/strings.xml | 22 +-- app/src/main/res/values-ky/strings.xml | 22 +-- app/src/main/res/values-lt/strings.xml | 22 +-- app/src/main/res/values-lv/strings.xml | 22 +-- app/src/main/res/values-mk/strings.xml | 56 +++--- app/src/main/res/values-ml/strings.xml | 22 +-- app/src/main/res/values-mr/strings.xml | 22 +-- app/src/main/res/values-ms/strings.xml | 22 +-- app/src/main/res/values-my/strings.xml | 22 +-- app/src/main/res/values-nb/strings.xml | 22 +-- app/src/main/res/values-nl/strings.xml | 28 +-- app/src/main/res/values-pa/strings.xml | 22 +-- app/src/main/res/values-pl/strings.xml | 22 +-- app/src/main/res/values-pt-rBR/strings.xml | 22 +-- app/src/main/res/values-pt/strings.xml | 22 +-- app/src/main/res/values-ro/strings.xml | 22 +-- app/src/main/res/values-ru/strings.xml | 22 +-- app/src/main/res/values-sk/strings.xml | 22 +-- app/src/main/res/values-sl/strings.xml | 22 +-- app/src/main/res/values-sq/strings.xml | 22 +-- app/src/main/res/values-sr/strings.xml | 56 +++--- app/src/main/res/values-sv/strings.xml | 22 +-- app/src/main/res/values-sw/strings.xml | 22 +-- app/src/main/res/values-ta/strings.xml | 22 +-- app/src/main/res/values-te/strings.xml | 22 +-- app/src/main/res/values-th/strings.xml | 22 +-- app/src/main/res/values-tl/strings.xml | 18 +- app/src/main/res/values-tr/strings.xml | 22 +-- app/src/main/res/values-ug/strings.xml | 22 +-- app/src/main/res/values-uk/strings.xml | 30 ++-- app/src/main/res/values-ur/strings.xml | 22 +-- app/src/main/res/values-vi/strings.xml | 22 +-- app/src/main/res/values-yue/strings.xml | 22 +-- app/src/main/res/values-zh-rCN/strings.xml | 22 +-- app/src/main/res/values-zh-rHK/strings.xml | 22 +-- app/src/main/res/values-zh-rTW/strings.xml | 22 +-- app/static-ips.gradle.kts | 4 +- 68 files changed, 915 insertions(+), 915 deletions(-) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index d6e4e8bbd5..ff1d4e455d 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -1012,15 +1012,15 @@ Gaan voort sonder om oor te dra - Edit name + Wysig naam Edit device name - Device name + Toestelnaam - Save + Stoor Device name updated @@ -1380,7 +1380,7 @@ Herwin tans rugsteunbesonderhede… - Skip restore + Slaan herstel oor Stel my in kennis van Vermeldings @@ -4330,7 +4330,7 @@ Dra rekening oor Slaan oor - Skip restore + Slaan herstel oor Klets rugsteune Dra rekening oor Dra rekening oor na nuwe Android-toestel @@ -7328,9 +7328,9 @@ Keur goed - Approve all + Keur almal goed - Deny all + Weier almal Skakel volskerm-kennisgewings aan? @@ -7507,7 +7507,7 @@ As jy herstel oorslaan, kan die oorblywende media en aanhegsels in jou rugsteun afgelaai word op \'n later tyd wanneer stoorspasie beskikbaar word. - Backup failed + Rugsteun het misluk An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Vind meer uit @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Maak Signal op jou ou toestel oop Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Slaan herstel oor diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 00594fbe79..2bf08e5d80 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1092,15 +1092,15 @@ المواصلة دون النقل - Edit name + تعديل الاسم Edit device name - Device name + ‫اسم الجهاز - Save + حفظ Device name updated @@ -1532,7 +1532,7 @@ جارٍ إعداد تفاصيل النسخة الاحتياطية… - Skip restore + تخطّى الاسترجاع نبّهني عندما يذكر أحدهم اسمي @@ -4774,7 +4774,7 @@ نقل الحساب تخطّي - Skip restore + تخطّى الاسترجاع النسخ الاحتياطية للدردشات نقل الحساب نقل الحساب إلى جهاز أندرويد جديد @@ -6640,7 +6640,7 @@ لمرّة واحدة - نيابة عن صديق + نيابةً عن صديق التبرُّع لصديق @@ -6764,7 +6764,7 @@ تمَّ الإرسال جزئيًا. انقر للتفاصيل. - فشل الإرسال. يُرجى النقر لإعادة المحاولة + فشل الإرسال. انقر لإعادة المحاولة. الرد على المجموعة @@ -6784,7 +6784,7 @@ لا ردود حاليًا - لا تستطيع الردّ على هذه القصة لأنك لم تعد عضواً في هذه المجموعة. + لا تستطيع الردّ على هذه القصة لأنك لم تعد عضوًا في هذه المجموعة. تفاعلتَ مع القصة @@ -6814,7 +6814,7 @@ %1$d مشاهدة - إظهار + عرض من يُمكنه مُشاهدة هذه القصة @@ -6822,7 +6822,7 @@ جميع جهات اتصال سيجنال - الكل إلا… + الكل إلّا… إخفاء قصتك عن أناس مُحدَّدين @@ -6850,29 +6850,29 @@ اِختر من يُمكنه رؤية قصتك. لن تؤثر التغييرات على القصص التي أرسلتها من قبل. - الإجابات وردود الفعل + الردود والتفاعلات - السماح بالإجابات وردود الفعل + السماح بالردود والتفاعلات تمكين الأفراد الذين يُمكنهم مشاهدة قصتك من التفاعل والرد عليها - جهة اتصال سيجنال + جهات اتصال سيجنال جهات اتصال سيجنال هم أشخاص اخترت أن تثق بهم سواءً عبر: بدء دردشة - قبول طلب التراسل معهم + قبول طلب التراسل وجودهم في جهات اتصال نظامك - "يُمكن لِجهات اتصالك رؤية اسمك وصورتك، كما يمكنهم الاطلاع على منشوراتك في \"قصتي\" إلا إذا أخفيتها عنهم." + "يُمكن لِجهات اتصالك رؤية اسمك وصورتك، كما يمكنهم الاطِّلاع على منشوراتك في \"قصتي\" إلّا إذا أخفيتها عنهم." - إضافة مُشاهد + إضافة مُشاهِد - إزالة القصة المُخصصة + حذف القصة المُخصَّصة - إزالة %1$s؟ + هل ترغبُ بإزالة %1$s؟ لن يتمكن هذا الشخص من رؤية قصتك بعد الآن. @@ -6880,15 +6880,15 @@ - تحرير اسم القصة + تعديل اسم القصة اسم القصة - يُرجى اللمس لإضافة نص ما + انقر لإضافة نص - أضف نصاً + أضِف نصًا انتهت إضافة النص @@ -6896,30 +6896,30 @@ الكاميرا - يُرجى كتابة أو لصق عنوان الصفحة + اكتب أو الصق عنوان URL - مشاركة رابط مع مشاهدي قصتك + مشاركة رابط مع مُشاهِدي قصتك البحث يُرجى إدخال رابط صحيح. - الكل إلا… + الكل إلّا… مشاركة فقط مع… تمّ - حذف قصة المجموعة؟ + هل ترغبُ بإزالة قصة المجموعة؟ - سيتم حذف \"%1$s\" + سيتمُّ حذف \"%1$s\". إزالة - حذف القصة المخصصة؟ + هل ترغبُ بحذف القصة المُخصَّصة؟ - سيتم حذف \"%1$s\" والتحديثات التي تمت مشاركتها في هذه القصة. + سيتمُّ حذف \"%1$s\" والتحديثات التي تمَّت مشاركتها في هذه القصة. حذف @@ -6929,9 +6929,9 @@ - لقد تعذر إرسال القصة. يُرجى التحقق من اتصالك ثم المحاولة مرة أخرى. + تعذَّر إرسال القصة. يُرجى التحقُّق من اتصالك ثم المحاولة مرّة أخرى. - أرسلْ + إرسال إيقاف وحذف @@ -6962,15 +6962,15 @@ هذا الحقل مطلوب. - توجد قصة بهذا الاسم مسبقا. + توجد قصة بهذا الاسم مسبقًا. - اختيار الجميع + اختيار الكُل - يُرجى اختيار صنف قصتك + اختر نوع قصتك قصة مُخصصة جديدة - ظاهرة فقط لأفراد معينين + ظاهرة فقط لأفراد مُعيَّنين قصة المجموعة @@ -6978,7 +6978,7 @@ اختيار المجموعات - تم النسخ إلى الحافظة + تمَّ النسخ إلى الحافظة الاطلاع على المزيد @@ -6990,11 +6990,11 @@ لا يوجد اتصال بشبكة الإنترنت - لقد تعذر تحميل المحتوى + تعذَّر تحميل المحتوى قصة مُرسَلة - لقد فشل إرسال القصة + فشل إرسال القصة عرض القصة @@ -7006,7 +7006,7 @@ - لقد تفاعلت مع قصة %1$s + تفاعلتَ مع قصة %1$s تفاعَلَ مع قصتك @@ -7014,20 +7014,20 @@ - رؤية المزيد + عرض المزيد زيارة الرابط %1$s · لمدة %2$d أيام - %1$s · لمدة %2$d أيام - %1$s · لمدة %2$d أيام + %1$s · لمدة %2$d يوم + %1$s · لمدة %2$d يومين %1$s · لمدة %2$d أيام - %1$s · لمدة %2$d يوم - %1$s · لمدة %2$d أيام + %1$s · لمدة %2$d يومًا + %1$s · لمدة %2$d أيامٍ - تبرّع لصديق + تبرَّع نيابةً عن صديق ادعم سيجنال عن طريق تقديم تبرّع إلى صديق أو فرد من العائلة يستخدم سيجنال. سَيتلقون شارة لِعرضها على حسابهم الشخصي لـ %1$d يوم @@ -7040,43 +7040,43 @@ التالي - اِختر مُستلم + اختر مُستلِم - تأكيد التبرّع + تأكيد التبرُّع إرسال إلى - سيتم إخطار المُستلم بالتبرّع في رسالة مباشرة منك إليه. أضف رسالتك الخاصة أدناه. + سيتمُّ إخطار المُستلم بالتبرُّع في رسالة مباشرة منك إليه. أضِف رسالتك الخاصة أدناه. - التبرع لمرة واحدة + التبرُّع لمرّة واحدة إضافة نص - جارٍ التحقّق من المستلم … + جارٍ التحقّق من المُستلِم … - قدّم %1$s تبرّعًا لك + قدَّم %1$s تبرُّعًا لك - شكراً لدعمك! + شكرًا لدعمك! - قدّم %1$s تبرّعًا لسيجنال نيابة عنك! أظهر دعمك لسيجنال على حسابك الشخصي. + قدَّم %1$s تبرّعًا لسيجنال نيابةً عنك! أظهِر دعمك لسيجنال على حسابك الشخصي. - قدّمت تبرّعًا لسيجنال نيابة عن %1$s. سَيتم منحهم خيار إظهار دعمهم على حسابهم الشخصي. + قدّمتَ تبرُّعًا لسيجنال نيابة عن %1$s. سَيتم منحهم خيار إظهار دعمهم على حسابهم الشخصي. - قبول الهدية + استخدام الشارة ليس الآن جارٍ استرداد شارة… - قدّمت تبرّعًا لسيجنال نيابة عن %1$s. سَيتم منحهم خيار إظهار دعمهم على حسابهم الشخصي. + قدّمتَ تبرُّعًا لسيجنال نيابةً عن %1$s. سَيتم منحهم خيار إظهار دعمهم على حسابهم الشخصي. انتهت صلاحية شارتك انتهت صلاحية شارتك، ولم تعد مرئية للآخرين على حسابك الشخصي. - للاستمرار في استخدام تكنولوجيا تمّ إنشاؤها من أجلك، يُرجى النظر في أن تصبح داعماً بشكل شهري. + للاستمرار في دعم التكنولوجيا التي تمَّ إنشاؤها من أجلك، يُرجى التفكير في أن تصبح داعمًا شهريًا. - قدّم تبرعًا شهريًا + قدِّم تبرُّعًا شهريًا ليس الآن @@ -7092,12 +7092,12 @@ - قصة جماعية · %1$d مشاهد - قصة جماعية · مشاهد واحد - قصة جماعية · %1$d مشاهدين - قصة جماعية · %1$d مشاهدين - قصة جماعية · %1$d مشاهدًا - قصة جماعية · %1$d مشاهد + قصة المجموعة· %1$d مشاهد + قصة المجموعة · مشاهد واحد + قصة المجموعة· %1$d مشاهدين + قصة المجموعة· %1$d مشاهدين + قصة المجموعة· %1$d مشاهدًا + قصة المجموعة· %1$d مشاهدٍ @@ -7114,8 +7114,8 @@ %1$s · %2$d مشاهد %1$s · %2$d مشاهدين %1$s · %2$d مشاهدين - %1$s · %2$d مشاهدين - %1$s · %2$d مشاهدين/مشاهد + %1$s · %2$d مشاهدًا + %1$s · %2$d مشاهدٍ @@ -7127,21 +7127,21 @@ %1$s · %2$d مُستبعد - انقر لرؤية مشاهديك + انقر لاختيار مشاهديك - إعدادات القصص + إعدادات القصة إزالة القصة حذف القصة - حذف قصة المجموعة؟ + هل ترغبُ بحذف قصة المجموعة؟ سَيتسبب هذا في إزالة القصة من هذه اللائحة. يُمكنك مواصلة مشاهدة القِصص من هذه المجموعة. إزالة - حذف القصة؟ + هل ترغبُ بحذف القصة؟ احذف القصة المخصصة \"%1$s\"؟ @@ -7159,47 +7159,47 @@ %1$d ساعات مُتبقيّة %1$d ساعة مُتبقيّة - %1$d ساعات مُتبقيّة - %1$d ساعات مُتبقيّة - %1$d ساعات مُتبقيّة - %1$d ساعات مُتبقيّة + %1$d ساعتان مُتبقيّتان + %1$d ساعاتٍ مُتبقيّة + %1$d ساعةً مُتبقيّة + %1$d ساعةٍ مُتبقيّة %1$d دقائق مُتبقيّة %1$d دقيقة مُتبقيّة - %1$d دقائق مُتبقيّة + %1$d دقيقتان مُتبقيّتان %1$d دقائق مُتبقيّة - %1$d دقائق مُتبقيّة - %1$d دقائق مُتبقيّة + %1$d دقيقةً مُتبقيّة + %1$d دقيقةٍ مُتبقيّة انتهت الصلاحية - المس للتقدّم + انقر للتقدُّم - اسحب للأعلى للتجاهل + اسحب للأعلى للتخطي اسحب لليمين للمُغادرة - عُلم + تم فتح قائمة السياق - %1$s . تم التحقق منه + %1$s . تمَّ التحقُّق منه - متحقق منه + مُتحقَّق منه - تغييرات رقم الأمان + التغييرات في رقم الأمان - قد يكون هؤلاء الأشخاص أعادوا تثبيت سيجنال أو غيّروا أجهزتهم. انقر على مُستلم ما للتأكد من رقم الأمان الجديد. هذا الإجراء اختياري. + قد يكون هؤلاء الأشخاص أعادوا تثبيت سيجنال أو غيّروا أجهزتهم. انقر على مُستلِم ما لتأكيد رقم الأمان الجديد. هذا الإجراء اختياري. - تحقق من رقم الأمان + التحقُّق من رقم الأمان - تم التحقق من رقم الأمان + تمَّ التحقُّق من رقم الأمان - تمت مراجعة كل جهات الاتصال، انقر إرسال للمواصلة. + تمَّت مراجعة كل جهات الاتصال. انقر إرسال للمتابعة. %1$d جهة اتصال قد يكونوا أعادوا تثبيت سيجنال أو غيّروا أجهزتهم. يُمكنك مراجعة أرقام الأمان الخاصة بهم أو المواصلة للإرسال. @@ -7210,9 +7210,9 @@ %1$d جهة اتصال قد يكونوا أعادوا تثبيت سيجنال أو غيّروا أجهزتهم. يمكنك مراجعة أرقام الأمان الخاصة بهم أو المواصلة للإرسال. - تحقق من رقم اﻷمان + تحقَّق من رقم اﻷمان - الحذف من القصة + حذف من القصة أرسل على كل حال @@ -7964,9 +7964,9 @@ الموافقة - Approve all + موافقة على الكل - Deny all + رفض الكل تفعيل إشعارات ملء الشاشة؟ @@ -8151,7 +8151,7 @@ إذا تخطّيت الاستعادة، يُمكن تنزيل الوسائط والمرفقات المتبقية في نسختك الاحتياطية في وقتٍ لاحق عند توفُّر مساحة التخزين. - Backup failed + فشل النسخ الاحتياطي An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -8192,7 +8192,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + اعرف المزيد @@ -8663,13 +8663,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + افتح سيجنال على جهازك القديم Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + تخطّى الاسترجاع diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index e0a6691778..3395d111f4 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -1012,15 +1012,15 @@ Köçürmədən davam et - Edit name + Adı redaktə et Edit device name - Device name + Cihaz adı - Save + Saxla Device name updated @@ -1380,7 +1380,7 @@ Ehtiyat nüsxə təfərrüatları əldə edilir… - Skip restore + Bərpanı ötür Adım çəkiləndə bildir @@ -4330,7 +4330,7 @@ Hesabı köçür Ötür - Skip restore + Bərpanı ötür Çat nüsxələri Hesabı köçür Hesabınızı yeni bir Android cihazına köçürün @@ -7328,9 +7328,9 @@ Təsdiqlə - Approve all + Hamısını qəbul et - Deny all + Hamısından imtina et Tam ekran bildirişləri aktivləşdirilsin? @@ -7507,7 +7507,7 @@ Bərpaetməni ötürsəniz, ehtiyat nüsxənizdə qalan media fayl və qoşmalar daha sonra cihaz yaddaşına kifayət qədər boş yer olduqda endirilə bilər. - Backup failed + Nüsxələnmədi An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Daha ətraflı @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Signal-ı köhnə cihazınızda açın Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Bərpanı ötür diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index adad2e0a1c..8c39c8c88a 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -1012,15 +1012,15 @@ Продължаване без прехвърляне - Edit name + Редактиране на име Edit device name - Device name + Име на устройството - Save + Запазване Device name updated @@ -1380,7 +1380,7 @@ Извличане на данни за резервно копие… - Skip restore + Пропуснете възстановяването Уведоми ме за споменавания @@ -4330,7 +4330,7 @@ Прехвърляне на акаунт Пропусни - Skip restore + Пропуснете възстановяването Архив на чатовете Прехвърляне на акаунт Прехвърляне на акаунта на ново устройство с Android @@ -7328,9 +7328,9 @@ Одобряване - Approve all + Одобряване на всички - Deny all + Отхвърляне на всички Включване на известията на цял екран? @@ -7507,7 +7507,7 @@ Ако пропуснете възстановяването, останалите прикачени файлове и мултимедия в резервното ви копие ще бъдат изтеглени по-нататък, когато има налично пространство за съхранение. - Backup failed + Архивирането не бе успешно An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Научете повече @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Отворете Signal на старото ви устройство Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Пропуснете възстановяването diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 87021b3d7a..685cac9a4d 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -1012,15 +1012,15 @@ ট্রান্সফার না করে চালিয়ে যান - Edit name + নাম এডিট করুন Edit device name - Device name + ডিভাইস এর নাম - Save + সেভ করুন Device name updated @@ -1380,7 +1380,7 @@ ব্যাকআপের তথ্য আনা হচ্ছে… - Skip restore + পুনরুদ্ধার এড়িয়ে যান মেনশন করা হলে আমাকে অবহিত করুন @@ -4330,7 +4330,7 @@ অ্যাকাউন্ট স্থানান্তর করুন বাদ দিয়ে যান - Skip restore + পুনরুদ্ধার এড়িয়ে যান চ্যাট ব্যাকআপ অ্যাকাউন্ট স্থানান্তর করুন একটি নতুন অ্যান্ড্রয়েড ডিভাইসে অ্যাকাউন্ট স্থানান্তর করুন @@ -7328,9 +7328,9 @@ অনুমোদন - Approve all + সবগুলো অনুমোদন করুন - Deny all + সবগুলো প্রত্যাখ্যান করুন পূর্ণ স্ক্রীন নোটিফিকেশন চালু করবেন? @@ -7507,7 +7507,7 @@ যদি আপনি আপনার ব্যাকআপে থাকা অবশিষ্ট মিডিয়া এবং সংযুক্তিগুলো পুনর্বহাল করা এড়িয়ে যান তবে পরে যখন স্টোরেজ স্পেস পাওয়া যাবে তখন সেগুলো ডাউনলোড করা যাবে। - Backup failed + ব্যাকআপ ব্যর্থ হয়েছে An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + আরো জানুন @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + আপনার পুরানো ডিভাইসে Signal খুলুন Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + পুনরুদ্ধার এড়িয়ে যান diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index f67fdc7973..ec1bee23d7 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -1052,15 +1052,15 @@ Nastavite bez prijenosa - Edit name + Uredi ime Edit device name - Device name + Naziv uređaja - Save + Pohrani Device name updated @@ -1456,7 +1456,7 @@ Preuzimanje detalja sigurnosne kopije… - Skip restore + Preskoči obnovu Obavijesti me kad me neko spomene @@ -4552,7 +4552,7 @@ Prenesi račun Preskoči - Skip restore + Preskoči obnovu Rezervne kopije chata Prenesi račun Prenesite račun na novi Android uređaj @@ -7646,9 +7646,9 @@ Odobri - Approve all + Odobri sve - Deny all + Odbij sve Uključiti obavještenja preko cijelog ekrana? @@ -7829,7 +7829,7 @@ Ako preskočite obnovu, preostali medijski sadržaj i prilozi u vašoj sigurnosnoj kopiji mogu se preuzeti kasnije kada prostor za pohranu postane dostupan. - Backup failed + Neuspjelo kreiranje rezervne kopije An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7870,7 +7870,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Saznaj više @@ -8329,13 +8329,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Otvorite Signal na svom starom uređaju Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Preskoči obnovu diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index ef33712afa..92a839f532 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -1012,15 +1012,15 @@ Continuar sense transferir - Edit name + Editar nom Edit device name - Device name + Nom del dispositiu - Save + Desar Device name updated @@ -1380,7 +1380,7 @@ Obtenint detalls de la còpia de seguretat… - Skip restore + Ometre la restauració Notifica\'m les mencions @@ -4330,7 +4330,7 @@ Transfereix el compte Omet - Skip restore + Ometre la restauració Còpies de seguretat de xats Transfereix el compte Transfereix el compte a un dispositiu d\'Android nou @@ -7328,9 +7328,9 @@ Aprova-la - Approve all + Aprovar-les totes - Deny all + Denegar-les totes Activar les notificacions de pantalla completa? @@ -7507,7 +7507,7 @@ Si omets la restauració, podràs descarregar la resta d\'arxius multimèdia de la teva còpia de seguretat quan s\'alliberi espai. - Backup failed + Ha fallat la còpia de seguretat. An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Més informació @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Obre Signal al teu dispositiu antic Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Ometre la restauració diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index ef58165c4c..2fa4cd682b 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -1052,15 +1052,15 @@ Pokračovat bez přenosu - Edit name + Upravit jméno Edit device name - Device name + Název zařízení - Save + Uložit Device name updated @@ -1456,7 +1456,7 @@ Načítají se údaje o zálohování… - Skip restore + Přeskočit obnovení Upozornit mne na zmínky @@ -4552,7 +4552,7 @@ Přenést účet Přeskočit - Skip restore + Přeskočit obnovení Zálohy chatů Přenést účet Přenést účet na nové Android zařízení @@ -7646,9 +7646,9 @@ Schválit - Approve all + Schválit všechny - Deny all + Zamítnout všechny Zapnout oznámení na celou obrazovku? @@ -7829,7 +7829,7 @@ Pokud obnovu přeskočíte, můžete zbývající média a přílohy v záloze stáhnout později, až bude k dispozici úložný prostor. - Backup failed + Zálohování selhalo An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7870,7 +7870,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Zjistit více @@ -8329,13 +8329,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Otevřete aplikaci Signal ve starém zařízení Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Přeskočit obnovení diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 9f152a2ed9..9cc6671a0d 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -1012,15 +1012,15 @@ Fortsæt uden at overføre - Edit name + Redigér navn Edit device name - Device name + Enhedsnavn - Save + Gem Device name updated @@ -1380,7 +1380,7 @@ Henter sikkerhedskopieringsoplysninger… - Skip restore + Spring gendannelse over Underret mig ved omtaler @@ -4330,7 +4330,7 @@ Overfør konto Spring over - Skip restore + Spring gendannelse over Sikkerhedskopier af chats Overfør konto Overfør konto til en ny Android-enhed @@ -7328,9 +7328,9 @@ Godkend - Approve all + Godkend alle - Deny all + Afvis alle Slå notifikationer på fuld skærm til? @@ -7507,7 +7507,7 @@ Hvis du springer gendannelse over, kan du downloade medierne og de vedhæftede filer i sikkerhedskopien senere, når der er tilgængelig lagerplads. - Backup failed + Sikkerhedskopiering mislykkedes An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Få mere at vide @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Åbn Signal på din gamle enhed Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Spring gendannelse over diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5697313f53..1fc752e7a1 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1012,15 +1012,15 @@ Ohne Übertragung fortfahren - Edit name + Name bearbeiten Edit device name - Device name + Gerätename - Save + Speichern Device name updated @@ -1380,7 +1380,7 @@ Backup-Details abrufen… - Skip restore + Wiederherstellen überspringen Mich bei Erwähnungen benachrichtigen @@ -4330,7 +4330,7 @@ Konto übertragen Überspringen - Skip restore + Wiederherstellen überspringen Chat-Sicherungen Konto übertragen Konto auf ein neues Android-Gerät übertragen @@ -7328,9 +7328,9 @@ Bestätigen - Approve all + Alle bestätigen - Deny all + Alle ablehnen Vollbildbenachrichtigungen aktivieren? @@ -7507,7 +7507,7 @@ Wenn du »Wiederherstellen« überspringst, kannst du die verbleibenden Medien und Anhänge in deinem Backup zu einem späteren Zeitpunkt herunterladen, sobald wieder Speicherplatz verfügbar ist. - Backup failed + Datensicherung gescheitert An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Mehr erfahren @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Öffne Signal auf deinem alten Gerät Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Wiederherstellen überspringen diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 222e406df0..944350d0d7 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -1012,15 +1012,15 @@ Συνέχεια χωρίς μεταφορά - Edit name + Επεξεργασία ονόματος Edit device name - Device name + Όνομα συσκεύης - Save + Αποθήκευση Device name updated @@ -1380,7 +1380,7 @@ Λήψη στοιχείων αντιγράφων ασφαλείας… - Skip restore + Παράλειψη επαναφοράς Να ειδοποιούμαι για αναφορές @@ -4330,7 +4330,7 @@ Μεταφορά λογαριασμού Παράλειψη - Skip restore + Παράλειψη επαναφοράς Αντίγραφα ασφαλείας συνομιλιών Μεταφορά λογαριασμού Μεταφορά λογαριασμού σε νέα συσκευή Android @@ -7328,9 +7328,9 @@ Έγκριση - Approve all + Έγκριση όλων - Deny all + Απόρριψη όλων Ενεργοποίηση ειδοποιήσεων σε πλήρη οθόνη; @@ -7507,7 +7507,7 @@ Αν παραλείψεις την επαναφορά, τα υπόλοιπα πολυμέσα και τα συνημμένα στο αντίγραφο ασφαλείας σου μπορούν να ληφθούν αργότερα, όταν θα υπάρχει διαθέσιμος χώρος αποθήκευσης. - Backup failed + Ή δημιουργία αντίγραφου ασφαλείας απέτυχε An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Μάθε περισσότερα @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Άνοιξε το Signal στη παλιά σου συσκευή Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Παράλειψη επαναφοράς diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 821a856722..c119712443 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1012,15 +1012,15 @@ Continuar sin transferir - Edit name + Editar nombre Edit device name - Device name + Nombre del dispositivo - Save + Guardar Device name updated @@ -1380,7 +1380,7 @@ Obteniendo detalles de la copia de seguridad… - Skip restore + Omitir restauración Notificarme cuando alguien me mencione @@ -4330,7 +4330,7 @@ Transferir cuenta Omitir - Skip restore + Omitir restauración Copias de seguridad de los chats Transferir cuenta Transferir cuenta a un dispositivo Android nuevo @@ -7328,9 +7328,9 @@ Aprobar - Approve all + Aprobar todas - Deny all + Denegar todas ¿Activar las notificaciones de pantalla completa? @@ -7507,7 +7507,7 @@ Si omites la restauración, podrás descargar el resto de los archivos adjuntos y multimedia de tu copia de seguridad cuando se libere espacio. - Backup failed + Error en la copia de seguridad An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Más información @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Abre Signal en tu dispositivo anterior Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Omitir restauración diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 9042a91127..f86a1a2e93 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -1012,15 +1012,15 @@ Jätka ilma andmeid üle kandmata - Edit name + Muuda nime Edit device name - Device name + Seadme nimi - Save + Salvesta Device name updated @@ -1380,7 +1380,7 @@ Varundamidandmete hankimine … - Skip restore + Jäta taastamine vahele Teavita mind mainimise korral @@ -4330,7 +4330,7 @@ Kanna konto üle Jäta vahele - Skip restore + Jäta taastamine vahele Vestluste varukoopiad Kanna konto üle Kanna konto üle uude Android-seadmesse @@ -7328,9 +7328,9 @@ Nõustu - Approve all + Kinnita kõik - Deny all + Keeldu kõigist Kas lülitada sisse täisekraani formaadis teavitused? @@ -7507,7 +7507,7 @@ Kui jätad taastamise vahele, saad oma varukoopias olevat meediat ja manuseid alla laadida hiljem, kui salvestusruumi vabaneb. - Backup failed + Varundamine ebaõnnestus An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Rohkem teavet @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Ava Signal oma vanas seadmes Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Jäta taastamine vahele diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index d052962079..ad12fd7657 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -1012,15 +1012,15 @@ Egin aurrera transferitu gabe - Edit name + Editatu izena Edit device name - Device name + Gailuaren izena - Save + Gorde Device name updated @@ -1380,7 +1380,7 @@ Babeskopiaren xehetasunak lortzen… - Skip restore + Saltatu leheneratzeko aukera Nahi dut aipamenak jakinaraztea @@ -4330,7 +4330,7 @@ Transferitu kontua Saltatu - Skip restore + Saltatu leheneratzeko aukera Txaten babeskopiak Transferitu kontua Transferitu kontua Android gailu berri batera @@ -7328,9 +7328,9 @@ Onartu - Approve all + Onartu guztiak - Deny all + Guztia ukatu Pantaila osoko jakinarazpenak aktibatu nahi dituzu? @@ -7507,7 +7507,7 @@ Leheneratze-prozesua saltatzen baduzu, babeskopiako multimedia-edukia eta eranskinak geroago deskarga ditzakezu, biltegian tokia egitean. - Backup failed + Babeskopiak huts egin du An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Informazio gehiago @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Ireki Signal gailu zaharrean Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Saltatu leheneratzeko aukera diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 2dc18e1791..664a7fd91d 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -1012,15 +1012,15 @@ ادامه بدون انتقال - Edit name + ویرایش نام Edit device name - Device name + نام دستگاه - Save + ذخیره Device name updated @@ -1380,7 +1380,7 @@ در حال دریافت جزئیات نسخه پشتیبان… - Skip restore + رد کردن بازیابی برای اشاره‌ها من را آگاه کن @@ -4330,7 +4330,7 @@ انتقال حساب کاربری رد کردن - Skip restore + رد کردن بازیابی پشتیبان‌های گفتگو انتقال حساب کاربری انتقال حساب کاربری به یک دستگاه اندروید‌ی جدید @@ -7328,9 +7328,9 @@ موافقت - Approve all + تأیید همه - Deny all + رد کردن همه اعلان‌های تمام‌صفحه روشن شود؟ @@ -7507,7 +7507,7 @@ اگر بازیابی رسانه‌ها و پیوست‌های باقیمانده را در پشتیبان‌گیری رد کنید، در آینده وقتی فضای ذخیره‌سازی کافی داشته باشید قابل دانلود است. - Backup failed + پشتیبان‌گیری ناموفق بود An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + اطلاعات بیشتر @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + سیگنال را در دستگاه قدیمی خود باز کنید Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + رد کردن بازیابی diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 6a989320f7..3da5895a06 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -1012,15 +1012,15 @@ Jatka ilman siirtoa - Edit name + Muokkaa nimeä Edit device name - Device name + Laitteen nimi - Save + Tallenna Device name updated @@ -1380,7 +1380,7 @@ Haetaan varmuuskopion tietoja… - Skip restore + Ohita palautus Ilmoita minulle maininnoista @@ -4330,7 +4330,7 @@ Siirrä tili Ohita - Skip restore + Ohita palautus Keskustelujen varmuuskopiot Siirrä tili Siirrä tili uuteen Android-laitteeseen @@ -7328,9 +7328,9 @@ Hyväksy - Approve all + Hyväksy kaikki - Deny all + Hylkää kaikki Otetaanko koko näytön ilmoitukset käyttöön? @@ -7507,7 +7507,7 @@ Jos ohitat palauttamisen, varmuuskopion jäljellä olevat mediat ja liitteet voidaan ladata myöhemmin, kun tallennustilaa vapautuu. - Backup failed + Varmuuskopio epäonnistui An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Lue lisää @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Avaa Signal vanhassa laitteessasi Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Ohita palautus diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index d6ea56372c..c788a5a34d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1012,15 +1012,15 @@ Continuer sans transférer - Edit name + Renommer Edit device name - Device name + Nom de l’appareil - Save + Enregistrer Device name updated @@ -1380,7 +1380,7 @@ Récupération des infos de la sauvegarde… - Skip restore + Ignorer la restauration Me prévenir quand on me mentionne @@ -2398,7 +2398,7 @@ Aucun groupe en commun - Examinez les demandes avec attention + Examinez attentivement les demandes %1$d groupe en commun @@ -3083,7 +3083,7 @@ Le groupe a atteint sa taille maximale Les groupes Signal ne peuvent pas accueillir plus de %1$d membres. Votre groupe a atteint la taille maximale recommandée - La performance des groupes Signal est optimale avec un maximum de %1$d membres. Si vous dépassez cette limite, l\'envoi et la réception des messages risquent de s\'en trouver ralentis. + Pour un fonctionnement optimal, les groupes Signal peuvent accueillir jusqu\'à %1$d membres. Si vous dépassez cette limite, l\'envoi et la réception des messages risquent de s\'en trouver ralentis. %1$d membre %1$d membres @@ -3104,7 +3104,7 @@ Autorisez l’accès à vos contacts. La liste de vos contacts est chiffrée et le service Signal n’y a pas accès. - Signal a besoin d’accéder à vos contacts afin de les afficher. + Pour afficher vos contacts, Signal doit accéder à l\'application Contacts. Afficher les contacts @@ -3120,19 +3120,19 @@ Message Signal - Texto non sécurisé - Message multimédia non sécurisé + SMS non sécurisé + MMS non sécurisé SIM %1$d Envoyer - Rédaction d’un message - Afficher/masquer le clavier des émojis + Rédiger un message + Afficher ou masquer le clavier des émojis Miniature de la pièce jointe - Afficher/masquer le tiroir de l’appareil photo à basse résolution + Joindre une photo ou une vidéo via l\'appareil photo ou la galerie Enregistrer et envoyer un message audio en pièce jointe Verrouiller l’enregistrement de pièces jointes au format audio - Le message n’a pas pu être envoyé. Veuillez vérifier votre connexion et réessayer. + Impossible d\'envoyer le message. Veuillez vérifier votre connexion et réessayer. Faire glisser pour annuler @@ -3144,7 +3144,7 @@ Non envoyé. - En attente d’approbation + En attente d\'approbation Remis Message lu @@ -3158,15 +3158,15 @@ Rejoindre l’appel Rappeler - Revenir à l’appel - L’appel est complet + Revenir à l\'appel + L\'appel est complet Inviter des amis - Activer les notifications d’appels - Mettre le contact à jour + Activer les notifications d\'appel + Mettre à jour le contact Bloquer la demande Aucun groupe en commun. Examinez attentivement les demandes avant de les accepter. - Aucun contact dans ce groupe. Examinez les demandes avec attention. + Aucun contact dans ce groupe. Examinez attentivement les demandes. Afficher Lorsque vous enverrez des messages à ce contact, le délai avant disparition des messages éphémères sera défini sur %1$s. @@ -3183,7 +3183,7 @@ Mettre à jour - Lecture … Mettre en pause + Lire ou mettre en pause Télécharger @@ -3194,7 +3194,7 @@ Média à vue unique Sticker Vous - Le message original est introuvable + Le message d\'origine est introuvable Story · %1$s @@ -3220,19 +3220,19 @@ Accepter Appeler quand même Rejoindre l’appel - Poursuivre l’appel - Quitter l’appel + Poursuivre l\'appel + Quitter l\'appel Les contacts suivants ont peut-être réinstallé Signal ou changé d\'appareil. Pour assurer la confidentialité de vos échanges, vérifiez les numéros de sécurité associés à ces personnes. Afficher - Confirmé précédemment + Précédemment confirmé - Notifications d’appels activées. - Activer les notifications d’appels + Notifications d\'appel activées. + Activer les notifications d\'appel Autoriser l\'activité en arrière-plan - Tout semble en ordre maintenant ! - Pour recevoir les notifications d\'appel, appuyez ici et activez l\'option \"Notifications\". + Tout est OK ! + Pour recevoir des notifications d\'appel, appuyez ici et activez l\'option \"Notifications\". Pour recevoir les notifications d’appel, touchez cette option, activez les notifications et vérifiez que le son et les pop-ups sont bien activés. Pour recevoir les notifications d\'appel, appuyez ici et autorisez l\'activité en arrière-plan sous Paramètres > Batterie. @@ -3260,39 +3260,39 @@ Désactivés - %1$d seconde - %1$d secondes + %1$d seconde + %1$d secondes - %1$d s + %1$d s - %1$d minute - %1$d minutes + %1$d minute + %1$d minutes - %1$d min + %1$d min - %1$d heure - %1$d heures + %1$d heure + %1$d heures - %1$d h + %1$d h - %1$d jour - %1$d jours + %1$d jour + %1$d jours - %1$d j + %1$d j - %1$d semaine - %1$d semaines + %1$d semaine + %1$d semaines - %1$d sem. + %1$d sem. %1$s %2$s @@ -3305,12 +3305,12 @@ Les numéros de sécurité associés à %1$s, %2$s et %3$s ont changé et ne sont plus confirmés. Cela peut signifier que quelqu\'un tente d\'intercepter vos communications. Mais il est aussi possible que ces contacts aient tout simplement réinstallé Signal. Le numéro de sécurité associé à %1$s vient de changer. - Vos numéros de sécurité avec %1$s et %2$s viennent de changer. - Vos numéros de sécurité avec %1$s, %2$s et %3$s viennent de changer. + Les numéros de sécurité associés à %1$s et %2$s viennent de changer. + Les numéros de sécurité associés à %1$s, %2$s et %3$s viennent de changer. -   autre - %1$d autres + %1$d autre + %1$d autres @@ -3330,7 +3330,7 @@ Renvoyer - L’estampille temporelle d’envoi a été copiée dans le presse-papiers. + Heure d\'envoi copiée dans le presse-papiers. Vos nouvelles stories s’afficheront ici. @@ -3345,7 +3345,7 @@ Déverrouiller - Signal exige que les paramètres de messages multimédias remettent les messages multimédias et de groupe en utilisant votre opérateur de réseau mobile. Votre appareil n’offre pas ces informations, ce qui arrive parfois pour les appareils verrouillés ou d’autres configurations restrictives. + Signal requiert que les paramètres des MMS remettent les messages multimédias et de groupe via votre opérateur de réseau mobile. Ces informations ne sont pas disponibles sur votre appareil, ce qui arrive parfois avec des appareils verrouillés ou d\'autres configurations restrictives. Pour envoyer des messages multimédias ou de groupe, touchez \"OK\" et définissez les paramètres demandés. Pour trouver les paramètres MMS de votre opérateur, recherchez son APN (nom du point d’accès). Vous n’aurez à le faire qu’une seule fois. @@ -3362,7 +3362,7 @@ À propos - Écrivez quelques mots à votre sujet… + Présentez-vous en quelques mots… %1$d/%2$d Exprimez-vous librement Chiffré @@ -4330,7 +4330,7 @@ Transfert de compte Ignorer - Skip restore + Ignorer la restauration Sauvegarde des conversations Transfert de compte Transférer le compte vers un nouvel appareil Android @@ -4960,7 +4960,7 @@ Vous n\'avez aucun groupe en commun avec cette personne. Pour éviter les messages indésirables, examinez attentivement les demandes avant de les accepter. - Aucun de vos contacts ou des personnes avec lesquelles vous discutez n’est dans ce groupe. Examinez attentivement les demandes avant d’accepter pour éviter les messages indésirés. + Aucun de vos contacts ne fait partie de ce groupe. Pour éviter les messages indésirables, examinez attentivement les demandes avant de les accepter. À propos des invitations par message OK @@ -5282,7 +5282,7 @@ Si vous activez cette option et démarrez de nouvelles conversations, les nouveaux messages que vous enverrez et recevrez dans ces conversations disparaîtront une fois lus. Si vous activez cette option, les nouveaux messages que vous enverrez et recevrez dans cette conversation disparaîtront une fois lus. - Désactivé + Désactivés 4 semaines 1 semaine 24 heures @@ -5866,7 +5866,7 @@ Cet utilisateur doit mettre à niveau Signal pour pouvoir recevoir des dons. - Votre don ne peut pas être envoyé à cause d\'une erreur de réseau. Vérifiez votre connexion et réessayez. + Impossible d\'envoyer votre don en raison d\'une erreur réseau. Veuillez vérifier votre connexion et réessayer. Votre don iDEAL n’a pas pu être traité. Essayez un autre mode de paiement ou contactez votre banque pour plus d’informations. @@ -6208,7 +6208,7 @@ Ajouter - Affichage désactivé + Confirmations de vue désactivées %1$s %2$s @@ -6373,7 +6373,7 @@ - Impossible d’envoyer la story. Vérifiez votre connexion et réessayez. + Impossible d\'envoyer la story. Vérifiez votre connexion et réessayez. Envoyer @@ -6674,7 +6674,7 @@ Stories - Confirmation de vue + Confirmations de vue Vous êtes informé lorsque vos contacts voient vos stories. Si cette option est désactivée, vous ne saurez pas si des spectateurs les ont vues. @@ -7062,8 +7062,8 @@ Échec de la suppression. - Impossible de supprimer le lien. Veuillez vérifier votre connexion et réessayer. - Impossible de supprimer tous les liens d’appels. Veuillez vérifier votre connexion et réessayer. + Impossible de supprimer le lien d\'appel. Veuillez vérifier votre connexion et réessayer. + Impossible de supprimer tous les liens d\'appel. Veuillez vérifier votre connexion et réessayer. Historique des appels supprimé @@ -7328,9 +7328,9 @@ Approuver - Approve all + Tout accepter - Deny all + Tout refuser Activer les notifications en plein écran ? @@ -7507,7 +7507,7 @@ Si vous choisissez d\'ignorer la restauration, vous pourrez télécharger ultérieurement les pièces jointes et médias stockés dans votre sauvegarde, lorsque l\'espace de stockage sera suffisant. - Backup failed + La sauvegarde a échoué An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + En savoir plus @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Ouvrez Signal sur votre ancien téléphone Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Ignorer la restauration diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index 209b12d17e..410598b2c1 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -1072,15 +1072,15 @@ Lean leis gan aistriú - Edit name + Ainm a Chur in Eagar Edit device name - Device name + Ainm an ghléis - Save + Sábháil Device name updated @@ -1494,7 +1494,7 @@ Sonraí cúltaca á bhfáil… - Skip restore + Scipeáil aischur Cuir Tráchtanna in iúl dom @@ -4663,7 +4663,7 @@ Cuntas a Aistriú Léim thar seo - Skip restore + Scipeáil aischur Cúltacaí na gcomhráite Cuntas a Aistriú Aistrigh cuntas chuig gléas nua Android @@ -7805,9 +7805,9 @@ Ceadaigh í - Approve all + Faomh gach ceann - Deny all + Diúltaigh do gach ceann Cas air fógraí lánscáileáin? @@ -7990,7 +7990,7 @@ Má scipeálann tú aischur beidh tú in ann na meáin agus ceangaltáin i do chúltaca a íoslódáil níos déanaí nuair a chuirfear spás stórála ar fáil. - Backup failed + Theip ar an gCúltaca An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -8031,7 +8031,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Tuilleadh faisnéise @@ -8496,13 +8496,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Oscail Signal ar do sheanghléas Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Scipeáil aischur diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index ebb21e154b..9ae2741f0b 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -1012,15 +1012,15 @@ Continuar sen transferencia - Edit name + Editar nome Edit device name - Device name + Nome do dispositivo - Save + Gardar Device name updated @@ -1380,7 +1380,7 @@ Buscando información da copia de seguranza… - Skip restore + Saltar restauración Notificarme as mencións @@ -4330,7 +4330,7 @@ Transferir conta Omitir - Skip restore + Saltar restauración Copias de seguranza das conversas Transferir conta Transferir a conta un novo dispositivo Android @@ -7328,9 +7328,9 @@ Aprobar - Approve all + Aprobar todas - Deny all + Rexeitar todas Activar as notificacións de pantalla completa? @@ -7507,7 +7507,7 @@ Se non completas a restauración, os arquivos e anexos restantes da túa copia de seguridade estarán dispoñibles para descargar máis tarde, cando se libere espazo de almacenamento. - Backup failed + Erro na copia de seguranza An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Máis información @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Abre Signal no antigo dispositivo Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Saltar restauración diff --git a/app/src/main/res/values-gu/strings.xml b/app/src/main/res/values-gu/strings.xml index 0f26be14f4..55d4710829 100644 --- a/app/src/main/res/values-gu/strings.xml +++ b/app/src/main/res/values-gu/strings.xml @@ -1012,15 +1012,15 @@ ટ્રાન્સફર કર્યા વિના ચાલુ રાખો - Edit name + નામમાં ફેરફાર કરો Edit device name - Device name + ડિવાઇસનું નામ - Save + સેવ કરો Device name updated @@ -1380,7 +1380,7 @@ બેકઅપ વિગતો મેળવી રહ્યાં છીએ… - Skip restore + રિસ્ટોર કરવાનું સ્કિપ કરો મને ઉલ્લેખો માટે સૂચિત કરો @@ -4330,7 +4330,7 @@ એકાઉન્ટ ટ્રાન્સફર કરો અવગણો - Skip restore + રિસ્ટોર કરવાનું સ્કિપ કરો ચેટ બૅકઅપ એકાઉન્ટ ટ્રાન્સફર કરો નવા Android ડિવાઇસમાં એકાઉન્ટ ટ્રાન્સફર કરો @@ -7328,9 +7328,9 @@ મંજૂર - Approve all + બધી મંજૂર કરો - Deny all + બધી નામંજૂર કરો ફૂલ સ્ક્રીન નોટિફિકેશન ચાલુ કરવા છે? @@ -7507,7 +7507,7 @@ જો તમે રિસ્ટોર કરવાનું છોડી દો છો તો તમારા બેકઅપમાંના બાકીના મીડિયા અને અટેચમેન્ટ જ્યારે સ્ટોરેજ સ્પેસ ઉપલબ્ધ થાય ત્યારે પછીના સમયે ડાઉનલોડ કરી શકાય છે. - Backup failed + બૅકઅપ નિષ્ફળ An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + વધુ જાણો @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + તમારા જૂના ડિવાઇસ પર Signal ખોલો Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + રિસ્ટોર કરવાનું સ્કિપ કરો diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 2fffea760b..9791dfa92c 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -1012,15 +1012,15 @@ बिना ट्रांसफ़र किए जारी रखें - Edit name + नाम संपादित करें Edit device name - Device name + डिवाइस का नाम - Save + सेव Device name updated @@ -1380,7 +1380,7 @@ बैकअप जानकारी फ़ेच की जा रही है… - Skip restore + रीस्टोर करना छोड़ें मेंछन किए जाने पर मुझे सूचित करें @@ -4330,7 +4330,7 @@ खाता ट्रांसफ़र करें छोड़ दे - Skip restore + रीस्टोर करना छोड़ें बैकअप चैट करें खाता ट्रांसफ़र करें खाते को किसी नए Android डिवाइस पर ट्रांसफ़र करें @@ -7328,9 +7328,9 @@ स्वीकार करें - Approve all + सभी को स्वीकार करें - Deny all + सभी को अस्वीकार करें फुल स्क्रीन अधिसूचना चालू करें? @@ -7507,7 +7507,7 @@ अगर आप बाकी के मीडिया और अटैचमेंट को अपने बैकअप में स्टोर नहीं करते हैं, तो स्टोरेज स्पेस उपलब्ध होने पर उन्हें बाद में डाउनलोड किया जा सकता है। - Backup failed + बैकअप विफल An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + अधिक जानें @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + अपने पुराने डिवाइस पर Signal खोलें Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + रीस्टोर करना छोड़ें diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 34d3292a8b..36cf3369d5 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -1052,15 +1052,15 @@ Nastavi bez prijenosa - Edit name + Uredite ime Edit device name - Device name + Naziv uređaja - Save + Spremi Device name updated @@ -1456,7 +1456,7 @@ Učitavanje podataka o sigurnosnoj kopiji… - Skip restore + Preskoči Obavijesti me samo za Spominjanja @@ -4552,7 +4552,7 @@ Prijenos računa Preskoči - Skip restore + Preskoči Sigurnosne kopije razgovora Prijenos računa Prijenos računa na novi Android uređaj @@ -7646,9 +7646,9 @@ Odobri - Approve all + Odobri sve - Deny all + Odbij sve Uključiti obavijesti preko cijelog zaslona? @@ -7829,7 +7829,7 @@ Ako preskočite ovaj korak, moći ćete preuzeti medijske zapise i privitke spremljene u sigurnosnoj kopiji kada na vašem uređaju bude dostupno dovoljno prostora za pohranu. - Backup failed + Sigurnosno kopiranje nije uspjelo An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7870,7 +7870,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Saznajte više @@ -8329,13 +8329,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Otvorite Signal na starom uređaju Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Preskoči diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 185010ed9f..be685de608 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -1012,15 +1012,15 @@ Folytatás átvitel nélkül - Edit name + Név szerkesztése Edit device name - Device name + Eszköz neve - Save + Mentés Device name updated @@ -1380,7 +1380,7 @@ Biztonsági mentés részleteinek lekérése… - Skip restore + Visszaállítás kihagyása Értesítsen említés esetén @@ -4330,7 +4330,7 @@ Fiók átvitele Kihagyás - Skip restore + Visszaállítás kihagyása Csevegések biztonsági mentése Fiók átvitele Fiók átvitele egy másik Android eszközre @@ -7328,9 +7328,9 @@ Jóváhagyás - Approve all + Összes jóváhagyása - Deny all + Összes elutasítása Bekapcsolod a teljes képernyős értesítéseket? @@ -7507,7 +7507,7 @@ Ha kihagyod a visszaállítást, a biztonsági másolatban lévő fennmaradó médiafájlok és mellékletek letölthetők később, amikor tárhely szabadul fel. - Backup failed + Sikertelen biztonsági mentés An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Tudj meg többet @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Nyisd meg a Signalt a régi eszközödön Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Visszaállítás kihagyása diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index ae851a4b73..8ff5aa42d3 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -992,15 +992,15 @@ Lanjutkan tanpa mentransfer - Edit name + Edit nama Edit device name - Device name + Nama Perangkat - Save + Simpan Device name updated @@ -1342,7 +1342,7 @@ Mengambil detail cadangan … - Skip restore + Lewati pemulihan Beritahu saya ketika ada penyebutan @@ -4219,7 +4219,7 @@ Transfer akun Lewati - Skip restore + Lewati pemulihan Cadangan obrolan Transfer akun Transfer akun ke perangkat Android baru @@ -7169,9 +7169,9 @@ Terima - Approve all + Setujui semua - Deny all + Tolak semua Aktifkan notifikasi layar penuh? @@ -7346,7 +7346,7 @@ Jika Anda melewati pemulihan, media dan lampiran yang masih ada di cadangan dapat diunduh lain waktu ketika ruang penyimpanan telah tersedia. - Backup failed + Cadangan gagal An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7387,7 +7387,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Pelajari selengkapnya @@ -7828,13 +7828,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Buka Signal di perangkat lama Anda Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Lewati pemulihan diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 4dda630d53..ac58070a26 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1012,15 +1012,15 @@ Prosegui senza il trasferimento - Edit name + Modifica nome Edit device name - Device name + Nome del dispositivo - Save + Salva Device name updated @@ -1380,7 +1380,7 @@ Recupero dettagli del backup… - Skip restore + Salta il ripristino Notificami per le menzioni @@ -4330,7 +4330,7 @@ Trasferisci account Salta - Skip restore + Salta il ripristino Backup delle chat Trasferisci account Trasferisci l\'account su un nuovo dispositivo Android @@ -7328,9 +7328,9 @@ Approva - Approve all + Approva tutto - Deny all + Rifiuta tutto Attivare le notifiche a schermo intero? @@ -7507,7 +7507,7 @@ Se scegli di saltare il ripristino, potrai scaricare i media e gli allegati rimanenti nel tuo backup in un secondo momento quando avrai più spazio di archiviazione. - Backup failed + Backup fallito An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Scopri di più @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Apri Signal sul tuo dispositivo precedente Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Salta il ripristino diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 96d3e9c9c2..903b84b31f 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -1052,15 +1052,15 @@ להמשיך בלי להעביר - Edit name + עריכת שם Edit device name - Device name + שם מכשיר - Save + שמירה Device name updated @@ -1456,7 +1456,7 @@ טוענים פרטי גיבוי… - Skip restore + דילוג על שחזור יידע אותי לגבי אזכורים @@ -4552,7 +4552,7 @@ העבר חשבון דלג - Skip restore + דילוג על שחזור גיבויי צ׳אטים העבר חשבון העבר חשבון אל מכשיר Android חדש @@ -7646,9 +7646,9 @@ אשר - Approve all + אישור הכל - Deny all + דחיית הכל להפעיל התראות במסך מלא? @@ -7829,7 +7829,7 @@ בחירה בדילוג על שחזור תגרום לכך שתהיה לך אפשרות להוריד את המדיה והקבצים המצורפים הנותרים בגיבוי שלך במועד מאוחר יותר, כשיתפנה שטח אחסון. - Backup failed + גיבוי נכשל An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7870,7 +7870,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + למידע נוסף @@ -8329,13 +8329,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + יש לפתוח את Signal במכשיר הישן שלך Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + דילוג על שחזור diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index f0b09ac9ad..af566770c1 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -992,15 +992,15 @@ 転送せずに続ける - Edit name + 名前を編集 Edit device name - Device name + 端末名 - Save + 保存 Device name updated @@ -1342,7 +1342,7 @@ バックアップの詳細を取得中… - Skip restore + 復元をスキップする メンションの通知 @@ -4219,7 +4219,7 @@ アカウントの移行 スキップする - Skip restore + 復元をスキップする チャットのバックアップ アカウントの移行 新しいAndroid端末にアカウントを移行します。 @@ -7169,9 +7169,9 @@ 承認する - Approve all + 全員承認する - Deny all + 全員拒否する 全画面通知をオンにしますか? @@ -7346,7 +7346,7 @@ 復元をスキップした場合、バックアップ内の残りのメディアと添付ファイルは、ストレージ容量が確保できたときにダウンロードできます。 - Backup failed + バックアップに失敗しました An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7387,7 +7387,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + 詳しく見る @@ -7828,13 +7828,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + 以前利用していた端末でSignalを開きます Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + 復元をスキップする diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 32ee43e997..ad23c811a2 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -1012,15 +1012,15 @@ გადატანის გარეშე გაგრძელება - Edit name + სახელის შეცვლა Edit device name - Device name + მოწყობილობის სახელი - Save + შენახვა Device name updated @@ -1380,7 +1380,7 @@ მიმდინარეობს სათადარიგო ასლების დეტალების მიღება… - Skip restore + აღდგენის გამოტოვება შემატყობინეთ, როცა მომნიშნავენ @@ -4330,7 +4330,7 @@ მონაცემების გადატანა გამოტოვება - Skip restore + აღდგენის გამოტოვება ჩატის სარეზერვო კოპიები მონაცემების გადატანა გადაიტანე მონაცემები Android-ის ახალ მოწყობილობაში @@ -7328,9 +7328,9 @@ დადასტურება - Approve all + ყველას დადასტურება - Deny all + ყველას უარყოფა გსურს შეტყობინებები სრულ ეკრანზე ჩართო? @@ -7507,7 +7507,7 @@ თუ აღდგენას გამოტოვებ, შეიძლება დარჩენილი მედია ფაილები და დანართები შენს სათადარიგო ასლებში მოგვიანებით ჩამოიტვირთოს, როცა მეხსიერებაში ადგილი გათავისუფლება. - Backup failed + სარეზერვო კოპიების შენახვა ვერ მოხერხდა An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + გაიგე მეტი @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + გახსენი Signal-ი შენს ძველ მობილურში Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + აღდგენის გამოტოვება diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index b615ae112c..e49906c925 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -1012,15 +1012,15 @@ Тасымалдамай жалғастыру - Edit name + Атын өзгерту Edit device name - Device name + Құрылғы аты - Save + Сақтау Device name updated @@ -1380,7 +1380,7 @@ Сақтық көшірме мәліметтері алынып жатыр… - Skip restore + Қалпына келтіру функциясын өткізіп жіберу Менің атым аталғанда хабарландыру алғым келеді @@ -4330,7 +4330,7 @@ Аккаунтты тасымалдау Өткізіп жіберу - Skip restore + Қалпына келтіру функциясын өткізіп жіберу Чаттың резервтік көшірмелері Аккаунтты тасымалдау Аккаунтты жаңа Android құрылғысына тасымалдау @@ -7328,9 +7328,9 @@ Мақұлдау - Approve all + Барлығын мақұлдау - Deny all + Ешбірін қабылдамау Толық экран хабарландыруларын қосу керек пе? @@ -7507,7 +7507,7 @@ Қалпына келтіру процесін өткізіп жіберсеңіз, жад көлемі жеткілікті болғанда, сақтық көшірмедегі қалған мультимедианы және тіркемелерді кейінірек жүктеп алуға болады. - Backup failed + Резервтік көшірме жасалмады An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Толық ақпарат @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Signal қолданбасын ескі құрылғыңызда ашыңыз Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Қалпына келтіру функциясын өткізіп жіберу diff --git a/app/src/main/res/values-km/strings.xml b/app/src/main/res/values-km/strings.xml index 7962ae9ce2..43fe61f09c 100644 --- a/app/src/main/res/values-km/strings.xml +++ b/app/src/main/res/values-km/strings.xml @@ -992,15 +992,15 @@ បន្តដោយមិនចាំបាច់ផ្ទេរ - Edit name + កែឈ្មោះ Edit device name - Device name + ឈ្មោះឧបករណ៍ - Save + រក្សាទុក Device name updated @@ -1342,7 +1342,7 @@ កំពុងទាញយកព័ត៌មានលម្អិតនៃការបម្រុងទុក… - Skip restore + រំលងការស្តារឡើងវិញ ជូនដំណឹងខ្ញុំសម្រាប់ការហៅឈ្មោះ @@ -4219,7 +4219,7 @@ បញ្ជូនគណនី រំលង - Skip restore + រំលងការស្តារឡើងវិញ ការបម្រុងទុកការជជែក បញ្ជូនគណនី បញ្ជូនគណនី ទៅកាន់ឧបករណ៍ Android ថ្មីមួយ @@ -7169,9 +7169,9 @@ អនុញាតិ - Approve all + អនុញ្ញាតទាំងអស់ - Deny all + បដិសេធទាំងអស់ បើកការជូនដំណឹងពេញអេក្រង់ឬ? @@ -7346,7 +7346,7 @@ ប្រសិនបើអ្នករំលងការស្ដារឡើងវិញ នោះមេឌៀ និងឯកសារភ្ជាប់ដែលនៅសល់នៅក្នុងការបម្រុងទុករបស់អ្នកអាចទាញយកបាននៅពេលក្រោយ នៅពេលដែលមានទំហំផ្ទុក។ - Backup failed + ការបម្រុងទុកបរាជ័យ An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7387,7 +7387,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + ស្វែងយល់បន្ថែម @@ -7828,13 +7828,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + បើក Signal នៅលើឧបករណ៍ចាស់របស់អ្នក Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + រំលងការស្តារឡើងវិញ diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index ce685515e3..9ae9e8cc2f 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -1012,15 +1012,15 @@ ವರ್ಗಾವಣೆ ಮಾಡದೆಯೇ ಮುಂದುವರೆಸಿ - Edit name + ಹೆಸರು ತಿದ್ದಿ Edit device name - Device name + ಸಾಧನದ ಹೆಸರು - Save + ಉಳಿಸಿ Device name updated @@ -1380,7 +1380,7 @@ ಬ್ಯಾಕಪ್ ವಿವರಗಳನ್ನು ಪಡೆಯಲಾಗುತ್ತಿದೆ… - Skip restore + ರಿಸ್ಟೋರ್ ಸ್ಕಿಪ್ ಮಾಡಿ \@ಉಲ್ಲೇಖಗಳು ಬಂದರೆ ಎಚ್ಚರಿಸು @@ -4330,7 +4330,7 @@ ಖಾತೆ ವರ್ಗಾಯಿಸಿ ಬಿಟ್ಟು ಮುಂದುವರಿ - Skip restore + ರಿಸ್ಟೋರ್ ಸ್ಕಿಪ್ ಮಾಡಿ ಚಾಟ್ ಬ್ಯಾಕಪ್‌ಗಳು ಖಾತೆ ವರ್ಗಾಯಿಸಿ ಹೊಸ ಆಂಡ್ರಾಯ್ಡ್ ಸಾಧನಕ್ಕೆ ಖಾತೆ ವರ್ಗಾವಣೆ ಮಾಡಿ @@ -7328,9 +7328,9 @@ ಅನುಮತಿಸಿ - Approve all + ಎಲ್ಲವನ್ನೂ ಅನುಮತಿಸಿ - Deny all + ಎಲ್ಲವನ್ನೂ ನಿರಾಕರಿಸಿ ಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆನ್ ಮಾಡಬೇಕೇ? @@ -7507,7 +7507,7 @@ ನಿಮ್ಮ ಬ್ಯಾಕಪ್‌ನಲ್ಲಿ ಉಳಿದಿರುವ ಮೀಡಿಯಾ ಮತ್ತು ಅಟ್ಯಾಚ್‌ಮೆಂಟ್‌ಗಳನ್ನು ರಿಸ್ಟೋರ್ ಮಾಡುವುದನ್ನು ನೀವು ಸ್ಕಿಪ್ ಮಾಡಿದರೆ, ನಂತರದ ಸಮಯದಲ್ಲಿ ಸಂಗ್ರಹಣೆ ಸ್ಥಳಾವಕಾಶ ಲಭ್ಯವಾದಾಗ ಡೌನ್‌ಲೋಡ್ ಮಾಡಬಹುದು. - Backup failed + ಬ್ಯಾಕಪ್ ವಿಫಲವಾಗಿದೆ An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + ನಿಮ್ಮ ಹಳೆಯ ಸಾಧನದಲ್ಲಿ Signal ಅನ್ನು ತೆರೆಯಿರಿ Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + ರಿಸ್ಟೋರ್ ಸ್ಕಿಪ್ ಮಾಡಿ diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 38f7fede77..5bdccb8d37 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -992,15 +992,15 @@ 전송하지 않고 계속 - Edit name + 이름 수정 Edit device name - Device name + 기기 이름 - Save + 저장 Device name updated @@ -1342,7 +1342,7 @@ 백업 세부 정보를 가져오는 중… - Skip restore + 복원 건너뛰기 멘션 알림 @@ -4219,7 +4219,7 @@ 계정 이전 건너뛰기 - Skip restore + 복원 건너뛰기 대화 백업 계정 이전 새 Android 장치로 계정 이전 @@ -7169,9 +7169,9 @@ 승인 - Approve all + 모두 승인 - Deny all + 모두 거부 전체 화면 알림을 켜시겠습니까? @@ -7346,7 +7346,7 @@ 복원을 건너뛸 경우 나중에 저장 공간을 사용할 수 있을 때 백업의 남은 미디어와 첨부 파일을 다운로드할 수 있습니다. - Backup failed + 백업을 실패했습니다. An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7387,7 +7387,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + 자세히 알아보기 @@ -7828,13 +7828,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + 이전 기기에서 Signal을 엽니다 Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + 복원 건너뛰기 diff --git a/app/src/main/res/values-ky/strings.xml b/app/src/main/res/values-ky/strings.xml index abcf2b8d45..3bdba20e76 100644 --- a/app/src/main/res/values-ky/strings.xml +++ b/app/src/main/res/values-ky/strings.xml @@ -992,15 +992,15 @@ Билдирүүлөрдү өткөрбөй улантуу - Edit name + Атты өзгөртүү Edit device name - Device name + Түзмөктүн аты - Save + Сактоо Device name updated @@ -1342,7 +1342,7 @@ Камдык көчүрмөнүн чоо-жайы алынууда… - Skip restore + Өткөрүп жиберүү Мени айтып өткөндөр тууралуу билип турайын @@ -4219,7 +4219,7 @@ Аккаунтту которуу Өткөрүп жиберүү - Skip restore + Өткөрүп жиберүү Маектердин камдык көчүрмөсү Аккаунтту которуу Аккаунтту жаңы Android түзмөгүнө өткөрүү @@ -7169,9 +7169,9 @@ Ырастоо - Approve all + Баарын кошуу - Deny all + Баарын четке кагуу Толук экран билдирмелерин күйгүзөсүзбү? @@ -7346,7 +7346,7 @@ Өткөрүп жиберсеңиз, камдык көчүрмөлөрдөгү калган медиа файлдар менен тиркемелерди кийинчерээк жетиштүү орун болгондо жүктөп алсаңыз болот. - Backup failed + Камдык көчүрмө сакталган жок An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7387,7 +7387,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Кененирээк маалымат @@ -7828,13 +7828,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Эски телефонуңузда Signal\'ды ачыңыз Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Өткөрүп жиберүү diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 55a86cc7e5..2acf7b132b 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -1052,15 +1052,15 @@ Tęsti neperkėlus - Edit name + Redaguoti vardą Edit device name - Device name + Įrenginio pavadinimas - Save + Įrašyti Device name updated @@ -1456,7 +1456,7 @@ Gaunama informacija apie atsarginę kopiją… - Skip restore + Praleisti atkūrimą Pranešti apie paminėjimus @@ -4552,7 +4552,7 @@ Perkelti paskyrą Praleisti - Skip restore + Praleisti atkūrimą Pokalbių atsarginės kopijos Perkelti paskyrą Perkelti paskyrą į naująjį „Android“ įrenginį @@ -7646,9 +7646,9 @@ Patvirtinti - Approve all + Visus patvirtinti - Deny all + Visus atmesti Įjungti viso ekrano pranešimus? @@ -7829,7 +7829,7 @@ Jei pasirinksite praleisti atkūrimą, likusius įrašus ir priedus iš atsarginės kopijos bus galima atsisiųsti vėliau, kai bus atlaisvinta vietos saugykloje. - Backup failed + Atsarginė kopija nepavyko An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7870,7 +7870,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Sužinoti daugiau @@ -8329,13 +8329,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Atverkite „Signal“ senajame telefone Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Praleisti atkūrimą diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 258ef18e3b..312b480b80 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -1032,15 +1032,15 @@ Turpināt bez pārsūtīšanas - Edit name + Rediģēt vārdu Edit device name - Device name + Ierīces nosaukums - Save + Saglabāt Device name updated @@ -1418,7 +1418,7 @@ Meklē rezerves kopijas informāciju… - Skip restore + Izlaist atjaunošanu Paziņot man par Pieminējumiem @@ -4441,7 +4441,7 @@ Nodot kontu Izlaist - Skip restore + Izlaist atjaunošanu Sarunu rezerves kopijas Pārnest kontu Pārnest kontu uz citu Android ierīci @@ -7487,9 +7487,9 @@ Apstiprināt - Approve all + Apstiprināt visus - Deny all + Noraidīt visus Vai ieslēgt pilnekrāna paziņojumus? @@ -7668,7 +7668,7 @@ Ja izlaidīsiet atjaunošanu, rezerves kopijā atlikušo multividi un pielikumus varēs lejupielādēt vēlāk, kad ierīcē būs pieejams vairāk krātuves. - Backup failed + Rezerves kopēšana neizdevās An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7709,7 +7709,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Uzzināt vairāk @@ -8162,13 +8162,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Atveriet Signal savā iepriekšējā ierīcē. Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Izlaist atjaunošanu diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 5c8555cdf5..c66c29372f 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -1012,19 +1012,19 @@ Продолжете без пренос - Edit name + Сменете го името - Edit device name + Изменете го името на уредот - Device name + Име на уредот - Save + Зачувај - Device name updated + Името на уредот е ажурирано - Unable to change device name. Try again later. + Не може да се смени името на уредот. Обидете се повторно подоцна. @@ -1052,11 +1052,11 @@ Пренесете ја историјата на пораки - Transfer your text messages and recent media to your linked device + Пренесете ги вашите текстуални пораки и неодамнешни медиумски датотеки на вашиот поврзан уред Не префрлајте - No old messages or media will be transferred to your linked device + Старите пораки и медиумските датотеки нема да се пренесат на вашиот поврзан уред Да се прекине поврзувањето со „%1$s“? @@ -1376,11 +1376,11 @@ Вашата последна резервна копија беше направена на %1$s во %2$s. - Your last backup was made on %1$s at %2$s. Your backup size is %3$s. + Вашата последна резервна копија беше направена на %1$s во %2$s. Големината на резервна копија е %3$s. Се преземаат деталите за резервната копија… - Skip restore + Прескокни враќање Извести кога некој ќе ме спомне @@ -2481,7 +2481,7 @@ Направивте премногу обиди за регистрација на овој број. Ве молиме обидете се повторно за %1$s. Не може да се поврзе со услугата. Ве молиме проверете ја Вашата интернет конекција и обидете се повторно. - Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. + Signal не можеше да испрати SMS код поради проблеми со давателот на услуга. Обидете се повторно за неколку часа. Не можевме да испратиме код за потврда преку SMS. Наместо тоа, обидете се да го добиете кодот преку гласовен повик. @@ -4330,7 +4330,7 @@ Префрли сметка Прескокни - Skip restore + Прескокни враќање Резервни копии на разговори Префрли сметка Префрли сметка на друг Android уред @@ -7328,9 +7328,9 @@ Дозволи - Approve all + Одобри ги сите - Deny all + Одбиј ги сите Сакате да се вклучат известувања на цел екран? @@ -7507,11 +7507,11 @@ Ако го прескокнете враќањето, преостанатите медиумски датотеки и прилози во вашата резервна копија ќе може да се преземат подоцна кога ќе се ослободи простор за складирање. - Backup failed + Создавањето на резервна копија не успеа - An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + Настана грешка и не можеше да се направи вашата резервна копија. Проверете дали ја користите последната верзија на Signal и обидете се повторно. Доколку проблемот продолжи, контактирајте со тимот за корисничка поддршка. - Check for update + Проверете дали има нова верзија @@ -7544,11 +7544,11 @@ Прескокнете го преземањето - Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + Не можеше да се заврши вашата последна резервна копија. Проверете дали вашиот телефон e поврзан на Wi-Fi и допрете на „Направи резервна копија сега“ за да се обидете повторно. - Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + Не можеше да се заврши вашата последна резервна копија. Проверете дали ја користите последната верзија на Signal и обидете се повторно. - Learn more + Дознајте повеќе @@ -7753,7 +7753,7 @@ Дознајте повеќе - Processing backup… + Се обработува резервната копија… Имате %1$s податоци од резервни копии кои не се на овој уред. Вашата резервна копија ќе биде избришана кога вашата претплата ќе заврши за %2$d ден. @@ -7775,7 +7775,7 @@ Се јави мрежна грешка. Проверете ја интернет врската и обидете се повторно. - Uploading messages… + Пораките се поставуваат… @@ -7991,17 +7991,17 @@ Во ред - No Backup to Restore + Нема податоци од резервна копија што може да се вратат - Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + Бидејќи се префрлате од iPhone на Android, единствен начин да ги пренесете вашите пораки и медиумски датотеки е да овозможите Signal резервни копии на вашиот стар уред. - Open Signal on your old device + Отворете го Signal на вашиот стар уред - Tap Settings > Backups + Допрете на Поставувања > Резервни копии - Enable backups and wait until your backup is complete + Овозможете резервни копии и почекајте да се направи вашата резервна копија - Skip restore + Прескокни враќање diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 9505d3f352..51f1ba5c7b 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -1012,15 +1012,15 @@ കൈമാറാതെ തുടരുക - Edit name + പേര് എഡിറ്റ് ചെയ്യുക Edit device name - Device name + ഉപകരണത്തിന്റെ പേര് - Save + സംരക്ഷിക്കൂ Device name updated @@ -1380,7 +1380,7 @@ ബാക്കപ്പ് വിശദാംശങ്ങൾ ലഭ്യമാക്കുന്നു… - Skip restore + പുനഃസ്ഥാപിക്കൽ ഒഴിവാക്കുക സൂചനകൾ എന്നെ അറിയിക്കുക @@ -4330,7 +4330,7 @@ അക്കൗണ്ട് മാറ്റിസ്ഥാപിക്കുക ഒഴിവാക്കുക - Skip restore + പുനഃസ്ഥാപിക്കൽ ഒഴിവാക്കുക ചാറ്റ് ബാക്കപ്പുകൾ അക്കൗണ്ട് മാറ്റിസ്ഥാപിക്കുക അക്കൌണ്ട് ഒരു പുതിയ ആൻഡ്രോയിഡ് ഉപകരണത്തിലേക്ക് മാറ്റിസ്ഥാപിക്കുക @@ -7328,9 +7328,9 @@ അംഗീകരിക്കുക - Approve all + എല്ലാം അനുവദിക്കുക - Deny all + എല്ലാം നിരസിക്കുക പൂർണ്ണ സ്ക്രീൻ അറിയിപ്പുകൾ ഓണാക്കണോ? @@ -7507,7 +7507,7 @@ നിങ്ങൾ പുനഃസ്ഥാപിക്കുന്നത് ഒഴിവാക്കുകയാണെങ്കിൽ, നിങ്ങളുടെ ബാക്കപ്പിലെ ശേഷിക്കുന്ന മീഡിയയും അറ്റാച്ച്‌മെൻ്റുകളും പിന്നീട് സ്റ്റോറേജ് ഇടം ലഭ്യമാകുമ്പോൾ ഡൗൺലോഡ് ചെയ്യാവുന്നതാണ്. - Backup failed + ബാക്കപ്പ് പരാജയപ്പെട്ടു  An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + കൂടുതലറിയുക @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + നിങ്ങളുടെ പഴയ ഉപകരണത്തിൽ Signal തുറക്കുക Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + പുനഃസ്ഥാപിക്കൽ ഒഴിവാക്കുക diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml index e550b7837f..f8af4ff2dc 100644 --- a/app/src/main/res/values-mr/strings.xml +++ b/app/src/main/res/values-mr/strings.xml @@ -1012,15 +1012,15 @@ हस्तांतरित न करता पुढे जा - Edit name + नाव संपादित करा Edit device name - Device name + डिव्हाईस नाव - Save + जतन करा Device name updated @@ -1380,7 +1380,7 @@ बॅकअप तपशील आणत आहे… - Skip restore + पुर्नस्थापित करणे वगळा उल्लेखा साठी मला सूचित करा @@ -4330,7 +4330,7 @@ खाते स्थानांतरित करा वगळा - Skip restore + पुर्नस्थापित करणे वगळा चॅट बॅकअप्स खाते स्थानांतरित करा नवीन Android डिव्हाइसवर खाते स्थानांतरित करा @@ -7328,9 +7328,9 @@ स्वीकार करा - Approve all + सर्व मंजूर करा - Deny all + सर्व नाकारा पूर्ण स्क्रिन अधिसूचना सुरू करावयाची? @@ -7507,7 +7507,7 @@ जर आपण पूर्ववत करायचे टाळलेत तर आपल्या बॅकअपमधील उरलेला मिडीया आणि अटॅचमेंट्स नंतर साठवणीसाठी जागा मिळाल्यावर डाऊनलोड करता येतात. - Backup failed + बॅकअप अयशस्वी An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + अधिक जाणून घ्या @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + आपल्या जुन्या डिव्हाईसवर Signal उघडा Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + पुर्नस्थापित करणे वगळा diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index 9943746f94..c1d22da777 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -992,15 +992,15 @@ Teruskan tanpa memindahkan - Edit name + Edit nama Edit device name - Device name + Nama peranti - Save + Simpan Device name updated @@ -1342,7 +1342,7 @@ Mengambil butiran sandaran… - Skip restore + Langkau pemulihan Maklumkan saya untuk Sebutan @@ -4219,7 +4219,7 @@ Pindahkan akaun Langkau - Skip restore + Langkau pemulihan Sandaran sembang Pindahkan akaun Pindahkan akaun ke peranti Android baharu @@ -7169,9 +7169,9 @@ Luluskan - Approve all + Luluskan semua - Deny all + Tolak semua Hidupkan pemberitahuan skrin penuh? @@ -7346,7 +7346,7 @@ Jika anda melangkau pemulihan, media dan lampiran yang masih ada dalam sandaran anda boleh dimuat turun kemudian apabila ruang storan tersedia. - Backup failed + Sandaran gagal An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7387,7 +7387,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Ketahui lebih lanjut @@ -7828,13 +7828,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Buka Signal pada peranti lama anda Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Langkau pemulihan diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index 13a805cb31..05f94e9d01 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -992,15 +992,15 @@ မလွှဲပြောင်းဘဲ ဆက်လက်လုပ်ဆောင်မည် - Edit name + အမည်ပြင်ရန် Edit device name - Device name + စက်အမည် - Save + သိမ်းရန် Device name updated @@ -1342,7 +1342,7 @@ ဘက်ခ်အပ်အသေးစိတ်ကို ရယူနေသည်… - Skip restore + ပြန်လည်ရယူခြင်းကို ကျော်မည် မန်းရှင်းများရှိလျှင် ကျွန်ုပ်ကိုအသိပေးပါ @@ -4219,7 +4219,7 @@ အကောင့်လွှဲမယ် ကျော်ပါ - Skip restore + ပြန်လည်ရယူခြင်းကို ကျော်မည် ချက်(တ်) အရန်သိမ်းဆည်းမှုများ အကောင့်လွှဲမယ် သင့် Android စက်အသစ်ပေါ်သို့ အကောင့်လွှဲပြောင်းမယ် @@ -7169,9 +7169,9 @@ ခွင့်ပြုမယ် - Approve all + အားလုံးကို ခွင့်ပြုမည် - Deny all + အားလုံးကို ငြင်းပယ်မည် မျက်နှာပြင်အပြည့် အသိပေးချက်များကို ဖွင့်မည်လား။ @@ -7346,7 +7346,7 @@ သင့် ဘက်ခ်အပ်တွင် ကျန်ရှိသည့်မီဒီယာနှင့် ပူးတွဲဖိုင်များအား ပြန်လည်ရယူခြင်းကို ကျော်လိုက်ပါက နောင်တစ်ချိန် သင့်စက်တွင် နေရာလွတ်ရှိသောအခါ ဘက်ခ်အပ်ကိုဒေါင်းလုဒ်ပြုလုပ်နိုင်သည်။ - Backup failed + အရန်သိမ်းဆည်းမှုများ မအောင်မြင်ပါ An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7387,7 +7387,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + ပိုမိုလေ့လာရန် @@ -7828,13 +7828,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + သင့်စက်အဟောင်းတွင် Signal ကိုဖွင့်ပါ Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + ပြန်လည်ရယူခြင်းကို ကျော်မည် diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 5f4e77a978..e5028a6719 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -1012,15 +1012,15 @@ Gå videre uten overføring - Edit name + Rediger navn Edit device name - Device name + Enhetsnavn - Save + Lagre Device name updated @@ -1380,7 +1380,7 @@ Henter informasjon om sikkerhetskopien … - Skip restore + Hopp over Varsle meg om omtaler @@ -4330,7 +4330,7 @@ Overfør konto Hopp over - Skip restore + Hopp over Sikkerhetskopi av samtaler Overfør konto Overfør konto til ny Android-enhet @@ -7328,9 +7328,9 @@ Godkjenn - Approve all + Godkjenn alle - Deny all + Avvis alle Vil du slå på varsler i fullskjerm? @@ -7507,7 +7507,7 @@ Hvis du velger å hoppe over dette trinnet, kan du laste ned resten av mediefilene og vedleggene i sikkerhetskopien din senere når du har nok lagringsplass. - Backup failed + Sikkerhetskopi feilet An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Les mer @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Åpne Signal på den gamle enheten. Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Hopp over diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 925ce9360b..a1f2f3bf58 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -1012,15 +1012,15 @@ Doorgaan zonder over te zetten - Edit name + Naam bewerken Edit device name - Device name + Apparaatnaam - Save + Opslaan Device name updated @@ -1380,7 +1380,7 @@ Back-upgegevens aan het ophalen… - Skip restore + Herstel overslaan Meldingen bij vermeldingen @@ -2286,8 +2286,8 @@ Belt naar %1$s Belt naar %1$s en %2$s - Belt naar %1$s, %2$s en %3$d - Belt naar %1$s, %2$s en %3$d + Belt naar %1$s, %2$s en %3$d ander + Belt naar %1$s, %2$s en %3$d anderen %1$s belt jou @@ -3128,7 +3128,7 @@ Bericht opstellen Emojitoetsenbord wisselen Bijlagevoorbeeld - Camera overzicht weergeven of verbergen + Camera of galerij openen voor foto- of videobijlage Audio opnemen en verzenden Vergrendel opnemen van audio @@ -4330,7 +4330,7 @@ Account overzetten Overslaan - Skip restore + Herstel overslaan Back-up van chats Account overzetten Alle gegevens overzetten naar een nieuw Android-apparaat @@ -7328,9 +7328,9 @@ Goedkeuren - Approve all + Alle goedkeuren - Deny all + Alle weigeren Meldingen op volledig scherm inschakelen? @@ -7507,7 +7507,7 @@ Als je herstellen overslaat, kunnen de resterende media en bijlagen in je back-up op een later moment worden gedownload wanneer er opslagruimte vrijkomt. - Backup failed + Het maken van een back-up is mislukt An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Meer lezen @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Open Signal op je oude apparaat Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Herstel overslaan diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index f54b14d4b6..aafd9b4c26 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -1012,15 +1012,15 @@ ਟ੍ਰਾਂਸਫਰ ਕੀਤੇ ਬਿਨਾਂ ਜਾਰੀ ਰੱਖੋ - Edit name + ਨਾਂ ਨੂੰ ਸੋਧੋ Edit device name - Device name + ਡਿਵਾਈਸ ਦਾ ਨਾਂ - Save + ਸੇਵ ਕਰੋ Device name updated @@ -1380,7 +1380,7 @@ ਬੈਕਅੱਪ ਦੇ ਵੇਰਵੇ ਪ੍ਰਾਪਤ ਕੀਤੇ ਜਾ ਰਹੇ ਹਨ… - Skip restore + ਰੀਸਟੋਰ ਕਰਨਾ ਛੱਡੋ ਹਵਾਲਿਆਂ ਲਈ ਮੈਨੂੰ ਸੂਚਿਤ ਕਰੋ @@ -4330,7 +4330,7 @@ ਖਾਤਾ ਟ੍ਰਾਂਸਫਰ ਕਰੋ ਛੱਡੋ - Skip restore + ਰੀਸਟੋਰ ਕਰਨਾ ਛੱਡੋ ਚੈਟ ਬੈਕਅਪ ਖਾਤਾ ਟ੍ਰਾਂਸਫਰ ਕਰੋ ਖਾਤੇ ਨੂੰ ਨਵੇਂ Android ਡਿਵਾਈਸ ਉੱਤੇ ਟ੍ਰਾਂਸਫਰ ਕਰੋ @@ -7328,9 +7328,9 @@ ਮਨਜ਼ੂਰ ਕਰੋ - Approve all + ਸਭ ਨੂੰ ਮਨਜ਼ੂਰ ਕਰੋ - Deny all + ਸਭ ਨੂੰ ਨਾਮਨਜ਼ੂਰ ਕਰੋ ਕੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਉੱਤੇ ਦਿਖਣ ਵਾਲੀਆਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਚਾਲੂ ਕਰਨਾ ਹੈ? @@ -7507,7 +7507,7 @@ ਜੇਕਰ ਤੁਸੀਂ ਰੀਸਟੋਰ ਕਰਨਾ ਛੱਡ ਦਿੰਦੇ ਹੋ, ਤਾਂ ਅਗਲੀ ਵਾਰ ਜਦੋਂ ਸਟੋਰੇਜ ਦੀ ਥਾਂ ਉਪਲਬਧ ਹੋਣ \'ਤੇ ਤੁਹਾਡੇ ਬੈਕਅੱਪ ਵਿੱਚ ਬਾਕੀ ਬਚਿਆ ਮੀਡੀਆ ਅਤੇ ਅਟੈਚਮੈਂਟਾਂ ਨੂੰ ਬਾਅਦ ਵਿੱਚ ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ। - Backup failed + ਬੈਕਅੱਪ ਅਸਫ਼ਲ ਹੈ An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + ਹੋਰ ਜਾਣੋ @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + ਆਪਣੇ ਪੁਰਾਣੇ ਡਿਵਾਈਸ \'ਤੇ Signal ਖੋਲ੍ਹੋ Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + ਰੀਸਟੋਰ ਕਰਨਾ ਛੱਡੋ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 841e95ece2..caa733f224 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -1052,15 +1052,15 @@ Kontynuuj bez przenoszenia - Edit name + Edytuj nazwę Edit device name - Device name + Nazwa urządzenia - Save + Zapisz Device name updated @@ -1456,7 +1456,7 @@ Pobieranie szczegółów kopii zapasowej… - Skip restore + Pomiń przywracanie Powiadom mnie o wzmiankach @@ -4552,7 +4552,7 @@ Przenieś konto Pomiń - Skip restore + Pomiń przywracanie Kopia zapasowa czatów Przenieś konto Przenieś konto na nowe urządzenie z systemem Android @@ -7646,9 +7646,9 @@ Zaakceptuj - Approve all + Zatwierdź wszystkich - Deny all + Odmów wszystkim Włączyć powiadomienia pełnoekranowe? @@ -7829,7 +7829,7 @@ Jeśli pominiesz przywracanie, pozostałe multimedia i załączniki z kopii zapasowej będzie można pobrać później, gdy zwolni się miejsce na dysku. - Backup failed + Zapisywanie kopii zapasowej nie powiodło się An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7870,7 +7870,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Dowiedz się więcej @@ -8329,13 +8329,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Otwórz Signal na swoim starym urządzeniu Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Pomiń przywracanie diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index e133135e4b..fb8840dc41 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -1012,15 +1012,15 @@ Continuar sem transferir - Edit name + Editar nome Edit device name - Device name + Nome do aparelho - Save + Salvar Device name updated @@ -1380,7 +1380,7 @@ Buscando detalhes do backup… - Skip restore + Pular restauração Notifique-me ao ser mencionado @@ -4330,7 +4330,7 @@ Transferir conta Ignorar - Skip restore + Pular restauração Backups de chat Transferir conta Transferir conta para um novo dispositivo Android @@ -7328,9 +7328,9 @@ Aprovar - Approve all + Aprovar todos - Deny all + Recusar todos Ativar notificações em tela cheia? @@ -7507,7 +7507,7 @@ Se você pular essa etapa, poderá baixar a mídia e os anexos restantes no seu backup posteriormente, quando houver espaço de armazenamento disponível. - Backup failed + Backup falhou An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Saiba mais @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Abra o Signal no seu dispositivo Android antigo Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Pular restauração diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 2168d3fc37..2a79bbf2d8 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -1012,15 +1012,15 @@ Continuar sem transferir - Edit name + Editar nome Edit device name - Device name + Nome do dispositivo - Save + Guardar Device name updated @@ -1380,7 +1380,7 @@ A obter detalhes da cópia de segurança… - Skip restore + Saltar restauro Notificar-me quando for \'Mencionado\' @@ -4330,7 +4330,7 @@ Transferir conta Saltar - Skip restore + Saltar restauro Cópias de segurança dos chats Transferir conta Transferir conta para um dispositivo Android novo @@ -7328,9 +7328,9 @@ Aprovar - Approve all + Aprovar todos - Deny all + Recusar todos Ativar as notificações de ecrã inteiro? @@ -7507,7 +7507,7 @@ Se saltar o restauro, os restantes ficheiros multimédia e anexos da cópia de segurança podem ser transferidos mais tarde, quando o espaço de armazenamento estiver disponível. - Backup failed + Falha ao fazer cópia de segurança An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Saber mais @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Abra o Signal no seu dispositivo antigo Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Saltar restauro diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index ba135ffa3c..112b8459b1 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -1032,15 +1032,15 @@ Continuă fără transfer - Edit name + Editează numele Edit device name - Device name + Numele dispozitivului - Save + Salvează Device name updated @@ -1418,7 +1418,7 @@ Se preiau detaliile backup-ului… - Skip restore + Sari peste restaurare Anunță-mă pentru Mențiuni @@ -4441,7 +4441,7 @@ Transfer cont Omite - Skip restore + Sari peste restaurare Backup-uri pt. conversații Transfer cont Transfer cont pe un nou dispozitiv Android @@ -7487,9 +7487,9 @@ Aprobă - Approve all + Aprob - Deny all + Nu aprob Activezi notificările pe ecran complet? @@ -7668,7 +7668,7 @@ Dacă omiți restaurarea, fișierele media rămase și atașamentele din backup pot fi descărcate mai târziu, când spațiul de stocare devine disponibil. - Backup failed + Backup-ul a eșuat An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7709,7 +7709,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Află mai multe @@ -8162,13 +8162,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Deschide Signal pe vechiul dispozitiv Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Sari peste restaurare diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 975bb92169..49771da404 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1052,15 +1052,15 @@ Продолжить без переноса - Edit name + Изменить имя Edit device name - Device name + Имя устройства - Save + Сохранить Device name updated @@ -1456,7 +1456,7 @@ Получение сведений о резервном копировании… - Skip restore + Пропустить восстановление Уведомлять меня об упоминаниях @@ -4552,7 +4552,7 @@ Перенести учётную запись Пропустить - Skip restore + Пропустить восстановление Резервные копии чатов Перенести учётную запись Перенести учётную запись на новое Android-устройство @@ -7646,9 +7646,9 @@ Принять - Approve all + Одобрить все - Deny all + Отклонить все Включить уведомления во весь экран? @@ -7829,7 +7829,7 @@ Если вы пропустите восстановление, оставшиеся медиафайлы и вложения из резервной копии могут быть загружены позднее, когда освободится место для хранения. - Backup failed + Резервное копирование не удалось An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7870,7 +7870,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Узнать больше @@ -8329,13 +8329,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Откройте Signal на старом устройстве Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Пропустить восстановление diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 9c8eb734b3..55e1114651 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -1052,15 +1052,15 @@ Pokračovať bez prenosu - Edit name + Upraviť meno Edit device name - Device name + Názov zariadenia - Save + Uložiť Device name updated @@ -1456,7 +1456,7 @@ Načítavajú sa podrobnosti o zálohe… - Skip restore + Preskočiť obnovenie Upozorni ma na Spomenutia @@ -4552,7 +4552,7 @@ Preniesť účet Preskočiť - Skip restore + Preskočiť obnovenie Zálohy četov Preniesť účet Preniesť účet do nového Android zariadenia @@ -7646,9 +7646,9 @@ Prijať - Approve all + Schváliť všetky - Deny all + Odmietnuť všetky Chcete zapnúť upozornenia na celú obrazovku? @@ -7829,7 +7829,7 @@ Ak preskočíte obnovenie, zostávajúce médiá a prílohy vo vašej zálohe si môžete stiahnuť neskôr, keď bude k dispozícii úložný priestor. - Backup failed + Zálohovanie zlyhalo An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7870,7 +7870,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Zistiť viac @@ -8329,13 +8329,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Otvorte Signal na svojom starom zariadení Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Preskočiť obnovenie diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 2bc54cd06e..b202af176e 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -1052,15 +1052,15 @@ Nadaljuj brez prenosa - Edit name + Uredi ime Edit device name - Device name + Ime naprave - Save + Shrani Device name updated @@ -1456,7 +1456,7 @@ Pridobivanje podatkov o varnostni kopiji … - Skip restore + Preskoči obnovitev Obveščaj me o omembah @@ -4552,7 +4552,7 @@ Prenos računa Preskoči - Skip restore + Preskoči obnovitev Varnostno kopiranje klepetov Prenos računa Prenos računa na novo androidno napravo @@ -7646,9 +7646,9 @@ Potrdi - Approve all + Potrdi vse - Deny all + Zavrni vse Želite vklopiti celozaslonska obvestila? @@ -7829,7 +7829,7 @@ Če obnovitev preskočite, lahko preostale medije in priponke iz varnostne kopije prenesete pozneje, ko bo na voljo prostor za shranjevanje. - Backup failed + Izdelava varnostne kopije ni uspela An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7870,7 +7870,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Preberite več @@ -8329,13 +8329,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Odprite aplikacijo Signal v stari napravi Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Preskoči obnovitev diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index cf48dddd78..fe7547412d 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -1012,15 +1012,15 @@ Vazhdo pa transferuar - Edit name + Ndrysho emrin Edit device name - Device name + Emër pajisjeje - Save + Ruaj Device name updated @@ -1380,7 +1380,7 @@ Duke marrë detajet e kopjeruajtjes… - Skip restore + Kapërce rikthimin Njoftomë për Përmendje @@ -4330,7 +4330,7 @@ Shpërngulni llogari Anashkaloje - Skip restore + Kapërce rikthimin Kopjeruajtje bisedash Shpërngulni llogari Shpërngulni llogari te një pajisje Android e re @@ -7328,9 +7328,9 @@ Miratoje - Approve all + Mirato të gjitha - Deny all + Refuzo të gjitha Të aktivizohen njoftimet me ekran të plotë? @@ -7507,7 +7507,7 @@ Nëse kapërcen rikthimin, mediat dhe bashkëngjitjet e mbetura në kopjeruajtje mund të shkarkohen më vonë kur hapësira ruajtëse të jetë e disponueshme. - Backup failed + Kopjeruajtja dështoi An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Mëso më shumë @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Hap Signal në pajisjen e vjetër Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Kapërce rikthimin diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 16b269fb8a..1c3648f42c 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -1012,19 +1012,19 @@ Настави без преноса - Edit name + Измени име - Edit device name + Промена назива уређаја - Device name + Име уређаја - Save + Сачувај - Device name updated + Назив уређаја је промењен - Unable to change device name. Try again later. + Промена назива уређаја није успела. Пробајте поново касније. @@ -1052,11 +1052,11 @@ Пренеси историју порука - Transfer your text messages and recent media to your linked device + Пренесите текстуалне поруке и недавне медије на повезани уређај Немој да пренесеш - No old messages or media will be transferred to your linked device + Старе поруке и медији неће бити пренесени на повезани уређај Прекините везу са уређајем „%1$s“? @@ -1376,11 +1376,11 @@ Последња резервна копија направљена је %1$s у %2$s. - Your last backup was made on %1$s at %2$s. Your backup size is %3$s. + Последња резервна копија направљена је %1$s у %2$s. Величина резервне копије је %3$s. Преузимамо детаље о резервној копији… - Skip restore + Прескочи враћање Обавести ме за помињања @@ -2481,7 +2481,7 @@ Превише пута сте покушали да региструјете овај број. Пробајте поново за %1$s. Повезивање са сервисом није успело. Проверите да ли сте повезани на интернет и пробајте поново. - Signal couldn\'t send an SMS code because of issues with the SMS provider. Try again in several hours. + Signal није успео да пошаље SMS код због проблема са SMS провајдером. Пробајте поново за неколико сати. Нисмо успели да вам пошаљемо шифру за верификацију. Можете да затражите шифру преко гласовног позива. @@ -4330,7 +4330,7 @@ Пренесите налог Прескочи - Skip restore + Прескочи враћање Резерве ћаскања Пренесите налог Пренесите налог на нови Android уређај @@ -7328,9 +7328,9 @@ Одобри - Approve all + Прихвати све - Deny all + Одбиј све Желите ли да укључите обавештења преко целог екрана? @@ -7507,11 +7507,11 @@ Ако прескочите враћање преосталих медија и прилога у резервној копији, можете их преузети касније када меморијски простор постане доступан. - Backup failed + Креирање резервне копије није успело - An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. + Дошло је до грешке и ваша резервна копија није направљена. Проверите да ли користите најновију верзију Signal-а и пробајте поново. Ако се проблем и даље буде појављивао, обратите се подршци. - Check for update + Ажурирај на нову верзију @@ -7544,11 +7544,11 @@ Прескочи преузимање - Your last backup couldn\'t be completed. Make sure your phone is connected to Wi-Fi and tap \"Back up now\" to try again. + Последње креирање резервне копије није успело. Проверите да ли је ваш телефон повезан на Wi-Fi и додирните „Направи резервну копију“ да пробате поново. - Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. + Последње креирање резервне копије није успело. Проверите да ли користите најновију верзију Signal-а и пробајте поново. - Learn more + Сазнајте више @@ -7753,7 +7753,7 @@ Сазнајте више - Processing backup… + Обрада резервне копије… Имате %1$s резервне копије података, који нису на овом уређају. Ваша резервна копија ће бити избрисана када се претплата заврши за %2$d дан. @@ -7775,7 +7775,7 @@ Дошло је до грешке у мрежи. Проверите да ли сте повезани на интернет и пробајте поново. - Uploading messages… + Отпремање порука… @@ -7991,17 +7991,17 @@ У реду - No Backup to Restore + Нема резервне копије за враћање - Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. + Пошто прелазите са iPhone-а на Android, једини начин да пренесете своје поруке и медије је активирање резервне копије Signal-а на старом уређају. - Open Signal on your old device + Отворите Signal на старом уређају - Tap Settings > Backups + Додирните Подешавања > Резервне копије - Enable backups and wait until your backup is complete + Активирајте резервне копије и сачекајте док се креирање резервне копије не заврши - Skip restore + Прескочи враћање diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 60a74338cd..b2ba6d9ed7 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -1012,15 +1012,15 @@ Fortsätt utan att överföra - Edit name + Redigera namn Edit device name - Device name + Enhetsnamn - Save + Spara Device name updated @@ -1380,7 +1380,7 @@ Hämtar säkerhetskopieringsdetaljer … - Skip restore + Hoppa över återställning Meddela mig om omnämnanden @@ -4330,7 +4330,7 @@ Överför konto Hoppa över - Skip restore + Hoppa över återställning Säkerhetskopior av chattar Överför konto Överför konto till en ny Android-enhet @@ -7328,9 +7328,9 @@ Godkänn - Approve all + Godkänn alla - Deny all + Neka alla Aktivera helskärmsaviseringar? @@ -7507,7 +7507,7 @@ Om du hoppar över återställning kan återstående media och bilagor i din säkerhetskopia laddas ner vid ett senare tillfälle när lagringsutrymme blir tillgängligt. - Backup failed + Säkerhetskopieringen misslyckades An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Läs mer @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Öppna Signal på din gamla enhet Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Hoppa över återställning diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index 13873776b1..626e40d718 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -1012,15 +1012,15 @@ Endelea bila kuhamisha - Edit name + Hariri jina Edit device name - Device name + Jina la kifaa - Save + Hifadhi Device name updated @@ -1380,7 +1380,7 @@ Inachukua maelezo ya hifadhi… - Skip restore + Ruka rejesha Niarifu Ninapotajwa @@ -4330,7 +4330,7 @@ Hamisha akaunti Ruka - Skip restore + Ruka rejesha Nakalahifadhi ya Gumzo Hamisha akaunti Hamishia akaunti kwenye kifaa kipya cha Android @@ -7328,9 +7328,9 @@ Idhinisha - Approve all + Wakubalie wote - Deny all + Wakatalie wote Washa arifa za skrini nzima? @@ -7507,7 +7507,7 @@ Ukiruka kurejesha video, picha na viambatisho vilivyosalia kwenye hifadhi nakala yako vinaweza kupakuliwa baadaye nafasi ya kuhifadhi itakapopatikana. - Backup failed + Nakala hifadhi haijawekwa An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Jifunze zaidi @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Fungua Signal kwenye kifaa chako cha zamani Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Ruka rejesha diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 12f2b84cf0..185a3776d9 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -1012,15 +1012,15 @@ இடமாற்றாமல் தொடர்க - Edit name + பெயரைத் திருத்தவும் Edit device name - Device name + சாதனத்தின் பெயர் - Save + சேமி Device name updated @@ -1380,7 +1380,7 @@ காப்புப்பிரதி விவரங்களைப் பெறுகிறது… - Skip restore + மீட்டமைப்பதைத் தவிர் என் பெயர் குறிப்புகளை எனக்கு அறிவி @@ -4330,7 +4330,7 @@ Signal கணக்கை நகர்த்தவும் தவிர் - Skip restore + மீட்டமைப்பதைத் தவிர் சாட் காப்புப்பிரதிகள் Signal கணக்கை நகர்த்தவும் கணக்கை புதிய சாதனத்திற்கு நகர்த்தவும் @@ -7328,9 +7328,9 @@ ஒப்புதல் - Approve all + அனைத்தையும் ஏற்றுக்கொள் - Deny all + அனைத்தையும் மறு முழுத் திரை அறிவிப்புகளை இயக்க வேண்டுமா? @@ -7507,7 +7507,7 @@ மீட்டமைப்பதைத் தவிர்த்தால், உங்கள் காப்புப்பிரதியில் மீதமுள்ள ஊடகம் மற்றும் இணைப்புகள் சேமிப்பகத்தில் இடம் கிடைக்கும்போது பதிவிறக்கம் செய்யப்படும். - Backup failed + மறுபிரதி தோல்வியுற்றது An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + மேலும் அறிக @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + உங்களின் பழைய சாதனத்தில் சிக்னலைத் திறக்கவும் Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + மீட்டமைப்பதைத் தவிர் diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index 1e327a9f15..cb647b77c2 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -1012,15 +1012,15 @@ బదిలీ చేయకుండానే కొనసాగించండి - Edit name + పేరు ఎడిట్ చేయండి Edit device name - Device name + పరికరం యొక్క పేరు - Save + సేవ్ చేయండి Device name updated @@ -1380,7 +1380,7 @@ బ్యాకప్ వివరాలను పొందుతోంది… - Skip restore + పునరుద్ధరించడాన్ని దాటవేయండి నాకు ప్రస్తావనల తెలియజేయండి @@ -4330,7 +4330,7 @@ ఖాతాను బదిలీ చేయడం వదిలివేయి - Skip restore + పునరుద్ధరించడాన్ని దాటవేయండి చాట్ బ్యాకప్‌లు ఖాతాను బదిలీ చేయడం ఖాతాను కొత్త Android పరికరానికి బదిలీ చేయండి @@ -7328,9 +7328,9 @@ ఆమోదించడానికి - Approve all + అన్నిటిని ఆమోదించండి - Deny all + అన్నిటిని తిరస్కరించండి పూర్తి స్క్రీన్ నోటిఫికేషన్లను ఆన్ చేసేదా? @@ -7507,7 +7507,7 @@ ఒకవేళ మీరు పునరుద్ధరించడాన్ని దాటవేస్తే, మీ బ్యాకప్‌లోని మిగిలిన మీడియా మరియు జోడింపులను నిల్వ స్థలం అందుబాటులోకి వచ్చిన తర్వాత డౌన్లోడ్ చేసుకోవచ్చు. - Backup failed + ప్రత్యామ్నాయ విఫలమైంది An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + మరింత తెలుసుకోండి @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + మీ పాత పరికరంలో Signal ను తెరవండి Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + పునరుద్ధరించడాన్ని దాటవేయండి diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index ad26391101..da06468c3e 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -992,15 +992,15 @@ ดำเนินการต่อโดยไม่ถ่ายโอน - Edit name + แก้ไขชื่อ Edit device name - Device name + ชื่ออุปกรณ์ - Save + บันทึก Device name updated @@ -1342,7 +1342,7 @@ กำลังเรียกดูข้อมูลสำรอง… - Skip restore + ข้ามการกู้คืน แจ้งให้ฉันรู้สำหรับการกล่าวถึง @@ -4219,7 +4219,7 @@ ถ่ายโอนบัญชี ข้าม - Skip restore + ข้ามการกู้คืน ข้อมูลสำรองของแชท ถ่ายโอนบัญชี ถ่ายโอนบัญชีไปยังอุปกรณ์ Android เครื่องใหม่ @@ -7169,9 +7169,9 @@ อนุมัติ - Approve all + อนุมัติทั้งหมด - Deny all + ปฏิเสธทั้งหมด เปิดการแจ้งเตือนแบบเต็มหน้าจอหรือไม่ @@ -7346,7 +7346,7 @@ หากข้ามการกู้คืน คุณจะสามารถดาวน์โหลดไฟล์สื่อและไฟล์แนบที่หลงเหลืออยู่ในข้อมูลสำรองได้ในภายหลังเมื่ออุปกรณ์มีพื้นที่จัดเก็บเพียงพอ - Backup failed + สำรองข้อมูลไม่สำเร็จ An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7387,7 +7387,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + เรียนรู้เพิ่มเติม @@ -7828,13 +7828,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + เปิดแอป Signal บนอุปกรณ์เครื่องเก่า Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + ข้ามการกู้คืน diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 601d30ef54..e1f6f8acbb 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -1012,7 +1012,7 @@ Magpatuloy nang hindi nililipat - Edit name + I-edit ang pangalan @@ -1020,7 +1020,7 @@ Device name - Save + I-save Device name updated @@ -1380,7 +1380,7 @@ Kinukuha ang backup details… - Skip restore + I-skip ang restore I-notify ako sa Mentions @@ -4330,7 +4330,7 @@ Transfer account Laktawan - Skip restore + I-skip ang restore Chat backups Transfer account I-transfer ang account sa bagong Android device @@ -7328,9 +7328,9 @@ Approve - Approve all + Aprubahan lahat - Deny all + Tanggihan lahat Gusto mo bang i-on ang full screen notifications? @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Matuto pa @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Buksan ang Signal sa lumang device mo Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + I-skip ang restore diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 19e5aaa79c..f01c924e73 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -1012,15 +1012,15 @@ Aktarmadan devam et - Edit name + İsmi düzenle Edit device name - Device name + Cihaz adı - Save + Kaydet Device name updated @@ -1380,7 +1380,7 @@ Yedekleme ayrıntıları getiriliyor… - Skip restore + Geri yüklemeyi atla Bahsedilmeleri bana bildir @@ -4330,7 +4330,7 @@ Hesabı aktar Atla - Skip restore + Geri yüklemeyi atla Sohbet yedekleri Hesabı aktar Hesabı yeni bir Android cihaza aktar @@ -7328,9 +7328,9 @@ Onayla - Approve all + Tümünü onayla - Deny all + Tümünü reddet Tam ekran bildirimleri açılsın mı? @@ -7507,7 +7507,7 @@ Geri yüklemeyi atlarsan yedeklemende kalan medya ve eklentiler daha sonra depolama alanı kullanılabilir hale geldiğinde indirilebilir. - Backup failed + Yedekleme başarısız oldu An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Daha fazlasını öğren @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Signal\'i eski cihazında aç Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Geri yüklemeyi atla diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index 01e6af1668..6cf087fbee 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -992,15 +992,15 @@ يۆتكىمەي تۇرۇپلا داۋاملاشتۇرۇڭ - Edit name + ئات تەھرىر Edit device name - Device name + ئۈسكۈنە نامى - Save + ساقلاش Device name updated @@ -1342,7 +1342,7 @@ زاپاسلاش تەپسىلاتلىرىنى قوبۇللاش… - Skip restore + ئەسلىگە كەلتۈرۈشتى ئاتلاڭ ئاتالسام ئەسكەرتسۇن @@ -4219,7 +4219,7 @@ ھېساباتنى يۆتكە ئاتلا - Skip restore + ئەسلىگە كەلتۈرۈشتى ئاتلاڭ پاراڭ زاپاسلانمىسى ھېساباتنى يۆتكە يېڭى بىر Android ئۈسكىنىسگە ھېساباتنى يۆتكە @@ -7169,9 +7169,9 @@ تەستىقلا - Approve all + ھەممىنى تەستىقلاڭ - Deny all + ھەممىنى رەت قىلىڭ پۈتۈن ئېكران ئۇقتۇرۇشىنى ئاچامسىز؟ @@ -7346,7 +7346,7 @@ ئەگەر زاپاس مېدىيا ۋە قوشۇمچە ھۆججەتلەرنى ئەسلىگە كەلتۈرۈشتىن ئاتلاپ ئۆتۈپ كەتسىڭىز ، كىيىن ساقلاش بوشلۇقى چىققاندا چۈشۈرگىلى بولىدۇ. - Backup failed + زاپاسلاش مەغلۇپ بولدى An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7387,7 +7387,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + تەپسىلاتى @@ -7828,13 +7828,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + كونا ئۈسكۈنىڭىزدە سىگنالنى ئېچىڭ Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + ئەسلىگە كەلتۈرۈشتى ئاتلاڭ diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index b7a33a7a96..c43dc28289 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1052,15 +1052,15 @@ Продовжити без перенесення - Edit name + Редагувати ім’я Edit device name - Device name + Назва пристрою - Save + Зберегти Device name updated @@ -1456,7 +1456,7 @@ Отримання інформації про резервну копію… - Skip restore + Не відновлювати Сповіщати про згадки @@ -3312,7 +3312,7 @@ Написання повідомлення Відрити клавіатуру емоджі Мініатюра вкладення - Перемикач швидкого прикріплення з камери + Відкрити камеру й прикріпити фото або відео Записати та надіслати голосове повідомлення Утримувати кнопку запису голосового повідомлення @@ -4552,7 +4552,7 @@ Перенести акаунт Пропустити - Skip restore + Не відновлювати Резервні копії чатів Перенести акаунт Перенесіть акаунт на новий пристрій з ОС Android @@ -5099,7 +5099,7 @@ Кодова фраза — це ще один спосіб відновлення платіжного профілю. Збережіть свою фразу - Оновить PIN-код + Оновіть PIN-код Якщо у вас велика сума на рахунку, рекомендуємо створити буквено-цифровий PIN-код для надійнішого захисту акаунту. Оновити PIN-код @@ -5138,7 +5138,7 @@ Ваша кодова фраза — це унікальна фраза з %1$d слів. Вона необхідна для відновлення балансу. Почати Ввести вручну - Вставити з буферу обміну + Вставити з буфера обміну Продовжити без збереження? @@ -5163,7 +5163,7 @@ Редагувати Ваша кодова фраза Запишіть ці %1$d слова в такому ж порядку. Збережіть цей список слів у надійному місці. - Перевірте, чи ви правильно ввели кодову фразу. + Перевірте, чи правильно ви ввели кодову фразу. Не робіть знімків екрана й не пересилайте електронною поштою. Платіжний профіль відновлено. Неправильна кодова фраза @@ -7646,9 +7646,9 @@ Прийняти - Approve all + Прийняти всі - Deny all + Відхилити всі Увімкнути повноекранні сповіщення? @@ -7829,7 +7829,7 @@ Якщо не відновити дані з резервної копії зараз, решту медіафайлів і вкладень можна буде завантажити пізніше, коли на пристрої буде вільне місце. - Backup failed + Не вдалося створити резервну копію An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7870,7 +7870,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Докладніше @@ -8329,13 +8329,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Відкрийте Signal на старому телефоні Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Не відновлювати diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index a7197c1306..82a1e34611 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -1012,15 +1012,15 @@ منتقلی کے بغیر جاری رکھیں - Edit name + نام ایڈٹ کریں Edit device name - Device name + ڈیوائس کا نام - Save + محفوظ کریں Device name updated @@ -1380,7 +1380,7 @@ بیک اپ کی تفصیلات حاصل کی جا رہی ہیں… - Skip restore + بحال کرنا نظر انداز کریں جب مجھے کوئی ذکر کرتا ہے تو مجھے مطلع کریں @@ -4330,7 +4330,7 @@ اکاؤنٹ منتقل کریں چھوڑ دو - Skip restore + بحال کرنا نظر انداز کریں چیٹ کے بیک اپس اکاؤنٹ منتقل کریں اکاؤنٹ کو کسی نئے Android ڈیوائس میں منتقل کریں @@ -7328,9 +7328,9 @@ منظور کریں - Approve all + سب منظور کریں - Deny all + سب مسترد کریں پوری اسکرین کی اطلاعات کو آن کریں؟ @@ -7507,7 +7507,7 @@ اگر آپ بحالی کے عمل کو چھوڑ دیتے ہیں، تو آپ کے بیک اپ میں موجود باقی میڈیا اور منسلکات کو بعد میں اس وقت ڈاؤن لوڈ کیا جا سکتا ہے جب اسٹوریج کی جگہ دستیاب ہو۔ - Backup failed + بیک اپ ناکام ہوگیا An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7548,7 +7548,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + مزید جانیں @@ -7995,13 +7995,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + اپنی پرانی ڈیوائس پر Signal کھولیں Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + بحال کرنا نظر انداز کریں diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 7732c913f5..af6210d726 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -992,15 +992,15 @@ Tiếp tục và không chuyển - Edit name + Sửa tên Edit device name - Device name + Tên thiết bị - Save + Lưu Device name updated @@ -1342,7 +1342,7 @@ Đang thu thập thông tin bản sao lưu… - Skip restore + Bỏ qua Thông báo khi tôi được nhắc tên @@ -4219,7 +4219,7 @@ Chuyển tài khoản Bỏ qua - Skip restore + Bỏ qua Sao lưu tin nhắn Chuyển tài khoản Chuyển tài khoản sang một thiết bị Android mới @@ -7169,9 +7169,9 @@ Đồng ý - Approve all + Chấp nhận tất cả - Deny all + Từ chối tất cả Bật thông báo toàn màn hình? @@ -7346,7 +7346,7 @@ Nếu bỏ qua việc khôi phục, các tập tin đa phương tiện và tập tin đính kèm còn lại trong bản sao lưu của bạn có thể được tải xuống sau khi có dung lượng trống. - Backup failed + Sao lưu thất bại. An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7387,7 +7387,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + Tìm hiểu thêm @@ -7828,13 +7828,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + Mở Signal trên thiết bị cũ của bạn Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + Bỏ qua diff --git a/app/src/main/res/values-yue/strings.xml b/app/src/main/res/values-yue/strings.xml index f782a2345a..dfebdc0eb4 100644 --- a/app/src/main/res/values-yue/strings.xml +++ b/app/src/main/res/values-yue/strings.xml @@ -992,15 +992,15 @@ 繼續但係唔轉移 - Edit name + 編輯名稱 Edit device name - Device name + 裝置名稱 - Save + 儲存 Device name updated @@ -1342,7 +1342,7 @@ 攞緊備份詳細資料… - Skip restore + 跳過還原 點名講起我嘅時候通知 @@ -4219,7 +4219,7 @@ 轉移帳戶 飛過 - Skip restore + 跳過還原 傾偈備份 過帳戶落新機 將帳戶轉移去一部新嘅 Android 機度 @@ -7169,9 +7169,9 @@ 批准 - Approve all + 全部批准 - Deny all + 全部拒絕 係咪要開啟全螢幕通知? @@ -7346,7 +7346,7 @@ 如果你跳過還原,備份入面剩低嘅媒體同附件可以之後喺儲存空間夠位嗰陣下載。 - Backup failed + 備份唔到 An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7387,7 +7387,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + 了解詳情 @@ -7828,13 +7828,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + 喺你部舊機打開 Signal Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + 跳過還原 diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 297938d5ef..8d422ed0d6 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -992,15 +992,15 @@ 继续但不传输 - Edit name + 编辑名称 Edit device name - Device name + 设备名称 - Save + 保存 Device name updated @@ -1342,7 +1342,7 @@ 正在获取备份详情… - Skip restore + 跳过恢复 提到我时通知我 @@ -4219,7 +4219,7 @@ 转移帐户 跳过 - Skip restore + 跳过恢复 聊天备份 转移帐户 转移帐户至新的 Android 设备 @@ -7169,9 +7169,9 @@ 批准 - Approve all + 全部批准 - Deny all + 全部拒绝 要开启全屏通知吗? @@ -7346,7 +7346,7 @@ 如果跳过恢复备份中剩余的媒体和附件,您后续可以在存储空间可用时下载它们。 - Backup failed + 备份失败 An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7387,7 +7387,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + 了解详情 @@ -7828,13 +7828,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + 在您的旧设备上打开 Signal Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + 跳过恢复 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 701bf75aca..420fc9d34e 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -992,15 +992,15 @@ 不轉移並繼續 - Edit name + 編輯名稱 Edit device name - Device name + 裝置名稱 - Save + 儲存 Device name updated @@ -1342,7 +1342,7 @@ 正在擷取備份詳細資訊… - Skip restore + 跳過還原 通知我有關提及 @@ -4219,7 +4219,7 @@ 轉移帳戶 略過 - Skip restore + 跳過還原 聊天備份 轉移帳戶 轉移帳戶至新的 Android 裝置 @@ -7169,9 +7169,9 @@ 核准 - Approve all + 全部批准 - Deny all + 全部拒絕 要開啟全螢幕通知? @@ -7346,7 +7346,7 @@ 如果你跳過還原,備份內尚餘的媒體和附件可以稍後在儲存空間可用時下載。 - Backup failed + 備份失敗 An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7387,7 +7387,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + 了解更多 @@ -7828,13 +7828,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + 在你的舊裝置開啟 Signal Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + 跳過還原 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index db8c440d31..859dadce51 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -992,15 +992,15 @@ 不轉移並繼續 - Edit name + 編輯名稱 Edit device name - Device name + 裝置名稱 - Save + 儲存 Device name updated @@ -1342,7 +1342,7 @@ 正在擷取備份詳細資訊… - Skip restore + 跳過還原 通知我提及。 @@ -4219,7 +4219,7 @@ 轉移帳號 略過 - Skip restore + 跳過還原 聊天備份 轉移帳號 將帳號轉移到新的Android裝置 @@ -7169,9 +7169,9 @@ 核准 - Approve all + 全部批准 - Deny all + 全部拒絕 要開啟全螢幕通知? @@ -7346,7 +7346,7 @@ 如果你跳過還原,備份內尚餘的媒體和附件可以稍後在儲存空間可用時下載。 - Backup failed + 備份失敗 An error occurred and your backup could not be completed. Make sure you\'re on the latest version of Signal and try again. If this problem persists, contact support. @@ -7387,7 +7387,7 @@ Your last backup couldn\'t be completed. Make sure you\'re on the latest version of Signal and try again. - Learn more + 了解更多 @@ -7828,13 +7828,13 @@ Because you\'re moving from iPhone to Android, the only way to transfer your messages and media is by enabling Signal Backups on your old device. - Open Signal on your old device + 在你的舊裝置開啟 Signal Tap Settings > Backups Enable backups and wait until your backup is complete - Skip restore + 跳過還原 diff --git a/app/static-ips.gradle.kts b/app/static-ips.gradle.kts index 7660d58d97..c61ff8b37c 100644 --- a/app/static-ips.gradle.kts +++ b/app/static-ips.gradle.kts @@ -1,9 +1,9 @@ rootProject.extra["service_ips"] = """new String[]{"13.248.212.111","76.223.92.165"}""" -rootProject.extra["storage_ips"] = """new String[]{"142.251.40.147"}""" +rootProject.extra["storage_ips"] = """new String[]{"142.250.81.243"}""" rootProject.extra["cdn_ips"] = """new String[]{"18.161.21.122","18.161.21.4","18.161.21.66","18.161.21.70"}""" rootProject.extra["cdn2_ips"] = """new String[]{"104.18.37.148","172.64.150.108"}""" rootProject.extra["cdn3_ips"] = """new String[]{"104.18.37.148","172.64.150.108"}""" -rootProject.extra["sfu_ips"] = """new String[]{"34.117.120.77"}""" +rootProject.extra["sfu_ips"] = """new String[]{"34.144.226.121"}""" rootProject.extra["content_proxy_ips"] = """new String[]{"107.178.250.75"}""" rootProject.extra["svr2_ips"] = """new String[]{"20.119.62.85"}""" rootProject.extra["cdsi_ips"] = """new String[]{"40.122.45.194"}""" From e47861796e3af0879dfd3a67f549674efb9c0c33 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 6 Dec 2024 16:08:08 -0500 Subject: [PATCH 56/56] Bump version to 7.27.1 --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 862722f96a..24464b86d7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,8 +21,8 @@ plugins { apply(from = "static-ips.gradle.kts") -val canonicalVersionCode = 1490 -val canonicalVersionName = "7.27.0" +val canonicalVersionCode = 1491 +val canonicalVersionName = "7.27.1" val currentHotfixVersion = 0 val maxHotfixVersions = 100