Skip to content

Commit 9064f1e

Browse files
committed
iOS volume button pluggin
1 parent 1380851 commit 9064f1e

9 files changed

Lines changed: 262 additions & 7 deletions

File tree

Assets/Plugins/VolumeButtonsPlugin/Android/VolumeButtonsActivity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
public class VolumeButtonsActivity extends UnityPlayerActivity {
1414

1515
/**
16-
* Keep in sync with VolumeButton.Event in C#
16+
* Keep in sync with VolumeButtonsEvent in C#
1717
*/
1818
private enum Event {
1919
VOLUME_DOWN(-1),

Assets/Plugins/VolumeButtonsPlugin/VolumeButtons.cs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections;
33
using System.Collections.Generic;
4+
using System.Runtime.InteropServices;
45

56
using UnityEngine;
67

@@ -18,7 +19,7 @@ void Start()
1819
#if UNITY_ANDROID
1920
_CallActivity("addGameObjectListener", gameObject.name);
2021
#elif UNITY_IOS
21-
// FIXME
22+
_AddGameObjectListener(gameObject.name, gameObject.name.Length);
2223
#else
2324
Debug.LogFormat(UnsupportedError, Application.platform, string.Join("", SupportedPlatforms));
2425
#endif
@@ -29,7 +30,7 @@ void OnDisable()
2930
#if UNITY_ANDROID
3031
_CallActivity("removeGameObjectListener", gameObject.name);
3132
#elif UNITY_IOS
32-
// FIXME
33+
_RemoveGameObjectListener(gameObject.name, gameObject.name.Length);
3334
#else
3435
Debug.LogFormat(UnsupportedError, Application.platform, string.Join("", SupportedPlatforms));
3536
#endif
@@ -47,14 +48,27 @@ private void _OnVolumeButtonEvent(string value)
4748
}
4849
}
4950

51+
#if UNITY_ANDROID
52+
5053
private void _CallActivity(string methodName, string arg0)
5154
{
5255
AndroidJNIHelper.debug = true;
53-
using (AndroidJavaClass unityPlayerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
54-
using (AndroidJavaObject activity = unityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity")) {
56+
using (AndroidJavaClass unityPlayerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
57+
{
58+
using (AndroidJavaObject activity = unityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity"))
59+
{
5560
Debug.LogFormat("Call {0}({1})", methodName, arg0);
5661
activity.Call(methodName, arg0);
5762
}
5863
}
5964
}
60-
}
65+
66+
#elif UNITY_IOS
67+
68+
[DllImport("__Internal", EntryPoint="VBP_addGameObjectListener")]
69+
private static extern void _AddGameObjectListener([MarshalAs(UnmanagedType.LPWStr)]string gameObjectName, int gameObjectNameLen);
70+
[DllImport("__Internal", EntryPoint="VBP_removeGameObjectListener")]
71+
private static extern void _RemoveGameObjectListener([MarshalAs(UnmanagedType.LPWStr)]string gameObjectName, int gameObjectNameLen);
72+
73+
#endif
74+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#import <Foundation/Foundation.h>
2+
3+
void VBP_addGameObjectListener(const char *go_name, int go_name_length);
4+
void VBP_removeGameObjectListener(const char *go_name, int go_name_length);

Assets/Plugins/VolumeButtonsPlugin/iOS/VolumeButtonsPlugin.h.meta

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#import "VolumeButtonsPlugin.h"
2+
3+
#import "UnityInterface.h"
4+
#import <AVFoundation/AVFoundation.h>
5+
#import <MediaPlayer/MediaPlayer.h>
6+
7+
@class _VBPAVAudioSessionObserver;
8+
9+
static const char* kMessageName = "_OnVolumeButtonEvent";
10+
static const char* kVolumeUp = "1";
11+
static const char* kVolumeDown = "-1";
12+
13+
static _VBPAVAudioSessionObserver *observer = NULL;
14+
15+
@interface _VBPAVAudioSessionObserver : NSObject {
16+
float _currentVolumeLevel;
17+
NSMutableArray<NSString *> *_gameObjectNames;
18+
MPVolumeView *_volumeView;
19+
}
20+
21+
-(void) addGameObject:(NSString *)objectName;
22+
-(void) removeGameObject:(NSString *)objectName;
23+
24+
@end
25+
26+
27+
@implementation _VBPAVAudioSessionObserver
28+
29+
-(id) init {
30+
if (self = [super init]) {
31+
_currentVolumeLevel = [[AVAudioSession sharedInstance] outputVolume];
32+
_gameObjectNames = [NSMutableArray new];
33+
}
34+
35+
return self;
36+
}
37+
38+
-(void) addGameObject:(NSString *)objectName {
39+
@synchronized(_gameObjectNames) {
40+
if (_gameObjectNames.count == 0) {
41+
AVAudioSession *session = [AVAudioSession sharedInstance];
42+
if (!session.isOtherAudioPlaying) {
43+
[session setActive:YES error:nil];
44+
}
45+
[session addObserver:self forKeyPath:@"outputVolume" options:NSKeyValueObservingOptionNew context:nil];
46+
}
47+
48+
[_gameObjectNames addObject:objectName];
49+
}
50+
}
51+
52+
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
53+
if ([@"outputVolume" isEqualToString:keyPath]) {
54+
float newVolumeLevel = [[AVAudioSession sharedInstance] outputVolume];
55+
@synchronized(_gameObjectNames) {
56+
for (NSString *gameObjectName in _gameObjectNames) {
57+
UnitySendMessage([gameObjectName cStringUsingEncoding:NSUTF8StringEncoding], kMessageName, (newVolumeLevel > _currentVolumeLevel ? kVolumeUp : kVolumeDown));
58+
}
59+
}
60+
61+
// https://forums.developer.apple.com/thread/107241
62+
if (newVolumeLevel > 0.999f) {
63+
_currentVolumeLevel = 0.9375f;
64+
[self hackSystemVolume:_currentVolumeLevel];
65+
} else if (newVolumeLevel < 0.001f) {
66+
_currentVolumeLevel = 0.0625f;
67+
[self hackSystemVolume:_currentVolumeLevel];
68+
} else {
69+
_currentVolumeLevel = newVolumeLevel;
70+
}
71+
} else {
72+
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
73+
}
74+
}
75+
76+
-(void) removeGameObject:(NSString *)objectName {
77+
@synchronized(_gameObjectNames) {
78+
[_gameObjectNames removeObject:objectName];
79+
80+
if (_gameObjectNames.count == 0) {
81+
AVAudioSession *session = [AVAudioSession sharedInstance];
82+
[session removeObserver:self forKeyPath:@"outputVolume"];
83+
}
84+
}
85+
}
86+
87+
-(void)hackSystemVolume:(float)volumeLevel {
88+
UISlider *view = [[[[MPVolumeView new] subviews] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"classForCoder == \"MPVolumeSlider\""]] firstObject];
89+
if ([view isKindOfClass:[UISlider class]]) {
90+
UISlider *slider = (UISlider *)view;
91+
[slider setValue:volumeLevel];
92+
}
93+
}
94+
95+
@end
96+
97+
void VBP_addGameObjectListener(const char *go_name, int go_name_length)
98+
{
99+
if (!observer) {
100+
observer = [_VBPAVAudioSessionObserver new];
101+
}
102+
103+
// https://answers.unity.com/questions/363476/how-to-pass-unicode-text-from-c-to-objective-c.html
104+
NSString* gon = [[NSString alloc]
105+
initWithBytes:go_name
106+
length:sizeof(__CHAR16_TYPE__) * go_name_length
107+
encoding:NSUTF16LittleEndianStringEncoding];
108+
109+
[observer addGameObject:gon];
110+
}
111+
112+
void VBP_removeGameObjectListener(const char *go_name, int go_name_length)
113+
{
114+
NSString* gon = [[NSString alloc]
115+
initWithBytes:go_name
116+
length:sizeof(__CHAR16_TYPE__) * go_name_length
117+
encoding:NSUTF16LittleEndianStringEncoding];
118+
119+
[observer removeGameObject:gon];
120+
}

Assets/Plugins/VolumeButtonsPlugin/iOS/VolumeButtonsPlugin.m.meta

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ProjectSettings/GraphicsSettings.asset

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ GraphicsSettings:
3333
- {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}
3434
- {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0}
3535
- {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0}
36-
- {fileID: 16003, guid: 0000000000000000f000000000000000, type: 0}
3736
- {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0}
3837
m_PreloadedShaders: []
3938
m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000,

ProjectSettings/ProjectSettings.asset

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ PlayerSettings:
165165
applicationIdentifier:
166166
Android: org.camobap.unity3d
167167
Standalone: com.Company.ProductName
168+
iPhone: org.camobap.unity3d
168169
buildNumber: {}
169170
AndroidBundleVersionCode: 1
170171
AndroidMinSdkVersion: 19
@@ -406,6 +407,9 @@ PlayerSettings:
406407
- m_BuildTarget: AndroidPlayer
407408
m_APIs: 150000000b000000
408409
m_Automatic: 0
410+
- m_BuildTarget: iOSSupport
411+
m_APIs: 08000000
412+
m_Automatic: 0
409413
m_BuildTargetVRSettings: []
410414
openGLRequireES31: 0
411415
openGLRequireES31AEP: 0

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Unity3d VolumeButtonsPlugin
2+
3+
This plugin was developed for Unity3d Android & iOS platforms to be able listen for Volume up/down button clicks, it doesn't override them
4+
5+
Implemented buttons:
6+
- [x] Volume Up
7+
- [x] Volume Down
8+
- [ ] Mute
9+
10+
## Getting Started
11+
12+
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.
13+
14+
### Prerequisites
15+
16+
```
17+
Unity3d 2019.x
18+
```
19+
20+
### Installing
21+
22+
To install this pulgin:
23+
- Copy `Assets/Plugin/VolumeButtonsPlugin` into your project
24+
- Make sure that you updated activity name to `org.camobap.unity3d.VolumeButtonsActivity` in your `Assets/Plugin/Android/AndroidManifest.xml`*
25+
- For iOS add `MediaPlayer.framework`
26+
27+
* If you already have another custom activity there, please change parent `UnityPlayerActivity` in `VolumeButtonsActivity.java` to yours one or vice-versa
28+
29+
## Running the tests
30+
31+
Test app have two blinking Panels
32+
33+
## Usage in your apps
34+
35+
For your propose you can define fuction with signature below:
36+
37+
```
38+
...
39+
40+
public void HandleVolumeButton(VolumeButtonsEventType e)
41+
{
42+
...
43+
}
44+
```
45+
46+
And attach it to `VolumeButtons.cs` component
47+
48+
## License
49+
50+
This project is licensed under the MIT License - see the [LICENSE.md](https://opensource.org/licenses/MIT) file for details

0 commit comments

Comments
 (0)