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: App API #99

Merged
merged 17 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dirty-books-prove.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@capacitor/background-runner": major
---

Adding a new App API, as well as app badge manipulation methods to the Notifications API
2 changes: 1 addition & 1 deletion .github/actions/setup-tools/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ runs:
java-version: '17'

- name: Install PNPM
uses: pnpm/action-setup@v2
uses: pnpm/action-setup@v4
id: pnpm-install
with:
version: 8
Expand Down
44 changes: 40 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,9 +441,11 @@ A simple string key / value store backed by UserDefaults on iOS and Shared Prefe

Send basic local notifications.

| Prop | Type | Description | Since |
| -------------- | ------------------------------------- | ----------------------------- | ----- |
| **`schedule`** | <code>(options: {}) =&gt; void</code> | Schedule a local notification | 1.0.0 |
| Prop | Type | Description | Since |
| ---------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------- | ----- |
| **`schedule`** | <code>(options: {}) =&gt; void</code> | Schedule a local notification | 1.0.0 |
| **`setBadge`** | <code>(options: <a href="#notificationbadgeoptions">NotificationBadgeOptions</a>) =&gt; void</code> | Set the application badge count | 2.0.0 |
| **`clearBadge`** | <code>() =&gt; void</code> | Clears the application badge count | 2.0.0 |


#### NotificationScheduleOptions
Expand All @@ -470,6 +472,15 @@ Send basic local notifications.
| **`channelId`** | <code>string</code> | Specifies the channel the notification should be delivered on. If channel with the given name does not exist then the notification will not fire. If not provided, it will use the default channel. Calls `setChannelId()` on [`NotificationCompat.Builder`](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder) with the provided value. Only available for Android 26+. | 1.0.0 |


#### NotificationBadgeOptions

| Prop | Type | Description | Since |
| -------------------------- | ------------------- | ------------------------------------------------------------------------------------- | ----- |
| **`count`** | <code>number</code> | The number to set on the application badge count. | 2.0.0 |
| **`notificationTitle`** | <code>string</code> | The **required** title for the associated badge count notification. Only for Android. | 2.0.0 |
| **`notificationSubtitle`** | <code>string</code> | The subtitle for the associated badge count notification. Only for Android. | 2.0.0 |


#### CapacitorGeolocation

Get access to device location information.
Expand All @@ -496,7 +507,7 @@ Get access to device location information.

Interact with a watch paired with this app

sendMessage, transferUserInfo and updateApplicationContext are raw routes to the WCSession delegate methods, but have no effects currently in a CapactiorWatch Watch application.
sendMessage, transferUserInfo and updateApplicationContext are raw routes to the WCSession delegate methods, but have no effects currently in a <a href="#capacitorwatch">CapacitorWatch</a> Watch application.
They could be used if a native watch app is developed as a companion app to a Capacitor app

| Prop | Type | Description |
Expand All @@ -509,4 +520,29 @@ They could be used if a native watch app is developed as a companion app to a Ca
| **`updateWatchData`** | <code>(options: { data: { [key: string]: string; }; }) =&gt; void</code> | Updates the data the watch is using to display variables in text and button fields |


#### CapacitorApp

| Prop | Type |
| -------------- | ------------------------------------------------------ |
| **`getState`** | <code>() =&gt; <a href="#appstate">AppState</a></code> |
| **`getInfo`** | <code>() =&gt; <a href="#appinfo">AppInfo</a></code> |


#### AppState

| Prop | Type | Description | Since |
| -------------- | -------------------- | --------------------------------- | ----- |
| **`isActive`** | <code>boolean</code> | Whether the app is active or not. | 1.0.0 |


#### AppInfo

| Prop | Type | Description | Since |
| ------------- | ------------------- | --------------------------------------------------------------------------------------------------- | ----- |
| **`name`** | <code>string</code> | The name of the app. | 1.0.0 |
| **`id`** | <code>string</code> | The identifier of the app. On iOS it's the Bundle Identifier. On Android it's the Application ID | 1.0.0 |
| **`build`** | <code>string</code> | The build version. On iOS it's the CFBundleVersion. On Android it's the versionCode. | 1.0.0 |
| **`version`** | <code>string</code> | The app version. On iOS it's the CFBundleShortVersionString. On Android it's package's versionName. | 1.0.0 |


</capacitor-api-docs>
57 changes: 57 additions & 0 deletions apps/example-app/src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,63 @@ addEventListener(
}
);

addEventListener("testCapacitorAppGetBadge", (resolve, reject, args) => {
try {
const value = CapacitorNotifications.getBadge();
console.log(JSON.stringify(value));
resolve(value);
} catch (err) {
console.error(err);
reject(err);
}
});

addEventListener("testCapacitorAppSetBadge", (resolve, reject, args) => {
try {
CapacitorNotifications.setBadge({
count: 55,
notificationTitle: "You have new messages",
notificationSubtitle: "testing, testing, 1, 2, 3",
});
resolve();
} catch (err) {
console.error(err);
reject(err);
}
});

addEventListener("testCapacitorAppClearBadge", (resolve, reject, args) => {
try {
CapacitorNotifications.clearBadge();
resolve();
} catch (err) {
console.error(err);
reject(err);
}
});

addEventListener("testCapacitorAppGetInfo", (resolve, reject, args) => {
try {
const info = CapacitorApp.getInfo();
console.log(JSON.stringify(info));
resolve(info);
} catch (err) {
console.error(err);
reject(err);
}
});

addEventListener("testCapacitorAppGetState", (resolve, reject, args) => {
try {
const state = CapacitorApp.getState();
console.log(JSON.stringify(state));
resolve(state);
} catch (err) {
console.error(err);
reject(err);
}
});

addEventListener("remoteNotification", (resolve, reject, args) => {
console.log("received silent push notification");

Expand Down
114 changes: 93 additions & 21 deletions apps/example-app/src/pages/Tab1.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
import { BackgroundRunner } from "@capacitor/background-runner";
import "./Tab1.css";


const Tab1: React.FC = () => {
const [commandOutput, setCommandOutput] = useState<string>("");

Expand Down Expand Up @@ -44,12 +43,10 @@ const Tab1: React.FC = () => {
label: "com.example.background.task",
event: "testCapKVSet",
details: {
value: "Hello World"
value: "Hello World",
},
});
setCommandOutput(
`success: stored value 'Hello World'`
);
setCommandOutput(`success: stored value 'Hello World'`);
} catch (err) {
setCommandOutput(`ERROR: ${err}`);
}
Expand All @@ -63,9 +60,7 @@ const Tab1: React.FC = () => {
event: "testCapKVGet",
details: {},
});
setCommandOutput(
`success: retrieved ${JSON.stringify(response)}`
);
setCommandOutput(`success: retrieved ${JSON.stringify(response)}`);
} catch (err) {
setCommandOutput(`ERROR: ${err}`);
}
Expand All @@ -77,18 +72,13 @@ const Tab1: React.FC = () => {
await BackgroundRunner.dispatchEvent({
label: "com.example.background.task",
event: "testCapKVRemove",
details: {

},
details: {},
});
setCommandOutput(
`success: value removed`
);
setCommandOutput(`success: value removed`);
} catch (err) {
setCommandOutput(`ERROR: ${err}`);
}
};


const onTestCapNotification = async () => {
setCommandOutput("");
Expand Down Expand Up @@ -146,6 +136,62 @@ const Tab1: React.FC = () => {
}
};

const onTestCapAppSetBadge = async () => {
setCommandOutput("");
try {
await BackgroundRunner.dispatchEvent({
label: "com.example.background.task",
event: "testCapacitorAppSetBadge",
details: {},
});
setCommandOutput(`success`);
} catch (err) {
setCommandOutput(`ERROR: ${err}`);
}
};

const onTestCapAppClearBadge = async () => {
setCommandOutput("");
try {
await BackgroundRunner.dispatchEvent({
label: "com.example.background.task",
event: "testCapacitorAppClearBadge",
details: {},
});
setCommandOutput(`success`);
} catch (err) {
setCommandOutput(`ERROR: ${err}`);
}
};

const onTestCapAppGetInfo = async () => {
setCommandOutput("");
try {
const response = await BackgroundRunner.dispatchEvent({
label: "com.example.background.task",
event: "testCapacitorAppGetInfo",
details: {},
});
setCommandOutput(`success: ${JSON.stringify(response)}`);
} catch (err) {
setCommandOutput(`ERROR: ${err}`);
}
};

const onTestCapAppGetState = async () => {
setCommandOutput("");
try {
const response = await BackgroundRunner.dispatchEvent({
label: "com.example.background.task",
event: "testCapacitorAppGetState",
details: {},
});
setCommandOutput(`success: ${JSON.stringify(response)}`);
} catch (err) {
setCommandOutput(`ERROR: ${err}`);
}
};

return (
<IonPage>
<IonHeader>
Expand All @@ -166,17 +212,37 @@ const Tab1: React.FC = () => {
autoGrow={true}
></IonTextarea>
</div>
<IonButton expand="block" color={"success"} onClick={onCheckPermissions}>
<IonButton
expand="block"
color={"success"}
onClick={onCheckPermissions}
>
Check API Permissions
</IonButton>
<IonButton expand="block" color={"success"} onClick={onRequestPermissions}>
<IonButton
expand="block"
color={"success"}
onClick={onRequestPermissions}
>
Request API Permissions
</IonButton>
<IonButton expand="block" onClick={onTestCapKVSet}>Capacitor KV - Set</IonButton>
<IonButton expand="block" onClick={onTestCapKVGet}>Capacitor KV - Get</IonButton>
<IonButton expand="block" onClick={onTestCapKVRemove}>Capacitor KV - Delete</IonButton>
<IonButton expand="block" onClick={onTestCapKVSet}>
Capacitor KV - Set
</IonButton>
<IonButton expand="block" onClick={onTestCapKVGet}>
Capacitor KV - Get
</IonButton>
<IonButton expand="block" onClick={onTestCapKVRemove}>
Capacitor KV - Delete
</IonButton>
<IonButton expand="block" onClick={onTestCapNotification}>
Test Capacitor Notification
Test Capacitor Notif.- Schedule Notification
</IonButton>
<IonButton expand="block" onClick={onTestCapAppSetBadge}>
Test Capacitor Notif. - Set Badge
</IonButton>
<IonButton expand="block" onClick={onTestCapAppClearBadge}>
Test Capacitor Notif. - Clear Badge
</IonButton>
<IonButton expand="block" onClick={onTestCapLocation}>
Test Capacitor Geolocation
Expand All @@ -187,6 +253,12 @@ const Tab1: React.FC = () => {
<IonButton expand="block" onClick={onTestCapDeviceNetwork}>
Test Capacitor Device - Network
</IonButton>
<IonButton expand="block" onClick={onTestCapAppGetInfo}>
Test Capacitor App - Get App Info
</IonButton>
<IonButton expand="block" onClick={onTestCapAppGetState}>
Test Capacitor App - Get App State
</IonButton>
</IonContent>
</IonPage>
);
Expand Down
22 changes: 22 additions & 0 deletions packages/android-js-engine/AndroidJSEngine/src/main/cpp/java.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ Java::Java(JNIEnv *env) {
env->DeleteLocalRef(tmp_class);
tmp_class = nullptr;

tmp_class = env->FindClass("io/ionic/android_js_engine/capacitor_api/AppAPI");
this->check_exception(env);

this->capacitor_app_api_class = (jclass)env->NewGlobalRef(tmp_class);
env->DeleteLocalRef(tmp_class);
tmp_class = nullptr;

this->capacitor_api_kv_field = env->GetFieldID(this->capacitor_api_class, "kv", "Lio/ionic/android_js_engine/capacitor_api/KVAPI;");
this->check_exception(env);

Expand All @@ -114,6 +121,9 @@ Java::Java(JNIEnv *env) {
this->capacitor_api_notification_field = env->GetFieldID(this->capacitor_api_class, "notifications", "Lio/ionic/android_js_engine/capacitor_api/NotificationsAPI;");
this->check_exception(env);

this->capacitor_api_app_field = env->GetFieldID(this->capacitor_api_class, "app", "Lio/ionic/android_js_engine/capacitor_api/AppAPI;");
this->check_exception(env);

this->capacitor_api_kv_set_method = env->GetMethodID(this->capacitor_kv_api_class, "set", "(Ljava/lang/String;Ljava/lang/String;)V");
this->check_exception(env);

Expand All @@ -134,6 +144,18 @@ Java::Java(JNIEnv *env) {

this->capacitor_api_notifications_schedule_method = env->GetMethodID(this->capacitor_notification_api_class, "schedule", "(Ljava/lang/String;)V");
this->check_exception(env);

this->capacitor_api_notifications_setBadge_method = env->GetMethodID(capacitor_notification_api_class, "setBadge", "(Ljava/lang/String;)V");
this->check_exception(env);

this->capacitor_api_notifications_clearBadge_method = env->GetMethodID(this->capacitor_notification_api_class, "clearBadge", "()V");
this->check_exception(env);

this->capacitor_api_app_getState_method = env->GetMethodID(this->capacitor_app_api_class, "getState", "()Ljava/lang/String;");
this->check_exception(env);

this->capacitor_api_app_getInfo_method = env->GetMethodID(this->capacitor_app_api_class, "getInfo", "()Ljava/lang/String;");
this->check_exception(env);
}

JNIEnv *Java::getEnv() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Java {
jclass capacitor_notification_api_class;
jclass capacitor_device_api_class;
jclass capacitor_geolocation_api_class;
jclass capacitor_app_api_class;

jmethodID web_api_fetch_method;
jmethodID web_api_byteArrayToString_method;
Expand All @@ -32,6 +33,10 @@ class Java {
jmethodID capacitor_api_device_getNetworkStatus_method;
jmethodID capacitor_api_geolocation_getCurrentPosition_method;
jmethodID capacitor_api_notifications_schedule_method;
jmethodID capacitor_api_notifications_setBadge_method;
jmethodID capacitor_api_notifications_clearBadge_method;
jmethodID capacitor_api_app_getInfo_method;
jmethodID capacitor_api_app_getState_method;

jfieldID native_js_response_ok_field;
jfieldID native_js_response_status_field;
Expand All @@ -43,6 +48,7 @@ class Java {
jfieldID capacitor_api_device_field;
jfieldID capacitor_api_notification_field;
jfieldID capacitor_api_geolocation_field;
jfieldID capacitor_api_app_field;

Java(JNIEnv *env);
JNIEnv *getEnv();
Expand Down
Loading
Loading