Platform, Identification & Kiosk Services
These three services handle device-level concerns: detecting what kind of device the application is running on (platformService), generating a unique and persistent identity for that device (identificationService), and providing a virtual keyboard for touch-screen environments (kioskService).
platformService
Introduction
platformService detects and manages the runtime platform -- web browser, iOS, Android, Cordova, desktop, mobile, tablet -- and exposes display settings (theme, zoom, kiosk mode, scrollbar visibility). It exists because the application must adapt its UI and behaviour to wildly different environments: a phone with a native keyboard, a POS tablet in kiosk mode, a desktop browser, or a Cordova-wrapped native app. Rather than scattering device checks throughout the codebase, platformService centralises them into a single injectable service.
When to use
- Platform-specific behaviour:
this.platform.is('cordova')to check if running as a native app. - Device classification:
this.platform._isMobile/_isDesktopfor layout decisions. - Theme management:
this.platform._theme = 'dark'to apply a theme globally. - Zoom control:
this.platform._zoomLevel = 1.5for accessibility. - Kiosk mode:
this.platform._isKiosk = trueto activate the virtual keyboard.
In practice, most components should use viewService (which delegates to platformService) rather than injecting platformService directly. Use platformService directly only in low-level services or when you need access to properties that viewService does not expose (e.g. Browser, RunningOS, DeviceInfo).
How it works
On construction, platformService waits for the Ionic platform to be ready, then:
- Detects platforms using both Ionic's
Platform.is()andngx-device-detector(for more accurate mobile/tablet/desktop classification). - Checks URL query parameters for a
?mode=mobileor?mode=desktopoverride. - Sets
_isMobileand_isDesktopbased on detection results and any forced mode.
Theme, kiosk, zoom, and scrollbar settings are managed via setters that update the DOM (CSS classes on document.body, CSS custom properties on document.documentElement) and emit change events.
import { platformService } from '@unpispas/upp-base';
constructor(private platform: platformService) {}
Platform detection
| Method / Property | Signature | Description |
|---|---|---|
is(platform) | (platform: AvailablePlats): boolean | Returns true if the current environment matches. Includes fallback checks: Android detection via user agent, iOS detection via /iPad|iPhone|iPod/. |
platforms() | (): AvailablePlats[] | Returns all detected platform types as an array. |
ready() | (): Promise<string> | Resolves when the Ionic platform is fully initialised. Always await this before using platform detection results. |
IsReady | boolean | Synchronous readiness flag. false until ready() resolves. |
DeviceInfo | DeviceInfo | Detailed device information from ngx-device-detector (OS, browser, device type, etc.). |
Browser | string | null | Detected browser: 'Chrome', 'Safari', 'Firefox', 'Opera', 'IE', or null. Cached after first access. |
RunningOS | OSInfo | null | Operating system details including processor architecture. Useful for offering platform-specific downloads. |
Available platform strings: ios, ipad, iphone, android, phablet, tablet, cordova, capacitor, electron, pwa, mobile, mobileweb, desktop, hybrid.
Device classification
| Property | Type | Description |
|---|---|---|
_isMobile | boolean | true if the device is classified as a phone (not a tablet). Respects forced mode from URL parameters. |
_isDesktop | boolean | true if the device is classified as desktop (including tablets). Respects forced mode. |
LegacyMobile | boolean | true for phones with native keyboards (excludes tablets). iPhones that identify as tablets are still classified as legacy mobile. |
LegacyTablet | boolean | true for tablets running a mobile OS (iPad, Android tablet). |
LegacyDesktop | boolean | true for everything that is not LegacyMobile -- desktops, laptops, and tablets. |
The "Legacy" classification is used to decide whether the device has a native on-screen keyboard (in which case the virtual kiosk keyboard should be disabled) or needs the kiosk keyboard.
Display settings
| Property | Type | Description |
|---|---|---|
_theme | 'dark' | 'light' | null | Current body theme. The setter adds/removes the CSS class on document.body and emits OnThemeChanged. |
_isKiosk | boolean | Virtual-keyboard kiosk mode. Automatically false on legacy mobile/tablet (they have native keyboards). Setting it emits OnKioskChanged. |
_zoomLevel | number | Application zoom level. Updates CSS custom properties (--body-scale-width, --body-zoom-level, etc.) on document.documentElement for CSS-based scaling. |
_scrollbar | boolean | Scrollbar visibility. Setting it emits OnThemeChanged. |
_viewMode | string (setter only) | Forces 'mobile' or 'desktop' mode. Adds ?mode=... to the URL and reloads the page. Use for testing or for kiosk devices that need to be forced into a specific mode. |
Observables
| Observable | Emits when |
|---|---|
OnKioskChanged | Kiosk mode toggles. |
OnThemeChanged | Theme or scrollbar changes. |
resize | Delegates to Ionic Platform.resize. |
OS info type
interface OSInfo {
syst: string | null; // 'win' | 'linux' | 'mac'
proc: string | null; // '32' | '64'
arch: string | null; // 'arm' | 'intel'
fext: string | null; // '.exe' | ''
}
Usage examples
Adapting UI to the platform
async ngOnInit() {
await this.platform.ready();
if (this.platform.is('cordova')) {
this.useNativeFileAccess = true;
}
if (this.platform._isMobile) {
this.showCompactLayout = true;
}
}
Offering a platform-specific download link
const os = this.platform.RunningOS;
if (os) {
this.downloadUrl = `https://releases.example.com/app-${os.syst}-${os.proc}${os.fext}`;
// e.g. "https://releases.example.com/app-win-64.exe"
}
Forcing desktop mode on a tablet
// This will add ?mode=desktop to the URL and reload
this.platform._viewMode = 'desktop';
Gotchas and tips
- Always
await this.platform.ready()before using detection results:_isMobile,LegacyMobile, etc. return incorrect values before the platform is ready. - Kiosk mode is disabled on phones and tablets with native keyboards: setting
_isKiosk = trueis a no-op onLegacyMobileorLegacyTabletdevices. _viewModecauses a page reload: it is not a live toggle. It modifies the URL and reloads, so the new mode takes effect from the initial platform detection.- Theme is applied via CSS class on
document.body: your styles must use the.darkor.lightclass selector (e.g.body.dark { ... }).
identificationService
Introduction
identificationService generates and persists a unique device ID by combining FingerprintJS browser fingerprinting with server-side storage. This device ID is used to identify the physical device across sessions -- even if the user clears cookies or uses a different browser profile, the server can map the calculated fingerprint back to the same device ID.
When to use
Call identificationService.DeviceId() once during the application bootstrap, before login. The returned ID is stored in stateService.device and included in every backend request via adhocService.
How it works
DeviceId()
│
▼
1. Calculate fingerprint (FingerprintJS + browser hash fallback)
│
▼
2. Check local storage: storeService.Get('upp-stored-deviceuuid-v2')
│ found? ──► use it
│ not found? ──▼
▼
3. Check server: GET device/getuuid?device={calculated}
│ found? ──► use server's UUID
│ not found? ──► use calculated fingerprint
│
▼
4. Persist to server: GET device/setuuid?calc={calculated}&uuid={final}
│
▼
5. Persist to local: storeService.Set('upp-stored-deviceuuid-v2', final)
│
▼
Return the device ID
The resolution order (local > server > calculated) ensures that:
- If the user has used this device before and local storage is intact, the same ID is returned instantly.
- If local storage was cleared but the server remembers the fingerprint, the original ID is recovered.
- If neither has a record, a new ID is generated from the fingerprint.
import { identificationService } from '@unpispas/upp-base';
constructor(private identification: identificationService) {}
Methods
| Method | Signature | Description |
|---|---|---|
DeviceId() | (): Promise<string> | Returns a unique device identifier. The result is synchronised to both server and local storage. |
Fingerprint calculation
The fingerprint is computed in two layers:
- FingerprintJS (
@fingerprintjs/fingerprintjs): generates avisitorIdbased on canvas fingerprinting, WebGL, audio context, and other browser characteristics. - Fallback hash: if FingerprintJS fails, a simpler hash is computed from
mimeTypes.length,userAgentdigits,plugins.length, and screen dimensions, prefixed withBROWSER.
The combined format is: VISITOR [{fingerprintId} - {browserHash}].
Usage example
Bootstrap flow
// In the app initialisation service
async bootstrap() {
const deviceId = await this.identification.DeviceId();
this.state.device = deviceId;
console.log('Device identified as:', deviceId);
// Now proceed with login...
}
Gotchas and tips
DeviceId()makes network calls: it contacts the server to check and persist the ID. If offline, it falls back to the calculated fingerprint.- The device ID is not a secret: it identifies the device, not the user. It is sent in every API request as a query parameter.
- FingerprintJS can fail: in privacy-focused browsers or with certain extensions, FingerprintJS may throw. The fallback hash ensures a device ID is always available, though it may be less unique.
kioskService
Introduction
kioskService manages a virtual on-screen keyboard for kiosk/touchscreen environments where no physical keyboard is available. In a POS (Point of Sale) environment, the application often runs on a touch-screen device without a keyboard. Rather than relying on the OS on-screen keyboard (which is inconsistent across platforms and hard to style), kioskService provides a fully custom keyboard that integrates with the application's input fields.
When to use
- You are building a kiosk or POS interface.
- The device is a desktop or tablet that does not have a physical keyboard.
- You want consistent keyboard behaviour across all platforms.
The kiosk keyboard is automatically disabled on phones and tablets that have native keyboards (LegacyMobile / LegacyTablet).
How it works
The kiosk system has three parts:
kioskService(the service): holds a reference to the currently focused input element and provides methods to show/hide the keyboard and simulate key presses.UiKioskInputDirective([uppNgkiosk]): a directive that you attach to input elements. Onfocus, it callskiosk.show()with the input element and keyboard type. Onblur, it callskiosk.hide().UiKioskBoardComponent(<upp-kiosk-board>): the visual keyboard component. It subscribes tokioskService.OnKeyBoardShowandOnKeyBoardHideto show/hide itself. It loads language-specific key layouts from JSON files.
<input uppNgkiosk data-kioskboard-type="all" />
│ focus event
▼
UiKioskInputDirective.onFocus()
│
▼
kioskService.show(inputElement, 'all')
│ emits OnKeyBoardShow
▼
UiKioskBoardComponent.show('all')
│ renders keyboard
▼
User taps a key
│
▼
kioskService.keyPress('a')
│ updates input.value, dispatches events
▼
Angular forms detect the change via 'input' and 'change' events
import { kioskService } from '@unpispas/upp-base';
constructor(private kiosk: kioskService) {}
Methods
| Method | Signature | Description |
|---|---|---|
show(input, type) | (input: HTMLInputElement | HTMLTextAreaElement, type: 'num' | 'price' | 'all'): void | Associates the keyboard with an input element and shows it. If the input is a password type, the keyboard switches to password mode. Disabled inputs are ignored. |
hide() | (): void | Hides the keyboard and detaches from the current input. |
keyPress(key) | (key: string): void | Simulates a key press on the associated input. Supports cursor position and selection (inserts at cursor, replaces selection). Special keys: 'backspace' (delete character), 'breakline' (newline in textareas). Dispatches keydown, input, and change events for Angular forms compatibility. |
ClearInput() | (): void | Clears the associated input's value and dispatches input and change events. |
value | string | null (getter) | Current value of the associated input element. Returns null if no input is associated. |
Observables
| Observable | Payload | Description |
|---|---|---|
OnKeyBoardShow | string | Emits the keyboard type ('password', 'num', 'price', or 'all'). |
OnKeyBoardHide | void | Emits when the keyboard is hidden. |
UiKioskInputDirective
Directive selector: [uppNgkiosk]. Attach it to any input or textarea to automatically integrate with the kiosk keyboard. The keyboard type is read from the data-kioskboard-type attribute.
<!-- Full alphanumeric keyboard -->
<input uppNgkiosk data-kioskboard-type="all" />
<!-- Numeric keypad -->
<input uppNgkiosk data-kioskboard-type="num" />
<!-- Price entry (cents mode) -->
<input uppNgkiosk data-kioskboard-type="price" />
UiKioskBoardComponent
Component selector: <upp-kiosk-board>. Place this once in your application shell. It renders the virtual keyboard UI with:
- Alphanumeric layout: loaded from language-specific JSON files (
assets/kioskboard/kioskboard-keys-{lang}.json). Supported languages:en,es,fr,de,hu,rs,tk. - Numeric layout: a calculator-style keypad for number and price inputs.
- Special character layout: symbols, punctuation, and currency characters.
- Caps lock toggle and special character toggle.
- Compact modes: left-aligned or right-aligned compact layout for narrow screens.
- Automatic language updates: when the language changes, the keyboard layout updates automatically.
Usage examples
Setting up the kiosk keyboard in the app shell
<!-- app.component.html -->
<ion-app>
<router-outlet></router-outlet>
<upp-kiosk-board></upp-kiosk-board>
</ion-app>
Using the directive on form inputs
<form>
<input type="text" uppNgkiosk data-kioskboard-type="all"
placeholder="Product name" [(ngModel)]="product.name" />
<input type="text" uppNgkiosk data-kioskboard-type="price"
placeholder="0.00" [(ngModel)]="product.price" />
</form>
Programmatic keyboard control
// Show the keyboard for a specific element
const inputEl = document.getElementById('search-input') as HTMLInputElement;
this.kiosk.show(inputEl, 'all');
// Simulate typing
this.kiosk.keyPress('H');
this.kiosk.keyPress('e');
this.kiosk.keyPress('l');
this.kiosk.keyPress('l');
this.kiosk.keyPress('o');
// Clear and hide
this.kiosk.ClearInput();
this.kiosk.hide();
Gotchas and tips
- The keyboard prevents focus loss: the
UiKioskBoardComponentinterceptsmousedownevents withpreventDefault()to keep the input focused while the user taps keys. This is essential for the keyboard to work correctly. - Password inputs auto-switch: if you call
show()on an input withtype="password", the keyboard type is overridden to'password'regardless of thedata-kioskboard-typeattribute. - Kiosk mode must be enabled globally: the keyboard only appears when
platformService._isKioskistrue. Enable it viaviewService.Kiosk = true. - Key layouts are loaded via HTTP: the JSON layout files must be available in the
assets/kioskboard/directory. If the file for the current language is missing, it falls back to the default language.
Related services
| Service | Relationship |
|---|---|
platformService | Controls whether kiosk mode is enabled. Provides OnKioskChanged. |
viewService | Exposes Kiosk getter/setter that persists the setting and delegates to platformService. |
languageService | Determines which keyboard layout JSON is loaded. |
configService | Persists the kiosk mode preference. |