Merge branch 'main' of https://github.com/benxu3/01 into async-interpreter
commit
632af7f7ba
@ -0,0 +1,29 @@
|
||||
# iOS/iPadOS Native Client
|
||||
|
||||
This repository contains the source code for the 01 iOS/iPadOS Native app. It is a work in progress and currently has a dedicated development button.
|
||||
|
||||
Feel free to improve this and make a pull request!
|
||||
|
||||
To run it on your own, you can either install the app directly through the current TestFlight [here](https://testflight.apple.com/join/v8SyuzMT), or build from the source code files in Xcode on your Mac.
|
||||
|
||||
## Instructions
|
||||
|
||||
Follow the **[software setup steps](https://github.com/OpenInterpreter/01?tab=readme-ov-file#software)** in the main repo's README first before you read this
|
||||
|
||||
In Xcode, open the 'zerooone-app' project file in the project folder, change the Signing Team and Bundle Identifier, and build.
|
||||
|
||||
## Using the App
|
||||
|
||||
To use the app there are four features:
|
||||
|
||||
### 1. The speak "Button"
|
||||
Made to emulate the button on the hardware models of 01, the big, yellow circle in the middle of the screen is what you hold when you want to speak to the model, and let go when you're finished speaking.
|
||||
|
||||
### 2. The settings button
|
||||
Tapping the settings button will allow you to input your websocket address so that the app can properly connect to your computer. If you're not sure how to obtain this, read the **'How to Install'** section below!
|
||||
|
||||
### 3. The reconnect button
|
||||
The arrow will be RED when the websocket connection is not live, and GREEN when it is. If you're making some changes you can easily reconnect by simply tapping the arrow button (or you can just start holding the speak button, too!).
|
||||
|
||||
### 4. The terminal button
|
||||
The terminal button allows you to see all response text coming in from the server side of the 01. You can toggle it by tapping on the button, and each toggle clears the on-device cache of text.
|
@ -0,0 +1,415 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 56;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
750E4C0B2BEDD11C00AEE3B1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 750E4C0A2BEDD11C00AEE3B1 /* AppDelegate.swift */; };
|
||||
750E4C0D2BEDD11C00AEE3B1 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 750E4C0C2BEDD11C00AEE3B1 /* SceneDelegate.swift */; };
|
||||
750E4C0F2BEDD11C00AEE3B1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 750E4C0E2BEDD11C00AEE3B1 /* ViewController.swift */; };
|
||||
750E4C122BEDD11C00AEE3B1 /* Base in Resources */ = {isa = PBXBuildFile; fileRef = 750E4C112BEDD11C00AEE3B1 /* Base */; };
|
||||
750E4C142BEDD11D00AEE3B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 750E4C132BEDD11D00AEE3B1 /* Assets.xcassets */; };
|
||||
750E4C172BEDD11D00AEE3B1 /* Base in Resources */ = {isa = PBXBuildFile; fileRef = 750E4C162BEDD11D00AEE3B1 /* Base */; };
|
||||
750E4C202BEDD16E00AEE3B1 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 750E4C1F2BEDD16E00AEE3B1 /* Starscream */; };
|
||||
755DC3B22BEE60A7002B66DF /* AudioRecording.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755DC3B12BEE60A7002B66DF /* AudioRecording.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
750E4C072BEDD11C00AEE3B1 /* zeroone-app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "zeroone-app.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
750E4C0A2BEDD11C00AEE3B1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
750E4C0C2BEDD11C00AEE3B1 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
750E4C0E2BEDD11C00AEE3B1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
750E4C112BEDD11C00AEE3B1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
750E4C132BEDD11D00AEE3B1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
750E4C162BEDD11D00AEE3B1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
750E4C182BEDD11D00AEE3B1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
755DC3B12BEE60A7002B66DF /* AudioRecording.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecording.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
750E4C042BEDD11C00AEE3B1 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
750E4C202BEDD16E00AEE3B1 /* Starscream in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
750E4BFE2BEDD11C00AEE3B1 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
750E4C092BEDD11C00AEE3B1 /* zeroone-app */,
|
||||
750E4C082BEDD11C00AEE3B1 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
750E4C082BEDD11C00AEE3B1 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
750E4C072BEDD11C00AEE3B1 /* zeroone-app.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
750E4C092BEDD11C00AEE3B1 /* zeroone-app */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
750E4C0A2BEDD11C00AEE3B1 /* AppDelegate.swift */,
|
||||
750E4C0C2BEDD11C00AEE3B1 /* SceneDelegate.swift */,
|
||||
750E4C0E2BEDD11C00AEE3B1 /* ViewController.swift */,
|
||||
750E4C102BEDD11C00AEE3B1 /* Main.storyboard */,
|
||||
750E4C132BEDD11D00AEE3B1 /* Assets.xcassets */,
|
||||
750E4C152BEDD11D00AEE3B1 /* LaunchScreen.storyboard */,
|
||||
750E4C182BEDD11D00AEE3B1 /* Info.plist */,
|
||||
755DC3B12BEE60A7002B66DF /* AudioRecording.swift */,
|
||||
);
|
||||
path = "zeroone-app";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
750E4C062BEDD11C00AEE3B1 /* zeroone-app */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 750E4C1B2BEDD11D00AEE3B1 /* Build configuration list for PBXNativeTarget "zeroone-app" */;
|
||||
buildPhases = (
|
||||
750E4C032BEDD11C00AEE3B1 /* Sources */,
|
||||
750E4C042BEDD11C00AEE3B1 /* Frameworks */,
|
||||
750E4C052BEDD11C00AEE3B1 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "zeroone-app";
|
||||
packageProductDependencies = (
|
||||
750E4C1F2BEDD16E00AEE3B1 /* Starscream */,
|
||||
);
|
||||
productName = "zeroone-app";
|
||||
productReference = 750E4C072BEDD11C00AEE3B1 /* zeroone-app.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
750E4BFF2BEDD11C00AEE3B1 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 1530;
|
||||
LastUpgradeCheck = 1530;
|
||||
TargetAttributes = {
|
||||
750E4C062BEDD11C00AEE3B1 = {
|
||||
CreatedOnToolsVersion = 15.3;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 750E4C022BEDD11C00AEE3B1 /* Build configuration list for PBXProject "zeroone-app" */;
|
||||
compatibilityVersion = "Xcode 14.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 750E4BFE2BEDD11C00AEE3B1;
|
||||
packageReferences = (
|
||||
750E4C1E2BEDD16D00AEE3B1 /* XCRemoteSwiftPackageReference "Starscream" */,
|
||||
);
|
||||
productRefGroup = 750E4C082BEDD11C00AEE3B1 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
750E4C062BEDD11C00AEE3B1 /* zeroone-app */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
750E4C052BEDD11C00AEE3B1 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
750E4C142BEDD11D00AEE3B1 /* Assets.xcassets in Resources */,
|
||||
750E4C172BEDD11D00AEE3B1 /* Base in Resources */,
|
||||
750E4C122BEDD11C00AEE3B1 /* Base in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
750E4C032BEDD11C00AEE3B1 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
750E4C0F2BEDD11C00AEE3B1 /* ViewController.swift in Sources */,
|
||||
750E4C0B2BEDD11C00AEE3B1 /* AppDelegate.swift in Sources */,
|
||||
755DC3B22BEE60A7002B66DF /* AudioRecording.swift in Sources */,
|
||||
750E4C0D2BEDD11C00AEE3B1 /* SceneDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
750E4C102BEDD11C00AEE3B1 /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
750E4C112BEDD11C00AEE3B1 /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
750E4C152BEDD11D00AEE3B1 /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
750E4C162BEDD11D00AEE3B1 /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
750E4C192BEDD11D00AEE3B1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
750E4C1A2BEDD11D00AEE3B1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
750E4C1C2BEDD11D00AEE3B1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 4;
|
||||
DEVELOPMENT_TEAM = W5NGQJV8X2;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "zeroone-app/Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = 01ForiOS;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "Audio data from microphone is needed to send commands.";
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.ontheroofstudios.zeroone-app";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
750E4C1D2BEDD11D00AEE3B1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 4;
|
||||
DEVELOPMENT_TEAM = W5NGQJV8X2;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "zeroone-app/Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = 01ForiOS;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "Audio data from microphone is needed to send commands.";
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||
INFOPLIST_KEY_UIMainStoryboardFile = Main;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.ontheroofstudios.zeroone-app";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
750E4C022BEDD11C00AEE3B1 /* Build configuration list for PBXProject "zeroone-app" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
750E4C192BEDD11D00AEE3B1 /* Debug */,
|
||||
750E4C1A2BEDD11D00AEE3B1 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
750E4C1B2BEDD11D00AEE3B1 /* Build configuration list for PBXNativeTarget "zeroone-app" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
750E4C1C2BEDD11D00AEE3B1 /* Debug */,
|
||||
750E4C1D2BEDD11D00AEE3B1 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
750E4C1E2BEDD16D00AEE3B1 /* XCRemoteSwiftPackageReference "Starscream" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/daltoniam/Starscream";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 4.0.8;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
750E4C1F2BEDD16E00AEE3B1 /* Starscream */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 750E4C1E2BEDD16D00AEE3B1 /* XCRemoteSwiftPackageReference "Starscream" */;
|
||||
productName = Starscream;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = 750E4BFF2BEDD11C00AEE3B1 /* Project object */;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
Binary file not shown.
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildLocationStyle</key>
|
||||
<string>UseAppPreferences</string>
|
||||
<key>CustomBuildLocationType</key>
|
||||
<string>RelativeToDerivedData</string>
|
||||
<key>DerivedDataLocationStyle</key>
|
||||
<string>Default</string>
|
||||
<key>ShowSharedSchemesAutomaticallyEnabled</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
@ -0,0 +1,36 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// zeroone-app
|
||||
//
|
||||
// Created by Elad Dekel on 2024-05-09.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@main
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: UISceneSession Lifecycle
|
||||
|
||||
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
||||
// Called when a new scene session is being created.
|
||||
// Use this method to select a configuration to create the new scene with.
|
||||
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
|
||||
// Called when the user discards a scene session.
|
||||
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
|
||||
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "O.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 38 KiB |
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "vector.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 460 B |
@ -0,0 +1,67 @@
|
||||
//
|
||||
// AudioRecording.swift
|
||||
// zeroone-app
|
||||
//
|
||||
// Created by Elad Dekel on 2024-05-10.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
class AudioRecording: NSObject, AVAudioRecorderDelegate {
|
||||
|
||||
var recorder: AVAudioRecorder!
|
||||
var session: AVAudioSession!
|
||||
|
||||
var isRecording = false
|
||||
|
||||
func startRecording() {
|
||||
session = AVAudioSession()
|
||||
let audio = getDocumentsDirectory().appendingPathComponent("tempvoice.wav") // indicates where the audio data will be recording to
|
||||
let s: [String: Any] = [
|
||||
AVFormatIDKey: kAudioFormatLinearPCM,
|
||||
AVSampleRateKey: 16000.0,
|
||||
AVNumberOfChannelsKey: 1,
|
||||
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
|
||||
]
|
||||
do {
|
||||
recorder = try AVAudioRecorder(url: audio, settings: s)
|
||||
try session.setActive(true)
|
||||
try session.setCategory(.playAndRecord, mode: .default)
|
||||
recorder!.delegate = self
|
||||
recorder!.record()
|
||||
isRecording = true
|
||||
|
||||
} catch {
|
||||
print("Error recording")
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getDocumentsDirectory() -> URL { // big thanks to twostraws for this helper function (hackingwithswift.com)
|
||||
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
|
||||
return paths[0]
|
||||
}
|
||||
|
||||
|
||||
func stopRecording() -> Data? {
|
||||
if isRecording && recorder != nil {
|
||||
recorder!.stop()
|
||||
let audio = getDocumentsDirectory().appendingPathComponent("tempvoice.wav")
|
||||
recorder = nil
|
||||
do {
|
||||
let data = try Data(contentsOf: audio) // sends raw audio data
|
||||
try FileManager.default.removeItem(at: audio) // deletes the file
|
||||
return data
|
||||
} catch {
|
||||
print(error.localizedDescription)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
print("not recording")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
@ -0,0 +1,127 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="zeroone_app" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="circle.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="8GG-Ei-Zce">
|
||||
<rect key="frame" x="79" y="304.66666666666669" width="245" height="243.66666666666657"/>
|
||||
<color key="tintColor" systemColor="systemYellowColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="245" id="ZNG-mL-QWz"/>
|
||||
<constraint firstAttribute="width" constant="245" id="kvy-0q-8ID"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="gJn-v2-d0Z">
|
||||
<rect key="frame" x="49" y="131" width="303.66666666666669" height="92.666666666666686"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="24"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="39" translatesAutoresizingMaskIntoConstraints="NO" id="Jep-bq-nBz">
|
||||
<rect key="frame" x="95.999999999999986" y="780.33333333333337" width="209.66666666666663" height="37.666666666666629"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" image="gear" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="4J4-Vq-uXz">
|
||||
<rect key="frame" x="0.0" y="-5" width="47.666666666666664" height="47.333333333333329"/>
|
||||
<color key="tintColor" systemColor="labelColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="47.666666666666664" id="RUe-bq-jRC"/>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="42" id="yem-fm-8uw"/>
|
||||
</constraints>
|
||||
<preferredSymbolConfiguration key="preferredSymbolConfiguration" configurationType="pointSize" pointSize="25" scale="large"/>
|
||||
</imageView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="arrow.clockwise" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="sTV-gO-axp">
|
||||
<rect key="frame" x="86.666666666666657" y="-1.3333333333333321" width="42" height="37.333333333333329"/>
|
||||
<color key="tintColor" systemColor="labelColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="42" id="gYc-1f-wW5"/>
|
||||
</constraints>
|
||||
<preferredSymbolConfiguration key="preferredSymbolConfiguration" configurationType="pointSize" pointSize="30"/>
|
||||
</imageView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="terminal" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="Czp-9u-tDH">
|
||||
<rect key="frame" x="167.66666666666669" y="2.6666666666666679" width="42" height="32.666666666666657"/>
|
||||
<color key="tintColor" systemColor="labelColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="42" id="ZZC-ol-tbv"/>
|
||||
</constraints>
|
||||
<preferredSymbolConfiguration key="preferredSymbolConfiguration" configurationType="pointSize" pointSize="30"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="Jep-bq-nBz" secondAttribute="height" multiplier="50:9" id="3oj-ZY-vQc"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" alpha="0.0" contentMode="scaleToFill" keyboardDismissMode="interactive" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="Vqf-Pz-bQv">
|
||||
<rect key="frame" x="40" y="95" width="322.66666666666669" height="394"/>
|
||||
<color key="backgroundColor" systemColor="systemGray6Color"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="Vqf-Pz-bQv" secondAttribute="height" multiplier="307:375" id="i7c-pN-3Yk"/>
|
||||
</constraints>
|
||||
<color key="textColor" systemColor="labelColor"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="8GG-Ei-Zce" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="2aD-gd-oFQ"/>
|
||||
<constraint firstItem="Jep-bq-nBz" firstAttribute="bottom" secondItem="6Tk-OE-BBY" secondAttribute="bottom" id="4rh-NU-Qkg"/>
|
||||
<constraint firstItem="Vqf-Pz-bQv" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="40" id="OLg-bI-RLB"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="Jep-bq-nBz" secondAttribute="trailing" constant="97" id="Wc2-3u-fpo"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="Vqf-Pz-bQv" secondAttribute="trailing" constant="40" id="cxX-KY-p3B"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="gJn-v2-d0Z" secondAttribute="trailing" constant="50" id="iNY-a6-Ww4"/>
|
||||
<constraint firstItem="gJn-v2-d0Z" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="49" id="owN-up-6fV"/>
|
||||
<constraint firstItem="8GG-Ei-Zce" firstAttribute="top" secondItem="gJn-v2-d0Z" secondAttribute="bottom" constant="80" id="pKl-kg-6Qv"/>
|
||||
<constraint firstItem="8GG-Ei-Zce" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="qrj-Tv-TZB"/>
|
||||
<constraint firstItem="Vqf-Pz-bQv" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="36" id="sST-jo-Jg6"/>
|
||||
<constraint firstItem="gJn-v2-d0Z" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="72" id="wFg-9E-IMx"/>
|
||||
<constraint firstItem="Jep-bq-nBz" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="96" id="zZy-C5-TdW"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="circle" destination="8GG-Ei-Zce" id="Q6Q-TY-A1z"/>
|
||||
<outlet property="infoText" destination="gJn-v2-d0Z" id="pR2-Ps-nKF"/>
|
||||
<outlet property="reconnectIcon" destination="sTV-gO-axp" id="iuS-Zj-2cd"/>
|
||||
<outlet property="settingsGear" destination="4J4-Vq-uXz" id="vgy-Jz-tEd"/>
|
||||
<outlet property="terminalButton" destination="Czp-9u-tDH" id="nhQ-4o-UHd"/>
|
||||
<outlet property="terminalFeed" destination="Vqf-Pz-bQv" id="h3N-1T-wNf"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="46.564885496183201" y="3.5211267605633805"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="arrow.clockwise" catalog="system" width="113" height="128"/>
|
||||
<image name="circle.fill" catalog="system" width="128" height="123"/>
|
||||
<image name="gear" catalog="system" width="128" height="122"/>
|
||||
<image name="terminal" catalog="system" width="128" height="93"/>
|
||||
<systemColor name="labelColor">
|
||||
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemGray6Color">
|
||||
<color red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemYellowColor">
|
||||
<color red="1" green="0.80000000000000004" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSSpeechRecognitionUsageDescription</key>
|
||||
<string>Your audio is used the convert your voice requests into text requests.</string>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UISceneConfigurationName</key>
|
||||
<string>Default Configuration</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
|
||||
<key>UISceneStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
@ -0,0 +1,52 @@
|
||||
//
|
||||
// SceneDelegate.swift
|
||||
// zeroone-app
|
||||
//
|
||||
// Created by Elad Dekel on 2024-05-09.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
|
||||
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
|
||||
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
||||
guard let _ = (scene as? UIWindowScene) else { return }
|
||||
}
|
||||
|
||||
func sceneDidDisconnect(_ scene: UIScene) {
|
||||
// Called as the scene is being released by the system.
|
||||
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
||||
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
||||
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
|
||||
}
|
||||
|
||||
func sceneDidBecomeActive(_ scene: UIScene) {
|
||||
// Called when the scene has moved from an inactive state to an active state.
|
||||
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
||||
}
|
||||
|
||||
func sceneWillResignActive(_ scene: UIScene) {
|
||||
// Called when the scene will move from an active state to an inactive state.
|
||||
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
||||
}
|
||||
|
||||
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||
// Called as the scene transitions from the background to the foreground.
|
||||
// Use this method to undo the changes made on entering the background.
|
||||
}
|
||||
|
||||
func sceneDidEnterBackground(_ scene: UIScene) {
|
||||
// Called as the scene transitions from the foreground to the background.
|
||||
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
||||
// to restore the scene back to its current state.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,366 @@
|
||||
//
|
||||
// ViewController.swift
|
||||
// zeroone-app
|
||||
//
|
||||
// Created by Elad Dekel on 2024-05-09.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Starscream
|
||||
import AVFoundation
|
||||
|
||||
|
||||
class ViewController: UIViewController, WebSocketDelegate {
|
||||
|
||||
@IBOutlet weak var terminalFeed: UITextView!
|
||||
@IBOutlet weak var terminalButton: UIImageView!
|
||||
@IBOutlet weak var reconnectIcon: UIImageView!
|
||||
@IBOutlet weak var circle: UIImageView!
|
||||
@IBOutlet weak var settingsGear: UIImageView!
|
||||
@IBOutlet weak var infoText: UILabel!
|
||||
|
||||
var audioRecordingInstance: AudioRecording?
|
||||
private var audioData = Data()
|
||||
private var audioPlayer: AVAudioPlayer?
|
||||
var address: String?
|
||||
var isConnected = false
|
||||
var recordingPermission = false
|
||||
var terminal = false
|
||||
var socket: WebSocket?
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
terminalFeed.layer.cornerRadius = 15
|
||||
infoText.text = "Hold to start once connected."
|
||||
// Create a gesture recognizer that tracks when the "button" is held
|
||||
let pressGesture = UILongPressGestureRecognizer(target: self, action: #selector(buttonPress(_:)))
|
||||
pressGesture.minimumPressDuration = 0.01
|
||||
circle.addGestureRecognizer(pressGesture)
|
||||
circle.isUserInteractionEnabled = true
|
||||
circle.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
// Create a geature recognizer for the settings button
|
||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(settingsGear(_:)))
|
||||
settingsGear.addGestureRecognizer(tapGesture)
|
||||
settingsGear.isUserInteractionEnabled = true
|
||||
|
||||
|
||||
let reconnectGesture = UITapGestureRecognizer(target: self, action: #selector(recconectIcon(_:)))
|
||||
reconnectIcon.addGestureRecognizer(reconnectGesture)
|
||||
reconnectIcon.isUserInteractionEnabled = true
|
||||
|
||||
|
||||
let terminal = UITapGestureRecognizer(target: self, action: #selector(terminalIcon(_:)))
|
||||
terminalButton.addGestureRecognizer(terminal)
|
||||
terminalButton.isUserInteractionEnabled = true
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
func checkRecordingPerms() {
|
||||
let sess = AVAudioSession.sharedInstance()
|
||||
switch (sess.recordPermission) {
|
||||
case.denied, .undetermined:
|
||||
sess.requestRecordPermission { (granted) in
|
||||
if granted {
|
||||
self.recordingPermission = true
|
||||
} else {
|
||||
let alert = UIAlertController(title: "Recording Not Permitted", message: "You must allow audio recording in order to send commands. Close the app and re-open it to try again.", preferredStyle: .alert)
|
||||
let action = UIAlertAction(title: "Understood", style: .default)
|
||||
alert.addAction(action)
|
||||
self.present(alert, animated: true)
|
||||
}
|
||||
}
|
||||
case .granted:
|
||||
recordingPermission = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
if ((UserDefaults.standard.value(forKey: "IPINFO")) != nil) {
|
||||
print("here")
|
||||
address = UserDefaults.standard.string(forKey: "IPINFO")
|
||||
establishConnection()
|
||||
} else {
|
||||
print("there")
|
||||
setAddress()
|
||||
}
|
||||
checkRecordingPerms()
|
||||
}
|
||||
|
||||
func receieved(data: String) {
|
||||
infoText.text = data
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
func setAddress() {
|
||||
let alert = UIAlertController(title: "Set the Address", message: "Input the address of the WebSocket (found in the terminal running 01 software)", preferredStyle: .alert)
|
||||
alert.addTextField { (field) in
|
||||
field.placeholder = "Enter Address Here"
|
||||
}
|
||||
let cancelButton = UIAlertAction(title: "Cancel", style: .cancel)
|
||||
alert.addAction(cancelButton)
|
||||
let submitButton = UIAlertAction(title: "Done", style: .default) { (_) in
|
||||
if let field = alert.textFields?.first, let text = field.text {
|
||||
UserDefaults.standard.setValue(text, forKey: "IPINFO")
|
||||
self.address = text
|
||||
self.establishConnection()
|
||||
// HAVE THE TEXT FIELD
|
||||
}
|
||||
}
|
||||
alert.addAction(submitButton)
|
||||
|
||||
present(alert, animated: true)
|
||||
}
|
||||
|
||||
@objc func recconectIcon(_ sender: UIGestureRecognizer) {
|
||||
infoText.text = ""
|
||||
self.establishConnection()
|
||||
}
|
||||
|
||||
@objc func terminalIcon(_ sender: UIGestureRecognizer) {
|
||||
if (terminal) {
|
||||
UIView.animate(withDuration: 0.3) {
|
||||
self.terminalFeed.text = ""
|
||||
self.terminalFeed.alpha = 0
|
||||
let moveT = CGAffineTransform(translationX: 0, y: -190)
|
||||
self.appendTranslation(transform: moveT)
|
||||
self.terminalButton.image = UIImage(systemName: "apple.terminal")
|
||||
} completion: { done in
|
||||
self.terminal = false
|
||||
}
|
||||
} else {
|
||||
UIView.animate(withDuration: 0.3) {
|
||||
self.terminalFeed.alpha = 1
|
||||
let moveT = CGAffineTransform(translationX: 0, y: 190)
|
||||
self.appendTranslation(transform: moveT)
|
||||
self.terminalButton.image = UIImage(systemName: "apple.terminal.fill")
|
||||
} completion: { done in
|
||||
self.terminal = true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@objc func settingsGear(_ sender: UIGestureRecognizer) {
|
||||
infoText.text = ""
|
||||
setAddress()
|
||||
}
|
||||
|
||||
func appendTranslation(transform: CGAffineTransform) {
|
||||
var currentTransform = self.circle.transform
|
||||
currentTransform = currentTransform.concatenating(transform)
|
||||
self.circle.transform = currentTransform
|
||||
}
|
||||
|
||||
@objc func buttonPress(_ sender: UILongPressGestureRecognizer) {
|
||||
infoText.text = ""
|
||||
let feedback = UIImpactFeedbackGenerator(style: .medium)
|
||||
if sender.state == .began {
|
||||
socket?.connect()
|
||||
// check for recording permission, if exists
|
||||
// it began, start recording!
|
||||
if (isConnected && recordingPermission) {
|
||||
audioRecordingInstance = AudioRecording()
|
||||
audioRecordingInstance!.startRecording()
|
||||
infoText.text = ""
|
||||
UIView.animate(withDuration: 0.1) {
|
||||
self.circle.tintColor = .green
|
||||
let newT = CGAffineTransform(scaleX: 0.7, y: 0.7)
|
||||
self.appendTranslation(transform: newT)
|
||||
feedback.prepare()
|
||||
feedback.impactOccurred()
|
||||
}
|
||||
} else {
|
||||
let errorFeedback = UIImpactFeedbackGenerator(style: .heavy)
|
||||
errorFeedback.prepare()
|
||||
errorFeedback.impactOccurred()
|
||||
if (isConnected && !recordingPermission) {
|
||||
infoText.text = "Not recording permission. Please close and re-open the app."
|
||||
} else {
|
||||
infoText.text = "Not connected."
|
||||
establishConnection()
|
||||
}
|
||||
UIView.animate(withDuration: 0.5) {
|
||||
self.circle.tintColor = .red
|
||||
} completion: { _ in
|
||||
self.circle.tintColor = .systemYellow
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} else if sender.state == .ended {
|
||||
if (isConnected && recordingPermission) {
|
||||
if (audioRecordingInstance != nil) {
|
||||
let response = audioRecordingInstance!.stopRecording()
|
||||
if (response != nil) {
|
||||
sendAudio(audio: response!)
|
||||
}
|
||||
UIView.animate(withDuration: 0.1) {
|
||||
self.circle.tintColor = .systemYellow
|
||||
let newT = CGAffineTransform(scaleX: 1.4, y: 1.4)
|
||||
self.appendTranslation(transform: newT)
|
||||
feedback.prepare()
|
||||
feedback.impactOccurred()
|
||||
}
|
||||
}
|
||||
}
|
||||
// stop recording and send the audio
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
func establishConnection() { //connect to the web socket
|
||||
if (address != nil) {
|
||||
var request = URLRequest(url: URL(string: "http://\(address!)")!)
|
||||
request.timeoutInterval = 5
|
||||
socket = WebSocket(request: request)
|
||||
socket!.delegate = self
|
||||
socket!.connect()
|
||||
} else {
|
||||
setAddress()
|
||||
}
|
||||
}
|
||||
|
||||
func didReceive(event: Starscream.WebSocketEvent, client: any Starscream.WebSocketClient) { // deal with receiving data from websocket
|
||||
switch event {
|
||||
case .connected( _):
|
||||
isConnected = true
|
||||
reconnectIcon.tintColor = .green
|
||||
case .disconnected(let reason, let code):
|
||||
isConnected = false
|
||||
reconnectIcon.tintColor = .red
|
||||
case .text(let string):
|
||||
if (terminal) {
|
||||
terminalFeed.text = terminalFeed.text + "\n>> \(string)"
|
||||
let range = NSMakeRange(terminalFeed.text.count - 1, 0)
|
||||
terminalFeed.scrollRangeToVisible(range)
|
||||
}
|
||||
if (string.contains("audio") && string.contains("bytes.raw") && string.contains("start")) {
|
||||
infoText.text = "Receiving response..."
|
||||
// it started collecting data!
|
||||
print("Audio is being receieved.")
|
||||
} else if (string.contains("audio") && string.contains("bytes.raw") && string.contains("end")) {
|
||||
infoText.text = ""
|
||||
print("Audio is no longer being receieved.")
|
||||
let wavHeader = createWAVHeader(audioDataSize: Int32(audioData.count - 44))
|
||||
// Combine header and data
|
||||
var completeWAVData = Data()
|
||||
completeWAVData.append(wavHeader)
|
||||
completeWAVData.append(audioData.subdata(in: 44..<audioData.count))
|
||||
do {
|
||||
try audioPlayer = AVAudioPlayer(data: completeWAVData)
|
||||
audioPlayer?.prepareToPlay()
|
||||
audioPlayer?.play()
|
||||
} catch {
|
||||
print("Error")
|
||||
}
|
||||
|
||||
}
|
||||
print("Received text: \(string)")
|
||||
case .binary(let data):
|
||||
audioData.append(data)
|
||||
print("Received data: \(data.count)")
|
||||
|
||||
case .ping(_):
|
||||
break
|
||||
case .pong(_):
|
||||
break
|
||||
case .viabilityChanged(_):
|
||||
break
|
||||
case .reconnectSuggested(_):
|
||||
break
|
||||
case .cancelled:
|
||||
isConnected = false
|
||||
reconnectIcon.tintColor = .red
|
||||
case .error(_):
|
||||
isConnected = false
|
||||
reconnectIcon.tintColor = .red
|
||||
case .peerClosed:
|
||||
isConnected = false
|
||||
reconnectIcon.tintColor = .red
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
func createWAVHeader(audioDataSize: Int32) -> Data {
|
||||
let headerSize: Int32 = 44 // Standard WAV header size
|
||||
let chunkSize: Int32 = 36 + audioDataSize
|
||||
let sampleRate: Int32 = 16000 // From i2s_config
|
||||
let numChannels: Int16 = 1 // From i2s_config (mono)
|
||||
let bitsPerSample: Int16 = 16 // From i2s_config
|
||||
let byteRate: Int32 = sampleRate * Int32(numChannels) * Int32(bitsPerSample) / 8
|
||||
let blockAlign: Int16 = numChannels * bitsPerSample / 8
|
||||
|
||||
var headerData = Data()
|
||||
|
||||
// RIFF Chunk
|
||||
headerData.append(stringToData("RIFF")) // ChunkID
|
||||
headerData.append(int32ToData(chunkSize)) // ChunkSize
|
||||
headerData.append(stringToData("WAVE")) // Format
|
||||
|
||||
// fmt Subchunk
|
||||
headerData.append(stringToData("fmt ")) // Subchunk1ID
|
||||
headerData.append(int32ToData(16)) // Subchunk1Size (16 for PCM)
|
||||
headerData.append(int16ToData(1)) // AudioFormat (1 for PCM)
|
||||
headerData.append(int16ToData(numChannels)) // NumChannels
|
||||
headerData.append(int32ToData(sampleRate)) // SampleRate
|
||||
headerData.append(int32ToData(byteRate)) // ByteRate
|
||||
headerData.append(int16ToData(blockAlign)) // BlockAlign
|
||||
headerData.append(int16ToData(bitsPerSample)) // BitsPerSample
|
||||
|
||||
// data Subchunk
|
||||
headerData.append(stringToData("data")) // Subchunk2ID
|
||||
headerData.append(int32ToData(audioDataSize)) // Subchunk2Size
|
||||
|
||||
return headerData
|
||||
}
|
||||
|
||||
func stringToData(_ string: String) -> Data {
|
||||
return string.data(using: .utf8)!
|
||||
}
|
||||
|
||||
func int16ToData(_ value: Int16) -> Data {
|
||||
var value = value.littleEndian
|
||||
return Data(bytes: &value, count: MemoryLayout<Int16>.size)
|
||||
}
|
||||
|
||||
func int32ToData(_ value: Int32) -> Data {
|
||||
var value = value.littleEndian
|
||||
return Data(bytes: &value, count: MemoryLayout<Int32>.size)
|
||||
}
|
||||
|
||||
|
||||
func sendAudio(audio: Data) {
|
||||
if (isConnected) {
|
||||
socket!.write(string: "{\"role\": \"user\", \"type\": \"audio\", \"format\": \"bytes.raw\", \"start\": true}")
|
||||
socket!.write(data: audio)
|
||||
socket!.write(string: "{\"role\": \"user\", \"type\": \"audio\", \"format\": \"bytes.raw\", \"end\": true}")
|
||||
} else {
|
||||
print("Not connected!")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in new issue