Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
Weblate 2018-07-03 08:38:13 +00:00
commit b8d655e1e3
58 changed files with 1039 additions and 277 deletions

View file

@ -4,8 +4,15 @@ Changes in 0.6.18 ()
Improvements:
* Upgrade MatrixKit version ().
* RoomVC: Add a re-request keys button on message unable to decrypt (#1879).
* Analytics: Move code from AppDelegate to a dedicated class: Analytics.
* Analytics: Track Matrix SDK stats (time to startup the app).
* Crypto: Add telemetry for events unable to decrypt (UTDs).
* Added the i18n localisation strings to the accessibility labels (#1842), thanks to @einMarco (PR#1906).
* Added titles to sound files ID3 tags.
Bug fix:
* RoomVC: Read receipts processing dramatically slows down UI (#1899).
* E2E messages not decrypted in notifs after logging back in (#1914).
Changes in 0.6.17 (2018-06-01)
===============================================
@ -1125,4 +1132,4 @@ Changes in Vector iOS in 0.1.0 (2016-01-29)
Changes in Vector iOS in 0.0.1 (2015-11-16)
===============================================
* Creation : The first implementation of Vector application based on Matrix iOS Kit v0.2.7.
* Creation : The first implementation of Vector application based on Matrix iOS Kit v0.2.7.

View file

@ -59,6 +59,13 @@
32471CE11F13AC1500BDF50A /* RoomMembershipExpandedWithPaginationTitleBubbleCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 32471CDF1F13AC1500BDF50A /* RoomMembershipExpandedWithPaginationTitleBubbleCell.m */; };
32471CE21F13AC1500BDF50A /* RoomMembershipExpandedWithPaginationTitleBubbleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32471CE01F13AC1500BDF50A /* RoomMembershipExpandedWithPaginationTitleBubbleCell.xib */; };
325E1C151E8D03950018D91E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 325E1C131E8D03950018D91E /* LaunchScreen.storyboard */; };
3267EFB220E2A04100FF1CAA /* Analytics.m in Sources */ = {isa = PBXBuildFile; fileRef = 3267EFB120E2A04100FF1CAA /* Analytics.m */; };
3267EFB720E379FE00FF1CAA /* CHANGES.rst in Resources */ = {isa = PBXBuildFile; fileRef = 3267EFB320E379FD00FF1CAA /* CHANGES.rst */; };
3267EFB820E379FE00FF1CAA /* Podfile in Resources */ = {isa = PBXBuildFile; fileRef = 3267EFB420E379FD00FF1CAA /* Podfile */; };
3267EFB920E379FE00FF1CAA /* AUTHORS.rst in Resources */ = {isa = PBXBuildFile; fileRef = 3267EFB520E379FD00FF1CAA /* AUTHORS.rst */; };
3267EFBA20E379FE00FF1CAA /* README.rst in Resources */ = {isa = PBXBuildFile; fileRef = 3267EFB620E379FD00FF1CAA /* README.rst */; };
3267EFC020E4A3DD00FF1CAA /* DecryptionFailureTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 3267EFBE20E4A3DD00FF1CAA /* DecryptionFailureTracker.m */; };
3267EFC320E5055800FF1CAA /* DecryptionFailure.m in Sources */ = {isa = PBXBuildFile; fileRef = 3267EFC220E5055800FF1CAA /* DecryptionFailure.m */; };
327382B51F276AD200356143 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 327382A81F276AD200356143 /* InfoPlist.strings */; };
327382B61F276AD200356143 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 327382AA1F276AD200356143 /* Localizable.strings */; };
327382B71F276AD200356143 /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = 327382AC1F276AD200356143 /* Vector.strings */; };
@ -113,6 +120,8 @@
9D686B069F967C4D4BBC610F /* Pods_RiotPods_SiriIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB50EEBE8214352B9EBD6394 /* Pods_RiotPods_SiriIntents.framework */; };
B19A173920B7F94800DF0BB0 /* DeactivateAccountViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B19A173820B7F94800DF0BB0 /* DeactivateAccountViewController.m */; };
B19A173B20B7F96700DF0BB0 /* DeactivateAccountViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B19A173A20B7F96700DF0BB0 /* DeactivateAccountViewController.storyboard */; };
B1D818C020EA4C7400D5F36D /* RiotSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D818BF20EA4C7400D5F36D /* RiotSettings.swift */; };
B1D818C120EA794400D5F36D /* RiotSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D818BF20EA4C7400D5F36D /* RiotSettings.swift */; };
DDDE2AB95F865F2292B1D315 /* Pods_RiotPods_RiotShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 23D7292481328A48B8D5D4ED /* Pods_RiotPods_RiotShareExtension.framework */; };
F0131DE51F2200D600CBF707 /* RiotSplitViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F0131DE41F2200D600CBF707 /* RiotSplitViewController.m */; };
F0173EB51FCF346800B5F6A3 /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = F0173EAF1FCF346800B5F6A3 /* Vector.strings */; };
@ -725,6 +734,16 @@
32471CDF1F13AC1500BDF50A /* RoomMembershipExpandedWithPaginationTitleBubbleCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RoomMembershipExpandedWithPaginationTitleBubbleCell.m; sourceTree = "<group>"; };
32471CE01F13AC1500BDF50A /* RoomMembershipExpandedWithPaginationTitleBubbleCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RoomMembershipExpandedWithPaginationTitleBubbleCell.xib; sourceTree = "<group>"; };
325E1C141E8D03950018D91E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
3267EFB020E2A04100FF1CAA /* Analytics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Analytics.h; path = Riot/Analytics/Analytics.h; sourceTree = SOURCE_ROOT; };
3267EFB120E2A04100FF1CAA /* Analytics.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = Analytics.m; path = Riot/Analytics/Analytics.m; sourceTree = SOURCE_ROOT; };
3267EFB320E379FD00FF1CAA /* CHANGES.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGES.rst; sourceTree = "<group>"; };
3267EFB420E379FD00FF1CAA /* Podfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Podfile; sourceTree = "<group>"; };
3267EFB520E379FD00FF1CAA /* AUTHORS.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AUTHORS.rst; sourceTree = "<group>"; };
3267EFB620E379FD00FF1CAA /* README.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.rst; sourceTree = "<group>"; };
3267EFBE20E4A3DD00FF1CAA /* DecryptionFailureTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DecryptionFailureTracker.m; path = Riot/Analytics/DecryptionFailureTracker.m; sourceTree = SOURCE_ROOT; };
3267EFBF20E4A3DD00FF1CAA /* DecryptionFailureTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DecryptionFailureTracker.h; path = Riot/Analytics/DecryptionFailureTracker.h; sourceTree = SOURCE_ROOT; };
3267EFC120E5055800FF1CAA /* DecryptionFailure.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DecryptionFailure.h; path = Riot/Analytics/DecryptionFailure.h; sourceTree = SOURCE_ROOT; };
3267EFC220E5055800FF1CAA /* DecryptionFailure.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = DecryptionFailure.m; path = Riot/Analytics/DecryptionFailure.m; sourceTree = SOURCE_ROOT; };
327382A91F276AD200356143 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = InfoPlist.strings; sourceTree = "<group>"; };
327382AB1F276AD200356143 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = Localizable.strings; sourceTree = "<group>"; };
327382AD1F276AD200356143 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = Vector.strings; sourceTree = "<group>"; };
@ -804,6 +823,9 @@
B19A173720B7F94800DF0BB0 /* DeactivateAccountViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeactivateAccountViewController.h; sourceTree = "<group>"; };
B19A173820B7F94800DF0BB0 /* DeactivateAccountViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DeactivateAccountViewController.m; sourceTree = "<group>"; };
B19A173A20B7F96700DF0BB0 /* DeactivateAccountViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = DeactivateAccountViewController.storyboard; sourceTree = "<group>"; };
B1D818BC20E66C3300D5F36D /* Riot-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Riot-Bridging-Header.h"; sourceTree = "<group>"; };
B1D818BF20EA4C7400D5F36D /* RiotSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RiotSettings.swift; sourceTree = "<group>"; };
B1D818C220EA7DB500D5F36D /* RiotShareExtension-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "RiotShareExtension-Bridging-Header.h"; path = "RiotShareExtension/RiotShareExtension-Bridging-Header.h"; sourceTree = SOURCE_ROOT; };
C195C53961EA28E6900AEB68 /* Pods-Riot.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Riot.release.xcconfig"; path = "Pods/Target Support Files/Pods-Riot/Pods-Riot.release.xcconfig"; sourceTree = "<group>"; };
C5258DFF261AA3AB228A3F11 /* Pods-RiotPods-RiotShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-RiotShareExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RiotPods-RiotShareExtension/Pods-RiotPods-RiotShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
DB50EEBE8214352B9EBD6394 /* Pods_RiotPods_SiriIntents.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RiotPods_SiriIntents.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -1489,6 +1511,7 @@
isa = PBXGroup;
children = (
2466B7551F2F80B800AE27B0 /* Info.plist */,
B1D818C220EA7DB500D5F36D /* RiotShareExtension-Bridging-Header.h */,
2466B7561F2F80B800AE27B0 /* RiotShareExtension.entitlements */,
24D6B3441F3C8F8A00FC7A71 /* ViewController */,
2439DD5F1F6BBE390090F42D /* Views */,
@ -1597,6 +1620,20 @@
path = "jitsi-meet";
sourceTree = "<group>";
};
3267EFAF20E2A00F00FF1CAA /* Analytics */ = {
isa = PBXGroup;
children = (
3267EFB020E2A04100FF1CAA /* Analytics.h */,
3267EFB120E2A04100FF1CAA /* Analytics.m */,
3267EFBF20E4A3DD00FF1CAA /* DecryptionFailureTracker.h */,
3267EFBE20E4A3DD00FF1CAA /* DecryptionFailureTracker.m */,
3267EFC120E5055800FF1CAA /* DecryptionFailure.h */,
3267EFC220E5055800FF1CAA /* DecryptionFailure.m */,
);
name = Analytics;
path = "New Group";
sourceTree = "<group>";
};
327382A71F276AD200356143 /* de.lproj */ = {
isa = PBXGroup;
children = (
@ -1796,6 +1833,7 @@
F083BB081E7009EC00A9B29C /* Riot */ = {
isa = PBXGroup;
children = (
3267EFAF20E2A00F00FF1CAA /* Analytics */,
F083BB091E7009EC00A9B29C /* API */,
F083BB0E1E7009EC00A9B29C /* Assets */,
F083BBE41E7009EC00A9B29C /* Categories */,
@ -2215,6 +2253,7 @@
F083BC151E7009EC00A9B29C /* Tools.m */,
F083BC161E7009EC00A9B29C /* RiotDesignValues.h */,
F083BC171E7009EC00A9B29C /* RiotDesignValues.m */,
B1D818BF20EA4C7400D5F36D /* RiotSettings.swift */,
);
path = Utils;
sourceTree = "<group>";
@ -2667,6 +2706,7 @@
F083BEA41E700B2800A9B29C /* Supporting Files */ = {
isa = PBXGroup;
children = (
B1D818BC20E66C3300D5F36D /* Riot-Bridging-Header.h */,
F0DD05CF1F615ECF00CB5292 /* LaunchScreenRiot.png */,
F083BBED1E7009EC00A9B29C /* empty.mm */,
F083BBEE1E7009EC00A9B29C /* GoogleService-Info.plist */,
@ -2682,6 +2722,10 @@
isa = PBXGroup;
children = (
F083BB081E7009EC00A9B29C /* Riot */,
3267EFB520E379FD00FF1CAA /* AUTHORS.rst */,
3267EFB320E379FD00FF1CAA /* CHANGES.rst */,
3267EFB420E379FD00FF1CAA /* Podfile */,
3267EFB620E379FD00FF1CAA /* README.rst */,
F083BB021E7005FD00A9B29C /* RiotTests */,
24CBEC4F1F0EAD310093EABB /* RiotShareExtension */,
92726A441F58737A004AD26F /* SiriIntents */,
@ -2870,6 +2914,7 @@
F094A9A11B78D8F000B1FBBF = {
CreatedOnToolsVersion = 6.2;
DevelopmentTeam = 7J4U792NQT;
LastSwiftMigration = 0940;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.ApplicationGroups.iOS = {
@ -2988,6 +3033,7 @@
F083BD641E7009ED00A9B29C /* direct_icon@3x.png in Resources */,
F083BE261E7009ED00A9B29C /* SegmentedViewController.xib in Resources */,
F083BE161E7009ED00A9B29C /* MediaAlbumContentViewController.xib in Resources */,
3267EFB720E379FE00FF1CAA /* CHANGES.rst in Resources */,
F083BD441E7009ED00A9B29C /* call_speaker_on_icon@3x.png in Resources */,
F0614A0F1EDDCCE700F5DC9A /* jump_to_unread@3x.png in Resources */,
F083BE511E7009ED00A9B29C /* RoomOutgoingEncryptedTextMsgBubbleCell.xib in Resources */,
@ -3040,6 +3086,7 @@
32BB89F0204D86DA002F3AEC /* InfoPlist.strings in Resources */,
F083BD3E1E7009ED00A9B29C /* call_hangup_icon@3x.png in Resources */,
32AE61F21F0D2183007255F4 /* InfoPlist.strings in Resources */,
3267EFB920E379FE00FF1CAA /* AUTHORS.rst in Resources */,
F083BDB91E7009ED00A9B29C /* remove_icon_pink.png in Resources */,
F083BE531E7009ED00A9B29C /* RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.xib in Resources */,
F083BD7F1E7009ED00A9B29C /* error@3x.png in Resources */,
@ -3070,6 +3117,7 @@
F083BD5F1E7009ED00A9B29C /* details_icon.png in Resources */,
F083BE241E7009ED00A9B29C /* RoomViewController.xib in Resources */,
F083BE7B1E7009ED00A9B29C /* RoomInputToolbarView.xib in Resources */,
3267EFB820E379FE00FF1CAA /* Podfile in Resources */,
F0E05A3D1EA0F9EB004B83FB /* tab_people_selected@2x.png in Resources */,
F0E05A451EA0F9EB004B83FB /* tab_rooms.png in Resources */,
F083BE751E7009ED00A9B29C /* RoomOutgoingTextMsgWithoutSenderNameBubbleCell.xib in Resources */,
@ -3319,6 +3367,7 @@
F0E05A3C1EA0F9EB004B83FB /* tab_people_selected.png in Resources */,
F083BD221E7009ED00A9B29C /* add_participant.png in Resources */,
F083BDC61E7009ED00A9B29C /* search_bg@2x.png in Resources */,
3267EFBA20E379FE00FF1CAA /* README.rst in Resources */,
F083BD421E7009ED00A9B29C /* call_speaker_on_icon.png in Resources */,
F04AF26D1F83A4C100D20F4D /* Localizable.strings in Resources */,
F083BD9A1E7009ED00A9B29C /* logo@3x.png in Resources */,
@ -3477,6 +3526,7 @@
buildActionMask = 2147483647;
files = (
24EEE5A01F23A08900B3C705 /* RoomTableViewCell.m in Sources */,
B1D818C120EA794400D5F36D /* RiotSettings.swift in Sources */,
24D6B3581F3C90D300FC7A71 /* ShareDataSource.m in Sources */,
245FC3EF1F3DD30800603C6A /* RecentCellData.m in Sources */,
24D6B35C1F3CA03600FC7A71 /* RoomsListViewController.m in Sources */,
@ -3543,6 +3593,7 @@
321082B21F0E9F40002E0091 /* RoomMembershipCollapsedBubbleCell.m in Sources */,
32C2356F1F7B871800E38FC5 /* WidgetPickerViewController.m in Sources */,
F083BE661E7009ED00A9B29C /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.m in Sources */,
3267EFB220E2A04100FF1CAA /* Analytics.m in Sources */,
F083BE141E7009ED00A9B29C /* HomeViewController.m in Sources */,
F083BDFB1E7009ED00A9B29C /* RoomSearchDataSource.m in Sources */,
32EF474920B6EE990031695C /* StickerPickerViewController.m in Sources */,
@ -3591,6 +3642,7 @@
F083BE2D1E7009ED00A9B29C /* ForgotPasswordInputsView.m in Sources */,
F083BE7E1E7009ED00A9B29C /* InviteRecentTableViewCell.m in Sources */,
F083BE3C1E7009ED00A9B29C /* RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */,
3267EFC320E5055800FF1CAA /* DecryptionFailure.m in Sources */,
F083BE211E7009ED00A9B29C /* RoomSearchViewController.m in Sources */,
32185B311F20FA2B00752141 /* LanguagePickerViewController.m in Sources */,
F083BE3E1E7009ED00A9B29C /* RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m in Sources */,
@ -3632,6 +3684,7 @@
F083BE781E7009ED00A9B29C /* RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */,
F083BE5E1E7009ED00A9B29C /* RoomIncomingAttachmentWithPaginationTitleBubbleCell.m in Sources */,
F083BD1E1E7009ED00A9B29C /* AppDelegate.m in Sources */,
B1D818C020EA4C7400D5F36D /* RiotSettings.swift in Sources */,
F083BE8A1E7009ED00A9B29C /* ExpandedRoomTitleView.m in Sources */,
F083BE0C1E7009ED00A9B29C /* ContactDetailsViewController.m in Sources */,
F083BE821E7009ED00A9B29C /* RecentTableViewCell.m in Sources */,
@ -3655,6 +3708,7 @@
F083BE101E7009ED00A9B29C /* CountryPickerViewController.m in Sources */,
F083BE581E7009ED00A9B29C /* RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */,
F083BDF61E7009ED00A9B29C /* Contact.m in Sources */,
3267EFC020E4A3DD00FF1CAA /* DecryptionFailureTracker.m in Sources */,
F083BE961E7009ED00A9B29C /* MessagesSearchResultAttachmentBubbleCell.m in Sources */,
F083BE391E7009ED00A9B29C /* RoomEncryptedDataBubbleCell.m in Sources */,
F083BDF01E7009ED00A9B29C /* UIViewController+RiotSearch.m in Sources */,
@ -3988,12 +4042,19 @@
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 7J4U792NQT;
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"COCOAPODS=1",
"IS_SHARE_EXTENSION=1",
);
INFOPLIST_FILE = RiotShareExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = im.vector.app.shareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/$(PRODUCT_NAME)/RiotShareExtension-Bridging-Header.h";
SWIFT_VERSION = 4.0;
};
name = Debug;
};
@ -4011,12 +4072,19 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 7J4U792NQT;
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"COCOAPODS=1",
"IS_SHARE_EXTENSION=1",
);
INFOPLIST_FILE = RiotShareExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = im.vector.app.shareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/$(PRODUCT_NAME)/RiotShareExtension-Bridging-Header.h";
SWIFT_VERSION = 4.0;
};
name = Release;
};
@ -4082,6 +4150,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEFINES_MODULE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
@ -4099,7 +4168,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -4128,6 +4197,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
DEFINES_MODULE = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
@ -4138,7 +4208,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
@ -4150,7 +4220,9 @@
isa = XCBuildConfiguration;
baseConfigurationReference = A5030B7C3C0B6EB83A9257BD /* Pods-RiotPods-Riot.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Riot/Riot.entitlements;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
DEVELOPMENT_TEAM = 7J4U792NQT;
@ -4177,6 +4249,9 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = im.vector.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/$(PRODUCT_NAME)/Riot-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@ -4185,7 +4260,9 @@
isa = XCBuildConfiguration;
baseConfigurationReference = F546BCBBB9BBEE67DB28878A /* Pods-RiotPods-Riot.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Riot/Riot.entitlements;
DEVELOPMENT_TEAM = 7J4U792NQT;
ENABLE_BITCODE = NO;
@ -4211,6 +4288,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = im.vector.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/$(PRODUCT_NAME)/Riot-Bridging-Header.h";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
@ -4218,6 +4297,7 @@
F094A9CC1B78D8F000B1FBBF /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
DEVELOPMENT_TEAM = 7J4U792NQT;
FRAMEWORK_SEARCH_PATHS = (
@ -4239,6 +4319,7 @@
F094A9CD1B78D8F000B1FBBF /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
DEVELOPMENT_TEAM = 7J4U792NQT;
FRAMEWORK_SEARCH_PATHS = (

View file

@ -24,6 +24,8 @@
#import <MatrixKit/MatrixKit.h>
#import "Riot-Swift.h"
static RageShakeManager* sharedInstance = nil;
@interface RageShakeManager() {
@ -111,7 +113,7 @@ static RageShakeManager* sharedInstance = nil;
// Start only if the application is in foreground
// And if the rageshake user setting is enabled
if ([AppDelegate theDelegate].isAppForeground
&& [[NSUserDefaults standardUserDefaults] boolForKey:@"enableRageShake"]
&& RiotSettings.shared.enableRageShake
&& !confirmationAlert)
{
NSLog(@"[RageShakeManager] Start shaking with [%@]", [responder class]);
@ -163,8 +165,7 @@ static RageShakeManager* sharedInstance = nil;
self->confirmationAlert = nil;
// Disable rageshake user setting
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"enableRageShake"];
[[NSUserDefaults standardUserDefaults] synchronize];
RiotSettings.shared.enableRageShake = NO;
}
}]];

View file

@ -0,0 +1,63 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <Foundation/Foundation.h>
#import <MatrixSDK/MatrixSDK.h>
#import "DecryptionFailureTracker.h"
/**
`Analytics` sends analytics to an analytics tool.
*/
@interface Analytics : NSObject <MXAnalyticsDelegate, MXDecryptionFailureDelegate>
/**
Returns the shared Analytics manager.
@return the shared Analytics manager.
*/
+ (instancetype)sharedInstance;
/**
Start doing analytics if the settings `enableCrashReport` is enabled.
*/
- (void)start;
/**
Stop doing analytics.
*/
- (void)stop;
/**
Track a screen display.
@param screenName the name of the displayed screen.
*/
- (void)trackScreen:(NSString*)screenName;
/**
Flush analytics data.
*/
- (void)dispatch;
/**
Track how long the launch screen has been displayed to the end user.
@param seconds the duration in seconds.
*/
- (void)trackLaunchScreenDisplayDuration: (NSTimeInterval)seconds;
@end

187
Riot/Analytics/Analytics.m Normal file
View file

@ -0,0 +1,187 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "Analytics.h"
#import "AppDelegate.h"
#import "Riot-Swift.h"
// All metrics are store under a Piwik category called "Metrics".
// Then, there are 2 Piwik actions: "iOS.startup" and "iOS.stats" (these actions
// are namespaced by plaform to have a nice rendering on the Piwik website).
// Then, we use constants defined by the Matrix SDK as Piwik Names (ex:"mountData")
NSString *const kAnalyticsMetricsCategory = @"Metrics";
NSString *const kAnalyticsMetricsActionPattern = @"iOS.%@";
// E2E telemetry is stored under a Piwik category called "E2E".
NSString *const kAnalyticsE2eCategory = @"E2E";
NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure";
NSString *const kAnalyticsE2eDecryptionFailureReasonNoReason = @"no_reason";
@import PiwikTracker;
@implementation Analytics
+ (instancetype)sharedInstance
{
static Analytics *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[Analytics alloc] init];
});
return sharedInstance;
}
- (void)start
{
NSDictionary *piwikConfig = [[NSUserDefaults standardUserDefaults] objectForKey:@"piwik"];
[PiwikTracker configureSharedInstanceWithSiteID:piwikConfig[@"siteId"]
baseURL:[NSURL URLWithString:piwikConfig[@"url"]]
userAgent:@"iOSPiwikTracker"];
// Check whether the user has enabled the sending of crash reports.
if (RiotSettings.shared.enableCrashReport)
{
[PiwikTracker shared].isOptedOut = NO;
[[PiwikTracker shared] setCustomVariableWithIndex:1 name:@"App Platform" value:@"iOS Platform"];
[[PiwikTracker shared] setCustomVariableWithIndex:2 name:@"App Version" value:[AppDelegate theDelegate].appVersion];
// The language is either the one selected by the user within the app
// or, else, the one configured by the OS
NSString *language = [NSBundle mxk_language] ? [NSBundle mxk_language] : [[NSBundle mainBundle] preferredLocalizations][0];
[[PiwikTracker shared] setCustomVariableWithIndex:4 name:@"Chosen Language" value:language];
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
if (account)
{
[[PiwikTracker shared] setCustomVariableWithIndex:7 name:@"Homeserver URL" value:account.mxCredentials.homeServer];
[[PiwikTracker shared] setCustomVariableWithIndex:8 name:@"Identity Server URL" value:account.identityServerURL];
}
// TODO: We should also track device and os version
// But that needs to be decided for all platforms
// Catch and log crashes
[MXLogger logCrashes:YES];
[MXLogger setBuildVersion:[AppDelegate theDelegate].build];
#ifdef DEBUG
// Disable analytics in debug as it pollutes stats
[PiwikTracker shared].isOptedOut = YES;
#endif
}
else
{
NSLog(@"[AppDelegate] The user decided to not send analytics");
[PiwikTracker shared].isOptedOut = YES;
[MXLogger logCrashes:NO];
}
}
- (void)stop
{
[PiwikTracker shared].isOptedOut = YES;
[MXLogger logCrashes:NO];
}
- (void)trackScreen:(NSString *)screenName
{
// Use the same pattern as Android
NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
NSString *appVersion = [AppDelegate theDelegate].appVersion;
[[PiwikTracker shared] trackWithView:@[@"ios", appName, appVersion, screenName]
url:nil];
}
- (void)dispatch
{
[[PiwikTracker shared] dispatch];
}
- (void)trackLaunchScreenDisplayDuration:(NSTimeInterval)seconds
{
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory];
[[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
action:action
name:kMXAnalyticsStartupLaunchScreen
number:@(seconds * 1000)
url:nil];
}
#pragma mark - MXAnalyticsDelegate
- (void)trackStartupStorePreloadDuration: (NSTimeInterval)seconds
{
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory];
[[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
action:action
name:kMXAnalyticsStartupStorePreload
number:@(seconds * 1000)
url:nil];
}
- (void)trackStartupMountDataDuration: (NSTimeInterval)seconds
{
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory];
[[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
action:action
name:kMXAnalyticsStartupMountData
number:@(seconds * 1000)
url:nil];
}
- (void)trackStartupSyncDuration: (NSTimeInterval)seconds isInitial: (BOOL)isInitial
{
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory];
[[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
action:action
name:isInitial ? kMXAnalyticsStartupInititialSync : kMXAnalyticsStartupIncrementalSync
number:@(seconds * 1000)
url:nil];
}
- (void)trackRoomCount: (NSUInteger)roomCount
{
NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStatsCategory];
[[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory
action:action
name:kMXAnalyticsStatsRooms
number:@(roomCount)
url:nil];
}
#pragma mark - MXDecryptionFailureDelegate
- (void)trackFailures:(NSUInteger)failuresCount
{
[[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsE2eCategory
action:kAnalyticsE2eDecryptionFailureAction
name:kAnalyticsE2eDecryptionFailureReasonNoReason
number:@(failuresCount)
url:nil];
}
@end

View file

@ -0,0 +1,39 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <Foundation/Foundation.h>
/**
`DecryptionFailure` represents a decryption failure.
*/
@interface DecryptionFailure : NSObject
/**
The id of the event that was unabled to decrypt.
*/
@property (nonatomic) NSString *failedEventId;
/**
The time the failure has been reported.
*/
@property (nonatomic, readonly) NSTimeInterval ts;
/**
Decryption failure reason.
*/
@property (nonatomic) NSString *reason;
@end

View file

@ -0,0 +1,31 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "DecryptionFailure.h"
@implementation DecryptionFailure
- (instancetype)init
{
self = [super init];
if (self)
{
_ts = [NSDate date].timeIntervalSince1970;
}
return self;
}
@end

View file

@ -0,0 +1,69 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <Foundation/Foundation.h>
@import MatrixSDK;
@protocol MXDecryptionFailureDelegate;
@interface DecryptionFailureTracker : NSObject
/**
Returns the shared tracker.
@return the shared tracker.
*/
+ (instancetype)sharedInstance;
/**
The delegate object to receive analytics events.
*/
@property (nonatomic) id<MXDecryptionFailureDelegate> delegate;
/**
Report an event unable to decrypt.
This error can be momentary. The DecryptionFailureTracker will check if it gets
fixed. Else, it will generate a failure (@see `trackFailures`).
@param event the event.
@param roomState the room state when the event was received.
@param userId my user id.
*/
- (void)reportUnableToDecryptErrorForEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState myUser:(NSString*)userId;
/**
Flush current data.
*/
- (void)dispatch;
@end
/**
The `MXDecryptionFailureDelegate` protocol receives some stats computed by
`DecryptionFailureTracker`.
*/
@protocol MXDecryptionFailureDelegate <NSObject>
/**
Stats for decryption failures.
@param failuresCount the number of decryption failures.
*/
- (void)trackFailures:(NSUInteger)failuresCount;
@end

View file

@ -0,0 +1,143 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "DecryptionFailureTracker.h"
#import "DecryptionFailure.h"
// Call `checkFailures` every `CHECK_INTERVAL`
#define CHECK_INTERVAL 5
// Give events a chance to be decrypted by waiting `GRACE_PERIOD` before counting
// and reporting them as failures
#define GRACE_PERIOD 60
@interface DecryptionFailureTracker()
{
// Reported failures
// Every `CHECK_INTERVAL`, this list is checked for failures that happened
// more than`GRACE_PERIOD` ago. Those that did are reported to the delegate.
NSMutableDictionary<NSString* /* eventId */, DecryptionFailure*> *reportedFailures;
// Event ids of failures that were tracked previously
NSMutableSet<NSString*> *trackedEvents;
// Timer for periodic check
NSTimer *checkFailuresTimer;
}
@end
@implementation DecryptionFailureTracker
+ (instancetype)sharedInstance
{
static DecryptionFailureTracker *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[DecryptionFailureTracker alloc] init];
});
return sharedInstance;
}
- (instancetype)init
{
self = [super init];
if (self)
{
reportedFailures = [NSMutableDictionary dictionary];
trackedEvents = [NSMutableSet set];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(eventDidDecrypt:) name:kMXEventDidDecryptNotification object:nil];
checkFailuresTimer = [NSTimer scheduledTimerWithTimeInterval:CHECK_INTERVAL
target:self
selector:@selector(checkFailures)
userInfo:nil
repeats:YES];
}
return self;
}
- (void)reportUnableToDecryptErrorForEvent:(MXEvent *)event withRoomState:(MXRoomState *)roomState myUser:(NSString *)userId
{
if (reportedFailures[event.eventId] || [trackedEvents containsObject:event.eventId])
{
return;
}
// Filter out "expected" UTDs
// We cannot decrypt messages sent before the user joined the room
MXRoomMember *myUser = [roomState memberWithUserId:userId];
if (!myUser || myUser.membership != MXMembershipJoin)
{
return;
}
DecryptionFailure *decryptionFailure = [[DecryptionFailure alloc] init];
decryptionFailure.failedEventId = event.eventId;
// TODO: Need to sync with all platforms
// decryptionFailure.reason =;
reportedFailures[event.eventId] = decryptionFailure;
}
- (void)dispatch
{
[self checkFailures];
}
#pragma mark - Private methods
/**
Mark reported failures that occured before tsNow - GRACE_PERIOD as failures that should be
tracked.
*/
- (void)checkFailures
{
NSTimeInterval tsNow = [NSDate date].timeIntervalSince1970;
NSMutableArray *failuresToTrack = [NSMutableArray array];
for (DecryptionFailure *reportedFailure in reportedFailures.allValues)
{
if (reportedFailure.ts < tsNow - GRACE_PERIOD)
{
[failuresToTrack addObject:reportedFailure];
[reportedFailures removeObjectForKey:reportedFailure.failedEventId];
[trackedEvents addObject:reportedFailure.failedEventId];
}
}
if (_delegate && failuresToTrack.count)
{
NSLog(@"[DecryptionFailureTracker] trackFailures: %@", @(failuresToTrack.count));
[_delegate trackFailures:failuresToTrack.count];
}
}
- (void)eventDidDecrypt:(NSNotification *)notif
{
// Could be an event in the reportedFailures, remove it
MXEvent *event = notif.object;
[reportedFailures removeObjectForKey:event.eventId];
}
@end

View file

@ -22,6 +22,7 @@
#import "JitsiViewController.h"
#import "RageShakeManager.h"
#import "Analytics.h"
#import "RiotDesignValues.h"
@ -128,12 +129,6 @@ extern NSString *const kAppDelegateNetworkStatusDidChangeNotification;
- (void)selectMatrixAccount:(void (^)(MXKAccount *selectedAccount))onSelection;
#pragma mark - Analytics
- (void)startAnalytics;
- (void)stopAnalytics;
- (void)trackScreen:(NSString*)screenName;
#pragma mark - Push notifications
- (void)registerUserNotificationSettings;

View file

@ -48,14 +48,14 @@
#import "WebViewViewController.h"
@import PiwikTracker;
// Calls
#import "CallViewController.h"
#import "MXSession+Riot.h"
#import "MXRoom+Riot.h"
#import "Riot-Swift.h"
//#define MX_CALL_STACK_OPENWEBRTC
#ifdef MX_CALL_STACK_OPENWEBRTC
#import <MatrixOpenWebRTCWrapper/MatrixOpenWebRTCWrapper.h>
@ -235,10 +235,6 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
MXSDKOptions *sdkOptions = [MXSDKOptions sharedInstance];
sdkOptions.applicationGroupIdentifier = @"group.im.vector";
// Track SDK performance on Google analytics
// TODO: needs the same tool
//sdkOptions.analyticsDelegate = [[MXGoogleAnalytics alloc] init];
// Redirect NSLogs to files only if we are not debugging
if (!isatty(STDERR_FILENO))
{
@ -461,15 +457,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
_isAppForeground = NO;
// Retrieve custom configuration
NSString* userDefaults = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UserDefaults"];
NSString *defaultsPathFromApp = [[NSBundle mainBundle] pathForResource:userDefaults ofType:@"plist"];
NSDictionary *defaults = [NSDictionary dictionaryWithContentsOfFile:defaultsPathFromApp];
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
[[NSUserDefaults standardUserDefaults] synchronize];
[self setupUserDefaults];
// Configure Google Analytics here if the option is enabled
[self startAnalytics];
// Configure our analytics. It will indeed start if the option is enabled
[MXSDKOptions sharedInstance].analyticsDelegate = [Analytics sharedInstance];
[DecryptionFailureTracker sharedInstance].delegate = [Analytics sharedInstance];
[[Analytics sharedInstance] start];
// Prepare Pushkit handling
_incomingPushEventIds = [NSMutableDictionary dictionary];
@ -563,7 +556,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
_isAppForeground = NO;
// Analytics: Force to send the pending actions
[[PiwikTracker shared] dispatch];
[[DecryptionFailureTracker sharedInstance] dispatch];
[[Analytics sharedInstance] dispatch];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
@ -594,7 +588,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
NSLog(@"[AppDelegate] applicationDidBecomeActive");
// Check if there is crash log to send
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"enableCrashReport"])
if (RiotSettings.shared.enableCrashReport)
{
[self checkExceptionToReport];
}
@ -1026,70 +1020,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
}
}
#pragma mark - Analytics
- (void)startAnalytics
{
NSDictionary *piwikConfig = [[NSUserDefaults standardUserDefaults] objectForKey:@"piwik"];
[PiwikTracker configureSharedInstanceWithSiteID:piwikConfig[@"siteId"]
baseURL:[NSURL URLWithString:piwikConfig[@"url"]]
userAgent:@"iOSPiwikTracker"];
// Check whether the user has enabled the sending of crash reports.
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"enableCrashReport"])
{
[PiwikTracker shared].isOptedOut = NO;
[[PiwikTracker shared] setCustomVariableWithIndex:1 name:@"App Platform" value:@"iOS Platform"];
[[PiwikTracker shared] setCustomVariableWithIndex:2 name:@"App Version" value:[self appVersion]];
// The language is either the one selected by the user within the app
// or, else, the one configured by the OS
NSString *language = [NSBundle mxk_language] ? [NSBundle mxk_language] : [[NSBundle mainBundle] preferredLocalizations][0];
[[PiwikTracker shared] setCustomVariableWithIndex:4 name:@"Chosen Language" value:language];
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
if (account)
{
[[PiwikTracker shared] setCustomVariableWithIndex:7 name:@"Homeserver URL" value:account.mxCredentials.homeServer];
[[PiwikTracker shared] setCustomVariableWithIndex:8 name:@"Identity Server URL" value:account.identityServerURL];
}
// TODO: We should also track device and os version
// But that needs to be decided for all platforms
// Catch and log crashes
[MXLogger logCrashes:YES];
[MXLogger setBuildVersion:[AppDelegate theDelegate].build];
#ifdef DEBUG
// Disable analytics in debug as it pollutes stats
[PiwikTracker shared].isOptedOut = YES;
#endif
}
else if ([[NSUserDefaults standardUserDefaults] objectForKey:@"enableCrashReport"])
{
NSLog(@"[AppDelegate] The user decided to not ");
[PiwikTracker shared].isOptedOut = YES;
[MXLogger logCrashes:NO];
}
}
- (void)stopAnalytics
{
[PiwikTracker shared].isOptedOut = YES;
[MXLogger logCrashes:NO];
}
- (void)trackScreen:(NSString *)screenName
{
// Use the same pattern as Android
NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
NSString *appVersion = [self appVersion];
[[PiwikTracker shared] trackWithView:@[@"ios", appName, appVersion, screenName]
url:nil];
}
#pragma mark - Crash handling
// Check if there is a crash log to send to server
- (void)checkExceptionToReport
@ -1471,7 +1402,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
@"user_id": account.mxCredentials.userId
};
BOOL isNotificationContentShown = !event.isEncrypted || account.showDecryptedContentInNotifications;
BOOL isNotificationContentShown = !event.isEncrypted || RiotSettings.shared.showDecryptedContentInNotifications;
if ((event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) && isNotificationContentShown)
{
eventNotification.category = @"QUICK_REPLY";
@ -1559,7 +1491,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
NSString *msgType = event.content[@"msgtype"];
NSString *content = event.content[@"body"];
if (event.isEncrypted && !account.showDecryptedContentInNotifications)
if (event.isEncrypted && !RiotSettings.shared.showDecryptedContentInNotifications)
{
// Hide the content
msgType = nil;
@ -2914,8 +2846,11 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
if (launchAnimationContainerView)
{
NSTimeInterval durationMs = [[NSDate date] timeIntervalSinceDate:launchAnimationStart] * 1000;
NSLog(@"[AppDelegate] LaunchAnimation was shown for %.3fms", durationMs);
NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:launchAnimationStart];
NSLog(@"[AppDelegate] LaunchAnimation was shown for %.3fms", duration * 1000);
// Track it on our analytics
[[Analytics sharedInstance] trackLaunchScreenDisplayDuration:duration];
// TODO: Send durationMs to Piwik
// Such information should be the same on all platforms
@ -4086,4 +4021,24 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
[self.gdprConsentViewController dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - Settings
- (void)setupUserDefaults
{
// Register "Riot-Defaults.plist" default values
NSString* userDefaults = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UserDefaults"];
NSString *defaultsPathFromApp = [[NSBundle mainBundle] pathForResource:userDefaults ofType:@"plist"];
NSDictionary *defaults = [NSDictionary dictionaryWithContentsOfFile:defaultsPathFromApp];
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
// Now use RiotSettings and NSUserDefaults to store `showDecryptedContentInNotifications` setting option
// Migrate this information from main MXKAccount to RiotSettings, if value is not in UserDefaults
if (!RiotSettings.shared.isShowDecryptedContentInNotificationsHasBeenSetOnce)
{
MXKAccount *currentAccount = [MXKAccountManager sharedManager].activeAccounts.firstObject;
RiotSettings.shared.showDecryptedContentInNotifications = currentAccount.showDecryptedContentInNotifications;
}
}
@end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="H1p-Uh-vWS">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="H1p-Uh-vWS">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13527"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Navigation items with more than one left or right bar item" minToolsVersion="7.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -51,7 +51,7 @@
<segue destination="e7G-NU-7ck" kind="show" identifier="showRoomDetails" id="vCz-dl-6xQ"/>
<segue destination="KDg-aD-xlK" kind="show" identifier="showRoomSearch" id="hdA-V1-9AF"/>
<segue destination="nDS-pp-sWM" kind="show" identifier="showMemberDetails" id="cUw-vU-gJq"/>
<segue destination="gkO-rP-nGK" kind="show" identifier="showContactDetails" action="showDetailViewController:sender:" id="f5u-Y1-7nt"/>
<segue destination="gkO-rP-nGK" kind="show" identifier="showContactDetails" id="f5u-Y1-7nt"/>
<segue destination="ZZb-IS-a1F" kind="presentation" identifier="showUnknownDevices" id="wUx-4y-ybn"/>
<segue destination="udm-55-AMb" kind="show" identifier="showContactPicker" id="Q6y-9M-Ugl"/>
</connections>
@ -247,7 +247,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
<tabBarItem key="tabBarItem" image="tab_home.png" selectedImage="tab_home_selected.png" id="hNI-yH-EXj">
<tabBarItem key="tabBarItem" title="" image="tab_home.png" selectedImage="tab_home_selected.png" id="hNI-yH-EXj">
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="accessibilityIdentifier" value="TabBarItemHome"/>
</userDefinedRuntimeAttributes>
@ -608,7 +608,7 @@
<image name="tab_rooms.png" width="25" height="25"/>
</resources>
<inferredMetricsTieBreakers>
<segue reference="bwO-oZ-2vj"/>
<segue reference="mhb-l9-pM3"/>
<segue reference="fIR-e3-ssz"/>
<segue reference="f5u-Y1-7nt"/>
<segue reference="vCz-dl-6xQ"/>

View file

@ -34,10 +34,6 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag)
*/
@property(nonatomic) BOOL containsLastMessage;
/**
A Boolean value that determines whether some read receipts are currently displayed in this bubble.
*/
@property(nonatomic) BOOL hasReadReceipts;
/**
The event id of the current selected event inside the bubble. Default is nil.

View file

@ -48,12 +48,11 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
// Increase maximum number of components
self.maxComponentCount = 20;
// Initialize receipts flag
_hasReadReceipts = NO;
// Force the update of the text message to take into account the potential read receipts in the bubble display.
// Note: we don't update this attributed string here because the RoomBubbleCellData instances are created on a processing
// thread different from the UI thread.
// Initialize read receipts
self.readReceipts = [NSMutableDictionary dictionary];
self.readReceipts[event.eventId] = [roomDataSource.room getEventReceipts:event.eventId sorted:YES];
// Reset attributedTextMessage to force reset MXKRoomCellData parameters
self.attributedTextMessage = nil;
}
@ -79,7 +78,6 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
[self refreshBubbleComponentsPosition];
}
shouldUpdateComponentsPosition = NO;
}
}
@ -163,9 +161,6 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
NSMutableAttributedString *currentAttributedTextMsg;
// Refresh the receipt flag during this process
_hasReadReceipts = NO;
NSInteger selectedComponentIndex = self.selectedComponentIndex;
NSInteger lastMessageIndex = self.containsLastMessage ? self.mostRecentComponentIndex : NSNotFound;
@ -203,11 +198,10 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
// Init attributed string with the first text component
currentAttributedTextMsg = [[NSMutableAttributedString alloc] initWithAttributedString:componentString];
}
// Vertical whitespace is added in case of read receipts
if ([roomDataSource.room getEventReceipts:component.event.eventId sorted:NO])
if (self.readReceipts[component.event.eventId].count)
{
_hasReadReceipts = YES;
// Add vertical whitespace in case of read receipts
[currentAttributedTextMsg appendAttributedString:[RoomBubbleCellData readReceiptVerticalWhitespace]];
}
@ -246,10 +240,9 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
// Append attributed text
[currentAttributedTextMsg appendAttributedString:componentString];
// Add vertical whitespace in case of read receipts
if ([roomDataSource.room getEventReceipts:component.event.eventId sorted:NO])
if (self.readReceipts[component.event.eventId].count)
{
_hasReadReceipts = YES;
// Add vertical whitespace in case of read receipts
[currentAttributedTextMsg appendAttributedString:[RoomBubbleCellData readReceiptVerticalWhitespace]];
}
}
@ -262,14 +255,13 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
{
// CAUTION: This method must be called on the main thread.
// Refresh the receipt flag during this process.
_hasReadReceipts = NO;
@synchronized(bubbleComponents)
{
// Check whether there is at least one component.
if (bubbleComponents.count)
{
BOOL hasReadReceipts = NO;
// Set position of the first component
CGFloat positionY = (self.attachment == nil || self.attachment.type == MXKAttachmentTypeFile || self.attachment.type == MXKAttachmentTypeAudio) ? MXKROOMBUBBLECELLDATA_TEXTVIEW_DEFAULT_VERTICAL_INSET : 0;
MXKRoomBubbleComponent *component;
@ -283,7 +275,7 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
if (component.attributedTextMessage)
{
_hasReadReceipts = ([roomDataSource.room getEventReceipts:component.event.eventId sorted:NO] != nil);
hasReadReceipts = (self.readReceipts[component.event.eventId].count > 0);
break;
}
}
@ -308,7 +300,7 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
}
// Vertical whitespace is added in case of read receipts
if (_hasReadReceipts)
if (hasReadReceipts)
{
[attributedString appendAttributedString:[RoomBubbleCellData readReceiptVerticalWhitespace]];
}
@ -348,9 +340,8 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
component.position = CGPointMake(0, positionY);
// Add vertical whitespace in case of read receipts.
if ([roomDataSource.room getEventReceipts:component.event.eventId sorted:NO])
if (self.readReceipts[component.event.eventId].count)
{
_hasReadReceipts = YES;
[attributedString appendAttributedString:[RoomBubbleCellData readReceiptVerticalWhitespace]];
}
@ -379,19 +370,6 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
}
}
- (void)setHasReadReceipts:(BOOL)hasReadReceipts
{
// Check whether there is something to do
if (_hasReadReceipts || hasReadReceipts)
{
// Update flag
_hasReadReceipts = hasReadReceipts;
// Recompute the text message layout
self.attributedTextMessage = nil;
}
}
- (void)setSelectedEventId:(NSString *)selectedEventId
{
// Check whether there is something to do
@ -517,6 +495,9 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
return NO;
}
// Update read receipts for this bubble
self.readReceipts[event.eventId] = [roomDataSource.room getEventReceipts:event.eventId sorted:YES];
return [super addEvent:event andRoomState:roomState];
}

View file

@ -96,24 +96,102 @@
- (void)didReceiveReceiptEvent:(MXEvent *)receiptEvent roomState:(MXRoomState *)roomState
{
// Override this callback to force rendering of each cell with read receipts information.
@synchronized(bubbles)
{
for (RoomBubbleCellData *cellData in bubbles)
// Do the processing on the same processing queue as MXKRoomDataSource
dispatch_async(MXKRoomDataSource.processingQueue, ^{
// Remove the previous displayed read receipt for each user who sent a
// new read receipt.
// To implement it, we need to find the sender id of each new read receipt
// among the read receipts array of all events in all bubbles.
NSMutableArray *readReceiptSenders = [receiptEvent.readReceiptSenders mutableCopy];
@synchronized(bubbles)
{
cellData.hasReadReceipts = NO;
NSMutableDictionary<NSString* /* eventId */, NSArray<MXReceiptData*> *> *updatedCellDataReadReceipts = [NSMutableDictionary dictionary];
for (RoomBubbleCellData *cellData in bubbles)
{
for (NSString *eventId in cellData.readReceipts)
{
for (MXReceiptData *receiptData in cellData.readReceipts[eventId])
{
NSMutableArray *foundSenders = [NSMutableArray array];
for (NSString *senderId in readReceiptSenders)
{
if ([receiptData.userId isEqualToString:senderId])
{
// We find an existing displayed receipt, remove it
[foundSenders addObject:senderId];
if (!updatedCellDataReadReceipts[eventId])
{
updatedCellDataReadReceipts[eventId] = cellData.readReceipts[eventId];
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"userId!=%@", receiptData.userId];
updatedCellDataReadReceipts[eventId] = [updatedCellDataReadReceipts[eventId] filteredArrayUsingPredicate:predicate];
break;
}
}
// As there is one (the last) read receipt displayed per user,
// we do not need to search for other read receipts of found users.
[readReceiptSenders removeObjectsInArray:foundSenders];
if (!readReceiptSenders.count)
{
// All senders have been found
break;
}
}
}
// Flush found changed to the cell data
for (NSString *eventId in updatedCellDataReadReceipts)
{
if (updatedCellDataReadReceipts[eventId].count)
{
cellData.readReceipts[eventId] = updatedCellDataReadReceipts[eventId];
}
else
{
cellData.readReceipts[eventId] = nil;
}
}
if (!readReceiptSenders.count)
{
// All senders have been found
break;
}
}
}
}
NSArray *readEventIds = receiptEvent.readReceiptEventIds;
for (NSString* eventId in readEventIds)
{
RoomBubbleCellData *cellData = [self cellDataOfEventWithEventId:eventId];
// Ignore the read receipts on the events without an actual display.
cellData.hasReadReceipts = !cellData.hasNoDisplay;
}
[super didReceiveReceiptEvent:receiptEvent roomState:roomState];
// Update cell data we have received a read receipt for
NSArray *readEventIds = receiptEvent.readReceiptEventIds;
for (NSString* eventId in readEventIds)
{
RoomBubbleCellData *cellData = [self cellDataOfEventWithEventId:eventId];
if (cellData)
{
@synchronized(bubbles)
{
if (!cellData.hasNoDisplay)
{
cellData.readReceipts[eventId] = [self.room getEventReceipts:eventId sorted:YES];
}
else
{
// Ignore the read receipts on the events without an actual display.
cellData.readReceipts[eventId] = nil;
}
}
}
}
dispatch_async(dispatch_get_main_queue(), ^{
// TODO: Be smarter and update only updated cells
[super didReceiveReceiptEvent:receiptEvent roomState:roomState];
});
});
}
#pragma mark -
@ -181,7 +259,7 @@
// Handle read receipts and read marker display.
// Ignore the read receipts on the bubble without actual display.
// Ignore the read receipts on collapsed bubbles
if ((self.showBubbleReceipts && cellData.hasReadReceipts && !isCollapsableCellCollapsed) || self.showReadMarker)
if ((self.showBubbleReceipts && cellData.readReceipts.count && !isCollapsableCellCollapsed) || self.showReadMarker)
{
// Read receipts container are inserted here on the right side into the content view.
// Some vertical whitespaces are added in message text view (see RoomBubbleCellData class) to insert correctly multiple receipts.
@ -194,10 +272,10 @@
if (component.event.sentState != MXEventSentStateFailed)
{
// Handle read receipts (if any)
if (self.showBubbleReceipts && cellData.hasReadReceipts && !isCollapsableCellCollapsed)
if (self.showBubbleReceipts && cellData.readReceipts.count && !isCollapsableCellCollapsed)
{
// Get the events receipts by ignoring the current user receipt.
NSArray* receipts = [self.room getEventReceipts:component.event.eventId sorted:YES];
NSArray* receipts = cellData.readReceipts[component.event.eventId];
NSMutableArray *roomMembers;
NSMutableArray *placeholders;

View file

@ -25,6 +25,8 @@
#import "AppDelegate.h"
#import "Riot-Swift.h"
#define RECENTSDATASOURCE_SECTION_DIRECTORY 0x01
#define RECENTSDATASOURCE_SECTION_INVITES 0x02
#define RECENTSDATASOURCE_SECTION_FAVORITES 0x04
@ -1193,8 +1195,8 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
if (_recentsDataSourceMode == RecentsDataSourceModeHome)
{
BOOL pinMissedNotif = [[NSUserDefaults standardUserDefaults] boolForKey:@"pinRoomsWithMissedNotif"];
BOOL pinUnread = [[NSUserDefaults standardUserDefaults] boolForKey:@"pinRoomsWithUnread"];
BOOL pinMissedNotif = RiotSettings.shared.pinRoomsWithMissedNotificationsOnHome;
BOOL pinUnread = RiotSettings.shared.pinRoomsWithUnreadMessagesOnHome;
NSComparator comparator = nil;
if (pinMissedNotif)

View file

@ -0,0 +1,4 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

View file

@ -22,6 +22,7 @@
#import "WidgetManager.h"
#import "MXDecryptionResult.h"
#import "DecryptionFailureTracker.h"
#pragma mark - Constants definitions
@ -117,34 +118,41 @@ NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator = @"/";
NSAttributedString *attributedString = [super attributedStringFromEvent:event withRoomState:roomState error:error];
if (event.sentState == MXEventSentStateSent
&& [event.decryptionError.domain isEqualToString:MXDecryptingErrorDomain]
&& event.decryptionError.code == MXDecryptingErrorUnknownInboundSessionIdCode)
&& [event.decryptionError.domain isEqualToString:MXDecryptingErrorDomain])
{
// Append to the displayed error an attibuted string with a tappable link
// so that the user can try to fix the UTC
NSMutableAttributedString *attributedStringWithRerequestMessage = [attributedString mutableCopy];
[attributedStringWithRerequestMessage appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]];
// Track e2e failures
dispatch_async(dispatch_get_main_queue(), ^{
[[DecryptionFailureTracker sharedInstance] reportUnableToDecryptErrorForEvent:event withRoomState:roomState myUser:mxSession.myUser.userId];
});
NSString *linkActionString = [NSString stringWithFormat:@"%@%@%@", kEventFormatterOnReRequestKeysLinkAction,
kEventFormatterOnReRequestKeysLinkActionSeparator,
event.eventId];
if (event.decryptionError.code == MXDecryptingErrorUnknownInboundSessionIdCode)
{
// Append to the displayed error an attibuted string with a tappable link
// so that the user can try to fix the UTD
NSMutableAttributedString *attributedStringWithRerequestMessage = [attributedString mutableCopy];
[attributedStringWithRerequestMessage appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]];
[attributedStringWithRerequestMessage appendAttributedString:
[[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"event_formatter_rerequest_keys_part1_link", @"Vector", nil)
attributes:@{
NSLinkAttributeName: linkActionString,
NSForegroundColorAttributeName: self.sendingTextColor,
NSFontAttributeName: self.encryptedMessagesTextFont
}]];
NSString *linkActionString = [NSString stringWithFormat:@"%@%@%@", kEventFormatterOnReRequestKeysLinkAction,
kEventFormatterOnReRequestKeysLinkActionSeparator,
event.eventId];
[attributedStringWithRerequestMessage appendAttributedString:
[[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"event_formatter_rerequest_keys_part2", @"Vector", nil)
attributes:@{
NSForegroundColorAttributeName: self.sendingTextColor,
NSFontAttributeName: self.encryptedMessagesTextFont
}]];
[attributedStringWithRerequestMessage appendAttributedString:
[[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"event_formatter_rerequest_keys_part1_link", @"Vector", nil)
attributes:@{
NSLinkAttributeName: linkActionString,
NSForegroundColorAttributeName: self.sendingTextColor,
NSFontAttributeName: self.encryptedMessagesTextFont
}]];
attributedString = attributedStringWithRerequestMessage;
[attributedStringWithRerequestMessage appendAttributedString:
[[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"event_formatter_rerequest_keys_part2", @"Vector", nil)
attributes:@{
NSForegroundColorAttributeName: self.sendingTextColor,
NSFontAttributeName: self.encryptedMessagesTextFont
}]];
attributedString = attributedStringWithRerequestMessage;
}
}
return attributedString;

View file

@ -17,6 +17,13 @@
#import "RiotDesignValues.h"
#ifdef IS_SHARE_EXTENSION
#import "RiotShareExtension-Swift.h"
#else
#import "Riot-Swift.h"
#endif
NSString *const kRiotDesignValuesDidChangeThemeNotification = @"kRiotDesignValuesDidChangeThemeNotification";
UIColor *kRiotPrimaryBgColor;
@ -134,7 +141,7 @@ UIKeyboardAppearance kRiotKeyboard;
- (void)accessibilityInvertColorsStatusDidChange
{
// Refresh the theme only for "auto"
NSString *theme = [[NSUserDefaults standardUserDefaults] stringForKey:@"userInterfaceTheme"];
NSString *theme = RiotSettings.shared.userInterfaceTheme;
if (!theme || [theme isEqualToString:@"auto"])
{
[self userInterfaceThemeDidChange];
@ -144,7 +151,7 @@ UIKeyboardAppearance kRiotKeyboard;
- (void)userInterfaceThemeDidChange
{
// Retrieve the current selected theme ("light" if none. "auto" is used as default from iOS 11).
NSString *theme = [[NSUserDefaults standardUserDefaults] stringForKey:@"userInterfaceTheme"];
NSString *theme = RiotSettings.shared.userInterfaceTheme;
if (!theme || [theme isEqualToString:@"auto"])
{

View file

@ -0,0 +1,115 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
/// Store Riot specific app settings.
@objcMembers
final class RiotSettings: NSObject {
// MARK: - Constants
private enum UserDefaultsKeys {
static let enableCrashReport = "enableCrashReport"
static let enableRageShake = "enableRageShake"
static let createConferenceCallsWithJitsi = "createConferenceCallsWithJitsi"
static let userInterfaceTheme = "userInterfaceTheme"
static let notificationsShowDecryptedContent = "showDecryptedContent"
static let pinRoomsWithMissedNotifications = "pinRoomsWithMissedNotif"
static let pinRoomsWithUnreadMessages = "pinRoomsWithUnread"
}
static let shared = RiotSettings()
// MARK: - Public
// MARK: Notifications
/// Indicate if `showDecryptedContentInNotifications` settings has been set once.
var isShowDecryptedContentInNotificationsHasBeenSetOnce: Bool {
return UserDefaults.standard.object(forKey: UserDefaultsKeys.notificationsShowDecryptedContent) != nil
}
/// Indicate if encrypted messages content should be displayed in notifications.
var showDecryptedContentInNotifications: Bool {
get {
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.notificationsShowDecryptedContent)
} set {
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.notificationsShowDecryptedContent)
}
}
/// Indicate if rooms with missed notifications should be displayed first on home screen.
var pinRoomsWithMissedNotificationsOnHome: Bool {
get {
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.pinRoomsWithMissedNotifications)
} set {
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.pinRoomsWithMissedNotifications)
}
}
/// Indicate if rooms with unread messages should be displayed first on home screen.
var pinRoomsWithUnreadMessagesOnHome: Bool {
get {
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.pinRoomsWithUnreadMessages)
} set {
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.pinRoomsWithUnreadMessages)
}
}
// MARK: User interface
var userInterfaceTheme: String? {
get {
return UserDefaults.standard.string(forKey: UserDefaultsKeys.userInterfaceTheme)
} set {
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.userInterfaceTheme)
}
}
// MARK: Other
/// Indicate if `enableCrashReport` settings has been set once.
var isEnableCrashReportHasBeenSetOnce: Bool {
return UserDefaults.standard.object(forKey: UserDefaultsKeys.enableCrashReport) != nil
}
var enableCrashReport: Bool {
get {
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.enableCrashReport)
} set {
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.enableCrashReport)
}
}
var enableRageShake: Bool {
get {
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.enableRageShake)
} set {
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.enableRageShake)
}
}
// MARK: Labs
var createConferenceCallsWithJitsi: Bool {
get {
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.createConferenceCallsWithJitsi)
} set {
UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.createConferenceCallsWithJitsi)
}
}
}

View file

@ -73,7 +73,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"AttachmentsViewer"];
[[Analytics sharedInstance] trackScreen:@"AttachmentsViewer"];
}
- (void)destroy

View file

@ -193,7 +193,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"Authentication"];
[[Analytics sharedInstance] trackScreen:@"Authentication"];
}
- (void)viewDidAppear:(BOOL)animated

View file

@ -135,7 +135,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"GroupDetails"];
[[Analytics sharedInstance] trackScreen:@"GroupDetails"];
}
- (void)viewWillDisappear:(BOOL)animated

View file

@ -182,7 +182,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"GroupDetailsHome"];
[[Analytics sharedInstance] trackScreen:@"GroupDetailsHome"];
// Release the potential pushed view controller
[self releasePushedViewController];

View file

@ -217,7 +217,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"GroupDetailsPeople"];
[[Analytics sharedInstance] trackScreen:@"GroupDetailsPeople"];
// Release the potential pushed view controller
[self releasePushedViewController];

View file

@ -181,7 +181,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"GroupDetailsRooms"];
[[Analytics sharedInstance] trackScreen:@"GroupDetailsRooms"];
// Release the potential pushed view controller
[self releasePushedViewController];

View file

@ -186,7 +186,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"Groups"];
[[Analytics sharedInstance] trackScreen:@"Groups"];
// Deselect the current selected row, it will be restored on viewDidAppear (if any)
NSIndexPath *indexPath = [self.groupsTableView indexPathForSelectedRow];

View file

@ -263,7 +263,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"ContactDetails"];
[[Analytics sharedInstance] trackScreen:@"ContactDetails"];
// Hide the bottom border of the navigation bar to display the expander header
[self hideNavigationBarBorder:YES];

View file

@ -73,7 +73,7 @@
@property (nonatomic) BOOL shouldScrollToTopOnRefresh;
/**
The Google Analytics Instance screen name (Default is "ContactsTable").
The analytics instance screen name (Default is "ContactsTable").
*/
@property (nonatomic) NSString *screenName;

View file

@ -144,7 +144,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:_screenName];
[[Analytics sharedInstance] trackScreen:_screenName];
// Check whether the access to the local contacts has not been already asked.
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)

View file

@ -99,7 +99,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"CountryPicker"];
[[Analytics sharedInstance] trackScreen:@"CountryPicker"];
}
- (void)destroy

View file

@ -96,7 +96,7 @@ static CGFloat const kTextFontSize = 15.0;
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"DeactivateAccount"];
[[Analytics sharedInstance] trackScreen:@"DeactivateAccount"];
}
- (void)viewDidLayoutSubviews

View file

@ -143,7 +143,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"DirectoryServerPicker"];
[[Analytics sharedInstance] trackScreen:@"DirectoryServerPicker"];
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {

View file

@ -105,7 +105,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"Directory"];
[[Analytics sharedInstance] trackScreen:@"Directory"];
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {

View file

@ -107,7 +107,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"FilesGlobalSearch"];
[[Analytics sharedInstance] trackScreen:@"FilesGlobalSearch"];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshSearchResult:) name:kMXSessionDidLeaveRoomNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshSearchResult:) name:kMXSessionNewRoomNotification object:nil];

View file

@ -114,7 +114,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"MessagesGlobalSearch"];
[[Analytics sharedInstance] trackScreen:@"MessagesGlobalSearch"];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshSearchResult:) name:kMXSessionDidLeaveRoomNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshSearchResult:) name:kMXSessionNewRoomNotification object:nil];

View file

@ -109,7 +109,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"CountryPicker"];
[[Analytics sharedInstance] trackScreen:@"CountryPicker"];
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;

View file

@ -26,6 +26,8 @@
#import "MXRoom+Riot.h"
#import "MXSession+Riot.h"
#import "Riot-Swift.h"
@interface MasterTabBarController ()
{
// Array of `MXSession` instances.
@ -77,6 +79,15 @@
_roomsViewController = [self.viewControllers objectAtIndex:TABBAR_ROOMS_INDEX];
_groupsViewController = [self.viewControllers objectAtIndex:TABBAR_GROUPS_INDEX];
// Set the accessibility labels for all buttons #1842
[_settingsBarButtonItem setAccessibilityLabel:NSLocalizedStringFromTable(@"settings_title", @"Vector", nil)];
[_searchBarButtonIem setAccessibilityLabel:NSLocalizedStringFromTable(@"search_default_placeholder", @"Vector", nil)];
[_homeViewController setAccessibilityLabel:NSLocalizedStringFromTable(@"title_home", @"Vector", nil)];
[_favouritesViewController setAccessibilityLabel:NSLocalizedStringFromTable(@"title_favourites", @"Vector", nil)];
[_peopleViewController setAccessibilityLabel:NSLocalizedStringFromTable(@"title_people", @"Vector", nil)];
[_roomsViewController setAccessibilityLabel:NSLocalizedStringFromTable(@"title_rooms", @"Vector", nil)];
[_groupsViewController setAccessibilityLabel:NSLocalizedStringFromTable(@"title_groups", @"Vector", nil)];
// Sanity check
NSAssert(_homeViewController && _favouritesViewController && _peopleViewController && _roomsViewController && _groupsViewController, @"Something wrong in Main.storyboard");
@ -136,10 +147,10 @@
else
{
// Check whether the user has been already prompted to send crash reports.
// (Check whether 'enableCrashReport' flag has been set once)
if (![[NSUserDefaults standardUserDefaults] objectForKey:@"enableCrashReport"])
// (Check whether 'enableCrashReport' flag has been set once)
if (!RiotSettings.shared.isEnableCrashReportHasBeenSetOnce)
{
[self promptUserBeforeUsingGoogleAnalytics];
[self promptUserBeforeUsingAnalytics];
}
[self refreshTabBarBadges];
@ -762,7 +773,7 @@
#pragma mark -
- (void)promptUserBeforeUsingGoogleAnalytics
- (void)promptUserBeforeUsingAnalytics
{
NSLog(@"[MasterTabBarController]: Invite the user to send crash reports");
@ -778,8 +789,7 @@
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"enableCrashReport"];
[[NSUserDefaults standardUserDefaults] synchronize];
RiotSettings.shared.enableCrashReport = NO;
if (weakSelf)
{
@ -792,21 +802,20 @@
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"yes"]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"enableCrashReport"];
[[NSUserDefaults standardUserDefaults] synchronize];
RiotSettings.shared.enableCrashReport = YES;
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
}
[[AppDelegate theDelegate] startAnalytics];
[[Analytics sharedInstance] start];
}]];
[currentAlert mxk_setAccessibilityIdentifier: @"HomeVCUseGoogleAnalyticsAlert"];
[currentAlert mxk_setAccessibilityIdentifier: @"HomeVCUseAnalyticsAlert"];
[self presentViewController:currentAlert animated:YES completion:nil];
}

View file

@ -157,7 +157,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"MediaAlbumContent"];
[[Analytics sharedInstance] trackScreen:@"MediaAlbumContent"];
self.navigationItem.title = _assetsCollection.localizedTitle;

View file

@ -236,7 +236,7 @@ static void *RecordingContext = &RecordingContext;
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"MediaPicker"];
[[Analytics sharedInstance] trackScreen:@"MediaPicker"];
if (!userAlbumsQueue)
{

View file

@ -79,7 +79,7 @@
@property (nonatomic) CGFloat stickyHeaderHeight;
/**
The Google Analytics Instance screen name (Default is "RecentsScreen").
The analytics instance screen name (Default is "RecentsScreen").
*/
@property (nonatomic) NSString *screenName;

View file

@ -231,7 +231,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:_screenName];
[[Analytics sharedInstance] trackScreen:_screenName];
// Deselect the current selected row, it will be restored on viewDidAppear (if any)
NSIndexPath *indexPath = [self.recentsTableView indexPathForSelectedRow];

View file

@ -108,7 +108,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"RoomFilesSearch"];
[[Analytics sharedInstance] trackScreen:@"RoomFilesSearch"];
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {

View file

@ -251,7 +251,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"RoomMemberDetails"];
[[Analytics sharedInstance] trackScreen:@"RoomMemberDetails"];
// Hide the bottom border of the navigation bar to display the expander header
[self hideNavigationBarBorder:YES];

View file

@ -109,7 +109,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"RoomMessagesSearch"];
[[Analytics sharedInstance] trackScreen:@"RoomMessagesSearch"];
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {

View file

@ -245,7 +245,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"RoomParticipants"];
[[Analytics sharedInstance] trackScreen:@"RoomParticipants"];
if (memberDetailsViewController)
{

View file

@ -101,7 +101,7 @@
}
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"RoomsSearch"];
[[Analytics sharedInstance] trackScreen:@"RoomsSearch"];
// Enable the search field by default at the screen opening
if (self.searchBarHidden)

View file

@ -289,7 +289,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"RoomSettings"];
[[Analytics sharedInstance] trackScreen:@"RoomSettings"];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdateRules:) name:kMXNotificationCenterDidUpdateRules object:nil];

View file

@ -119,6 +119,8 @@
#import "EventFormatter.h"
#import "Riot-Swift.h"
@interface RoomViewController ()
{
// The expanded header
@ -450,7 +452,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"ChatRoom"];
[[Analytics sharedInstance] trackScreen:@"ChatRoom"];
// Refresh the room title view
[self refreshRoomTitle];
@ -3012,7 +3014,7 @@
}
// If enabled, create the conf using jitsi widget and open it directly
else if ([[NSUserDefaults standardUserDefaults] boolForKey:@"createConferenceCallsWithJitsi"]
else if (RiotSettings.shared.createConferenceCallsWithJitsi
&& self.roomDataSource.room.state.joinedMembers.count > 2)
{
[self startActivityIndicator];

View file

@ -46,6 +46,8 @@
#import "GBDeviceInfo_iOS.h"
#import "Riot-Swift.h"
NSString* const kSettingsViewControllerPhoneBookCountryCellId = @"kSettingsViewControllerPhoneBookCountryCellId";
enum
@ -423,7 +425,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"Settings"];
[[Analytics sharedInstance] trackScreen:@"Settings"];
// Release the potential pushed view controller
[self releasePushedViewController];
@ -1681,7 +1683,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_show_decrypted_content", @"Vector", nil);
labelAndSwitchCell.mxkSwitch.on = account.showDecryptedContentInNotifications;
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.showDecryptedContentInNotifications;
labelAndSwitchCell.mxkSwitch.enabled = account.isPushKitNotificationActive;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleShowDecodedContent:) forControlEvents:UIControlEventTouchUpInside];
@ -1705,7 +1707,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_pin_rooms_with_missed_notif", @"Vector", nil);
labelAndSwitchCell.mxkSwitch.on = [[NSUserDefaults standardUserDefaults] boolForKey:@"pinRoomsWithMissedNotif"];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.pinRoomsWithMissedNotificationsOnHome;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(togglePinRoomsWithMissedNotif:) forControlEvents:UIControlEventTouchUpInside];
@ -1716,7 +1718,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_pin_rooms_with_unread", @"Vector", nil);
labelAndSwitchCell.mxkSwitch.on = [[NSUserDefaults standardUserDefaults] boolForKey:@"pinRoomsWithUnread"];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.pinRoomsWithUnreadMessagesOnHome;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(togglePinRoomsWithUnread:) forControlEvents:UIControlEventTouchUpInside];
@ -1782,7 +1784,8 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:kSettingsViewControllerPhoneBookCountryCellId];
}
NSString *theme = [[NSUserDefaults standardUserDefaults] stringForKey:@"userInterfaceTheme"];
NSString *theme = RiotSettings.shared.userInterfaceTheme;
if (!theme)
{
if (@available(iOS 11.0, *))
@ -1942,7 +1945,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
MXKTableViewCellWithLabelAndSwitch* sendCrashReportCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
sendCrashReportCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_send_crash_report", @"Vector", nil);
sendCrashReportCell.mxkSwitch.on = [[NSUserDefaults standardUserDefaults] boolForKey:@"enableCrashReport"];
sendCrashReportCell.mxkSwitch.on = RiotSettings.shared.enableCrashReport;
sendCrashReportCell.mxkSwitch.enabled = YES;
[sendCrashReportCell.mxkSwitch addTarget:self action:@selector(toggleSendCrashReport:) forControlEvents:UIControlEventTouchUpInside];
@ -1953,7 +1956,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
MXKTableViewCellWithLabelAndSwitch* enableRageShakeCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
enableRageShakeCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_enable_rageshake", @"Vector", nil);
enableRageShakeCell.mxkSwitch.on = [[NSUserDefaults standardUserDefaults] boolForKey:@"enableRageShake"];
enableRageShakeCell.mxkSwitch.on = RiotSettings.shared.enableRageShake;
enableRageShakeCell.mxkSwitch.enabled = YES;
[enableRageShakeCell.mxkSwitch addTarget:self action:@selector(toggleEnableRageShake:) forControlEvents:UIControlEventTouchUpInside];
@ -2042,7 +2045,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_create_conference_with_jitsi", @"Vector", nil);
labelAndSwitchCell.mxkSwitch.on = [[NSUserDefaults standardUserDefaults] boolForKey:@"createConferenceCallsWithJitsi"];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.createConferenceCallsWithJitsi;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleJitsiForConference:) forControlEvents:UIControlEventTouchUpInside];
@ -2820,8 +2823,8 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
- (void)toggleShowDecodedContent:(id)sender
{
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
account.showDecryptedContentInNotifications = !account.showDecryptedContentInNotifications;
UISwitch *switchButton = (UISwitch*)sender;
RiotSettings.shared.showDecryptedContentInNotifications = switchButton.isOn;
}
- (void)toggleLocalContactsSync:(id)sender
@ -2847,25 +2850,25 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
- (void)toggleSendCrashReport:(id)sender
{
BOOL enable = [[NSUserDefaults standardUserDefaults] boolForKey:@"enableCrashReport"];
BOOL enable = RiotSettings.shared.enableCrashReport;
if (enable)
{
NSLog(@"[SettingsViewController] disable automatic crash report sending");
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"enableCrashReport"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(@"[SettingsViewController] disable automatic crash report and analytics sending");
[[AppDelegate theDelegate] stopAnalytics];
RiotSettings.shared.enableCrashReport = NO;
[[Analytics sharedInstance] stop];
// Remove potential crash file.
[MXLogger deleteCrashLog];
}
else
{
NSLog(@"[SettingsViewController] enable automatic crash report sending");
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"enableCrashReport"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(@"[SettingsViewController] enable automatic crash report and analytics sending");
[[AppDelegate theDelegate] startAnalytics];
RiotSettings.shared.enableCrashReport = YES;
[[Analytics sharedInstance] start];
}
}
@ -2875,8 +2878,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
{
UISwitch *switchButton = (UISwitch*)sender;
[[NSUserDefaults standardUserDefaults] setBool:switchButton.isOn forKey:@"enableRageShake"];
[[NSUserDefaults standardUserDefaults] synchronize];
RiotSettings.shared.enableRageShake = switchButton.isOn;
[self.tableView reloadData];
}
@ -2887,9 +2889,8 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
if (sender && [sender isKindOfClass:UISwitch.class])
{
UISwitch *switchButton = (UISwitch*)sender;
[[NSUserDefaults standardUserDefaults] setBool:switchButton.isOn forKey:@"createConferenceCallsWithJitsi"];
[[NSUserDefaults standardUserDefaults] synchronize];
RiotSettings.shared.createConferenceCallsWithJitsi = switchButton.isOn;
[self.tableView reloadData];
}
@ -3012,16 +3013,14 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
{
UISwitch *switchButton = (UISwitch*)sender;
[[NSUserDefaults standardUserDefaults] setBool:switchButton.on forKey:@"pinRoomsWithMissedNotif"];
[[NSUserDefaults standardUserDefaults] synchronize];
RiotSettings.shared.pinRoomsWithMissedNotificationsOnHome = switchButton.on;
}
- (void)togglePinRoomsWithUnread:(id)sender
{
UISwitch *switchButton = (UISwitch*)sender;
[[NSUserDefaults standardUserDefaults] setBool:switchButton.on forKey:@"pinRoomsWithUnread"];
[[NSUserDefaults standardUserDefaults] synchronize];
RiotSettings.shared.pinRoomsWithUnreadMessagesOnHome = switchButton.on;
}
- (void)toggleCommunityFlair:(id)sender
@ -3661,15 +3660,14 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
newTheme = @"black";
}
NSString *theme = [[NSUserDefaults standardUserDefaults] stringForKey:@"userInterfaceTheme"];
NSString *theme = RiotSettings.shared.userInterfaceTheme;
if (newTheme && ![newTheme isEqualToString:theme])
{
// Clear fake Riot Avatars based on the previous theme.
[AvatarGenerator clear];
// The user wants to select this theme
[[NSUserDefaults standardUserDefaults] setObject:newTheme forKey:@"userInterfaceTheme"];
[[NSUserDefaults standardUserDefaults] synchronize];
RiotSettings.shared.userInterfaceTheme = newTheme;
[self.tableView reloadData];
}

View file

@ -137,7 +137,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"UnifiedSearch"];
[[Analytics sharedInstance] trackScreen:@"UnifiedSearch"];
// Let's child display the loading not the home view controller
if (self.activityIndicator)

View file

@ -116,7 +116,7 @@
[super viewWillAppear:animated];
// Screen tracking
[[AppDelegate theDelegate] trackScreen:@"UnknowDevices"];
[[Analytics sharedInstance] trackScreen:@"UnknowDevices"];
[self.tableView reloadData];
}

View file

@ -114,19 +114,6 @@
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<br/><br/>
</li>
<li>
<b>GoogleAnalytics</b> (<a
href="https://www.google.com/analytics">https://www.google.com/analytics</a>)
<br/><br/>Measure your app performance.
<br/><br/>Copyright (c) 2011-2016 Google Inc. All rights reserved.
<br/><br/>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License at:
<br/><br/><a
href="https://www.apache.org/licenses/LICENSE-2.0">https://www.apache.org/licenses/LICENSE-2.0</a>
<br/><br/>Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
<br/><br/>Google Analytics Terms of Service: <a
href="https://www.google.com/analytics/terms">https://www.google.com/analytics/terms</a>
<br/><br/>
</li>
<li>
<b>GZIP</b> (<a
href="https://github.com/nicklockwood/GZIP">https://github.com/nicklockwood/GZIP</a>)

View file

@ -0,0 +1,4 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//