feat: expose application orientation (#7602)

This commit is contained in:
Vasil Chimev
2019-08-14 13:47:15 +03:00
committed by GitHub
parent a14bc9f86d
commit e2c3c8c084
15 changed files with 161 additions and 155 deletions

View File

@@ -1,21 +1,16 @@
// >> application-require import * as app from "tns-core-modules/application";
import * as app from "tns-core-modules/application";
import * as platform from "tns-core-modules/platform"; import * as platform from "tns-core-modules/platform";
// << application-require
// >> application-app-check
if (app.android) {
console.log("We are running on Android device!");
} else if (app.ios) {
console.log("We are running on iOS device");
}
// << application-app-check
import * as TKUnit from "../tk-unit"; import * as TKUnit from "../tk-unit";
if (app.android) {
console.log("We are running on an Android device!");
} else if (app.ios) {
console.log("We are running on an iOS device!");
}
export function testInitialized() { export function testInitialized() {
if (platform.device.os === platform.platformNames.android) { if (platform.device.os === platform.platformNames.android) {
// we have the android defined
TKUnit.assert(app.android, "Application module not properly intialized"); TKUnit.assert(app.android, "Application module not properly intialized");
} else if (platform.device.os === platform.platformNames.ios) { } else if (platform.device.os === platform.platformNames.ios) {
TKUnit.assert(app.ios, "Application module not properly intialized"); TKUnit.assert(app.ios, "Application module not properly intialized");

View File

@@ -37,12 +37,13 @@ if (app.android) {
} }
// << application-app-android-broadcast // << application-app-android-broadcast
export var testAndroidApplicationInitialized = function () { export function testAndroidApplicationInitialized() {
TKUnit.assert(app.android, "Android application not initialized."); TKUnit.assert(app.android, "Android application not initialized.");
TKUnit.assert(app.android.context, "Android context not initialized."); TKUnit.assert(app.android.context, "Android context not initialized.");
TKUnit.assert(app.android.foregroundActivity, "Android foregroundActivity not initialized."); TKUnit.assert(app.android.foregroundActivity, "Android foregroundActivity not initialized.");
TKUnit.assert(app.android.foregroundActivity.isNativeScriptActivity, "Andorid foregroundActivity.isNativeScriptActivity is true"); TKUnit.assert(app.android.foregroundActivity.isNativeScriptActivity, "Andorid foregroundActivity.isNativeScriptActivity is false.");
TKUnit.assert(app.android.startActivity, "Android startActivity not initialized."); TKUnit.assert(app.android.startActivity, "Android startActivity not initialized.");
TKUnit.assert(app.android.nativeApp, "Android nativeApp not initialized."); TKUnit.assert(app.android.nativeApp, "Android nativeApp not initialized.");
TKUnit.assert(app.android.orientation, "Android orientation not initialized.");
TKUnit.assert(app.android.packageName, "Android packageName not initialized."); TKUnit.assert(app.android.packageName, "Android packageName not initialized.");
}; }

View File

@@ -1,4 +1,2 @@
/* tslint:disable */
//@private
import * as android from "./application-tests.android"; import * as android from "./application-tests.android";
import * as iOS from "./application-tests.ios"; import * as iOS from "./application-tests.ios";

View File

@@ -1,5 +1,6 @@
/* tslint:disable:no-unused-variable */ /* tslint:disable:no-unused-variable */
import * as app from "tns-core-modules/application"; import * as app from "tns-core-modules/application";
import * as TKUnit from "../tk-unit";
export * from "./application-tests-common"; export * from "./application-tests-common";
@@ -39,3 +40,12 @@ if (app.ios) {
} }
// << application-ios-delegate // << application-ios-delegate
export function testIOSApplicationInitialized() {
TKUnit.assert(app.ios, "iOS application not initialized.");
TKUnit.assert(app.ios.delegate, "iOS delegate not initialized.");
TKUnit.assert(app.ios.nativeApp, "iOS nativeApp not initialized.");
TKUnit.assert(app.ios.orientation, "iOS orientation not initialized.");
TKUnit.assert(app.ios.window, "iOS window not initialized.");
TKUnit.assert(app.ios.rootController, "iOS root controller not initialized.");
}

View File

@@ -1,34 +0,0 @@
---
nav-title: "application How-To"
title: "application"
environment: nativescript
description: "Examples for using application"
previous_url: /ApiReference/application/HOW-TO
---
# Application
The Application module provides abstraction over the platform-specific Application implementations.
It is the main BCL module and is required for other BCL modules to work properly.
The default bootstrap.js implementation for each platform loads and initializes this module.
{%snippet application-require%}
The pre-required `app` module is used throughout the following code snippets.
### Checking the target platform
Use the following code in case you need to check somewhere in your code the platform you are running against:
{%snippet application-app-check%}
### Using the Android-specific implementation
Accessing the Android-specific object instance (will be undefined if running on iOS)
{%snippet application-app-android%}
### Using the Android Application context
{%snippet application-app-android-context%}
### Tracking the current Activity
{%snippet application-app-android-current%}
### Registering a Broadcast Receiver (Android)
{%snippet application-app-android-broadcast%}
### Adding a Notification Observer (iOS)
{%snippet application-ios-observer%}

View File

@@ -1,46 +1,41 @@
import * as TKUnit from "../tk-unit"; import * as TKUnit from "../tk-unit";
import * as app from "tns-core-modules/application"; import * as app from "tns-core-modules/application";
import { isIOS, isAndroid } from "tns-core-modules/platform";
// >> platform-require
import * as platformModule from "tns-core-modules/platform"; import * as platformModule from "tns-core-modules/platform";
// << platform-require
export function test_setTimeout_isDefined() { export function test_platform() {
var expected; let expectedPlatform;
if (app.android) { if (app.android) {
expected = "Android"; expectedPlatform = "Android";
} else {
expectedPlatform = "iOS";
} }
else { TKUnit.assertEqual(platformModule.device.os, expectedPlatform);
expected = "iOS";
}
TKUnit.assertEqual(platformModule.device.os, expected, "device.os");
} }
export function snippet_print_all() { export function test_device_screen() {
// >> platform-current TKUnit.assert(platformModule.device.model, "Device model not initialized.");
console.log("Device model: " + platformModule.device.model); TKUnit.assert(platformModule.device.manufacturer, "Device manufacturer not initialized.");
console.log("Device type: " + platformModule.device.deviceType); TKUnit.assert(platformModule.device.deviceType, "Device type not initialized.");
console.log("Device manufacturer: " + platformModule.device.manufacturer); TKUnit.assert(platformModule.device.uuid, "Device UUID not initialized.");
console.log("Preferred language: " + platformModule.device.language);
console.log("Preferred region: " + platformModule.device.region);
console.log("OS: " + platformModule.device.os);
console.log("OS version: " + platformModule.device.osVersion);
console.log("SDK version: " + platformModule.device.sdkVersion);
console.log("Device UUID: " + platformModule.device.uuid);
console.log("Screen width (px): " + platformModule.screen.mainScreen.widthPixels); TKUnit.assert(platformModule.device.language, "Preferred language not initialized.");
console.log("Screen height (px): " + platformModule.screen.mainScreen.heightPixels); TKUnit.assert(platformModule.device.region, "Preferred region not initialized.");
console.log("Screen width (DIPs): " + platformModule.screen.mainScreen.widthDIPs);
console.log("Screen height (DIPs): " + platformModule.screen.mainScreen.heightDIPs); TKUnit.assert(platformModule.device.os, "OS not initialized.");
console.log("Screen scale: " + platformModule.screen.mainScreen.scale); TKUnit.assert(platformModule.device.osVersion, "OS version not initialized.");
// << platform-current TKUnit.assert(platformModule.device.sdkVersion, "SDK version not initialized.");
TKUnit.assert(platformModule.screen.mainScreen.widthPixels, "Screen width (px) not initialized.");
TKUnit.assert(platformModule.screen.mainScreen.heightPixels, "Screen height (px) not initialized.");
TKUnit.assert(platformModule.screen.mainScreen.widthDIPs, "Screen width (DIPs) not initialized.");
TKUnit.assert(platformModule.screen.mainScreen.heightDIPs, "Screen height (DIPs) not initialized.");
TKUnit.assert(platformModule.screen.mainScreen.scale, "Screen scale not initialized.");
} }
export function testIsIOSandIsAndroid() { export function test_IsAndroid_IsIOS() {
if (isIOS) { if (platformModule.isIOS) {
TKUnit.assertTrue(!!NSObject, "isIOS is true-ish but common iOS APIs are not available."); TKUnit.assertTrue(!!NSObject, "isIOS is true-ish but common iOS APIs are not available.");
} else if (isAndroid) { } else if (platformModule.isAndroid) {
TKUnit.assertTrue(!!android, "isAndroid is true but common 'android' package is not available."); TKUnit.assertTrue(!!android, "isAndroid is true-ish but common 'android' package is not available.");
} }
} }

View File

@@ -35,10 +35,10 @@ export { Observable };
import { import {
AndroidApplication, AndroidApplication,
CssChangedEventData, CssChangedEventData,
DiscardedErrorEventData,
iOSApplication, iOSApplication,
LoadAppCSSEventData, LoadAppCSSEventData,
UnhandledErrorEventData, UnhandledErrorEventData
DiscardedErrorEventData,
} from "./application"; } from "./application";
export { UnhandledErrorEventData, DiscardedErrorEventData, CssChangedEventData, LoadAppCSSEventData }; export { UnhandledErrorEventData, DiscardedErrorEventData, CssChangedEventData, LoadAppCSSEventData };

View File

@@ -42,6 +42,7 @@ export class AndroidApplication extends Observable implements AndroidApplication
public static activityNewIntentEvent = ActivityNewIntent; public static activityNewIntentEvent = ActivityNewIntent;
public static activityRequestPermissionsEvent = ActivityRequestPermissions; public static activityRequestPermissionsEvent = ActivityRequestPermissions;
private _orientation: "portrait" | "landscape" | "unknown";
public paused: boolean; public paused: boolean;
public nativeApp: android.app.Application; public nativeApp: android.app.Application;
public context: android.content.Context; public context: android.content.Context;
@@ -80,6 +81,22 @@ export class AndroidApplication extends Observable implements AndroidApplication
this._pendingReceiverRegistrations.length = 0; this._pendingReceiverRegistrations.length = 0;
} }
get orientation(): "portrait" | "landscape" | "unknown" {
if (!this._orientation) {
const resources = this.context.getResources();
const configuration = <android.content.res.Configuration>resources.getConfiguration();
const orientation = configuration.orientation;
this._orientation = getOrientationValue(orientation);
}
return this._orientation;
}
set orientation(value: "portrait" | "landscape" | "unknown") {
this._orientation = value;
}
public registerBroadcastReceiver(intentFilter: string, onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void) { public registerBroadcastReceiver(intentFilter: string, onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void) {
ensureBroadCastReceiverClass(); ensureBroadCastReceiverClass();
const that = this; const that = this;
@@ -232,6 +249,17 @@ global.__onLiveSync = function __onLiveSync(context?: ModuleContext) {
livesync(rootView, context); livesync(rootView, context);
}; };
function getOrientationValue(orientation: number): "portrait" | "landscape" | "unknown" {
switch (orientation) {
case android.content.res.Configuration.ORIENTATION_LANDSCAPE:
return "landscape";
case android.content.res.Configuration.ORIENTATION_PORTRAIT:
return "portrait";
default:
return "unknown";
}
}
function initLifecycleCallbacks() { function initLifecycleCallbacks() {
const setThemeOnLaunch = profile("setThemeOnLaunch", (activity: androidx.appcompat.app.AppCompatActivity) => { const setThemeOnLaunch = profile("setThemeOnLaunch", (activity: androidx.appcompat.app.AppCompatActivity) => {
// Set app theme after launch screen was used during startup // Set app theme after launch screen was used during startup
@@ -321,7 +349,6 @@ function initLifecycleCallbacks() {
return lifecycleCallbacks; return lifecycleCallbacks;
} }
let currentOrientation: number;
function initComponentCallbacks() { function initComponentCallbacks() {
let componentCallbacks = new android.content.ComponentCallbacks2({ let componentCallbacks = new android.content.ComponentCallbacks2({
onLowMemory: profile("onLowMemory", function () { onLowMemory: profile("onLowMemory", function () {
@@ -335,32 +362,19 @@ function initComponentCallbacks() {
}), }),
onConfigurationChanged: profile("onConfigurationChanged", function (newConfig: android.content.res.Configuration) { onConfigurationChanged: profile("onConfigurationChanged", function (newConfig: android.content.res.Configuration) {
const newOrientation = newConfig.orientation; const newConfigOrientation = newConfig.orientation;
if (newOrientation === currentOrientation) { const newOrientation = getOrientationValue(newConfigOrientation);
return;
if (androidApp.orientation !== newOrientation) {
androidApp.orientation = newOrientation;
notify(<OrientationChangedEventData>{
eventName: orientationChangedEvent,
android: androidApp.nativeApp,
newValue: androidApp.orientation,
object: androidApp
});
} }
currentOrientation = newOrientation;
let newValue;
switch (newOrientation) {
case android.content.res.Configuration.ORIENTATION_LANDSCAPE:
newValue = "landscape";
break;
case android.content.res.Configuration.ORIENTATION_PORTRAIT:
newValue = "portrait";
break;
default:
newValue = "unknown";
break;
}
notify(<OrientationChangedEventData>{
eventName: orientationChangedEvent,
android: androidApp.nativeApp,
newValue: newValue,
object: androidApp
});
}) })
}); });

View File

@@ -413,6 +413,12 @@ export class AndroidApplication extends Observable {
*/ */
startActivity: any /* androidx.appcompat.app.AppCompatActivity */; startActivity: any /* androidx.appcompat.app.AppCompatActivity */;
/**
* Gets the orientation of the application.
* Available values: "portrait", "landscape", "unknown".
*/
orientation: "portrait" | "landscape" | "unknown";
/** /**
* The name of the application package. * The name of the application package.
*/ */
@@ -581,16 +587,22 @@ export interface iOSApplication {
*/ */
window: any /* UIWindow */; window: any /* UIWindow */;
/**
* The [UIApplication](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/index.html).
*/
nativeApp: any /* UIApplication */;
/** /**
* The [UIApplicationDelegate](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html) class. * The [UIApplicationDelegate](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html) class.
*/ */
delegate: any /* typeof UIApplicationDelegate */; delegate: any /* typeof UIApplicationDelegate */;
/**
* Gets or sets the orientation of the application.
* Available values: "portrait", "landscape", "unknown".
*/
orientation: "portrait" | "landscape" | "unknown";
/**
* The [UIApplication](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/index.html).
*/
nativeApp: any /* UIApplication */;
/** /**
* Adds an observer to the default notification center for the specified notification. * Adds an observer to the default notification center for the specified notification.
* For more information, please visit 'https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/#//apple_ref/occ/instm/NSNotificationCenter/addObserver:selector:name:object:' * For more information, please visit 'https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/#//apple_ref/occ/instm/NSNotificationCenter/addObserver:selector:name:object:'

View File

@@ -78,9 +78,9 @@ class CADisplayLinkTarget extends NSObject {
class IOSApplication implements IOSApplicationDefinition { class IOSApplication implements IOSApplicationDefinition {
private _delegate: typeof UIApplicationDelegate; private _delegate: typeof UIApplicationDelegate;
private _currentOrientation = UIDevice.currentDevice.orientation;
private _window: UIWindow; private _window: UIWindow;
private _observers: Array<NotificationObserver>; private _observers: Array<NotificationObserver>;
private _orientation: "portrait" | "landscape" | "unknown";
private _rootView: View; private _rootView: View;
constructor() { constructor() {
@@ -90,7 +90,16 @@ class IOSApplication implements IOSApplicationDefinition {
this.addNotificationObserver(UIApplicationDidEnterBackgroundNotification, this.didEnterBackground.bind(this)); this.addNotificationObserver(UIApplicationDidEnterBackgroundNotification, this.didEnterBackground.bind(this));
this.addNotificationObserver(UIApplicationWillTerminateNotification, this.willTerminate.bind(this)); this.addNotificationObserver(UIApplicationWillTerminateNotification, this.willTerminate.bind(this));
this.addNotificationObserver(UIApplicationDidReceiveMemoryWarningNotification, this.didReceiveMemoryWarning.bind(this)); this.addNotificationObserver(UIApplicationDidReceiveMemoryWarningNotification, this.didReceiveMemoryWarning.bind(this));
this.addNotificationObserver(UIDeviceOrientationDidChangeNotification, this.orientationDidChange.bind(this)); this.addNotificationObserver(UIApplicationDidChangeStatusBarOrientationNotification, this.didChangeStatusBarOrientation.bind(this));
}
get orientation(): "portrait" | "landscape" | "unknown" {
if (!this._orientation) {
const statusBarOrientation = UIApplication.sharedApplication.statusBarOrientation;
this._orientation = this.getOrientationValue(statusBarOrientation);
}
return this._orientation;
} }
get rootController(): UIViewController { get rootController(): UIViewController {
@@ -197,40 +206,39 @@ class IOSApplication implements IOSApplicationDefinition {
} }
} }
private didReceiveMemoryWarning(notification: NSNotification) { private didChangeStatusBarOrientation(notification: NSNotification) {
notify(<ApplicationEventData>{ eventName: lowMemoryEvent, object: this, ios: UIApplication.sharedApplication }); const statusBarOrientation = UIApplication.sharedApplication.statusBarOrientation;
} const newOrientation = this.getOrientationValue(statusBarOrientation);
private orientationDidChange(notification: NSNotification) { if (this._orientation !== newOrientation) {
const orientation = UIDevice.currentDevice.orientation; this._orientation = newOrientation;
if (this._currentOrientation !== orientation) {
this._currentOrientation = orientation;
let newValue: "portrait" | "landscape" | "unknown";
switch (orientation) {
case UIDeviceOrientation.LandscapeRight:
case UIDeviceOrientation.LandscapeLeft:
newValue = "landscape";
break;
case UIDeviceOrientation.Portrait:
case UIDeviceOrientation.PortraitUpsideDown:
newValue = "portrait";
break;
default:
newValue = "unknown";
break;
}
notify(<OrientationChangedEventData>{ notify(<OrientationChangedEventData>{
eventName: orientationChangedEvent, eventName: orientationChangedEvent,
ios: this, ios: this,
newValue: newValue, newValue: this._orientation,
object: this object: this
}); });
} }
} }
private didReceiveMemoryWarning(notification: NSNotification) {
notify(<ApplicationEventData>{ eventName: lowMemoryEvent, object: this, ios: UIApplication.sharedApplication });
}
private getOrientationValue(orientation: number): "portrait" | "landscape" | "unknown" {
switch (orientation) {
case UIInterfaceOrientation.LandscapeRight:
case UIInterfaceOrientation.LandscapeLeft:
return "landscape";
case UIInterfaceOrientation.PortraitUpsideDown:
case UIInterfaceOrientation.Portrait:
return "portrait";
case UIInterfaceOrientation.Unknown:
return "unknown";
}
}
public _onLivesync(context?: ModuleContext): void { public _onLivesync(context?: ModuleContext): void {
// Handle application root module // Handle application root module
const isAppRootModuleChanged = context && context.path && context.path.includes(getMainEntry().moduleName) && context.type !== "style"; const isAppRootModuleChanged = context && context.path && context.path.includes(getMainEntry().moduleName) && context.type !== "style";

View File

@@ -19,10 +19,6 @@ class Device implements DeviceDefinition {
private _language: string; private _language: string;
private _region: string; private _region: string;
get os(): string {
return platformNames.android;
}
get manufacturer(): string { get manufacturer(): string {
if (!this._manufacturer) { if (!this._manufacturer) {
this._manufacturer = android.os.Build.MANUFACTURER; this._manufacturer = android.os.Build.MANUFACTURER;
@@ -31,6 +27,10 @@ class Device implements DeviceDefinition {
return this._manufacturer; return this._manufacturer;
} }
get os(): string {
return platformNames.android;
}
get osVersion(): string { get osVersion(): string {
if (!this._osVersion) { if (!this._osVersion) {
this._osVersion = android.os.Build.VERSION.RELEASE; this._osVersion = android.os.Build.VERSION.RELEASE;

View File

@@ -215,6 +215,8 @@ export module ios {
} }
/** /**
* @deprecated use application.orientation instead
*
* Gets an information about if current mode is Landscape. * Gets an information about if current mode is Landscape.
*/ */
export function isLandscape(): boolean; export function isLandscape(): boolean;

View File

@@ -8,7 +8,8 @@ export * from "./utils-common";
let mainScreenScale; let mainScreenScale;
function isOrientationLandscape(orientation: number) { function isOrientationLandscape(orientation: number) {
return orientation === UIDeviceOrientation.LandscapeLeft || orientation === UIDeviceOrientation.LandscapeRight; return orientation === UIDeviceOrientation.LandscapeLeft /* 3 */ ||
orientation === UIDeviceOrientation.LandscapeRight /* 4 */;
} }
export module layout { export module layout {
@@ -79,11 +80,15 @@ export module ios {
} }
export function isLandscape(): boolean { export function isLandscape(): boolean {
const device = UIDevice.currentDevice; console.log("utils.ios.isLandscape() is deprecated; use application.orientation instead");
const deviceOrientation = UIDevice.currentDevice.orientation;
const statusBarOrientation = UIApplication.sharedApplication.statusBarOrientation; const statusBarOrientation = UIApplication.sharedApplication.statusBarOrientation;
const isDeviceOrientationLandscape = isOrientationLandscape(deviceOrientation);
const isStatusBarOrientationLandscape = isOrientationLandscape(statusBarOrientation); const isStatusBarOrientationLandscape = isOrientationLandscape(statusBarOrientation);
return isOrientationLandscape(device.orientation) || isStatusBarOrientationLandscape; return isDeviceOrientationLandscape || isStatusBarOrientationLandscape;
} }
export const MajorVersion = NSString.stringWithString(UIDevice.currentDevice.systemVersion).intValue; export const MajorVersion = NSString.stringWithString(UIDevice.currentDevice.systemVersion).intValue;
@@ -153,7 +158,7 @@ export function openFile(filePath: string): boolean {
} }
// Need this so that we can use this function inside the ios module (avoid name clashing). // Need this so that we can use this function inside the ios module (avoid name clashing).
const openFileAtRootModule = openFile; const openFileAtRootModule = openFile;
export function GC() { export function GC() {
__collect(); __collect();