I have an Ionic / Cordova application which I (also) build for for Windows 10 (UWP).
On a tablet (eg Microsoft surface), I want to be able to shrink the application when the soft keyboard is invoked.
I have not found a direct way of doing this, so as a work around, I do this on an input focus/blur events, however, I would at least like to be able to determine whether or not a soft keyboard is being used, so I do NOT do this when running on a desktop.
Here is my attempt (which does not work)
public static isUsingWindowSoftKeyboard(): boolean {
try {
Utils.logger.info('isUsingWindowSoftKeyboard begin');
if (!Utils.isWindows())
return false;
let w: any = window;
let touchCapabilities = new w.Windows.Devices.Input.TouchCapabilities();
let ss = stringify(touchCapabilities);
Utils.logger.info(ss);
let keyboardCapabilities = new w.Windows.Devices.Input.KeyboardCapabilities();
ss = stringify(keyboardCapabilities);
Utils.logger.info(ss);
Utils.logger.info('isUsingWindowSoftKeyboard end')
return true;
} catch (error) {
Utils.logger.error(`Utils.isUsingWindowSoftKeyboard: ${error}`);
return false;
}
}
In the above when I log out the ss
variable (the only way I seem to be able to get any debug info on Windows), it is just empty, so I assume the call to TouchCapabilities()
and KeyboardCapabilities()
is just not working.
How might I be able to do what I describe above?
I didn't quite go far enough. Even though stringify
does not show anything, at least one of the properties are actually there (perhaps they are actually getters
).
Anyway, the following DID return 1 for keyboard when running on a desktop..
let keyboardCapabilities = new w.Windows.Devices.Input.KeyboardCapabilities();
Utils.logger.info(`Keyboard present: ${keyboardCapabilities.keyboardPresent}`);
I also tried touch capabilities properties...
let touchCapabilities = new w.Windows.Devices.Input.TouchCapabilities();
Utils.logger.info(`touch present: ${touchCapabilities.TouchPresent}`);
Utils.logger.info(`Contacts: ${touchCapabilities.Contacts}`);
These both did report undefined
, but it does show that the property touchCapabilities
IS defined, otherwise I would have got a null exception when trying to access the two properties.
I now just need to test this on a tablet with a touch screen (which I can do when back in the work office where I have a surface tablet).
The one other avenue I need to re-investigate is the use of getForCurrentView()
as in the following..
let w: any = window;
let vm = w.Windows.UI.ViewManagement;
this.logger.info(`vm is ${(vm ? "defined" : "undefined")}`);
let ip = vm.InputPane;
this.logger.info(`ip is ${(ip ? "defined" : "undefined")}`);
let inputPane = vm.InputPane.getForCurrentView();
this.logger.info(`inputPane is ${(inputPane ? "defined" : "undefined")}`);
if (inputPane) {
inputPane.addEventListener('show', async ev => {
this.logger.info(`inputPane show fired !!!`);
})
}
I did not get the above to work, but I do see getForCurrentView()
mentioned in other searches, even in the context of winjs
. Perhaps I was calling in the wrong place, so will try again. If this works, it would be better, as using the show
and 'hide' events I could then resize the app even if the user manually closes the soft keyboard rather than relying on an inputs focus
and blur
.
Here is what worked for me. I may as well add the whole class. The "gist" of it is in the two listeners added to w.Windows.UI.ViewManagement.InputPane.getForCurrentView()
I added the following service, and I call it from the main component ngAfterViewInit()
import { Injectable } from '@angular/core';
import { Utils } from '@shared/utils';
import { Logger } from './logger.service';
/**
* Manages the Windows soft keyboard showing / hiding and resizing the app when it is shown/hidden
*/
@Injectable()
export class WindowsKeyboardService {
/**
* Construction
* @param logger - logger
*/
constructor(private logger: Logger) { }
/**
* Call from main component once the view is visible, so we can get access to Windows Input pane
* and hook up the soft keyboard handlers
*/
public async hookupKeyboardHandlers() : Promise<void> {
try {
this.logger.info('hookupKeyboardHandlers');
// Only need this for Windows. For ioS/Android the keyboard plugin does all this for us.
if (!await Utils.isWindows()) {
this.logger.info('WindowsKeyboardService.hookupKeyboardHandlers - not windows. Skipping');
return;
}
let w = <any>window;
const inputPane = w.Windows.UI.ViewManagement.InputPane.getForCurrentView();
if (!inputPane) {
this.logger.error('WindowsKeyboardService.hookupKeyboardHandlers: could not get inputPane');
return;
}
inputPane.addEventListener('showing', _ => this.onWindowsKeyboardUp);
inputPane.addEventListener('hiding', _ => this.onWindowsKeyboardClose);
} catch (error) {
this.logger.error(`WindowsKeyboardService.hookupKeyboardHandlers: ${error}`)
}
}
/**
* Raised when a Windows soft keyboard is opened
*/
private onWindowsKeyboardUp() : void {
try {
this.logger.info("onWindowKeyboardUp");
// Just half viewportHeight for now (until can find out how to get keyboard height - if even possible)
let viewportHeight: number = window.innerHeight;
let kbHeight = viewportHeight / 2;
this.logger.info(`viewportHeight: ${viewportHeight}`);
const focusedElement = document.activeElement;
if (!focusedElement) {
this.logger.info('WindowsKeyboardService.onWindowsKeyboardUp: Could not get focused input');
return;
}
let inputFieldOffsetFromBottomViewPort: number = viewportHeight - focusedElement.getBoundingClientRect().bottom;
let inputScrollPixels = kbHeight - inputFieldOffsetFromBottomViewPort;
const ionApp = document.getElementsByTagName("ion-app")[0];
this.logger.info(ionApp ? "got app" : "not got app");
// Set margin to give space for native keyboard.
ionApp.style["margin-bottom"] = kbHeight.toString() + "px";
// But this diminishes ion-content and may hide the input field...
if (inputScrollPixels > 0) {
const content = document.getElementsByTagName('ion-content');
if (!content || content.length == 0)
return;
const topMostContent = content[content.length - 1];
setTimeout(async () => {
let ionScroll = await topMostContent.getScrollElement();
ionScroll.animate({
scrollTop: ionScroll.scrollTop + inputScrollPixels
}, 300);
}, 300); // Matches scroll animation from css.
}
} catch (error) {
this.logger.error(`WindowsKeyboardService.onWindowKeyboardUp: ${error}`);
}
}
/**
* Raised when a Windows soft keyboard is closed
*/
private onWindowsKeyboardClose(): void {
try {
this.logger.info("WindowsKeyboardService.onWindowKeyboardClose");
const ionApp = document.getElementsByTagName("ion-app")[0];
ionApp.style["margin-bottom"] = "0";
} catch (error) {
this.logger.error(`WindowsKeyboardService.onWindowKeyboardClose: ${error}`);
}
}
}
isWindows
has the following...
public static async isWindows(): Promise<boolean> {
await Utils.platform.ready();
const result = window.cordova != undefined && window.cordova.platformId == "windows";
return result;
}
And I call it as follows form the main app component..
public async ngAfterViewInit(): Promise<void> {
try {
this.logger.info('AppComponent: ngAfterViewInit');
this.windowsKeyboardService.hookupKeyboardHandlers();
} catch (error) {
this.logger.error(`AppComponent: ngAfterViewInit: ${error}`);
}
}