jsdw_ios/QuickLocation/Section/AppRestrict/AppRestrictVC.swift

188 lines
6.7 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// AppRestrictVC.swift
// QuickLocation
//
import FamilyControls
import UIKit
import RxSwift
import RxCocoa
import ManagedSettings
@available(iOS 16.0, *)
final class AppRestrictVC: BaseViewController {
private var rootView: AppRestrictView!
private var tokens: [ApplicationToken] = []
private var linkingToken: ApplicationToken?
override func loadView() {
rootView = AppRestrictView(frame: UIScreen.main.bounds)
view = rootView
}
override func viewDidLoad() {
super.viewDidLoad()
rootView.tableView.dataSource = self
rootView.tableView.delegate = self
rootView.tableView.register(AppRestrictCell.self, forCellReuseIdentifier: AppRestrictCell.reuseId)
bind()
reload()
}
private func bind() {
rootView.navView.onBackTapped = { [weak self] in
self?.navigationController?.popViewController(animated: true)
}
rootView.addBtn.rx.tap.subscribe(onNext: { [weak self] in
self?.addApps()
}).disposed(by: disposeBag)
}
private func reload() {
tokens = AppRestrictManager.shared.applicationTokens
rootView.setListVisible(!tokens.isEmpty, rowCount: tokens.count)
rootView.tableView.reloadData()
}
private func addApps() {
Task { @MainActor in
do {
if !AppRestrictManager.shared.isAuthorized {
try await AppRestrictManager.shared.requestAuthorization()
}
guard AppRestrictManager.shared.isAuthorized else {
DLToast.show(text: "需要屏幕使用时间权限")
return
}
let initialSelection = AppRestrictManager.shared.selection
let presenter = FamilyActivityPickerPresenter(selection: initialSelection)
presenter.onComplete = { [weak self] selection in
let addedApplications = selection.applicationTokens
.subtracting(initialSelection.applicationTokens)
AppRestrictManager.shared.mergeSelection(selection)
self?.reload()
if addedApplications.isEmpty, !selection.categoryTokens.isEmpty {
self?.showCategorySelectionNotice()
} else if !addedApplications.isEmpty {
self?.showApplicationTokens(addedApplications)
}
}
present(presenter, animated: false)
} catch {
DLToast.show(text: "授权失败,请在设置中开启屏幕使用时间权限")
}
}
}
private func showCategorySelectionNotice() {
let alert = UIAlertController(
title: "请选择具体应用",
message: "系统不会返回分类“全部”中的具体应用。请展开分类,取消“全部”,再逐个勾选需要配对的应用。",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "取消", style: .cancel))
alert.addAction(UIAlertAction(title: "重新选择", style: .default) { [weak self] _ in
self?.addApps()
})
present(alert, animated: true)
}
private func showApplicationTokens(_ tokens: Set<ApplicationToken>) {
let values = tokens.compactMap { token in
AppRestrictTokenCodec.encode(token)?.base64EncodedString()
}.sorted()
guard !values.isEmpty else {
DLToast.show(text: "ApplicationToken 编码失败")
return
}
let message = values.enumerated().map { index, value in
values.count == 1 ? value : "应用 \(index + 1)\n\(value)"
}.joined(separator: "\n\n")
let alert = UIAlertController(
title: "ApplicationToken",
message: message,
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "复制", style: .default) { _ in
UIPasteboard.general.string = values.joined(separator: "\n")
DLToast.show(text: "Token 已复制")
})
alert.addAction(UIAlertAction(title: "关闭", style: .cancel))
present(alert, animated: true)
}
private func openLink(for token: ApplicationToken) {
linkingToken = token
let vc = SelectActivityVC()
vc.onSelect = { [weak self] item in
guard let self, let token = self.linkingToken else { return }
AppRestrictManager.shared.link(
catalogId: item.id,
token: token,
displayName: item.name,
iconURL: item.iconURL
)
self.linkingToken = nil
self.reload()
}
let nav = UINavigationController(rootViewController: vc)
present(nav, animated: true)
}
private func deleteApp(_ token: ApplicationToken) {
AppRestrictManager.shared.removeApplication(token)
reload()
}
}
@available(iOS 16.0, *)
extension AppRestrictVC: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
tokens.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: AppRestrictCell.reuseId, for: indexPath) as! AppRestrictCell
let token = tokens[indexPath.row]
let catalog = AppRestrictManager.shared.catalogItem(for: token)
cell.configure(token: token, catalog: catalog)
cell.onPair = { [weak self] in
self?.openLink(for: token)
}
cell.onDelete = { [weak self] in
self?.deleteApp(token)
}
return cell
}
}
/// Fallback when system is below iOS 16.
final class AppRestrictUnsupportedVC: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let nav = BaseNavigationView(title: "应用配对库")
view.addSubview(nav)
nav.layoutChain.top().edgesHorzontal().height(kNaviHeight)
nav.onBackTapped = { [weak self] in
self?.navigationController?.popViewController(animated: true)
}
let lab = UILabel()
lab.text = "该功能需要 iOS 16 及以上系统"
lab.textAlignment = .center
lab.textColor = UIColor(hexStr: "#767676")
view.addSubview(lab)
lab.layoutChain.center()
}
}
enum AppRestrictFactory {
static func make() -> UIViewController {
if #available(iOS 16.0, *) {
return AppRestrictVC()
}
return AppRestrictUnsupportedVC()
}
}