Skip to content
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

feat: add android embedding and merge main #10465

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions packages/core/application/application.android.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { embedded } from 'ui/embedding';
import { profile } from '../profiling';
import { View } from '../ui';
import { AndroidActivityCallbacks, NavigationEntry } from '../ui/frame/frame-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 && embedded()) {
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
26 changes: 26 additions & 0 deletions packages/core/ui/embedding/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { View } from '../../ui/core/view';

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

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

let view: View | undefined;

export function setContentView(contentView: View | undefined): void {
view = contentView;
}

export function getContentView(): View {
if (!view) {
throw new Error("{N} Core: Fragment content view not set or set to 'undefined'");
}
return view;
}
193 changes: 134 additions & 59 deletions packages/core/ui/frame/activity.android.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,137 @@
import '../../globals';
import { setActivityCallbacks, AndroidActivityCallbacks } from '.';
import { setActivityCallbacks } from '.';
import { Application } from '../../application';
import { embedded } from 'ui/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 isEmbedded = embedded();

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());
}