Add MXKSoundPlayer
MODIFY
MatrixKit.xcodeproj/project.pbxproj
MatrixKit.xcodeproj/project.pbxproj
71DE22E71BCE3FF200284153 /* MXKRoomBubbleCellDataWithIncomingAppendingMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 71DE22E51BCE3FF200284153 /* MXKRoomBubbleCellDataWithIncomingAppendingMode.m */; };
71EBE6631C04608100E7D953 /* MXKRoomActivitiesView.m in Sources */ = {isa = PBXBuildFile; fileRef = 71EBE6621C04608100E7D953 /* MXKRoomActivitiesView.m */; };
79FA5AB121247287B85FA151 /* libPods-MatrixKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F0ED791C0728768E176BE57 /* libPods-MatrixKitTests.a */; };
+ 92663A6C1EF6E5B3005FB712 /* MXKSoundPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 92663A6B1EF6E5B3005FB712 /* MXKSoundPlayer.m */; };
ACD9DAA6B4DCFC7E33E12A97 /* libPods-MatrixKitSample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 665BACA3D253B4407B1C4FD7 /* libPods-MatrixKitSample.a */; };
CE14CA661E80122600E329A3 /* MXKAttachmentAnimator.m in Sources */ = {isa = PBXBuildFile; fileRef = CE14CA631E80122600E329A3 /* MXKAttachmentAnimator.m */; };
CE14CA671E80122600E329A3 /* MXKAttachmentInteractionController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE14CA651E80122600E329A3 /* MXKAttachmentInteractionController.m */; };
71EBE6611C04608100E7D953 /* MXKRoomActivitiesView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXKRoomActivitiesView.h; sourceTree = "<group>"; };
71EBE6621C04608100E7D953 /* MXKRoomActivitiesView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXKRoomActivitiesView.m; sourceTree = "<group>"; };
8C751E43C4D87B309065E3C8 /* Pods-MatrixKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-MatrixKitTests/Pods-MatrixKitTests.release.xcconfig"; sourceTree = "<group>"; };
+ 92663A6A1EF6E5B3005FB712 /* MXKSoundPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXKSoundPlayer.h; sourceTree = "<group>"; };
+ 92663A6B1EF6E5B3005FB712 /* MXKSoundPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXKSoundPlayer.m; sourceTree = "<group>"; };
9F0ED791C0728768E176BE57 /* libPods-MatrixKitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MatrixKitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
BFDF88630F521F8C5854F64F /* Pods-MatrixKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MatrixKitTests/Pods-MatrixKitTests.debug.xcconfig"; sourceTree = "<group>"; };
C1C97E32A7F1D6C4B99A47EF /* Pods-MatrixKitSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixKitSample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MatrixKitSample/Pods-MatrixKitSample.debug.xcconfig"; sourceTree = "<group>"; };
F0F148C41AB31240005F5D4A /* MXKTools.h */,
F0F148C51AB31240005F5D4A /* MXKTools.m */,
F0F535BC1ACD748E00B603F8 /* MXKResponderRageShaking.h */,
+ 92663A6A1EF6E5B3005FB712 /* MXKSoundPlayer.h */,
+ 92663A6B1EF6E5B3005FB712 /* MXKSoundPlayer.m */,
);
path = Utils;
sourceTree = "<group>";
F01449AD1B53FA7600EA7D73 /* MXKPushRuleCreationTableViewCell.m in Sources */,
3235CD771C32DC8A0084EA40 /* MXKSearchDataSource.m in Sources */,
F0CF98DD1B0B7FDC00EAE373 /* MXKTableViewCellWithLabelAndSubLabel.m in Sources */,
+ 92663A6C1EF6E5B3005FB712 /* MXKSoundPlayer.m in Sources */,
F00FA85D1C0867F900E25826 /* MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.m in Sources */,
F02D21491B7109950002DE01 /* MXKConstants.m in Sources */,
F07E18101ABC2EDA00DE3766 /* MXKRoomDataSource.m in Sources */,

ADD
MatrixKit/Utils/MXKSoundPlayer.h
+/*
+ Copyright 2017 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MXKSoundPlayer : NSObject
+
++ (instancetype)sharedInstance;
+
++ (instancetype)init NS_UNAVAILABLE;
++ (instancetype)new NS_UNAVAILABLE;
+
+- (void)playSoundAt:(NSURL *)url repeat:(BOOL)repeat vibrate:(BOOL)vibrate routeToBuiltInReciever:(BOOL)useBuiltInReciever;
+- (void)stopPlaying;
+
+- (void)vibrateWithRepeat:(BOOL)repeat;
+- (void)stopVibrating;
+
+@end
+
+NS_ASSUME_NONNULL_END

ADD
MatrixKit/Utils/MXKSoundPlayer.m
+/*
+ Copyright 2017 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "MXKSoundPlayer.h"
+
+#import <AVFoundation/AVFAudio.h>
+#import <AudioToolbox/AudioServices.h>
+
+static const NSTimeInterval kVibrationInterval = 1.24875;
+
+@interface MXKSoundPlayer () <AVAudioPlayerDelegate>
+
+@property (nonatomic, nullable) AVAudioPlayer *audioPlayer;
+
+@property (nonatomic, getter=isVibrating) BOOL vibrating;
+
+@end
+
+@implementation MXKSoundPlayer
+
++ (instancetype)sharedInstance
+{
+ static MXKSoundPlayer *soundPlayer;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ soundPlayer = [MXKSoundPlayer alloc];
+ });
+ return soundPlayer;
+}
+
+- (void)playSoundAt:(NSURL *)url repeat:(BOOL)repeat vibrate:(BOOL)vibrate routeToBuiltInReciever:(BOOL)useBuiltInReciever
+{
+ if (self.audioPlayer)
+ {
+ [self stopPlaying];
+ }
+
+ self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
+
+ if (!self.audioPlayer)
+ return;
+
+ self.audioPlayer.delegate = self;
+ self.audioPlayer.numberOfLoops = repeat ? -1 : 0;
+ [self.audioPlayer prepareToPlay];
+
+ // Setup AVAudioSession
+ // We use SoloAmbient instead of Playback category to respect silent mode
+ NSString *audioSessionCategory = useBuiltInReciever ? AVAudioSessionCategoryPlayAndRecord : AVAudioSessionCategorySoloAmbient;
+ [[AVAudioSession sharedInstance] setCategory:audioSessionCategory error:nil];
+
+ if (vibrate)
+ [self vibrateWithRepeat:repeat];
+
+ [self.audioPlayer play];
+}
+
+- (void)stopPlaying
+{
+ if (self.audioPlayer)
+ {
+ [self.audioPlayer stop];
+ self.audioPlayer = nil;
+
+ // Release the audio session to allow resuming of background music app
+ [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
+ }
+
+ if (self.isVibrating)
+ {
+ [self stopVibrating];
+ }
+}
+
+- (void)vibrateWithRepeat:(BOOL)repeat
+{
+ self.vibrating = YES;
+
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kVibrationInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
+
+ NSNumber *objRepeat = @(repeat);
+ AudioServicesAddSystemSoundCompletion(kSystemSoundID_Vibrate,
+ NULL,
+ kCFRunLoopCommonModes,
+ vibrationCompleted,
+ (__bridge_retained void * _Nullable)(objRepeat));
+ });
+}
+
+- (void)stopVibrating
+{
+ self.vibrating = NO;
+ AudioServicesRemoveSystemSoundCompletion(kSystemSoundID_Vibrate);
+}
+
+void vibrationCompleted(SystemSoundID ssID, void* __nullable clientData)
+{
+ NSNumber *objRepeat = (__bridge NSNumber *)clientData;
+ BOOL repeat = [objRepeat boolValue];
+ CFRelease(clientData);
+
+ MXKSoundPlayer *soundPlayer = [MXKSoundPlayer sharedInstance];
+
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kVibrationInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ if (repeat && soundPlayer.isVibrating)
+ {
+ [soundPlayer vibrateWithRepeat:repeat];
+ }
+ else
+ {
+ [soundPlayer stopVibrating];
+ }
+ });
+}
+
+#pragma mark - AVAudioPlayerDelegate
+
+// This method is called only when the end of the player's track is reached.
+// If you call `stop` or `pause` on player this method won't be called
+- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
+{
+ self.audioPlayer = nil;
+
+ // Release the audio session to allow resuming of background music app
+ [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
+}
+
+@end

Add MXKSoundPlayer