Browse Source

Add new Class

marinofaggiana 6 years ago
parent
commit
9405e97be2

+ 16 - 0
Nextcloud.xcodeproj/project.pbxproj

@@ -551,6 +551,8 @@
 		F7DFB7F2219C5C0000680748 /* NCCreateFormUploadFileText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7DFB7F1219C5C0000680748 /* NCCreateFormUploadFileText.swift */; };
 		F7DFB7F4219C5CA800680748 /* NCCreateFormUploadScanDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7DFB7F3219C5CA800680748 /* NCCreateFormUploadScanDocument.swift */; };
 		F7DFB7F6219C5F2300680748 /* NCCreateFormUploadRichdocuments.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7DFB7F5219C5F2300680748 /* NCCreateFormUploadRichdocuments.storyboard */; };
+		F7E0E1DC22327885006B0911 /* NCAudioRecorderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E0E1DB22327885006B0911 /* NCAudioRecorderViewController.swift */; };
+		F7E0E1DE22327DBA006B0911 /* NCAudioRecorderViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7E0E1DD22327DBA006B0911 /* NCAudioRecorderViewController.storyboard */; };
 		F7E9C41B20F4CA870040CF18 /* CCTransfers.m in Sources */ = {isa = PBXBuildFile; fileRef = F7E9C41820F4CA870040CF18 /* CCTransfers.m */; };
 		F7ECBA6D1E239DCD003E6328 /* NCCreateFormUploadRichdocuments.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7ECBA6C1E239DCD003E6328 /* NCCreateFormUploadRichdocuments.swift */; };
 		F7F54CE51E5B14C700E19C62 /* ImageError.png in Resources */ = {isa = PBXBuildFile; fileRef = F7F54CAF1E5B14C700E19C62 /* ImageError.png */; };
@@ -1391,6 +1393,8 @@
 		F7DFB7F1219C5C0000680748 /* NCCreateFormUploadFileText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCreateFormUploadFileText.swift; sourceTree = "<group>"; };
 		F7DFB7F3219C5CA800680748 /* NCCreateFormUploadScanDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCreateFormUploadScanDocument.swift; sourceTree = "<group>"; };
 		F7DFB7F5219C5F2300680748 /* NCCreateFormUploadRichdocuments.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCCreateFormUploadRichdocuments.storyboard; sourceTree = "<group>"; };
+		F7E0E1DB22327885006B0911 /* NCAudioRecorderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCAudioRecorderViewController.swift; sourceTree = "<group>"; };
+		F7E0E1DD22327DBA006B0911 /* NCAudioRecorderViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCAudioRecorderViewController.storyboard; sourceTree = "<group>"; };
 		F7E45E6D21E75BF200579249 /* ja-JP */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ja-JP"; path = "ja-JP.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		F7E9C41520F4CA870040CF18 /* CCTransfers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTransfers.h; sourceTree = "<group>"; };
 		F7E9C41820F4CA870040CF18 /* CCTransfers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTransfers.m; sourceTree = "<group>"; };
@@ -2785,6 +2789,15 @@
 			path = "Create cloud";
 			sourceTree = "<group>";
 		};
+		F7E0E1DA22327885006B0911 /* AudioRecorder */ = {
+			isa = PBXGroup;
+			children = (
+				F7E0E1DB22327885006B0911 /* NCAudioRecorderViewController.swift */,
+				F7E0E1DD22327DBA006B0911 /* NCAudioRecorderViewController.storyboard */,
+			);
+			path = AudioRecorder;
+			sourceTree = "<group>";
+		};
 		F7E9C41320F4CA870040CF18 /* Transfers */ = {
 			isa = PBXGroup;
 			children = (
@@ -2938,6 +2951,7 @@
 				F78F74322163753B00C2ADAD /* Trash */,
 				F7E9C41320F4CA870040CF18 /* Transfers */,
 				F70784811A2C8A0D00AC9FFF /* UploadFromOtherUpp */,
+				F7E0E1DA22327885006B0911 /* AudioRecorder */,
 				F7BFFA991A24D7BB0044ED85 /* Utility */,
 			);
 			path = iOSClient;
@@ -3332,6 +3346,7 @@
 				F762CB981EACB84400B38484 /* icon-info@2x.png in Resources */,
 				F7D423361F0596AC009C9782 /* AppIcon-167.png in Resources */,
 				F7F54CF71E5B14C700E19C62 /* PlayButtonOverlayLargeTap.png in Resources */,
+				F7E0E1DE22327DBA006B0911 /* NCAudioRecorderViewController.storyboard in Resources */,
 				F710E8111EF95C9C00DC2427 /* ImagesIntro.xcassets in Resources */,
 				F77B0F4D1D118A16002130FE /* CCShare.storyboard in Resources */,
 				F7F54D021E5B14C700E19C62 /* UIBarButtonItemGrid@3x.png in Resources */,
@@ -3843,6 +3858,7 @@
 				F7D4238C1F0596C6009C9782 /* UIXToolbarView.m in Sources */,
 				F726EEEC1FED1C820030B9C8 /* NCEndToEndInitialize.swift in Sources */,
 				F79A65C62191D95E00FF6DCC /* NCSelect.swift in Sources */,
+				F7E0E1DC22327885006B0911 /* NCAudioRecorderViewController.swift in Sources */,
 				F70CAE3A1F8CF31A008125FD /* NCEndToEndEncryption.m in Sources */,
 				F73B4F0C1F470D9100BBEE4B /* nsHebrewProber.cpp in Sources */,
 				F762CAFB1EACB66200B38484 /* XLFormDatePickerCell.m in Sources */,

+ 99 - 0
iOSClient/AudioRecorder/NCAudioRecorderViewController.storyboard

@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="9IE-bj-VJb">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--Audio Recorder View Controller-->
+        <scene sceneID="eNh-2I-c1P">
+            <objects>
+                <viewController storyboardIdentifier="RecorderViewController" modalPresentationStyle="currentContext" id="9IE-bj-VJb" customClass="NCAudioRecorderViewController" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="fma-yb-dlL"/>
+                        <viewControllerLayoutGuide type="bottom" id="6hQ-x4-s9V"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="tQN-Gk-6M1">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ztv-M0-yUI" userLabel="contentContainerView">
+                                <rect key="frame" x="87.5" y="208.5" width="200" height="250"/>
+                                <subviews>
+                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vr6-IX-Yee" customClass="VoiceRecordHUD" customModule="Nextcloud" customModuleProvider="target">
+                                        <rect key="frame" x="7" y="32" width="186" height="186"/>
+                                        <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
+                                        <constraints>
+                                            <constraint firstAttribute="width" secondItem="vr6-IX-Yee" secondAttribute="height" id="c7R-AE-rou"/>
+                                        </constraints>
+                                        <userDefinedRuntimeAttributes>
+                                            <userDefinedRuntimeAttribute type="number" keyPath="rate">
+                                                <real key="value" value="0.5"/>
+                                            </userDefinedRuntimeAttribute>
+                                            <userDefinedRuntimeAttribute type="color" keyPath="fillColor">
+                                                <color key="value" red="0.0" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            </userDefinedRuntimeAttribute>
+                                        </userDefinedRuntimeAttributes>
+                                    </view>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="120″" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yIp-rq-klm">
+                                        <rect key="frame" x="85" y="15" width="30" height="17"/>
+                                        <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                        <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="bottom" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Lnv-LR-qq5">
+                                        <rect key="frame" x="0.0" y="0.0" width="200" height="250"/>
+                                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                                        <state key="normal" title="Tap to finish">
+                                            <color key="titleColor" red="0.94117647059999998" green="0.91764705879999997" blue="0.85490196080000003" alpha="1" colorSpace="calibratedRGB"/>
+                                        </state>
+                                        <connections>
+                                            <action selector="stop" destination="9IE-bj-VJb" eventType="touchUpInside" id="fjb-L2-YTP"/>
+                                        </connections>
+                                    </button>
+                                </subviews>
+                                <color key="backgroundColor" red="0.32549019610000002" green="0.83529411760000005" blue="0.76078431369999999" alpha="1" colorSpace="calibratedRGB"/>
+                                <constraints>
+                                    <constraint firstItem="yIp-rq-klm" firstAttribute="top" secondItem="Ztv-M0-yUI" secondAttribute="top" constant="15" id="1S8-2v-d7k"/>
+                                    <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="vr6-IX-Yee" secondAttribute="trailing" id="6e6-C9-ZWQ"/>
+                                    <constraint firstItem="yIp-rq-klm" firstAttribute="bottom" secondItem="vr6-IX-Yee" secondAttribute="top" id="E3m-Yu-zdJ"/>
+                                    <constraint firstAttribute="height" constant="250" id="G5W-ie-MCH"/>
+                                    <constraint firstItem="Lnv-LR-qq5" firstAttribute="leading" secondItem="Ztv-M0-yUI" secondAttribute="leading" id="JlM-Ww-iut"/>
+                                    <constraint firstAttribute="bottom" secondItem="Lnv-LR-qq5" secondAttribute="bottom" id="Kzr-BQ-zK4"/>
+                                    <constraint firstAttribute="trailing" secondItem="Lnv-LR-qq5" secondAttribute="trailing" id="NNn-Ce-Y8V"/>
+                                    <constraint firstAttribute="width" constant="200" id="Qu2-eD-5VF"/>
+                                    <constraint firstItem="vr6-IX-Yee" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Ztv-M0-yUI" secondAttribute="leading" id="Wmx-H6-3OK"/>
+                                    <constraint firstAttribute="centerX" secondItem="vr6-IX-Yee" secondAttribute="centerX" id="Zvz-EB-nL6"/>
+                                    <constraint firstAttribute="centerX" secondItem="yIp-rq-klm" secondAttribute="centerX" id="hwa-S6-oPz"/>
+                                    <constraint firstAttribute="centerY" secondItem="vr6-IX-Yee" secondAttribute="centerY" id="iqe-Ov-fVD"/>
+                                    <constraint firstItem="Lnv-LR-qq5" firstAttribute="top" secondItem="Ztv-M0-yUI" secondAttribute="top" id="mrG-va-2Su"/>
+                                </constraints>
+                                <userDefinedRuntimeAttributes>
+                                    <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
+                                        <integer key="value" value="10"/>
+                                    </userDefinedRuntimeAttribute>
+                                </userDefinedRuntimeAttributes>
+                            </view>
+                        </subviews>
+                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                        <constraints>
+                            <constraint firstItem="Ztv-M0-yUI" firstAttribute="centerY" secondItem="tQN-Gk-6M1" secondAttribute="centerY" id="4ah-zH-qPv"/>
+                            <constraint firstItem="Ztv-M0-yUI" firstAttribute="centerX" secondItem="tQN-Gk-6M1" secondAttribute="centerX" id="QMu-hw-0s3"/>
+                        </constraints>
+                    </view>
+                    <connections>
+                        <outlet property="durationLabel" destination="yIp-rq-klm" id="a8F-JI-uNe"/>
+                        <outlet property="tapToFinishBtn" destination="Lnv-LR-qq5" id="vsS-bs-9DO"/>
+                        <outlet property="voiceRecordHUD" destination="vr6-IX-Yee" id="s4E-Ka-QV9"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="Nq7-s5-9VP" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="599" y="140"/>
+        </scene>
+    </scenes>
+</document>

+ 288 - 0
iOSClient/AudioRecorder/NCAudioRecorderViewController.swift

@@ -0,0 +1,288 @@
+//
+//  NCAudioRecorderViewController.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 08/03/19.
+//  Copyright (c) 2017 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  --------------------------------
+//  Based on code of Venkat Kukunuru
+//  --------------------------------
+
+import Foundation
+import UIKit
+import AVFoundation
+import QuartzCore
+
+protocol NCAudioRecorderViewControllerDelegate : class {
+    func didFinishRecording(_ NCAudioRecorderViewController: NCAudioRecorderViewController)
+}
+
+class NCAudioRecorderViewController: UIViewController , NCAudioRecorderDelegate {
+    
+    open weak var delegate: NCAudioRecorderViewControllerDelegate?
+    var recording: NCAudioRecorder!
+    var recordDuration = 0
+    
+    @IBOutlet weak var tapToFinishBtn: UIButton!
+    @IBOutlet weak var durationLabel: UILabel!
+    @IBOutlet weak var voiceRecordHUD: VoiceRecordHUD!
+    
+    // MARK: View Life Cycle
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        //createRecorder()
+    }
+    
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        
+        voiceRecordHUD.update(0.0)
+        voiceRecordHUD.fillColor = UIColor.green
+        durationLabel.text = ""
+    }
+    
+    func createRecorder(fileName: String) {
+        recording = NCAudioRecorder(to: fileName) //"recording.m4a"
+        recording.delegate = self
+        
+        // Optionally, you can prepare the recording in the background to
+        // make it start recording faster when you hit `record()`.
+        
+        DispatchQueue.global().async {
+            // Background thread
+            do {
+                try self.recording.prepare()
+            } catch {
+                print(error)
+            }
+        }
+    }
+    
+    func startRecording() {
+        recordDuration = 0
+        do {
+            try recording.record()
+        } catch {
+            print(error)
+        }
+    }
+    
+    @IBAction func stop() {
+        
+        delegate?.didFinishRecording(self)
+        dismiss(animated: true, completion: nil)
+        
+        recordDuration = 0
+        recording.stop()
+        voiceRecordHUD.update(0.0)
+        
+    }
+    
+    func audioMeterDidUpdate(_ db: Float) {
+        print("db level: %f", db)
+        
+        self.recording.recorder?.updateMeters()
+        let ALPHA = 0.05
+        let peakPower = pow(10, (ALPHA * Double((self.recording.recorder?.peakPower(forChannel: 0))!)))
+        var rate: Double = 0.0
+        if (peakPower <= 0.2) {
+            rate = 0.2
+        } else if (peakPower > 0.9) {
+            rate = 1.0
+        } else {
+            rate = peakPower
+        }
+        
+        voiceRecordHUD.update(CGFloat(rate))
+        voiceRecordHUD.fillColor = UIColor.green
+        recordDuration += 1
+        durationLabel.text = String(recordDuration)
+    }
+    
+}
+
+@objc public protocol NCAudioRecorderDelegate: AVAudioRecorderDelegate {
+    @objc optional func audioMeterDidUpdate(_ dB: Float)
+}
+
+open class NCAudioRecorder : NSObject {
+    
+    @objc public enum State: Int {
+        case none, record, play
+    }
+    
+    static var directory: String {
+        return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
+    }
+    
+    open weak var delegate: NCAudioRecorderDelegate?
+    open fileprivate(set) var url: URL
+    open fileprivate(set) var state: State = .none
+    
+    open var bitRate = 192000
+    open var sampleRate = 44100.0
+    open var channels = 1
+    
+    fileprivate let session = AVAudioSession.sharedInstance()
+    var recorder: AVAudioRecorder?
+    fileprivate var player: AVAudioPlayer?
+    fileprivate var link: CADisplayLink?
+    
+    var metering: Bool {
+        return delegate?.responds(to: #selector(NCAudioRecorderDelegate.audioMeterDidUpdate(_:))) == true
+    }
+    
+    // MARK: - Initializers
+    
+    public init(to: String) {
+        url = URL(fileURLWithPath: NCAudioRecorder.directory).appendingPathComponent(to)
+        super.init()
+    }
+    
+    // MARK: - Record
+    
+    open func prepare() throws {
+        let settings: [String: AnyObject] = [
+            AVFormatIDKey : NSNumber(value: Int32(kAudioFormatAppleLossless) as Int32),
+            AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue as AnyObject,
+            AVEncoderBitRateKey: bitRate as AnyObject,
+            AVNumberOfChannelsKey: channels as AnyObject,
+            AVSampleRateKey: sampleRate as AnyObject
+        ]
+        
+        recorder = try AVAudioRecorder(url: url, settings: settings)
+        recorder?.prepareToRecord()
+        recorder?.delegate = delegate
+        recorder?.isMeteringEnabled = metering
+    }
+    
+    open func record() throws {
+        if recorder == nil {
+            try prepare()
+        }
+        
+        try session.setCategory(.playAndRecord, mode: .default)
+        try session.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
+        
+        recorder?.record()
+        state = .record
+        
+        if metering {
+            startMetering()
+        }
+    }
+    
+    // MARK: - Playback
+    
+    open func play() throws {
+        try session.setCategory(.playback, mode: .default)
+        
+        player = try AVAudioPlayer(contentsOf: url)
+        player?.play()
+        state = .play
+    }
+    
+    open func stop() {
+        switch state {
+        case .play:
+            player?.stop()
+            player = nil
+        case .record:
+            recorder?.stop()
+            recorder = nil
+            stopMetering()
+        default:
+            break
+        }
+        
+        state = .none
+    }
+    
+    // MARK: - Metering
+    
+    @objc func updateMeter() {
+        guard let recorder = recorder else { return }
+        
+        recorder.updateMeters()
+        
+        let dB = recorder.averagePower(forChannel: 0)
+        
+        delegate?.audioMeterDidUpdate?(dB)
+    }
+    
+    fileprivate func startMetering() {
+        link = CADisplayLink(target: self, selector: #selector(NCAudioRecorder.updateMeter))
+        link?.add(to: RunLoop.current, forMode: RunLoop.Mode.common)
+    }
+    
+    fileprivate func stopMetering() {
+        link?.invalidate()
+        link = nil
+    }
+}
+
+@IBDesignable
+class VoiceRecordHUD: UIView {
+    @IBInspectable var rate: CGFloat = 0.0
+    
+    @IBInspectable var fillColor: UIColor = UIColor.green {
+        didSet {
+            setNeedsDisplay()
+        }
+    }
+    var image: UIImage! {
+        didSet {
+            setNeedsDisplay()
+        }
+    }
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        image = UIImage(named: "Microphone")
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        image = UIImage(named: "Microphone")
+    }
+    
+    func update(_ rate: CGFloat) {
+        self.rate = rate
+        setNeedsDisplay()
+    }
+    
+    override func draw(_ rect: CGRect) {
+        let context = UIGraphicsGetCurrentContext()
+        context?.translateBy(x: 0, y: bounds.size.height)
+        context?.scaleBy(x: 1, y: -1)
+        
+        context?.draw(image.cgImage!, in: bounds)
+        context?.clip(to: bounds, mask: image.cgImage!)
+        
+        context?.setFillColor(fillColor.cgColor.components!)
+        context?.fill(CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height * rate))
+    }
+    
+    override func prepareForInterfaceBuilder() {
+        let bundle = Bundle(for: type(of: self))
+        image = UIImage(named: "Microphone", in: bundle, compatibleWith: self.traitCollection)
+    }
+}

+ 15 - 0
iOSClient/Images.xcassets/microphone.imageset/Contents.json

@@ -0,0 +1,15 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "Microphone.png"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "preserves-vector-representation" : true
+  }
+}

BIN
iOSClient/Images.xcassets/microphone.imageset/Microphone.png