Xamarin.Forms AVPlayer exit fullscreen from method - xamarin.forms

I have a Xamarin application which plays a video for 30 seconds and then shows a dialog. The user can continue to watch (dismiss the dialog) or click an action in the dialog (which then should close full-screen and navigate elsewhere in the app).
I have this working on the simulator. However, when I run the following code on an real iOS device, the app crashes without any stacktrace.
ViewModel.cs
var openLink = await _userDialogs.ConfirmAsync("...", cancelText: "Dismiss", okText: "Yes");
if (openLink)
{
await _navigationService.Close(scope);
MessagingCenter.Send(Application.Current, "CloseFullscreen");
}
VideoRenderer.cs
_playerViewController = new AVPlayerViewController()
{
ExitsFullScreenWhenPlaybackEnds = true
};
player = new AVPlayer();
_playerViewController.Player = player;
...
SetNativeControl(_playerViewController.View);
MessagingCenter.Subscribe<Xamarin.Forms.Application>(this, "CloseFullscreen", (app) =>
{
BeginInvokeOnMainThread(() =>
{
var selectorName = "exitFullScreenAnimated:completionHandler:";
var selector = new ObjCRuntime.Selector(selectorName);
if (_playerViewController.RespondsToSelector(selector))
_playerViewController.PerformSelector(selector, NSObject.FromObject(true), 0);
});
});
Please note that I am not using the CrossMediaManager plugin due to this bug: https://github.com/Baseflow/XamarinMediaManager/issues/629

Related

edit already present button upon being pressed

I have a simple Discord Bot setup, that automatically sends embeds with a button into newly created Tickets (first part) and then picks up the button press and sends a conformation (second part), which is also supposed to then update the button to be both deactivated and have a different label.
How would I go about this? All other questions/guides had the embed sent using a prior interaction and therefore could use its attributes.
client.on('messageCreate', (message) => { //Sends the Embed with Button upon Tank Ticket Creation
if (message.author.id === '149565760950239232' && message.channel.name.includes('ticket')) {
const tanksEmbed = new EmbedBuilder() //The Embed sent on Ticket Creation
.setColor(0xb054c8)
.setTitle('First steps to get your tank souls:')
.setDescription('Press the button below to submit your order.')
.setFooter({ text: 'Contact Aldoraz#0001 for issues and questions' })
const tanksButton = new ActionRowBuilder() //The button on the Embed
.addComponents(
new ButtonBuilder()
.setCustomId('tanks_button')
.setLabel('Submit Tank Order')
.setStyle(ButtonStyle.Success)
.setEmoji('🪙')
.setDisabled(false),
);
message.channel.send({
embeds: [tanksEmbed],
components: [tanksButton]
});
}
});
client.on('interactionCreate', (interaction) => { // Sends Conformation on Button press and Updates Button
if (!interaction.isButton() && !interaction.customId === 'tanks_button') return;
console.log('Button pressed!');
let channel = client.channels.cache.find(channel => channel.id === interaction.channelId);
interaction.reply('Button was pressed!')
const tanksButtonClicked = new ActionRowBuilder() //The updated button on the Embed
.addComponents(
new ButtonBuilder()
.setCustomId('tanks_button_pressed')
.setLabel('Order Submitted!')
.setStyle(ButtonStyle.Success)
.setEmoji('🪙')
.setDisabled(true),
);
interaction.message.edit({components: tanksButtonClicked})
});
You don't need to use client.on("interactionCreate"), that's for slash commands.
What you need to do in order to collect the button is create a collector on the message, and updating the button once it's pressed.
You can also create the collector on the channel, but doing it on the message is preferred.
Add async to your client event:
client.on('messageCreate', async (message) => {})
Store your message in a variable:
const MSG = await message.channel.send({embeds: [tanksEmbed], components: [tanksButton]});
Create your filter and collector
const filter = i => !i.user.bot;
const collector = MSG.createMessageComponentCollector({ filter, time: 20000 });
Then, if the button is pressed, send the form and modify the button.
collector.on(`collect`, async i => {
// here we check if someone else used the button
if(i.member.id != message.author.id) {
return i.reply({ content: `${i.member} This button is not for you`, ephemeral: true})
}
if(i.customId === 'tanks_button') {
// Your code that does its thing.
tanksButton.components[0].setLabel('Your new Label').setDisabled(true);
MSG.edit({embeds: [tankEmbed], components: [tanksButton]});
}
})

TypeOrm - Can't create a connection outside of main function

I have a problem with typeorm, function createConnection works only in index file, if I try to run it in any other file it gets stuck waiting for connection.
export async function saveKU(data: KUData) {
console.log("Foo");
let connection = await createConnection(typeormConfig);
console.log("Received!: " + connection);
const user = connection
.getRepository(KU)
.createQueryBuilder("ku")
.where("ku.ku_number = :ku_number", { ku_number: "54645" })
.getOne();
console.log(user);
}
Message received never gets logged, but then if I run the exact same script in the main function
const main = async () => {
// quit application when all windows are closed
app.on("window-all-closed", () => {
// on macOS it is common for applications to stay open until the user explicitly quits
if (process.platform !== "darwin") {
app.quit();
}
});
app.on("activate", () => {
// on macOS it is common to re-create a window even after all windows have been closed
if (mainWindow === null) {
mainWindow = createMainWindow();
}
});
app.allowRendererProcessReuse = true;
// create main BrowserWindow when electron is ready
app.on("ready", () => {
mainWindow = createMainWindow();
});
let connection = await createConnection(typeormConfig);
console.log("Received!: " + connection);
const user = connection
.getRepository(KU)
.createQueryBuilder("ku")
.where("ku.ku_number = :ku_number", { ku_number: "54645" })
.getOne();
console.log(user);
};
Everything works fine, no error is showing up in both cases, I have no idea what the problem could be, the closest to my example is the following link: https://senorihl.github.io/2019/03/electron-typescript-react-typeorm/
Except that I'm using electron-webpack and creating connections in an index.tsx file doesn't work for me, but rather index.ts

How to update the UI with FCM background notifications?

I want to navigate to some page when user taps on the notification. I'm using cloud messaging and flutter_local_notifications. I've managed to the do it with foreground notifications. It was pretty straightforward. I pasted the same code to my background notification handler, didn't work. Also I've look for onTap callback for notifications but couldn't find anything related to that.
Here's my background notification handler.
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
var androidDetails = AndroidNotificationDetails(
"Channel ID",
"Shannonside",
"Shannonside Channel",
);
var iosDetails = IOSNotificationDetails();
var details = NotificationDetails(
android: androidDetails,
iOS: iosDetails,
);
if (message.notification != null) {
final title = message.notification.title;
final body = message.notification.body;
await NotificationService.localNotification.show(0, title, body, details);
}
if (message.data != null) {
var articleId = message.data['articleId'];
var category = message.data['category'];
if (articleId != null && category != null) {
print("ArticleID: $articleId Category $category");
//#TODO Add navigation service and move to the article detail
NavigatorService.instance.navigateTo("/articlePage", arguments: {
"articleId": articleId.toString().toLowerCase(),
"category": category.toString().toLowerCase(),
});
}
}
}
It's not working, not even my function fired up. Also they stated in the documentation that it's not possible.
Since the handler runs in its own isolate outside your applications context, it is not possible to update application state or execute any UI impacting logic. You can however perform logic such as HTTP requests, IO operations (updating local storage), communicate with other plugins etc.
I know some apps do that, they open some page when you click on notification. Like a dynamic link. I want to implement this in my app.
You can use function
FirebaseMessaging.instance
.getInitialMessage()
.then((RemoteMessage message) {
print("FirebaseMessaging.getInitialMessage");
if (message != null) {
Navigator.of(context).pushNamed('/call');
}
});
This function only run the first time when the app open, It gets last message

Azure: Why is JavaScript STT-SDK from a default microphone just for PC, but not for mobile phones?

The Azure JavaScript-STT-SDK works well on PC, but it does not work on mobile phones.
const audioConfig = AudioConfig.fromDefaultMicrophoneInput()
//code
speech() {
let that = this;
const speechConfig = SpeechConfig.fromSubscription(
"*******",
"*******"
);
speechConfig.speechRecognitionLanguage = "zh-CN";
const audioConfig = AudioConfig.fromDefaultMicrophoneInput();
const recognizer = new SpeechRecognizer(speechConfig, audioConfig);
recognizer.start;
recognizer.recognizeOnceAsync(result => {
switch (result.reason) {
case ResultReason.RecognizedSpeech:
console.log(`RECOGNIZED: Text=${result.text}`);
that.inputMsg = result.text;
console.log(" Intent not recognized.");
break;
case ResultReason.NoMatch:
console.log("NOMATCH: Speech could not be recognized.");
that.tts("没有听到您说话喔~");
break;
case ResultReason.Canceled:
const cancellation = CancellationDetails.fromResult(result);
console.log(`CANCELED: Reason=${cancellation.reason}`);
if (cancellation.reason == CancellationReason.Error) {
console.log(`CANCELED: ErrorCode=${cancellation.ErrorCode}`);
console.log(
`CANCELED: ErrorDetails=${cancellation.errorDetails}`
);
console.log("CANCELED: Did you update the subscription info?");
}
break;
}
});
},
For now, to use it in microphone you need to use the browser SDK, for a sample you could refer to this doc: Recognize speech from a microphone.
Node: when you test with the browser, it doesn't work on the Safari browser. On Safari, the sample web page needs to be hosted on a web server; Safari doesn't allow websites loaded from a local file to use the microphone.

navigator.connection.type not working even if device is ready *or* device is never ready

I'm trying to make a simple app with Phonegap, compiled with Adobe Phonegap builder. I've found and used the well documented example for using navigator.connection.type which is below, and to which I've added another line, to generate an alert box when the device is ready. It doesn't even get that far. I've had it at some points show that endless spinning circle, by moving this code from the head to the body of the page, but that is no help in the end. Testing on iOs and Android devices gives the same result, and the config.xml does include:-
<plugin name="NetworkStatus" value="CDVConnection" />
<plugin name="NetworkStatus" value="org.apache.cordova.NetworkManager" />
Any help greatly appreciated.
// Wait for Cordova to load
//
document.addEventListener("deviceready", onDeviceReady, false);
// Cordova is loaded and it is now safe to make calls Cordova methods
//
function onDeviceReady() {
alert('Device is ready');
checkConnection();
}
function checkConnection() {
var networkState = navigator.connection.type;
var states = {};
states[Connection.UNKNOWN] = 'Unknown connection';
states[Connection.ETHERNET] = 'Ethernet connection';
states[Connection.WIFI] = 'WiFi connection';
states[Connection.CELL_2G] = 'Cell 2G connection';
states[Connection.CELL_3G] = 'Cell 3G connection';
states[Connection.CELL_4G] = 'Cell 4G connection';
states[Connection.CELL] = 'Cell generic connection';
states[Connection.NONE] = 'No network connection';
alert('Connection type: ' + states[networkState]);
}
</script>
You should also wait until all your scripts are loaded. Wrap everything in a onBodyLoad like so:
function onBodyLoad() {
// these are useful later in the app, might as well set early
window.isRipple = (window.tinyHippos != null);
window.isPhoneGap = /^file:\/{3}[^\/]/i.test(window.location.href);
window.isIOS = !window.isRipple && navigator.userAgent.match(/(ios|iphone|ipod|ipad)/gi) != null;
window.isAndroid = !window.isRipple && navigator.userAgent.match(/(android)/gi) != null;
// stuff I use for debugging in chrome
if (window.isPhoneGap) {
document.addEventListener("deviceready", onDeviceReady, false);
} else {
onDeviceReady();
}
}
And add to your body tag:
<body onload="onBodyLoad()">
And the rest of my code for additional references:
function checkOffLine(minutes) {
if (window.lastCheckTime == null) {
window.lastCheckTime = 0;
}
var currentTime = (new Date()).getTime();
if (currentTime < (window.lastCheckTime + minutes * 60000)) return;
window.lastCheckTime = currentTime;
// ios does not allow you to exit the application so just warn
// ios also require you to warn or your app get rejected
if (window.isIOS) {
navigator.notification.alert('This application may not function properly without an internet connection.');
} else {
navigator.notification.confirm(
'This application may not function properly without an internet connection. Continue working offline?', // message
function(button) // callback to invoke with index of button pressed
{
if (button == 1) {
navigator.app.exitApp();
}
},
'Warning', // title
'Exit,Continue' // buttonLabels
);
}
}
function checkConnection() {
// your check connection type code here or just use navigator.onLine
if (!navigator.onLine) {
// don't be annoying, only confirm for once every every 5 minutes
checkOffLine(5);
}
}
// initial phonegap deviceready handler
function onDeviceReady() {
console.log('Application started');
angular.bootstrap(document, ['assetsApp']);
if (window.isPhoneGap) {
document.addEventListener("offline", checkConnection, false);
checkConnection();
}
};
function onBodyLoad() {
// these are useful later in the app, might as well set early
window.isRipple = (window.tinyHippos != null);
window.isPhoneGap = /^file:\/{3}[^\/]/i.test(window.location.href);
window.isIOS = !window.isRipple && navigator.userAgent.match(/(ios|iphone|ipod|ipad)/gi) != null;
window.isAndroid = !window.isRipple && navigator.userAgent.match(/(android)/gi) != null;
// stuff I use for debugging in chrome
if (window.isPhoneGap) {
document.addEventListener("deviceready", onDeviceReady, false);
} else {
onDeviceReady();
}
}
I had the same issue and found I had to run "cordova build" and then the status was returned correctly.
BEWARE
When I run cordova build, it appears to take everything in my ~/app/www directory and overried everything in app/platforms/android/assets/www/
My "install process" is as follows:
cordova create app com.app "App"
cd app
cordova platform add android
cordova plugin add org.apache.cordova.network-information
cordova plugin add org.apache.cordova.camera
cordova plugin add org.apache.cordova.geolocation
cordova build
I can then do code changes in app/www and when happy, 'deploy' it using 'cordova build' (which seems to always copy the files to app/platforms/android/assets/www/.
If I add another plugin using: (for example)
cordova plugin add org.apache.cordova.file
then I need to run
cordova build
to have it work.
I hope this helps
(I am using cordova 3.3.1-0.1.2 )

Resources