diff --git a/OONIProbeUITests/OONIProbeUITests.swift b/OONIProbeUITests/OONIProbeUITests.swift index 4485a9cbe..5ea6c1dc3 100644 --- a/OONIProbeUITests/OONIProbeUITests.swift +++ b/OONIProbeUITests/OONIProbeUITests.swift @@ -149,11 +149,11 @@ class OONIProbeUITests: XCTestCase { XCTAssertFalse(tablesQuery.cells.element(boundBy: 0).images["not_uploaded_icon"].exists) settingsButton.tap() - tablesQuery.cells.element(boundBy: 3).tap() - let resultSwitch = tablesQuery.cells.element(boundBy: 0).children(matching: .switch).element + tablesQuery.cells.element(boundBy: 2).tap() // Privacy Settings + let resultSwitch = tablesQuery.cells.element(boundBy: 0).children(matching: .switch).element // Automatically publish results if ((resultSwitch.value as? String) == "1"){ - resultSwitch.tap() + resultSwitch.tap() // turn off automatic publishing of results } //From https://stackoverflow.com/questions/44222966/from-an-xcuitest-how-can-i-check-the-on-off-state-of-a-uiswitch diff --git a/ooniprobe.xcodeproj/project.pbxproj b/ooniprobe.xcodeproj/project.pbxproj index bce38e71d..b8cc63cc6 100644 --- a/ooniprobe.xcodeproj/project.pbxproj +++ b/ooniprobe.xcodeproj/project.pbxproj @@ -12,10 +12,12 @@ 17E7EDC021BFEE0C001961C7 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17E7EDBF21BFEE0C001961C7 /* SnapshotHelper.swift */; }; 526C702A25C99AB200C7A164 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 526C702925C99AB100C7A164 /* Colors.xcassets */; }; 58E18F9EBF4FAD4EFCE020F3 /* Pods_ooniprobe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB32DF3D6FC174AA1AF76009 /* Pods_ooniprobe.framework */; }; + 793587D32B8E081600038F88 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793587D22B8E081600038F88 /* Utils.swift */; }; 793587BA2B852EDD00038F88 /* OoniRunViewUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793587B92B852EDD00038F88 /* OoniRunViewUITests.swift */; }; 7940AA8B28117E9000C0EB5D /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7940AA8A28117E9000C0EB5D /* ShareViewController.swift */; }; 7940AA8E28117E9000C0EB5D /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7940AA8C28117E9000C0EB5D /* MainInterface.storyboard */; }; 7940AA9228117E9000C0EB5D /* share.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 7940AA8828117E9000C0EB5D /* share.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 7945903D2C21BFB1008116BF /* OONIDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7945903C2C21BFB1008116BF /* OONIDescriptor.swift */; }; 79780FCF27E9F18E002A38B1 /* Languages.plist in Resources */ = {isa = PBXBuildFile; fileRef = 79780FCE27E9F18E002A38B1 /* Languages.plist */; }; 7AED19812A6EC9A2003B265A /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AED19802A6EC9A2003B265A /* libresolv.tbd */; }; 7AED19832A6EC9C7003B265A /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AED19822A6EC9C7003B265A /* libresolv.tbd */; }; @@ -219,12 +221,15 @@ 17E7EDBF21BFEE0C001961C7 /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SnapshotHelper.swift; path = fastlane/SnapshotHelper.swift; sourceTree = SOURCE_ROOT; }; 526C702925C99AB100C7A164 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; 588219FACC9F793A15BDEA33 /* Pods_OONIProbeUnitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OONIProbeUnitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 793587D22B8E081600038F88 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; 793587B92B852EDD00038F88 /* OoniRunViewUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OoniRunViewUITests.swift; sourceTree = ""; }; 7940AA8828117E9000C0EB5D /* share.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = share.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 7940AA8A28117E9000C0EB5D /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; 7940AA8D28117E9000C0EB5D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; 7940AA8F28117E9000C0EB5D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 7940AA972811840900C0EB5D /* share.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = share.entitlements; sourceTree = ""; }; + 7945903B2C21BFB1008116BF /* ooniprobe-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ooniprobe-Bridging-Header.h"; sourceTree = ""; }; + 7945903C2C21BFB1008116BF /* OONIDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OONIDescriptor.swift; sourceTree = ""; }; 79780FCE27E9F18E002A38B1 /* Languages.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Languages.plist; sourceTree = ""; }; 79AA093C2A86E44400C23E27 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = ""; }; 79AA093D2A86E47600C23E27 /* my */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = my; path = my.lproj/Localizable.strings; sourceTree = ""; }; @@ -654,6 +659,8 @@ 526C702925C99AB100C7A164 /* Colors.xcassets */, D4A2F5EB1A6C3244001B8460 /* LaunchScreen.xib */, D4A2F5DC1A6C3244001B8460 /* Supporting Files */, + 793587D22B8E081600038F88 /* Utils.swift */, + 7945903B2C21BFB1008116BF /* ooniprobe-Bridging-Header.h */, ); path = ooniprobe; sourceTree = ""; @@ -720,6 +727,7 @@ ED25280F1CEA34DA0073A29B /* Model */ = { isa = PBXGroup; children = ( + 7945903C2C21BFB1008116BF /* OONIDescriptor.swift */, ED90A91A2198DE5000204B46 /* Database */, ED90A9232198DE5000204B46 /* Settings */, EDB252A120FA46DA00B4EDE4 /* JsonResult */, @@ -1276,6 +1284,7 @@ D4A2F5D81A6C3244001B8460 = { CreatedOnToolsVersion = 6.1.1; DevelopmentTeam = MADPSAYN6T; + LastSwiftMigration = 1540; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.BackgroundModes = { @@ -1539,6 +1548,7 @@ buildActionMask = 2147483647; files = ( EDB3D64F26204C9300724ECF /* Experimental.m in Sources */, + 7945903D2C21BFB1008116BF /* OONIDescriptor.swift in Sources */, ED4DF7B52607970C00521C5B /* Signal.m in Sources */, ED365217223A40480093180B /* FailedTestDetailsViewController.m in Sources */, ED1CC80220159D970041089A /* TestRunningViewController.m in Sources */, @@ -1621,6 +1631,7 @@ EDE47C41241FC6BC0013A1CB /* NetworkSession.m in Sources */, EDF4ED26248A9A64001A5406 /* Advanced.m in Sources */, EDA17AFB208A30E100D46D0F /* MiddleBoxesDetailsViewController.m in Sources */, + 793587D32B8E081600038F88 /* Utils.swift in Sources */, ED1CC7FF20158D630041089A /* ConfigureButton.m in Sources */, ED3932C41E3114740064CD11 /* AboutViewController.m in Sources */, ED2E066F21D4867B00E9B9EE /* HttpInvalidRequestLine.m in Sources */, @@ -1745,6 +1756,7 @@ 17E7EDBC21BFEDD1001961C7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -1777,6 +1789,7 @@ 17E7EDBD21BFEDD1001961C7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -2012,6 +2025,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ooniprobe/ooniprobe.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -2045,6 +2059,9 @@ PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = ""; STRIP_INSTALLED_PRODUCT = NO; + SWIFT_OBJC_BRIDGING_HEADER = "ooniprobe/ooniprobe-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; USER_HEADER_SEARCH_PATHS = ""; VALID_ARCHS = "arm64 x86_64"; @@ -2060,6 +2077,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ooniprobe/ooniprobe.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -2095,6 +2113,8 @@ PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = ""; STRIP_INSTALLED_PRODUCT = NO; + SWIFT_OBJC_BRIDGING_HEADER = "ooniprobe/ooniprobe-Bridging-Header.h"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; USER_HEADER_SEARCH_PATHS = ""; VALID_ARCHS = arm64; @@ -2105,6 +2125,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = AC948DA31A71FB931BBB1BB3 /* Pods-OONIProbeUnitTests.debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -2136,6 +2157,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = CAFF8CF760913F0065D0615D /* Pods-OONIProbeUnitTests.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; diff --git a/ooniprobe/Colors.xcassets/color_background.colorset/Contents.json b/ooniprobe/Colors.xcassets/color_background.colorset/Contents.json new file mode 100644 index 000000000..7bed699c7 --- /dev/null +++ b/ooniprobe/Colors.xcassets/color_background.colorset/Contents.json @@ -0,0 +1,56 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF5", + "green" : "0xF3", + "red" : "0xF1" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF5", + "green" : "0xF3", + "red" : "0xF1" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x40", + "green" : "0x3A", + "red" : "0x34" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ooniprobe/Images.xcassets/ooni_probe_logo.imageset/ooni_probe_logo.pdf b/ooniprobe/Images.xcassets/ooni_probe_logo.imageset/ooni_probe_logo.pdf index cb12903bd..879b3c658 100644 Binary files a/ooniprobe/Images.xcassets/ooni_probe_logo.imageset/ooni_probe_logo.pdf and b/ooniprobe/Images.xcassets/ooni_probe_logo.imageset/ooni_probe_logo.pdf differ diff --git a/ooniprobe/Images.xcassets/timer.imageset/Contents.json b/ooniprobe/Images.xcassets/timer.imageset/Contents.json new file mode 100644 index 000000000..33b93aa4e --- /dev/null +++ b/ooniprobe/Images.xcassets/timer.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "timer.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ooniprobe/Images.xcassets/timer.imageset/timer.png b/ooniprobe/Images.xcassets/timer.imageset/timer.png new file mode 100644 index 000000000..3277fb324 Binary files /dev/null and b/ooniprobe/Images.xcassets/timer.imageset/timer.png differ diff --git a/ooniprobe/OONIDescriptor.swift b/ooniprobe/OONIDescriptor.swift new file mode 100644 index 000000000..a96065cf5 --- /dev/null +++ b/ooniprobe/OONIDescriptor.swift @@ -0,0 +1,244 @@ +import Foundation +import UIKit + +/// `NettestName` is an enumeration that defines a set of named constants for different nettests. +/// Each case in the enumeration represents a different nettest. +enum NettestName: String { + case webConnectivity = "web_connectivity" // Represents the Web Connectivity test + case whatsapp = "whatsapp" // Represents the WhatsApp test + case telegram = "telegram" // Represents the Telegram test + case facebookMessenger = "facebook_messenger" // Represents the Facebook Messenger test + case signal = "signal" // Represents the Signal test + case psiphon = "psiphon" // Represents the Psiphon test + case tor = "tor" // Represents the Tor test + case ndt = "ndt" // Represents the NDT test + case dash = "dash" // Represents the DASH test + case httpHeaderFieldManipulation = "http_header_field_manipulation" // Represents the HTTP Header Field Manipulation test + case httpInvalidRequestLine = "http_invalid_request_line" // Represents the HTTP Invalid Request Line test + case stunReachability = "stunreachability" // Represents the STUN Reachability test + case dnsCheck = "dnscheck" // Represents the DNS Check test + case riseupVPN = "riseupvpn" // Represents the RiseupVPN test + case echCheck = "echcheck" // Represents the ECH Check test + case torsf = "torsf" // Represents the TorSF test + case vanillaTor = "vanilla_tor" // Represents the Vanilla Tor test +} + +/// `Nettest` is a class that represents a nettest. +/// It contains information about the nettest such as the name, inputs, etc. +/// The class also provides a method to get the test object for the nettest. +@objc(Nettest) +public class Nettest: NSObject { + + // MARK: Initializers + init(name: String, inputs: [String]?) { + self.name = name + self.inputs = inputs + } + + // MARK: Properties + @objc dynamic var name: String + @objc dynamic var inputs: [String]? + + // MARK: Methods + @objc public func getTest() -> AbstractTest { + switch NettestName(rawValue: self.name) { + case .webConnectivity: + return WebConnectivity() + case .whatsapp: + return Whatsapp() + case .telegram: + return Telegram() + case .facebookMessenger: + return FacebookMessenger() + case .signal: + return Signal() + case .psiphon: + return Psiphon() + case .tor: + return Tor() + case .ndt: + return NdtTest() + case .dash: + return Dash() + case .httpHeaderFieldManipulation: + return HttpHeaderFieldManipulation() + case .httpInvalidRequestLine: + return HttpInvalidRequestLine() + case .riseupVPN: + return RiseupVPN() + default: + return Experimental(name: name) + } + } +} + +/// `OONIDescriptor` is a class that represents an OONI descriptor. +/// It contains information about the descriptor such as the name, title, icon, color, etc. +/// It also contains information about the nettests that are part of the descriptor. +/// The class also provides a method to get the OONI descriptors for the OONI dashboard. +/// The class also provides a method to get the test suite for the current descriptor. +@objc(OONIDescriptor) +public class OONIDescriptor: NSObject { + + // MARK: Initializers + init(name: String, + title: String, + shortDescription: String, + longDescription: String, + icon: String, + color: UIColor, + animation: String?, + dataUsage: String, + nettest: [Nettest], + longRunningTests: [Nettest]?) { + self.name = name + self.title = title + self.shortDescription = shortDescription + self.longDescription = longDescription + self.icon = icon + self.color = color + self.animation = animation + self.dataUsage = dataUsage + self.nettest = nettest + self.longRunningTests = longRunningTests + } + + // MARK: Properties + @objc dynamic var name: String + @objc dynamic var title: String + @objc dynamic var shortDescription: String + @objc dynamic var longDescription: String + @objc dynamic var icon: String + @objc dynamic var color: UIColor + @objc dynamic var animation: String? + @objc dynamic var dataUsage: String + @objc dynamic var nettest: [Nettest] + @objc dynamic var longRunningTests: [Nettest]? + + // MARK: Methods + + // Get the OONI descriptors for the OONI dashboard. + @objc public static func getOONIDescriptors() -> [OONIDescriptor] { + + [ + OONIDescriptor( + name: "websites", + title: NSLocalizedString("Test.Websites.Fullname", comment: ""), + shortDescription: NSLocalizedString("Dashboard.Websites.Card.Description", comment: ""), + longDescription: NSLocalizedString("Dashboard.Websites.Overview.Paragraph", comment: ""), + icon: "websites", + color: UIColor(named: "color_indigo6")!, + animation: "websites", + dataUsage: "~ 8 MB", + nettest: [ + Nettest(name: NettestName.webConnectivity.rawValue, inputs: []) + ], + longRunningTests: [] + ), + + OONIDescriptor( + name: "instant_messaging", + title: NSLocalizedString("Test.InstantMessaging.Fullname", comment: ""), + shortDescription: NSLocalizedString("Dashboard.InstantMessaging.Card.Description", comment: ""), + longDescription: NSLocalizedString("Dashboard.InstantMessaging.Overview.Paragraph", comment: ""), + icon: "instant_messaging", + color: UIColor(named: "color_indigo5")!, + animation: "instant_messaging", + dataUsage: "< 1 MB", + nettest: [ + Nettest(name: NettestName.whatsapp.rawValue, inputs: []), + Nettest(name: NettestName.telegram.rawValue, inputs: []), + Nettest(name: NettestName.facebookMessenger.rawValue, inputs: []), + Nettest(name: NettestName.signal.rawValue, inputs: []) + ], + longRunningTests: [] + ), + + OONIDescriptor( + name: "circumvention", + title: NSLocalizedString("Test.Circumvention.Fullname", comment: ""), + shortDescription: NSLocalizedString("Dashboard.Circumvention.Card.Description", comment: ""), + longDescription: NSLocalizedString("Dashboard.Circumvention.Overview.Paragraph", comment: ""), + icon: "circumvention", + color: UIColor(named: "color_indigo2")!, + animation: "circumvention", + dataUsage: "< 1 MB", + nettest: [ + Nettest(name: NettestName.psiphon.rawValue, inputs: []), + Nettest(name: NettestName.tor.rawValue, inputs: []) + ], + longRunningTests: [] + ), + + OONIDescriptor( + name: "performance", + title: NSLocalizedString("Test.Performance.Fullname", comment: ""), + shortDescription: NSLocalizedString("Dashboard.Performance.Card.Description", comment: ""), + longDescription: NSLocalizedString("Dashboard.Performance.Overview.Paragraph", comment: ""), + icon: "performance", + color: UIColor(named: "color_indigo4")!, + animation: "performance", + dataUsage: "5 - 200 MB", + nettest: [ + Nettest(name: NettestName.ndt.rawValue, inputs: []), + Nettest(name: NettestName.dash.rawValue, inputs: []), + Nettest(name: NettestName.httpHeaderFieldManipulation.rawValue, inputs: []), + Nettest(name: NettestName.httpInvalidRequestLine.rawValue, inputs: []) + ], + longRunningTests: [] + ), + + OONIDescriptor( + name: "experimental", + title: NSLocalizedString("Test.Experimental.Fullname", comment: ""), + shortDescription: NSLocalizedString("Dashboard.Experimental.Card.Description", comment: ""), + longDescription: NSLocalizedString("Dashboard.Experimental.Overview.Paragraph", comment: ""), + icon: "experimental", + color: UIColor(named: "color_indigo1")!, + animation: "experimental", + dataUsage: NSLocalizedString("TestResults.NotAvailable", comment: ""), + nettest: [ + Nettest(name: "stunreachability", inputs: []), + Nettest(name: "dnscheck", inputs: []), + Nettest(name: "riseupvpn", inputs: []), + Nettest(name: "echcheck", inputs: []), + ], + longRunningTests: [ + Nettest(name: "torsf", inputs: []), + Nettest(name: "vanilla_tor", inputs: []), + ] + ) + ] + } + + + /// Returns the test suite for the current descriptor. + /// + /// @return DynamicTestSuite representing the test suite for the current descriptor. + @objc public func getTestSuites() -> Any { + DynamicTestSuite(descriptor: self) + } +} + + +/// This class is used to create [AbstractTest] dynamically for all instances where a Test Suite is required. +/// It is used to create a test suite from a Descriptor. +/// It acts as a bridge between the Descriptor format and the [AbstractSuite]. +@objc(DynamicTestSuite) +class DynamicTestSuite: AbstractSuite { + // MARK: Initializers + @objc init(descriptor: OONIDescriptor) { + self.descriptor = descriptor + super.init() + self.name = descriptor.name + self.dataUsage = descriptor.dataUsage + let tests = NSMutableArray() + tests.addObjects(from: descriptor.nettest.map { nettest in + nettest.getTest() + }) + self.testList = tests + } + + // MARK: Properties + @objc dynamic var descriptor: OONIDescriptor +} diff --git a/ooniprobe/Storyboards/Dashboard.storyboard b/ooniprobe/Storyboards/Dashboard.storyboard index 2e3bfc852..741d4dc64 100644 --- a/ooniprobe/Storyboards/Dashboard.storyboard +++ b/ooniprobe/Storyboards/Dashboard.storyboard @@ -28,10 +28,10 @@ - + - + @@ -57,7 +57,7 @@ - - + @@ -511,7 +511,7 @@ measurement-kit: x.y.z - + @@ -522,7 +522,7 @@ measurement-kit: x.y.z - + diff --git a/ooniprobe/Utility/SettingsUtility.m b/ooniprobe/Utility/SettingsUtility.m index b24257cdb..bab78e54f 100644 --- a/ooniprobe/Utility/SettingsUtility.m +++ b/ooniprobe/Utility/SettingsUtility.m @@ -6,7 +6,7 @@ @implementation SettingsUtility + (NSArray*)getSettingsCategories{ - return @[@"notifications", @"automated_testing", @"test_options", @"privacy", @"advanced", @"ooni_backend_proxy", @"send_email", @"about_ooni"]; + return @[@"notifications", @"test_options", @"privacy", @"advanced", @"ooni_backend_proxy", @"send_email", @"about_ooni"]; } + (NSArray*)getSettingsForCategory:(NSString*)categoryName{ @@ -15,10 +15,11 @@ + (NSArray*)getSettingsForCategory:(NSString*)categoryName{ return @[@"notifications_enabled"]; } if ([categoryName isEqualToString:@"automated_testing"]) { - if ([SettingsUtility isAutomatedTestEnabled]) + if ([SettingsUtility isAutomatedTestEnabled]) { return @[@"automated_testing_enabled", @"automated_testing_wifionly", @"automated_testing_charging"]; - else + } else { return @[@"automated_testing_enabled"]; + } } else if ([categoryName isEqualToString:@"privacy"]) { return @[@"upload_results", @"send_crash"]; @@ -28,25 +29,25 @@ + (NSArray*)getSettingsForCategory:(NSString*)categoryName{ return @[@"AppleLanguages", @"debug_logs", @"storage_usage" , @"warn_vpn_in_use"]; } else if ([categoryName isEqualToString:@"test_options"]) { - return [TestUtility getTestOptionTypes]; - } - else if ([[TestUtility getTestOptionTypes] containsObject:categoryName]) - return [SettingsUtility getSettingsForTest:categoryName :YES]; - else + return @[@"automated_testing_enabled", @"automated_testing_wifionly", @"automated_testing_charging", @"website_categories", @"max_runtime_enabled", @"max_runtime"]; + } else { return nil; + } } + (NSString*)getTypeForSetting:(NSString*)setting{ - if ([setting isEqualToString:@"experimental"] || [setting isEqualToString:@"long_running_tests_in_foreground"]) + if ([setting isEqualToString:@"experimental"] || [setting isEqualToString:@"long_running_tests_in_foreground"]) { return @"bool"; - else if ([setting isEqualToString:@"website_categories"] || [[TestUtility getTestOptionTypes] containsObject:setting]) + } else if ([setting isEqualToString:@"website_categories"]) { return @"segue"; - else if ([setting isEqualToString:@"monthly_mobile_allowance"] || - [setting isEqualToString:@"monthly_wifi_allowance"] || - [setting isEqualToString:@"max_runtime"]) + } else if ([setting isEqualToString:@"monthly_mobile_allowance"] || + [setting isEqualToString:@"monthly_wifi_allowance"] || + [setting isEqualToString:@"max_runtime"]) { return @"int"; - if ([setting isEqualToString:@"storage_usage"] || [setting isEqualToString:@"AppleLanguages"]) + } + if ([setting isEqualToString:@"storage_usage"] || [setting isEqualToString:@"AppleLanguages"]) { return @"button"; + } return @"bool"; } diff --git a/ooniprobe/Utils.swift b/ooniprobe/Utils.swift new file mode 100644 index 000000000..370663b78 --- /dev/null +++ b/ooniprobe/Utils.swift @@ -0,0 +1,43 @@ +import Foundation +import UIKit + +extension UIViewController { + + @objc(resizeImage:newWidth:newHeight:) + func resizeImage(image: UIImage, newWidth: CGFloat, newHeight: CGFloat) -> UIImage? { + let scale = newWidth / image.size.width + + let newSize = CGSize(width: newWidth, height: image.size.height * scale) + UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0) + image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight)) + let newImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return newImage + } + + @objc(tintedImageColors:startColor:endColor:) + func tintedImageColors(image: UIImage, startColor: CGColor, endColor: CGColor?) -> UIImage { + UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale); + guard let context = UIGraphicsGetCurrentContext() else { + return UIImage() + } + context.translateBy(x: 0, y: image.size.height) + context.scaleBy(x: 1, y: -1) + + context.setBlendMode(.normal) + let rect = CGRect.init(x: 0, y: 0, width: image.size.width, height: image.size.height) + + // Create gradient + let colors = [startColor, endColor ?? startColor] as CFArray + let space = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: space, colors: colors, locations: nil) + + // Apply gradient + context.clip(to: rect, mask: image.cgImage!) + context.drawLinearGradient(gradient!, start: CGPoint(x: 0, y: 0), end: CGPoint(x: 0, y: image.size.height), options: .drawsAfterEndLocation) + let gradientImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return gradientImage! + } +} diff --git a/ooniprobe/View/DashboardTableViewController.m b/ooniprobe/View/DashboardTableViewController.m index cca72aa1e..759286078 100644 --- a/ooniprobe/View/DashboardTableViewController.m +++ b/ooniprobe/View/DashboardTableViewController.m @@ -3,6 +3,7 @@ #import "ThirdPartyServices.h" #import "Suite.h" #import "RunningTest.h" +#import "ooniprobe-Swift.h" @interface DashboardTableViewController () @@ -83,19 +84,19 @@ -(void)showHideVPNToast{ -(void)setShadowRunButton{ self.runButton.layer.cornerRadius = 20; self.runButton.layer.masksToBounds = YES; + [self.runButton setImage:[self resizeImage:[UIImage imageNamed:@"timer"] newWidth:23 newHeight:25] forState:UIControlStateNormal]; + self.runButton.imageView.contentMode = UIViewContentModeScaleAspectFit; + self.runButton.imageEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0); + self.runButton.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft; [self.runButton setTitle:[NSString stringWithFormat:@"%@", NSLocalizedString(@"Dashboard.Card.Run", nil)] forState:UIControlStateNormal]; self.viewForShadowRunButton.backgroundColor = [UIColor clearColor]; - - self.viewForShadowRunButton.layer.shadowRadius = 5.0f; + self.viewForShadowRunButton.layer.shadowColor = [[UIColor colorNamed:@"color_black"] colorWithAlphaComponent:0.8f].CGColor; - self.viewForShadowRunButton.layer.shadowOffset = CGSizeMake(0.0f, 2.0f); - self.viewForShadowRunButton.layer.shadowOpacity = 0.6f; - self.viewForShadowRunButton.layer.masksToBounds = NO; } -(void)loadTests{ - items = [TestUtility getTestObjects]; + items = [[OONIDescriptor getOONIDescriptors] mutableCopy]; [self.tableView reloadData]; } @@ -127,17 +128,29 @@ - (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSIntege - (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { DashboardTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; - AbstractSuite *test = [items objectAtIndex:indexPath.row]; + OONIDescriptor *test = [items objectAtIndex:indexPath.row]; if (cell == nil) { cell = [[DashboardTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"]; } - [cell setTestSuite:test]; + [cell setDescriptor:test]; return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { } +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { + if (section == 0) { + return @"OONI TESTS"; + } + return @""; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { + return 20; +} + + -(IBAction)run:(id)sender{ if ([TestUtility checkConnectivity:self] && [TestUtility checkTestRunning:self]){ @@ -152,7 +165,12 @@ -(IBAction)run:(id)sender{ -(IBAction)runAll{ if ([TestUtility checkConnectivity:self] && [TestUtility checkTestRunning:self]){ - [[RunningTest currentTest] setAndRun:[NSMutableArray arrayWithArray:items] inView: self]; + // convert items to DynamicTestSuite + NSMutableArray *testSuites = [[NSMutableArray alloc] init]; + for (OONIDescriptor *decriptor in self.items){ + [testSuites addObject:[[DynamicTestSuite alloc] initWithDescriptor:decriptor]]; + } + [[RunningTest currentTest] setAndRun:testSuites inView: self]; [self reloadConstraints]; } } @@ -161,10 +179,10 @@ - (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([[segue identifier] isEqualToString:@"toTestOverview"]){ NSIndexPath* indexPath = [self.tableView indexPathForSelectedRow]; TestOverviewViewController *vc = (TestOverviewViewController * )segue.destinationViewController; - AbstractSuite *testSuite = [items objectAtIndex:indexPath.row]; - [vc setTestSuite:testSuite]; + OONIDescriptor *test = [items objectAtIndex:indexPath.row]; + [vc setDescriptor:test]; [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; - } + } } diff --git a/ooniprobe/View/RunTest/TestOverviewViewController.h b/ooniprobe/View/RunTest/TestOverviewViewController.h index deea5a82f..adbaa7930 100644 --- a/ooniprobe/View/RunTest/TestOverviewViewController.h +++ b/ooniprobe/View/RunTest/TestOverviewViewController.h @@ -12,7 +12,7 @@ UIColor *defaultColor; } -@property (nonatomic, strong) AbstractSuite *testSuite; +@property (nonatomic, strong) id descriptor; @property (strong, nonatomic) IBOutlet UIImageView *testImage; @property (strong, nonatomic) IBOutlet UILabel *testNameLabel; diff --git a/ooniprobe/View/RunTest/TestOverviewViewController.m b/ooniprobe/View/RunTest/TestOverviewViewController.m index d9fbea26c..36f788133 100644 --- a/ooniprobe/View/RunTest/TestOverviewViewController.m +++ b/ooniprobe/View/RunTest/TestOverviewViewController.m @@ -1,13 +1,14 @@ #import "TestOverviewViewController.h" #import "ThirdPartyServices.h" #import "RunningTest.h" +#import "ooniprobe-Swift.h" @interface TestOverviewViewController () @end @implementation TestOverviewViewController -@synthesize testSuite; +@synthesize descriptor; - (void)viewDidLoad { [super viewDidLoad]; @@ -15,13 +16,13 @@ - (void)viewDidLoad { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(settingsChanged) name:@"settingsChanged" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeConstraints) name:@"networkTestEndedUI" object:nil]; - [self.testNameLabel setText:[LocalizationUtility getNameForTest:testSuite.name]]; - NSString *testLongDesc = [LocalizationUtility getLongDescriptionForTest:testSuite.name]; + [self.testNameLabel setText:[LocalizationUtility getNameForTest:[descriptor performSelector:@selector(name)]]]; + NSString *testLongDesc = [LocalizationUtility getLongDescriptionForTest:[descriptor performSelector:@selector(name)]]; [self.testDescriptionLabel setFont:[UIFont fontWithName:@"FiraSans-Regular" size:14]]; [self.testDescriptionLabel setTextColor:[UIColor colorNamed:@"color_gray9"]]; NSMutableDictionary *linkAttributes = [NSMutableDictionary dictionary]; - [linkAttributes setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCTUnderlineStyleAttributeName]; - [linkAttributes setObject:[UIColor colorNamed:@"color_base"] forKey:(NSString *)kCTForegroundColorAttributeName]; + linkAttributes[(NSString *) kCTUnderlineStyleAttributeName] = @YES; + linkAttributes[(NSString *) kCTForegroundColorAttributeName] = [UIColor colorNamed:@"color_base"]; self.testDescriptionLabel.linkAttributes = [NSDictionary dictionaryWithDictionary:linkAttributes]; [self.testDescriptionLabel setMarkdown:testLongDesc]; if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) { @@ -37,19 +38,15 @@ - (void)viewDidLoad { [[UIApplication sharedApplication] openURL:url]; }]; [self.runButton setTitle:[NSString stringWithFormat:@"%@", NSLocalizedString(@"Dashboard.Overview.Run", nil)] forState:UIControlStateNormal]; - if ([testSuite.name isEqualToString:@"websites"]) + if ([[descriptor performSelector:@selector(name)] isEqualToString:@"websites"]) [self.websitesButton setTitle:[NSString stringWithFormat:@"%@", NSLocalizedString(@"Dashboard.Overview.ChooseWebsites", nil)] forState:UIControlStateNormal]; else [self.websitesButton setHidden:YES]; [self reloadLastMeasurement]; - [self.testImage setImage:[UIImage imageNamed:[NSString stringWithFormat:@"%@", testSuite.name]]]; - defaultColor = [TestUtility getBackgroundColorForTest:testSuite.name]; + [self.testImage setImage:[UIImage imageNamed:[NSString stringWithFormat:@"%@", [descriptor performSelector:@selector(name)]]]]; + defaultColor = [TestUtility getBackgroundColorForTest:[descriptor performSelector:@selector(name)]]; [self.runButton setTitleColor:defaultColor forState:UIControlStateNormal]; - if (testSuite.getTestList.count <= 0) { - self.runButton.userInteractionEnabled = NO; - [self.runButton setTitleColor:[UIColor colorNamed:@"color_gray3"] forState:UIControlStateNormal]; - } [self.backgroundView setBackgroundColor:defaultColor]; [NavigationBarUtility setNavigationBar:self.navigationController.navigationBar color:defaultColor]; self.navigationController.navigationBar.topItem.title = @""; @@ -82,13 +79,12 @@ -(void)reloadLastMeasurement{ dispatch_async(dispatch_get_main_queue(), ^{ [self.estimatedLabel setText:NSLocalizedString(@"Dashboard.Overview.Estimated", nil)]; [self.estimatedDetailLabel setText: - [NSString stringWithFormat:@"%@ %@", - testSuite.dataUsage, - [LocalizationUtility getReadableRuntime:[testSuite getRuntime]]]]; + [NSString stringWithFormat:@"%@ %@", [self->descriptor performSelector:@selector(dataUsage)], + [LocalizationUtility getReadableRuntime:[[[DynamicTestSuite alloc] initWithDescriptor:self->descriptor] getRuntime]]]]; [self.lastrunLabel setText:NSLocalizedString(@"Dashboard.Overview.LatestTest", nil)]; NSString *ago; - SRKResultSet *results = [[[[[Result query] limit:1] where:[NSString stringWithFormat:@"test_group_name = '%@'", testSuite.name]] orderByDescending:@"start_time"] fetch]; + SRKResultSet *results = [[[[[Result query] limit:1] where:[NSString stringWithFormat:@"test_group_name = '%@'", [self->descriptor performSelector:@selector(name)]]] orderByDescending:@"start_time"] fetch]; if ([results count] > 0){ ago = [[[results objectAtIndex:0] start_time] timeAgoSinceNow]; } @@ -99,8 +95,7 @@ -(void)reloadLastMeasurement{ } -(void)settingsChanged{ - [testSuite.testList removeAllObjects]; - [testSuite getTestList]; + // NOTE(aanorbel): Reload the descriptor when the settings change. [self reloadLastMeasurement]; } @@ -124,16 +119,18 @@ - (void)willMoveToParentViewController:(UIViewController *)parent { -(IBAction)run:(id)sender{ if ([TestUtility checkConnectivity:self] && [TestUtility checkTestRunning:self]){ - [[RunningTest currentTest] setAndRun:[NSMutableArray arrayWithObject:testSuite] inView: self]; + [[RunningTest currentTest] setAndRun:[NSMutableArray arrayWithObject:[[DynamicTestSuite alloc] initWithDescriptor:descriptor]] inView: self]; [self changeConstraints]; } } +/// NOTE(aanorbel): `performSegueWithIdentifier:@"toTestSettings"` is never called in the codebase thus, this method is not used. +/// I would like to remove it but needs to be sure of the impact. - (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([[segue identifier] isEqualToString:@"toTestSettings"]){ SettingsTableViewController *vc = (SettingsTableViewController * )segue.destinationViewController; - [vc setTestSuite:testSuite]; + [vc setTestSuite:[[DynamicTestSuite alloc] initWithDescriptor:descriptor]]; } } diff --git a/ooniprobe/View/Settings/SettingsCategoriesTableViewController.m b/ooniprobe/View/Settings/SettingsCategoriesTableViewController.m index c99795301..b4858e2fd 100644 --- a/ooniprobe/View/Settings/SettingsCategoriesTableViewController.m +++ b/ooniprobe/View/Settings/SettingsCategoriesTableViewController.m @@ -14,6 +14,8 @@ - (void)awakeFromNib{ - (void)viewDidLoad { [super viewDidLoad]; + self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // remove separator + self.tableView.backgroundColor = [UIColor colorNamed:@"color_gray1"]; [NavigationBarUtility setNavigationBar:self.navigationController.navigationBar]; categories = [SettingsUtility getSettingsCategories]; } @@ -45,6 +47,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell.textLabel.text = [LocalizationUtility getNameForSetting:current]; cell.textLabel.textColor = [UIColor colorNamed:@"color_gray9"]; cell.imageView.image = [UIImage imageNamed:current]; + cell.backgroundColor = [UIColor colorNamed:@"color_gray1"]; return cell; } diff --git a/ooniprobe/View/Settings/SettingsTableViewController.m b/ooniprobe/View/Settings/SettingsTableViewController.m index d53c24a8c..83d2a8470 100644 --- a/ooniprobe/View/Settings/SettingsTableViewController.m +++ b/ooniprobe/View/Settings/SettingsTableViewController.m @@ -54,6 +54,8 @@ -(void)reloadSettings { //hide rows smooth dispatch_async(dispatch_get_main_queue(), ^{ [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade]; + self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // remove separator + self.tableView.backgroundColor = [UIColor colorNamed:@"color_gray1"]; }); } @@ -63,8 +65,9 @@ -(void)viewWillDisappear:(BOOL)animated{ [testSuite.testList removeAllObjects]; [testSuite getTestList]; } - if (testSuite != nil || [[TestUtility getTestOptionTypes] containsObject:category]) + if (testSuite != nil) { [[NSNotificationCenter defaultCenter] postNotificationName:@"settingsChanged" object:nil]; + } } #pragma mark - Table view data source @@ -113,9 +116,6 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N else { cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; } - if ([[TestUtility getTestOptionTypes] containsObject:current]){ - cell.imageView.image = [UIImage imageNamed:current]; - } cell.textLabel.text = [LocalizationUtility getNameForSetting:current]; cell.textLabel.textColor = [UIColor colorNamed:@"color_gray9"]; UISwitch *switchview = [[UISwitch alloc] initWithFrame:CGRectZero]; @@ -132,13 +132,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N } else cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; - if ([[TestUtility getTestOptionTypes] containsObject:current]){ - if ([NSLocale characterDirectionForLanguage:[NSLocale preferredLanguages][0]] == NSLocaleLanguageDirectionRightToLeft) { - cell.imageView.image = [self imageWithImage:[UIImage imageNamed:current] convertToSize:CGSizeMake(32, 32)]; - } else { - cell.imageView.image = [UIImage imageNamed:current]; - } - } + cell.textLabel.text = [LocalizationUtility getNameForSetting:current]; cell.textLabel.textColor = [UIColor colorNamed:@"color_gray9"]; cell.accessoryView = nil; @@ -188,6 +182,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell.accessoryView = cleanButton; } } + cell.backgroundColor = [UIColor colorNamed:@"color_gray1"]; return cell; } diff --git a/ooniprobe/View/Settings/WebsiteCategoriesTableViewController.m b/ooniprobe/View/Settings/WebsiteCategoriesTableViewController.m index a63099fc4..bacfcf0c1 100644 --- a/ooniprobe/View/Settings/WebsiteCategoriesTableViewController.m +++ b/ooniprobe/View/Settings/WebsiteCategoriesTableViewController.m @@ -12,6 +12,8 @@ - (void)viewDidLoad { self.navigationController.navigationBar.topItem.title = @""; self.tableView.estimatedRowHeight = 60.0; self.tableView.rowHeight = UITableViewAutomaticDimension; + self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // remove separator + self.tableView.backgroundColor = [UIColor colorNamed:@"color_gray1"]; } -(void)viewWillAppear:(BOOL)animated{ @@ -53,6 +55,8 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N else switchview.on = YES; cell.accessoryView = switchview; + cell.backgroundColor = [UIColor colorNamed:@"color_gray1"]; + return cell; } diff --git a/ooniprobe/View/TestResults/Rows/DashboardTableViewCell.h b/ooniprobe/View/TestResults/Rows/DashboardTableViewCell.h index 5745bd6db..e0aea8b6a 100644 --- a/ooniprobe/View/TestResults/Rows/DashboardTableViewCell.h +++ b/ooniprobe/View/TestResults/Rows/DashboardTableViewCell.h @@ -2,6 +2,7 @@ #import "TestUtility.h" #import "RunButton.h" #import "Suite.h" +#import "ooniprobe-Swift.h" @interface DashboardTableViewCell : UITableViewCell @property (strong, nonatomic) IBOutlet UIView *cardbackgroundView; @@ -9,5 +10,5 @@ @property (strong, nonatomic) IBOutlet UILabel *descLabel; @property (strong, nonatomic) IBOutlet UIImageView *testLogo; --(void)setTestSuite:(AbstractSuite*)testSuite; +-(void)setDescriptor:(OONIDescriptor*)descriptor; @end diff --git a/ooniprobe/View/TestResults/Rows/DashboardTableViewCell.m b/ooniprobe/View/TestResults/Rows/DashboardTableViewCell.m index be70d9ef1..df3137cd1 100644 --- a/ooniprobe/View/TestResults/Rows/DashboardTableViewCell.m +++ b/ooniprobe/View/TestResults/Rows/DashboardTableViewCell.m @@ -5,7 +5,6 @@ @implementation DashboardTableViewCell - (void)awakeFromNib { [super awakeFromNib]; [self setRoundedView]; - [self setShadow]; } - (void)setSelected:(BOOL)selected animated:(BOOL)animated { @@ -13,63 +12,20 @@ - (void)setSelected:(BOOL)selected animated:(BOOL)animated { // Configure the view for the selected state } -- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated{ - [super setHighlighted:highlighted animated:animated]; - if (highlighted) { - [UIView - animateWithDuration:0.3f - delay:0 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - self.contentView.layer.shadowOpacity = 0; - self.contentView.alpha = 0.7f; - } - completion: NULL - ]; - } else { - [UIView - animateWithDuration:0.8f - delay:0.5f - options:UIViewAnimationOptionCurveEaseInOut - animations:^{ - self.contentView.layer.shadowOpacity = 0.6f; - self.contentView.alpha = 1.f; - } - completion: NULL - ]; - } -} - --(void)setTestSuite:(AbstractSuite*)testSuite{ - [self.titleLabel setText:[LocalizationUtility getNameForTest:testSuite.name]]; - [self.descLabel setText:[LocalizationUtility getDescriptionForTest:testSuite.name]]; - if (testSuite.getTestList.count <= 0) { - [self.titleLabel setTextColor:[UIColor colorNamed:@"disabled_test_text"]]; - [self.descLabel setTextColor:[UIColor colorNamed:@"disabled_test_text"]]; - [self.testLogo setImage:[self imageWithGradient:[UIImage imageNamed:[NSString stringWithFormat:@"%@", testSuite.name]] startColor:[UIColor colorNamed:@"disabled_test_text"] endColor:[UIColor colorNamed:@"disabled_test_text"]]]; - [self.cardbackgroundView setBackgroundColor:[UIColor colorNamed:@"disabled_test_background"]]; - } else { - [self.titleLabel setTextColor:[UIColor colorNamed:@"color_gray9"]]; - [self.descLabel setTextColor:[UIColor colorNamed:@"color_gray9"]]; - [self.testLogo setImage:[self imageWithGradient:[UIImage imageNamed:[NSString stringWithFormat:@"%@", testSuite.name]] startColor:[TestUtility getGradientColorForTest:testSuite.name] endColor:[TestUtility getColorForTest:testSuite.name]]]; - [self.cardbackgroundView setBackgroundColor:[UIColor colorNamed:@"color_gray0"]]; - } +-(void)setDescriptor:(OONIDescriptor*)descriptor{ + [self.titleLabel setText:descriptor.title]; + [self.descLabel setText:descriptor.shortDescription]; + [self.titleLabel setTextColor:[UIColor colorNamed:@"color_gray9"]]; + [self.descLabel setTextColor:[UIColor colorNamed:@"color_gray9"]]; + [self.cardbackgroundView setBackgroundColor:[UIColor colorNamed:@"color_gray0"]]; + [self.testLogo setImage:[self imageWithGradient:[UIImage imageNamed:descriptor.icon] startColor:[TestUtility getGradientColorForTest:descriptor.name] endColor:[TestUtility getColorForTest:descriptor.name]]]; } -(void)setRoundedView{ - self.cardbackgroundView.layer.cornerRadius = 5; + self.cardbackgroundView.layer.cornerRadius = 8; self.cardbackgroundView.layer.masksToBounds = YES; -} - --(void)setShadow{ - self.backgroundColor = [UIColor clearColor]; - self.contentView.backgroundColor = [UIColor clearColor]; - - self.contentView.layer.shadowRadius = 5; - self.contentView.layer.shadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.8f].CGColor; - self.contentView.layer.shadowOffset = CGSizeMake(0.0f, 1); - self.contentView.layer.shadowOpacity = 0.6f; - self.contentView.layer.masksToBounds = NO; + self.cardbackgroundView.layer.borderColor = [UIColor colorNamed:@"color_gray3"].CGColor; + self.cardbackgroundView.layer.borderWidth = 2.0; } //From https://stackoverflow.com/questions/8098130/how-can-i-tint-a-uiimage-with-gradient diff --git a/ooniprobe/ooniprobe-Bridging-Header.h b/ooniprobe/ooniprobe-Bridging-Header.h new file mode 100644 index 000000000..99a557856 --- /dev/null +++ b/ooniprobe/ooniprobe-Bridging-Header.h @@ -0,0 +1,9 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "DashboardTableViewController.h" +#import "TestOverviewViewController.h" +#import "AbstractSuite.h" +#import "Tests.h" +#import "UIView+Toast.h"