MediaManager volume control for Xamarin.Forms - android

I'm trying to make a radio app with MediaManager on Xamarin.Forms for Android and iOS. I want to include a volume slider in the app. I've implemented it like this in XAML:
<Slider x:Name="slider" Grid.Column="1" Grid.ColumnSpan="3" Grid.Row="4" VerticalOptions="Center" ValueChanged="ChangeMediaVolume" Minimum="0" Maximum="10" Value="5" Margin="5"/>
And for the code behind I've used this method:
private void ChangeMediaVolume(object sender, ValueChangedEventArgs args)
{
int value = (int)slider.Value;
CrossMediaManager.Current.Volume.MaxVolume = 10;
CrossMediaManager.Current.Volume.CurrentVolume = value;
}
It works perfectly in the iOS emulator, but when I launch my Android emulator, it crashes and highlights the CrossMediaManager items with the message
System.NullReferenceException has been thrown. Object reference not
set to an instance of an object.
I'm not quite sure how to fix it or why it works for one platform and not the other.

Yes. This is a plugin issue, I got the same result, here is a workaround for android.
You can use dependenceService to achieve that.
1.Create a interface.
public interface IControlVolume
{
int setControlVolume(int value);
}
2.Achieve it in android.
[assembly: Dependency(typeof(MyControlVolume))]
namespace VideoPlay.Droid
{
class MyControlVolume : IControlVolume
{
public int setControlVolume(int value)
{
var audioMgr = (AudioManager)Forms.Context.GetSystemService(Context.AudioService);
//set the volume
audioMgr.SetStreamVolume(Android.Media.Stream.Music, value,VolumeNotificationFlags.PlaySound);
//get current value
int Volume = audioMgr.GetStreamVolume(Android.Media.Stream.Music);
return Volume;
}
}
}
Here is running gif.
https://imgur.com/a/odE79sc

So I seem to have figured out the issue. When I launch the app, the player doesn’t start until the play button has been pressed. Because of this, when the app launches the crossmediamanager isn’t fully instantiated. This is why it’s returning a null. I caught the exception so that it would load, and once the player starts everything works perfectly.

Related

Unity Ads 4.0 - Multiple OnUnityAdsShowComplete Callbacks for Rewarded ads

Using the Rewarded ad script found on the Unity Ads SDK, I'm running into an issue where the ShowAd() IUnityAdsShowListener => OnUnityAdsShowComplete is firing the debug log incrementally. The first Ad I watch returns one line stating the ad is completed, the second Ad I watch, fires off 2 logs, the third 3, then 4, etc...As if each ShowAd() subscribes a new listener to the callback. is this normal?
Im not using a button to call the ShowAd method, rather I'm just calling a function with a delegate from an AdsManager class.
public delegate void OnSuccessfulAd();
private OnSuccessfulAd _myCallback = null;
public void ShowAd(OnSuccessfulAd myMethod)
{
_myCallback = myMethod;
Advertisement.Show(_adUnitId, this);
}
public void OnUnityAdsShowComplete(string adUnitId, UnityAdsShowCompletionState showCompletionState)
{
if (adUnitId.Equals(_adUnitId) && showCompletionState.Equals(UnityAdsShowCompletionState.COMPLETED))
{
Debug.Log("Unity Ads Rewarded Ad Completed"); //Gets called more times each ad
// Grant a reward.
_myCallback?.Invoke();
}
}
Experiencing the same issue. As a temporary solution I nullify _myCallback after invoking it
I am working on the Ads 4.0.0 version in Unity.
What I observed even for Button Click is that its giving multiple Logs in Unity Editor.
However if I run the Code in my mobile Build its working as expected only 1 Log gets generated and Reward also remains same on multiple ad watches.
The issues seems to be in the CallBack in unity, My suggestion would be Try the Build in your App Once Hopefully that fixes the issue.
Unity Rewarded Ads Code from Official Page
As Unity has Deprecated most of the Previous Listeners in the New Ads 4.0.0 I think its better to cross check in the Build.
I was facing the exact same issue.
My OnUnityAdsShowComplete event callback is invoking one of my delegate.
Every references in my delegate was not accessible.
Look like the OnUnityAdsShowComplete is done on a different thread than the main one.
If in your delegate callback you have a simple Time.timeScale=1f, this should cause a crash since Time is no reachable.
Also this has observable only with reward Ads type.
My solution was to call the show() method inside a coroutine.
By having a flag set i.e. isAdRunning
You can poll that flag and when the flag is false isAdRunnin = false then you can call your delegate.
Very annoying.
Here an example. To make the code the simpliest as possible. All the IUnityAds interfaces are in the same class. This is why you will see this as listener arguments.
using UnityEngine.Advertisements;
private bool isAdsRunning = false;
// Ads start callback
public void OnUnityAdsShowStart(string placementId)
{
isAdsRunning = true;
}
// Ads complete callback
public void OnUnityAdsShowComplete(string placementId, UnityAdsShowCompletionState showCompletionState)
{
isAdsRunning = false;
}
// Entry point to show reward ads
public void ShowRewardedAd()
{
StartCoroutine(RewardRoutine());
}
IEnumerator RewardRoutine()
{
while (Advertisement.isShowing)
{
yield return null;
}
// In 4.0 Ads need to be loaded first, after initialization
// just another flag to make sure everything is initialized :)
while (!isAdLoaded)
{
yield return null;
}
// Show Ads
Advertisement.Show(adsUnitIdReward, this);
yield return new WaitForSeconds(0.25f);
while(isAdsRunning)
{
yield return null;
}
// My custom delegate
AdAction.Invoke();
}
Peace and Love.

Pause background service in Xamarin.Forms

I have an application that occasionally speaks via the systems text to speech(TTS) system, but if there's a background service (like an audiobook, or music stream) running at the same time they overlap.
I would like to pause the media, play my TTS, then unpause the media. I've looked, but can't find any solutions.
I believe if I were to play actual audio from my app, it would pause the media until my playback was complete (if I understand what I've found correctly). But TTS doesn't seem to have the same affect. The speech is totally dynamic, so I can't just record all the options.
Using the latest Xamarin.Forms, I've looked into all the media nuget packages I could find, and they all seem pretty centered on controlling media from files.
My only potential thought (I don't like it), is to maybe play an empty audio file while the TTS is running. But would like a more elegant solution if it exists.
(I don't care about iOS at the moment, so if it's an android only solution, I'm okay with it. And if it's native (java/kotlin), I can convert/incorporate it.)
Agree with rbonestell said, you can use DependencyService and AudioFocus to achieve it, when you record the audio, you can create interface in PCL.
public interface IControl
{
void StopBackgroundMusic();
}
When you record the audio, you can executed the DependencyService with following code.
private void Button_Clicked(object sender, EventArgs e)
{
DependencyService.Get<IControl>().StopBackgroundMusic();
//record the audio
}
In android folder, you can create a StopMusicService to achieve that.
[assembly: Dependency(typeof(StopMusicService))]
namespace TTSDemo.Droid
{
public class StopMusicService : IControl
{
AudioManager audioMan;
AudioManager.IOnAudioFocusChangeListener listener;
public void StopBackgroundMusic()
{
audioMan = (AudioManager)Android.App.Application.Context.GetSystemService(Context.AudioService);
listener = new MyAudioListener(this);
var ret = audioMan.RequestAudioFocus(listener, Stream.Music, AudioFocus.Gain);
}
}
internal class MyAudioListener :Java.Lang.Object, AudioManager.IOnAudioFocusChangeListener
{
private StopMusicService stopMusicService;
public MyAudioListener(StopMusicService stopMusicService)
{
this.stopMusicService = stopMusicService;
}
public void OnAudioFocusChange([GeneratedEnum] AudioFocus focusChange)
{
// throw new NotImplementedException();
}
}
}
Thanks to Leon Lu - MSFT, I was able to go in the right direction. I took his implementation (which has some deprecated calls to the Android API), and updated it for what I needed.
I'll be doing a little more work making sure it's stable and functional. I'll also see if I can clean it up a little too. But here's what works on my first test:
[assembly: Dependency(typeof(MediaService))]
namespace ...Droid.Services
{
public class MediaService : IMediaService
public async Task PauseBackgroundMusicForTask(Func<Task> onFocusGranted)
{
var manager = (AudioManager)Android.App.Application.Context.GetSystemService(Context.AudioService);
var builder = new AudioFocusRequestClass.Builder(AudioFocus.GainTransientMayDuck);
var focusRequest = builder.Build();
var ret = manager.RequestAudioFocus(focusRequest);
if (ret == AudioFocusRequest.Granted)
{
await onFocusGranted?.Invoke();
manager.AbandonAudioFocusRequest(focusRequest);
}
}
}
}

Unity Firebase Realtime Database ValueChanged Listener Null Value, Android Build Only

I first encountered this in my app, but found it is reproducible in minimalist projects as well. I have a public database (read & write: true) and in Unity I use the following script to add a listener to a value in my database and to update some text on the screen when that value changes. I have a button in my scene which calls "ToggleValue()" to switch between existing and null values.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Firebase.Database;
public class DataTest : MonoBehaviour {
public Text displayText;
private bool toggleState = false;
void Start () {
FirebaseDatabase.DefaultInstance.GetReference("test").ValueChanged += HandleTestChanged;
}
void HandleTestChanged(object sender, ValueChangedEventArgs args) {
if (args.DatabaseError != null) {
displayText.text = args.DatabaseError.Message;
Debug.LogError(args.DatabaseError.Message);
return;
} else {
displayText.text = args.Snapshot.Value.ToString();
}
}
public void ToggleValue() {
toggleState = !toggleState;
Dictionary<string, object> testChanges = new Dictionary<string, object>();
if (toggleState) {
testChanges.Add("test", true);
} else {
testChanges.Add("test", null);
}
FirebaseDatabase.DefaultInstance.RootReference.UpdateChildrenAsync(new Dictionary<string, object>(testChanges));
}
}
This works perfectly fine in the editor. When clicking the button the text on screen displays "True" and "null" at the appropriate times. However, when I build to Android, I get different results. The first time the value is set to True, the text updates to read "True". However, on the second press, when the listener should trigger with a value of "null", nothing happens. The "HandleTestChanged" function is not called, no new value is passed in. Having the Firebase Realtime Database console window open on my desktop I can see that the value is still being set correctly, it's just not updating the clients.
Is anyone else able to reproduce this? Does anyone have a workaround for this?
I am using Unity 2018.2.1f1, Firebase Unity SDK 5.2.0, Android 7.1.1.

How to get the android keyboard to appear when using Qt For Android?

I've created a minimal working example of an Input box I'd like to develop using a QGraphicsItem. Here is the code (I'd figure the .h is not necessary):
TestEditor::TestEditor()
{
text = "";
boundingBox = QRectF(0,0,200,100);
}
QRectF TestEditor::boundingRect() const{
return boundingBox;
}
void TestEditor::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){
painter->setBrush(QBrush(Qt::gray));
painter->drawRect(boundingBox);
painter->setBrush(QBrush(Qt::black));
painter->drawText(boundingBox,text);
}
void TestEditor::keyReleaseEvent(QKeyEvent *event){
qDebug() << "Aca toy";
text = text + event->text();
update();
}
My tester application is simply adding it to a graphics view to test it:
TestEditor *editor = new TestEditor();
editor->setText("Algo de texto como para empezar");
editor->setFlag(QGraphicsItem::ItemAcceptsInputMethod,true);
editor->setFlag(QGraphicsItem::ItemIsFocusable,true);
editor->setFlag(QGraphicsItem::ItemIsSelectable,true);
ui->gvScreen->scene()->addItem(editor);
When I test this on my PC it works fine. When I compile it for android, I get the problem that keyboard doesn't appear so I can't try it out. How can I force the keyboard to appear?
Well In case anyone is wondering I've found a way to force the android keyboard to show.
QInputMethod *keyboard = QGuiApplication::inputMethod();
keyboard->show();
I've lost the code where I used it so I don't rembember if QGuiApplication can be called from anywhere. But if it can't you can simply sotre the pointer to the keyboard from your main form/class and pass it as a parameter to any sort of required item or class

SDL android NDK managing return button

I am using the SDL-2.0.3 along with NDK-r10e, I'm attempting to make the return button switch the app to the background so I tried to use the function SDL_MinimizeWindow() but It does nothing ! is this a bug or do I miss something ?
here is my code :
if(event.key.keysym.sym == SDLK_AC_BACK)
{
SDL_MinimizeWindow(window);
SDL_Log("window minimized !\n");
}
everything just work fine and I get the log message when the button is pressed but the window is not minimized
That doesn't appear to be supported on Android (there's not really anything corresponding to minimizing a "window" on Android, unless you count finishing an Activity).
The SDL_MinimizeWindow function looks like this:
void
SDL_MinimizeWindow(SDL_Window * window)
{
CHECK_WINDOW_MAGIC(window, );
if (window->flags & SDL_WINDOW_MINIMIZED) {
return;
}
SDL_UpdateFullscreenMode(window, SDL_FALSE);
if (_this->MinimizeWindow) {
_this->MinimizeWindow(_this, window);
}
}
Where _this is an SDL_VideoDevice *, which is set to point to an SDL_VideoDevice for the appropriate platform at runtime. The Android video driver only sets up the following 3 Window-related functions:
device->CreateWindow = Android_CreateWindow;
device->SetWindowTitle = Android_SetWindowTitle;
device->DestroyWindow = Android_DestroyWindow;
Trying to perform any other operations on an SDL_Window on Android is likely to do nothing.
Some further information in the form of a couple of lines of code from SDL_androidwindow.c:
window->flags &= ~SDL_WINDOW_RESIZABLE; /* window is NEVER resizeable */
window->flags |= SDL_WINDOW_FULLSCREEN; /* window is always fullscreen */

Categories

Resources