initial commit
This commit is contained in:
54
.gitignore
vendored
Normal file
54
.gitignore
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/xcode,macos
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=xcode,macos
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### macOS Patch ###
|
||||
# iCloud generated files
|
||||
*.icloud
|
||||
|
||||
### Xcode ###
|
||||
## User settings
|
||||
xcuserdata/
|
||||
|
||||
## Xcode 8 and earlier
|
||||
*.xcscmblueprint
|
||||
*.xccheckout
|
||||
|
||||
### Xcode Patch ###
|
||||
*.xcodeproj/*
|
||||
!*.xcodeproj/project.pbxproj
|
||||
!*.xcodeproj/xcshareddata/
|
||||
!*.xcodeproj/project.xcworkspace/
|
||||
!*.xcworkspace/contents.xcworkspacedata
|
||||
/*.gcno
|
||||
**/xcshareddata/WorkspaceSettings.xcsettings
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/xcode,macos
|
@@ -7,6 +7,9 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
F12B73452C55505E0064A230 /* NeuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F12B73442C55505E0064A230 /* NeuButton.swift */; };
|
||||
F12B73472C55AC6B0064A230 /* PlaybackManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F12B73462C55AC6B0064A230 /* PlaybackManager.swift */; };
|
||||
F12B73492C55C0C40064A230 /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = F12B73482C55C0C40064A230 /* .gitignore */; };
|
||||
F13839702C51BABD00B4814F /* InfinifiIOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F138396F2C51BABD00B4814F /* InfinifiIOSApp.swift */; };
|
||||
F13839722C51BABD00B4814F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13839712C51BABD00B4814F /* ContentView.swift */; };
|
||||
F13839742C51BABE00B4814F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F13839732C51BABE00B4814F /* Assets.xcassets */; };
|
||||
@@ -14,6 +17,9 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
F12B73442C55505E0064A230 /* NeuButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeuButton.swift; sourceTree = "<group>"; };
|
||||
F12B73462C55AC6B0064A230 /* PlaybackManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackManager.swift; sourceTree = "<group>"; };
|
||||
F12B73482C55C0C40064A230 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = SOURCE_ROOT; };
|
||||
F138396C2C51BABD00B4814F /* InfinifiIOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = InfinifiIOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F138396F2C51BABD00B4814F /* InfinifiIOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfinifiIOSApp.swift; sourceTree = "<group>"; };
|
||||
F13839712C51BABD00B4814F /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
@@ -55,6 +61,9 @@
|
||||
F13839712C51BABD00B4814F /* ContentView.swift */,
|
||||
F13839732C51BABE00B4814F /* Assets.xcassets */,
|
||||
F13839752C51BABE00B4814F /* Preview Content */,
|
||||
F12B73442C55505E0064A230 /* NeuButton.swift */,
|
||||
F12B73462C55AC6B0064A230 /* PlaybackManager.swift */,
|
||||
F12B73482C55C0C40064A230 /* .gitignore */,
|
||||
);
|
||||
path = InfinifiIOS;
|
||||
sourceTree = "<group>";
|
||||
@@ -126,6 +135,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F13839772C51BABE00B4814F /* Preview Assets.xcassets in Resources */,
|
||||
F12B73492C55C0C40064A230 /* .gitignore in Resources */,
|
||||
F13839742C51BABE00B4814F /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -137,7 +147,9 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F12B73452C55505E0064A230 /* NeuButton.swift in Sources */,
|
||||
F13839722C51BABD00B4814F /* ContentView.swift in Sources */,
|
||||
F12B73472C55AC6B0064A230 /* PlaybackManager.swift in Sources */,
|
||||
F13839702C51BABD00B4814F /* InfinifiIOSApp.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -275,6 +287,7 @@
|
||||
DEVELOPMENT_TEAM = 5FG7YZ49ZA;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = infinifi;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
@@ -304,6 +317,7 @@
|
||||
DEVELOPMENT_TEAM = 5FG7YZ49ZA;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = infinifi;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
|
38
InfinifiIOS/Assets.xcassets/BaseColor.colorset/Contents.json
Normal file
38
InfinifiIOS/Assets.xcassets/BaseColor.colorset/Contents.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF5",
|
||||
"green" : "0xF1",
|
||||
"red" : "0xEF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x2E",
|
||||
"green" : "0x1E",
|
||||
"red" : "0x1E"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xCC",
|
||||
"green" : "0xC0",
|
||||
"red" : "0xBC"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x5A",
|
||||
"green" : "0x47",
|
||||
"red" : "0x45"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
38
InfinifiIOS/Assets.xcassets/TextColor.colorset/Contents.json
Normal file
38
InfinifiIOS/Assets.xcassets/TextColor.colorset/Contents.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x5A",
|
||||
"green" : "0x47",
|
||||
"red" : "0x45"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF4",
|
||||
"green" : "0xD6",
|
||||
"red" : "0xCD"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@@ -1,21 +1,48 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// InfinifiIOS
|
||||
//
|
||||
// Created by Kenneth on 24/07/2024.
|
||||
//
|
||||
|
||||
import AVKit
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
VStack {
|
||||
Image(systemName: "globe")
|
||||
.imageScale(.large)
|
||||
.foregroundStyle(.tint)
|
||||
Text("Hello, world!")
|
||||
@ObservedObject private var playbackManager = PlaybackManager()
|
||||
|
||||
private func toggleAudioPlayback() {
|
||||
switch playbackManager.playbackState {
|
||||
case .playing:
|
||||
playbackManager.stop()
|
||||
case .paused:
|
||||
playbackManager.nextTrack()
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
let buttonImageName = switch playbackManager.playbackState {
|
||||
case .paused: "play"
|
||||
case .playing: "pause"
|
||||
}
|
||||
|
||||
VStack(alignment: .center) {
|
||||
Text("infinifi")
|
||||
.font(.title3)
|
||||
.bold()
|
||||
.monospaced()
|
||||
.padding()
|
||||
|
||||
Spacer()
|
||||
|
||||
NeuButton(action: {
|
||||
toggleAudioPlayback()
|
||||
}) {
|
||||
Image(systemName: buttonImageName)
|
||||
.font(.system(size: 24))
|
||||
.tint(.text)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.frame(
|
||||
maxWidth: .infinity,
|
||||
maxHeight: .infinity
|
||||
)
|
||||
.background(.base)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,3 @@
|
||||
//
|
||||
// InfinifiIOSApp.swift
|
||||
// InfinifiIOS
|
||||
//
|
||||
// Created by Kenneth on 24/07/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
|
63
InfinifiIOS/NeuButton.swift
Normal file
63
InfinifiIOS/NeuButton.swift
Normal file
@@ -0,0 +1,63 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct NeuButtonStyle: PrimitiveButtonStyle {
|
||||
private let onPressStateChange: (_ isPressed: Bool) -> Void
|
||||
private let impact = UIImpactFeedbackGenerator(style: .medium)
|
||||
|
||||
init(onPressStateChange: @escaping (_: Bool) -> Void) {
|
||||
self.onPressStateChange = onPressStateChange
|
||||
}
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.onTapGesture {
|
||||
configuration.trigger()
|
||||
}
|
||||
.onLongPressGesture(minimumDuration: 0) {} onPressingChanged: { isPressing in
|
||||
onPressStateChange(isPressing)
|
||||
impact.impactOccurred()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NeuButton<Content>: View where Content: View {
|
||||
let content: () -> Content
|
||||
let action: () -> Void
|
||||
|
||||
@State private var isPressed = false
|
||||
|
||||
init(action: @escaping () -> Void, @ViewBuilder content: @escaping () -> Content) {
|
||||
self.content = content
|
||||
self.action = action
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Button(action: action) {
|
||||
Rectangle()
|
||||
.fill(isPressed ? .base : .text)
|
||||
.offset(
|
||||
x: isPressed ? 0 : 4,
|
||||
y: isPressed ? 0 : 4
|
||||
)
|
||||
.overlay {
|
||||
ZStack {
|
||||
Rectangle()
|
||||
.fill(isPressed ? .text : .base)
|
||||
|
||||
content()
|
||||
.foregroundStyle(isPressed ? .base : .text)
|
||||
}
|
||||
}
|
||||
}
|
||||
.border(.text, width: 2)
|
||||
.frame(width: 64, height: 64)
|
||||
.offset(
|
||||
x: isPressed ? 6 : 0,
|
||||
y: isPressed ? 6 : 0
|
||||
)
|
||||
.buttonStyle(NeuButtonStyle { isPressed in
|
||||
self.isPressed = isPressed
|
||||
})
|
||||
}
|
||||
}
|
39
InfinifiIOS/PlaybackManager.swift
Normal file
39
InfinifiIOS/PlaybackManager.swift
Normal file
@@ -0,0 +1,39 @@
|
||||
import AVKit
|
||||
import Foundation
|
||||
|
||||
enum PlaybackState {
|
||||
case playing
|
||||
case paused
|
||||
}
|
||||
|
||||
class PlaybackManager: ObservableObject {
|
||||
@Published var playbackState: PlaybackState = .paused
|
||||
@Published var hasError = false
|
||||
|
||||
private var audioPlayer = AVPlayer()
|
||||
|
||||
func nextTrack() {
|
||||
let now = Date().timeIntervalSince1970
|
||||
// add timestamp to the url to prevent caching
|
||||
let playerItem = AVPlayerItem(url: URL(string: "https://infinifi.cafe/current.mp3?t=\(now)")!)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(playbackFinished), name: AVPlayerItem.didPlayToEndTimeNotification, object: playerItem)
|
||||
|
||||
audioPlayer.replaceCurrentItem(with: playerItem)
|
||||
audioPlayer.play()
|
||||
|
||||
playbackState = .playing
|
||||
}
|
||||
|
||||
func stop() {
|
||||
audioPlayer.pause()
|
||||
playbackState = .paused
|
||||
}
|
||||
|
||||
@objc
|
||||
private func playbackFinished() {
|
||||
NotificationCenter.default.removeObserver(self, name: AVPlayerItem.didPlayToEndTimeNotification, object: audioPlayer.currentItem)
|
||||
if playbackState == .playing {
|
||||
nextTrack()
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user