Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat(android): ability to embed into platform host projects #10465

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 1, 2024
Merged
3 changes: 2 additions & 1 deletion packages/core/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ dist
css/parser.js*
css/system-classes.js*
!css-value/**/*.*
!fetch/**/*.*
!fetch/**/*.*
/coverage
13 changes: 12 additions & 1 deletion packages/core/application/application.android.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { profile } from '../profiling';
import { View } from '../ui';
import { View } from '../ui/core/view';
import { isEmbedded } from '../ui/embedding';
import { AndroidActivityCallbacks, NavigationEntry } from '../ui/frame/frame-common';
import type { AndroidApplication as IAndroidApplication } from './application';
import { ApplicationCommon } from './application-common';
Expand All @@ -10,6 +11,12 @@ declare namespace com {
class NativeScriptApplication extends android.app.Application {
static getInstance(): NativeScriptApplication;
}

namespace embedding {
class ApplicationHolder {
static getInstance(): android.app.Application;
}
}
}
}

Expand Down Expand Up @@ -358,6 +365,10 @@ export class AndroidApplication extends ApplicationCommon implements IAndroidApp
nativeApp = com.tns.NativeScriptApplication.getInstance();
}

if (!nativeApp && isEmbedded()) {
nativeApp = com.tns.embedding.ApplicationHolder.getInstance();
}

// the getInstance might return null if com.tns.NativeScriptApplication exists but is not the starting app type
if (!nativeApp) {
// TODO: Should we handle the case when a custom application type is provided and the user has not explicitly initialized the application module?
Expand Down
5 changes: 3 additions & 2 deletions packages/core/application/application.ios.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { profile } from '../profiling';
import { View } from '../ui';
import { View } from '../ui/core/view';
import { isEmbedded } from '../ui/embedding';
import { IOSHelper } from '../ui/core/view/view-helper';
import { NavigationEntry } from '../ui/frame/frame-interfaces';
import * as Utils from '../utils';
Expand Down Expand Up @@ -367,7 +368,7 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
});

if (this._window) {
if (root !== null && !NativeScriptEmbedder.sharedInstance().delegate) {
if (root !== null && !isEmbedded()) {
this.setWindowContent(root);
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nativescript/core",
"version": "8.8.0",
"version": "8.8.0-embed.1",
"description": "A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.",
"main": "index",
"types": "index.d.ts",
Expand Down
26 changes: 26 additions & 0 deletions packages/core/ui/embedding/index.android.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { View } from '../../ui/core/view';

declare namespace org {
namespace nativescript {
class Bootstrap {
static isEmbeddedNativeScript: boolean;
}
}
}

export function isEmbedded(): boolean {
return org.nativescript?.Bootstrap?.isEmbeddedNativeScript;
}

let embeddedView: View | undefined;

export function setEmbeddedView(view: View | undefined): void {
embeddedView = view;
}

export function getEmbeddedView(): View {
if (!embeddedView) {
throw new Error("{N} Core: Fragment content view not set or set to 'undefined'");
}
return embeddedView;
}
10 changes: 10 additions & 0 deletions packages/core/ui/embedding/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { View } from '../../ui/core/view';

/**
* Whether the app is embedded into a host project or standalone project
*/
export function isEmbedded(): boolean;

export function setEmbeddedView(view: View | undefined): void;

export function getEmbeddedView(): View;
3 changes: 3 additions & 0 deletions packages/core/ui/embedding/index.ios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function isEmbedded(): boolean {
return !!NativeScriptEmbedder.sharedInstance().delegate;
}
191 changes: 132 additions & 59 deletions packages/core/ui/frame/activity.android.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,135 @@
import '../../globals';
import { setActivityCallbacks, AndroidActivityCallbacks } from '.';
import { setActivityCallbacks } from '.';
import { Application } from '../../application';
import { isEmbedded } from '../embedding';

/**
* NOTE: We cannot use NativeClass here because this is used in appComponents in webpack.config
* Whereby it bypasses the decorator transformation, hence pure es5 style written here
*/
const superProto = androidx.appcompat.app.AppCompatActivity.prototype;
(<any>androidx.appcompat.app.AppCompatActivity).extend('com.tns.NativeScriptActivity', {
init() {
// init must at least be defined
},
onCreate(savedInstanceState: android.os.Bundle): void {
Application.android.init(this.getApplication());

// Set isNativeScriptActivity in onCreate.
// The JS constructor might not be called because the activity is created from Android.
this.isNativeScriptActivity = true;
if (!this._callbacks) {
setActivityCallbacks(this);
}

this._callbacks.onCreate(this, savedInstanceState, this.getIntent(), superProto.onCreate);
},

onNewIntent(intent: android.content.Intent): void {
this._callbacks.onNewIntent(this, intent, superProto.setIntent, superProto.onNewIntent);
},

onSaveInstanceState(outState: android.os.Bundle): void {
this._callbacks.onSaveInstanceState(this, outState, superProto.onSaveInstanceState);
},

onStart(): void {
this._callbacks.onStart(this, superProto.onStart);
},

onStop(): void {
this._callbacks.onStop(this, superProto.onStop);
},

onDestroy(): void {
this._callbacks.onDestroy(this, superProto.onDestroy);
},

onPostResume(): void {
this._callbacks.onPostResume(this, superProto.onPostResume);
},

onBackPressed(): void {
this._callbacks.onBackPressed(this, superProto.onBackPressed);
},

onRequestPermissionsResult(requestCode: number, permissions: Array<string>, grantResults: Array<number>): void {
this._callbacks.onRequestPermissionsResult(this, requestCode, permissions, grantResults, undefined /*TODO: Enable if needed*/);
},

onActivityResult(requestCode: number, resultCode: number, data: android.content.Intent): void {
this._callbacks.onActivityResult(this, requestCode, resultCode, data, superProto.onActivityResult);
},
});
const EMPTY_FN = () => {};
declare const com: any;

if (!isEmbedded()) {
/**
* NOTE: We cannot use NativeClass here because this is used in appComponents in webpack.config
* Whereby it bypasses the decorator transformation, hence pure es5 style written here
*/
const superProto = androidx.appcompat.app.AppCompatActivity.prototype;
(<any>androidx.appcompat.app.AppCompatActivity).extend('com.tns.NativeScriptActivity', {
init() {
// init must at least be defined
},
onCreate(savedInstanceState: android.os.Bundle): void {
Application.android.init(this.getApplication());

// Set isNativeScriptActivity in onCreate.
// The JS constructor might not be called because the activity is created from Android.
this.isNativeScriptActivity = true;
if (!this._callbacks) {
setActivityCallbacks(this);
}

this._callbacks.onCreate(this, savedInstanceState, this.getIntent(), superProto.onCreate);
},

onNewIntent(intent: android.content.Intent): void {
this._callbacks.onNewIntent(this, intent, superProto.setIntent, superProto.onNewIntent);
},

onSaveInstanceState(outState: android.os.Bundle): void {
this._callbacks.onSaveInstanceState(this, outState, superProto.onSaveInstanceState);
},

onStart(): void {
this._callbacks.onStart(this, superProto.onStart);
},

onStop(): void {
this._callbacks.onStop(this, superProto.onStop);
},

onDestroy(): void {
this._callbacks.onDestroy(this, superProto.onDestroy);
},

onPostResume(): void {
this._callbacks.onPostResume(this, superProto.onPostResume);
},

onBackPressed(): void {
this._callbacks.onBackPressed(this, superProto.onBackPressed);
},

onRequestPermissionsResult(requestCode: number, permissions: Array<string>, grantResults: Array<number>): void {
this._callbacks.onRequestPermissionsResult(this, requestCode, permissions, grantResults, undefined /*TODO: Enable if needed*/);
},

onActivityResult(requestCode: number, resultCode: number, data: android.content.Intent): void {
this._callbacks.onActivityResult(this, requestCode, resultCode, data, superProto.onActivityResult);
},
});
} else {
const Callbacks = com.tns.embedding.EmbeddableActivityCallbacks.extend({
init() {
// init must at least be defined
},
onCreate(savedInstanceState: android.os.Bundle): void {
const activity = this.getActivity();

Application.android.init(activity.getApplication());

// Set isNativeScriptActivity in onCreate.
// The JS constructor might not be called because the activity is created from Android.
activity.isNativeScriptActivity = true;
if (!activity._callbacks) {
setActivityCallbacks(activity);
}

activity._callbacks.onCreate(activity, savedInstanceState, activity.getIntent(), EMPTY_FN);
},

onNewIntent(intent: android.content.Intent): void {
const activity = this.getActivity();
activity._callbacks.onNewIntent(activity, intent, EMPTY_FN, EMPTY_FN);
},

onSaveInstanceState(outState: android.os.Bundle): void {
const activity = this.getActivity();
activity._callbacks.onSaveInstanceState(activity, outState, EMPTY_FN);
},

onStart(): void {
const activity = this.getActivity();
activity._callbacks.onStart(activity, EMPTY_FN);
},

onStop(): void {
const activity = this.getActivity();
activity._callbacks.onStop(activity, EMPTY_FN);
},

onDestroy(): void {
const activity = this.getActivity();
activity._callbacks.onDestroy(activity, EMPTY_FN);
},

onPostResume(): void {
const activity = this.getActivity();
activity._callbacks.onPostResume(activity, EMPTY_FN);
},

onBackPressed(): void {
const activity = this.getActivity();
activity._callbacks.onBackPressed(activity, EMPTY_FN);
},

onRequestPermissionsResult(requestCode: number, permissions: Array<string>, grantResults: Array<number>): void {
const activity = this.getActivity();
activity._callbacks.onRequestPermissionsResult(activity, requestCode, permissions, grantResults, undefined /*TODO: Enable if needed*/);
},

onActivityResult(requestCode: number, resultCode: number, data: android.content.Intent): void {
const activity = this.getActivity();
activity._callbacks.onActivityResult(activity, requestCode, resultCode, data, EMPTY_FN);
},
});

com.tns.embedding.CallbacksStore.setActivityCallbacks(new Callbacks());
}
Loading
Loading