IONIC/AngularJs on IOS - wrong innerWidth and innerHeight when changing screen orientation - ios

It's my first time asking a question on stackoverflow, so i'll try to be as specific as I can. Thanks for your help.
Currently I'm developing an application for Androïd and IOS with Angular/IONIC solution.
Actually I've developed a service using cordova-plugin-screen-orientation 1.4.2 version plugin to change the orientation of my screen for some views. In fact, that work perfectly on both OS. But I also need when it's done, to catch the width and height of my device.
So there is my problem. On Android everything works, but on IOS, it looks like when I change the orientation I catch always the old values. (if I change for a landscape, I catch the previous portrait orientation values).
I tried different algorythm like the more simple : change the orientation and when it's done catch the values.
Currently I'm trying with a watcher, for the same result.
Here is my code :
class WindowService extends Service {
constructor($resource,$config, $window, Watcher) {
'ngInject';
super();
this.orientations = {
1:"portrait",
2:"landscape"
}
//imported library
this.md = new MobileDetect($window.navigator.userAgent);
// if it's a tablet Android, md.tablet() != null && md.os() == 'AndroidOS'
// if it's an iPad md.tablet() == 'iPad'
// if it's an iPhone md.mobile() == 'iPhone'
// if it's an Android phone, md.phone() != null && md.os() == 'AndroidOS'
console.log("md > ", this.md.mobile());
this.maxHeight = "100vh";
this.maxWidth = "100vw";
if (this.isAndroid()){
this.maxHeight = $window.innerHeight + "px";
this.maxWidth = $window.innerWidth + "px";
this.maxHeightWatcher = Watcher.watch(() => $window.innerHeight, innerHeight => {
this.maxHeight = $window.innerHeight + "px";
this.maxWidth = $window.innerWidth + "px";
});
}
}
setOrientation(){
this.invoke(($window) => {
if ($window.screen.unlockOrientation){
$window.screen.unlockOrientation();
if ((this.md.phone() != null && this.md.os() == 'AndroidOS') || this.md.mobile() == 'iPhone'){
$window.screen.lockOrientation('portrait');
}else if ((this.md.tablet() != null && this.md.os() == 'AndroidOS') || this.md.tablet() == 'iPad'){
$window.screen.lockOrientation('landscape');
}else {
$window.screen.lockOrientation('landscape');
}
}
});
}
//THIS S THE FUNCTION I CALL
changeOrientation(orientationId){
this.invoke(($window) => {
this.lockScreenOrientation($window, orientationId).then(res => {
console.log("changeOrientation > ", res);
}).catch(err => {
console.log("changeOrientation > ", err);
});
});
}
lockScreenOrientation($window, orientationId){
if (this.orientations[orientationId] != undefined){
if (this.orientations[orientationId] != $window.screen.orientation){
if ($window.screen.unlockOrientation){
$window.screen.unlockOrientation();
console.log("lockOrientation > ", $window.screen.lockOrientation(this.orientations[orientationId]));
return Promise.resolve(this.orientations[orientationId]);
}else{
return Promise.reject("[ERROR] cordova-plugin-screen-orientation hadn't been loaded.")
}
}else{
return Promise.resolve(this.orientations[orientationId]);
}
}else{
return Promise.reject("[ERROR] The orientation specified is Unknown.")
}
}
getDevice(){
if ((this.md.phone() != null && this.md.os() == 'AndroidOS')){
return "Android Smartphone";
}else if (this.md.mobile() == 'iPhone'){
return "iPhone";
}else if ((this.md.tablet() != null && this.md.os() == 'AndroidOS')){
return "Android Tablet";
}else if (this.md.tablet() == 'iPad'){
return "iPad";
}else
return "Unknown";
}
isSmartphone(){
if ((this.md.phone() != null && this.md.os() == 'AndroidOS') || this.md.mobile() == 'iPhone'){
return true;
}
return false;
}
isTablet(){
if ((this.md.tablet() != null && this.md.os() == 'AndroidOS') || this.md.tablet() == 'iPad')
return true;
return false;
}
isIOS() {
if (this.md.mobile() == 'iPhone' || this.md.tablet() == 'iPad')
return true;
return false;
}
isAndroid() {
if ((this.md.phone() != null && this.md.os() == 'AndroidOS')
|| (this.md.tablet() != null && this.md.os() == 'AndroidOS'))
return true;
return false;
}
getMaxHeight(){
return this.maxHeight;
}
getMaxWidth(){
return this.maxWidth;
}
}
Service.register(WindowService);
<ion-side-menu-content drag-content="false" ng-controller="HeaderController" style="height:{{view.maxHeight}};width: {{view.maxWidth}};">
....
</ion-side-menu-content>
The class Watcher is just an little extension of the basic watcher of angular. It works the same way.
[edit] Sorry I forgot to specify something : as you can see in the js code, in the constructor method, if it's IOS I just put 100vh fr the height and 100vw for the width. And I have the same result : the change from portrait to landscape keep the portrait size values.

Related

android 12 AudioManager.setCommunicationDevice not working

I'm building a voice call app for android using WebView. Everything works fine except in android 12, I can not use the earpiece (or even wired headphones in some phones) for audio out. The audio is always playing through loudspeaker. And some of the users reported that they can hear the internal ring(Which is a MediaPlayer object) in earpiece, but once the call answered it switches to loudspeaker. And the worst part is, I'm not able to reproduce this issue as this works perfectly on all my test phones (android 12).
My code,
private fun setAudioOutDevice(target: AudioDeviceInfo?) {
//need some delay for bluetooth sco
var audioChangeDelay: Long = 15
if (System.currentTimeMillis() - lastAudioChangeTime <= 300) audioChangeDelay = 300
Handler(Looper.getMainLooper()).postDelayed({
var targetDevice = target
//no device selected. scan all output devices and select one automatically
if (targetDevice == null) {
var wiredHeadsetDevice: AudioDeviceInfo? =
getAudioDevice(AudioDeviceInfo.TYPE_WIRED_HEADSET)
if (wiredHeadsetDevice == null) wiredHeadsetDevice =
getAudioDevice(AudioDeviceInfo.TYPE_WIRED_HEADPHONES)
val bluetoothDevice: AudioDeviceInfo? =
getAudioDevice(AudioDeviceInfo.TYPE_BLUETOOTH_SCO)
val speakerDevice: AudioDeviceInfo? =
getAudioDevice(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)
val earpieceDevice: AudioDeviceInfo? =
getAudioDevice(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE)
//update global variables
isBluetoothAvailable = bluetoothDevice != null
//disable BT if wired headset connected
if (wiredHeadsetDevice != null) isBluetoothAvailable = false
//choose an output device
targetDevice =
if (callType == videoCall && bluetoothDevice == null && wiredHeadsetDevice == null) speakerDevice
else if (callType == videoCall && wiredHeadsetDevice == null && !isBluetoothAudioEnabled) speakerDevice
else wiredHeadsetDevice
?: if (isBluetoothAvailable && isBluetoothAudioEnabled && bluetoothDevice != null) bluetoothDevice
else earpieceDevice ?: speakerDevice
//no earpiece
if (earpieceDevice == null) {
setBigStatus("Unable to access Earpiece", 0)
}
}
//set output device
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
var success = false
try {
// am.clearCommunicationDevice()
if (targetDevice != null) success = am.setCommunicationDevice(targetDevice)
} catch (e: Exception) {
debugMsg(e.message.toString())
}
if (!success) setBigStatus(getString(R.string.unable_to_set_audio), 0)
}
//older devices, android 11 or lower
else {
am.mode = AudioManager.MODE_IN_COMMUNICATION
if (targetDevice?.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
changeSco(false)
am.isSpeakerphoneOn = true
} else {
if (targetDevice?.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) changeSco(true)
else changeSco(false)
am.isSpeakerphoneOn = false
}
}
}, audioChangeDelay)
}
private fun changeSco(enable: Boolean) {
if (enable) {
am.startBluetoothSco()
am.isBluetoothScoOn = true
} else {
am.stopBluetoothSco()
am.isBluetoothScoOn = false
}
}
private fun getAudioDevice(type: Int): AudioDeviceInfo? {
val audioDevices = am.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
for (deviceInfo in audioDevices) {
if (type == deviceInfo.type) return deviceInfo
}
return null
}
The above code is working fine on android 11 and lower and even on some android 12 phones. How can I make this work on all devices?
There is an issue tracker here. But it seems that nobody is working on it.
UPDATE Today I tried it on a pixel 3a and it gives mixed behaviors. Sometimes it plays on the earpiece or wired headset but for most of the calls, it plays on the loudspeaker only. And even if I don't set any mode, it automatically switches to MODE_IN_COMMUNICATION right after the stream starts. I think, android is trying to figure out the proper mode by itself and that messing with my app logic.

Application crash while accessing data element of linked list when data is NULL

I am using following code to traverse through linked list in objective c
const MSList *calls = linphone_core_get_calls(LC);
if (calls == NULL)
{
[self dismissCtrl];
//how to check current is which screen is on
// while ((currentView == CallView.compositeViewDescription) ||
// (currentView == CallIncomingView.compositeViewDescription) ||
// (currentView == CallOutgoingView.compositeViewDescription)) {
// [self popCurrentView];
// }
} else {
linphone_call_resume((LinphoneCall *)calls->data);
while (calls)
{
if(calls->data != NULL && calls->data != nil && calls->data != (__bridge void *)((id)[NSNull null]))
{
//crash
if (linphone_call_get_state((LinphoneCall *)calls->data) == LinphoneCallIncomingReceived ||
linphone_call_get_state((LinphoneCall *)calls->data) == LinphoneCallIncomingEarlyMedia) {
[self displayIncomingCall:(LinphoneCall *)calls->data];
break;
}
}
calls = calls->next;
}
Application is crashing when entire list is not null but its data,previous or next value is NULL. I have added code to check if data is NULL but if it(data) is NULL, then i will not be able to access it and in condition itself application is crashing. How to prevent this ? I have attached screen-shot for where application is crashing and what is the value that list contains at that time.
if (calls == NULL) {
[self dismissCtrl];
//how to check current is which screen is on
// while ((currentView == CallView.compositeViewDescription) ||
// (currentView == CallIncomingView.compositeViewDescription) ||
// (currentView == CallOutgoingView.compositeViewDescription)) {
// [self popCurrentView];
// }
}
else
{
size_t count = bctbx_list_size(calls);
linphone_call_resume((LinphoneCall *)calls->data);
int i = 0;
while (calls )
{
if(i < count)
{
if ( calls->data == (__bridge void *)((id)[NSNull null]) || calls->data == NULL || calls->data == nil )
{
return;
}
LinphoneCall *objCall = (LinphoneCall *)calls->data;
LinphoneCallState state = (objCall != NULL) ? linphone_call_get_state(call) : 0;
if (state == LinphoneCallIncomingReceived ||
state == LinphoneCallIncomingEarlyMedia)
{
[self displayIncomingCall:(LinphoneCall *)calls->data];
break;
}
//calls ? calls->data : NULL
// calls = calls ? calls->next : NULL;
calls = calls->next;
i = i+1;
}
else
{
break;
}
}

Prevent Keypress-events ( f.e. enter-key)

I'm looking for stop keyboard events from ui-grid . For example: ui-grid navigates to the next row by pressing the enter-key, here i need to prevent this action. Even using a cellTemplate which implements an ng-keypress-listener, I can't stop ui-grid to switch to the next row by hitting the enter-key.
Does anyone know how prevent ui-grid or switch off the keyboards events?
It works like charm, thanks a lot :)
I use now a decorator service to overwrite the method getDirection from ui-grid.
FYI. Here is the code:
angular
.module('app')
.config(function($provide){
$provide.decorator('uiGridCellNavService', function ($delegate, uiGridConstants, uiGridCellNavConstants) {
var getDirection = $delegate.getDirection;
$delegate.getDirection = function (evt) {
if (evt.keyCode === uiGridConstants.keymap.LEFT ||
(evt.keyCode === uiGridConstants.keymap.TAB && evt.shiftKey)) {
return uiGridCellNavConstants.direction.LEFT;
}
if (evt.keyCode === uiGridConstants.keymap.RIGHT ||
evt.keyCode === uiGridConstants.keymap.TAB) {
return uiGridCellNavConstants.direction.RIGHT;
}
if (evt.keyCode === uiGridConstants.keymap.UP ||
(evt.keyCode === uiGridConstants.keymap.ENTER && evt.shiftKey) ) {
return uiGridCellNavConstants.direction.UP;
}
if (evt.keyCode === uiGridConstants.keymap.PG_UP){
return uiGridCellNavConstants.direction.PG_UP;
}
// delete listener for key ENTER
if (evt.keyCode === uiGridConstants.keymap.DOWN) {
return uiGridCellNavConstants.direction.DOWN;
}
// original code
/*if (evt.keyCode === uiGridConstants.keymap.DOWN ||
evt.keyCode === uiGridConstants.keymap.ENTER) {
return uiGridCellNavConstants.direction.DOWN;
}*/
if (evt.keyCode === uiGridConstants.keymap.PG_DOWN){
return uiGridCellNavConstants.direction.PG_DOWN;
}
return null;
};
return $delegate;
})
You can adjust the default functionality by overwriting their services/directives.
I created a Plunkr for your case.
Copy the whole service containing the keyListener for cellNavigation and remove the ENTER-watcher.
getDirection: function (evt) {
if (evt.keyCode === uiGridConstants.keymap.LEFT ||
(evt.keyCode === uiGridConstants.keymap.TAB && evt.shiftKey)) {
return uiGridCellNavConstants.direction.LEFT;
}
if (evt.keyCode === uiGridConstants.keymap.RIGHT ||
evt.keyCode === uiGridConstants.keymap.TAB) {
return uiGridCellNavConstants.direction.RIGHT;
}
/*
if (evt.keyCode === uiGridConstants.keymap.UP ||
(evt.keyCode === uiGridConstants.keymap.ENTER && evt.shiftKey) ) {
return uiGridCellNavConstants.direction.UP;
}
*/
if (evt.keyCode === uiGridConstants.keymap.PG_UP){
return uiGridCellNavConstants.direction.PG_UP;
}
/*
if (evt.keyCode === uiGridConstants.keymap.DOWN ||
evt.keyCode === uiGridConstants.keymap.ENTER && !(evt.ctrlKey || evt.altKey)) {
return uiGridCellNavConstants.direction.DOWN;
}
*/
if (evt.keyCode === uiGridConstants.keymap.PG_DOWN){
return uiGridCellNavConstants.direction.PG_DOWN;
}
return null;
},
The direction.UP and direction.DOWN checks are commented out but everything else works as expected. I appended the service at the bottom of the Plunkr app.js.

Tic tac toe programming iOS

For some reason the function the checkForWin is returning always a NO.
Because of that I am not able to retrieve the winner.
Else if suggest a different logic to judge winner
I am using this function every time user puts up a symbol
-(BOOL) checkForWin{
NSLog(#"yes");
// HORIZONTAL WINS
if((s1.image == s2.image) & (s2.image == s3.image) & (s1.image != NULL))
{
return YES;
}
else if((s4.image == s5.image) & (s5.image == s6.image) & (s4.image != NULL))
{
return YES;
}
else if((s7.image == s8.image) & (s8.image == s9.image) & (s7.image != NULL))
{
return YES;
}
// VERTICAL WINS
else if((s1.image == s4.image) & (s4.image == s7.image) & (s1.image != NULL))
{
return YES;
}
else if((s2.image == s5.image) & (s5.image == s8.image) & (s2.image != NULL))
{
return YES;
}
else if((s3.image == s6.image) & (s6.image == s9.image) & (s3.image != NULL))
{
return YES;
}
// DIAGONAL WINS
else if((s1.image == s5.image) & (s5.image == s9.image) & (s1.image != NULL))
{
return YES;
}
else if((s3.image == s5.image) & (s5.image == s7.image) & (s3.image != NULL))
{
return YES;
}
//right now return 1 becuase we havn't implemented this yet
else{
return NO;
}
}
-(void)displayWinner{
if([self checkForWin] == YES){
if(playerToken==1){
lbl2.text =#"X is the WINNER";
} else {
lbl2.text =#"O is the WINNER";
}
}
}
Maybe you can write
if([s1.image isEqual:s2.image2]){
}
this code instead of this control statement
if((s1.image == s2.image) & (s2.image == s3.image) & (s1.image != NULL))
{
return YES;
}
The answer by Yılmaz Gürsoy is probably correct.
The reason, is that you are comparing the references to the images, and not the image data. The references are probably not the same, while the data probably is.
He is suggesting that you compare the data instead of the references.
In my oppinion though, you should actually add a member to the s# object, containing a tristate value. could be an integer.
/**
s.x is avalue indicating which image is shown (x, O, or empty)
x > 0 means 'X'
x == 0 means 'O'
x < 0 means empty
**/
and then set it when you assign the image. and check against that.
otherwise you will be wasting time comparing data, to get the exact same end result.

Set an OnClickListener for an SVG element

Say I have an SVG element, as follows. How do I add an onClickListener?
solved, see below.
I'm going to guess you're meaning a FieldChangeListener rather than an OnClickListener (wrong platform ;). SVGImage isn't part of the RIM-developed objects, so unfortunately you won't be able to. Anything that is going to be able to have a FieldChangeListner has to be a subclass of the net.rim.device.api.ui.Field class.
Just in case someone's interested in how it's done...
try {
InputStream inputStream = getClass().getResourceAsStream("/svg/sphere1.svg");
_image = (SVGImage)SVGImage.createImage(inputStream, null);
_animator = SVGAnimator.createAnimator(_image, "net.rim.device.api.ui.Field");
_document = _image.getDocument();
_svg123 = (SVGElement)_document.getElementById("123");
}
catch (IOException e) { e.printStackTrace(); }
Field _svgField = (Field)_animator.getTargetComponent();
_svgField.setBackground(blackBackground);
add(_svgField);
_svg123.addEventListener("click", this, false);
_svg123.addEventListener("DOMFocusIn", this, false);
_svg123.addEventListener("DOMFocusOut", this, false);
}
public void handleEvent(Event evt) {
if( _svg123 == evt.getCurrentTarget() && evt.getType() == "click" ){ Dialog.alert("You clicked 123"); }
if( _svg123 == evt.getCurrentTarget() && evt.getType() == "DOMFocusIn" ) { ((SVGElement) _document.getElementById("outStroke123")).setTrait("fill", "#FF0000"); }
if( _svg123 == evt.getCurrentTarget() && evt.getType() == "DOMFocusOut" ) { ((SVGElement) _document.getElementById("outStroke123")).setTrait("fill", "#2F4F75"); }
}

Resources