You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
351 lines
13 KiB
351 lines
13 KiB
import SwiftUI
|
|
import AVFoundation
|
|
import Photos
|
|
|
|
struct AppPermissionsView: View {
|
|
@EnvironmentObject private var languageManager: LanguageManager
|
|
@State private var cameraPermissionStatus: AVAuthorizationStatus = .notDetermined
|
|
@State private var photoPermissionStatus: PHAuthorizationStatus = .notDetermined
|
|
|
|
var body: some View {
|
|
NavigationView {
|
|
ZStack {
|
|
// 背景渐变
|
|
LinearGradient(
|
|
gradient: Gradient(colors: [
|
|
Color(.systemBackground),
|
|
Color(.systemGray6).opacity(0.2)
|
|
]),
|
|
startPoint: .top,
|
|
endPoint: .bottom
|
|
)
|
|
.ignoresSafeArea()
|
|
|
|
ScrollView {
|
|
VStack(spacing: 24) {
|
|
// 顶部图标
|
|
VStack(spacing: 16) {
|
|
ZStack {
|
|
Circle()
|
|
.fill(
|
|
LinearGradient(
|
|
gradient: Gradient(colors: [
|
|
Color.blue.opacity(0.1),
|
|
Color.blue.opacity(0.05)
|
|
]),
|
|
startPoint: .topLeading,
|
|
endPoint: .bottomTrailing
|
|
)
|
|
)
|
|
.frame(width: 80, height: 80)
|
|
|
|
Image(systemName: "lock.shield")
|
|
.font(.system(size: 36, weight: .light))
|
|
.foregroundColor(.blue)
|
|
}
|
|
|
|
Text("app_permissions".localized)
|
|
.font(.system(size: 28, weight: .bold, design: .rounded))
|
|
.foregroundColor(.primary)
|
|
.id(languageManager.refreshTrigger)
|
|
}
|
|
.padding(.top, 20)
|
|
|
|
// 权限说明卡片
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
HStack {
|
|
Image(systemName: "info.circle")
|
|
.font(.system(size: 20, weight: .medium))
|
|
.foregroundColor(.blue)
|
|
.frame(width: 32)
|
|
|
|
Text("permissions_info".localized)
|
|
.font(.system(size: 18, weight: .semibold))
|
|
|
|
Spacer()
|
|
}
|
|
|
|
Text("permissions_description".localized)
|
|
.font(.system(size: 14))
|
|
.foregroundColor(.secondary)
|
|
.lineLimit(nil)
|
|
}
|
|
.padding(20)
|
|
.background(
|
|
RoundedRectangle(cornerRadius: 16)
|
|
.fill(Color(.systemBackground))
|
|
.shadow(color: .black.opacity(0.05), radius: 8, x: 0, y: 2)
|
|
)
|
|
.padding(.horizontal, 20)
|
|
|
|
// 相机权限卡片
|
|
PermissionCard(
|
|
icon: "camera.fill",
|
|
iconColor: .blue,
|
|
title: "camera_permission".localized,
|
|
description: "camera_permission_description".localized,
|
|
status: cameraPermissionStatus.displayText,
|
|
statusColor: cameraPermissionStatus.statusColor,
|
|
action: {
|
|
requestCameraPermission()
|
|
},
|
|
actionTitle: cameraPermissionStatus.actionTitle
|
|
)
|
|
|
|
// 相册权限卡片
|
|
PermissionCard(
|
|
icon: "photo.fill",
|
|
iconColor: .green,
|
|
title: "photo_permission".localized,
|
|
description: "photo_permission_description".localized,
|
|
status: photoPermissionStatus.displayText,
|
|
statusColor: photoPermissionStatus.statusColor,
|
|
action: {
|
|
requestPhotoPermission()
|
|
},
|
|
actionTitle: photoPermissionStatus.actionTitle
|
|
)
|
|
|
|
// 系统设置卡片
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
HStack {
|
|
Image(systemName: "gearshape.fill")
|
|
.font(.system(size: 20, weight: .medium))
|
|
.foregroundColor(.orange)
|
|
.frame(width: 32)
|
|
|
|
Text("system_settings".localized)
|
|
.font(.system(size: 18, weight: .semibold))
|
|
|
|
Spacer()
|
|
}
|
|
|
|
Text("system_settings_description".localized)
|
|
.font(.system(size: 14))
|
|
.foregroundColor(.secondary)
|
|
.lineLimit(nil)
|
|
|
|
Button(action: {
|
|
openSystemSettings()
|
|
}) {
|
|
HStack {
|
|
Image(systemName: "arrow.up.right.square")
|
|
.font(.system(size: 16, weight: .medium))
|
|
Text("open_system_settings".localized)
|
|
.font(.system(size: 16, weight: .medium))
|
|
}
|
|
.foregroundColor(.white)
|
|
.padding(.horizontal, 20)
|
|
.padding(.vertical, 12)
|
|
.background(
|
|
RoundedRectangle(cornerRadius: 8)
|
|
.fill(Color.blue)
|
|
)
|
|
}
|
|
}
|
|
.padding(20)
|
|
.background(
|
|
RoundedRectangle(cornerRadius: 16)
|
|
.fill(Color(.systemBackground))
|
|
.shadow(color: .black.opacity(0.05), radius: 8, x: 0, y: 2)
|
|
)
|
|
.padding(.horizontal, 20)
|
|
|
|
Spacer(minLength: 30)
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("App Permissions")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.onAppear {
|
|
checkPermissions()
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - 权限检查
|
|
private func checkPermissions() {
|
|
cameraPermissionStatus = AVCaptureDevice.authorizationStatus(for: .video)
|
|
photoPermissionStatus = PHPhotoLibrary.authorizationStatus()
|
|
}
|
|
|
|
// MARK: - 请求相机权限
|
|
private func requestCameraPermission() {
|
|
AVCaptureDevice.requestAccess(for: .video) { granted in
|
|
DispatchQueue.main.async {
|
|
self.cameraPermissionStatus = granted ? .authorized : .denied
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - 请求相册权限
|
|
private func requestPhotoPermission() {
|
|
PHPhotoLibrary.requestAuthorization { status in
|
|
DispatchQueue.main.async {
|
|
self.photoPermissionStatus = status
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - 打开系统设置
|
|
private func openSystemSettings() {
|
|
if let settingsUrl = URL(string: UIApplication.openSettingsURLString) {
|
|
UIApplication.shared.open(settingsUrl)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - 权限卡片组件
|
|
struct PermissionCard: View {
|
|
let icon: String
|
|
let iconColor: Color
|
|
let title: String
|
|
let description: String
|
|
let status: String
|
|
let statusColor: Color
|
|
let action: () -> Void
|
|
let actionTitle: String
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
HStack {
|
|
Image(systemName: icon)
|
|
.font(.system(size: 20, weight: .medium))
|
|
.foregroundColor(iconColor)
|
|
.frame(width: 32)
|
|
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(title)
|
|
.font(.system(size: 18, weight: .semibold))
|
|
Text(description)
|
|
.font(.system(size: 14))
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
Spacer()
|
|
}
|
|
|
|
HStack {
|
|
Text(status)
|
|
.font(.system(size: 14, weight: .medium))
|
|
.foregroundColor(statusColor)
|
|
|
|
Spacer()
|
|
|
|
Button(action: action) {
|
|
Text(actionTitle)
|
|
.font(.system(size: 14, weight: .medium))
|
|
.foregroundColor(.white)
|
|
.padding(.horizontal, 16)
|
|
.padding(.vertical, 8)
|
|
.background(
|
|
RoundedRectangle(cornerRadius: 6)
|
|
.fill(statusColor)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
.padding(20)
|
|
.background(
|
|
RoundedRectangle(cornerRadius: 16)
|
|
.fill(Color(.systemBackground))
|
|
.shadow(color: .black.opacity(0.05), radius: 8, x: 0, y: 2)
|
|
)
|
|
.padding(.horizontal, 20)
|
|
}
|
|
}
|
|
|
|
// MARK: - 权限状态扩展
|
|
extension AVAuthorizationStatus {
|
|
var displayText: String {
|
|
switch self {
|
|
case .notDetermined:
|
|
return "not_determined".localized
|
|
case .restricted:
|
|
return "restricted".localized
|
|
case .denied:
|
|
return "denied".localized
|
|
case .authorized:
|
|
return "authorized".localized
|
|
@unknown default:
|
|
return "unknown".localized
|
|
}
|
|
}
|
|
|
|
var statusColor: Color {
|
|
switch self {
|
|
case .notDetermined:
|
|
return .orange
|
|
case .restricted, .denied:
|
|
return .red
|
|
case .authorized:
|
|
return .green
|
|
@unknown default:
|
|
return .gray
|
|
}
|
|
}
|
|
|
|
var actionTitle: String {
|
|
switch self {
|
|
case .notDetermined:
|
|
return "request_permission".localized
|
|
case .restricted, .denied:
|
|
return "open_settings".localized
|
|
case .authorized:
|
|
return "permission_granted".localized
|
|
@unknown default:
|
|
return "unknown".localized
|
|
}
|
|
}
|
|
}
|
|
|
|
extension PHAuthorizationStatus {
|
|
var displayText: String {
|
|
switch self {
|
|
case .notDetermined:
|
|
return "not_determined".localized
|
|
case .restricted:
|
|
return "restricted".localized
|
|
case .denied:
|
|
return "denied".localized
|
|
case .authorized:
|
|
return "authorized".localized
|
|
case .limited:
|
|
return "limited".localized
|
|
@unknown default:
|
|
return "unknown".localized
|
|
}
|
|
}
|
|
|
|
var statusColor: Color {
|
|
switch self {
|
|
case .notDetermined:
|
|
return .orange
|
|
case .restricted, .denied:
|
|
return .red
|
|
case .authorized, .limited:
|
|
return .green
|
|
@unknown default:
|
|
return .gray
|
|
}
|
|
}
|
|
|
|
var actionTitle: String {
|
|
switch self {
|
|
case .notDetermined:
|
|
return "request_permission".localized
|
|
case .restricted, .denied:
|
|
return "open_settings".localized
|
|
case .authorized, .limited:
|
|
return "permission_granted".localized
|
|
@unknown default:
|
|
return "unknown".localized
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
AppPermissionsView()
|
|
.environmentObject(LanguageManager.shared)
|
|
}
|