USB-Meter / USB Meter / Extensions / CBManagerState.swift
1 contributor
191 lines | 6.416kb
//
//  CBManagerState.swift
//  USB Meter
//
//  Created by Bogdan Timofte on 02/03/2020.
//  Copyright © 2020 Bogdan Timofte. All rights reserved.
//

//import Foundation

import CoreBluetooth
import SwiftUI

//Manager States
//.poweredOff   A state that indicates Bluetooth is currently powered off.
//.poweredOn    A state that indicates Bluetooth is currently powered on and available to use.
//.resetting    A state that indicates the connection with the system service was momentarily lost.
//.unauthorized A state that indicates the application isn’t authorized to use the Bluetooth low energy role.
//.unknown      The manager’s state is unknown.
//.unsupported  A state that indicates this device doesn’t support the Bluetooth low energy central or client role.

extension CBManagerState {
    var description: String  {
        switch self {
        case .poweredOff:
            return "CBManagerState.poweredOff"
        case .poweredOn:
            return "CBManagerState.poweredOn"
        case .resetting:
            return "CBManagerState.resetting"
        case .unauthorized:
            return "CBManagerState.unauthorized"
        case .unknown:
            return "CBManagerState.unknown"
        case .unsupported:
            return "CBManagerState.unsupported"
        default:
            return "CBManagerState.other"
        }
    }
    
    var color: Color  {
        switch self {
        case .poweredOff:
            return Color.red
        case .poweredOn:
            return Color.blue
        case .resetting:
            return Color.green
        case .unauthorized:
            return Color.orange
        case .unknown:
            return Color.secondary
        case .unsupported:
            return Color.gray
        default:
            return Color.yellow
        }
    }
    
    var helpView: AnyView  {
        switch self {
        case .poweredOff:
            return AnyView(poweredOffHelperView())
        case .poweredOn:
            return AnyView(poweredOnHelperView())
        case .resetting:
            return AnyView(resettingHelperView())
        case .unauthorized:
            return AnyView(unauthorizedHelperView())
        case .unknown:
            return AnyView(unknownHelperView())
        case .unsupported:
            return AnyView(unsupportedHelperView())
        default:
            return AnyView(defaultHelperView())
        }
    }
    
    private struct poweredOffHelperView: View {
        var body: some View {
            BluetoothHelpCard(
                title: "Bluetooth Off",
                detail: "Bluetooth is turned off on this device. You can enable it in Settings > Bluetooth.",
                tint: .red
            )
        }
    }
    private struct poweredOnHelperView: View {
        var body: some View {
            BluetoothHelpCard(
                title: "Bluetooth Ready",
                detail: "Bluetooth is powered on and ready for scanning.",
                tint: .blue
            )
        }
    }
    
    private struct resettingHelperView: View {
        var body: some View {
            BluetoothHelpCard(
                title: "Bluetooth Resetting",
                detail: "The Bluetooth stack is temporarily resetting. Wait a moment and try again.",
                tint: .green
            )
        }
    }
    
    private struct unauthorizedHelperView: View {
        var body: some View {
            VStack(alignment: .leading, spacing: 12) {
                Text("Bluetooth Access Needed")
                    .font(.headline)
                Text("This application does not have permission to access Bluetooth. You can enable it in Settings.")
                    .font(.footnote)
                    .foregroundColor(.secondary)
                Button(action: { UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil) }) {
                    Text("Settings")
                }
                .padding(.horizontal, 14)
                .padding(.vertical, 10)
                .meterCard(tint: .orange, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 14)
                .buttonStyle(.plain)
            }
            .frame(maxWidth: .infinity, alignment: .leading)
            .padding(20)
            .meterCard(tint: .orange, fillOpacity: 0.18, strokeOpacity: 0.24)
            .padding()
        }
    }
    
    private struct unknownHelperView: View {
        var body: some View {
            BluetoothHelpCard(
                title: "Unknown Bluetooth State",
                detail: "Bluetooth is reporting an unknown state. Wait a moment and check again.",
                tint: .secondary
            )
        }
    }
    
    private struct unsupportedHelperView: View {
        var body: some View {
            BluetoothHelpCard(
                title: "Bluetooth Unsupported",
                detail: "This device does not support the Bluetooth capabilities required by these USB meters.",
                tint: .gray
            )
        }
    }
    
    private struct defaultHelperView: View {
        var body: some View {
            BluetoothHelpCard(
                title: "Other Bluetooth State",
                detail: "Bluetooth is in an unexpected state. Try again, then contact the developer if it persists.",
                tint: .yellow
            )
        }
    }

    private struct BluetoothHelpCard: View {
        let title: String
        let detail: String
        let tint: Color

        var body: some View {
            ScrollView {
                VStack(alignment: .leading, spacing: 10) {
                    Text(title)
                        .font(.system(.title3, design: .rounded).weight(.bold))
                    Text(detail)
                        .font(.footnote)
                        .foregroundColor(.secondary)
                }
                .frame(maxWidth: .infinity, alignment: .leading)
                .padding(20)
                .meterCard(tint: tint, fillOpacity: 0.18, strokeOpacity: 0.24)
                .padding()
            }
            .background(
                LinearGradient(
                    colors: [tint.opacity(0.14), Color.clear],
                    startPoint: .topLeading,
                    endPoint: .bottomTrailing
                )
                .ignoresSafeArea()
            )
        }
    }
}