mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Merge pull request #5270 from vector-im/doug/5035_posthog
Replace Matomo with PostHog
This commit is contained in:
commit
62c521a8b2
112 changed files with 3358 additions and 605 deletions
|
@ -165,8 +165,20 @@ final class BuildSettings: NSObject {
|
|||
static let roomsAllowToJoinPublicRooms: Bool = true
|
||||
|
||||
// MARK: - Analytics
|
||||
static let analyticsServerUrl = URL(string: "https://piwik.riot.im/piwik.php")
|
||||
static let analyticsAppId = "14"
|
||||
#if DEBUG
|
||||
/// Host to use for PostHog analytics during development. Set to nil to disable analytics in debug builds.
|
||||
static let analyticsHost: String? = "https://posthog-poc.lab.element.dev"
|
||||
/// Public key for submitting analytics during development. Set to nil to disable analytics in debug builds.
|
||||
static let analyticsKey: String? = "rs-pJjsYJTuAkXJfhaMmPUNBhWliDyTKLOOxike6ck8"
|
||||
#else
|
||||
/// Host to use for PostHog analytics. Set to nil to disable analytics.
|
||||
static let analyticsHost: String? = "https://posthog.hss.element.io"
|
||||
/// Public key for submitting analytics. Set to nil to disable analytics.
|
||||
static let analyticsKey: String? = "phc_Jzsm6DTm6V2705zeU5dcNvQDlonOR68XvX2sh1sEOHO"
|
||||
#endif
|
||||
|
||||
/// The URL to open with more information about analytics terms.
|
||||
static let analyticsTermsURL = URL(string: "https://element.io/cookie-policy")!
|
||||
|
||||
|
||||
// MARK: - Bug report
|
||||
|
|
8
Podfile
8
Podfile
|
@ -3,7 +3,7 @@ source 'https://cdn.cocoapods.org/'
|
|||
# Uncomment this line to define a global platform for your project
|
||||
platform :ios, '12.1'
|
||||
|
||||
# Use frameforks to allow usage of pod written in Swift (like PiwikTracker)
|
||||
# Use frameworks to allow usage of pods written in Swift
|
||||
use_frameworks!
|
||||
|
||||
# Different flavours of pods to MatrixSDK. Can be one of:
|
||||
|
@ -67,8 +67,10 @@ abstract_target 'RiotPods' do
|
|||
pod 'KeychainAccess', '~> 4.2.2'
|
||||
pod 'WeakDictionary', '~> 2.0'
|
||||
|
||||
# Piwik for analytics
|
||||
pod 'MatomoTracker', '~> 7.4.1'
|
||||
# PostHog for analytics
|
||||
pod 'PostHog', '~> 1.4.4'
|
||||
pod 'AnalyticsEvents', :git => 'https://github.com/matrix-org/matrix-analytics-events.git', :branch => 'release/swift'
|
||||
# pod 'AnalyticsEvents', :path => '../matrix-analytics-events/AnalyticsEvents.podspec'
|
||||
|
||||
# Remove warnings from "bad" pods
|
||||
pod 'OLMKit', :inhibit_warnings => true
|
||||
|
|
25
Podfile.lock
25
Podfile.lock
|
@ -14,6 +14,7 @@ PODS:
|
|||
- AFNetworking/Serialization (4.0.1)
|
||||
- AFNetworking/UIKit (4.0.1):
|
||||
- AFNetworking/NSURLSession
|
||||
- AnalyticsEvents (0.1.0)
|
||||
- BlueCryptor (1.0.32)
|
||||
- BlueECC (1.2.5)
|
||||
- BlueRSA (1.0.200)
|
||||
|
@ -56,9 +57,6 @@ PODS:
|
|||
- LoggerAPI (1.9.200):
|
||||
- Logging (~> 1.1)
|
||||
- Logging (1.4.0)
|
||||
- MatomoTracker (7.4.1):
|
||||
- MatomoTracker/Core (= 7.4.1)
|
||||
- MatomoTracker/Core (7.4.1)
|
||||
- MatrixSDK (0.20.15):
|
||||
- MatrixSDK/Core (= 0.20.15)
|
||||
- MatrixSDK/Core (0.20.15):
|
||||
|
@ -76,6 +74,7 @@ PODS:
|
|||
- OLMKit/olmcpp (= 3.2.5)
|
||||
- OLMKit/olmc (3.2.5)
|
||||
- OLMKit/olmcpp (3.2.5)
|
||||
- PostHog (1.4.4)
|
||||
- ReadMoreTextView (3.0.1)
|
||||
- Realm (10.16.0):
|
||||
- Realm/Headers (= 10.16.0)
|
||||
|
@ -103,6 +102,7 @@ PODS:
|
|||
- ZXingObjC/All (3.6.5)
|
||||
|
||||
DEPENDENCIES:
|
||||
- AnalyticsEvents (from `https://github.com/matrix-org/matrix-analytics-events.git`, branch `release/swift`)
|
||||
- DGCollectionViewLeftAlignFlowLayout (~> 1.0.4)
|
||||
- Down (~> 0.11.0)
|
||||
- DSWaveformImage (~> 6.1.1)
|
||||
|
@ -116,10 +116,10 @@ DEPENDENCIES:
|
|||
- KeychainAccess (~> 4.2.2)
|
||||
- KTCenterFlowLayout (~> 1.3.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatomoTracker (~> 7.4.1)
|
||||
- MatrixSDK (= 0.20.15)
|
||||
- MatrixSDK/JingleCallStack (= 0.20.15)
|
||||
- OLMKit
|
||||
- PostHog (~> 1.4.4)
|
||||
- ReadMoreTextView (~> 3.0.1)
|
||||
- Reusable (~> 4.1)
|
||||
- SideMenu (~> 6.5)
|
||||
|
@ -157,9 +157,9 @@ SPEC REPOS:
|
|||
- libPhoneNumber-iOS
|
||||
- LoggerAPI
|
||||
- Logging
|
||||
- MatomoTracker
|
||||
- MatrixSDK
|
||||
- OLMKit
|
||||
- PostHog
|
||||
- ReadMoreTextView
|
||||
- Realm
|
||||
- Reusable
|
||||
|
@ -173,8 +173,19 @@ SPEC REPOS:
|
|||
- zxcvbn-ios
|
||||
- ZXingObjC
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
AnalyticsEvents:
|
||||
:branch: release/swift
|
||||
:git: https://github.com/matrix-org/matrix-analytics-events.git
|
||||
|
||||
CHECKOUT OPTIONS:
|
||||
AnalyticsEvents:
|
||||
:commit: f1805ad7c3fafa7fd9c6e2eaa9e0165f8142ecd2
|
||||
:git: https://github.com/matrix-org/matrix-analytics-events.git
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce
|
||||
AnalyticsEvents: 333bf47d67dc628fadd29ce887b7ac93d8bd6e05
|
||||
BlueCryptor: b0aee3d9b8f367b49b30de11cda90e1735571c24
|
||||
BlueECC: 0d18e93347d3ec6d41416de21c1ffa4d4cd3c2cc
|
||||
BlueRSA: dfeef51db96bcc4edec654956c1581adbda4e6a3
|
||||
|
@ -198,9 +209,9 @@ SPEC CHECKSUMS:
|
|||
libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75
|
||||
LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d
|
||||
Logging: beeb016c9c80cf77042d62e83495816847ef108b
|
||||
MatomoTracker: 24a846c9d3aa76933183fe9d47fd62c9efa863fb
|
||||
MatrixSDK: 2f4d3aacb1c53e2785f0be71d24b8e62e5c5c056
|
||||
OLMKit: 9fb4799c4a044dd2c06bda31ec31a12191ad30b5
|
||||
PostHog: 4b6321b521569092d4ef3a02238d9435dbaeb99f
|
||||
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
|
||||
Realm: b6027801398f3743fc222f096faa85281b506e6c
|
||||
Reusable: 6bae6a5e8aa793c9c441db0213c863a64bce9136
|
||||
|
@ -214,6 +225,6 @@ SPEC CHECKSUMS:
|
|||
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
|
||||
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
||||
|
||||
PODFILE CHECKSUM: 989bcc8b1857dc64a9b810ddaf4446903adbe162
|
||||
PODFILE CHECKSUM: e60814fe2084a7dca3f82c3a1c4a1b763ae822c0
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
|
|
123
Riot/Assets/Images.xcassets/Authentication/Analytics/AnalyticsCheckmark.imageset/AnalyticsTick.pdf
vendored
Normal file
123
Riot/Assets/Images.xcassets/Authentication/Analytics/AnalyticsCheckmark.imageset/AnalyticsTick.pdf
vendored
Normal file
|
@ -0,0 +1,123 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 1.000000 -1.000000 cm
|
||||
0.049479 0.742188 0.545395 scn
|
||||
10.000000 23.000000 m
|
||||
3.947715 23.000000 -1.000000 18.052284 -1.000000 12.000000 c
|
||||
1.000000 12.000000 l
|
||||
1.000000 16.947716 5.052285 21.000000 10.000000 21.000000 c
|
||||
10.000000 23.000000 l
|
||||
h
|
||||
-1.000000 12.000000 m
|
||||
-1.000000 5.947716 3.947715 1.000000 10.000000 1.000000 c
|
||||
10.000000 3.000000 l
|
||||
5.052285 3.000000 1.000000 7.052285 1.000000 12.000000 c
|
||||
-1.000000 12.000000 l
|
||||
h
|
||||
10.000000 1.000000 m
|
||||
16.052284 1.000000 21.000000 5.947716 21.000000 12.000000 c
|
||||
19.000000 12.000000 l
|
||||
19.000000 7.052285 14.947715 3.000000 10.000000 3.000000 c
|
||||
10.000000 1.000000 l
|
||||
h
|
||||
21.000000 12.000000 m
|
||||
21.000000 18.052284 16.052284 23.000000 10.000000 23.000000 c
|
||||
10.000000 21.000000 l
|
||||
14.947715 21.000000 19.000000 16.947716 19.000000 12.000000 c
|
||||
21.000000 12.000000 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 5.545532 4.655060 cm
|
||||
0.049479 0.742188 0.545395 scn
|
||||
0.717378 6.153159 m
|
||||
0.332610 6.549356 -0.300487 6.558620 -0.696684 6.173852 c
|
||||
-1.092881 5.789084 -1.102146 5.155987 -0.717378 4.759790 c
|
||||
0.717378 6.153159 l
|
||||
h
|
||||
3.257576 2.102139 m
|
||||
2.540198 1.405455 l
|
||||
2.728505 1.211555 2.987285 1.102139 3.257576 1.102139 c
|
||||
3.527867 1.102139 3.786646 1.211555 3.974954 1.405455 c
|
||||
3.257576 2.102139 l
|
||||
h
|
||||
11.626469 9.284243 m
|
||||
12.011237 9.680439 12.001972 10.313537 11.605776 10.698304 c
|
||||
11.209579 11.083073 10.576482 11.073808 10.191713 10.677610 c
|
||||
11.626469 9.284243 l
|
||||
h
|
||||
-0.717378 4.759790 m
|
||||
2.540198 1.405455 l
|
||||
3.974954 2.798823 l
|
||||
0.717378 6.153159 l
|
||||
-0.717378 4.759790 l
|
||||
h
|
||||
3.974954 1.405455 m
|
||||
11.626469 9.284243 l
|
||||
10.191713 10.677610 l
|
||||
2.540198 2.798823 l
|
||||
3.974954 1.405455 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
1679
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 22.000000 22.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000001769 00000 n
|
||||
0000001792 00000 n
|
||||
0000001965 00000 n
|
||||
0000002039 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
2098
|
||||
%%EOF
|
15
Riot/Assets/Images.xcassets/Authentication/Analytics/AnalyticsCheckmark.imageset/Contents.json
vendored
Normal file
15
Riot/Assets/Images.xcassets/Authentication/Analytics/AnalyticsCheckmark.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AnalyticsTick.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
641
Riot/Assets/Images.xcassets/Authentication/Analytics/AnalyticsLogo.imageset/AnalyticsLogo.pdf
vendored
Normal file
641
Riot/Assets/Images.xcassets/Authentication/Analytics/AnalyticsLogo.imageset/AnalyticsLogo.pdf
vendored
Normal file
|
@ -0,0 +1,641 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< /Type /XObject
|
||||
/Length 2 0 R
|
||||
/Group << /Type /Group
|
||||
/S /Transparency
|
||||
>>
|
||||
/Subtype /Form
|
||||
/Resources << /ExtGState << /E2 << /ca 0.400000 >>
|
||||
/E1 << /ca 0.400000 >>
|
||||
>> >>
|
||||
/BBox [ 0.000000 0.000000 119.000000 93.000000 ]
|
||||
>>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 14.875000 -0.000015 cm
|
||||
0.049479 0.742188 0.545395 scn
|
||||
0.000000 44.521278 m
|
||||
0.000000 69.109695 20.036579 89.042557 44.625000 89.042557 c
|
||||
44.625000 89.042557 l
|
||||
69.213417 89.042557 89.250000 69.109695 89.250000 44.521278 c
|
||||
89.250000 44.521278 l
|
||||
89.250000 19.932861 69.213417 0.000000 44.625000 0.000000 c
|
||||
44.625000 0.000000 l
|
||||
20.036579 0.000000 0.000000 19.932861 0.000000 44.521278 c
|
||||
0.000000 44.521278 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 52.227402 46.594299 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
0.000000 20.730219 m
|
||||
0.000000 22.447567 1.395428 23.839752 3.116776 23.839752 c
|
||||
14.592426 23.839752 23.895279 14.558517 23.895279 3.109533 c
|
||||
23.895279 1.392185 22.499851 0.000000 20.778503 0.000000 c
|
||||
19.057156 0.000000 17.661728 1.392185 17.661728 3.109533 c
|
||||
17.661728 11.123821 11.149731 17.620686 3.116776 17.620686 c
|
||||
1.395428 17.620686 0.000000 19.012871 0.000000 20.730219 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
q
|
||||
-1.000000 0.000000 -0.000000 -1.000000 66.772202 42.448242 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
0.000000 20.730206 m
|
||||
0.000000 22.447552 1.395429 23.839737 3.116779 23.839737 c
|
||||
14.592443 23.839737 23.895306 14.558502 23.895306 3.109520 c
|
||||
23.895306 1.392172 22.499878 -0.000011 20.778526 -0.000011 c
|
||||
19.057177 -0.000011 17.661749 1.392172 17.661749 3.109520 c
|
||||
17.661749 11.123808 11.149744 17.620672 3.116779 17.620672 c
|
||||
1.395429 17.620672 0.000000 19.012857 0.000000 20.730206 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
q
|
||||
-0.000000 1.000000 -1.000000 -0.000000 57.366512 37.265598 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
0.000000 20.722975 m
|
||||
0.000000 22.444323 1.392186 23.839752 3.109534 23.839752 c
|
||||
14.558520 23.839752 23.839758 14.536892 23.839758 3.061234 c
|
||||
23.839758 1.339886 22.447573 -0.055544 20.730225 -0.055544 c
|
||||
19.012875 -0.055544 17.620689 1.339886 17.620689 3.061234 c
|
||||
17.620689 11.094195 11.123824 17.606197 3.109534 17.606197 c
|
||||
1.392186 17.606197 0.000000 19.001625 0.000000 20.722975 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
q
|
||||
-0.000000 -1.000000 1.000000 -0.000000 61.633194 51.777008 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
0.000000 20.722975 m
|
||||
0.000000 22.444324 1.392186 23.839752 3.109534 23.839752 c
|
||||
14.558520 23.839752 23.839758 14.536893 23.839758 3.061237 c
|
||||
23.839758 1.339888 22.447573 -0.055540 20.730225 -0.055540 c
|
||||
19.012875 -0.055540 17.620689 1.339888 17.620689 3.061237 c
|
||||
17.620689 11.094196 11.123824 17.606197 3.109534 17.606197 c
|
||||
1.392186 17.606197 0.000000 19.001627 0.000000 20.722975 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 28.123089 16.695465 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
10.448288 0.000008 m
|
||||
11.057861 0.000008 11.560491 0.448122 11.646045 1.077618 c
|
||||
12.576446 7.617959 13.464069 8.514189 19.795069 9.229038 c
|
||||
20.436726 9.303724 20.917965 9.826525 20.917965 10.434681 c
|
||||
20.917965 11.053506 20.447418 11.554968 19.805763 11.640323 c
|
||||
13.506846 12.461866 12.694082 13.262072 11.646045 19.802414 c
|
||||
11.539103 20.431910 11.057861 20.869354 10.448288 20.869354 c
|
||||
9.849410 20.869354 9.346780 20.431910 9.250531 19.791744 c
|
||||
8.330826 13.251402 7.443202 12.355172 1.112203 11.640323 c
|
||||
0.470547 11.565637 0.000000 11.053506 0.000000 10.434681 c
|
||||
0.000000 9.826525 0.459853 9.314394 1.112203 9.229038 c
|
||||
7.411120 8.354148 8.191800 7.607290 9.250531 1.066948 c
|
||||
9.368169 0.437452 9.860105 0.000008 10.448288 0.000008 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 28.123089 15.195465 cm
|
||||
0.049479 0.742188 0.545395 scn
|
||||
11.646045 2.577618 m
|
||||
10.903506 2.683247 l
|
||||
10.902877 2.678619 l
|
||||
11.646045 2.577618 l
|
||||
h
|
||||
19.795069 10.729038 m
|
||||
19.879219 9.983770 l
|
||||
19.881781 9.984068 l
|
||||
19.795069 10.729038 l
|
||||
h
|
||||
19.805763 13.140323 m
|
||||
19.904661 13.883777 l
|
||||
19.902761 13.884024 l
|
||||
19.805763 13.140323 l
|
||||
h
|
||||
11.646045 21.302414 m
|
||||
12.386630 21.421087 l
|
||||
12.385450 21.428030 l
|
||||
11.646045 21.302414 l
|
||||
h
|
||||
9.250531 21.291744 m
|
||||
8.508834 21.403259 l
|
||||
8.507838 21.396183 l
|
||||
9.250531 21.291744 l
|
||||
h
|
||||
1.112203 13.140323 m
|
||||
1.028052 13.885592 l
|
||||
1.025491 13.885293 l
|
||||
1.112203 13.140323 l
|
||||
h
|
||||
1.112203 10.729038 m
|
||||
1.215387 11.471931 l
|
||||
1.209505 11.472700 l
|
||||
1.112203 10.729038 l
|
||||
h
|
||||
9.250531 2.566948 m
|
||||
8.510169 2.447100 l
|
||||
8.511623 2.438120 l
|
||||
8.513294 2.429176 l
|
||||
9.250531 2.566948 l
|
||||
h
|
||||
10.448288 0.750008 m
|
||||
11.450765 0.750008 12.255557 1.493191 12.389213 2.476614 c
|
||||
10.902877 2.678619 l
|
||||
10.865425 2.403053 10.664957 2.250008 10.448288 2.250008 c
|
||||
10.448288 0.750008 l
|
||||
h
|
||||
12.388570 2.471989 m
|
||||
12.620602 4.103085 12.844038 5.334888 13.138243 6.288948 c
|
||||
13.429995 7.235054 13.776924 7.858273 14.225985 8.307880 c
|
||||
15.140809 9.223818 16.666159 9.620979 19.879219 9.983774 c
|
||||
19.710920 11.474303 l
|
||||
16.592981 11.122249 14.509018 10.713870 13.164680 9.367895 c
|
||||
12.484159 8.686545 12.039045 7.814714 11.704848 6.730967 c
|
||||
11.373105 5.655174 11.136688 4.322321 10.903521 2.683245 c
|
||||
12.388570 2.471989 l
|
||||
h
|
||||
19.881781 9.984068 m
|
||||
20.873766 10.099530 21.667965 10.918526 21.667965 11.934681 c
|
||||
20.167965 11.934681 l
|
||||
20.167965 11.734525 19.999683 11.507918 19.708359 11.474010 c
|
||||
19.881781 9.984068 l
|
||||
h
|
||||
21.667965 11.934681 m
|
||||
21.667965 12.966555 20.881077 13.753887 19.904659 13.883774 c
|
||||
19.706867 12.396872 l
|
||||
20.013762 12.356048 20.167965 12.140457 20.167965 11.934681 c
|
||||
21.667965 11.934681 l
|
||||
h
|
||||
19.902761 13.884024 m
|
||||
18.330524 14.089085 17.151228 14.287123 16.235826 14.561028 c
|
||||
15.331247 14.831694 14.731587 15.163220 14.286853 15.607450 c
|
||||
13.840208 16.053589 13.492528 16.670565 13.191763 17.611713 c
|
||||
12.888441 18.560867 12.648228 19.788363 12.386598 21.421082 c
|
||||
10.905493 21.183746 l
|
||||
11.167881 19.546295 11.422276 18.221136 11.762950 17.155106 c
|
||||
12.106180 16.081072 12.551881 15.220337 13.226795 14.546188 c
|
||||
13.903622 13.870131 14.753702 13.438795 15.805837 13.123979 c
|
||||
16.847151 12.812399 18.131544 12.602333 19.708765 12.396622 c
|
||||
19.902761 13.884024 l
|
||||
h
|
||||
12.385450 21.428030 m
|
||||
12.223795 22.379583 11.461336 23.119354 10.448288 23.119354 c
|
||||
10.448288 21.619354 l
|
||||
10.654386 21.619354 10.854410 21.484234 10.906639 21.176800 c
|
||||
12.385450 21.428030 l
|
||||
h
|
||||
10.448288 23.119354 m
|
||||
9.455997 23.119354 8.656921 22.387987 8.508867 21.403254 c
|
||||
9.992196 21.180237 l
|
||||
10.036638 21.475830 10.242823 21.619354 10.448288 21.619354 c
|
||||
10.448288 23.119354 l
|
||||
h
|
||||
8.507838 21.396183 m
|
||||
8.278477 19.765110 8.056973 18.533388 7.764218 17.579443 c
|
||||
7.473907 16.633463 7.127907 16.010515 6.679636 15.561167 c
|
||||
5.766263 14.645599 4.241331 14.248406 1.028053 13.885587 c
|
||||
1.196352 12.395059 l
|
||||
4.314074 12.747088 6.398453 13.155437 7.741569 14.501781 c
|
||||
8.421542 15.183390 8.865580 16.055490 9.198210 17.139366 c
|
||||
9.528394 18.215273 9.762733 19.548208 9.993224 21.187307 c
|
||||
8.507838 21.396183 l
|
||||
h
|
||||
1.025491 13.885293 m
|
||||
0.028613 13.769261 -0.750000 12.956783 -0.750000 11.934681 c
|
||||
0.750000 11.934681 l
|
||||
0.750000 12.150229 0.912482 12.362013 1.198914 12.395352 c
|
||||
1.025491 13.885293 l
|
||||
h
|
||||
-0.750000 11.934681 m
|
||||
-0.750000 10.922595 0.016880 10.115961 1.014900 9.985377 c
|
||||
1.209505 11.472700 l
|
||||
0.902826 11.512827 0.750000 11.730454 0.750000 11.934681 c
|
||||
-0.750000 11.934681 l
|
||||
h
|
||||
1.009022 9.986170 m
|
||||
2.582546 9.767614 3.760892 9.563175 4.675031 9.286510 c
|
||||
5.578484 9.013078 6.174878 8.682938 6.616406 8.241908 c
|
||||
7.059544 7.799271 7.404263 7.187467 7.703608 6.250257 c
|
||||
8.005574 5.304842 8.245770 4.080437 8.510169 2.447100 c
|
||||
9.990894 2.686794 l
|
||||
9.725928 4.323629 9.471515 5.645210 9.132493 6.706642 c
|
||||
8.790851 7.776280 8.348207 8.632186 7.676467 9.303166 c
|
||||
7.003119 9.975756 6.157125 10.405144 5.109545 10.722197 c
|
||||
4.072651 11.036015 2.791318 11.253017 1.215384 11.471908 c
|
||||
1.009022 9.986170 l
|
||||
h
|
||||
8.513294 2.429176 m
|
||||
8.688872 1.489628 9.455215 0.750008 10.448288 0.750008 c
|
||||
10.448288 2.250008 l
|
||||
10.264993 2.250008 10.047464 2.385277 9.987769 2.704720 c
|
||||
8.513294 2.429176 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 72.573807 58.608093 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
8.619835 -0.000011 m
|
||||
9.122732 -0.000011 9.537402 0.373419 9.607985 0.897997 c
|
||||
10.375565 6.348283 11.107853 7.095141 16.330927 7.690849 c
|
||||
16.860292 7.753087 17.257317 8.188755 17.257317 8.695551 c
|
||||
17.257317 9.211239 16.869114 9.629124 16.339748 9.700253 c
|
||||
11.143145 10.384872 10.472614 11.051710 9.607985 16.501997 c
|
||||
9.519756 17.026575 9.122732 17.391113 8.619835 17.391113 c
|
||||
8.125761 17.391113 7.711091 17.026575 7.631686 16.493105 c
|
||||
6.872929 11.042820 6.140640 10.295961 0.917567 9.700253 c
|
||||
0.388201 9.638015 0.000000 9.211239 0.000000 8.695551 c
|
||||
0.000000 8.188755 0.379379 7.761978 0.917567 7.690849 c
|
||||
6.114172 6.961774 6.758233 6.339392 7.631686 0.889107 c
|
||||
7.728737 0.364527 8.134583 -0.000011 8.619835 -0.000011 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 72.573807 57.608093 cm
|
||||
0.049479 0.742188 0.545395 scn
|
||||
9.607985 1.897997 m
|
||||
9.112861 1.967726 l
|
||||
9.112450 1.964672 l
|
||||
9.607985 1.897997 l
|
||||
h
|
||||
16.330927 8.690849 m
|
||||
16.387587 8.194067 l
|
||||
16.389311 8.194269 l
|
||||
16.330927 8.690849 l
|
||||
h
|
||||
16.339748 10.700253 m
|
||||
16.406334 11.195801 l
|
||||
16.405056 11.195970 l
|
||||
16.339748 10.700253 l
|
||||
h
|
||||
9.607985 17.501997 m
|
||||
10.101830 17.580339 l
|
||||
10.101059 17.584925 l
|
||||
9.607985 17.501997 l
|
||||
h
|
||||
7.631686 17.493105 m
|
||||
7.137113 17.566721 l
|
||||
7.136462 17.562048 l
|
||||
7.631686 17.493105 l
|
||||
h
|
||||
0.917567 10.700253 m
|
||||
0.860907 11.197035 l
|
||||
0.859183 11.196833 l
|
||||
0.917567 10.700253 l
|
||||
h
|
||||
0.917567 8.690849 m
|
||||
0.987038 9.186015 l
|
||||
0.983079 9.186539 l
|
||||
0.917567 8.690849 l
|
||||
h
|
||||
7.631686 1.889107 m
|
||||
7.137843 1.809963 l
|
||||
7.140029 1.798147 l
|
||||
7.631686 1.889107 l
|
||||
h
|
||||
8.619835 0.499989 m
|
||||
9.388696 0.499989 10.001672 1.074373 10.103518 1.831324 c
|
||||
9.112450 1.964672 l
|
||||
9.073133 1.672462 8.856770 1.499989 8.619835 1.499989 c
|
||||
8.619835 0.499989 l
|
||||
h
|
||||
10.103099 1.828268 m
|
||||
10.294624 3.188222 10.480069 4.223436 10.725984 5.028956 c
|
||||
10.970345 5.829387 11.264832 6.369568 11.654185 6.763333 c
|
||||
12.442908 7.560994 13.744701 7.892640 16.387587 8.194070 c
|
||||
16.274267 9.187629 l
|
||||
13.694078 8.893350 12.018190 8.553713 10.943106 7.466446 c
|
||||
10.400556 6.917747 10.041607 6.212054 9.769561 5.320940 c
|
||||
9.499068 4.434915 9.305134 3.332915 9.112870 1.967726 c
|
||||
10.103099 1.828268 l
|
||||
h
|
||||
16.389311 8.194269 m
|
||||
17.155991 8.284410 17.757317 8.920855 17.757317 9.695551 c
|
||||
16.757317 9.695551 l
|
||||
16.757317 9.456655 16.564592 9.221766 16.272543 9.187428 c
|
||||
16.389311 8.194269 l
|
||||
h
|
||||
17.757317 9.695551 m
|
||||
17.757317 10.482604 17.162529 11.094193 16.406334 11.195800 c
|
||||
16.273165 10.204706 l
|
||||
16.575699 10.164056 16.757317 9.939874 16.757317 9.695551 c
|
||||
17.757317 9.695551 l
|
||||
h
|
||||
16.405056 11.195970 m
|
||||
15.107601 11.366901 14.126670 11.532858 13.361839 11.764020 c
|
||||
12.604449 11.992933 12.090017 12.277006 11.704502 12.665974 c
|
||||
11.317406 13.056538 11.022324 13.591100 10.770527 14.386979 c
|
||||
10.517109 15.187984 10.317721 16.219311 10.101810 17.580338 c
|
||||
9.114160 17.423656 l
|
||||
9.330563 16.059540 9.539227 14.963654 9.817105 14.085340 c
|
||||
10.096603 13.201900 10.456060 12.505035 10.994250 11.962027 c
|
||||
11.534022 11.417421 12.215625 11.065775 13.072525 10.806786 c
|
||||
13.921983 10.550046 14.973595 10.375915 16.274443 10.204536 c
|
||||
16.405056 11.195970 l
|
||||
h
|
||||
10.101059 17.584925 m
|
||||
9.977288 18.320837 9.395552 18.891113 8.619835 18.891113 c
|
||||
8.619835 17.891113 l
|
||||
8.849913 17.891113 9.062225 17.732313 9.114909 17.419067 c
|
||||
10.101059 17.584925 l
|
||||
h
|
||||
8.619835 18.891113 m
|
||||
7.859531 18.891113 7.250215 18.326427 7.137135 17.566717 c
|
||||
8.126238 17.419493 l
|
||||
8.171968 17.726725 8.391990 17.891113 8.619835 17.891113 c
|
||||
8.619835 18.891113 l
|
||||
h
|
||||
7.136462 17.562048 m
|
||||
6.947139 16.202108 6.763302 15.166948 6.518592 14.361504 c
|
||||
6.275430 13.561155 5.981721 13.021152 5.592998 12.627558 c
|
||||
4.805452 11.830145 3.503936 11.498478 0.860908 11.197033 c
|
||||
0.974226 10.203474 l
|
||||
3.554271 10.497736 5.230436 10.837353 6.304493 11.924868 c
|
||||
6.846570 12.473737 7.204643 13.179608 7.475406 14.070805 c
|
||||
7.744622 14.956905 7.936855 16.058958 8.126910 17.424164 c
|
||||
7.136462 17.562048 l
|
||||
h
|
||||
0.859183 11.196833 m
|
||||
0.089259 11.106312 -0.500000 10.475979 -0.500000 9.695551 c
|
||||
0.500000 9.695551 l
|
||||
0.500000 9.946500 0.687143 10.169718 0.975950 10.203673 c
|
||||
0.859183 11.196833 l
|
||||
h
|
||||
-0.500000 9.695551 m
|
||||
-0.500000 8.923503 0.079786 8.297226 0.852054 8.195160 c
|
||||
0.983079 9.186539 l
|
||||
0.678971 9.226731 0.500000 9.454006 0.500000 9.695551 c
|
||||
-0.500000 9.695551 l
|
||||
h
|
||||
0.848098 8.195699 m
|
||||
2.146421 8.013546 3.126409 7.842214 3.889960 7.608789 c
|
||||
4.646159 7.377612 5.157835 7.094736 5.540681 6.708459 c
|
||||
5.924912 6.320785 6.217544 5.790504 6.468155 4.997945 c
|
||||
6.720429 4.200129 6.919805 3.171420 7.137986 1.809986 c
|
||||
8.125387 1.968225 l
|
||||
7.906841 3.331934 7.698164 4.424881 7.421624 5.299438 c
|
||||
7.143422 6.179253 6.786478 6.872063 6.250936 7.412404 c
|
||||
5.714010 7.954142 5.035716 8.304206 4.182313 8.565100 c
|
||||
3.336262 8.823746 2.287016 9.003614 0.987036 9.186000 c
|
||||
0.848098 8.195699 l
|
||||
h
|
||||
7.140029 1.798147 m
|
||||
7.274719 1.070122 7.860904 0.499989 8.619835 0.499989 c
|
||||
8.619835 1.499989 l
|
||||
8.408263 1.499989 8.182755 1.658932 8.123343 1.980066 c
|
||||
7.140029 1.798147 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 107.227104 75.129669 cm
|
||||
0.049479 0.742188 0.545395 scn
|
||||
5.619252 -0.000010 m
|
||||
5.947090 -0.000010 6.217412 0.238985 6.263425 0.574716 c
|
||||
6.763808 4.062898 7.241186 4.540888 10.646096 4.922141 c
|
||||
10.991189 4.961974 11.250008 5.240800 11.250008 5.565150 c
|
||||
11.250008 5.895190 10.996941 6.162637 10.651848 6.208159 c
|
||||
7.264192 6.646316 6.827075 7.073092 6.263425 10.561275 c
|
||||
6.205909 10.897006 5.947090 11.130310 5.619252 11.130310 c
|
||||
5.297166 11.130310 5.026845 10.897006 4.975080 10.555585 c
|
||||
4.480448 7.067402 4.003070 6.589413 0.598160 6.208159 c
|
||||
0.253068 6.168327 0.000000 5.895190 0.000000 5.565150 c
|
||||
0.000000 5.240800 0.247316 4.967664 0.598160 4.922141 c
|
||||
3.985816 4.455533 4.405678 4.057208 4.975080 0.569025 c
|
||||
5.038347 0.233294 5.302918 -0.000010 5.619252 -0.000010 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 103.008408 84.868683 cm
|
||||
0.049479 0.742188 0.545395 scn
|
||||
3.160831 -0.000001 m
|
||||
3.345240 -0.000001 3.497297 0.134433 3.523178 0.323282 c
|
||||
3.804644 2.285384 4.073170 2.554254 5.988433 2.768708 c
|
||||
6.182547 2.791114 6.328133 2.947954 6.328133 3.130401 c
|
||||
6.328133 3.316049 6.185782 3.466487 5.991668 3.492094 c
|
||||
4.086111 3.738557 3.840232 3.978619 3.523178 5.940721 c
|
||||
3.490826 6.129570 3.345240 6.260803 3.160831 6.260803 c
|
||||
2.979658 6.260803 2.827601 6.129570 2.798484 5.937521 c
|
||||
2.520254 3.975418 2.251728 3.706549 0.336465 3.492094 c
|
||||
0.142351 3.469688 0.000000 3.316049 0.000000 3.130401 c
|
||||
0.000000 2.947954 0.139115 2.794315 0.336465 2.768708 c
|
||||
2.242023 2.506241 2.478195 2.282184 2.798484 0.320081 c
|
||||
2.834072 0.131233 2.982893 -0.000001 3.160831 -0.000001 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 103.711479 69.564484 cm
|
||||
0.049479 0.742188 0.545395 scn
|
||||
3.863233 0.000004 m
|
||||
4.088622 0.000004 4.274468 0.164312 4.306101 0.395127 c
|
||||
4.650115 2.793253 4.978312 3.121871 7.319186 3.383983 c
|
||||
7.556437 3.411368 7.734375 3.603061 7.734375 3.826052 c
|
||||
7.734375 4.052955 7.560391 4.236824 7.323141 4.268121 c
|
||||
4.994129 4.569354 4.693611 4.862762 4.306101 7.260888 c
|
||||
4.266560 7.491703 4.088622 7.652100 3.863233 7.652100 c
|
||||
3.641799 7.652100 3.455953 7.491703 3.420365 7.256976 c
|
||||
3.080306 4.858850 2.752109 4.530232 0.411235 4.268121 c
|
||||
0.173984 4.240736 0.000000 4.052955 0.000000 3.826052 c
|
||||
0.000000 3.603061 0.170030 3.415280 0.411235 3.383983 c
|
||||
2.740246 3.063190 3.028902 2.789341 3.420365 0.391215 c
|
||||
3.463861 0.160400 3.645753 0.000004 3.863233 0.000004 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 -0.070938 78.607880 cm
|
||||
0.049479 0.742188 0.545395 scn
|
||||
5.268045 -0.000012 m
|
||||
5.575393 -0.000012 5.828820 0.224045 5.871957 0.538792 c
|
||||
6.341066 3.808963 6.788608 4.257079 9.980709 4.614503 c
|
||||
10.304233 4.651846 10.546875 4.913247 10.546875 5.217325 c
|
||||
10.546875 5.526737 10.309625 5.777468 9.986101 5.820146 c
|
||||
6.810176 6.230918 6.400379 6.631021 5.871957 9.901192 c
|
||||
5.818036 10.215940 5.575393 10.434662 5.268045 10.434662 c
|
||||
4.966090 10.434662 4.712663 10.215940 4.664135 9.895857 c
|
||||
4.200418 6.625686 3.752876 6.177571 0.560775 5.820146 c
|
||||
0.237251 5.782803 0.000000 5.526737 0.000000 5.217325 c
|
||||
0.000000 4.913247 0.231859 4.657181 0.560775 4.614503 c
|
||||
3.736700 4.177058 4.130321 3.803629 4.664135 0.533457 c
|
||||
4.723447 0.218710 4.971482 -0.000012 5.268045 -0.000012 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
/E1 gs
|
||||
1.000000 0.000000 -0.000000 1.000000 9.772858 88.346924 cm
|
||||
0.049479 0.742188 0.545395 scn
|
||||
2.458421 -0.000008 m
|
||||
2.601850 -0.000008 2.720115 0.104552 2.740246 0.251434 c
|
||||
2.959164 1.777514 3.168016 1.986634 4.657663 2.153433 c
|
||||
4.808641 2.170859 4.921874 2.292846 4.921874 2.434749 c
|
||||
4.921874 2.579142 4.811157 2.696150 4.660179 2.716066 c
|
||||
3.178081 2.907759 2.986843 3.094474 2.740246 4.620554 c
|
||||
2.715083 4.767436 2.601850 4.869507 2.458421 4.869507 c
|
||||
2.317508 4.869507 2.199242 4.767436 2.176596 4.618064 c
|
||||
1.960194 3.091985 1.751342 2.882864 0.261695 2.716066 c
|
||||
0.110717 2.698639 0.000000 2.579142 0.000000 2.434749 c
|
||||
0.000000 2.292846 0.108201 2.173349 0.261695 2.153433 c
|
||||
1.743793 1.949291 1.927482 1.775024 2.176596 0.248944 c
|
||||
2.204275 0.102062 2.320024 -0.000008 2.458421 -0.000008 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
/E2 gs
|
||||
1.000000 0.000000 -0.000000 1.000000 13.288479 82.086121 cm
|
||||
0.049479 0.742188 0.545395 scn
|
||||
2.458421 -0.000008 m
|
||||
2.601850 -0.000008 2.720116 0.104552 2.740247 0.251434 c
|
||||
2.959164 1.777514 3.168017 1.986634 4.657664 2.153433 c
|
||||
4.808642 2.170859 4.921875 2.292846 4.921875 2.434749 c
|
||||
4.921875 2.579142 4.811158 2.696150 4.660181 2.716066 c
|
||||
3.178082 2.907759 2.986844 3.094474 2.740247 4.620554 c
|
||||
2.715084 4.767436 2.601850 4.869507 2.458421 4.869507 c
|
||||
2.317509 4.869507 2.199243 4.767436 2.176596 4.618064 c
|
||||
1.960195 3.091985 1.751342 2.882864 0.261695 2.716066 c
|
||||
0.110717 2.698639 0.000000 2.579142 0.000000 2.434749 c
|
||||
0.000000 2.292846 0.108201 2.173349 0.261695 2.153433 c
|
||||
1.743793 1.949291 1.927483 1.775024 2.176596 0.248944 c
|
||||
2.204276 0.102062 2.320025 -0.000008 2.458421 -0.000008 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
17245
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
<< /Type /XObject
|
||||
/Length 4 0 R
|
||||
/Group << /Type /Group
|
||||
/S /Transparency
|
||||
>>
|
||||
/Subtype /Form
|
||||
/Resources << >>
|
||||
/BBox [ 0.000000 0.000000 119.000000 93.000000 ]
|
||||
>>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
0.000000 93.000000 m
|
||||
119.000000 93.000000 l
|
||||
119.000000 0.000000 l
|
||||
0.000000 0.000000 l
|
||||
0.000000 93.000000 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
234
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /XObject << /X1 1 0 R >>
|
||||
/ExtGState << /E1 << /SMask << /Type /Mask
|
||||
/G 3 0 R
|
||||
/S /Alpha
|
||||
>>
|
||||
/Type /ExtGState
|
||||
>> >>
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Length 7 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
/E1 gs
|
||||
/X1 Do
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
7 0 obj
|
||||
46
|
||||
endobj
|
||||
|
||||
8 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 119.000000 93.000000 ]
|
||||
/Resources 5 0 R
|
||||
/Contents 6 0 R
|
||||
/Parent 9 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
9 0 obj
|
||||
<< /Kids [ 8 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
10 0 obj
|
||||
<< /Pages 9 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 11
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000017630 00000 n
|
||||
0000017654 00000 n
|
||||
0000018137 00000 n
|
||||
0000018159 00000 n
|
||||
0000018457 00000 n
|
||||
0000018559 00000 n
|
||||
0000018580 00000 n
|
||||
0000018754 00000 n
|
||||
0000018828 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 10 0 R
|
||||
/Size 11
|
||||
>>
|
||||
startxref
|
||||
18888
|
||||
%%EOF
|
15
Riot/Assets/Images.xcassets/Authentication/Analytics/AnalyticsLogo.imageset/Contents.json
vendored
Normal file
15
Riot/Assets/Images.xcassets/Authentication/Analytics/AnalyticsLogo.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AnalyticsLogo.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -41,6 +41,7 @@
|
|||
"retry" = "Retry";
|
||||
"on" = "On";
|
||||
"off" = "Off";
|
||||
"enable" = "Enable";
|
||||
"cancel" = "Cancel";
|
||||
"save" = "Save";
|
||||
"join" = "Join";
|
||||
|
@ -77,6 +78,7 @@
|
|||
|
||||
// Accessibility
|
||||
"accessibility_checkbox_label" = "checkbox";
|
||||
"accessibility_button_label" = "button";
|
||||
|
||||
// Authentication
|
||||
"auth_login" = "Log in";
|
||||
|
@ -577,7 +579,7 @@ Tap the + to start adding people.";
|
|||
"settings_term_conditions" = "Terms & Conditions";
|
||||
"settings_privacy_policy" = "Privacy Policy";
|
||||
"settings_third_party_notices" = "Third-party Notices";
|
||||
"settings_send_crash_report" = "Send anon crash & usage data";
|
||||
"settings_analytics_and_crash_data" = "Send crash and analytics data";
|
||||
"settings_enable_rageshake" = "Rage shake to report bug";
|
||||
"settings_clear_cache" = "Clear cache";
|
||||
|
||||
|
@ -945,8 +947,24 @@ Tap the + to start adding people.";
|
|||
"no_voip_title" = "Incoming call";
|
||||
"no_voip" = "%@ is calling you but %@ does not support calls yet.\nYou can ignore this notification and answer the call from another device or you can reject it.";
|
||||
|
||||
// Crash report
|
||||
"google_analytics_use_prompt" = "Would you like to help improve %@ by automatically reporting anonymous crash reports and usage data?";
|
||||
// Analytics
|
||||
"analytics_prompt_title" = "Help improve %@";
|
||||
"analytics_prompt_message_new_user" = "Help us identify issues and improve Element by sharing anonymous usage data. To understand how people use multiple devices, we’ll generate a random identifier, shared by your devices.";
|
||||
"analytics_prompt_message_upgrade" = "You previously consented to share anonymous usage data with us. Now, to help understand how people use multiple devices, we’ll generate a random identifier, shared by your devices.";
|
||||
/* Note: The placeholder is for the contents of analytics_prompt_terms_link_new_user */
|
||||
"analytics_prompt_terms_new_user" = "You can read all our terms %@.";
|
||||
"analytics_prompt_terms_link_new_user" = "here";
|
||||
/* Note: The placeholder is for the contents of analytics_prompt_terms_link_upgrade */
|
||||
"analytics_prompt_terms_upgrade" = "Read all our terms %@. Is that OK?";
|
||||
"analytics_prompt_terms_link_upgrade" = "here";
|
||||
/* Note: The word "don't" is formatted in bold */
|
||||
"analytics_prompt_point_1" = "We <b>don't</b> record or profile any account data";
|
||||
/* Note: The word "don't" is formatted in bold */
|
||||
"analytics_prompt_point_2" = "We <b>don't</b> share information with third parties";
|
||||
"analytics_prompt_point_3" = "You can turn this off anytime in settings";
|
||||
"analytics_prompt_not_now" = "Not now";
|
||||
"analytics_prompt_yes" = "Yes, that's fine";
|
||||
"analytics_prompt_stop" = "Stop sharing";
|
||||
|
||||
// Crypto
|
||||
"e2e_enabling_on_app_update" = "%@ now supports end-to-end encryption but you need to log in again to enable it.\n\nYou can do it now or later from the application settings.";
|
||||
|
|
|
@ -1897,6 +1897,34 @@ Library.
|
|||
SOFTWARE.
|
||||
<br/><br/>
|
||||
</li>
|
||||
<li>
|
||||
<b>PostHog iOS</b> (<a href="https://github.com/PostHog/posthog-ios">https://github.com/PostHog/posthog-ios</a>)
|
||||
<br/><br/>
|
||||
The MIT License (MIT)
|
||||
<br/><br/>
|
||||
Copyright (c) 2020 PostHog (part of Hiberly Inc)
|
||||
<br/><br/>
|
||||
Copyright (c) 2016 Segment.io, Inc.
|
||||
<br/><br/>
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
<br/><br/>
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
<br/><br/>
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
<br/><br/>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -20,6 +20,8 @@ internal typealias AssetImageTypeAlias = ImageAsset.Image
|
|||
// swiftlint:disable identifier_name line_length nesting type_body_length type_name
|
||||
internal enum Asset {
|
||||
internal enum Images {
|
||||
internal static let analyticsCheckmark = ImageAsset(name: "AnalyticsCheckmark")
|
||||
internal static let analyticsLogo = ImageAsset(name: "AnalyticsLogo")
|
||||
internal static let socialLoginButtonApple = ImageAsset(name: "social_login_button_apple")
|
||||
internal static let socialLoginButtonFacebook = ImageAsset(name: "social_login_button_facebook")
|
||||
internal static let socialLoginButtonGithub = ImageAsset(name: "social_login_button_github")
|
||||
|
|
|
@ -15,6 +15,10 @@ public class VectorL10n: NSObject {
|
|||
public static var accept: String {
|
||||
return VectorL10n.tr("Vector", "accept")
|
||||
}
|
||||
/// button
|
||||
public static var accessibilityButtonLabel: String {
|
||||
return VectorL10n.tr("Vector", "accessibility_button_label")
|
||||
}
|
||||
/// checkbox
|
||||
public static var accessibilityCheckboxLabel: String {
|
||||
return VectorL10n.tr("Vector", "accessibility_checkbox_label")
|
||||
|
@ -31,6 +35,58 @@ public class VectorL10n: NSObject {
|
|||
public static func activeCallDetails(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "active_call_details", p1)
|
||||
}
|
||||
/// Help us identify issues and improve Element by sharing anonymous usage data. To understand how people use multiple devices, we’ll generate a random identifier, shared by your devices.
|
||||
public static var analyticsPromptMessageNewUser: String {
|
||||
return VectorL10n.tr("Vector", "analytics_prompt_message_new_user")
|
||||
}
|
||||
/// You previously consented to share anonymous usage data with us. Now, to help understand how people use multiple devices, we’ll generate a random identifier, shared by your devices.
|
||||
public static var analyticsPromptMessageUpgrade: String {
|
||||
return VectorL10n.tr("Vector", "analytics_prompt_message_upgrade")
|
||||
}
|
||||
/// Not now
|
||||
public static var analyticsPromptNotNow: String {
|
||||
return VectorL10n.tr("Vector", "analytics_prompt_not_now")
|
||||
}
|
||||
/// We <b>don't</b> record or profile any account data
|
||||
public static var analyticsPromptPoint1: String {
|
||||
return VectorL10n.tr("Vector", "analytics_prompt_point_1")
|
||||
}
|
||||
/// We <b>don't</b> share information with third parties
|
||||
public static var analyticsPromptPoint2: String {
|
||||
return VectorL10n.tr("Vector", "analytics_prompt_point_2")
|
||||
}
|
||||
/// You can turn this off anytime in settings
|
||||
public static var analyticsPromptPoint3: String {
|
||||
return VectorL10n.tr("Vector", "analytics_prompt_point_3")
|
||||
}
|
||||
/// Stop sharing
|
||||
public static var analyticsPromptStop: String {
|
||||
return VectorL10n.tr("Vector", "analytics_prompt_stop")
|
||||
}
|
||||
/// here
|
||||
public static var analyticsPromptTermsLinkNewUser: String {
|
||||
return VectorL10n.tr("Vector", "analytics_prompt_terms_link_new_user")
|
||||
}
|
||||
/// here
|
||||
public static var analyticsPromptTermsLinkUpgrade: String {
|
||||
return VectorL10n.tr("Vector", "analytics_prompt_terms_link_upgrade")
|
||||
}
|
||||
/// You can read all our terms %@.
|
||||
public static func analyticsPromptTermsNewUser(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "analytics_prompt_terms_new_user", p1)
|
||||
}
|
||||
/// Read all our terms %@. Is that OK?
|
||||
public static func analyticsPromptTermsUpgrade(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "analytics_prompt_terms_upgrade", p1)
|
||||
}
|
||||
/// Help improve %@
|
||||
public static func analyticsPromptTitle(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "analytics_prompt_title", p1)
|
||||
}
|
||||
/// Yes, that's fine
|
||||
public static var analyticsPromptYes: String {
|
||||
return VectorL10n.tr("Vector", "analytics_prompt_yes")
|
||||
}
|
||||
/// Please review and accept the policies of this homeserver:
|
||||
public static var authAcceptPolicies: String {
|
||||
return VectorL10n.tr("Vector", "auth_accept_policies")
|
||||
|
@ -1235,6 +1291,10 @@ public class VectorL10n: NSObject {
|
|||
public static var emojiPickerTitle: String {
|
||||
return VectorL10n.tr("Vector", "emoji_picker_title")
|
||||
}
|
||||
/// Enable
|
||||
public static var enable: String {
|
||||
return VectorL10n.tr("Vector", "enable")
|
||||
}
|
||||
/// Send an encrypted message…
|
||||
public static var encryptedRoomMessagePlaceholder: String {
|
||||
return VectorL10n.tr("Vector", "encrypted_room_message_placeholder")
|
||||
|
@ -1439,10 +1499,6 @@ public class VectorL10n: NSObject {
|
|||
public static var gdprConsentNotGivenAlertReviewNowAction: String {
|
||||
return VectorL10n.tr("Vector", "gdpr_consent_not_given_alert_review_now_action")
|
||||
}
|
||||
/// Would you like to help improve %@ by automatically reporting anonymous crash reports and usage data?
|
||||
public static func googleAnalyticsUsePrompt(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "google_analytics_use_prompt", p1)
|
||||
}
|
||||
/// Home
|
||||
public static var groupDetailsHome: String {
|
||||
return VectorL10n.tr("Vector", "group_details_home")
|
||||
|
@ -4227,6 +4283,10 @@ public class VectorL10n: NSObject {
|
|||
public static var settingsAdvanced: String {
|
||||
return VectorL10n.tr("Vector", "settings_advanced")
|
||||
}
|
||||
/// Send crash and analytics data
|
||||
public static var settingsAnalyticsAndCrashData: String {
|
||||
return VectorL10n.tr("Vector", "settings_analytics_and_crash_data")
|
||||
}
|
||||
/// Call invitations
|
||||
public static var settingsCallInvitations: String {
|
||||
return VectorL10n.tr("Vector", "settings_call_invitations")
|
||||
|
@ -4755,10 +4815,6 @@ public class VectorL10n: NSObject {
|
|||
public static var settingsSecurity: String {
|
||||
return VectorL10n.tr("Vector", "settings_security")
|
||||
}
|
||||
/// Send anon crash & usage data
|
||||
public static var settingsSendCrashReport: String {
|
||||
return VectorL10n.tr("Vector", "settings_send_crash_report")
|
||||
}
|
||||
/// SENDING IMAGES AND VIDEOS
|
||||
public static var settingsSendingMedia: String {
|
||||
return VectorL10n.tr("Vector", "settings_sending_media")
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
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>
|
||||
|
||||
|
||||
// Metrics related to notifications
|
||||
FOUNDATION_EXPORT NSString *const AnalyticsNoficationsCategory;
|
||||
FOUNDATION_EXPORT NSString *const AnalyticsNoficationsTimeToDisplayContent;
|
||||
/**
|
||||
The analytics value for accept/decline of the identity server's terms.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString *const AnalyticsContactsIdentityServerAccepted;
|
||||
|
||||
|
||||
/**
|
||||
`Analytics` sends analytics to an analytics tool.
|
||||
*/
|
||||
@interface Analytics : NSObject <MXAnalyticsDelegate>
|
||||
|
||||
/**
|
||||
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;
|
||||
|
||||
@end
|
|
@ -1,162 +0,0 @@
|
|||
/*
|
||||
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 "GeneratedInterface-Swift.h"
|
||||
|
||||
|
||||
NSString *const AnalyticsNoficationsCategory = @"notifications";
|
||||
NSString *const AnalyticsNoficationsTimeToDisplayContent = @"timelineDisplay";
|
||||
NSString *const AnalyticsContactsIdentityServerAccepted = @"identityServerAccepted";
|
||||
|
||||
|
||||
// Duration data will be visible under the Piwik category called "Performance".
|
||||
// Other values will be visible in "Metrics".
|
||||
// Some Matomo screenshots are available at https://github.com/vector-im/element-ios/pull/3789.
|
||||
NSString *const kAnalyticsPerformanceCategory = @"Performance";
|
||||
NSString *const kAnalyticsMetricsCategory = @"Metrics";
|
||||
|
||||
|
||||
@import MatomoTracker;
|
||||
|
||||
@interface Analytics ()
|
||||
{
|
||||
MatomoTracker *matomoTracker;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation Analytics
|
||||
|
||||
+ (instancetype)sharedInstance
|
||||
{
|
||||
static Analytics *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[Analytics alloc] init];
|
||||
});
|
||||
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
matomoTracker = [[MatomoTracker alloc] initWithSiteId:BuildSettings.analyticsAppId
|
||||
baseURL:BuildSettings.analyticsServerUrl
|
||||
userAgent:@"iOSMatomoTracker"];
|
||||
[self migrateFromFourPointFourSharedInstance];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)migrateFromFourPointFourSharedInstance
|
||||
{
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"migratedFromFourPointFourSharedInstance"]) return;
|
||||
[matomoTracker copyFromOldSharedInstance];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:true forKey:@"migratedFromFourPointFourSharedInstance"];
|
||||
}
|
||||
|
||||
- (void)start
|
||||
{
|
||||
// Check whether the user has enabled the sending of crash reports.
|
||||
if (RiotSettings.shared.enableCrashReport)
|
||||
{
|
||||
matomoTracker.isOptedOut = NO;
|
||||
|
||||
[matomoTracker setCustomVariableWithIndex:1 name:@"App Platform" value:@"iOS Platform"];
|
||||
[matomoTracker 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];
|
||||
[matomoTracker setCustomVariableWithIndex:4 name:@"Chosen Language" value:language];
|
||||
|
||||
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
|
||||
if (account)
|
||||
{
|
||||
[matomoTracker setCustomVariableWithIndex:7 name:@"Homeserver URL" value:account.mxCredentials.homeServer];
|
||||
[matomoTracker 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
|
||||
matomoTracker.isOptedOut = YES;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
MXLogDebug(@"[AppDelegate] The user decided to not send analytics");
|
||||
matomoTracker.isOptedOut = YES;
|
||||
[MXLogger logCrashes:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stop
|
||||
{
|
||||
matomoTracker.isOptedOut = YES;
|
||||
[MXLogger logCrashes:NO];
|
||||
}
|
||||
|
||||
- (void)trackScreen:(NSString *)screenName
|
||||
{
|
||||
// Use the same pattern as Android
|
||||
NSString *appName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"];
|
||||
NSString *appVersion = [AppDelegate theDelegate].appVersion;
|
||||
|
||||
[matomoTracker trackWithView:@[@"ios", appName, appVersion, screenName]
|
||||
url:nil];
|
||||
}
|
||||
|
||||
- (void)dispatch
|
||||
{
|
||||
[matomoTracker dispatch];
|
||||
}
|
||||
|
||||
#pragma mark - MXAnalyticsDelegate
|
||||
|
||||
- (void)trackDuration:(NSTimeInterval)seconds category:(NSString*)category name:(NSString*)name
|
||||
{
|
||||
// Report time in ms to make figures look better in Matomo
|
||||
NSNumber *value = @(seconds * 1000);
|
||||
[matomoTracker trackWithEventWithCategory:kAnalyticsPerformanceCategory
|
||||
action:category
|
||||
name:name
|
||||
number:value
|
||||
url:nil];
|
||||
}
|
||||
|
||||
- (void)trackValue:(NSNumber*)value category:(NSString*)category name:(NSString*)name
|
||||
{
|
||||
[matomoTracker trackWithEventWithCategory:kAnalyticsMetricsCategory
|
||||
action:category
|
||||
name:name
|
||||
number:value
|
||||
url:nil];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
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>
|
||||
|
||||
/**
|
||||
Failure reasons as defined in https://docs.google.com/document/d/1es7cTCeJEXXfRCTRgZerAM2Wg5ZerHjvlpfTW-gsOfI.
|
||||
*/
|
||||
struct DecryptionFailureReasonStruct
|
||||
{
|
||||
__unsafe_unretained NSString * const unspecified;
|
||||
__unsafe_unretained NSString * const olmKeysNotSent;
|
||||
__unsafe_unretained NSString * const olmIndexError;
|
||||
__unsafe_unretained NSString * const unexpected;
|
||||
};
|
||||
extern const struct DecryptionFailureReasonStruct DecryptionFailureReason;
|
||||
|
||||
/**
|
||||
`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
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
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"
|
||||
|
||||
const struct DecryptionFailureReasonStruct DecryptionFailureReason = {
|
||||
.unspecified = @"unspecified_error",
|
||||
.olmKeysNotSent = @"olm_keys_not_sent_error",
|
||||
.olmIndexError = @"olm_index_error",
|
||||
.unexpected = @"unexpected_error"
|
||||
};
|
||||
|
||||
@implementation DecryptionFailure
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
_ts = [NSDate date].timeIntervalSince1970;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
|
@ -23,7 +23,8 @@ final class RiotSettings: NSObject {
|
|||
// MARK: - Constants
|
||||
|
||||
public enum UserDefaultsKeys {
|
||||
static let enableCrashReport = "enableCrashReport"
|
||||
static let enableAnalytics = "enableAnalytics"
|
||||
static let matomoAnalytics = "enableCrashReport"
|
||||
static let notificationsShowDecryptedContent = "showDecryptedContent"
|
||||
static let allowStunServerFallback = "allowStunServerFallback"
|
||||
static let pinRoomsWithMissedNotificationsOnHome = "pinRoomsWithMissedNotif"
|
||||
|
@ -100,13 +101,31 @@ final class RiotSettings: NSObject {
|
|||
|
||||
// MARK: Other
|
||||
|
||||
/// Indicate if `enableCrashReport` settings has been set once.
|
||||
var isEnableCrashReportHasBeenSetOnce: Bool {
|
||||
return RiotSettings.defaults.object(forKey: UserDefaultsKeys.enableCrashReport) != nil
|
||||
/// Whether the user was previously shown the Matomo analytics prompt.
|
||||
var hasSeenAnalyticsPrompt: Bool {
|
||||
RiotSettings.defaults.object(forKey: UserDefaultsKeys.enableAnalytics) != nil
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDefaultsKeys.enableCrashReport, defaultValue: false, storage: defaults)
|
||||
var enableCrashReport
|
||||
/// Whether the user has both seen the Matomo analytics prompt and declined it.
|
||||
var hasDeclinedMatomoAnalytics: Bool {
|
||||
RiotSettings.defaults.object(forKey: UserDefaultsKeys.matomoAnalytics) != nil && !RiotSettings.defaults.bool(forKey: UserDefaultsKeys.matomoAnalytics)
|
||||
}
|
||||
|
||||
/// Whether the user previously accepted the Matomo analytics prompt.
|
||||
/// This allows these users to be shown a different prompt to explain the changes.
|
||||
var hasAcceptedMatomoAnalytics: Bool {
|
||||
RiotSettings.defaults.bool(forKey: UserDefaultsKeys.matomoAnalytics)
|
||||
}
|
||||
|
||||
/// `true` when the user has opted in to send analytics.
|
||||
@UserDefault(key: UserDefaultsKeys.enableAnalytics, defaultValue: false, storage: defaults)
|
||||
var enableAnalytics
|
||||
|
||||
/// Indicates if the device has already called identify for this session to PostHog.
|
||||
/// This is separate to `enableAnalytics` as logging out will leave analytics
|
||||
/// enabled but reset identification.
|
||||
@UserDefault(key: "isIdentifiedForAnalytics", defaultValue: false, storage: defaults)
|
||||
var isIdentifiedForAnalytics
|
||||
|
||||
@UserDefault(key: "enableRageShake", defaultValue: false, storage: defaults)
|
||||
var enableRageShake
|
||||
|
|
259
Riot/Modules/Analytics/Analytics.swift
Normal file
259
Riot/Modules/Analytics/Analytics.swift
Normal file
|
@ -0,0 +1,259 @@
|
|||
//
|
||||
// Copyright 2021 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 PostHog
|
||||
import AnalyticsEvents
|
||||
|
||||
/// A class responsible for managing an analytics client
|
||||
/// and sending events through this client.
|
||||
@objcMembers class Analytics: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/// The singleton instance to be used within the Riot target.
|
||||
static let shared = Analytics()
|
||||
|
||||
/// The analytics client to send events with.
|
||||
private var client: AnalyticsClientProtocol = PostHogAnalyticsClient()
|
||||
|
||||
/// The service used to interact with account data settings.
|
||||
private var service: AnalyticsService?
|
||||
|
||||
/// Whether or not the object is enabled and sending events to the server.
|
||||
var isRunning: Bool { client.isRunning }
|
||||
|
||||
/// Whether to show the user the analytics opt in prompt.
|
||||
var shouldShowAnalyticsPrompt: Bool {
|
||||
// Only show the prompt once, and when analytics are configured in BuildSettings.
|
||||
!RiotSettings.shared.hasSeenAnalyticsPrompt && PHGPostHogConfiguration.standard != nil
|
||||
}
|
||||
|
||||
/// Indicates whether the user previously accepted Matomo analytics and should be shown the upgrade prompt.
|
||||
var promptShouldDisplayUpgradeMessage: Bool {
|
||||
RiotSettings.shared.hasAcceptedMatomoAnalytics
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
/// Opts in to analytics tracking with the supplied session.
|
||||
/// - Parameter session: An optional session to use to when reading/generating the analytics ID.
|
||||
/// The session will be ignored if not running.
|
||||
func optIn(with session: MXSession?) {
|
||||
RiotSettings.shared.enableAnalytics = true
|
||||
startIfEnabled()
|
||||
|
||||
guard let session = session else { return }
|
||||
useAnalyticsSettings(from: session)
|
||||
}
|
||||
|
||||
/// Stops analytics tracking and calls `reset` to clear any IDs and event queues.
|
||||
func optOut() {
|
||||
RiotSettings.shared.enableAnalytics = false
|
||||
|
||||
// The order is important here. PostHog ignores the reset if stopped.
|
||||
reset()
|
||||
client.stop()
|
||||
|
||||
MXLog.debug("[Analytics] Stopped.")
|
||||
}
|
||||
|
||||
/// Starts the analytics client if the user has opted in, otherwise does nothing.
|
||||
func startIfEnabled() {
|
||||
guard RiotSettings.shared.enableAnalytics, !isRunning else { return }
|
||||
|
||||
client.start()
|
||||
|
||||
// Sanity check in case something went wrong.
|
||||
guard client.isRunning else { return }
|
||||
|
||||
MXLog.debug("[Analytics] Started.")
|
||||
|
||||
// Catch and log crashes
|
||||
MXLogger.logCrashes(true)
|
||||
MXLogger.setBuildVersion(AppDelegate.theDelegate().build)
|
||||
}
|
||||
|
||||
/// Use the analytics settings from the supplied session to configure analytics.
|
||||
/// For now this is only used for (pseudonymous) identification.
|
||||
/// - Parameter session: The session to read analytics settings from.
|
||||
func useAnalyticsSettings(from session: MXSession) {
|
||||
guard
|
||||
RiotSettings.shared.enableAnalytics,
|
||||
!RiotSettings.shared.isIdentifiedForAnalytics
|
||||
else { return }
|
||||
|
||||
let service = AnalyticsService(session: session)
|
||||
self.service = service
|
||||
|
||||
service.settings { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
|
||||
switch result {
|
||||
case .success(let settings):
|
||||
self.identify(with: settings)
|
||||
self.service = nil
|
||||
case .failure:
|
||||
MXLog.error("[Analytics] Failed to use analytics settings. Will continue to run without analytics ID.")
|
||||
self.service = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets the any IDs and event queues in the analytics client. This method should
|
||||
/// be called on sign-out to maintain opt-in status, whilst ensuring the next
|
||||
/// account used isn't associated with the previous one.
|
||||
/// Note: **MUST** be called before stopping PostHog or the reset is ignored.
|
||||
func reset() {
|
||||
client.reset()
|
||||
MXLog.debug("[Analytics] Reset.")
|
||||
RiotSettings.shared.isIdentifiedForAnalytics = false
|
||||
|
||||
// Stop collecting crash logs
|
||||
MXLogger.logCrashes(false)
|
||||
}
|
||||
|
||||
/// Flushes the event queue in the analytics client, uploading all pending events.
|
||||
/// Normally events are sent in batches. Call this method when you need an event
|
||||
/// to be sent immediately.
|
||||
func forceUpload() {
|
||||
client.flush()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
/// Identify (pseudonymously) any future events with the ID from the analytics account data settings.
|
||||
/// - Parameter settings: The settings to use for identification. The ID must be set *before* calling this method.
|
||||
private func identify(with settings: AnalyticsSettings) {
|
||||
guard let id = settings.id else {
|
||||
MXLog.error("[Analytics] identify(with:) called before an ID has been generated.")
|
||||
return
|
||||
}
|
||||
|
||||
client.identify(id: id)
|
||||
MXLog.debug("[Analytics] Identified.")
|
||||
RiotSettings.shared.isIdentifiedForAnalytics = true
|
||||
}
|
||||
|
||||
/// Capture an event in the `client`.
|
||||
/// - Parameter event: The event to capture.
|
||||
private func capture(event: AnalyticsEventProtocol) {
|
||||
client.capture(event)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public tracking methods
|
||||
// The following methods are exposed for compatibility with Objective-C as
|
||||
// the `capture` method and the generated events cannot be bridged from Swift.
|
||||
extension Analytics {
|
||||
/// Track the presentation of a screen
|
||||
/// - Parameters:
|
||||
/// - screen: The screen that was shown.
|
||||
/// - milliseconds: An optional value representing how long the screen was shown for in milliseconds.
|
||||
func trackScreen(_ screen: AnalyticsScreen, duration milliseconds: Int?) {
|
||||
let event = AnalyticsEvent.Screen(durationMs: milliseconds, screenName: screen.screenName)
|
||||
client.screen(event)
|
||||
}
|
||||
|
||||
/// The the presentation of a screen without including a duration
|
||||
/// - Parameter screen: The screen that was shown
|
||||
func trackScreen(_ screen: AnalyticsScreen) {
|
||||
trackScreen(screen, duration: nil)
|
||||
}
|
||||
|
||||
/// Track an element that has been tapped
|
||||
/// - Parameters:
|
||||
/// - tap: The element that was tapped
|
||||
/// - index: The index of the element, if it's in a list of elements
|
||||
func trackTap(_ tap: AnalyticsUIElement, index: Int?) {
|
||||
let event = AnalyticsEvent.Click(index: index, name: tap.elementName)
|
||||
client.capture(event)
|
||||
}
|
||||
|
||||
/// Track an element that has been tapped without including an index
|
||||
/// - Parameters:
|
||||
/// - tap: The element that was tapped
|
||||
func trackTap(_ tap: AnalyticsUIElement) {
|
||||
trackTap(tap, index: nil)
|
||||
}
|
||||
|
||||
/// Track an E2EE error that occurred
|
||||
/// - Parameters:
|
||||
/// - reason: The error that occurred.
|
||||
/// - count: The number of times that error occurred.
|
||||
func trackE2EEError(_ reason: DecryptionFailureReason, count: Int) {
|
||||
for _ in 0..<count {
|
||||
let event = AnalyticsEvent.Error(context: nil, domain: .E2EE, name: reason.errorName)
|
||||
capture(event: event)
|
||||
}
|
||||
}
|
||||
|
||||
/// Track whether the user accepted or declined the terms to an identity server.
|
||||
/// **Note** This method isn't currently implemented.
|
||||
/// - Parameter accepted: Whether the terms were accepted.
|
||||
func trackIdentityServerAccepted(_ accepted: Bool) {
|
||||
// Do we still want to track this?
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MXAnalyticsDelegate
|
||||
extension Analytics: MXAnalyticsDelegate {
|
||||
func trackDuration(_ milliseconds: Int, name: MXTaskProfileName, units: UInt) {
|
||||
guard let analyticsName = name.analyticsName else {
|
||||
MXLog.warning("[Analytics] Attempt to capture unknown profile task: \(name.rawValue)")
|
||||
return
|
||||
}
|
||||
|
||||
let event = AnalyticsEvent.PerformanceTimer(context: nil, itemCount: Int(units), name: analyticsName, timeMs: milliseconds)
|
||||
capture(event: event)
|
||||
}
|
||||
|
||||
func trackCallStarted(withVideo isVideo: Bool, numberOfParticipants: Int, incoming isIncoming: Bool) {
|
||||
let event = AnalyticsEvent.CallStarted(isVideo: isVideo, numParticipants: numberOfParticipants, placed: !isIncoming)
|
||||
capture(event: event)
|
||||
}
|
||||
|
||||
func trackCallEnded(withDuration duration: Int, video isVideo: Bool, numberOfParticipants: Int, incoming isIncoming: Bool) {
|
||||
let event = AnalyticsEvent.CallEnded(durationMs: duration, isVideo: isVideo, numParticipants: numberOfParticipants, placed: !isIncoming)
|
||||
capture(event: event)
|
||||
}
|
||||
|
||||
func trackCallError(with reason: __MXCallHangupReason, video isVideo: Bool, numberOfParticipants: Int, incoming isIncoming: Bool) {
|
||||
let callEvent = AnalyticsEvent.CallError(isVideo: isVideo, numParticipants: numberOfParticipants, placed: !isIncoming)
|
||||
let event = AnalyticsEvent.Error(context: nil, domain: .VOIP, name: reason.errorName)
|
||||
capture(event: callEvent)
|
||||
capture(event: event)
|
||||
}
|
||||
|
||||
func trackCreatedRoom(asDM isDM: Bool) {
|
||||
let event = AnalyticsEvent.CreatedRoom(isDM: isDM)
|
||||
capture(event: event)
|
||||
}
|
||||
|
||||
func trackJoinedRoom(asDM isDM: Bool, memberCount: UInt) {
|
||||
guard let roomSize = AnalyticsEvent.JoinedRoom.RoomSize(memberCount: memberCount) else {
|
||||
MXLog.warning("[Analytics] Attempt to capture joined room with invalid member count: \(memberCount)")
|
||||
return
|
||||
}
|
||||
|
||||
let event = AnalyticsEvent.JoinedRoom(isDM: isDM, roomSize: roomSize)
|
||||
capture(event: event)
|
||||
}
|
||||
|
||||
/// **Note** This method isn't currently implemented.
|
||||
func trackContactsAccessGranted(_ granted: Bool) {
|
||||
// Do we still want to track this?
|
||||
}
|
||||
}
|
48
Riot/Modules/Analytics/AnalyticsClientProtocol.swift
Normal file
48
Riot/Modules/Analytics/AnalyticsClientProtocol.swift
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// Copyright 2021 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 AnalyticsEvents
|
||||
|
||||
/// A protocol representing an analytics client.
|
||||
protocol AnalyticsClientProtocol {
|
||||
/// Whether the analytics client is currently reporting data or ignoring it.
|
||||
var isRunning: Bool { get }
|
||||
|
||||
/// Starts the analytics client reporting data.
|
||||
func start()
|
||||
|
||||
/// Associate the client with an ID. This is persisted until `reset` is called.
|
||||
/// - Parameter id: The ID to associate with the user.
|
||||
func identify(id: String)
|
||||
|
||||
/// Reset all stored properties and any event queues on the client. Note that
|
||||
/// the client will remain active, but in a fresh unidentified state.
|
||||
func reset()
|
||||
|
||||
/// Stop the analytics client reporting data.
|
||||
func stop()
|
||||
|
||||
/// Send any queued events immediately.
|
||||
func flush()
|
||||
|
||||
/// Capture the supplied analytics event.
|
||||
/// - Parameter event: The event to capture.
|
||||
func capture(_ event: AnalyticsEventProtocol)
|
||||
|
||||
/// Capture the supplied analytics screen event.
|
||||
/// - Parameter event: The screen event to capture.
|
||||
func screen(_ event: AnalyticsScreenProtocol)
|
||||
}
|
114
Riot/Modules/Analytics/AnalyticsScreen.swift
Normal file
114
Riot/Modules/Analytics/AnalyticsScreen.swift
Normal file
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// Copyright 2021 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
|
||||
import AnalyticsEvents
|
||||
|
||||
@objc enum AnalyticsScreen: Int {
|
||||
case sidebar
|
||||
case home
|
||||
case favourites
|
||||
case people
|
||||
case rooms
|
||||
case searchRooms
|
||||
case searchMessages
|
||||
case searchPeople
|
||||
case searchFiles
|
||||
case room
|
||||
case roomDetails
|
||||
case roomMembers
|
||||
case user
|
||||
case roomSearch
|
||||
case roomUploads
|
||||
case roomSettings
|
||||
case roomNotifications
|
||||
case roomDirectory
|
||||
case switchDirectory
|
||||
case startChat
|
||||
case createRoom
|
||||
case settings
|
||||
case settingsSecurity
|
||||
case settingsDefaultNotifications
|
||||
case settingsMentionsAndKeywords
|
||||
case deactivateAccount
|
||||
case group
|
||||
case myGroups
|
||||
case inviteFriends
|
||||
|
||||
/// The screen name reported to the AnalyticsEvent.
|
||||
var screenName: AnalyticsEvent.Screen.ScreenName {
|
||||
switch self {
|
||||
case .sidebar:
|
||||
return .MobileSidebar
|
||||
case .home:
|
||||
return .Home
|
||||
case .favourites:
|
||||
return .MobileFavourites
|
||||
case .people:
|
||||
return .MobilePeople
|
||||
case .rooms:
|
||||
return .MobileRooms
|
||||
case .searchRooms:
|
||||
return .MobileSearchRooms
|
||||
case .searchMessages:
|
||||
return .MobileSearchMessages
|
||||
case .searchPeople:
|
||||
return .MobileSearchPeople
|
||||
case .searchFiles:
|
||||
return .MobileSearchFiles
|
||||
case .room:
|
||||
return .Room
|
||||
case .roomDetails:
|
||||
return .RoomDetails
|
||||
case .roomMembers:
|
||||
return .RoomMembers
|
||||
case .user:
|
||||
return .User
|
||||
case .roomSearch:
|
||||
return .RoomSearch
|
||||
case .roomUploads:
|
||||
return .RoomUploads
|
||||
case .roomSettings:
|
||||
return .RoomSettings
|
||||
case .roomNotifications:
|
||||
return .RoomNotifications
|
||||
case .roomDirectory:
|
||||
return .RoomDirectory
|
||||
case .switchDirectory:
|
||||
return .MobileSwitchDirectory
|
||||
case .startChat:
|
||||
return .StartChat
|
||||
case .createRoom:
|
||||
return .CreateRoom
|
||||
case .settings:
|
||||
return .Settings
|
||||
case .settingsSecurity:
|
||||
return .SettingsSecurity
|
||||
case .settingsDefaultNotifications:
|
||||
return .SettingsDefaultNotifications
|
||||
case .settingsMentionsAndKeywords:
|
||||
return .SettingsMentionsAndKeywords
|
||||
case .deactivateAccount:
|
||||
return .DeactivateAccount
|
||||
case .group:
|
||||
return .Group
|
||||
case .myGroups:
|
||||
return .MyGroups
|
||||
case .inviteFriends:
|
||||
return .MobileInviteFriends
|
||||
}
|
||||
}
|
||||
}
|
82
Riot/Modules/Analytics/AnalyticsScreenTimer.swift
Normal file
82
Riot/Modules/Analytics/AnalyticsScreenTimer.swift
Normal file
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// Copyright 2021 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 UIKit
|
||||
|
||||
/// An object to record how long a screen has been presented for and
|
||||
/// report the screen's display to the `Analytics` object.
|
||||
@objcMembers class AnalyticsScreenTimer: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/// The screen being tracked.
|
||||
private let screen: AnalyticsScreen
|
||||
|
||||
/// The date that the screen was presented to the user.
|
||||
private var startDate: Date?
|
||||
/// Whether the app was backgrounded whilst the screen was being presented.
|
||||
private var didPause = false
|
||||
|
||||
/// The duration in milliseconds that the screen has been shown for. The value will
|
||||
/// be reported as `nil` if the timer isn't running, or if the app was backgrounded
|
||||
/// during the screen's display.
|
||||
private var duration: Int? {
|
||||
guard let startDate = startDate else {
|
||||
MXLog.warning("[AnalyticsScreenTimer] Duration requested on a stopped timer!")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Consider the duration invalid if the app has been backgrounded
|
||||
guard !didPause else { return nil }
|
||||
|
||||
let timeInterval = Date().timeIntervalSince(startDate)
|
||||
return Int(timeInterval * 1000)
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
/// Create a new screen timer for the specified screen.
|
||||
/// - Parameter screen: The screen that should be timed.
|
||||
init(screen: AnalyticsScreen) {
|
||||
self.screen = screen
|
||||
|
||||
super.init()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(pause), name: UIApplication.willResignActiveNotification, object: nil)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
/// Start the timer.
|
||||
func start() {
|
||||
startDate = Date()
|
||||
}
|
||||
|
||||
/// Stop the timer and report the screen to `Analytics`.
|
||||
func stop() {
|
||||
guard let duration = duration else { return }
|
||||
|
||||
Analytics.shared.trackScreen(screen, duration: duration)
|
||||
self.startDate = nil
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
/// Record that the timer has been interrupted by the app moving to the background.
|
||||
@objc private func pause() {
|
||||
didPause = true
|
||||
}
|
||||
}
|
77
Riot/Modules/Analytics/AnalyticsService.swift
Normal file
77
Riot/Modules/Analytics/AnalyticsService.swift
Normal file
|
@ -0,0 +1,77 @@
|
|||
//
|
||||
// Copyright 2021 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
|
||||
|
||||
enum AnalyticsServiceError: Error {
|
||||
/// The session supplied to the service does not have a state of `MXSessionStateRunning`.
|
||||
case sessionIsNotRunning
|
||||
/// An error occurred but the session did not report what it was.
|
||||
case unknown
|
||||
}
|
||||
|
||||
/// A service responsible for handling the `im.vector.analytics` event from the user's account data.
|
||||
class AnalyticsService {
|
||||
let session: MXSession
|
||||
|
||||
/// Creates an analytics service with the supplied session.
|
||||
/// - Parameter session: The session to use when reading analytics settings from account data.
|
||||
init(session: MXSession) {
|
||||
self.session = session
|
||||
}
|
||||
|
||||
/// The analytics settings for the current user. Calling this method will check whether the settings already
|
||||
/// contain an `id` property and if not, will add one to the account data before calling the completion.
|
||||
/// - Parameter completion: A completion handler that will be called when the request completes.
|
||||
///
|
||||
/// The request will fail if the service's session does not have the `MXSessionStateRunning` state.
|
||||
func settings(completion: @escaping (Result<AnalyticsSettings, Error>) -> Void) {
|
||||
// Only use the session if it is running otherwise we could wipe out an existing analytics ID.
|
||||
guard session.state == .running else {
|
||||
MXLog.warning("[AnalyticsService] Aborting attempt to read analytics settings. The session may not be up-to-date.")
|
||||
completion(.failure(AnalyticsServiceError.sessionIsNotRunning))
|
||||
return
|
||||
}
|
||||
|
||||
let settings = AnalyticsSettings(accountData: session.accountData)
|
||||
|
||||
// The id has already be set so we are done here.
|
||||
if settings.id != nil {
|
||||
completion(.success(settings))
|
||||
return
|
||||
}
|
||||
|
||||
// Create a new ID and modify the event dictionary.
|
||||
let id = UUID().uuidString
|
||||
|
||||
var eventDictionary = settings.dictionary
|
||||
eventDictionary[AnalyticsSettings.Constants.idKey] = id
|
||||
|
||||
session.setAccountData(eventDictionary, forType: AnalyticsSettings.eventType) { [weak self] in
|
||||
guard let self = self else {
|
||||
completion(.failure(AnalyticsServiceError.unknown))
|
||||
return
|
||||
}
|
||||
|
||||
MXLog.debug("[AnalyticsService] Successfully updated analytics settings in account data.")
|
||||
let settings = AnalyticsSettings(accountData: self.session.accountData)
|
||||
completion(.success(settings))
|
||||
} failure: { error in
|
||||
MXLog.warning("[AnalyticsService] Failed to update analytics settings.")
|
||||
completion(.failure(error ?? AnalyticsServiceError.unknown))
|
||||
}
|
||||
}
|
||||
}
|
65
Riot/Modules/Analytics/AnalyticsSettings.swift
Normal file
65
Riot/Modules/Analytics/AnalyticsSettings.swift
Normal file
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// Copyright 2021 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
|
||||
|
||||
/// An analytics settings event from the user's account data.
|
||||
struct AnalyticsSettings {
|
||||
static let eventType = "im.vector.analytics"
|
||||
|
||||
enum Constants {
|
||||
static let idKey = "id"
|
||||
static let webOptInKey = "pseudonymousAnalyticsOptIn"
|
||||
}
|
||||
|
||||
/// A randomly generated analytics token for this user.
|
||||
/// This is suggested to be a UUID string.
|
||||
let id: String?
|
||||
|
||||
/// Whether the user has opted in on web or not. This is unused on iOS but necessary
|
||||
/// to store here so that it's value is preserved when updating the account data if we
|
||||
/// generated an ID on iOS.
|
||||
///
|
||||
/// `true` if opted in on web, `false` if opted out on web and `nil` if the web prompt is not yet seen.
|
||||
private let webOptIn: Bool?
|
||||
}
|
||||
|
||||
extension AnalyticsSettings {
|
||||
// Private as AnalyticsSettings should only be created from an MXSession
|
||||
private init(dictionary: Dictionary<AnyHashable, Any>?) {
|
||||
self.id = dictionary?[Constants.idKey] as? String
|
||||
self.webOptIn = dictionary?[Constants.webOptInKey] as? Bool
|
||||
}
|
||||
|
||||
/// A dictionary representation of the settings.
|
||||
var dictionary: Dictionary<AnyHashable, Any> {
|
||||
var dictionary = [AnyHashable: Any]()
|
||||
dictionary[Constants.idKey] = id
|
||||
dictionary[Constants.webOptInKey] = webOptIn
|
||||
|
||||
return dictionary
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public initializer
|
||||
|
||||
extension AnalyticsSettings {
|
||||
/// Create the analytics settings from account data.
|
||||
/// - Parameter accountData: The account data to read the event from.
|
||||
init(accountData: MXAccountData) {
|
||||
self.init(dictionary: accountData.accountData(forEventType: AnalyticsSettings.eventType))
|
||||
}
|
||||
}
|
32
Riot/Modules/Analytics/AnalyticsUIElement.swift
Normal file
32
Riot/Modules/Analytics/AnalyticsUIElement.swift
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// Copyright 2021 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 AnalyticsEvents
|
||||
|
||||
/// A tappable UI element that can be track in Analytics.
|
||||
@objc enum AnalyticsUIElement: Int {
|
||||
case sendMessageButton
|
||||
|
||||
/// The element name reported to the AnalyticsEvent.
|
||||
var elementName: AnalyticsEvent.Click.Name {
|
||||
switch self {
|
||||
// Note: This is a test element that doesn't need to be captured.
|
||||
// It will likely be removed when the AnalyticsEvent.Click is updated.
|
||||
case .sendMessageButton:
|
||||
return .SendMessageButton
|
||||
}
|
||||
}
|
||||
}
|
53
Riot/Modules/Analytics/DecryptionFailure.swift
Normal file
53
Riot/Modules/Analytics/DecryptionFailure.swift
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// Copyright 2021 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 AnalyticsEvents
|
||||
|
||||
/// Failure reasons as defined in https://docs.google.com/document/d/1es7cTCeJEXXfRCTRgZerAM2Wg5ZerHjvlpfTW-gsOfI.
|
||||
@objc enum DecryptionFailureReason: Int {
|
||||
case unspecified
|
||||
case olmKeysNotSent
|
||||
case olmIndexError
|
||||
case unexpected
|
||||
|
||||
var errorName: AnalyticsEvent.Error.Name {
|
||||
switch self {
|
||||
case .unspecified:
|
||||
return .OlmUnspecifiedError
|
||||
case .olmKeysNotSent:
|
||||
return .OlmKeysNotSentError
|
||||
case .olmIndexError:
|
||||
return .OlmIndexError
|
||||
case .unexpected:
|
||||
return .UnknownError
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `DecryptionFailure` represents a decryption failure.
|
||||
@objcMembers class DecryptionFailure: NSObject {
|
||||
/// The id of the event that was unabled to decrypt.
|
||||
let failedEventId: String
|
||||
/// The time the failure has been reported.
|
||||
let ts: TimeInterval = Date().timeIntervalSince1970
|
||||
/// Decryption failure reason.
|
||||
let reason: DecryptionFailureReason
|
||||
|
||||
init(failedEventId: String, reason: DecryptionFailureReason) {
|
||||
self.failedEventId = failedEventId
|
||||
self.reason = reason
|
||||
}
|
||||
}
|
|
@ -16,8 +16,9 @@
|
|||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "DecryptionFailure.h"
|
||||
@class DecryptionFailureTracker;
|
||||
|
||||
@class Analytics;
|
||||
@import MatrixSDK;
|
||||
|
||||
@interface DecryptionFailureTracker : NSObject
|
||||
|
@ -32,7 +33,7 @@
|
|||
/**
|
||||
The delegate object to receive analytics events.
|
||||
*/
|
||||
@property (nonatomic, weak) id<MXAnalyticsDelegate> delegate;
|
||||
@property (nonatomic, weak) Analytics *delegate;
|
||||
|
||||
/**
|
||||
Report an event unable to decrypt.
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
|
||||
#import "DecryptionFailureTracker.h"
|
||||
#import "GeneratedInterface-Swift.h"
|
||||
|
||||
|
||||
// Call `checkFailures` every `CHECK_INTERVAL`
|
||||
|
@ -90,31 +91,32 @@ NSString *const kDecryptionFailureTrackerAnalyticsCategory = @"e2e.failure";
|
|||
return;
|
||||
}
|
||||
|
||||
DecryptionFailure *decryptionFailure = [[DecryptionFailure alloc] init];
|
||||
decryptionFailure.failedEventId = event.eventId;
|
||||
NSString *failedEventId = event.eventId;
|
||||
DecryptionFailureReason reason;
|
||||
|
||||
// Categorise the error
|
||||
switch (event.decryptionError.code)
|
||||
{
|
||||
case MXDecryptingErrorUnknownInboundSessionIdCode:
|
||||
decryptionFailure.reason = DecryptionFailureReason.olmKeysNotSent;
|
||||
reason = DecryptionFailureReasonOlmKeysNotSent;
|
||||
break;
|
||||
|
||||
case MXDecryptingErrorOlmCode:
|
||||
decryptionFailure.reason = DecryptionFailureReason.olmIndexError;
|
||||
reason = DecryptionFailureReasonOlmIndexError;
|
||||
break;
|
||||
|
||||
case MXDecryptingErrorEncryptionNotEnabledCode:
|
||||
case MXDecryptingErrorUnableToDecryptCode:
|
||||
decryptionFailure.reason = DecryptionFailureReason.unexpected;
|
||||
reason = DecryptionFailureReasonUnexpected;
|
||||
break;
|
||||
|
||||
default:
|
||||
decryptionFailure.reason = DecryptionFailureReason.unspecified;
|
||||
reason = DecryptionFailureReasonUnspecified;
|
||||
break;
|
||||
}
|
||||
|
||||
reportedFailures[event.eventId] = decryptionFailure;
|
||||
reportedFailures[event.eventId] = [[DecryptionFailure alloc] initWithFailedEventId:failedEventId
|
||||
reason:reason];
|
||||
}
|
||||
|
||||
- (void)dispatch
|
||||
|
@ -152,17 +154,17 @@ NSString *const kDecryptionFailureTrackerAnalyticsCategory = @"e2e.failure";
|
|||
if (failuresToTrack.count)
|
||||
{
|
||||
// Sort failures by error reason
|
||||
NSMutableDictionary<NSString*, NSNumber*> *failuresCounts = [NSMutableDictionary dictionary];
|
||||
NSMutableDictionary<NSNumber*, NSNumber*> *failuresCounts = [NSMutableDictionary dictionary];
|
||||
for (DecryptionFailure *failure in failuresToTrack)
|
||||
{
|
||||
failuresCounts[failure.reason] = @(failuresCounts[failure.reason].unsignedIntegerValue + 1);
|
||||
failuresCounts[@(failure.reason)] = @(failuresCounts[@(failure.reason)].unsignedIntegerValue + 1);
|
||||
}
|
||||
|
||||
MXLogDebug(@"[DecryptionFailureTracker] trackFailures: %@", failuresCounts);
|
||||
|
||||
for (NSString *reason in failuresCounts)
|
||||
for (NSNumber *reason in failuresCounts)
|
||||
{
|
||||
[_delegate trackValue:failuresCounts[reason] category:kDecryptionFailureTrackerAnalyticsCategory name:reason];
|
||||
[self.delegate trackE2EEError:reason.integerValue count:failuresCounts[reason].integerValue];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright 2020 The Matrix.org Foundation C.I.C
|
||||
// Copyright 2021 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.
|
||||
|
@ -14,20 +14,23 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
import AnalyticsEvents
|
||||
|
||||
|
||||
typedef NSString *const MXKAnalyticsCategory NS_TYPED_EXTENSIBLE_ENUM;
|
||||
|
||||
/**
|
||||
The analytics category for local contacts.
|
||||
*/
|
||||
static MXKAnalyticsCategory const MXKAnalyticsCategoryContacts = @"localContacts";
|
||||
|
||||
|
||||
typedef NSString *const MXKAnalyticsName NS_TYPED_EXTENSIBLE_ENUM;
|
||||
|
||||
/**
|
||||
The analytics value for accept/decline of local contacts access.
|
||||
*/
|
||||
static MXKAnalyticsName const MXKAnalyticsNameContactsAccessGranted = @"accessGranted";
|
||||
extension AnalyticsEvent.JoinedRoom.RoomSize {
|
||||
init?(memberCount: UInt) {
|
||||
switch memberCount {
|
||||
case 2:
|
||||
self = .Two
|
||||
case 3...10:
|
||||
self = .ThreeToTen
|
||||
case 11...100:
|
||||
self = .ElevenToOneHundred
|
||||
case 101...1000:
|
||||
self = .OneHundredAndOneToAThousand
|
||||
case 1001...:
|
||||
self = .MoreThanAThousand
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// Copyright 2021 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 AnalyticsEvents
|
||||
|
||||
extension __MXCallHangupReason {
|
||||
var errorName: AnalyticsEvent.Error.Name {
|
||||
switch self {
|
||||
case .userHangup:
|
||||
return .VoipUserHangup
|
||||
case .inviteTimeout:
|
||||
return .VoipInviteTimeout
|
||||
case .iceFailed:
|
||||
return .VoipIceFailed
|
||||
case .iceTimeout:
|
||||
return .VoipIceTimeout
|
||||
case .userMediaFailed:
|
||||
return .VoipUserMediaFailed
|
||||
case .unknownError:
|
||||
return .UnknownError
|
||||
default:
|
||||
return .UnknownError
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// Copyright 2021 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 AnalyticsEvents
|
||||
|
||||
extension MXTaskProfileName {
|
||||
var analyticsName: AnalyticsEvent.PerformanceTimer.Name? {
|
||||
switch self {
|
||||
case .startupIncrementalSync:
|
||||
return .StartupIncrementalSync
|
||||
case .startupInitialSync:
|
||||
return .StartupInitialSync
|
||||
case .startupLaunchScreen:
|
||||
return .StartupLaunchScreen
|
||||
case .startupStorePreload:
|
||||
return .StartupStorePreload
|
||||
case .startupMountData:
|
||||
return .StartupStoreReady
|
||||
case .initialSyncRequest:
|
||||
return .InitialSyncRequest
|
||||
case .initialSyncParsing:
|
||||
return .InitialSyncParsing
|
||||
case .notificationsOpenEvent:
|
||||
return .NotificationsOpenEvent
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
28
Riot/Modules/Analytics/PHGPostHogConfiguration.swift
Normal file
28
Riot/Modules/Analytics/PHGPostHogConfiguration.swift
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// Copyright 2021 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 PostHog
|
||||
|
||||
extension PHGPostHogConfiguration {
|
||||
static var standard: PHGPostHogConfiguration? {
|
||||
guard let apiKey = BuildSettings.analyticsKey, let host = BuildSettings.analyticsHost else { return nil }
|
||||
|
||||
let configuration = PHGPostHogConfiguration(apiKey: apiKey, host: host)
|
||||
configuration.shouldSendDeviceID = false
|
||||
|
||||
return configuration
|
||||
}
|
||||
}
|
65
Riot/Modules/Analytics/PostHogAnalyticsClient.swift
Normal file
65
Riot/Modules/Analytics/PostHogAnalyticsClient.swift
Normal file
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// Copyright 2021 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 PostHog
|
||||
import AnalyticsEvents
|
||||
|
||||
/// An analytics client that reports events to a PostHog server.
|
||||
class PostHogAnalyticsClient: AnalyticsClientProtocol {
|
||||
/// The PHGPostHog object used to report events.
|
||||
private var postHog: PHGPostHog?
|
||||
|
||||
var isRunning: Bool { postHog?.enabled ?? false }
|
||||
|
||||
func start() {
|
||||
// Only start if analytics have been configured in BuildSettings
|
||||
guard let configuration = PHGPostHogConfiguration.standard else { return }
|
||||
|
||||
if postHog == nil {
|
||||
postHog = PHGPostHog(configuration: configuration)
|
||||
}
|
||||
|
||||
postHog?.enable()
|
||||
}
|
||||
|
||||
func identify(id: String) {
|
||||
postHog?.identify(id)
|
||||
}
|
||||
|
||||
func reset() {
|
||||
postHog?.reset()
|
||||
}
|
||||
|
||||
func stop() {
|
||||
postHog?.disable()
|
||||
|
||||
// As of PostHog 1.4.4, setting the client to nil here doesn't release
|
||||
// it. Keep it around to avoid having multiple instances if the user re-enables
|
||||
}
|
||||
|
||||
func flush() {
|
||||
postHog?.flush()
|
||||
}
|
||||
|
||||
func capture(_ event: AnalyticsEventProtocol) {
|
||||
postHog?.capture(event.eventName, properties: event.properties)
|
||||
}
|
||||
|
||||
func screen(_ event: AnalyticsScreenProtocol) {
|
||||
postHog?.screen(event.screenName.rawValue, properties: event.properties)
|
||||
}
|
||||
|
||||
}
|
|
@ -22,7 +22,6 @@
|
|||
#import "JitsiViewController.h"
|
||||
|
||||
#import "RageShakeManager.h"
|
||||
#import "Analytics.h"
|
||||
|
||||
#import "ThemeService.h"
|
||||
#import "UniversalLink.h"
|
||||
|
|
|
@ -433,16 +433,16 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
_isAppForeground = NO;
|
||||
_handleSelfVerificationRequest = YES;
|
||||
|
||||
// Configure our analytics. It will indeed start if the option is enabled
|
||||
Analytics *analytics = [Analytics sharedInstance];
|
||||
// Configure our analytics. It will start if the option is enabled
|
||||
Analytics *analytics = Analytics.shared;
|
||||
[MXSDKOptions sharedInstance].analyticsDelegate = analytics;
|
||||
[DecryptionFailureTracker sharedInstance].delegate = [Analytics sharedInstance];
|
||||
[DecryptionFailureTracker sharedInstance].delegate = analytics;
|
||||
|
||||
MXBaseProfiler *profiler = [MXBaseProfiler new];
|
||||
profiler.analytics = analytics;
|
||||
[MXSDKOptions sharedInstance].profiler = profiler;
|
||||
|
||||
[analytics start];
|
||||
[analytics startIfEnabled];
|
||||
|
||||
self.localAuthenticationService = [[LocalAuthenticationService alloc] initWithPinCodePreferences:[PinCodePreferences shared]];
|
||||
|
||||
|
@ -587,7 +587,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
|
||||
// Analytics: Force to send the pending actions
|
||||
[[DecryptionFailureTracker sharedInstance] dispatch];
|
||||
[[Analytics sharedInstance] dispatch];
|
||||
[Analytics.shared forceUpload];
|
||||
}
|
||||
|
||||
- (void)applicationWillEnterForeground:(UIApplication *)application
|
||||
|
@ -648,9 +648,13 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
MXLogDebug(@"[AppDelegate] afterAppUnlockedByPin");
|
||||
|
||||
// Check if there is crash log to send
|
||||
if (RiotSettings.shared.enableCrashReport)
|
||||
if (RiotSettings.shared.enableAnalytics)
|
||||
{
|
||||
#if DEBUG
|
||||
// Don't show alerts for crashes during development.
|
||||
#else
|
||||
[self checkExceptionToReport];
|
||||
#endif
|
||||
}
|
||||
|
||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||
|
@ -1880,6 +1884,11 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
|
||||
[self.pushNotificationService checkPushKitPushersInSession:mxSession];
|
||||
}
|
||||
else if (mxSession.state == MXSessionStateRunning)
|
||||
{
|
||||
// Configure analytics from the session if necessary
|
||||
[Analytics.shared useAnalyticsSettingsFrom:mxSession];
|
||||
}
|
||||
else if (mxSession.state == MXSessionStateClosed)
|
||||
{
|
||||
[self removeMatrixSession:mxSession];
|
||||
|
@ -2225,6 +2234,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
// Reset push notification store
|
||||
[self.pushNotificationStore reset];
|
||||
|
||||
// Reset analytics
|
||||
[Analytics.shared reset];
|
||||
|
||||
#ifdef MX_CALL_STACK_ENDPOINT
|
||||
// Erase all created certificates and private keys by MXEndpointCallStack
|
||||
for (MXKAccount *account in MXKAccountManager.sharedManager.accounts)
|
||||
|
@ -2390,8 +2402,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
|
||||
launchAnimationContainerView = launchLoadingView;
|
||||
|
||||
[MXSDKOptions.sharedInstance.profiler startMeasuringTaskWithName:kMXAnalyticsStartupLaunchScreen
|
||||
category:kMXAnalyticsStartupCategory];
|
||||
[MXSDKOptions.sharedInstance.profiler startMeasuringTaskWithName:MXTaskProfileNameStartupLaunchScreen];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2400,7 +2411,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
if (launchAnimationContainerView)
|
||||
{
|
||||
id<MXProfiler> profiler = MXSDKOptions.sharedInstance.profiler;
|
||||
MXTaskProfile *launchTaskProfile = [profiler taskProfileWithName:kMXAnalyticsStartupLaunchScreen category:kMXAnalyticsStartupCategory];
|
||||
MXTaskProfile *launchTaskProfile = [profiler taskProfileWithName:MXTaskProfileNameStartupLaunchScreen];
|
||||
if (launchTaskProfile)
|
||||
{
|
||||
[profiler stopMeasuringTaskWithProfile:launchTaskProfile];
|
||||
|
|
|
@ -309,9 +309,6 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
|
|||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"Authentication"];
|
||||
|
||||
[_keyboardAvoider startAvoiding];
|
||||
}
|
||||
|
@ -330,7 +327,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
|
|||
return;
|
||||
}
|
||||
|
||||
// Verify that the app does not show the authentification screean whereas
|
||||
// Verify that the app does not show the authentication screen whereas
|
||||
// the user has already logged in.
|
||||
// This bug rarely happens (https://github.com/vector-im/riot-ios/issues/1643)
|
||||
// but it invites the user to log in again. They will then lose all their
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#import "MatrixKit.h"
|
||||
|
||||
@class RootTabEmptyView;
|
||||
@class AnalyticsScreenTimer;
|
||||
|
||||
/**
|
||||
Notification to be posted when recents data is ready. Notification object will be the RecentsViewController instance.
|
||||
|
@ -85,16 +86,16 @@ FOUNDATION_EXPORT NSString *const RecentsViewControllerDataReadyNotification;
|
|||
*/
|
||||
@property (nonatomic) CGFloat stickyHeaderHeight;
|
||||
|
||||
/**
|
||||
The analytics instance screen name (Default is "RecentsScreen").
|
||||
*/
|
||||
@property (nonatomic) NSString *screenName;
|
||||
|
||||
/**
|
||||
Empty view to display when there is no item to show on the screen.
|
||||
*/
|
||||
@property (nonatomic, weak) RootTabEmptyView *emptyView;
|
||||
|
||||
/**
|
||||
The screen timer used for analytics if they've been enabled. The default value is nil.
|
||||
*/
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
/**
|
||||
Return the sticky header for the specified section of the table view
|
||||
|
||||
|
|
|
@ -106,9 +106,6 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
|||
self.enableBarTintColorStatusChange = NO;
|
||||
self.rageShakeManager = [RageShakeManager sharedManager];
|
||||
|
||||
// Set default screen name
|
||||
_screenName = @"RecentsScreen";
|
||||
|
||||
// Enable the search bar in the recents table, and remove the search option from the navigation bar.
|
||||
_enableSearchBar = YES;
|
||||
self.enableBarButtonSearch = NO;
|
||||
|
@ -259,9 +256,6 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
|||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:_screenName];
|
||||
|
||||
// Reset back user interactions
|
||||
self.userInteractionEnabled = YES;
|
||||
|
||||
|
@ -329,11 +323,14 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
|
|||
// the selected room (if any) is highlighted.
|
||||
[self refreshCurrentSelectedCell:YES];
|
||||
}
|
||||
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
|
|
|
@ -42,6 +42,8 @@
|
|||
__weak id kThemeServiceDidChangeThemeNotificationObserver;
|
||||
}
|
||||
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GroupsViewController
|
||||
|
@ -74,6 +76,8 @@
|
|||
|
||||
// Set itself as delegate by default.
|
||||
self.delegate = self;
|
||||
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenMyGroups];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
|
@ -203,9 +207,6 @@
|
|||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"Groups"];
|
||||
|
||||
// Deselect the current selected row, it will be restored on viewDidAppear (if any)
|
||||
NSIndexPath *indexPath = [self.groupsTableView indexPathForSelectedRow];
|
||||
if (indexPath)
|
||||
|
@ -258,11 +259,14 @@
|
|||
// the selected group (if any) is highlighted.
|
||||
[self refreshCurrentSelectedCell:YES];
|
||||
}
|
||||
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
#pragma mark - Override MXKGroupListViewController
|
||||
|
|
|
@ -48,6 +48,8 @@
|
|||
|
||||
@property (nonatomic, readonly) DTHTMLAttributedStringBuilderWillFlushCallback longDescriptionSanitizationCallback;
|
||||
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GroupHomeViewController
|
||||
|
@ -95,6 +97,8 @@
|
|||
MXStrongifyAndReturnIfNil(self);
|
||||
[element sanitizeWith:allowedHTMLTags bodyFont:self->_groupLongDescription.font imageHandler:[self groupLongDescriptionImageHandler]];
|
||||
};
|
||||
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenGroup];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
|
@ -205,9 +209,6 @@
|
|||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"GroupDetailsHome"];
|
||||
|
||||
// Release the potential pushed view controller
|
||||
[self releasePushedViewController];
|
||||
|
||||
|
@ -259,6 +260,18 @@
|
|||
[self cancelRegistrationOnGroupChangeNotifications];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
|
|
|
@ -219,9 +219,6 @@
|
|||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"GroupDetailsPeople"];
|
||||
|
||||
// Release the potential pushed view controller
|
||||
[self releasePushedViewController];
|
||||
|
||||
|
|
|
@ -183,9 +183,6 @@
|
|||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"GroupDetailsRooms"];
|
||||
|
||||
// Release the potential pushed view controller
|
||||
[self releasePushedViewController];
|
||||
|
||||
|
|
|
@ -136,9 +136,6 @@
|
|||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"GroupDetails"];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#import "ContactTableViewCell.h"
|
||||
|
||||
@class ContactsTableViewController;
|
||||
@class AnalyticsScreenTimer;
|
||||
|
||||
/**
|
||||
`ContactsTableViewController` delegate.
|
||||
|
@ -85,11 +86,6 @@
|
|||
*/
|
||||
@property (nonatomic) BOOL shouldScrollToTopOnRefresh;
|
||||
|
||||
/**
|
||||
The analytics instance screen name (Default is "ContactsTable").
|
||||
*/
|
||||
@property (nonatomic) NSString *screenName;
|
||||
|
||||
/**
|
||||
Callback used to take into account the change of the user interface theme.
|
||||
*/
|
||||
|
@ -124,5 +120,10 @@
|
|||
*/
|
||||
@property (nonatomic, weak) id<ContactsTableViewControllerDelegate> contactsTableViewControllerDelegate;
|
||||
|
||||
/**
|
||||
The screen timer used for analytics if they've been enabled. The default value is nil.
|
||||
*/
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -76,8 +76,6 @@
|
|||
// Setup `MXKViewControllerHandling` properties
|
||||
self.enableBarTintColorStatusChange = NO;
|
||||
self.rageShakeManager = [RageShakeManager sharedManager];
|
||||
|
||||
_screenName = @"ContactsTable";
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
|
@ -159,9 +157,6 @@
|
|||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:_screenName];
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
|
@ -182,6 +177,12 @@
|
|||
[self updateFooterViewVisibility];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
|
@ -206,6 +207,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
/**
|
||||
|
|
|
@ -232,9 +232,6 @@
|
|||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"ContactDetails"];
|
||||
|
||||
// Hide the bottom border of the navigation bar to display the expander header
|
||||
[self hideNavigationBarBorder:YES];
|
||||
|
|
|
@ -53,6 +53,7 @@ final class EnterNewRoomDetailsViewController: UIViewController {
|
|||
item.isEnabled = false
|
||||
return item
|
||||
}()
|
||||
private var screenTimer = AnalyticsScreenTimer(screen: .createRoom)
|
||||
|
||||
private enum RowType {
|
||||
case `default`
|
||||
|
@ -215,10 +216,17 @@ final class EnterNewRoomDetailsViewController: UIViewController {
|
|||
self.keyboardAvoider?.startAvoiding()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
screenTimer.start()
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
self.keyboardAvoider?.stopAvoiding()
|
||||
|
||||
screenTimer.stop()
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
|
|
|
@ -39,9 +39,9 @@
|
|||
{
|
||||
[super finalizeInit];
|
||||
|
||||
self.screenName = @"Favourites";
|
||||
|
||||
self.enableDragging = YES;
|
||||
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenFavourites];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#import "MatrixKit.h"
|
||||
|
||||
@class AnalyticsScreenTimer;
|
||||
|
||||
/**
|
||||
`HomeFilesSearchViewController` displays the files search in user's rooms under a `HomeViewController` segment.
|
||||
*/
|
||||
|
@ -27,4 +29,9 @@
|
|||
*/
|
||||
@property (nonatomic, readonly) MXEvent *selectedEvent;
|
||||
|
||||
/**
|
||||
The screen timer used for analytics if they've been enabled. The default value is nil.
|
||||
*/
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
|
|
@ -109,9 +109,6 @@
|
|||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[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];
|
||||
}
|
||||
|
@ -124,6 +121,18 @@
|
|||
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionNewRoomNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)refreshSearchResult:(NSNotification *)notif
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#import "MatrixKit.h"
|
||||
|
||||
@class AnalyticsScreenTimer;
|
||||
|
||||
/**
|
||||
`HomeMessagesSearchViewController` displays messages search in user's rooms under a `HomeViewController` segment.
|
||||
*/
|
||||
|
@ -27,4 +29,9 @@
|
|||
*/
|
||||
@property (nonatomic, readonly) MXEvent *selectedEvent;
|
||||
|
||||
/**
|
||||
The screen timer used for analytics if they've been enabled. The default value is nil.
|
||||
*/
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
|
|
@ -115,9 +115,6 @@
|
|||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[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];
|
||||
|
@ -131,6 +128,18 @@
|
|||
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionNewRoomNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)refreshSearchResult:(NSNotification *)notif
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
id kThemeServiceDidChangeThemeNotificationObserver;
|
||||
}
|
||||
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation DirectoryViewController
|
||||
|
@ -46,6 +48,8 @@
|
|||
// Setup `MXKViewControllerHandling` properties
|
||||
self.enableBarTintColorStatusChange = NO;
|
||||
self.rageShakeManager = [RageShakeManager sharedManager];
|
||||
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenRoomDirectory];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
|
@ -106,9 +110,6 @@
|
|||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"Directory"];
|
||||
|
||||
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
|
||||
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
@ -135,6 +136,8 @@
|
|||
// the selected room (if any) is highlighted.
|
||||
[self refreshCurrentSelectedCell:YES];
|
||||
}
|
||||
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
|
@ -148,6 +151,12 @@
|
|||
[super viewWillDisappear:animated];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
- (void)displayWitDataSource:(PublicRoomsDirectoryDataSource *)dataSource2
|
||||
{
|
||||
// Let the data source provide cells
|
||||
|
|
|
@ -79,12 +79,13 @@
|
|||
|
||||
[titles addObject:[VectorL10n searchRooms]];
|
||||
recentsViewController = [RecentsViewController recentListViewController];
|
||||
recentsViewController.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenSearchRooms];
|
||||
recentsViewController.enableSearchBar = NO;
|
||||
recentsViewController.screenName = @"UnifiedSearchRooms";
|
||||
[viewControllers addObject:recentsViewController];
|
||||
|
||||
[titles addObject:[VectorL10n searchMessages]];
|
||||
messagesSearchViewController = [HomeMessagesSearchViewController searchViewController];
|
||||
messagesSearchViewController.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenSearchMessages];
|
||||
[viewControllers addObject:messagesSearchViewController];
|
||||
|
||||
// Add search People tab
|
||||
|
@ -92,11 +93,13 @@
|
|||
peopleSearchViewController = [ContactsTableViewController contactsTableViewController];
|
||||
peopleSearchViewController.contactsTableViewControllerDelegate = self;
|
||||
peopleSearchViewController.disableFindYourContactsFooter = YES;
|
||||
peopleSearchViewController.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenSearchPeople];
|
||||
[viewControllers addObject:peopleSearchViewController];
|
||||
|
||||
// add Files tab
|
||||
[titles addObject:[VectorL10n searchFiles]];
|
||||
filesSearchViewController = [HomeFilesSearchViewController searchViewController];
|
||||
filesSearchViewController.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenSearchFiles];
|
||||
[viewControllers addObject:filesSearchViewController];
|
||||
|
||||
[self initWithTitles:titles viewControllers:viewControllers defaultSelected:0];
|
||||
|
@ -144,9 +147,6 @@
|
|||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"UnifiedSearch"];
|
||||
|
||||
// Let's child display the loading not the home view controller
|
||||
if (self.activityIndicator)
|
||||
{
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
selectedRoomId = nil;
|
||||
selectedCollectionViewContentOffset = -1;
|
||||
|
||||
self.screenName = @"Home";
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenHome];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#import "MXKAppSettings.h"
|
||||
#import <MatrixSDK/MXTools.h>
|
||||
#import "MXKSwiftHeader.h"
|
||||
#import "MXKAnalyticsConstants.h"
|
||||
|
||||
#pragma mark - Constants definitions
|
||||
|
||||
|
@ -884,9 +883,7 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
|
|||
// Request address book access
|
||||
[[CNContactStore new] requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
||||
|
||||
[MXSDKOptions.sharedInstance.analyticsDelegate trackValue:[NSNumber numberWithBool:granted]
|
||||
category:MXKAnalyticsCategoryContacts
|
||||
name:MXKAnalyticsNameContactsAccessGranted];
|
||||
[MXSDKOptions.sharedInstance.analyticsDelegate trackContactsAccessGranted:granted];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
|
|
|
@ -163,9 +163,6 @@
|
|||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"MediaAlbumContent"];
|
||||
|
||||
self.navigationItem.title = _assetsCollection.localizedTitle;
|
||||
|
||||
|
|
|
@ -212,9 +212,6 @@
|
|||
[super viewWillAppear:animated];
|
||||
|
||||
[self userInterfaceThemeDidChange];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"MediaPicker"];
|
||||
|
||||
if (!userAlbumsQueue)
|
||||
{
|
||||
|
|
|
@ -73,5 +73,7 @@ final class InviteFriendsPresenter: NSObject {
|
|||
}
|
||||
|
||||
self.presentingViewController?.present(viewController, animated: animated, completion: nil)
|
||||
|
||||
Analytics.shared.trackScreen(.inviteFriends, duration: nil)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
|
||||
directRoomsSectionNumber = 0;
|
||||
|
||||
self.screenName = @"People";
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenPeople];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
|
|
|
@ -73,14 +73,6 @@
|
|||
return ThemeService.shared.theme.statusBarStyle;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"AttachmentsViewer"];
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
[super destroy];
|
||||
|
|
|
@ -16,6 +16,8 @@ limitations under the License.
|
|||
|
||||
#import "MatrixKit.h"
|
||||
|
||||
@class AnalyticsScreenTimer;
|
||||
|
||||
/**
|
||||
This view controller displays the attachments of a room. Only one matrix session is handled by this view controller.
|
||||
*/
|
||||
|
@ -23,4 +25,9 @@ limitations under the License.
|
|||
|
||||
@property (nonatomic) BOOL showCancelBarButtonItem;
|
||||
|
||||
/**
|
||||
The screen timer used for analytics if they've been enabled. The default value is nil.
|
||||
*/
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
|
|
@ -110,6 +110,14 @@
|
|||
[UIView setAnimationsEnabled:NO];
|
||||
[self roomInputToolbarView:self.inputToolbarView heightDidChanged:0 completion:nil];
|
||||
[UIView setAnimationsEnabled:YES];
|
||||
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
- (void)userInterfaceThemeDidChange
|
||||
|
|
|
@ -104,6 +104,8 @@
|
|||
|
||||
@property(nonatomic, strong) UserVerificationCoordinatorBridgePresenter *userVerificationCoordinatorBridgePresenter;
|
||||
|
||||
@property(nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RoomMemberDetailsViewController
|
||||
|
@ -139,6 +141,8 @@
|
|||
|
||||
// Keep visible the status bar by default.
|
||||
isStatusBarHidden = NO;
|
||||
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenUser];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
|
@ -239,9 +243,6 @@
|
|||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"RoomMemberDetails"];
|
||||
|
||||
[self userInterfaceThemeDidChange];
|
||||
|
||||
// Hide the bottom border of the navigation bar to display the expander header
|
||||
|
@ -264,6 +265,18 @@
|
|||
self.bottomImageView.hidden = YES;
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
|
||||
{
|
||||
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
||||
|
|
|
@ -92,6 +92,11 @@
|
|||
*/
|
||||
@property (nonatomic, weak) id<RoomParticipantsViewControllerDelegate> delegate;
|
||||
|
||||
/**
|
||||
The screen timer used for analytics if they've been enabled. The default value is nil.
|
||||
*/
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
/**
|
||||
Returns the `UINib` object initialized for a `RoomParticipantsViewController`.
|
||||
|
||||
|
|
|
@ -245,9 +245,6 @@
|
|||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"RoomParticipants"];
|
||||
|
||||
// Refresh display
|
||||
[self refreshTableView];
|
||||
|
@ -268,6 +265,8 @@
|
|||
[contactsPickerViewController destroy];
|
||||
contactsPickerViewController = nil;
|
||||
}
|
||||
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
|
@ -284,6 +283,12 @@
|
|||
[self searchBarCancelButtonClicked:_searchBarView];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
- (void)withdrawViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion
|
||||
{
|
||||
// Check whether the current view controller is displayed inside a segmented view controller in order to withdraw the right item
|
||||
|
|
|
@ -39,9 +39,11 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
|
|||
participants.enableMention = true
|
||||
participants.mxRoom = self.room
|
||||
participants.delegate = self
|
||||
participants.screenTimer = AnalyticsScreenTimer(screen: .roomMembers)
|
||||
|
||||
let files = RoomFilesViewController()
|
||||
files.finalizeInit()
|
||||
files.screenTimer = AnalyticsScreenTimer(screen: .roomUploads)
|
||||
MXKRoomDataSource.load(withRoomId: self.room.roomId, andMatrixSession: self.session) { (dataSource) in
|
||||
guard let dataSource = dataSource as? MXKRoomDataSource else { return }
|
||||
dataSource.filterMessagesWithURL = true
|
||||
|
@ -52,6 +54,7 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
|
|||
|
||||
let settings = RoomSettingsViewController()
|
||||
settings.finalizeInit()
|
||||
settings.screenTimer = AnalyticsScreenTimer(screen: .roomSettings)
|
||||
settings.initWith(self.session, andRoomId: self.room.roomId)
|
||||
|
||||
if self.room.isDirect {
|
||||
|
|
|
@ -40,6 +40,7 @@ final class RoomInfoListViewController: UIViewController {
|
|||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
private var isRoomDirect: Bool = false
|
||||
private var screenTimer = AnalyticsScreenTimer(screen: .roomDetails)
|
||||
|
||||
private lazy var closeButton: CloseButton = {
|
||||
let button = CloseButton()
|
||||
|
@ -128,12 +129,22 @@ final class RoomInfoListViewController: UIViewController {
|
|||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
screenTimer.start()
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
mainTableView.vc_relayoutHeaderView()
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
screenTimer.stop()
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
coordinator.animate(alongsideTransition: {_ in
|
||||
self.basicInfoView.updateTrimmingOnTopic()
|
||||
|
|
|
@ -256,6 +256,8 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
|
|||
@property (nonatomic, strong) UserSuggestionCoordinatorBridge *userSuggestionCoordinator;
|
||||
@property (nonatomic, weak) IBOutlet UIView *userSuggestionContainerView;
|
||||
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RoomViewController
|
||||
|
@ -338,6 +340,8 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
|
|||
|
||||
_voiceMessageController = [[VoiceMessageController alloc] initWithThemeService:ThemeService.shared mediaServiceProvider:VoiceMessageMediaServiceProvider.sharedProvider];
|
||||
self.voiceMessageController.delegate = self;
|
||||
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenRoom];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
|
@ -567,9 +571,6 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
|
|||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"ChatRoom"];
|
||||
|
||||
// Refresh the room title view
|
||||
[self refreshRoomTitle];
|
||||
|
||||
|
@ -610,8 +611,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
|
|||
[self.roomDataSource reload];
|
||||
[LegacyAppDelegate theDelegate].lastNavigatedRoomIdFromPush = nil;
|
||||
|
||||
notificationTaskProfile = [MXSDKOptions.sharedInstance.profiler startMeasuringTaskWithName:AnalyticsNoficationsTimeToDisplayContent
|
||||
category:AnalyticsNoficationsCategory];
|
||||
notificationTaskProfile = [MXSDKOptions.sharedInstance.profiler startMeasuringTaskWithName:MXTaskProfileNameNotificationsOpenEvent];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -707,6 +707,9 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
|
|||
hasJitsiCall = NO;
|
||||
[self reloadBubblesTable:YES];
|
||||
}
|
||||
|
||||
// Screen tracking
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
|
@ -742,6 +745,8 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
|
|||
hasJitsiCall = YES;
|
||||
[self reloadBubblesTable:YES];
|
||||
}
|
||||
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
|
|
|
@ -109,9 +109,6 @@
|
|||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"RoomFilesSearch"];
|
||||
|
||||
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
|
||||
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
|
|
@ -111,9 +111,6 @@
|
|||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"RoomMessagesSearch"];
|
||||
|
||||
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
|
||||
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
MXKSearchDataSource *filesSearchDataSource;
|
||||
}
|
||||
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RoomSearchViewController
|
||||
|
@ -49,6 +51,8 @@
|
|||
[super finalizeInit];
|
||||
|
||||
// The navigation bar tint color and the rageShake Manager are handled by super (see SegmentedViewController).
|
||||
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenRoomSearch];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
|
@ -106,9 +110,6 @@
|
|||
[self.activityIndicator stopAnimating];
|
||||
self.activityIndicator = nil;
|
||||
}
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"RoomsSearch"];
|
||||
|
||||
// Enable the search field by default at the screen opening
|
||||
if (self.searchBarHidden)
|
||||
|
@ -124,6 +125,8 @@
|
|||
// Refresh the search results.
|
||||
// Note: We wait for 'viewDidAppear' call to consider the actual view size during this update.
|
||||
[self updateSearch];
|
||||
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
|
@ -138,6 +141,12 @@
|
|||
[super viewWillDisappear:animated];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
{
|
||||
return ThemeService.shared.theme.statusBarStyle;
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#import "MediaPickerViewController.h"
|
||||
#import "TableViewCellWithCheckBoxes.h"
|
||||
|
||||
@class AnalyticsScreenTimer;
|
||||
|
||||
/**
|
||||
List the settings fields. Used to preselect/edit a field
|
||||
*/
|
||||
|
@ -52,5 +54,10 @@ typedef enum : NSUInteger {
|
|||
*/
|
||||
@property (nonatomic) RoomSettingsViewControllerField selectedRoomSettingsField;
|
||||
|
||||
/**
|
||||
The screen timer used for analytics if they've been enabled. The default value is nil.
|
||||
*/
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -311,9 +311,6 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
|
|||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"RoomSettings"];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdateRules:) name:kMXNotificationCenterDidUpdateRules object:nil];
|
||||
|
||||
|
@ -334,6 +331,8 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
|
|||
{
|
||||
self.selectedRoomSettingsField = _selectedRoomSettingsField;
|
||||
}
|
||||
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
|
@ -351,6 +350,12 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
|
|||
}
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
// Those methods are called when the viewcontroller is added or removed from a container view controller.
|
||||
- (void)willMoveToParentViewController:(nullable UIViewController *)parent
|
||||
{
|
||||
|
|
|
@ -38,6 +38,9 @@
|
|||
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
|
||||
id kThemeServiceDidChangeThemeNotificationObserver;
|
||||
}
|
||||
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation DirectoryServerPickerViewController
|
||||
|
@ -49,6 +52,8 @@
|
|||
// Setup `MXKViewControllerHandling` properties
|
||||
self.enableBarTintColorStatusChange = NO;
|
||||
self.rageShakeManager = [RageShakeManager sharedManager];
|
||||
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenSwitchDirectory];
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
|
@ -145,9 +150,6 @@
|
|||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"DirectoryServerPicker"];
|
||||
|
||||
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
|
||||
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
|
@ -158,6 +160,12 @@
|
|||
[dataSource loadData];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
if (kAppDelegateDidTapStatusBarNotificationObserver)
|
||||
|
@ -169,6 +177,12 @@
|
|||
[super viewWillDisappear:animated];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
- (void)displayWithDataSource:(MXKDirectoryServersDataSource*)theDataSource
|
||||
onComplete:(void (^)(id<MXKDirectoryServerCellDataStoring> cellData))onComplete;
|
||||
{
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
{
|
||||
[super finalizeInit];
|
||||
|
||||
self.screenName = @"Rooms";
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenRooms];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
|
|
|
@ -63,6 +63,7 @@ final class ShowDirectoryCoordinator: ShowDirectoryCoordinatorType {
|
|||
|
||||
private func createDirectoryServerPickerViewController() -> DirectoryServerPickerViewController {
|
||||
let controller = DirectoryServerPickerViewController()
|
||||
controller.finalizeInit()
|
||||
let dataSource: MXKDirectoryServersDataSource = MXKDirectoryServersDataSource(matrixSession: session)
|
||||
dataSource.finalizeInitialization()
|
||||
dataSource.roomDirectoryServers = BuildSettings.publicRoomsDirectoryServers
|
||||
|
|
|
@ -68,6 +68,8 @@ final class ShowDirectoryViewController: UIViewController {
|
|||
}()
|
||||
|
||||
private var sections: [ShowDirectorySection] = []
|
||||
|
||||
private let screenTimer = AnalyticsScreenTimer(screen: .roomDirectory)
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
|
@ -104,10 +106,17 @@ final class ShowDirectoryViewController: UIViewController {
|
|||
self.keyboardAvoider?.startAvoiding()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
screenTimer.start()
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
self.keyboardAvoider?.stopAvoiding()
|
||||
|
||||
screenTimer.stop()
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
|
|
|
@ -107,7 +107,7 @@ extension ServiceTermsModalCoordinator: ServiceTermsModalScreenCoordinatorDelega
|
|||
|
||||
func serviceTermsModalScreenCoordinatorDidAccept(_ coordinator: ServiceTermsModalScreenCoordinatorType) {
|
||||
if serviceTerms.serviceType == MXServiceTypeIdentityService {
|
||||
Analytics.sharedInstance().trackValue(1, category: MXKAnalyticsCategory.contacts.rawValue, name: AnalyticsContactsIdentityServerAccepted)
|
||||
Analytics.shared.trackIdentityServerAccepted(true)
|
||||
}
|
||||
|
||||
self.delegate?.serviceTermsModalCoordinatorDidAccept(self)
|
||||
|
@ -119,7 +119,7 @@ extension ServiceTermsModalCoordinator: ServiceTermsModalScreenCoordinatorDelega
|
|||
|
||||
func serviceTermsModalScreenCoordinatorDidDecline(_ coordinator: ServiceTermsModalScreenCoordinatorType) {
|
||||
if serviceTerms.serviceType == MXServiceTypeIdentityService {
|
||||
Analytics.sharedInstance().trackValue(1, category: MXKAnalyticsCategory.contacts.rawValue, name: AnalyticsContactsIdentityServerAccepted)
|
||||
Analytics.shared.trackIdentityServerAccepted(false)
|
||||
disableIdentityServer()
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ extension ServiceTermsModalCoordinator: ServiceTermsModalScreenCoordinatorDelega
|
|||
extension ServiceTermsModalCoordinator: UIAdaptivePresentationControllerDelegate {
|
||||
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
|
||||
if serviceTerms.serviceType == MXServiceTypeIdentityService {
|
||||
Analytics.sharedInstance().trackValue(0, category: MXKAnalyticsCategory.contacts.rawValue, name: AnalyticsContactsIdentityServerAccepted)
|
||||
Analytics.shared.trackIdentityServerAccepted(false)
|
||||
}
|
||||
|
||||
self.delegate?.serviceTermsModalCoordinatorDidDismissInteractively(self)
|
||||
|
|
|
@ -47,6 +47,8 @@ static CGFloat const kTextFontSize = 15.0;
|
|||
|
||||
@property (weak, nonatomic) id <NSObject> themeDidChangeNotificationObserver;
|
||||
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - Implementation
|
||||
|
@ -62,6 +64,12 @@ static CGFloat const kTextFontSize = 15.0;
|
|||
return viewController;
|
||||
}
|
||||
|
||||
- (void)finalizeInit
|
||||
{
|
||||
[super finalizeInit];
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenDeactivateAccount];
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
id<NSObject> notificationObserver = self.themeDidChangeNotificationObserver;
|
||||
|
@ -95,9 +103,12 @@ static CGFloat const kTextFontSize = 15.0;
|
|||
[super viewWillAppear:animated];
|
||||
|
||||
[self userInterfaceThemeDidChange];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"DeactivateAccount"];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
|
@ -107,6 +118,12 @@ static CGFloat const kTextFontSize = 15.0;
|
|||
[self.deactivateAcccountButton.layer setCornerRadius:kButtonCornerRadius];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
{
|
||||
return ThemeService.shared.theme.statusBarStyle;
|
||||
|
|
|
@ -106,14 +106,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"CountryPicker"];
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
{
|
||||
cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
|
|
|
@ -95,14 +95,6 @@
|
|||
return ThemeService.shared.theme.statusBarStyle;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"CountryPicker"];
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
[super destroy];
|
||||
|
|
|
@ -161,9 +161,6 @@ enum {
|
|||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"ManageSession"];
|
||||
|
||||
// Release the potential pushed view controller
|
||||
[self releasePushedViewController];
|
||||
|
||||
|
|
|
@ -119,6 +119,8 @@ TableViewSectionsDelegate>
|
|||
@property (nonatomic, strong) SetPinCoordinatorBridgePresenter *setPinCoordinatorBridgePresenter;
|
||||
@property (nonatomic, strong) CrossSigningSetupCoordinatorBridgePresenter *crossSigningSetupCoordinatorBridgePresenter;
|
||||
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SecurityViewController
|
||||
|
@ -142,6 +144,8 @@ TableViewSectionsDelegate>
|
|||
// Setup `MXKViewControllerHandling` properties
|
||||
self.enableBarTintColorStatusChange = NO;
|
||||
self.rageShakeManager = [RageShakeManager sharedManager];
|
||||
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenSettingsSecurity];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
|
@ -250,9 +254,6 @@ TableViewSectionsDelegate>
|
|||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"Security"];
|
||||
|
||||
// Release the potential pushed view controller
|
||||
[self releasePushedViewController];
|
||||
|
||||
|
@ -268,6 +269,12 @@ TableViewSectionsDelegate>
|
|||
[self loadCrossSigning];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
@ -279,6 +286,12 @@ TableViewSectionsDelegate>
|
|||
}
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
#pragma mark - Internal methods
|
||||
|
||||
- (void)updateSections
|
||||
|
|
|
@ -283,6 +283,8 @@ TableViewSectionsDelegate>
|
|||
@property (nonatomic) BOOL isPreparingIdentityService;
|
||||
@property (nonatomic, strong) ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter;
|
||||
|
||||
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SettingsViewController
|
||||
|
@ -315,6 +317,8 @@ TableViewSectionsDelegate>
|
|||
isSavingInProgress = NO;
|
||||
isResetPwdInProgress = NO;
|
||||
is3PIDBindingInProgress = NO;
|
||||
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenSettings];
|
||||
}
|
||||
|
||||
- (void)updateSections
|
||||
|
@ -776,9 +780,6 @@ TableViewSectionsDelegate>
|
|||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"Settings"];
|
||||
|
||||
// Refresh display
|
||||
[self refreshSettings];
|
||||
|
@ -808,6 +809,8 @@ TableViewSectionsDelegate>
|
|||
[self releasePushedViewController];
|
||||
|
||||
[self.settingsDiscoveryTableViewSection reload];
|
||||
|
||||
[self.screenTimer start];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
|
@ -851,6 +854,12 @@ TableViewSectionsDelegate>
|
|||
}
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[self.screenTimer stop];
|
||||
}
|
||||
|
||||
#pragma mark - Internal methods
|
||||
|
||||
- (void)pushViewController:(UIViewController*)viewController
|
||||
|
@ -2251,11 +2260,11 @@ TableViewSectionsDelegate>
|
|||
{
|
||||
MXKTableViewCellWithLabelAndSwitch* sendCrashReportCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
|
||||
|
||||
sendCrashReportCell.mxkLabel.text = [VectorL10n settingsSendCrashReport];
|
||||
sendCrashReportCell.mxkSwitch.on = RiotSettings.shared.enableCrashReport;
|
||||
sendCrashReportCell.mxkLabel.text = VectorL10n.settingsAnalyticsAndCrashData;
|
||||
sendCrashReportCell.mxkSwitch.on = RiotSettings.shared.enableAnalytics;
|
||||
sendCrashReportCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
|
||||
sendCrashReportCell.mxkSwitch.enabled = YES;
|
||||
[sendCrashReportCell.mxkSwitch addTarget:self action:@selector(toggleSendCrashReport:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[sendCrashReportCell.mxkSwitch addTarget:self action:@selector(toggleAnalytics:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
cell = sendCrashReportCell;
|
||||
}
|
||||
|
@ -3115,27 +3124,20 @@ TableViewSectionsDelegate>
|
|||
[[MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mainSession] reset];
|
||||
}
|
||||
|
||||
- (void)toggleSendCrashReport:(id)sender
|
||||
- (void)toggleAnalytics:(UISwitch *)sender
|
||||
{
|
||||
BOOL enable = RiotSettings.shared.enableCrashReport;
|
||||
if (enable)
|
||||
if (sender.isOn)
|
||||
{
|
||||
MXLogDebug(@"[SettingsViewController] disable automatic crash report and analytics sending");
|
||||
|
||||
RiotSettings.shared.enableCrashReport = NO;
|
||||
|
||||
[[Analytics sharedInstance] stop];
|
||||
|
||||
// Remove potential crash file.
|
||||
[MXLogger deleteCrashLog];
|
||||
MXLogDebug(@"[SettingsViewController] enable automatic crash report and analytics sending");
|
||||
[Analytics.shared optInWith:self.mainSession];
|
||||
}
|
||||
else
|
||||
{
|
||||
MXLogDebug(@"[SettingsViewController] enable automatic crash report and analytics sending");
|
||||
MXLogDebug(@"[SettingsViewController] disable automatic crash report and analytics sending");
|
||||
[Analytics.shared optOut];
|
||||
|
||||
RiotSettings.shared.enableCrashReport = YES;
|
||||
|
||||
[[Analytics sharedInstance] start];
|
||||
// Remove potential crash file.
|
||||
[MXLogger deleteCrashLog];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ final class SideMenuViewController: UIViewController {
|
|||
private var keyboardAvoider: KeyboardAvoider?
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
private var screenTimer = AnalyticsScreenTimer(screen: .sidebar)
|
||||
|
||||
private var sideMenuActionViews: [SideMenuActionView] = []
|
||||
private weak var sideMenuVersionView: SideMenuVersionView?
|
||||
|
@ -86,8 +87,14 @@ final class SideMenuViewController: UIViewController {
|
|||
navigationController?.setNavigationBarHidden(true, animated: animated)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
screenTimer.start()
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
screenTimer.stop()
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
|
|
|
@ -73,8 +73,6 @@
|
|||
{
|
||||
[super finalizeInit];
|
||||
|
||||
self.screenName = @"StartChat";
|
||||
|
||||
_isAddParticipantSearchBarEditing = NO;
|
||||
|
||||
// Prepare room participants
|
||||
|
@ -82,6 +80,8 @@
|
|||
|
||||
// Assign itself as delegate
|
||||
self.contactsTableViewControllerDelegate = self;
|
||||
|
||||
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenStartChat];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
|
|
|
@ -193,5 +193,6 @@ typedef NS_ENUM(NSUInteger, MasterTabBarIndex) {
|
|||
- (void)masterTabBarController:(MasterTabBarController *)masterTabBarController didSelectRoomPreviewWithParameters:(RoomPreviewNavigationParameters*)roomPreviewNavigationParameters completion:(void (^)(void))completion;
|
||||
- (void)masterTabBarController:(MasterTabBarController *)masterTabBarController didSelectContact:(MXKContact*)contact withPresentationParameters:(ScreenPresentationParameters*)presentationParameters;
|
||||
- (void)masterTabBarController:(MasterTabBarController *)masterTabBarController didSelectGroup:(MXGroup*)group inMatrixSession:(MXSession*)matrixSession presentationParameters:(ScreenPresentationParameters*)presentationParameters;
|
||||
- (void)masterTabBarController:(MasterTabBarController *)masterTabBarController shouldPresentAnalyticsPromptForMatrixSession:(MXSession*)matrixSession;
|
||||
|
||||
@end
|
||||
|
|
|
@ -70,6 +70,11 @@
|
|||
|
||||
@property(nonatomic) BOOL reviewSessionAlertHasBeenDisplayed;
|
||||
|
||||
/**
|
||||
A flag to indicate that the analytics prompt should be shown during `-addMatrixSession:`.
|
||||
*/
|
||||
@property(nonatomic) BOOL presentAnalyticsPromptOnAddSession;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MasterTabBarController
|
||||
|
@ -196,11 +201,18 @@
|
|||
|
||||
if (!authIsShown)
|
||||
{
|
||||
// Check whether the user has been already prompted to send crash reports.
|
||||
// (Check whether 'enableCrashReport' flag has been set once)
|
||||
if (!RiotSettings.shared.isEnableCrashReportHasBeenSetOnce)
|
||||
// Check whether the user should be prompted to send analytics.
|
||||
if (Analytics.shared.shouldShowAnalyticsPrompt)
|
||||
{
|
||||
[self promptUserBeforeUsingAnalytics];
|
||||
MXSession *mxSession = self.mxSessions.firstObject;
|
||||
if (mxSession)
|
||||
{
|
||||
[self promptUserBeforeUsingAnalyticsForSession:mxSession];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.presentAnalyticsPromptOnAddSession = YES;
|
||||
}
|
||||
}
|
||||
|
||||
[self refreshTabBarBadges];
|
||||
|
@ -405,6 +417,12 @@
|
|||
return;
|
||||
}
|
||||
|
||||
if (self.presentAnalyticsPromptOnAddSession)
|
||||
{
|
||||
self.presentAnalyticsPromptOnAddSession = NO;
|
||||
[self promptUserBeforeUsingAnalyticsForSession:mxSession];
|
||||
}
|
||||
|
||||
// Check whether the controller's view is loaded into memory.
|
||||
if (self.homeViewController)
|
||||
{
|
||||
|
@ -921,50 +939,14 @@
|
|||
|
||||
#pragma mark -
|
||||
|
||||
- (void)promptUserBeforeUsingAnalytics
|
||||
- (void)promptUserBeforeUsingAnalyticsForSession:(MXSession *)mxSession
|
||||
{
|
||||
MXLogDebug(@"[MasterTabBarController]: Invite the user to send crash reports");
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
|
||||
NSString *appDisplayName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"];
|
||||
|
||||
currentAlert = [UIAlertController alertControllerWithTitle:[VectorL10n googleAnalyticsUsePrompt:appDisplayName] message:nil preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n no]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
RiotSettings.shared.enableCrashReport = NO;
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
self->currentAlert = nil;
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n yes]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
RiotSettings.shared.enableCrashReport = YES;
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
self->currentAlert = nil;
|
||||
}
|
||||
|
||||
[[Analytics sharedInstance] start];
|
||||
|
||||
}]];
|
||||
|
||||
[currentAlert mxk_setAccessibilityIdentifier: @"HomeVCUseAnalyticsAlert"];
|
||||
[self presentViewController:currentAlert animated:YES completion:nil];
|
||||
// Analytics aren't collected on iOS 12 & 13.
|
||||
if (@available(iOS 14.0, *))
|
||||
{
|
||||
MXLogDebug(@"[MasterTabBarController]: Invite the user to send analytics");
|
||||
[self.masterTabBarDelegate masterTabBarController:self shouldPresentAnalyticsPromptForMatrixSession:mxSession];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Review session
|
||||
|
|
|
@ -487,6 +487,19 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
|
|||
self.splitViewMasterPresentableDelegate?.splitViewMasterPresentableWantsToResetDetail(self)
|
||||
}
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
private func presentAnalyticsPrompt(with session: MXSession) {
|
||||
let parameters = AnalyticsPromptCoordinatorParameters(session: session, navigationRouter: navigationRouter)
|
||||
let coordinator = AnalyticsPromptCoordinator(parameters: parameters)
|
||||
coordinator.completion = { [weak self, weak coordinator] in
|
||||
guard let self = self, let coordinator = coordinator else { return }
|
||||
self.remove(childCoordinator: coordinator)
|
||||
}
|
||||
|
||||
coordinator.start()
|
||||
add(childCoordinator: coordinator)
|
||||
}
|
||||
|
||||
// MARK: UserSessions management
|
||||
|
||||
private func registerUserSessionsServiceNotifications() {
|
||||
|
@ -578,6 +591,12 @@ extension TabBarCoordinator: MasterTabBarControllerDelegate {
|
|||
|
||||
self.masterTabBarController.navigationItem.leftBarButtonItem = sideMenuBarButtonItem
|
||||
}
|
||||
|
||||
func masterTabBarController(_ masterTabBarController: MasterTabBarController!, shouldPresentAnalyticsPromptForMatrixSession matrixSession: MXSession!) {
|
||||
if #available(iOS 14.0, *) {
|
||||
presentAnalyticsPrompt(with: matrixSession)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - RoomCoordinatorDelegate
|
||||
|
|
|
@ -120,9 +120,6 @@
|
|||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking
|
||||
[[Analytics sharedInstance] trackScreen:@"UnknowDevices"];
|
||||
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#import "RoomInputToolbarView.h"
|
||||
#import "NSArray+Element.h"
|
||||
#import "ShareItemSender.h"
|
||||
#import "HTMLFormatter.h"
|
||||
|
||||
// MatrixKit common imports, shared with all targets
|
||||
#import "MatrixKit-Bridging-Header.h"
|
||||
|
@ -62,4 +63,3 @@
|
|||
#import "MXKRoomDataSourceManager.h"
|
||||
#import "MXRoom+Sync.h"
|
||||
#import "UIAlertController+MatrixKit.h"
|
||||
#import "MXKAnalyticsConstants.h"
|
||||
|
|
38
Riot/Utils/HTMLFormatter.h
Normal file
38
Riot/Utils/HTMLFormatter.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// Copyright 2021 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 <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface HTMLFormatter : NSObject
|
||||
|
||||
/** Builds an attributed string from a string containing html.
|
||||
@param htmlString The html string to use.
|
||||
@param allowedTags The html tags that should be allowed.
|
||||
@param fontSize The default font size to use.
|
||||
|
||||
Note: It is recommended to include "p" and "body" tags in
|
||||
`allowedTags` as these are often added when parsing.
|
||||
*/
|
||||
- (NSAttributedString * _Nonnull)formatHTML:(NSString * _Nonnull)htmlString
|
||||
withAllowedTags:(NSArray<NSString *> * _Nonnull)allowedTags
|
||||
fontSize:(CGFloat)fontSize;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
61
Riot/Utils/HTMLFormatter.m
Normal file
61
Riot/Utils/HTMLFormatter.m
Normal file
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// Copyright 2021 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 "HTMLFormatter.h"
|
||||
#import "GeneratedInterface-Swift.h"
|
||||
|
||||
@implementation HTMLFormatter
|
||||
|
||||
- (NSAttributedString *)formatHTML:(NSString *)htmlString withAllowedTags:(NSArray<NSString *> *)allowedTags fontSize:(CGFloat)fontSize
|
||||
{
|
||||
// TODO: This method should be more general purpose and usable from MXKEventFormatter and GroupHomeViewController
|
||||
// FIXME: The implementation is currently in Objective-C as there is a crash in the callback when implemented in Swift
|
||||
UIFont *font = [UIFont systemFontOfSize:fontSize];
|
||||
|
||||
// Do some sanitisation before finalizing the string
|
||||
DTHTMLAttributedStringBuilderWillFlushCallback sanitizeCallback = ^(DTHTMLElement *element) {
|
||||
[element sanitizeWith:allowedTags bodyFont:font imageHandler:nil];
|
||||
};
|
||||
|
||||
NSDictionary *options = @{
|
||||
DTUseiOS6Attributes: @(YES), // Enable it to be able to display the attributed string in a UITextView
|
||||
DTDefaultFontFamily: font.familyName,
|
||||
DTDefaultFontName: font.fontName,
|
||||
DTDefaultFontSize: @(font.pointSize),
|
||||
DTDefaultLinkDecoration: @(NO),
|
||||
DTWillFlushBlockCallBack: sanitizeCallback
|
||||
};
|
||||
|
||||
// Do not use the default HTML renderer of NSAttributedString because this method
|
||||
// runs on the UI thread which we want to avoid because renderHTMLString is called
|
||||
// most of the time from a background thread.
|
||||
// Use DTCoreText HTML renderer instead.
|
||||
// Using DTCoreText, which renders static string, helps to avoid code injection attacks
|
||||
// that could happen with the default HTML renderer of NSAttributedString which is a
|
||||
// webview.
|
||||
NSAttributedString *string = [[NSAttributedString alloc] initWithHTMLData:[htmlString dataUsingEncoding:NSUTF8StringEncoding] options:options documentAttributes:NULL];
|
||||
|
||||
// Apply additional treatments
|
||||
string = [MXKTools removeDTCoreTextArtifacts:string];
|
||||
|
||||
if (!string) {
|
||||
return [[NSAttributedString alloc] initWithString:htmlString];
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
@end
|
35
Riot/Utils/HTMLFormatter.swift
Normal file
35
Riot/Utils/HTMLFormatter.swift
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Copyright 2021 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
|
||||
|
||||
extension HTMLFormatter {
|
||||
/// Builds an attributed string by replacing a `%@` placeholder with the supplied link text and URL.
|
||||
/// - Parameters:
|
||||
/// - string: The string to be formatted.
|
||||
/// - link: The link text to be inserted.
|
||||
/// - url: The URL to be linked to.
|
||||
/// - Returns: An attributed string.
|
||||
func format(_ string: String, with link: String, using url: URL) -> NSAttributedString {
|
||||
let baseString = NSMutableAttributedString(string: string)
|
||||
let attributedLink = NSAttributedString(string: link, attributes: [.link: url])
|
||||
|
||||
let linkRange = (baseString.string as NSString).range(of: "%@")
|
||||
baseString.replaceCharacters(in: linkRange, with: attributedLink)
|
||||
|
||||
return baseString
|
||||
}
|
||||
}
|
128
RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptModels.swift
Normal file
128
RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptModels.swift
Normal file
|
@ -0,0 +1,128 @@
|
|||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh AnalyticsPrompt AnalyticsPrompt
|
||||
//
|
||||
// Copyright 2021 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
|
||||
|
||||
// The state is never modified so this is unnecessary.
|
||||
enum AnalyticsPromptStateAction { }
|
||||
|
||||
enum AnalyticsPromptViewAction {
|
||||
/// Enable analytics.
|
||||
case enable
|
||||
/// Disable analytics.
|
||||
case disable
|
||||
/// Open the service terms link.
|
||||
case openTermsURL
|
||||
}
|
||||
|
||||
enum AnalyticsPromptViewModelResult {
|
||||
/// Enable analytics.
|
||||
case enable
|
||||
/// Disable analytics.
|
||||
case disable
|
||||
}
|
||||
|
||||
struct AnalyticsPromptViewState: BindableState {
|
||||
/// The type of prompt to display.
|
||||
let promptType: AnalyticsPromptType
|
||||
/// Localized attributed strings created in the coordinator.
|
||||
let strings: AnalyticsPromptStringsProtocol
|
||||
}
|
||||
|
||||
/// A collection of strings for the UI that need to be created in
|
||||
/// the coordinator or mocked in the RiotSwiftUI target.
|
||||
protocol AnalyticsPromptStringsProtocol {
|
||||
var appDisplayName: String { get }
|
||||
|
||||
var point1: NSAttributedString { get }
|
||||
var point2: NSAttributedString { get }
|
||||
|
||||
var termsNewUser: NSAttributedString { get }
|
||||
var termsUpgrade: NSAttributedString { get }
|
||||
}
|
||||
|
||||
enum AnalyticsPromptType {
|
||||
case newUser(termsString: NSAttributedString)
|
||||
case upgrade(termsString: NSAttributedString)
|
||||
}
|
||||
|
||||
extension AnalyticsPromptType {
|
||||
/// The main description string that should be displayed.
|
||||
var message: String {
|
||||
switch self {
|
||||
case .newUser:
|
||||
return VectorL10n.analyticsPromptMessageNewUser
|
||||
case .upgrade:
|
||||
return VectorL10n.analyticsPromptMessageUpgrade
|
||||
}
|
||||
}
|
||||
|
||||
/// The terms string that should be displayed.
|
||||
var termsStrings: NSAttributedString {
|
||||
switch self {
|
||||
case .newUser(let termsString), .upgrade(let termsString):
|
||||
return termsString
|
||||
}
|
||||
}
|
||||
|
||||
/// The title for the enable button.
|
||||
var enableButtonTitle: String {
|
||||
switch self {
|
||||
case .newUser:
|
||||
return VectorL10n.enable
|
||||
case .upgrade:
|
||||
return VectorL10n.analyticsPromptYes
|
||||
}
|
||||
}
|
||||
|
||||
/// The title for the disable button.
|
||||
var disableButtonTitle: String {
|
||||
switch self {
|
||||
case .newUser:
|
||||
return VectorL10n.analyticsPromptNotNow
|
||||
case .upgrade:
|
||||
return VectorL10n.analyticsPromptStop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AnalyticsPromptType: CaseIterable {
|
||||
static var allCases: [AnalyticsPromptType] {
|
||||
let strings = MockAnalyticsPromptStrings()
|
||||
return [
|
||||
.newUser(termsString: strings.termsNewUser),
|
||||
.upgrade(termsString: strings.termsUpgrade)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extension AnalyticsPromptType: Identifiable {
|
||||
var id: String {
|
||||
switch self {
|
||||
case .newUser:
|
||||
return "newUser"
|
||||
case .upgrade:
|
||||
return "upgrade"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For the RiotSwiftUI target presentation.
|
||||
extension AnalyticsPromptType: CustomStringConvertible {
|
||||
var description: String { id }
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh AnalyticsPrompt AnalyticsPrompt
|
||||
//
|
||||
// Copyright 2021 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 SwiftUI
|
||||
import Combine
|
||||
|
||||
@available(iOS 14, *)
|
||||
typealias AnalyticsPromptViewModelType = StateStoreViewModel<AnalyticsPromptViewState,
|
||||
AnalyticsPromptStateAction,
|
||||
AnalyticsPromptViewAction>
|
||||
@available(iOS 14, *)
|
||||
class AnalyticsPromptViewModel: AnalyticsPromptViewModelType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
let termsURL: URL
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var completion: ((AnalyticsPromptViewModelResult) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
/// Initialize a view model with the specified prompt type and app display name.
|
||||
init(promptType: AnalyticsPromptType, strings: AnalyticsPromptStringsProtocol, termsURL: URL) {
|
||||
self.termsURL = termsURL
|
||||
super.init(initialViewState: AnalyticsPromptViewState(promptType: promptType, strings: strings))
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
override func process(viewAction: AnalyticsPromptViewAction) {
|
||||
switch viewAction {
|
||||
case .enable:
|
||||
enable()
|
||||
case .disable:
|
||||
disable()
|
||||
case .openTermsURL:
|
||||
openTermsURL()
|
||||
}
|
||||
}
|
||||
|
||||
override class func reducer(state: inout AnalyticsPromptViewState, action: AnalyticsPromptStateAction) {
|
||||
// There is no mutable state to reduce :)
|
||||
}
|
||||
|
||||
/// Enable analytics. The call to the Analytics class is made in the completion.
|
||||
private func enable() {
|
||||
completion?(.enable)
|
||||
}
|
||||
|
||||
/// Disable analytics. The call to the Analytics class is made in the completion.
|
||||
private func disable() {
|
||||
completion?(.disable)
|
||||
}
|
||||
|
||||
/// Open the service terms link.
|
||||
private func openTermsURL() {
|
||||
UIApplication.shared.open(termsURL)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh AnalyticsPrompt AnalyticsPrompt
|
||||
/*
|
||||
Copyright 2021 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 SwiftUI
|
||||
|
||||
struct AnalyticsPromptCoordinatorParameters {
|
||||
/// The session to use if analytics are enabled.
|
||||
let session: MXSession
|
||||
/// The navigation router used to display the prompt.
|
||||
let navigationRouter: NavigationRouterType
|
||||
}
|
||||
|
||||
final class AnalyticsPromptCoordinator: Coordinator {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let parameters: AnalyticsPromptCoordinatorParameters
|
||||
private let analyticsPromptHostingController: UIViewController
|
||||
private var _analyticsPromptViewModel: Any? = nil
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
fileprivate var analyticsPromptViewModel: AnalyticsPromptViewModel {
|
||||
return _analyticsPromptViewModel as! AnalyticsPromptViewModel
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
var completion: (() -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
init(parameters: AnalyticsPromptCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
|
||||
let strings = AnalyticsPromptStrings()
|
||||
let promptType: AnalyticsPromptType
|
||||
|
||||
if Analytics.shared.promptShouldDisplayUpgradeMessage {
|
||||
promptType = .upgrade(termsString: strings.termsUpgrade)
|
||||
} else {
|
||||
promptType = .newUser(termsString: strings.termsNewUser)
|
||||
}
|
||||
|
||||
let viewModel = AnalyticsPromptViewModel(promptType: promptType, strings: strings, termsURL: BuildSettings.analyticsTermsURL)
|
||||
|
||||
let view = AnalyticsPrompt(viewModel: viewModel.context)
|
||||
_analyticsPromptViewModel = viewModel
|
||||
analyticsPromptHostingController = VectorHostingController(rootView: view)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
guard #available(iOS 14.0, *) else {
|
||||
MXLog.debug("[AnalyticsPromptCoordinator] start: Invalid iOS version, returning.")
|
||||
return
|
||||
}
|
||||
|
||||
MXLog.debug("[AnalyticsPromptCoordinator] did start.")
|
||||
|
||||
parameters.navigationRouter.present(toPresentable(), animated: true)
|
||||
|
||||
analyticsPromptViewModel.completion = { [weak self] result in
|
||||
MXLog.debug("[AnalyticsPromptCoordinator] AnalyticsPromptViewModel did complete with result: \(result).")
|
||||
|
||||
guard let self = self else { return }
|
||||
|
||||
switch result {
|
||||
case .enable:
|
||||
Analytics.shared.optIn(with: self.parameters.session)
|
||||
self.parameters.navigationRouter.dismissModule(animated: true, completion: nil)
|
||||
self.completion?()
|
||||
case .disable:
|
||||
Analytics.shared.optOut()
|
||||
self.parameters.navigationRouter.dismissModule(animated: true, completion: nil)
|
||||
self.completion?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.analyticsPromptHostingController
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// Copyright 2021 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
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
struct AnalyticsPromptStrings: AnalyticsPromptStringsProtocol {
|
||||
let appDisplayName = AppInfo.current.displayName
|
||||
|
||||
let point1 = HTMLFormatter().formatHTML(VectorL10n.analyticsPromptPoint1, withAllowedTags: ["b", "p"], fontSize: UIFont.systemFontSize)
|
||||
let point2 = HTMLFormatter().formatHTML(VectorL10n.analyticsPromptPoint2, withAllowedTags: ["b", "p"], fontSize: UIFont.systemFontSize)
|
||||
|
||||
let termsNewUser = HTMLFormatter().format(VectorL10n.analyticsPromptTermsNewUser("%@"),
|
||||
with: VectorL10n.analyticsPromptTermsLinkNewUser,
|
||||
using: BuildSettings.analyticsTermsURL)
|
||||
let termsUpgrade = HTMLFormatter().format(VectorL10n.analyticsPromptTermsUpgrade("%@"),
|
||||
with: VectorL10n.analyticsPromptTermsLinkUpgrade,
|
||||
using: BuildSettings.analyticsTermsURL)
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh AnalyticsPrompt AnalyticsPrompt
|
||||
//
|
||||
// Copyright 2021 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
|
||||
import SwiftUI
|
||||
|
||||
/// Using an enum for the screen allows you define the different state cases with
|
||||
/// the relevant associated data for each case.
|
||||
@available(iOS 14.0, *)
|
||||
enum MockAnalyticsPromptScreenState: MockScreenState, CaseIterable {
|
||||
/// The type of prompt to display.
|
||||
case promptType(AnalyticsPromptType)
|
||||
|
||||
/// The associated screen
|
||||
var screenType: Any.Type {
|
||||
AnalyticsPrompt.self
|
||||
}
|
||||
|
||||
/// A list of screen state definitions
|
||||
static var allCases: [MockAnalyticsPromptScreenState] {
|
||||
AnalyticsPromptType.allCases.map { MockAnalyticsPromptScreenState.promptType($0) }
|
||||
}
|
||||
|
||||
/// Generate the view struct for the screen state.
|
||||
var screenView: ([Any], AnyView) {
|
||||
let promptType: AnalyticsPromptType
|
||||
switch self {
|
||||
case .promptType(let analyticsPromptType):
|
||||
promptType = analyticsPromptType
|
||||
}
|
||||
let viewModel = AnalyticsPromptViewModel(promptType: promptType,
|
||||
strings: MockAnalyticsPromptStrings(),
|
||||
termsURL: URL(string: "https://element.io/cookie-policy")!)
|
||||
|
||||
return (
|
||||
[promptType, viewModel],
|
||||
AnyView(AnalyticsPrompt(viewModel: viewModel.context)
|
||||
.addDependency(MockAvatarService.example))
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// Copyright 2021 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 UIKit
|
||||
|
||||
struct MockAnalyticsPromptStrings: AnalyticsPromptStringsProtocol {
|
||||
var appDisplayName = "Element"
|
||||
|
||||
let point1: NSAttributedString
|
||||
let point2: NSAttributedString
|
||||
|
||||
let termsNewUser: NSAttributedString
|
||||
let termsUpgrade: NSAttributedString
|
||||
|
||||
let shortString = NSAttributedString(string: "This is a short string.")
|
||||
let longString = NSAttributedString(string: "This is a very long string that will be used to test the layout over multiple lines of text to ensure everything is correct.")
|
||||
|
||||
init() {
|
||||
let point1 = NSMutableAttributedString(string: "We ")
|
||||
point1.append(NSAttributedString(string: "don't", attributes: [.font: UIFont.boldSystemFont(ofSize: UIFont.systemFontSize)]))
|
||||
point1.append(NSAttributedString(string: " record or profile any account data"))
|
||||
self.point1 = point1
|
||||
|
||||
let point2 = NSMutableAttributedString(string: "We ")
|
||||
point2.append(NSAttributedString(string: "don't", attributes: [.font: UIFont.boldSystemFont(ofSize: UIFont.systemFontSize)]))
|
||||
point2.append(NSAttributedString(string: " share information with third parties"))
|
||||
self.point2 = point2
|
||||
|
||||
let termsNewUser = NSMutableAttributedString(string: "You can read all our terms ")
|
||||
termsNewUser.append(NSAttributedString(string: "here", attributes: [.link: URL(string: "https://element.io/cookie-policy")!]))
|
||||
termsNewUser.append(NSAttributedString(string: "."))
|
||||
self.termsNewUser = termsNewUser
|
||||
|
||||
let termsUpgrade = NSMutableAttributedString(string: "Read all our terms ")
|
||||
termsUpgrade.append(NSAttributedString(string: "here", attributes: [.link: URL(string: "https://element.io/cookie-policy")!]))
|
||||
termsUpgrade.append(NSAttributedString(string: ". Is that OK?"))
|
||||
self.termsUpgrade = termsUpgrade
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue