From 81dd1f644d45a00b2b842adeec4985f52121e1fe Mon Sep 17 00:00:00 2001 From: ned Date: Thu, 24 Mar 2022 19:48:56 +0100 Subject: [PATCH] support new installation options in API v1.5 --- appdb/API/API+Configuration.swift | 2 - appdb/API/API+Install.swift | 7 +- appdb/API/API.swift | 11 +++- ...itionalInstallOptionsViewController.swift" | 66 +++++++++---------- appdb/Startup/Preferences.swift | 18 +++++ .../Downloads/Library/Library+Extension.swift | 19 +++--- .../Cells/Download/Details+DownloadCell.swift | 4 +- appdb/Tabs/Featured/Details/Details.swift | 19 +++--- .../Tabs/Settings/Settings+StaticCells.swift | 20 +++++- 9 files changed, 106 insertions(+), 60 deletions(-) diff --git a/appdb/API/API+Configuration.swift b/appdb/API/API+Configuration.swift index b50ffa1b..dc482dc1 100644 --- a/appdb/API/API+Configuration.swift +++ b/appdb/API/API+Configuration.swift @@ -49,10 +49,8 @@ extension API { Preferences.set(.deviceName, to: data["name"].stringValue) Preferences.set(.deviceVersion, to: data["ios_version"].stringValue) - Preferences.set(.enableIapPatch, to: data["enable_inapp_patch"].stringValue == "yes") Preferences.set(.disableRevocationChecks, to: data["disable_protection_checks"].stringValue == "yes") Preferences.set(.forceDisablePRO, to: data["is_pro_disabled"].stringValue == "yes") - Preferences.set(.enableTrainer, to: data["enable_trainer"].stringValue == "yes") Preferences.set(.signingIdentityType, to: data["signing_identity_type"].stringValue) Preferences.set(.optedOutFromEmails, to: data["is_opted_out_from_emails"].stringValue == "yes") diff --git a/appdb/API/API+Install.swift b/appdb/API/API+Install.swift index 446eb02a..103849e1 100644 --- a/appdb/API/API+Install.swift +++ b/appdb/API/API+Install.swift @@ -11,8 +11,11 @@ import SwiftyJSON extension API { - static func install(id: String, type: ItemType, alongsideId: String = "", displayName: String = "", completion:@escaping (_ error: String?) -> Void) { - AF.request(endpoint, parameters: ["action": Actions.install.rawValue, "type": type.rawValue, "id": id, "is_alongside": alongsideId.lowercased(), "display_name": displayName, "lang": languageCode], headers: headersWithCookie) + static func install(id: String, type: ItemType, additionalOptions: [AdditionalInstallationParameters: Any] = [:], completion:@escaping (_ error: String?) -> Void) { + var parameters: [String: Any] = ["action": Actions.install.rawValue, "type": type.rawValue, "id": id, "lang": languageCode] + for (key, value) in additionalOptions { parameters[key.rawValue] = value } + + AF.request(endpoint, parameters: parameters, headers: headersWithCookie) .responseJSON { response in switch response.result { case .success(let value): diff --git a/appdb/API/API.swift b/appdb/API/API.swift index c0edbb74..8a77ad96 100644 --- a/appdb/API/API.swift +++ b/appdb/API/API.swift @@ -142,9 +142,16 @@ enum ConfigurationParameters: String { case askForOptions = "params[ask_for_installation_options]" case clearDevEntity = "params[clear_developer_entity]" case disableProtectionChecks = "params[disable_protection_checks]" - case enableIapPatch = "params[enable_inapp_patch]" case forceDisablePRO = "params[is_pro_disabled]" - case enableTrainer = "params[enable_trainer]" case signingIdentityType = "params[signing_identity_type]" case optedOutFromEmails = "params[is_opted_out_from_emails]" } + +enum AdditionalInstallationParameters: String { + case alongside = "enable_features[alongside]" + case name = "enable_features[name]" + case inApp = "enable_features[inapp]" + case trainer = "enable_features[trainer]" + case removePlugins = "enable_features[remove_plugins]" + case pushNotifications = "enable_features[push]" +} diff --git "a/appdb/Resources/\342\200\242 AdditionalInstallOptionsView/AdditionalInstallOptionsViewController.swift" "b/appdb/Resources/\342\200\242 AdditionalInstallOptionsView/AdditionalInstallOptionsViewController.swift" index c9d49e73..160310c4 100644 --- "a/appdb/Resources/\342\200\242 AdditionalInstallOptionsView/AdditionalInstallOptionsViewController.swift" +++ "b/appdb/Resources/\342\200\242 AdditionalInstallOptionsView/AdditionalInstallOptionsViewController.swift" @@ -46,7 +46,9 @@ class AdditionalInstallOptionsNavController: UINavigationController, AdditionalI func updateHeight() { setupConstraints() - UIView.animate(withDuration: 0.2, animations: view.superview!.layoutIfNeeded) + if let sv = view.superview { + UIView.animate(withDuration: 0.2, animations: sv.layoutIfNeeded) + } } override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { @@ -59,58 +61,54 @@ class AdditionalInstallOptionsNavController: UINavigationController, AdditionalI } class AdditionalInstallOptionsViewController: TableViewController { + weak var heightDelegate: AdditionalInstallOptionsHeightDelegate? - var onCompletion: ((Bool, String, String) -> Void)? + var onCompletion: ((_ patchIap: Bool, _ enableGameTrainer: Bool, _ removePlugins: Bool, _ enablePushNotifications: Bool, + _ duplicateApp: Bool, _ newId: String, _ newName: String) -> Void)? - private var duplicateApp = true private var newId: String = "" private var newName: String = "" var cancelled = true - private let placeholder: String = Global.randomString(length: 4).lowercased() + private let placeholder: String = Global.randomString(length: 5).lowercased() private let rowHeight: CGFloat = 50 var height: CGFloat { - let navbarHeight: CGFloat = navigationController?.navigationBar.frame.height ?? 0 - return navbarHeight + rowHeight * CGFloat(duplicateApp ? 3 : 2) + let navbarHeight: CGFloat = navigationController?.navigationBar.frame.height ?? 44 + return navbarHeight + rowHeight * CGFloat(sections.first!.rows.count) } lazy var sections: [Static.Section] = [ Section(rows: [ - Row(text: "Duplicate app".localized(), accessory: .switchToggle(value: duplicateApp) { [unowned self] newValue in - self.duplicateApp = newValue - self.setInstallButtonEnabled() - self.switchMode() - }, cellClass: SimpleStaticCell.self), Row(text: "New display name".localized(), cellClass: StaticTextFieldCell.self, context: ["placeholder": "Use Original".localized(), "callback": { [unowned self] (newName: String) in self.newName = newName self.setInstallButtonEnabled() }] ), + Row(text: "Patch in-app Purchases".localized(), cellClass: SwitchCell.self, context: ["valueChange": { new in + Preferences.set(.enableIapPatch, to: new) + }, "value": Preferences.enableIapPatch]), + Row(text: "Enable Game Trainer".localized(), cellClass: SwitchCell.self, context: ["valueChange": { new in + Preferences.set(.enableTrainer, to: new) + }, "value": Preferences.enableTrainer]), + Row(text: "Remove Plugins".localized(), cellClass: SwitchCell.self, context: ["valueChange": { new in + Preferences.set(.removePlugins, to: new) + }, "value": Preferences.removePlugins]), + Row(text: "Enable Push Notifications".localized(), cellClass: SwitchCell.self, context: ["valueChange": { new in + Preferences.set(.enablePushNotifications, to: new) + }, "value": Preferences.enablePushNotifications]), + Row(text: "Duplicate app".localized(), cellClass: SwitchCell.self, context: ["valueChange": { (new: Bool) in + Preferences.set(.duplicateApp, to: new) + self.setInstallButtonEnabled() + }, "value": Preferences.duplicateApp]), Row(text: "New ID".localized(), cellClass: StaticTextFieldCell.self, context: ["placeholder": placeholder, "callback": { [unowned self] (newId: String) in self.newId = newId.isEmpty ? self.placeholder : newId self.setInstallButtonEnabled() - }] - ) - ]) - ] - - lazy var compactSections: [Static.Section] = [ - Section(rows: [ - Row(text: "Duplicate app".localized(), accessory: .switchToggle(value: duplicateApp) { [unowned self] newValue in - self.duplicateApp = newValue - self.setInstallButtonEnabled() - self.switchMode() - }, cellClass: SimpleStaticCell.self), - Row(text: "New display name".localized(), cellClass: StaticTextFieldCell.self, context: - ["placeholder": "Use Original".localized(), "callback": { [unowned self] (newName: String) in - self.newName = newName - self.setInstallButtonEnabled() - }] + }, "forceLowercase": true, "characterLimit": 5] ) ]) ] @@ -126,6 +124,9 @@ class AdditionalInstallOptionsViewController: TableViewController { // Hide last separator tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 1)) + if #available(iOS 15.0, *) { + tableView.sectionHeaderTopPadding = 0 + } tableView.rowHeight = rowHeight tableView.isScrollEnabled = false @@ -138,23 +139,18 @@ class AdditionalInstallOptionsViewController: TableViewController { dataSource.sections = sections } - private func switchMode() { - heightDelegate?.updateHeight() - dataSource.sections = duplicateApp ? sections : compactSections - } - @objc private func dismissAnimated() { cancelled = true dismiss(animated: true) } @objc private func proceedWithInstall() { - onCompletion?(self.duplicateApp, self.newId, self.newName) + onCompletion?(Preferences.enableIapPatch, Preferences.enableTrainer, Preferences.removePlugins, Preferences.enablePushNotifications, Preferences.duplicateApp, self.newId.lowercased(), self.newName) cancelled = false dismiss(animated: true) } private func setInstallButtonEnabled() { - navigationItem.rightBarButtonItem?.isEnabled = !newId.contains(" ") + navigationItem.rightBarButtonItem?.isEnabled = newId.count == 5 && !newId.contains(" ") } } diff --git a/appdb/Startup/Preferences.swift b/appdb/Startup/Preferences.swift index 501a4c6c..b81a0e79 100644 --- a/appdb/Startup/Preferences.swift +++ b/appdb/Startup/Preferences.swift @@ -30,6 +30,9 @@ extension Defaults.Keys { static let enableTrainer = Key("enableTrainer", default: false) static let signingIdentityType = Key("signingIdentityType", default: "auto") static let optedOutFromEmails = Key("optedOutFromEmails", default: false) + static let removePlugins = Key("removePlugins", default: false) + static let enablePushNotifications = Key("enablePush", default: false) + static let duplicateApp = Key("duplicateApp", default: true) } // Sensitive data is stored in Keychain @@ -160,6 +163,18 @@ enum Preferences { static var optedOutFromEmails: Bool { defaults[.optedOutFromEmails] } + + static var removePlugins: Bool { + defaults[.removePlugins] + } + + static var enablePushNotifications: Bool { + defaults[.enablePushNotifications] + } + + static var duplicateApp: Bool { + defaults[.duplicateApp] + } } extension Preferences { @@ -209,6 +224,9 @@ extension Preferences { UserDefaults.standard.removeObject(forKey: Defaults.Keys.enableTrainer.name) UserDefaults.standard.removeObject(forKey: Defaults.Keys.signingIdentityType.name) UserDefaults.standard.removeObject(forKey: Defaults.Keys.optedOutFromEmails.name) + UserDefaults.standard.removeObject(forKey: Defaults.Keys.removePlugins.name) + UserDefaults.standard.removeObject(forKey: Defaults.Keys.enablePushNotifications.name) + UserDefaults.standard.removeObject(forKey: Defaults.Keys.duplicateApp.name) } // Remove secure keys diff --git a/appdb/Tabs/Downloads/Library/Library+Extension.swift b/appdb/Tabs/Downloads/Library/Library+Extension.swift index 02e41a15..579340ab 100644 --- a/appdb/Tabs/Downloads/Library/Library+Extension.swift +++ b/appdb/Tabs/Downloads/Library/Library+Extension.swift @@ -268,8 +268,8 @@ extension Library { if Preferences.deviceIsLinked { setButtonTitle("Requesting...") - func install(alongsideId: String = "", displayName: String = "") { - API.install(id: sender.linkId, type: .myAppstore, alongsideId: alongsideId, displayName: displayName) { [weak self] error in + func install(_ additionalOptions: [AdditionalInstallationParameters: Any] = [:]) { + API.install(id: sender.linkId, type: .myAppstore, additionalOptions: additionalOptions) { [weak self] error in guard let self = self else { return } if let error = error { @@ -308,12 +308,15 @@ extension Library { } } - vc.onCompletion = { (duplicate: Bool, newId: String, newName: String) in - if duplicate { - install(alongsideId: newId, displayName: newName) - } else { - install(displayName: newName) - } + vc.onCompletion = { (patchIap: Bool, enableGameTrainer: Bool, removePlugins: Bool, enablePushNotifications: Bool, duplicateApp: Bool, newId: String, newName: String) in + var additionalOptions: [AdditionalInstallationParameters: Any] = [:] + if patchIap { additionalOptions[.inApp] = 1 } + if enableGameTrainer { additionalOptions[.trainer] = 1 } + if removePlugins { additionalOptions[.removePlugins] = 1 } + if enablePushNotifications { additionalOptions[.pushNotifications] = 1 } + if duplicateApp && !newId.isEmpty { additionalOptions[.alongside] = newId } + if !newName.isEmpty { additionalOptions[.name] = newName } + install(additionalOptions) } } else { install() diff --git a/appdb/Tabs/Featured/Details/Cells/Download/Details+DownloadCell.swift b/appdb/Tabs/Featured/Details/Cells/Download/Details+DownloadCell.swift index f006d6c1..d3068b26 100644 --- a/appdb/Tabs/Featured/Details/Cells/Download/Details+DownloadCell.swift +++ b/appdb/Tabs/Featured/Details/Cells/Download/Details+DownloadCell.swift @@ -95,7 +95,7 @@ class DetailsDownload: DetailsCell { host.bottom ~== cracker.top ~- 3 host.leading ~== host.superview!.leading ~+ Global.Size.margin.value - host.width ~<= host.superview!.width * 0.6 + host.width ~<= host.superview!.width * 0.65 uploader.leading ~== cracker.leading uploader.trailing ~== cracker.trailing @@ -177,7 +177,7 @@ class DetailsDownloadUnified: DetailsCell { host.centerY ~== button.centerY ~- 9 host.leading ~== host.superview!.leading ~+ Global.Size.margin.value - host.width ~<= host.superview!.width * 0.6 + host.width ~<= host.superview!.width * 0.65 cracker.leading ~== host.leading cracker.trailing ~== host.trailing diff --git a/appdb/Tabs/Featured/Details/Details.swift b/appdb/Tabs/Featured/Details/Details.swift index a460b3e2..df3d4907 100644 --- a/appdb/Tabs/Featured/Details/Details.swift +++ b/appdb/Tabs/Featured/Details/Details.swift @@ -285,8 +285,8 @@ class Details: LoadingTableView { if Preferences.deviceIsLinked { setButtonTitle("Requesting...") - func install(alongsideId: String = "", displayName: String = "") { - API.install(id: sender.linkId, type: self.contentType, alongsideId: alongsideId, displayName: displayName) { [weak self] error in + func install(_ additionalOptions: [AdditionalInstallationParameters: Any] = [:]) { + API.install(id: sender.linkId, type: self.contentType, additionalOptions: additionalOptions) { [weak self] error in guard let self = self else { return } if let error = error { @@ -333,12 +333,15 @@ class Details: LoadingTableView { } } - vc.onCompletion = { (duplicate: Bool, newId: String, newName: String) in - if duplicate { - install(alongsideId: newId, displayName: newName) - } else { - install(displayName: newName) - } + vc.onCompletion = { (patchIap: Bool, enableGameTrainer: Bool, removePlugins: Bool, enablePushNotifications: Bool, duplicateApp: Bool, newId: String, newName: String) in + var additionalOptions: [AdditionalInstallationParameters: Any] = [:] + if patchIap { additionalOptions[.inApp] = 1 } + if enableGameTrainer { additionalOptions[.trainer] = 1 } + if removePlugins { additionalOptions[.removePlugins] = 1 } + if enablePushNotifications { additionalOptions[.pushNotifications] = 1 } + if duplicateApp && !newId.isEmpty { additionalOptions[.alongside] = newId } + if !newName.isEmpty { additionalOptions[.name] = newName } + install(additionalOptions) } } else { install() diff --git a/appdb/Tabs/Settings/Settings+StaticCells.swift b/appdb/Tabs/Settings/Settings+StaticCells.swift index 1e6b012f..30ad1042 100644 --- a/appdb/Tabs/Settings/Settings+StaticCells.swift +++ b/appdb/Tabs/Settings/Settings+StaticCells.swift @@ -270,6 +270,8 @@ class StaticTextFieldCell: SimpleStaticCell, UITextFieldDelegate { var textfieldDidEndEditing: ((String) -> Void)? var textField: UITextField! + var characterLimit: Int? + var forceLowercase = false override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) @@ -309,11 +311,27 @@ class StaticTextFieldCell: SimpleStaticCell, UITextFieldDelegate { if let callback = row.context?["callback"] as? (String) -> Void { self.textfieldDidEndEditing = callback } + if let limit = row.context?["characterLimit"] as? Int { + characterLimit = limit + } + if let lower = row.context?["forceLowercase"] as? Bool { + forceLowercase = lower + } } @objc func textFieldDidChange(_ textField: UITextField) { guard let text = textField.text else { return } - textfieldDidEndEditing?(text) + var adjustedText = text + + if forceLowercase { + adjustedText = adjustedText.lowercased() + } + if let limit = characterLimit { + adjustedText = String(adjustedText.prefix(limit)) + } + + textField.text = adjustedText + textfieldDidEndEditing?(adjustedText) } func textFieldShouldReturn(_ textField: UITextField) -> Bool {