Commit 8628b13b authored by yqz's avatar yqz

Merge branch 'dev_main' into yQz0507

* dev_main:
  fix bugs
  adjust 数据上报
parents c91e1482 6542bcb4
......@@ -15,6 +15,10 @@
049F146D2DDB1F64007B5A9B /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04BD915F2D9D68AD00055CEB /* SwiftUI.framework */; };
049F14782DDB1F66007B5A9B /* LockSreenExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 049F146B2DDB1F64007B5A9B /* LockSreenExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
04BBB4E62DC0748F00D7E3AB /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04BBB4E52DC0748F00D7E3AB /* StoreKit.framework */; };
04C299E22DDE02530054D76C /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04C299E12DDE02530054D76C /* AdSupport.framework */; };
04C299E42DDE025D0054D76C /* AdServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04C299E32DDE025D0054D76C /* AdServices.framework */; };
04C299E62DDE026D0054D76C /* AppTrackingTransparency.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04C299E52DDE026D0054D76C /* AppTrackingTransparency.framework */; };
04CA06622DDDB40E009A15E3 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 04CA06612DDDB40E009A15E3 /* KeychainSwift */; };
04CF31702DA7E78F001C87CA /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04BD7A442DA3BA1700A24C4B /* Intents.framework */; };
04CF31772DA7E790001C87CA /* ChargeShow.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 04CF316F2DA7E78F001C87CA /* ChargeShow.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
04CF317D2DA7E7BE001C87CA /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 04CF317C2DA7E7BE001C87CA /* Intents.intentdefinition */; };
......@@ -83,6 +87,9 @@
04BD7A4F2DA3BA1700A24C4B /* IntentsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IntentsUI.framework; path = System/Library/Frameworks/IntentsUI.framework; sourceTree = SDKROOT; };
04BD915D2D9D68AD00055CEB /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
04BD915F2D9D68AD00055CEB /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
04C299E12DDE02530054D76C /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; };
04C299E32DDE025D0054D76C /* AdServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdServices.framework; path = System/Library/Frameworks/AdServices.framework; sourceTree = SDKROOT; };
04C299E52DDE026D0054D76C /* AppTrackingTransparency.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppTrackingTransparency.framework; path = System/Library/Frameworks/AppTrackingTransparency.framework; sourceTree = SDKROOT; };
04CF316F2DA7E78F001C87CA /* ChargeShow.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ChargeShow.appex; sourceTree = BUILT_PRODUCTS_DIR; };
04CF317C2DA7E7BE001C87CA /* Intents.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = Intents.intentdefinition; sourceTree = "<group>"; };
04EC294C2DD342220049739A /* SVProgressHUD.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SVProgressHUD.framework; sourceTree = BUILT_PRODUCTS_DIR; };
......@@ -197,9 +204,13 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
04C299E62DDE026D0054D76C /* AppTrackingTransparency.framework in Frameworks */,
04BBB4E62DC0748F00D7E3AB /* StoreKit.framework in Frameworks */,
04C299E42DDE025D0054D76C /* AdServices.framework in Frameworks */,
04C299E22DDE02530054D76C /* AdSupport.framework in Frameworks */,
042EF7462DD4A3F90028DE2C /* Kingfisher in Frameworks */,
3A00E856852A8783E544CD7D /* Pods_PhoneManager.framework in Frameworks */,
04BBB4E62DC0748F00D7E3AB /* StoreKit.framework in Frameworks */,
04CA06622DDDB40E009A15E3 /* KeychainSwift in Frameworks */,
04EC294B2DD33B480049739A /* GoogleSignIn in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
......@@ -210,6 +221,9 @@
27ECDADD9059AB5043B8E1E9 /* Frameworks */ = {
isa = PBXGroup;
children = (
04C299E52DDE026D0054D76C /* AppTrackingTransparency.framework */,
04C299E32DDE025D0054D76C /* AdServices.framework */,
04C299E12DDE02530054D76C /* AdSupport.framework */,
04EC294C2DD342220049739A /* SVProgressHUD.framework */,
04BBB4E52DC0748F00D7E3AB /* StoreKit.framework */,
6028F60B696E2F97EAA2325C /* Pods_PhoneManager.framework */,
......@@ -387,6 +401,7 @@
packageReferences = (
04EC26182DD33B070049739A /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */,
042EF7442DD4A3780028DE2C /* XCRemoteSwiftPackageReference "Kingfisher" */,
04CA06602DDDB3F8009A15E3 /* XCRemoteSwiftPackageReference "keychain-swift" */,
);
preferredProjectObjectVersion = 77;
productRefGroup = EB388E5C2D8A61A800629B0D /* Products */;
......@@ -1012,6 +1027,14 @@
minimumVersion = 8.3.2;
};
};
04CA06602DDDB3F8009A15E3 /* XCRemoteSwiftPackageReference "keychain-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/evgenyneu/keychain-swift.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 24.0.0;
};
};
04EC26182DD33B070049739A /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/google/GoogleSignIn-iOS";
......@@ -1028,6 +1051,11 @@
package = 042EF7442DD4A3780028DE2C /* XCRemoteSwiftPackageReference "Kingfisher" */;
productName = Kingfisher;
};
04CA06612DDDB40E009A15E3 /* KeychainSwift */ = {
isa = XCSwiftPackageProductDependency;
package = 04CA06602DDDB3F8009A15E3 /* XCRemoteSwiftPackageReference "keychain-swift" */;
productName = KeychainSwift;
};
04EC294A2DD33B480049739A /* GoogleSignIn */ = {
isa = XCSwiftPackageProductDependency;
package = 04EC26182DD33B070049739A /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */;
......
{
"originHash" : "c64907f28a6f12cd9166d202e75e2e3776dc750a2e196ae17a655106dc8ba134",
"originHash" : "de1a4dc6f64c5ce471b3f7c477ef76458170ad953fc5100955926b1f2889707a",
"pins" : [
{
"identity" : "app-check",
......@@ -55,6 +55,15 @@
"version" : "4.1.1"
}
},
{
"identity" : "keychain-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/evgenyneu/keychain-swift.git",
"state" : {
"revision" : "5e1b02b6a9dac2a759a1d5dbc175c86bd192a608",
"version" : "24.0.0"
}
},
{
"identity" : "kingfisher",
"kind" : "remoteSourceControl",
......
......@@ -11,6 +11,7 @@ import Photos
import GoogleMobileAds
import UserMessagingPlatform
import GoogleSignIn
import AdjustSdk
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
......@@ -32,18 +33,36 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
window?.rootViewController = rootNav
window?.makeKeyAndVisible()
// let Ssoryboard = UIStoryboard(name: "LauchVC", bundle: nil)
//
// if let current = Ssoryboard.instantiateViewController(identifier: "LauchVCID") as? LauchVC {
//
// window?.rootViewController = current
// window?.makeKeyAndVisible()
// }
// 小组件数据 同步
let battery = WidgetPublicModel.battery()
let storage = WidgetPublicModel.UseDiskSpace() * 100
widgetAppgourp.share.PushWidgetData(battery: Int(battery), storage: Int(storage))
let battery = WidgetPublicModel.battery()
let storage = WidgetPublicModel.UseDiskSpace() * 100
widgetAppgourp.share.PushWidgetData(battery: Int(battery), storage: Int(storage))
//初始化adjust
let yourAppToken = "6mouvwgw7ksg"
#if DEBUG
let environment = ADJEnvironmentSandbox
let adjustConfig = ADJConfig(
appToken: yourAppToken,
environment: environment)
adjustConfig?.logLevel = ADJLogLevel.verbose
#else
let environment = ADJEnvironmentProduction
let adjustConfig = ADJConfig(
appToken: yourAppToken,
environment: environment)
adjustConfig?.logLevel = ADJLogLevel.suppress
#endif
adjustConfig?.delegate = self
Adjust.initSdk(adjustConfig)
// 获取内购价格
IAPManager.share.fetchProducts { p in
Print("获取内购商品信息",p as Any)
......@@ -60,6 +79,22 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// 设置app动态按钮
setupDynamicShortcuts()
Adjust.attribution { attribution in
Print("获取归因数据11",attribution?.jsonResponse)
// let trackerToken = attribution?.trackerToken
// let trackerName = attribution?.trackerName
// let network = attribution?.network
// let campaign = attribution?.campaign
// let adgroup = attribution?.adgroup
// let creative = attribution?.creative
// let clickLabel = attribution?.clickLabel
// let costType = attribution?.costType
// let costAmount = attribution?.costAmount
// let costCurrency = attribution?.costCurrency
}
return true
}
......@@ -141,6 +176,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
case .NoNet:
break
case .WIFI,.WWAN:
if UserDef.shard.isFirstStart{
Print("首次启动获取网络上报")
APIReportManager.shared.startReport(type: .app_start)
UserDef.shard.saveFirstStart()
}
break
}
}
......
......@@ -7,6 +7,7 @@
import Foundation
import UIKit
import AdjustSdk
extension AppDelegate{
......@@ -15,7 +16,7 @@ extension AppDelegate{
type: "com.app.phonemanager.iap.distance",
localizedTitle: "Unlock Exclusive Discounts",
localizedSubtitle: "Your special offer is awaiting—don't miss this limited-time second chance benefit!",
icon: UIApplicationShortcutIcon(systemImageName: "star.fill"),
icon: UIApplicationShortcutIcon(templateImageName: ""), //UIApplicationShortcutIcon(systemImageName: "star.fill"),
userInfo: nil
)
UIApplication.shared.shortcutItems = [shortcutItem]
......@@ -52,3 +53,17 @@ extension AppDelegate{
}
}
extension AppDelegate:AdjustDelegate{
func adjustAttributionChanged(_ attribution: ADJAttribution?) {
guard let attribution = attribution else { return }
// 可以将这些数据上报给自己的服务器或者做其他处理
print("归因数据:", attribution)
}
}
......@@ -136,6 +136,11 @@ class GroupDatabase {
// 查询所有数据
func queryAll() -> [(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int, groupId: String)] {
if PhotoManager.shared.permissionStatus != .authorized {
return []
}
let queryStatementString = "SELECT * FROM groups ORDER BY rowid DESC;"
var queryStatement: OpaquePointer?
var result: [(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int, groupId: String)] = []
......
......@@ -133,6 +133,12 @@ class TrashDatabase {
// 查询所有数据
func queryAll() -> [(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int)] {
if PhotoManager.shared.permissionStatus != .authorized {
return []
}
let queryStatementString = "SELECT * FROM trash;"
var queryStatement: OpaquePointer?
var result: [(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int)] = []
......
//
// AESCrypto.swift
// PhoneManager
//
// Created by edy on 2025/5/21.
//
import Foundation
import CommonCrypto
import CryptoKit
struct AESCrypto {
// 需要修改为与安卓一致的密钥
private static let key = "z1lle6aeg531um0k"
static func encryptGCM(_ string: String) -> String? {
guard let data = string.data(using: .utf8),
let keyData = key.data(using: .utf8) else { return nil }
do {
// CryptoKit 自动生成 nonce,长度可能与安卓不一致
let nonce = AES.GCM.Nonce()
let symmetricKey = SymmetricKey(data: keyData)
let sealedBox = try AES.GCM.seal(data, using: symmetricKey, nonce: nonce)
let combined = sealedBox.combined
return combined?.base64EncodedString()
} catch {
print("加密错误: \(error)")
return nil
}
}
/// AES-GCM 解密方法
/// - Parameter encrypted: 加密后的 base64 字符串(包含 nonce 和认证标签)
/// - Returns: 返回解密后的原始字符串
static func decryptGCM(_ encrypted: String) -> String? {
guard let combinedData = Data(base64Encoded: encrypted),
let keyData = key.data(using: .utf8) else { return nil }
do {
// 创建 SymmetricKey
let symmetricKey = SymmetricKey(data: keyData)
// 从组合数据创建 sealed box
let sealedBox = try AES.GCM.SealedBox(combined: combinedData)
// 解密数据
let decryptedData = try AES.GCM.open(sealedBox, using: symmetricKey)
// 转换为字符串
return String(data: decryptedData, encoding: .utf8)
} catch {
print("解密错误: \(error)")
return nil
}
}
}
//
// APIReportManager.swift
// PhoneManager
//
// Created by edy on 2025/5/21.
//
import Foundation
struct APIReportManager{
static let shared = APIReportManager()
let bundleId = "com.app.phonemanager"
let hostUrl = "https://rp.sayyedandroid.online/phonemanagersp?pkg=com.app.phonemanager"
let DEBUG = true
// 开始上报
func startReport(type:APIReportEnum,ext:[String:Any] = [:]){
let data = getdataBody(action: type.name, ext: ext)
let bp = getbpBody()
let content = [
"data":data,
"bp":bp
]
Print("上报数据",content)
if let json = content.toJSONString(){
guard let aesString = AESCrypto.encryptGCM(json) else{
return
}
reportAPIRequest(for: aesString)
}
}
private func reportAPIRequest(for aesStr:String){
HttpRequest.postBodyWithString(to: hostUrl, bodyValue: aesStr) { result in
switch result {
case .success(let jsonString):
Print("上报成功",jsonString)
case .failure(let failure):
Print("请求异常",failure.localizedDescription)
}
}
}
private func getdataBody(action:String,ext:[String:Any]) ->[String:Any]{
var dic = [
"action":action,
] as? [String:Any]
if ext.count > 0{
dic?["ext"] = ext
}
return dic ?? [:]
}
private func getbpBody() ->[String:String]{
let body = [
"\(bundleId)_3": UIDevice.current.model, //手机型号
"\(bundleId)_4": "Apple", // 手机厂商
"\(bundleId)_5": UIDevice.current.systemVersion, //系统版本号
"\(bundleId)_8": Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "", // APP版本号
"\(bundleId)_9": DeviceIDManager.shared.deviceID, //UUID
"\(bundleId)_13": "iOS", // platform
"\(bundleId)_14": Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "", //版本号
"\(bundleId)_15": "AppStore", //渠道标识
"\(bundleId)_24": DEBUG ? "debug" : "release", //环境 debug:开发环境 release:正式环境
"\(bundleId)_34": TimeZone.current.identifier, //手机本地时区
"\(bundleId)_35": DeviceIDManager.shared.deviceID //UUID
]
return body
}
}
enum APIReportEnum{
// 广告上报枚举
case ad_pull_start
case ad_pull
case ad_prepare_show
case ad_show
case ad_show_error
case ad_limit_error
case ad_click
case ad_price
// app启动
case app_start
// 归因上报枚举
case source_atrribute
case source_atrribute_error
// 订阅枚举
case apple_billing_error
case apple_billing_click
case apple_billing_consume
case apple_billing_success
// 页面展示枚举
case Duplicates_show
case Similar_show
case Videos_show
case SimilarScreenshots_show
case Screenshots_show
case SimilarVideos_show
case Other_show
case SecretSpace_show
case Charging_show
case Compress_show
case EmailCleaner_show
case Contacts_show
}
extension APIReportEnum{
var name: String {
switch self {
// 广告上报枚举
case .ad_pull_start: return "ad_pull_start"
case .ad_pull: return "ad_pull"
case .ad_prepare_show: return "ad_prepare_show"
case .ad_show: return "ad_show"
case .ad_show_error: return "ad_show_error"
case .ad_limit_error: return "ad_limit_error"
case .ad_click: return "ad_click"
case .ad_price: return "ad_price"
case .app_start: return "app_start"
// 归因上报枚举
case .source_atrribute: return "source_atrribute"
case .source_atrribute_error: return "source_atrribute_error"
// 订阅枚举
case .apple_billing_error: return "apple_billing_error"
case .apple_billing_click: return "apple_billing_click"
case .apple_billing_consume: return "apple_billing_consume"
case .apple_billing_success: return "apple_billing_success"
// 页面展示枚举
case .Duplicates_show: return "Duplicates_show"
case .Similar_show: return "Similar_show"
case .Videos_show: return "Videos_show"
case .SimilarScreenshots_show: return "SimilarScreenshots_show"
case .Screenshots_show: return "Screenshots_show"
case .SimilarVideos_show: return "SimilarVideos_show"
case .Other_show: return "Other_show"
case .SecretSpace_show: return "SecretSpace_show"
case .Charging_show: return "Charging_show"
case .Compress_show: return "Compress_show"
case .EmailCleaner_show: return "EmailCleaner_show"
case .Contacts_show: return "Contacts_show"
}
}
}
import Foundation
import KeychainSwift
class DeviceIDManager {
static let shared = DeviceIDManager()
private let keychain = KeychainSwift()
private let deviceIDKey = "com.app.phonemanager.device_unique_id"
private init() {}
var deviceID: String {
// 尝试从 Keychain 获取已存储的 UUID
if let savedID = keychain.get(deviceIDKey) {
return savedID
}
// 如果没有存储过,生成新的 UUID 并保存
let newID = UUID().uuidString
keychain.set(newID, forKey: deviceIDKey)
return newID
}
}
extension Dictionary where Key == String {
func toJSONString() -> String? {
do {
let jsonData = try JSONSerialization.data(withJSONObject: self, options: [])
if let jsonString = String(data: jsonData, encoding: .utf8) {
return jsonString
}
} catch {
print("JSON转换错误: \(error)")
}
return nil
}
}
\ No newline at end of file
......@@ -45,6 +45,22 @@ enum SubscriptionType {
case lifetime
}
enum PayState {
case weekSubscribe
case yearSubscribe
case nonConsumable
var productId:String{
switch self {
case .weekSubscribe:
return "com.app.phonemanager.week.member"
case .yearSubscribe:
return "com.app.phonemanager.year.member"
case .nonConsumable:
return "com.app.phonemanager.lifetime.member"
}
}
}
// MARK: - IAPManager
class IAPManager: NSObject {
......@@ -56,11 +72,8 @@ class IAPManager: NSObject {
private let productRequestTimeout: TimeInterval = 15
private let alert = PMLoadingHUD.share
enum PayState {
case weekSubscribe
case yearSubscribe
case nonConsumable
}
var currentVCName:String = ""
// MARK: - Properties
private struct ProductID {
......@@ -153,36 +166,6 @@ extension IAPManager {
}
// 检查订阅信息
/// - Parameter completion: 回调闭包,返回订阅状态和到期时间
// func checkSubscriptionState(completion: @escaping (_ isSubscribed: Bool, _ expiresDate: Date?) -> Void) {
// verifyReceiptWithApple { result in
// switch result {
// case .success(let receipt):
// // 打印完整收据信息,方便调试
// //print("收据信息:\(receipt)")
// let status = self.checkSubscriptionStatus(receiptInfo: receipt)
// DispatchQueue.main.async {
// completion(status.isActive, status.expiresDate)
// // 更新本地订阅状态
// self.isSubscribed = status.isActive
// #if DEBUG
// if let expDate = status.expiresDate {
// let formatter = DateFormatter()
// formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
// Print("订阅状态:\(status.isActive ? "已订阅" : "未订阅"),到期时间:\(formatter.string(from: expDate))")
// }
// #endif
// }
//
// case .failure(_):
// // 处理错误情况
// self.refreshReceipt()
// completion(false, nil)
// }
// }
// }
// 修改回调方法,增加订阅类型参数
func checkSubscriptionState(completion: @escaping (_ isSubscribed: Bool, _ subscriptionType: SubscriptionType, _ expiresDate: Date?) -> Void) {
verifyReceiptWithApple { result in
......@@ -211,7 +194,8 @@ extension IAPManager {
}
func purchase(_ state: PayState = .weekSubscribe, completion: @escaping (Result<Bool, IAPError>) -> Void) {
if state == .weekSubscribe,weekProduct == nil{
completion(.failure(.noProductsFound))
return
......@@ -245,6 +229,8 @@ extension IAPManager {
payment = SKPayment(product: lifetimeProduct!)
}
APIReportManager.shared.startReport(type: .apple_billing_click, ext: ["productId":payment.productIdentifier,"from":currentVCName])
SKPaymentQueue.default().add(payment)
alert.show("Processing the purchase request...", "")
}
......@@ -516,56 +502,6 @@ extension IAPManager: SKPaymentTransactionObserver {
extension IAPManager{
// func checkSubscriptionStatus(receiptInfo: [String: Any]) -> (isActive: Bool, isTrial: Bool, expiresDate: Date?) {
// // 首先检查 in_app 购买记录
// if let receipt = receiptInfo["receipt"] as? [String: Any],
// let inAppPurchases = receipt["in_app"] as? [[String: Any]] {
// // 检查是否存在永久商品购买记录
// let hasLifetimePurchase = inAppPurchases.contains { purchase in
// return purchase["product_id"] as? String == ProductID.lifetimeMember
// }
//
// if hasLifetimePurchase {
// print("检测到永久商品购买记录")
// return (true, false, nil)
// }
// }
//
// // 如果没有永久商品,继续检查订阅
// guard let latestReceiptInfo = receiptInfo["latest_receipt_info"] as? [[String: Any]] else {
// print("没有找到任何购买记录")
// return (false, false, nil)
// }
//
// // 按过期时间排序,确保获取最新的订阅记录
// let sortedReceipts = latestReceiptInfo.sorted { receipt1, receipt2 in
// let date1 = getExpiryDate(from: receipt1) ?? Date.distantPast
// let date2 = getExpiryDate(from: receipt2) ?? Date.distantPast
// return date1 > date2
// }
//
// guard let lastReceipt = sortedReceipts.first else {
// return (false, false, nil)
// }
//
// // 解析试用期状态
// let isTrial = (lastReceipt["is_trial_period"] as? String) == "true"
//
// // 获取过期时间
// let expiresDate = getExpiryDate(from: lastReceipt)
// let isActive = expiresDate?.compare(Date()) == .orderedDescending
//
// #if DEBUG
// if let expDate = expiresDate {
// let formatter = DateFormatter()
// formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
// print("订阅状态:\(isActive ? "已订阅" : "未订阅"),到期时间:\(formatter.string(from: expDate))")
// }
// #endif
//
// return (isActive, isTrial, expiresDate)
// }
func checkSubscriptionStatus(receiptInfo: [String: Any]) -> (isActive: Bool, isTrial: Bool, subscriptionType: SubscriptionType, expiresDate: Date?) {
// 首先检查 in_app 购买记录
if let receipt = receiptInfo["receipt"] as? [String: Any],
......
//
// HttpRequest.swift
// Weather
//
// Created by apple on 2024/9/23.
//
import Foundation
struct HttpRequest{
static func getFormData<T:Codable>(to urlString: String, completion: @escaping (Result<T, Error>) -> Void) {
// 设置请求的 URL
guard let urlComponents = URLComponents(string: urlString) else {
completion(.failure(NSError(domain: "Invalid URL", code: 101, userInfo: nil)))
return
}
// 创建 URL 请求
guard let url = urlComponents.url else {
completion(.failure(NSError(domain: "Invalid URL Components", code: 102, userInfo: nil)))
return
}
// 创建 URLRequest 对象
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
print("请求地址",url.absoluteString)
// 执行网络请求
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
// 处理错误
if let error = error {
print("Error in request: \(error.localizedDescription)")
DispatchQueue.main.async {
completion(.failure(error))
}
return
}
// 确保有数据返回
guard let data = data else {
print("No data returned")
DispatchQueue.main.async {
completion(.failure(NSError(domain: "No data returned", code: 103, userInfo: nil)))
}
return
}
// 解析响应(如果有的话)
do {
let model = try JSONDecoder().decode(T.self, from: data)
// print("请求数据",model)
DispatchQueue.main.async {
completion(.success(model))
}
} catch {
print("数据解析失败: \(error.localizedDescription)")
DispatchQueue.main.async {
completion(.failure(NSError(domain: "数据解析失败", code: 104, userInfo: nil)))
}
}
}
// 启动任务
task.resume()
}
static func postFormData(to urlString: String, parameters: [String: Any], completion: @escaping (Result<String, Error>) -> Void) {
// 设置请求的 URL
guard let urlComponents = URLComponents(string: urlString) else {
completion(.failure(NSError(domain: "Invalid URL", code: 101, userInfo: nil)))
return
}
// 创建 URL 请求
guard let url = urlComponents.url else {
completion(.failure(NSError(domain: "Invalid URL Components", code: 102, userInfo: nil)))
return
}
// 创建 URLRequest 对象
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
// 将参数字典转换为 JSON 数据
do {
let jsonData = try JSONSerialization.data(withJSONObject: parameters, options: [])
request.httpBody = jsonData
} catch {
completion(.failure(NSError(domain: "参数序列化失败", code: 105, userInfo: nil)))
return
}
print("请求地址", url.absoluteString)
print("请求参数", parameters)
// 执行网络请求
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
// 处理错误
if let error = error {
print("Error in request: \(error.localizedDescription)")
DispatchQueue.main.async {
completion(.failure(error))
}
return
}
// 确保有数据返回
guard let data = data else {
print("No data returned")
DispatchQueue.main.async {
completion(.failure(NSError(domain: "No data returned", code: 103, userInfo: nil)))
}
return
}
// 将返回的数据转换为字符串
if let jsonString = String(data: data, encoding: .utf8) {
DispatchQueue.main.async {
completion(.success(jsonString))
}
} else {
DispatchQueue.main.async {
completion(.failure(NSError(domain: "JSON转换字符串失败", code: 104, userInfo: nil)))
}
}
}
// 启动任务
task.resume()
}
static func postBodyWithString(to urlString: String, bodyValue: String, completion: @escaping (Result<String, Error>) -> Void) {
// 设置请求的 URL
guard let urlComponents = URLComponents(string: urlString) else {
completion(.failure(NSError(domain: "Invalid URL", code: 101, userInfo: nil)))
return
}
// 创建 URL 请求
guard let url = urlComponents.url else {
completion(.failure(NSError(domain: "Invalid URL Components", code: 102, userInfo: nil)))
return
}
// 创建 URLRequest 对象
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
// 直接将字符串转换为Data
guard let data = bodyValue.data(using: .utf8) else {
completion(.failure(NSError(domain: "字符串转换Data失败", code: 105, userInfo: nil)))
return
}
request.httpBody = data
print("请求地址", url.absoluteString)
print("请求参数", bodyValue)
// 执行网络请求
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
// 处理错误
if let error = error {
print("Error in request: \(error.localizedDescription)")
DispatchQueue.main.async {
completion(.failure(error))
}
return
}
// 确保有数据返回
guard let data = data else {
print("No data returned")
DispatchQueue.main.async {
completion(.failure(NSError(domain: "No data returned", code: 103, userInfo: nil)))
}
return
}
// 将返回的数据转换为字符串
if let jsonString = String(data: data, encoding: .utf8) {
DispatchQueue.main.async {
completion(.success(jsonString))
}
} else {
DispatchQueue.main.async {
completion(.failure(NSError(domain: "JSON转换字符串失败", code: 104, userInfo: nil)))
}
}
}
// 启动任务
task.resume()
}
}
import Foundation
// 顶层响应模型
struct ResponseModel<T: Codable>: Codable {
let status: Int
let msg: String
let sign: String
let result: ResultModel<T>
let enc: String?
let security: String
}
// 结果模型
struct ResultModel<T: Codable>: Codable {
let data: T
let extras: String?
}
\ No newline at end of file
......@@ -35,7 +35,8 @@ class ChargeViewController:BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
APIReportManager.shared.startReport(type: .Charging_show)
titleView.addSubview(detailsBtn)
......
......@@ -101,6 +101,7 @@ class CompressController : BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
APIReportManager.shared.startReport(type: .Compress_show)
view.backgroundColor = .white
self.navigationController?.navigationBar.isHidden = true
getViewData()
......
......@@ -30,7 +30,7 @@ class ContactViewController : BaseViewController {
}()
override func viewDidLoad() {
APIReportManager.shared.startReport(type: .Contacts_show)
self.view.backgroundColor = .white
self.view.addSubview(self.navView)
......
......@@ -322,6 +322,7 @@ class HomeInfoViewController:BaseViewController {
self.setDefaultPage()
}
configSelectAll()
}
......@@ -442,6 +443,14 @@ class HomeInfoViewController:BaseViewController {
}
}
func configSelectAll(){
seletedAllBtn.isSelected = true
self.seletedAllBtn.width = seletedAllBtn.isSelected ? 131 : 115
seletedAllBtn.x = titleView.width - marginLR - seletedAllBtn.width
tablewView.changeALlValue(isSeleted: seletedAllBtn.isSelected)
}
@objc func seletedAllBtnClick() {
DispatchQueue.main.async {[weak self] in
......
......@@ -115,7 +115,7 @@ class HomeViewController:BaseViewController {
self.setupUI()
// 调用下追踪权限
checkTrackingAuthorization()
// checkTrackingAuthorization()
homeView = HomeView(frame: view.bounds)
homeView?.y = cWindow?.safeAreaInsets.top ?? 20
......@@ -126,6 +126,11 @@ class HomeViewController:BaseViewController {
guard let model = model else{ return }
DispatchQueue.main.async {
let vc:HomeInfoViewController = HomeInfoViewController(ids: model.assets , type: type,titleText: model.folderName)
if model.folderName == HomeUIEnum.Dublicates.title{
APIReportManager.shared.startReport(type: .Duplicates_show)
}else{
APIReportManager.shared.startReport(type: .Similar_show)
}
self.navigationController?.pushViewController(vc, animated: true)
}
}
......@@ -135,6 +140,7 @@ class HomeViewController:BaseViewController {
if otherItemRow == 0 {
DispatchQueue.main.async {
let vc:HomeVideoDetailController = HomeVideoDetailController()
APIReportManager.shared.startReport(type: .Videos_show)
self.navigationController?.pushViewController(vc, animated: true)
}
}
......@@ -142,12 +148,14 @@ class HomeViewController:BaseViewController {
if otherItemRow == 1 {
DispatchQueue.main.async {
let vc:HomeInfoViewController = HomeInfoViewController(ids: model.assets , type: .similarScreenshots ,titleText: model.folderName)
APIReportManager.shared.startReport(type: .SimilarScreenshots_show)
self.navigationController?.pushViewController(vc, animated: true)
}
}
if otherItemRow == 3 {
DispatchQueue.main.async {
let vc:HomeInfoViewController = HomeInfoViewController(ids: model.assets , type: .SimilarVideos ,titleText: model.folderName)
APIReportManager.shared.startReport(type: .SimilarVideos_show)
self.navigationController?.pushViewController(vc, animated: true)
}
}
......@@ -155,6 +163,11 @@ class HomeViewController:BaseViewController {
if otherItemRow == 2 || otherItemRow == 4{
DispatchQueue.main.async {
let vc = HomePhotosDetailViewController(mediaType: otherItemRow == 2 ? .screenshots : .Other)
if otherItemRow == 2{
APIReportManager.shared.startReport(type: .Screenshots_show)
}else{
APIReportManager.shared.startReport(type: .Other_show)
}
self.navigationController?.pushViewController(vc, animated: true)
}
}
......@@ -224,6 +237,7 @@ class HomeViewController:BaseViewController {
let nav:BaseNavViewController = BaseNavViewController(rootViewController: vc)
nav.modalPresentationStyle = .fullScreen
self.present(nav, animated: true)
break
case .lifetime,.year:
let vc : PayCompletedViewController = PayCompletedViewController()
vc.modalPresentationStyle = .fullScreen
......@@ -292,6 +306,7 @@ extension HomeViewController {
case .authorized:
// 用户已授权跟踪
print("用户已授权应用进行跟踪")
Print("idfa",AdvManager.shared.getIDFA())
case .denied:
// 用户拒绝了跟踪请求
print("用户拒绝了应用的跟踪请求")
......@@ -317,6 +332,7 @@ extension HomeViewController {
case .authorized:
// 用户已授权跟踪
print("用户已授权应用进行跟踪")
Print("idfa",AdvManager.shared.getIDFA())
case .denied:
// 用户拒绝了跟踪请求
print("用户拒绝了应用的跟踪请求")
......
......@@ -315,6 +315,7 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
let model = viewModel.cardGroup[indexPath.row] //model?.otherModelArray[indexPath.row]
return itemWidth + 12 + UILabel.getSizeWith(font: UIFont.systemFont(ofSize: 16, weight: .bold),lineSpacing: 5, width: itemWidth - 32, numberOfLines: 0, content: model.folderName).height
}
}
......
......@@ -205,7 +205,9 @@ extension HomeTitleCollectionCell:UICollectionViewDelegate,UICollectionViewDataS
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// 计算 cell 宽度
return CGSize(width:collectionView.height - 18, height: collectionView.height - 18) // 宽高相等,形成网格
let width = max(0, collectionView.height - 18) // 使用实际的宽度计算方法
let height = max(0,collectionView.height - 18) // 使用实际的高度计算方法
return CGSize(width:width, height: height) // 宽高相等,形成网格
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
......
......@@ -215,12 +215,14 @@ extension HomeVideoCoverCell{
playerLayer.isHidden = false
if videoURL == videoUrl{
Print("地址相同,无需刷新")
// Print("地址相同,无需刷新")
return
}
videoUrl = videoURL
Print("地址不同,需刷新")
// if videoPlayer
// Print("地址不同,需刷新")
let item = AVPlayerItem.init(url: videoURL)
......
......@@ -58,7 +58,7 @@ class MaintaiDetailViewController: BaseViewController {
}
removeBtn = UIButton()
removeBtn.setTitle("Un-keep All", for: .normal)
removeBtn.setTitle("Not-keep All", for: .normal)
removeBtn.addTarget(self, action: #selector(removeAll), for: .touchUpInside)
removeBtn.setTitleColor(.black, for: .normal)
removeBtn.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .semibold)
......@@ -210,25 +210,16 @@ extension MaintaiDetailViewController:UITableViewDelegate,UITableViewDataSource{
return cell
}
if viewModel.souces[indexPath.row].first?.mediaType == 1,viewModel.souces[indexPath.row].count == 1{
let cell = tableView.dequeueReusableCell(withIdentifier: "MaintaiDetailPicCell") as! MaintaiDetailPicCell
cell.model = viewModel.souces[indexPath.row].first
cell.selectBtn.isSelected = selectAsset.contains(viewModel.souces[indexPath.row])
cell.selectChangeBlock = {[weak self] in
guard let weakSelf = self else { return }
weakSelf.dealPickAssetModel(index: indexPath.row)
weakSelf.tableView.reloadData()
}
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "MaintaiDetialVideoCell") as! MaintaiDetialVideoCell
cell.model = viewModel.souces[indexPath.row].first
cell.selectBtn.isSelected = selectAsset.contains(viewModel.souces[indexPath.row])
cell.selectChangeBlock = {[weak self] in
guard let weakSelf = self else { return }
weakSelf.dealPickAssetModel(index: indexPath.row)
weakSelf.tableView.reloadData()
// 不要刷新整个cell,只更新选中状态
if let current = tableView.cellForRow(at: indexPath) as? MaintaiDetialVideoCell{
current.selectBtn.isSelected = weakSelf.selectAsset.contains(weakSelf.viewModel.souces[indexPath.row])
}
}
return cell
......
......@@ -54,7 +54,7 @@ class MaintainViewListController: BaseViewController {
}
removeBtn = UIButton()
removeBtn.setTitle("Un-keep All", for: .normal)
removeBtn.setTitle("Not-keep All", for: .normal)
removeBtn.addTarget(self, action: #selector(removeAll), for: .touchUpInside)
removeBtn.setTitleColor(.black, for: .normal)
removeBtn.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .semibold)
......
......@@ -40,7 +40,7 @@ class MaintaiDetailImageSmallCell: UICollectionViewCell {
selectBtn.setImage(UIImage.init(named: "icon_maintai_unselect_small"), for: .normal)
selectBtn.setImage(UIImage.init(named: "icon_maintai_select_small"), for: .selected)
addSubview(selectBtn)
selectBtn.addTarget(self, action: #selector(selectChange), for: .touchUpInside)
// selectBtn.addTarget(self, action: #selector(selectChange), for: .touchUpInside)
selectBtn.snp.makeConstraints { make in
make.right.bottom.equalTo(0)
make.size.equalTo(30)
......@@ -54,7 +54,7 @@ class MaintaiDetailImageSmallCell: UICollectionViewCell {
}
@objc func selectChange(){
selectChangeBlock?()
// selectChangeBlock?()
}
}
......@@ -75,6 +75,10 @@ class MaintaiDetailTableViewCell: UITableViewCell {
}
deinit{
Print("释放图像cell",self)
}
}
extension MaintaiDetailTableViewCell:UICollectionViewDelegate,UICollectionViewDataSource{
......@@ -96,13 +100,19 @@ extension MaintaiDetailTableViewCell:UICollectionViewDelegate,UICollectionViewDa
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MaintaiDetailImageSmallCell", for: indexPath) as! MaintaiDetailImageSmallCell
cell.model = source[indexPath.row]
cell.selectBtn.isSelected = selectAsset.contains(source[indexPath.row])
cell.selectChangeBlock = {[weak self] in
guard let weakSelf = self else { return }
weakSelf.dealSelect(model: weakSelf.source[indexPath.row])
}
// cell.selectChangeBlock = {[weak self] in
// guard let weakSelf = self else { return }
// weakSelf.dealSelect(model: weakSelf.source[indexPath.row])
// }
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath){
if collectionView == pageCollectionView{
dealSelect(model: source[indexPath.row])
}
}
func dealSelect(model:AssetModel){
......@@ -122,6 +132,7 @@ extension MaintaiDetailTableViewCell:UICollectionViewDelegate,UICollectionViewDa
selectCallBack?(selectAsset)
}
}
extension MaintaiDetailTableViewCell:UIScrollViewDelegate{
......@@ -191,4 +202,6 @@ extension MaintaiDetailTableViewCell:UIScrollViewDelegate{
self.mainCollectionView.contentOffset = CGPoint(x: index * Int(self.bigW), y: 0)
draggView = .null
}
}
......@@ -10,17 +10,12 @@ import AVKit
class MaintaiDetialVideoCell: UITableViewCell {
// private var player: AVPlayer?
// private var playerLayer: AVPlayerLayer?
var selectBtn:UIButton!
var selectChangeBlock:(() ->Void)?
// var videoPath:String?{
// didSet{
// setPlayerInfo()
// }
// }
var playerLayer:AVPlayerLayer!
override func awakeFromNib() {
super.awakeFromNib()
......@@ -44,6 +39,8 @@ class MaintaiDetialVideoCell: UITableViewCell {
}
private func setupPlayer() {
playerLayer = AVPlayerLayer.init(player: videoPlayer)
playerLayer.backgroundColor = UIColor.black.cgColor
contentView.layer.addSublayer(playerLayer)
}
......@@ -76,28 +73,18 @@ class MaintaiDetialVideoCell: UITableViewCell {
}()
lazy var playerLayer:AVPlayerLayer = {
let playerLayer = AVPlayerLayer.init(player: videoPlayer)
playerLayer.backgroundColor = UIColor.black.cgColor
return playerLayer
}()
func configure(with videoURL: URL?) {
guard let videoURL = videoURL else{
videoPlayer.pause()
return
}
if videoPlayer.rate != 0{
return
}
Print("视频url",videoURL)
let item = AVPlayerItem.init(url: videoURL)
videoPlayer.replaceCurrentItem(with: item)
videoPlayer.play()
NotificationCenter.default.addObserver(self,
......@@ -107,17 +94,20 @@ class MaintaiDetialVideoCell: UITableViewCell {
}
@objc private func playerDidFinishPlaying() {
videoPlayer.seek(to: .zero)
videoPlayer.play()
}
@objc func selectChange(){
selectChangeBlock?()
}
deinit{
print("释放",self)
// 停止播放
videoPlayer.pause()
videoPlayer.replaceCurrentItem(with: nil)
// 移除观察者
NotificationCenter.default.removeObserver(self)
}
}
......@@ -8,6 +8,7 @@
import UIKit
import Lottie
import Foundation
import AppTrackingTransparency
class PermissionVC:UIViewController {
......@@ -37,15 +38,15 @@ class PermissionVC:UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
checkTrackingAuthorization()
setupUI()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
isPPClick = false
......@@ -244,3 +245,61 @@ extension PermissionVC: UITextViewDelegate {
return false
}
}
extension PermissionVC {
// 检查跟踪授权状态
func checkTrackingAuthorization() {
if #available(iOS 14, *) {
let status = ATTrackingManager.trackingAuthorizationStatus
switch status {
case .authorized:
// 用户已授权跟踪
print("用户已授权应用进行跟踪")
Print("idfa",AdvManager.shared.getIDFA())
case .denied:
// 用户拒绝了跟踪请求
print("用户拒绝了应用的跟踪请求")
case .restricted:
// 由于系统限制,无法跟踪用户
print("由于系统限制,无法跟踪用户")
case .notDetermined:
// 用户尚未对跟踪请求做出决定
print("用户尚未对跟踪请求做出决定,再次请求授权")
requestTrackingAuthorization()
@unknown default:
break
}
} else {
// iOS 14 以下系统不支持 ATT 框架
// 可以执行其他操作
}
}
func requestTrackingAuthorization() {
if #available(iOS 14, *) {
ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in
switch status {
case .authorized:
// 用户已授权跟踪
print("用户已授权应用进行跟踪")
Print("idfa",AdvManager.shared.getIDFA())
case .denied:
// 用户拒绝了跟踪请求
print("用户拒绝了应用的跟踪请求")
case .restricted:
// 由于系统限制,无法跟踪用户
print("由于系统限制,无法跟踪用户")
case .notDetermined:
// 用户尚未对跟踪请求做出决定
print("用户尚未对跟踪请求做出决定")
@unknown default:
break
}
})
} else {
// iOS 14 以下系统不支持 ATT 框架
// 可以执行其他操作
}
}
}
......@@ -130,17 +130,20 @@ extension HomePayViewController {
}
private func payTouch() -> Void {
IAPManager.share.purchase((homePayView?.type == 0) ? .weekSubscribe : .nonConsumable) {[weak self] result in
let subscribe:PayState = homePayView?.type == 0 ? .weekSubscribe : .nonConsumable
IAPManager.share.purchase(subscribe) {[weak self] result in
guard let weakSelf = self else { return }
switch result {
case .success(let success):
Print("内购成功",success)
DispatchQueue.main.async {
APIReportManager.shared.startReport(type: .apple_billing_success, ext: ["productId":subscribe.productId,"from":IAPManager.share.currentVCName])
DispatchQueue.main.async {
SVProgressHUD.showSuccess(withStatus: "purchase succeeds")
weakSelf.dismiss(animated: true)
}
case .failure(let failure):
Print("内购失败",failure)
APIReportManager.shared.startReport(type: .apple_billing_error, ext: ["productId":subscribe.productId,"info":failure.localizedDescription,"from":IAPManager.share.currentVCName])
DispatchQueue.main.async {
SVProgressHUD.showError(withStatus: failure.localizedDescription)
}
......@@ -180,6 +183,9 @@ extension HomePayViewController {
let nav:BaseNavViewController = BaseNavViewController(rootViewController: vc)
nav.modalPresentationStyle = .fullScreen
guard let rt = UIViewController.topMostViewController() else { return }
// Print(rt.class)
let className = NSStringFromClass(type(of: rt))
IAPManager.share.currentVCName = className
rt.present(nav, animated: true)
}
......
......@@ -57,7 +57,7 @@ class PayDistanceViewController: UIViewController {
let distanceL = UILabel()
distanceL.text = "70% Discount"
distanceL.text = "50% Discount"
distanceL.font = UIFont.systemFont(ofSize: 40, weight: .semibold)
distanceL.textColor = UIColor.colorWithHex(hexStr: "#111111")
scrollView.addSubview(distanceL)
......@@ -74,12 +74,6 @@ class PayDistanceViewController: UIViewController {
yearDistacePrice.text = ""//"$39.99 $19.99 / Year"
scrollView.addSubview(yearDistacePrice)
// let priceAtt = createRichPriceText(
// originalPrice: "$39.99",
// discountedPrice: "$19.99 / Year"
// )
// yearDistacePrice.attributedText = priceAtt
let cancelLabel = UILabel()
cancelLabel.font = UIFont.systemFont(ofSize: 12,weight: .semibold)
cancelLabel.textColor = UIColor.colorWithHex(hexStr: "#0082FF")
......
......@@ -53,6 +53,7 @@ class SecretViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
APIReportManager.shared.startReport(type: .SecretSpace_show)
_ = Resource.isDirect()
if isShow {
self.ShowUI()
......
......@@ -195,6 +195,9 @@ extension TrashViewController:UIScrollViewDelegate{
let data : [AssetModel] = TrashDataManager.getCurrentMediaTypeTrashData(mediaType: info.0)
let type : TrashTypeEnum = info.0
DispatchQueue.main.async {
if self.delBtn == nil{
return
}
if data.count <= 0{
self.delBtn.setTitle("Delete", for: .normal)
self.delBtn.backgroundColor = UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1)
......
......@@ -28,19 +28,23 @@ class TrashContenAssetCell: UICollectionViewCell {
var model:AssetModel?{
didSet{
guard let model = model else{return}
if self.mediaType == .video {
PhotoAndVideoMananger.mananger.getPreImageFromVideo(identifier: model.localIdentifier, completed: { [weak self] image in
guard let self else {return}
DispatchQueue.main.async {
self.assetImage.image = image
}
})
}else {
DispatchQueue.main.async {
self.assetImage.image = PhotoAndVideoMananger.mananger.getImageFromAssetID(id: model.localIdentifier)
}
}
let image = self.mediaType == .video ? "videosmoren" : "photosmoren"
self.assetImage.asset.load(withLocalIdentifier: model.localIdentifier,placeholder: UIImage.init(named: image))
// if self.mediaType == .video {
// PhotoAndVideoMananger.mananger.getPreImageFromVideo(identifier: model.localIdentifier, completed: { [weak self] image in
// guard let self else {return}
// DispatchQueue.main.async {
// self.assetImage.image = image
// }
// })
// }else {
// DispatchQueue.main.async {
// self.assetImage.image = PhotoAndVideoMananger.mananger.getImageFromAssetID(id: model.localIdentifier)
// }
// }
}
}
......
......@@ -70,7 +70,7 @@ class TrashContenView: UIView {
layout.minimumInteritemSpacing = 12
layout.minimumLineSpacing = 12
layout.sectionInset = UIEdgeInsets(top: 20, left: 16, bottom: 0, right: 16)
layout.itemSize = CGSize(width: (ScreenW-56)/3, height: (ScreenW-56)/3)
layout.itemSize = CGSize(width: Int((ScreenW-56)/3), height: Int((ScreenW-56)/3))
collectionView = UICollectionView.init(frame: CGRect.zero,collectionViewLayout: layout)
collectionView.delegate = self
collectionView.backgroundColor = UIColor.colorWithHex(hexStr: "#F2F6FC")
......
......@@ -93,3 +93,20 @@ func GETCURRENTNAV() -> UINavigationController? {
let rt = k?.rootViewController
return (pre as? UINavigationController) ?? ((rt as? UITabBarController)?.selectedViewController as? UINavigationController) ?? (rt as? UINavigationController)
}
func GETCURRENTVC() -> UIViewController? {
let k = UIApplication.shared.windows.filter({$0.isKeyWindow}).first
let pre = k?.rootViewController?.presentedViewController
let rt = k?.rootViewController
return pre ?? ((rt as? UITabBarController)?.selectedViewController) ?? rt
}
func GETCURRENTVCNAME() -> String {
guard let currentVC = GETCURRENTVC() else { return "" }
let className = NSStringFromClass(type(of: currentVC))
// 如果需要去掉模块名,可以用下面这行
// return className.components(separatedBy: ".").last ?? className
return className
}
......@@ -13,11 +13,15 @@ class UserDef:NSObject {
var isShowLanding:Bool = false
var isFirstStart:Bool = true
override init() {
let defaults:UserDefaults = UserDefaults.standard
self.isShowLanding = defaults.value(forKey: "isShowLanding") as? Bool ?? false
self.isFirstStart = defaults.value(forKey: "isFirstStart") as? Bool ?? true
}
func saveUserDefToSandBox() {
......@@ -29,6 +33,16 @@ class UserDef:NSObject {
defaults.synchronize()
}
func saveFirstStart(){
let defaults:UserDefaults = UserDefaults.standard
defaults.setValue(false, forKey: "isFirstStart")
defaults.synchronize()
}
class func saveKeyWithValue(key:String,value:Any) {
UserDefaults.standard.set(value, forKey: key)
......
......@@ -18,6 +18,7 @@ target 'PhoneManager' do
# pod 'GoogleSignIn'
pod 'MJRefresh' , '3.7.9'
pod 'GoogleAPIClientForREST/Gmail'
pod 'Adjust', '~> 5.4.0'
post_install do |installer|
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment