How can I use Android TextToSpeak in a MVVMCross plugin? - android

I have seen plenty of examples of how to use Android TextToSpeak in an Activity, and have also managed to get this to work just fine. I've also managed to get it to work using a bound service in a plugin, but it seems overcomplicated for my purposes. Here is my VoiceService class:
public class VoiceService : IVoiceService, TextToSpeech.IOnInitListener
{
public event EventHandler FinishedSpeakingEventHandler;
private TextToSpeech _tts;
public void Init()
{
// Use a speech progress listener so we get notified when the service finishes speaking the prompt
var progressListener = new SpeechProgressListener();
progressListener.FinishedSpeakingEventHandler += OnUtteranceCompleted;
//_tts = new TextToSpeech(Application.Context, this);
_tts = new TextToSpeech(Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity, this);
_tts.SetOnUtteranceProgressListener(progressListener);
}
public void OnInit(OperationResult status)
{
// THIS EVENT NEVER FIRES!
Console.WriteLine("VoiceService TextToSpeech Initialised. Status: " + status);
if (status == OperationResult.Success)
{
}
}
public void Speak(string prompt)
{
if (!string.IsNullOrEmpty(prompt))
{
var map = new Dictionary<string, string> { { TextToSpeech.Engine.KeyParamUtteranceId, new Guid().ToString() } };
_tts.Speak(prompt, QueueMode.Flush, map);
Console.WriteLine("tts_Speak: " + prompt);
}
else
{
Console.WriteLine("tts_Speak: PROMPT IS NULL OR EMPTY!");
}
}
/// <summary>
/// When we finish speaking, call the event handler
/// </summary>
public void OnUtteranceCompleted(object sender, EventArgs e)
{
if (FinishedSpeakingEventHandler != null)
{
FinishedSpeakingEventHandler(this, new EventArgs());
}
}
public void Dispose()
{
//throw new NotImplementedException();
}
public IntPtr Handle { get; private set; }
}
Note that the OnInit method never gets called.
In my viewmodel I'd like to do this:
_voiceService.Init();
_voiceService.FinishedSpeakingEventHandler += _voiceService_FinishedSpeakingEventHandler;
... and then later ...
_voiceService.Speak(prompt);
When I do this I get these messages in the output:
10-13 08:13:59.734 I/TextToSpeech( 2298): Sucessfully bound to com.google.android.tts
(happens when I create the new TTS object)
and
10-13 08:14:43.924 W/TextToSpeech( 2298): speak failed: not bound to TTS engine
(when I call tts.Speak(prompt))
If I was using an activity I would create an intent to get this to work, but I'm unsure how to do that in a plugin.
Thanks in advance,
David

Don't implement Handle yourself, instead derive from Java.Lang.Object
public class VoiceService : Java.Lang.Object, IVoiceService, TextToSpeech.IOnInitListener
and remove your Dispose() and Handle implementation
More info here: http://developer.xamarin.com/guides/android/advanced_topics/java_integration_overview/android_callable_wrappers/
Also, I suggest you take an async approach when implementing your service, which would make calling it from view-model something like
await MvxResolve<ITextToSpeechService>().SpeakAsync(text);

Related

How to pass a message from Native Android back to Flutter?

I know that we can pass data from Flutter to Native Android, like #UpaJah mentioned in one of his answers:
How to pass a message from Flutter to Native?
But i want to get a response from native android so that i can update my UI accordingly in Flutter. I just have to get response from native by any means. How can i do that?
Update:
I tried this piece of code as #liu-silong had mentioned in the answer:
bleChannel.invokeMethod("updateUI", 1, new MethodChannel.Result() {
#Override
public void success(#Nullable Object result) {
Log.d(TAG, "success");
}
#Override
public void error(String errorCode, #Nullable String errorMessage, #Nullable Object errorDetails) {
Log.d(TAG, "errorCode: " + errorCode);
}
#Override
public void notImplemented() {
Log.d(TAG, "notImplemented");
}
});
Now my code gets inside notImplemented callback. Any suggestion?
You can call Dart methods through MethodChannel on the Android side
Android:
private MethodChannel channel;
channel = new MethodChannel(getFlutterEngine().getDartExecutor(), "channel_name");
// invoke dart method (in the main thread)
channel.invokeMethod("foo", new HashMap<String, Object>());
Dart
final MethodChannel channel = new MethodChannel("channel_name");
channel.setMethodCallHandler(_methodCallHandler);
Future<dynamic> _methodCallHandler(MethodCall call) {
if(call.method == 'foo'){
// do sth...
}
}
Or you can also use EventChannel.
https://github.com/liusilong/stack_q

Making multiple asynchronous request using Retrofit

In my android application I have a screen where I have 3 spinners that need to be
filled from APIs call.
static List<TripCode> tripCodeList = new ArrayList<>();
static List<Fleet> truckList = new ArrayList<>();
static List<Trailer> trailerList = new ArrayList<>();
And I don't want to inflate the layout unless I get the response from all the 3 different API calls so this is what I'm doing
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
if (MyApplication.isConnected()) {
getTripCodes();
} else {
Toast.makeText(this, "No internet Connection", Toast.LENGTH_LONG).show();
setContentView(R.layout.no_internet_connection);
}
}
Basically , I removed setContentView(R.layout.activity_create_trip);
from onCreate() And I called getTripCodes()
here's the code for getTripCodes()
public void getTripCodes() {
MyApplication.showProgressDialog(getString(R.string.please_wait), this);
IMyAPI iMyAPI = MyApplication.getIMyAPI();
Call<List<TripCode>> call = iMyAPI.getTripCodes();
call.enqueue(new Callback<List<TripCode>>() {
#Override
public void onResponse(Call<List<TripCode>> call, Response<List<TripCode>> response) {
if (response.isSuccessful() && response.body() != null) {
tripCodeList = response.body();
Log.d("test", "getTripCodes success = " + tripCodeList.size());
getTrucks();
} else {
MyApplication.dismissProgressDialog();
}
}
#Override
public void onFailure(Call<List<TripCode>> call, Throwable t) {
MyApplication.dismissProgressDialog();
}
});
}
So in the success of the call I'm calling the other function getTrucks() which also get result from API and in the success it will call getTrailers()
But I think it's a waste of time, because I can call the three function all together in parallel, and then check if all the list are filled or not.
But I don't know how to do it. How can I check if all the calls are success? And if one of them has failed, how will I know which one exactly failed?
I Believe for your problem you can easily use Retrofit 2.6.0 which has coroutine support and you can declare all the function's as suspended function's and dispatch them with async/launch dispatcher and if you want to wait for some result in some case use await() to wait for the result.
And use RxJava/liveData for responsive UI
sample code for you will look like
//maybe from Activity for ViewModel you can use ViewModelScope
GlobalScope.launch{
result1= async{ getTripCodes() }
result2= async{ getTrucks() }
result3= async{ getTrailers() }
doSomethingWithTripCodes(result1.await())
doSomethingWIthTrucks(result2.await())
doSomethingTrailers(result3.await())
}
Reference:
post1

How to use Wifimanager.LocalOnlyHotspotCallback in Xamarin.Forms

the environment is Xamarin.forms in android,
but there are no Information about this.
how can i get WifiConfiguration from callback.onstarted ?
OR can i WifiManager.LocalOnlyHotspotReservation get value from callback.onstarted ?
please check below code, the code is about to using wifi AP over oreo version
when java code, i refer this article
a link
private WifiManager wifiManager;
private WifiManager.LocalOnlyHotspotReservation reservation;
void SetHotSpot()
{
wifiManager = (WifiManager)Android.App.Application.Context.GetSystemService(Context.WifiService);
WifiManager.LocalOnlyHotspotCallback callback = new WifiManager.LocalOnlyHotspotCallback();
callback.OnStarted( reservation);
wifiManager.StartLocalOnlyHotspot(callback, new Handler());
}
void getConfiguration(object sender, System.EventArgs e)
{
if (reservation != null)
{
Log.Debug("config", reservation.WifiConfiguration.Ssid);
Log.Debug("config", reservation.WifiConfiguration.NetworkId.ToString());
Log.Debug("config", reservation.WifiConfiguration.PreSharedKey);
Log.Debug("config", reservation.WifiConfiguration.Bssid);
}
}
but when i click button, reservation is null. so Log Dose not output anything.
I converted the Java code here and came up with the following solution which seems to be working kindly take a look and let me know whether or not it works for you.
Add a Callback class that inherits from WifiManager.LocalOnlyHotspotCallback and pass the Activity in my case it is the MainActivity.
public class OreoWifiManagerCallback : WifiManager.LocalOnlyHotspotCallback
{
private const string TAG = nameof(OreoWifiManagerCallback);
private MainActivity mainActivity;
public OreoWifiManager(Activity _activity)
{
if (_activity.GetType() == typeof(MainActivity))
mainActivity = (MainActivity)_activity;
}
public override void OnStarted(WifiManager.LocalOnlyHotspotReservation reservation)
{
base.OnStarted(reservation);
Log.Debug(TAG, "Wifi Hotspot is on now");
mainActivity.mReservation = reservation;
}
public override void OnFailed([GeneratedEnum] LocalOnlyHotspotCallbackErrorCode reason)
{
base.OnFailed(reason);
Log.Debug(TAG, "onStopped: ");
}
public override void OnStopped()
{
base.OnStopped();
Log.Debug(TAG, "onFailed: ");
}
}
Then add a property in the MainActivity to keep track of the reservations
public WifiManager.LocalOnlyHotspotReservation mReservation { get; set; }
And then use these methods to turn on or off wifi in that Activity, also note that you can have a global field for wifi manager if needed.
private void TurnOnHotspot()
{
var WifiManager = (WifiManager)this.Application.GetSystemService(Android.Content.Context.WifiService);
WifiManager.StartLocalOnlyHotspot(new OreoWifiManagerCallback(this), new Handler());
}
private void TurnOffHotspot()
{
if (mReservation != null)
{
mReservation.Close();
}
}
Good luck
Feel free to revert at any time

RxAndroid Subscribe code never called

I'm fairly new to RxJava and RxAndroid, and while some things work, I'm now completely stumped by what I see as basic functionality not working.
I have a subscribe call on a Subject that never seems to run, and I can't figure out why:
public class PairManager implements DiscoveryManagerListener {
private Subscription wifiAvailableSubscription;
private Subscription debugSubscription;
private DiscoveryManager discoveryManager;
private AsyncSubject<Map<String, ConnectableDevice>> availableDevices;
public PairManager(Context appContext) {
DiscoveryManager.init(appContext);
discoveryManager = DiscoveryManager.getInstance();
discoveryManager.addListener(this);
availableDevices = AsyncSubject.<Map<String, ConnectableDevice>> create();
//
// This subscription doesn't work
//
debugSubscription = availableDevices
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Map<String, ConnectableDevice>>() {
#Override
public void call(Map<String, ConnectableDevice> stringConnectableDeviceMap) {
//
// This code is never run !
//
Timber.d(">> Available devices changed %s", stringConnectableDeviceMap);
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
Timber.d("Subscription failed %s", throwable);
}
});
availableDevices.onNext(Collections.<String, ConnectableDevice>emptyMap());
wifiAvailableSubscription = ReactiveNetwork.observeNetworkConnectivity(appContext)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Connectivity>() {
#Override
public void call(Connectivity connectivity) {
if (connectivity.getState().equals(NetworkInfo.State.CONNECTED) && connectivity.getType() == ConnectivityManager.TYPE_WIFI) {
discoveryManager.start();
} else {
discoveryManager.stop();
availableDevices.onNext(Collections.<String, ConnectableDevice>emptyMap());
}
}
});
}
public AsyncSubject<Map<String, ConnectableDevice>> getAvailableDevices() {
return availableDevices;
}
#Override
public void onDeviceAdded(DiscoveryManager manager, ConnectableDevice device) {
Timber.d("onDeviceAdded %s", device);
availableDevices.onNext(manager.getAllDevices());
Timber.d("Sanity check %s", availableDevices.getValue());
}
// ...
}
Is there a way to debug what is going wrong? I have tried creating basic Observable.from-type calls and logging those, and that works as expected. The sanity check log in onDeviceAdded also prints and indicates that availableDevices has in fact updated as expected. What am I doing wrong?
I've found the issue, I've used AsyncSubjects which only ever emit values when they are Completed, where I expect the functionality of BehaviorSubjects.
From the doccumentation:
When Connectivity changes, subscriber will be notified. Connectivity can change its state or type.
You say:
I have a subscribe call on a Subject
A subject won't return te last value. I will only return a value when onNext is called. I assume the Connectivity never changes so it never fires.

Long running RxJava Subscriptions with refreshable data

I'm looking to set up a long running data subscription to a particular data object in Android/RxJava. Specifically a combination of a Retrofit REST call paired with cached data. I've done this pretty simply just wrapping an API call with data, were the API call is Retrofit returning an Observable:
class OpenWeather {
...
Observable<CurrentWeather> OpenWeather.getLocalWeather()
...
}
The simple implementation would be:
public static Observable<CurrentWeather> getWeatherOnce() {
if (currentWeather != null)
return Observable.just(currentWeather);
return OpenWeather.getLocalWeather()
.map(weather -> currentWeather = weather);
}
private static CurrentWeather currentWeather;
The problem is that there is no way to notify when the "current weather" has been updated. The simplest way to add refreshable data with long running updates between subscriptions would be to use a BehaviorSubject like such:
public class DataModel {
public enum DataState {
ANY, // whatever is available, don't require absolute newest
LATEST, // needs to be the latest and anything new
}
private final static BehaviorSubject<CurrentWeather> currentWeatherSubject = BehaviorSubject.create();
public static Observable<CurrentWeather> getCurrentWeather(DataState state) {
synchronized (currentWeatherSubject) {
if (state == DataState.LATEST || currentWeatherSubject.getValue() == null) {
OpenWeather.getLocalWeather()
.subscribeOn(Schedulers.io())
.toSingle()
.subscribe(new SingleSubscriber<CurrentWeather>() {
#Override
public void onSuccess(CurrentWeather currentWeather) {
currentWeatherSubject.onNext(currentWeather);
}
#Override
public void onError(Throwable error) {
// ?? currentWeatherSubject.onError(error);
}
});
}
}
return currentWeatherSubject.asObservable();
}
}
Using the BehaviorSubject, when getting the current weather, get either the last cached entry and any updates as they occur. Thoughts?
So I'm sure I'm doing something wrong here as there seems there should be an easier way or more elegant way.

Categories

Resources