Commit 470c2abd authored by CZ1004's avatar CZ1004

Merge branch 'dev_main' into dev_zhaoqian

* dev_main:
  fix bug
  fix bugs
  合并
  修复崩溃
  优化
  fix bugs
  adjust 数据上报
  优化
parents 3f3bdcb5 28c5d648
......@@ -9,14 +9,15 @@ import WidgetKit
import SwiftUI
let kind: String = "LockSreen"
let kind1: String = "LockSreen1"
let kind: String = "com.app.phonemanager.lockSreenWidget"
let kind1: String = "com.app.phonemanager.lockSreenWidgetBattery"
@main
struct LockSreenBundle: WidgetBundle {
var body: some Widget {
LockStorageSreen(type: lockSreenType(isStorage: true, kind: kind, displayName: "Storage", description: "Monitor your storage space"))
LockStorageSreen(type: lockSreenType(kind: kind1, displayName: "Battery", description: "Monitor your battery"))
LockBattSreen(type: lockSreenType(kind: kind1, displayName: "Battery", description: "Monitor your battery"))
// LockStorageSreen(type: lockSreenType(kind: kind1, displayName: "Battery", description: "Monitor your battery"))
LockBothSreen()
}
}
......@@ -24,7 +25,26 @@ struct LockSreenBundle: WidgetBundle {
struct LockStorageSreen: Widget {
var type:lockSreenType = lockSreenType()
var body: some WidgetConfiguration {
StaticConfiguration(kind: type.kind, provider: LockSreenProvider()) { entry in
StaticConfiguration(kind: kind, provider: LockSreenProvider()) { entry in
if #available(iOS 17.0, *) {
LockSreenEntryView(type: type, entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
} else {
LockSreenEntryView(type: type, entry: entry)
.padding()
.background(Color.clear)
}
}
.configurationDisplayName(type.displayName)
.description(type.description)
.supportedFamilies([.accessoryCircular,.accessoryRectangular])
}
}
struct LockBattSreen: Widget {
var type:lockSreenType = lockSreenType()
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind1, provider: LockSreenProvider()) { entry in
if #available(iOS 17.0, *) {
LockSreenEntryView(type: type, entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
......@@ -41,7 +61,7 @@ struct LockStorageSreen: Widget {
}
struct LockBothSreen: Widget {
let kind: String = "LockSreenBoth"
let kind: String = "com.app.phonemanager.lockSreenWidgetBoth"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: LockSreenProvider()) { entry in
if #available(iOS 17.0, *) {
......
......@@ -10,17 +10,17 @@ import SwiftUI
struct LockSreenProvider: TimelineProvider {
func placeholder(in context: Context) -> LockSreenEntry {
LockSreenEntry(date: .now, widgets: 0, widgetBattery: 0, widgetStorage: 0, widgetAllSpace: 0, widgetUseSpace: 0, batteryState: "Off", isCharging: false)
return LockSreenEntry(date: .now, widgets: 0, widgetBattery: 1, widgetStorage: 1, widgetAllSpace: 1, widgetUseSpace: 1, batteryState: "Off", isCharging: false)
}
func getSnapshot(in context: Context, completion: @escaping (LockSreenEntry) -> ()) {
let entry = LockSreenEntry(date: .now, widgets: 0, widgetBattery: 0, widgetStorage: 0, widgetAllSpace: 0, widgetUseSpace: 0, batteryState: "Off", isCharging: false)
let entry = LockSreenEntry(date: .now, widgets: 1, widgetBattery: 1, widgetStorage: 1, widgetAllSpace: 1, widgetUseSpace: 1, batteryState: "Off", isCharging: false)
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<LockSreenEntry>) -> ()) {
let entry = loadSharedData()
let timeline = Timeline(entries: [entry], policy: .after(Date().addingTimeInterval(60)))
let timeline = Timeline(entries: [entry], policy: .after(Date().addingTimeInterval(100)))
completion(timeline)
}
......@@ -29,18 +29,19 @@ struct LockSreenProvider: TimelineProvider {
let data:Data = sharedDefaults.object(forKey: "widgetSharedData") as? Data,
let decodedData = try? JSONDecoder().decode(WidgetData.self, from: data)
else {
return LockSreenEntry(date: Date(), widgets: 0, widgetBattery: 0, widgetStorage: 0 ,widgetAllSpace: 0 ,widgetUseSpace: 0 ,batteryState: "Off" , isCharging: false)
return LockSreenEntry(date: Date(), widgets: 0, widgetBattery: 0, widgetStorage: 0 ,widgetAllSpace: 1 ,widgetUseSpace: 0 ,batteryState: "Off" , isCharging: false)
}
return LockSreenEntry(date: Date(), widgets: decodedData.widget, widgetBattery: decodedData.battery, widgetStorage: decodedData.widgetStorage,widgetAllSpace: decodedData.AllSpace,widgetUseSpace: decodedData.UseSpace ,batteryState: decodedData.batteryState ,isCharging: decodedData.isCharging)
}
}
struct WidgetData: Codable {
var userId: String
var widget: Int
var battery: Int
var widgetStorage: Int
var widgetStorage :Int
var AllSpace: Int
var UseSpace: Int
let batteryState: String
......
......@@ -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,32 @@ 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))
//初始化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
......@@ -61,6 +76,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
}
......@@ -142,6 +173,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,15 +7,16 @@
import Foundation
import UIKit
import AdjustSdk
extension AppDelegate{
func setupDynamicShortcuts() {
let shortcutItem = UIApplicationShortcutItem(
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"),
localizedTitle: "🎁 Unlock Special Offers",
localizedSubtitle: "A Special Offer Just for You!",
icon: UIApplicationShortcutIcon(systemImageName: "chevron.right"), //UIApplicationShortcutIcon(templateImageName: "icon_gift_sa"), //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)
}
}
{
"images" : [
{
"filename" : "ic_close_similar.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_close_similar@2x.png",
"filename" : "Frame_1171276341@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_close_similar@3x.png",
"filename" : "Frame_1171276341@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
......
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group_1171275227@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_1171275227@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group_1171275228@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_1171275228@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "ic_close_similar.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_close_similar@2x.png",
"filename" : "Frame_1171276341@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_close_similar@3x.png",
"filename" : "Frame_1171276341@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
......
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "优惠图标@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "优惠图标@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "ic_list_setting.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_list_setting@2x.png",
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_list_setting@3x.png",
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
......
......@@ -5,12 +5,12 @@
"scale" : "1x"
},
{
"filename" : "Group_1@2x.png",
"filename" : "Group_1171275249@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group_1@3x.png",
"filename" : "Group_1171275249@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
......
......@@ -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],
......
......@@ -121,9 +121,9 @@ class PhotoManager{
private(set) var otherTotalSize:Int64 = 0
var trash = TrashDatabase.shared.queryAll().compactMap{$0.localIdentifier}
var trash:[String] = []
var keep = GroupDatabase.shared.queryAll().compactMap{$0.localIdentifier}
var keep:[String] = []
private var currentPage: Int = 0
private let pageSize: Int = 50 // 每次加载的数量
......
//
// 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)
......
......@@ -71,6 +71,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)
......
......@@ -38,7 +38,7 @@ class CustomDupHeaderView : UITableViewHeaderFooterView {
if self.selectStatus {
selectLabel.text = "Select All"
}else{
selectLabel.text = "Deselect All"
selectLabel.text = "Unselect all"
}
let tap = UITapGestureRecognizer()
......@@ -78,7 +78,7 @@ extension CustomDupHeaderView {
@objc func cellSelectTap(){
if self.selectStatus {
self.subLabel.text = "Deselect All"
self.subLabel.text = "Unselect all"
}else {
self.subLabel.text = "Select All"
}
......
......@@ -86,7 +86,7 @@ class SelectAllButton : UIView {
private func updateText() {
imageView.image = isSelected ? UIImage(named: "ic_close_similar") : UIImage(named: "ic_check_similar")
label.textColor = isSelected ? UIColor(red: 0.4, green: 0.4, blue: 0.4, alpha: 1) : UIColor(red: 0, green: 0.51, blue: 1, alpha: 1)
label.text = isSelected ? "Deselect All" : "Select All"
label.text = isSelected ? "Unselect all" : "Select All"
// 强制布局更新以适应新文本
setNeedsLayout()
......
......@@ -201,7 +201,7 @@ extension ContactDupNormalView : UITableViewDelegate,UITableViewDataSource{
if let tempData = self.selectData[String(section)] {
if tempData.count == self.dataSourceModel[section].count{
// 改变UI
view.subLabel.text = "Deselect All"
view.subLabel.text = "Unselect all"
}else {
view.subLabel.text = "Select All"
}
......
//
// PMKeepListAlert.swift
// PhoneManager
//
// Created by edy on 2025/5/21.
//
import UIKit
class PMKeepListAlert: UIViewController {
var CallBlock:(()->Void)?
class var isHint:Bool{
set {
UserDefaults.standard.set(newValue, forKey: "PMKeepListAlertKey")
UserDefaults.standard.synchronize()
}
get{
return UserDefaults.standard.object(forKey: "PMKeepListAlertKey") as? Bool ?? false
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .clear
}
@IBAction func sureActions(_ sender: Any) {
guard let call = CallBlock else { return }
call()
self.dismiss(animated: true)
}
func show(_ call:@escaping (()->Void)) -> Void {
CallBlock = call
self.modalPresentationStyle = .overFullScreen
cWindow?.rootViewController?.present(self, animated: true)
}
}
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="PMKeepListAlert" customModule="PhoneManager" customModuleProvider="target">
<connections>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="B1J-nX-M3q">
<rect key="frame" x="28" y="293.66666666666669" width="337" height="265.00000000000006"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="Ueh-B7-hWk">
<rect key="frame" x="0.0" y="24" width="337" height="217"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_list_setting" translatesAutoresizingMaskIntoConstraints="NO" id="rs0-yB-u5r">
<rect key="frame" x="142.33333333333334" y="0.0" width="52.333333333333343" height="52"/>
</imageView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalCentering" spacing="24" translatesAutoresizingMaskIntoConstraints="NO" id="h9O-7b-s0R">
<rect key="frame" x="45" y="72" width="247" height="145"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="eX4-Ad-TS2">
<rect key="frame" x="0.0" y="0.0" width="247" height="75"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="It has been added to the reserved list" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7UJ-3b-QMP">
<rect key="frame" x="0.0" y="0.0" width="247" height="38.333333333333336"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
<color key="textColor" red="0.066666666666666666" green="0.066666666666666666" blue="0.066666666666666666" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="This photo will no longer be shown to you. You can manage this list in &quot;Settings&quot;." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eHU-Fj-F18">
<rect key="frame" x="0.0" y="46.333333333333314" width="247" height="28.666666666666671"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="12"/>
<color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZWx-A7-NHk">
<rect key="frame" x="0.0" y="99" width="247" height="46"/>
<color key="backgroundColor" red="0.0" green="0.50980392156862742" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="247" id="1T6-ht-7hA"/>
<constraint firstAttribute="height" constant="46" id="zii-lK-niY"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="18"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Sure"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="Radius">
<real key="value" value="23"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="sureActions:" destination="-1" eventType="touchUpInside" id="ruW-6q-iyj"/>
</connections>
</button>
</subviews>
</stackView>
</subviews>
</stackView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="Ueh-B7-hWk" secondAttribute="bottom" constant="24" id="2L7-RI-FEX"/>
<constraint firstItem="Ueh-B7-hWk" firstAttribute="top" secondItem="B1J-nX-M3q" secondAttribute="top" constant="24" id="OSF-Th-BAm"/>
<constraint firstItem="Ueh-B7-hWk" firstAttribute="leading" secondItem="B1J-nX-M3q" secondAttribute="leading" id="xnt-Zp-k8L"/>
<constraint firstAttribute="trailing" secondItem="Ueh-B7-hWk" secondAttribute="trailing" id="znQ-8r-LKQ"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="Radius">
<real key="value" value="12"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="B1J-nX-M3q" firstAttribute="centerY" secondItem="i5M-Pr-FkT" secondAttribute="centerY" id="GEa-l1-i6T"/>
<constraint firstItem="fnl-2z-Ty3" firstAttribute="trailing" secondItem="B1J-nX-M3q" secondAttribute="trailing" constant="28" id="baG-Ab-XsQ"/>
<constraint firstItem="B1J-nX-M3q" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" constant="28" id="xNr-Ec-FCf"/>
</constraints>
<point key="canvasLocation" x="131" y="-11"/>
</view>
</objects>
<resources>
<image name="ic_list_setting" width="52.333332061767578" height="52"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
//
// PMSwipHintView.swift
// PhoneManager
//
// Created by edy on 2025/5/21.
//
import UIKit
class PMSwipHintView: UIView {
class func xib() -> PMSwipHintView {
let nib = UINib(nibName: "PMSwipHintView", bundle: nil).instantiate(withOwner: nil).first as! PMSwipHintView
nib.frame = CGRectMake(0, 0, ScreenW, ScreenH)
return nib
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
UIView.animate(withDuration: AniDuration-0.2) {
self.alpha = 0
} completion: { su in
self.removeFromSuperview()
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="FIR-bL-suQ" customClass="PMSwipHintView" customModule="PhoneManager" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" spacing="35" translatesAutoresizingMaskIntoConstraints="NO" id="bdi-4y-If1">
<rect key="frame" x="125.66666666666667" y="286.66666666666669" width="141.66666666666663" height="279.00000000000006"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" spacing="12" translatesAutoresizingMaskIntoConstraints="NO" id="fI1-sX-2fc">
<rect key="frame" x="0.0" y="0.0" width="141.66666666666666" height="122"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Group_1171275227" translatesAutoresizingMaskIntoConstraints="NO" id="Aof-Vb-mCI">
<rect key="frame" x="0.0" y="0.0" width="141.66666666666666" height="90"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" usesAttributedText="YES" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jIo-6A-Wmj">
<rect key="frame" x="0.0" y="102" width="141.66666666666666" height="20"/>
<attributedString key="attributedText">
<fragment content="Swipe left to ">
<attributes>
<color key="NSColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<font key="NSFont" metaFont="system" size="18"/>
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment content="delete">
<attributes>
<color key="NSColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<font key="NSFont" metaFont="system" size="18"/>
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
</attributedString>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" spacing="12" translatesAutoresizingMaskIntoConstraints="NO" id="VQh-MR-znh">
<rect key="frame" x="0.0" y="157" width="141.66666666666666" height="122"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Group_1171275228" translatesAutoresizingMaskIntoConstraints="NO" id="bNs-p1-kQ0">
<rect key="frame" x="0.0" y="0.0" width="141.66666666666666" height="90"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" usesAttributedText="YES" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xwG-Xf-9nh">
<rect key="frame" x="0.0" y="101.99999999999994" width="141.66666666666666" height="20"/>
<attributedString key="attributedText">
<fragment content="Right swipe to ">
<attributes>
<color key="NSColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<font key="NSFont" metaFont="system" size="18"/>
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment content="save">
<attributes>
<color key="NSColor" red="0.32156862745098036" green="0.7803921568627451" blue="0.46274509803921571" alpha="1" colorSpace="calibratedRGB"/>
<font key="NSFont" metaFont="system" size="18"/>
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
</attributedString>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
</subviews>
</stackView>
</subviews>
<viewLayoutGuide key="safeArea" id="kYu-v0-w8e"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.69815582482993188" colorSpace="custom" customColorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="bdi-4y-If1" firstAttribute="centerX" secondItem="FIR-bL-suQ" secondAttribute="centerX" id="o0G-df-lDw"/>
<constraint firstItem="bdi-4y-If1" firstAttribute="centerY" secondItem="FIR-bL-suQ" secondAttribute="centerY" id="wCr-gG-BXr"/>
</constraints>
<point key="canvasLocation" x="131" y="-13"/>
</view>
</objects>
<resources>
<image name="Group_1171275227" width="102.33333587646484" height="90"/>
<image name="Group_1171275228" width="102.33333587646484" height="90"/>
</resources>
</document>
......@@ -23,28 +23,32 @@ class VideoViewController: UIViewController {
setupPlayer()
}
var player:AVPlayer?
func setupPlayer() {
// 1. 创建播放器
if let url = self.url {
let player = AVPlayer(url: url)
player.isMuted = true
// 2. 配置播放器控制器
playerViewController.player = player
playerViewController.delegate = self
playerViewController.showsPlaybackControls = true
// 3. 添加到当前视图
addChild(playerViewController)
view.addSubview(playerViewController.view)
playerViewController.view.frame = view.bounds
playerViewController.didMove(toParent: self)
// 4. 自动播放
if self.isAutoPlay {
player.play()
self.player = AVPlayer(url: url)
self.player?.isMuted = true
self.player?.volume = 0
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
// 2. 配置播放器控制器
self.playerViewController.player = self.player
self.playerViewController.delegate = self
self.playerViewController.showsPlaybackControls = true
self.playerViewController.player?.isMuted = true
// 3. 添加到当前视图
self.addChild(self.playerViewController)
self.view.addSubview(self.playerViewController.view)
self.playerViewController.view.frame = self.view.bounds
self.playerViewController.didMove(toParent: self)
// 4. 自动播放
if self.isAutoPlay {
self.player?.play()
self.player?.isMuted = true
}
}
}
}
}
......
......@@ -21,7 +21,7 @@ class HomeInfoViewController:BaseViewController {
btn.setImage(UIImage.init(named: "ic_check_similar"), for: .normal)
btn.setTitle("Select All", for: .normal)
btn.setImage(UIImage.init(named: "ic_close_similar"), for: .selected)
btn.setTitle("Deselect All", for: .selected)
btn.setTitle("Unselect all", for: .selected)
btn.setTitleColor(UIColor.colorWithHex(hexStr: mColor), for: .normal)
btn.setTitleColor(UIColor.colorWithHex(hexStr: black3Color), for: .selected)
btn.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .bold)
......@@ -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
......@@ -261,6 +275,8 @@ class HomeViewController:BaseViewController {
isShowCharge = true
}
homeView?.viewModel.reloadTrashAndKeep()
}
override func viewDidAppear(_ animated: Bool) {
......@@ -290,6 +306,7 @@ extension HomeViewController {
case .authorized:
// 用户已授权跟踪
print("用户已授权应用进行跟踪")
Print("idfa",AdvManager.shared.getIDFA())
case .denied:
// 用户拒绝了跟踪请求
print("用户拒绝了应用的跟踪请求")
......@@ -315,6 +332,7 @@ extension HomeViewController {
case .authorized:
// 用户已授权跟踪
print("用户已授权应用进行跟踪")
Print("idfa",AdvManager.shared.getIDFA())
case .denied:
// 用户拒绝了跟踪请求
print("用户拒绝了应用的跟踪请求")
......
......@@ -26,6 +26,16 @@ class PhotoRemoveViewController: BaseViewController {
}
}
var isHints:Bool{
set {
UserDefaults.standard.set(newValue, forKey: "swipIsShowKey")
UserDefaults.standard.synchronize()
}
get{
return UserDefaults.standard.object(forKey: "swipIsShowKey") as? Bool ?? false
}
}
private var bottomConstraint: Constraint?
let formatter = DateFormatter()
......@@ -227,6 +237,12 @@ class PhotoRemoveViewController: BaseViewController {
self.view.addSubview(self.trashButton)
self.view.addSubview(self.keepListButton)
if isHints == false {
isHints = true
self.view.addSubview(PMSwipHintView.xib())
}
}
// 拿到当前类型垃圾桶数据的最后一个
......@@ -420,6 +436,16 @@ class PhotoRemoveViewController: BaseViewController {
Singleton.shared.keepList[type] = [self.dataModel[currentIndex]]
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.31) {
if PMKeepListAlert.isHint == false {
PMKeepListAlert.isHint = true
let alert = PMKeepListAlert()
self.view.showBlur()
alert.show {
self.view.hideBlur()
}
}
}
}
func clearSigtonTrashData(){
......
......@@ -407,7 +407,14 @@ extension HomeInfoView:UITableViewDataSource,UITableViewDelegate {
self.keepAllNoDataCallBack()
}
}
if PMKeepListAlert.isHint == false {
PMKeepListAlert.isHint = true
let alert = PMKeepListAlert()
self.responderViewController()?.view.showBlur()
alert.show {
self.responderViewController()?.view.hideBlur()
}
}
}
cell.type = self.type
......
......@@ -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
}
}
......
......@@ -18,7 +18,6 @@ class PhotosRemoveBaseView: UIView {
var innerVideoController : VideoViewController?
func reload(index:Int) -> Void {
if let url = url {
if self.innerVideoController == nil {
......@@ -27,13 +26,23 @@ class PhotosRemoveBaseView: UIView {
vc.isAutoPlay = index == 0
vc.url = url
self.imageView.removeFromSuperview()
self.addSubview(vc.view)
vc.view.snp.makeConstraints { make in
make.left.top.bottom.right.equalToSuperview()
}
vc.setupPlayer()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
vc.playerViewController.player?.isMuted = true
vc.playerViewController.player?.volume = 0
vc.player?.isMuted = true
vc.player?.volume = 0
}
}
}else {
self.innerVideoController?.playerViewController.player?.play()
self.innerVideoController?.player?.isMuted = true
self.innerVideoController?.player?.volume = 0
}
}
}
......
......@@ -37,7 +37,7 @@ class VideoDetaiNavView : UIView {
btn.setImage(UIImage.init(named: "ic_check_similar"), for: .normal)
btn.setTitle("Select All", for: .normal)
btn.setImage(UIImage.init(named: "ic_close_similar"), for: .selected)
btn.setTitle("Deselect All", for: .selected)
btn.setTitle("Unselect all", for: .selected)
btn.setTitleColor(UIColor.colorWithHex(hexStr: mColor), for: .normal)
btn.setTitleColor(UIColor.colorWithHex(hexStr: black3Color), for: .selected)
btn.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .bold)
......
......@@ -58,7 +58,7 @@ class HomeInfoTableViewCell:UITableViewCell {
seletedAllBtn = UIButton()
seletedAllBtn?.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .bold)
seletedAllBtn?.setTitle("Select All", for: .normal)
seletedAllBtn?.setTitle("Deselect All", for: .selected)
seletedAllBtn?.setTitle("Unselect all", for: .selected)
seletedAllBtn?.setTitleColor(UIColor.colorWithHex(hexStr: mColor), for: .normal)
seletedAllBtn?.sizeToFit()
......
......@@ -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 {
......
......@@ -180,6 +180,7 @@ class HomeVideoCoverCell: UICollectionViewCell {
lazy var videoPlayer:AVPlayer = {
let palyer = AVPlayer.init()
palyer.volume = 0
palyer.isMuted = true
// 设置音频会话,允许混音
try? AVAudioSession.sharedInstance().setCategory(.playback, options: .mixWithOthers)
try? AVAudioSession.sharedInstance().setActive(true)
......@@ -214,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)
......
......@@ -39,8 +39,9 @@ class MaintaiDetailImageSmallCell: UICollectionViewCell {
selectBtn = UIButton()
selectBtn.setImage(UIImage.init(named: "icon_maintai_unselect_small"), for: .normal)
selectBtn.setImage(UIImage.init(named: "icon_maintai_select_small"), for: .selected)
selectBtn.isUserInteractionEnabled = false
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 +55,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)
}
}
......@@ -16,25 +16,25 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SyR-iC-5WC">
<rect key="frame" x="16" y="301" width="361" height="250"/>
<rect key="frame" x="16" y="284.66666666666663" width="361" height="282.66666666666663"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_list_setting" translatesAutoresizingMaskIntoConstraints="NO" id="aer-UI-Hdf">
<rect key="frame" x="166.66666666666666" y="22" width="28" height="28"/>
<rect key="frame" x="154.33333333333334" y="20" width="52.333333333333343" height="52"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stop retaining all photos?" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ITE-9H-tkI">
<rect key="frame" x="84.333333333333329" y="74" width="192.66666666666669" height="19.333333333333329"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
<rect key="frame" x="64.333333333333329" y="96" width="232.66666666666669" height="24"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
<color key="textColor" red="0.066666666669999999" green="0.066666666669999999" blue="0.066666666669999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="The retained list will be cleared and all photos will be displayed in the next scan." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="35j-Q4-9L6">
<rect key="frame" x="26" y="97.333333333333314" width="309" height="28.666666666666671"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="12"/>
<rect key="frame" x="26" y="124.00000000000001" width="309" height="33.666666666666671"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FLo-zT-dpO">
<rect key="frame" x="73" y="150" width="215" height="43"/>
<rect key="frame" x="73" y="181.66666666666663" width="215" height="43"/>
<color key="backgroundColor" red="0.0" green="0.50980392159999999" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="43" id="1O1-hE-Gz9"/>
......@@ -53,7 +53,11 @@
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="68F-oy-zob">
<rect key="frame" x="157" y="205" width="47" height="29"/>
<rect key="frame" x="73" y="229.66666666666669" width="215" height="43"/>
<constraints>
<constraint firstAttribute="width" constant="215" id="37L-vp-Feq"/>
<constraint firstAttribute="height" constant="43" id="c9Y-uS-7rN"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="14"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Cancel">
......@@ -66,16 +70,16 @@
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="68F-oy-zob" secondAttribute="bottom" constant="10" id="4AF-7b-M1O"/>
<constraint firstItem="ITE-9H-tkI" firstAttribute="top" secondItem="aer-UI-Hdf" secondAttribute="bottom" constant="24" id="4n6-Nx-JEk"/>
<constraint firstItem="FLo-zT-dpO" firstAttribute="centerX" secondItem="35j-Q4-9L6" secondAttribute="centerX" id="Btb-F7-pNk"/>
<constraint firstItem="FLo-zT-dpO" firstAttribute="top" secondItem="35j-Q4-9L6" secondAttribute="bottom" constant="24" id="D5x-HU-13q"/>
<constraint firstAttribute="height" constant="250" id="ROB-W2-QK9"/>
<constraint firstAttribute="trailing" secondItem="35j-Q4-9L6" secondAttribute="trailing" constant="26" id="XKK-A0-h6s"/>
<constraint firstItem="68F-oy-zob" firstAttribute="centerX" secondItem="FLo-zT-dpO" secondAttribute="centerX" id="dfa-Sn-bR1"/>
<constraint firstItem="aer-UI-Hdf" firstAttribute="centerX" secondItem="SyR-iC-5WC" secondAttribute="centerX" id="gaW-p7-bEw"/>
<constraint firstItem="68F-oy-zob" firstAttribute="top" secondItem="FLo-zT-dpO" secondAttribute="bottom" constant="12" id="iwJ-qh-Bcp"/>
<constraint firstItem="68F-oy-zob" firstAttribute="top" secondItem="FLo-zT-dpO" secondAttribute="bottom" constant="5" id="iwJ-qh-Bcp"/>
<constraint firstItem="ITE-9H-tkI" firstAttribute="centerX" secondItem="aer-UI-Hdf" secondAttribute="centerX" id="pPT-En-OiR"/>
<constraint firstItem="aer-UI-Hdf" firstAttribute="top" secondItem="SyR-iC-5WC" secondAttribute="top" constant="22" id="qQG-VB-7uJ"/>
<constraint firstItem="aer-UI-Hdf" firstAttribute="top" secondItem="SyR-iC-5WC" secondAttribute="top" constant="20" id="qQG-VB-7uJ"/>
<constraint firstItem="35j-Q4-9L6" firstAttribute="top" secondItem="ITE-9H-tkI" secondAttribute="bottom" constant="4" id="xdP-7n-d3I"/>
<constraint firstItem="35j-Q4-9L6" firstAttribute="leading" secondItem="SyR-iC-5WC" secondAttribute="leading" constant="26" id="yW5-v3-r3e"/>
</constraints>
......@@ -97,7 +101,7 @@
</view>
</objects>
<resources>
<image name="ic_list_setting" width="28.333333969116211" height="28"/>
<image name="ic_list_setting" width="52.333332061767578" height="52"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
......
......@@ -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 框架
// 可以执行其他操作
}
}
}
......@@ -55,7 +55,7 @@
<constraint firstAttribute="width" constant="85" id="6LD-4Y-C7t" customClass="ScreenWidthRatioConstraint" customModule="PhoneManager" customModuleProvider="target"/>
<constraint firstAttribute="height" constant="21" id="YL8-Mf-YdS" customClass="ScreenWidthRatioConstraint" customModule="PhoneManager" customModuleProvider="target"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="12"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
......
......@@ -208,8 +208,9 @@ class HomePayView:UIView {
self.addSubview(contentView2!)
contentView2?.snp.makeConstraints({ make in
make.top.equalTo(tipsView.snp.bottom).offset(12)
make.centerX.equalToSuperview()
make.width.equalToSuperview().offset(-30)
// make.centerX.equalToSuperview()
// make.width.equalToSuperview().offset(-32)
make.left.right.equalToSuperview().inset(15)
make.height.equalTo(60.RH())
})
payButton?.snp.makeConstraints({ make in
......@@ -244,7 +245,7 @@ class HomePayView:UIView {
contentView2Switch?.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-16)
make.centerY.equalToSuperview()
make.width.equalTo(69)
make.width.equalTo(46)
make.height.equalTo(28)
}
contentView2Switch?.addTarget(self, action: #selector(switchValueChanged(_:)), for: .valueChanged)
......@@ -422,7 +423,7 @@ class HomePayView:UIView {
trailTitle = UILabel()
trailTitle.text = "" //"Free for 7 days, then $6.99/week"
trailTitle.textColor = UIColor.colorWithHex(hexStr: black3Color)
trailTitle.textColor = UIColor.colorWithHex(hexStr: black6Color)
trailTitle.font = UIFont.scaledSystemFont(ofSize: 12, weight: .regular)
tipsView.addSubview(trailTitle)
trailTitle.snp.makeConstraints { make in
......
......@@ -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)
}
......
......@@ -37,6 +37,13 @@ class PMShowVideoCell: UICollectionViewCell {
}
}
}
selectBtn.snp.updateConstraints { make in
// if type == 0 {
// make.bottom.equalToSuperview().offset(-10)
// }else{
make.bottom.equalToSuperview().offset(-40)
// }
}
}
override init(frame: CGRect) {
......
......@@ -31,22 +31,27 @@ class SecretSetViewController: BaseViewController, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.titleView.backBtn.setImage(UIImage(named: "ic_close_charging"), for: .normal)
self.titleView.model.isBackBtnShow = true
titleView.model.title = "Secret Library"
titleView.titleLabel.textColor = .black
titleView.titleLabel.textAlignment = .left
setUI()
if secretType != .verify || (SettingConfiguration.share.config.faceId ?? false ) == false {
ps.becomeFirstResponder()
}else{
self.faceID()
}
collect.register(SecretSetPsCell.self, forCellWithReuseIdentifier: SecretSetPsCellID)
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
if self.secretType != .verify || (SettingConfiguration.share.config.faceId ?? false ) == false {
self.ps.becomeFirstResponder()
}else{
self.faceID()
}
}
}
func faceID() -> Void {
if PMFaceIDManger.request() {
PMFaceIDManger.authenticateWithFaceID(reason: "Unlock privacy space") {[weak self] success, error in
......
......@@ -53,6 +53,7 @@ class SecretViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
APIReportManager.shared.startReport(type: .SecretSpace_show)
_ = Resource.isDirect()
if isShow {
self.ShowUI()
......@@ -86,6 +87,8 @@ class SecretViewController: BaseViewController {
}else{
secretLock.setImage(UIImage(named: "ic_lock_secret"), for: .normal)
}
self.loadData()
self.secretCollect.reloadData()
}
@discardableResult
......@@ -122,16 +125,16 @@ class SecretViewController: BaseViewController {
self.dataSource = self.loadData()
self.bottomm.state = .add
}
if SettingConfiguration.share.config.removeImg {
// if SettingConfiguration.share.config.removeImg {
PMAlert(messsage:"These photos or videos will be deleted from your private space" , action: ["Cancel","Delete"] , complate: { alert, selectidx in
if selectidx == 1 {
DelCallBlock()
}
alert.dismiss(animated: true)
})
}else{
DelCallBlock()
}
// }else{
// DelCallBlock()
// }
}
@objc private func lockTouch(_ sender:UIButton) -> Void {
......@@ -141,7 +144,6 @@ class SecretViewController: BaseViewController {
set.Callback = { cr in
set.dismiss(animated: true)
}
set.modalPresentationStyle = .fullScreen
self.present(set, animated: true)
}else{
......@@ -240,9 +242,9 @@ class SecretViewController: BaseViewController {
private lazy var bottomm: SecretBottomView = {
let b = SecretBottomView()
view.addSubview(b)
b.callback = { [weak self] in
b.callback = { [weak self] idx in
guard let self = self else { return }
if b.state == .add {
if idx < 0 {
if IAPManager.share.isSubscribed == false {
// 添加的时候需要先弹出广告
if AdvManager.shared.advTimeAfterInAPP <= 0{
......@@ -275,6 +277,15 @@ class SecretViewController: BaseViewController {
self.AddImagePicker(idx)
}
}
}else if idx == 0 {
let all = self.selectArray.allObjects.sorted { a, b in
guard let ia = a as? Int,
let ib = b as? Int else { return false }
return ia > ib
}
if let name = self.dataSource.first {
}
}else{
// 删除图片的时候不弹广告和内购
self.deleteData()
......@@ -294,7 +305,7 @@ class SecretViewController: BaseViewController {
btn.setImage(UIImage.init(named: "ic_check_similar"), for: .normal)
btn.setTitle("Select All", for: .normal)
btn.setImage(UIImage.init(named: "ic_close_similar"), for: .selected)
btn.setTitle("Deselect All", for: .selected)
btn.setTitle("Unselect all", for: .selected)
btn.setTitleColor(UIColor.colorWithHex(hexStr: mColor), for: .normal)
btn.setTitleColor(UIColor.colorWithHex(hexStr: black3Color), for: .selected)
btn.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .bold)
......
......@@ -16,11 +16,16 @@ class SecretBottomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(stack)
addSubview(but)
but.snp.makeConstraints { make in
make.height.equalTo(46)
make.top.left.right.equalToSuperview().inset(15)
}
stack.snp.makeConstraints { make in
make.left.right.top.equalToSuperview().inset(15)
make.height.equalTo(46)
}
layer.insertSublayer(clayer, at: 0)
}
......@@ -39,11 +44,21 @@ class SecretBottomView: UIView {
return l
}()
var callback:(()->Void)?
var callback:((Int)->Void)?
@objc func AddNew() -> Void {
guard (callback != nil) else {return}
callback!()
callback!(-1)
}
@objc func shareAction() -> Void {
guard (callback != nil) else {return}
callback!(0)
}
@objc func delAction() -> Void {
guard (callback != nil) else {return}
callback!(1)
}
private lazy var but: UIButton = {
......@@ -57,15 +72,54 @@ class SecretBottomView: UIView {
return b
}()
private lazy var share: UIButton = {
let b = UIButton(type: .custom)
b.backgroundColor = .colorWithHex(hexStr: "#F2F6FC")
b.layer.cornerRadius = 46/2.0
b.clipsToBounds = true
b.setTitle("Share", for: .normal)
b.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .medium)
b.setImage(UIImage(named: "ic_Share_secret"), for: .normal)
b.setTitleColor(UIColor.colorWithHex(hexStr: "#333333"), for: .normal)
b.addTarget(self, action: #selector(shareAction), for: .touchUpInside)
return b
}()
private lazy var del: UIButton = {
let b = UIButton(type: .custom)
b.backgroundColor = .colorWithHex(hexStr: "#F2F6FC")
b.layer.cornerRadius = 46/2.0
b.clipsToBounds = true
b.setTitle("Delete", for: .normal)
b.setTitleColor(UIColor.colorWithHex(hexStr: "#333333"), for: .normal)
b.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .medium)
b.setImage(UIImage(named: "ic_Del_secret"), for: .normal)
b.addTarget(self, action: #selector(delAction), for: .touchUpInside)
return b
}()
private lazy var stack: UIStackView = {
let st = UIStackView()
st.isHidden = true
addSubview(st)
st.spacing = 15
st.distribution = .fillEqually
st.addArrangedSubview(share)
st.addArrangedSubview(del)
return st
}()
var state:SecretState = .add {
didSet{
if state == .add {
but.setTitle("Add New", for: .normal)
but.setImage(UIImage(named: "ic_add_secret"), for: .normal)
}else{
but.setTitle("Delete", for: .normal)
but.setImage(UIImage(), for: .normal)
}
but.isHidden = !(state == .add)
stack.isHidden = (state == .add)
// if state == .add {
// but.setTitle("Add New", for: .normal)
// but.setImage(UIImage(named: "ic_add_secret"), for: .normal)
// }else{
// but.setTitle("Delete", for: .normal)
// but.setImage(UIImage(), for: .normal)
// }
}
}
......
......@@ -10,12 +10,6 @@ import Foundation
class SettingViewController : BaseViewController , UITableViewDelegate, UITableViewDataSource{
var modelData: [SettingModel]?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
modelData = getSettingViewInfo()
self.tableView.reloadData()
}
lazy var tableView : UITableView = {
let cY = titleView.height + titleView.y
......@@ -46,11 +40,9 @@ class SettingViewController : BaseViewController , UITableViewDelegate, UITableV
self.titleView.titleLabel.textColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
modelData = getSettingViewInfo()
self.view.addSubview(tableView)
self.tableView.reloadData()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let model : SettingModel = modelData![section]
......@@ -161,12 +153,13 @@ class SettingViewController : BaseViewController , UITableViewDelegate, UITableV
emailLoginSignOut()
break
case settingLabels.UsePIN.rawValue:
vibrate()
secretspace()
vibrate()
break
case settingLabels.UseFaceID.rawValue:
vibrate()
SetFaceID(indexPath)
vibrate()
break
case settingLabels.ChangePIN.rawValue:
changePIN()
......@@ -247,8 +240,8 @@ class SettingViewController : BaseViewController , UITableViewDelegate, UITableV
// MARK: - 隐私空间PIN
private func secretspace() -> Void {
let seting = SecretSetViewController()
if SettingConfiguration.share.config.secret?.count ?? 0 == 4 {
let seting = SecretSetViewController()
seting.secretType = .del
seting.Callback = {[weak self] success in
guard let self = self else { return }
......@@ -259,7 +252,6 @@ class SettingViewController : BaseViewController , UITableViewDelegate, UITableV
}
self.navigationController?.pushViewController(seting, animated: true)
}else{
let seting = SecretSetViewController()
seting.secretType = .create
seting.Callback = {[weak self] success in
guard let self = self else { return }
......@@ -295,12 +287,8 @@ class SettingViewController : BaseViewController , UITableViewDelegate, UITableV
private func changePIN() -> Void {
let seting = SecretSetViewController()
seting.secretType = .change
seting.Callback = {[weak self] success in
guard let self = self else { return }
if success {
// self.modelData = getSettingViewInfo()
// self.tableView.reloadData()
}
seting.Callback = { success in
}
self.navigationController?.pushViewController(seting, animated: true)
}
......
......@@ -27,31 +27,31 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tips_duplicates_1" translatesAutoresizingMaskIntoConstraints="NO" id="5sP-3G-R5V">
<rect key="frame" x="41" y="185" width="346" height="137.66666666666663"/>
<rect key="frame" x="41" y="150" width="346" height="137.66666666666663"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2 Duplicates" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XMa-dY-JFG">
<rect key="frame" x="40" y="147" width="97" height="20"/>
<rect key="frame" x="40" y="112" width="97" height="20"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>
<color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tips_duplicates_1" translatesAutoresizingMaskIntoConstraints="NO" id="LIQ-mA-CKi">
<rect key="frame" x="41" y="380.66666666666669" width="346" height="137.00000000000006"/>
<rect key="frame" x="41" y="345.66666666666669" width="346" height="137"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2 Duplicates" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dRr-c6-YoB">
<rect key="frame" x="40" y="342.66666666666669" width="97" height="20"/>
<rect key="frame" x="40" y="307.66666666666669" width="97" height="20"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>
<color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Duplicates" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="U5t-M8-fCd">
<rect key="frame" x="151.66666666666666" y="544.66666666666663" width="124.99999999999997" height="30"/>
<rect key="frame" x="151.66666666666666" y="509.66666666666674" width="124.99999999999997" height="30"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="25"/>
<color key="textColor" red="0.10196078431372549" green="0.10196078431372549" blue="0.10196078431372549" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="OOD-68-wMD">
<rect key="frame" x="25" y="592.66666666666663" width="390" height="50.333333333333371"/>
<rect key="frame" x="25" y="557.66666666666663" width="390" height="50.333333333333371"/>
<string key="text">These are duplicate photos - they are completely identical to each other. Immediately delete all duplicate items and keep the original version.</string>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
......@@ -87,7 +87,7 @@
<constraint firstItem="fnl-2z-Ty3" firstAttribute="trailing" secondItem="5sP-3G-R5V" secondAttribute="trailing" constant="53" id="h2C-hf-maH"/>
<constraint firstItem="OOD-68-wMD" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" constant="25" id="jJ3-Yj-u0w"/>
<constraint firstItem="OOD-68-wMD" firstAttribute="top" secondItem="U5t-M8-fCd" secondAttribute="bottom" constant="18" id="lO2-YE-hId" customClass="ScreenHeightRatioConstraint" customModule="PhoneManager" customModuleProvider="target"/>
<constraint firstItem="XMa-dY-JFG" firstAttribute="top" secondItem="fnl-2z-Ty3" secondAttribute="top" constant="85" id="lQN-9H-9H5" customClass="ScreenHeightRatioConstraint" customModule="PhoneManager" customModuleProvider="target"/>
<constraint firstItem="XMa-dY-JFG" firstAttribute="top" secondItem="fnl-2z-Ty3" secondAttribute="top" constant="50" id="lQN-9H-9H5" customClass="ScreenHeightRatioConstraint" customModule="PhoneManager" customModuleProvider="target"/>
<constraint firstItem="LIQ-mA-CKi" firstAttribute="trailing" secondItem="5sP-3G-R5V" secondAttribute="trailing" id="qKI-bl-RGS"/>
<constraint firstItem="LIQ-mA-CKi" firstAttribute="top" secondItem="dRr-c6-YoB" secondAttribute="bottom" constant="18" id="qWW-HD-yM0" customClass="ScreenHeightRatioConstraint" customModule="PhoneManager" customModuleProvider="target"/>
<constraint firstItem="U5t-M8-fCd" firstAttribute="centerX" secondItem="LIQ-mA-CKi" secondAttribute="centerX" id="yXP-XE-VJ9"/>
......
......@@ -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")
......
......@@ -99,7 +99,7 @@ class EmailCleanListController: BaseViewController {
btn.setImage(UIImage.init(named: "ic_check_similar"), for: .normal)
btn.setTitle("Select All", for: .normal)
btn.setImage(UIImage.init(named: "ic_close_similar"), for: .selected)
btn.setTitle("Deselect All", for: .selected)
btn.setTitle("Unselect all", for: .selected)
btn.setTitleColor(UIColor.colorWithHex(hexStr: mColor), for: .normal)
btn.setTitleColor(UIColor.colorWithHex(hexStr: black3Color), for: .selected)
btn.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .bold)
......
......@@ -35,7 +35,13 @@ class PMScaleImageView: UIView , UIScrollViewDelegate {
}
self.showImg.snp.remakeConstraints({ make in
make.left.equalToSuperview().offset((self.width - size.width)/2.0 )
make.top.equalToSuperview().offset((self.height - size.height)/2.0 )
if size.height < self.height / 2.0 {
make.top.equalToSuperview().offset((self.height - size.height)/2.0 - 20 )
}else if self.height - size.height > 20 {
make.top.equalToSuperview().offset(10)
}else{
make.top.equalToSuperview().offset((self.height - size.height)/2.0 )
}
make.size.equalTo(size)
})
})
......
......@@ -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|
......
......@@ -22,7 +22,7 @@ struct Provider: TimelineProvider {
func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> Void) {
let entry = loadSharedData()
let timeline = Timeline(entries: [entry], policy: .after(Date().addingTimeInterval(60)))
let timeline = Timeline(entries: [entry], policy: .after(Date().addingTimeInterval(120)))
completion(timeline)
}
......@@ -419,58 +419,58 @@ struct BatteryWidget2: Widget {
struct AWidgetStorageView : View {
var entry: Provider.Entry
var body: some View {
if #available(iOSApplicationExtension 17.0, *) {
ZStack {
Text("\(entry.widgetBattery)")
}.foregroundColor(Color("333333"))
.containerBackground(for: .widget) {
AccessoryWidgetBackground()
}
} else {
ZStack {
Text("Hello")
}
}
}
}
struct LockScreenWidgetView :View {
var entry: Provider.Entry
@Environment(\.widgetFamily) var family
var body: some View {
switch family {
case .accessoryCircular:
AWidgetStorageView(entry: entry)
default:
EmptyView()
}
}
}
struct widgetLockSreenView: Widget {
let kind = "com.app.lockscreen.widget"
var body: some WidgetConfiguration {
if #available(iOSApplicationExtension 16.0, *) {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
LockScreenWidgetView(entry: entry)
}
.configurationDisplayName("Storage")
.description("Monitor your storage space")
.supportedFamilies([
.accessoryCircular,
])
} else {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
LockScreenWidgetView(entry: entry)
}
.configurationDisplayName("")
.description("")
.supportedFamilies([])
}
}
}
//struct AWidgetStorageView : View {
// var entry: Provider.Entry
// var body: some View {
// if #available(iOSApplicationExtension 17.0, *) {
// ZStack {
// Text("\(entry.widgetBattery)")
// }.foregroundColor(Color("333333"))
// .containerBackground(for: .widget) {
// AccessoryWidgetBackground()
// }
// } else {
// ZStack {
// Text("Hello")
// }
// }
// }
//}
//struct LockScreenWidgetView :View {
// var entry: Provider.Entry
// @Environment(\.widgetFamily) var family
// var body: some View {
// switch family {
// case .accessoryCircular:
// AWidgetStorageView(entry: entry)
// default:
// EmptyView()
// }
// }
//}
//struct widgetLockSreenView: Widget {
// let kind = "com.app.lockscreen.widget"
// var body: some WidgetConfiguration {
// if #available(iOSApplicationExtension 16.0, *) {
// StaticConfiguration(kind: kind, provider: Provider()) { entry in
// LockScreenWidgetView(entry: entry)
// }
// .configurationDisplayName("Storage")
// .description("Monitor your storage space")
// .supportedFamilies([
// .accessoryCircular,
// ])
// } else {
// StaticConfiguration(kind: kind, provider: Provider()) { entry in
// LockScreenWidgetView(entry: entry)
// }
// .configurationDisplayName("")
// .description("")
// .supportedFamilies([])
// }
// }
//}
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