Skip to content

Commit

Permalink
AudioPlayer features #58: Sleep timer
Browse files Browse the repository at this point in the history
  • Loading branch information
filimo committed Dec 27, 2019
1 parent 385c4eb commit e2ce7a5
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 62 deletions.
4 changes: 4 additions & 0 deletions ReaderTranslator.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@
F0D31C5123491C23003CF86B /* GTranslatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0D31C5023491C23003CF86B /* GTranslatorView.swift */; };
F0D31C5223491C23003CF86B /* GTranslatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0D31C5023491C23003CF86B /* GTranslatorView.swift */; };
F0D3551C23A6BE3C009FBA74 /* GTranslatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0D3551B23A6BE3C009FBA74 /* GTranslatorView.swift */; };
F0DAB5E323B66DE400C1A4D3 /* TestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DAB5E223B66DE400C1A4D3 /* TestView.swift */; };
F0DADAE5239BE8C200CFE2B1 /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = F0DADAE4239BE8C200CFE2B1 /* SwiftSoup */; };
F0EDE34B236418E000E0B81C /* DOMEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0EE0A0F23478C86004A5EAD /* DOMEvent.swift */; };
F0EDE34D23641B1300E0B81C /* Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0EDE34C23641B1300E0B81C /* Stack.swift */; };
Expand Down Expand Up @@ -524,6 +525,7 @@
F0D2E333234BA49000D95994 /* Safari.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Safari.swift; sourceTree = "<group>"; };
F0D31C5023491C23003CF86B /* GTranslatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GTranslatorView.swift; sourceTree = "<group>"; };
F0D3551B23A6BE3C009FBA74 /* GTranslatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GTranslatorView.swift; sourceTree = "<group>"; };
F0DAB5E223B66DE400C1A4D3 /* TestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestView.swift; sourceTree = "<group>"; };
F0EDE34C23641B1300E0B81C /* Stack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stack.swift; sourceTree = "<group>"; };
F0EDFB1E239E2F210048CFD1 /* BookmarksView_Controls_ActionMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksView_Controls_ActionMenu.swift; sourceTree = "<group>"; };
F0EDFB21239E467B0048CFD1 /* ReaderView_Pdf_Toolbar_PlayButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderView_Pdf_Toolbar_PlayButtons.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -805,6 +807,7 @@
F00AD753239627EB005FAFE1 /* PlayerContols */,
F0AB182323A5501C00A71CA6 /* ViewModes */,
F0120379239182DA008D0B47 /* PlayerContentView.swift */,
F0DAB5E223B66DE400C1A4D3 /* TestView.swift */,
);
path = Views;
sourceTree = "<group>";
Expand Down Expand Up @@ -1414,6 +1417,7 @@
F02B04C523A2930D00F93B84 /* ConnectionView.swift in Sources */,
F0D3551C23A6BE3C009FBA74 /* GTranslatorView.swift in Sources */,
F0120378239182DA008D0B47 /* SceneDelegate.swift in Sources */,
F0DAB5E323B66DE400C1A4D3 /* TestView.swift in Sources */,
F033F41223A6D3A800605325 /* DirectoryObserver.swift in Sources */,
F08EBB0523956202009025D3 /* FileListView.swift in Sources */,
F046182E23A3A4AF0041E726 /* ConnectionClientStatus.swift in Sources */,
Expand Down
77 changes: 73 additions & 4 deletions ReaderTranslatorPlayer/Store/AudioStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,32 @@
// Copyright © 2019 Viktor Kushnerov. All rights reserved.
//

import Foundation
import AVFoundation
import SwiftUI

final class AudioStore: ObservableObject {
private init() {}
final class AudioStore: NSObject, ObservableObject {
private override init() { super.init() }
static var shared = AudioStore()

private var timer: Timer?
private var sleepTimer: Timer?
private(set) var player: AVAudioPlayer?
private var someObservationContext = ""

@Published var currentStatus = "0.0/0.0"
@Published var files: [URL] = []
@Published var isPlaying = false {
willSet {
guard let player = FileListView.player else { return }
guard let player = player else { return }

if newValue {
let currentTime = player.currentTime
player.play()
player.currentTime = currentTime
startTimer()
} else {
player.pause()
invalidate()
}
}
}
Expand All @@ -30,4 +40,63 @@ final class AudioStore: ObservableObject {
@Published(key: "isVoiceEnabled") var isEnabled = true
@Published(key: "voiceVolume") var volume: Float = 1
@Published(key: "audioRate") var rate: Float = 1
@Published var sleepAfter = 0
}

extension AudioStore {
func openAudio(url: URL) {
do {
player = try AVAudioPlayer(contentsOf: url)
guard let player = player else { return }
player.delegate = self
player.enableRate = true
player.rate = rate
} catch {
print(error)
}
}

func invalidate() {
timer?.invalidate()
}

func startTimer() {
invalidate()
timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true) { _ in
guard let player = self.player else { return }
self.currentStatus = String(format: "%.1f/%.1f", player.currentTime, player.duration)
}
}

func setSleepTimer(minutes: Int) {
sleepTimer = Timer.scheduledTimer(withTimeInterval: Double(minutes), repeats: false) { _ in
self.isPlaying = false
}
}

func stopSleepTimer() {
sleepTimer?.invalidate()
sleepTimer = nil
}

var remainTimerTime: String {
guard let fireDate = self.sleepTimer?.fireDate else { return "The timer is off" }
let time = RelativeDateTimeFormatter().localizedString(for: fireDate, relativeTo: Date())
return "The timer will turn off \(time)"
}
}

extension AudioStore: AVAudioPlayerDelegate {
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
if flag {
guard let lastAudio = lastAudio else { return }
guard let current = files.firstIndex(of: lastAudio) else { return }
let next = files.index(after: current)
if files.indices.contains(next) {
let url = files[next]
openAudio(url: url)
isPlaying = true
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import SwiftUI
struct AudioRateView: View {
@State var audioRate: Float = AudioStore.shared.rate {
didSet {
FileListView.player?.rate = audioRate
AudioStore.shared.player?.rate = audioRate
AudioStore.shared.rate = audioRate
}
}
Expand Down
84 changes: 50 additions & 34 deletions ReaderTranslatorPlayer/Views/PlayerContols/PlayerContolsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,62 +8,78 @@

import SwiftUI

private var timer: Timer?

struct PlayerControlsView: View {
@ObservedObject var audioStore = AudioStore.shared

@State var currentStatus = "0.0/0.0"
@State var showSafari = false
@State var showHosts = false

private var playPauseButton: some View {
Button(
action: { self.audioStore.isPlaying.toggle() },
label: { Text(audioStore.isPlaying ? "Pause" : "Play") }
)
.buttonStyle(RoundButtonStyle())
}
@State var showTimer = false

var body: some View {
VStack(spacing: 10) {
statusView.frame(width: 100)
Text("\(audioStore.currentStatus)").frame(width: 100)
AudioRateView()
RewindButtonsView()
HStack(spacing: 40) {
Button(action: {
self.showHosts = true
}, label: { Image(systemName: "wifi") })
.padding(.leading)
.buttonStyle(RoundButtonStyle())
HStack(spacing: 20) {
timerButton
hostsButton
Spacer()
playPauseButton
Button(action: { self.showSafari = true }, label: { Text("Safari") })
.buttonStyle(RoundButtonStyle())
}
safariButton
}.padding([.leading, .trailing], 20)
}
.sheet(isPresented: $showSafari) {
SafariView(url: .constant(URL(string: "https://www.ldoceonline.com")))
}
.sheet(isPresented: $showHosts) { ConnectionView() }
.actionSheet(isPresented: $showTimer) {
ActionSheet(
title: Text("Sleep timer"),
message: Text(self.audioStore.remainTimerTime),
buttons: timerValueButtons
)
}
}

private var playPauseButton: some View {
Button(
action: { self.audioStore.isPlaying.toggle() },
label: { Text(audioStore.isPlaying ? "Pause" : "Play") }
).buttonStyle(RoundButtonStyle())
}

private var statusView: some View {
let status = Text("\(currentStatus)")
private var timerButton: some View {
Button(
action: { self.showTimer = true },
label: { Image(systemName: "timer") }
).buttonStyle(RoundButtonStyle())
}

return Group {
if audioStore.isPlaying {
status.onAppear { self.startTimer() }
} else {
status.onAppear { timer?.invalidate() }
}
}
private var hostsButton: some View {
Button(
action: { self.showHosts = true },
label: { Image(systemName: "wifi") }
).buttonStyle(RoundButtonStyle())
}

private var safariButton: some View {
Button(
action: { self.showSafari = true },
label: { Text("Safari") }
).buttonStyle(RoundButtonStyle())
}

private func startTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true) { _ in
guard let player = FileListView.player else { return }
self.currentStatus = String(format: "%.1f/%.1f", player.currentTime, player.duration)
private var timerValueButtons: [ActionSheet.Button] {
Array(0...20).map { val in
let val = val * 10
let value = val == 0 ? "Off" : String(describing: val)
let action = {
if val == 0 {
self.audioStore.stopSleepTimer()

} else { self.audioStore.setSleepTimer(minutes: val * 60) }
}
return .default(Text("\(value)"), action: action)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import SwiftUI
struct RewindButtonsView: View {
var body: some View {
HStack {
Button(action: { FileListView.player?.currentTime = 0 }, label: { Text("|<") })
Button(action: { AudioStore.shared.player?.currentTime = 0 }, label: { Text("|<") })
.buttonStyle(RoundButtonStyle())
rewindButton(label: "-50", step: -50)
rewindButton(label: "-5", step: -5)
Expand All @@ -24,7 +24,7 @@ struct RewindButtonsView: View {

private func rewindButton(label: String, step: Double) -> some View {
Button(
action: { FileListView.player?.currentTime += step },
action: { AudioStore.shared.player?.currentTime += step },
label: { Text(label).frame(width: 35) }
)
.buttonStyle(RoundButtonStyle())
Expand Down
47 changes: 47 additions & 0 deletions ReaderTranslatorPlayer/Views/TestView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// test.swift
// ReaderTranslatorPlayer
//
// Created by Viktor Kushnerov on 27/12/19.
// Copyright © 2019 Viktor Kushnerov. All rights reserved.
//

import SwiftUI

struct TestView: View {
@State var pp = 1
@State var show = false

var body: some View {
VStack {
// Spacer()
Text("1000")
.onTapGesture {
self.show = true
}
.overlay(
Group {
if show {
Picker(selection: $pp, label: Text("")) {
Text("1111").tag(1)
Text("1111").tag(2)
Text("1111").tag(3)
Text("1111").tag(4)
Text("1111").tag(5)
}
.background(Color.secondary)
.foregroundColor(Color.primary)
} else {
EmptyView()
}
}
)
}
}
}

struct test_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
Loading

0 comments on commit e2ce7a5

Please sign in to comment.