From 6a9b55bc561ecb715425fd6ec59135ed989844e2 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 5 Nov 2020 13:28:38 -0800 Subject: [PATCH 01/60] generate notifications plugin --- lerna.json | 1 + notifications/.eslintignore | 2 + notifications/.gitignore | 61 ++ notifications/.prettierignore | 2 + notifications/CONTRIBUTING.md | 42 ++ notifications/CapacitorNotifications.podspec | 17 + notifications/README.md | 19 + notifications/android/.gitignore | 1 + notifications/android/build.gradle | 56 ++ notifications/android/gradle.properties | 24 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58695 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + notifications/android/gradlew | 183 ++++++ notifications/android/gradlew.bat | 100 +++ notifications/android/proguard-rules.pro | 21 + notifications/android/settings.gradle | 2 + .../android/ExampleInstrumentedTest.java | 26 + .../android/src/main/AndroidManifest.xml | 3 + .../plugins/notifications/Notifications.java | 8 + .../notifications/NotificationsPlugin.java | 22 + .../main/res/layout/bridge_layout_main.xml | 15 + .../com/getcapacitor/ExampleUnitTest.java | 18 + .../ios/Plugin.xcodeproj/project.pbxproj | 569 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Plugin.xcscheme | 77 +++ .../xcschemes/PluginTests.xcscheme | 68 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + notifications/ios/Plugin/Info.plist | 24 + notifications/ios/Plugin/Notifications.swift | 7 + .../ios/Plugin/NotificationsPlugin.h | 10 + .../ios/Plugin/NotificationsPlugin.m | 8 + .../ios/Plugin/NotificationsPlugin.swift | 18 + notifications/ios/PluginTests/Info.plist | 22 + .../NotificationsPluginTests.swift | 25 + notifications/ios/Podfile | 16 + notifications/package.json | 71 +++ notifications/rollup.config.js | 21 + notifications/src/definitions.ts | 9 + notifications/src/index.ts | 18 + notifications/src/web.ts | 14 + notifications/tsconfig.json | 23 + 43 files changed, 1661 insertions(+) create mode 100644 notifications/.eslintignore create mode 100644 notifications/.gitignore create mode 100644 notifications/.prettierignore create mode 100644 notifications/CONTRIBUTING.md create mode 100644 notifications/CapacitorNotifications.podspec create mode 100644 notifications/README.md create mode 100644 notifications/android/.gitignore create mode 100644 notifications/android/build.gradle create mode 100644 notifications/android/gradle.properties create mode 100644 notifications/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 notifications/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 notifications/android/gradlew create mode 100644 notifications/android/gradlew.bat create mode 100644 notifications/android/proguard-rules.pro create mode 100644 notifications/android/settings.gradle create mode 100644 notifications/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java create mode 100644 notifications/android/src/main/AndroidManifest.xml create mode 100644 notifications/android/src/main/java/com/capacitorjs/plugins/notifications/Notifications.java create mode 100644 notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationsPlugin.java create mode 100644 notifications/android/src/main/res/layout/bridge_layout_main.xml create mode 100644 notifications/android/src/test/java/com/getcapacitor/ExampleUnitTest.java create mode 100644 notifications/ios/Plugin.xcodeproj/project.pbxproj create mode 100644 notifications/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 notifications/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme create mode 100644 notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme create mode 100644 notifications/ios/Plugin.xcworkspace/contents.xcworkspacedata create mode 100644 notifications/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 notifications/ios/Plugin/Info.plist create mode 100644 notifications/ios/Plugin/Notifications.swift create mode 100644 notifications/ios/Plugin/NotificationsPlugin.h create mode 100644 notifications/ios/Plugin/NotificationsPlugin.m create mode 100644 notifications/ios/Plugin/NotificationsPlugin.swift create mode 100644 notifications/ios/PluginTests/Info.plist create mode 100644 notifications/ios/PluginTests/NotificationsPluginTests.swift create mode 100644 notifications/ios/Podfile create mode 100644 notifications/package.json create mode 100644 notifications/rollup.config.js create mode 100644 notifications/src/definitions.ts create mode 100644 notifications/src/index.ts create mode 100644 notifications/src/web.ts create mode 100644 notifications/tsconfig.json diff --git a/lerna.json b/lerna.json index 55932b913..f338de466 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,7 @@ "keyboard", "motion", "network", + "notifications", "screen-reader", "share", "status-bar", diff --git a/notifications/.eslintignore b/notifications/.eslintignore new file mode 100644 index 000000000..9d0b71a3c --- /dev/null +++ b/notifications/.eslintignore @@ -0,0 +1,2 @@ +build +dist diff --git a/notifications/.gitignore b/notifications/.gitignore new file mode 100644 index 000000000..70ccbf713 --- /dev/null +++ b/notifications/.gitignore @@ -0,0 +1,61 @@ +# node files +dist +node_modules + +# iOS files +Pods +Podfile.lock +Build +xcuserdata + +# macOS files +.DS_Store + + + +# Based on Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore + +# Built application files +*.apk +*.ap_ + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin +gen +out + +# Gradle files +.gradle +build + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation + +# Android Studio captures folder +captures + +# IntelliJ +*.iml +.idea + +# Keystore files +# Uncomment the following line if you do not want to check your keystore files in. +#*.jks + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild diff --git a/notifications/.prettierignore b/notifications/.prettierignore new file mode 100644 index 000000000..9d0b71a3c --- /dev/null +++ b/notifications/.prettierignore @@ -0,0 +1,2 @@ +build +dist diff --git a/notifications/CONTRIBUTING.md b/notifications/CONTRIBUTING.md new file mode 100644 index 000000000..019c41eef --- /dev/null +++ b/notifications/CONTRIBUTING.md @@ -0,0 +1,42 @@ +# Contributing + +This guide provides instructions for contributing to this Capacitor plugin. + +## Developing + +### Local Setup + +1. Fork and clone the repo. +1. Install the dependencies. + + ```shell + npm install + ``` + +1. Install SwiftLint if you're on macOS. + + ```shell + brew install swiftlint + ``` + +### Scripts + +#### `npm run build` + +Build the plugin web assets and generate plugin API documentation using [`@capacitor/docgen`](https://github.com/ionic-team/capacitor-docgen). + +It will compile the TypeScript code from `src/` into ESM JavaScript in `dist/esm/`. These files are used in apps with bundlers when your plugin is imported. + +Then, Rollup will bundle the code into a single file at `dist/plugin.js`. This file is used in apps without bundlers by including it as a script in `index.html`. + +#### `npm run verify` + +Build and validate the web and native projects. + +This is useful to run in CI to verify that the plugin builds for all platforms. + +#### `npm run lint` / `npm run fmt` + +Check formatting and code quality, autoformat/autofix if possible. + +This template is integrated with ESLint, Prettier, and SwiftLint. Using these tools is completely optional, but the [Capacitor Community](https://github.com/capacitor-community/) strives to have consistent code style and structure for easier cooperation. diff --git a/notifications/CapacitorNotifications.podspec b/notifications/CapacitorNotifications.podspec new file mode 100644 index 000000000..544e7a2e2 --- /dev/null +++ b/notifications/CapacitorNotifications.podspec @@ -0,0 +1,17 @@ +require 'json' + +package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) + +Pod::Spec.new do |s| + s.name = 'CapacitorNotifications' + s.version = package['version'] + s.summary = package['description'] + s.license = package['license'] + s.homepage = package['repository']['url'] + s.author = package['author'] + s.source = { :git => package['repository']['url'], :tag => s.version.to_s } + s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}' + s.ios.deployment_target = '11.0' + s.dependency 'Capacitor' + s.swift_version = '5.1' +end diff --git a/notifications/README.md b/notifications/README.md new file mode 100644 index 000000000..16dd0c4d8 --- /dev/null +++ b/notifications/README.md @@ -0,0 +1,19 @@ +# @capacitor/notifications + +The Notifications API provides access to native push and local notifications. + +## Install + +```bash +npm install @capacitor/notifications +npx cap sync +``` + +## API + + + + + + + diff --git a/notifications/android/.gitignore b/notifications/android/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/notifications/android/.gitignore @@ -0,0 +1 @@ +/build diff --git a/notifications/android/build.gradle b/notifications/android/build.gradle new file mode 100644 index 000000000..034dc47f7 --- /dev/null +++ b/notifications/android/build.gradle @@ -0,0 +1,56 @@ +ext { + junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.12' + androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.1' + androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.2.0' +} + +buildscript { + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:4.0.1' + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 29 + defaultConfig { + minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 21 + targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 29 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +repositories { + google() + jcenter() + mavenCentral() +} + + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation project(':capacitor-android') + testImplementation "junit:junit:$junitVersion" + androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" + androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" +} diff --git a/notifications/android/gradle.properties b/notifications/android/gradle.properties new file mode 100644 index 000000000..0566c221d --- /dev/null +++ b/notifications/android/gradle.properties @@ -0,0 +1,24 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true diff --git a/notifications/android/gradle/wrapper/gradle-wrapper.jar b/notifications/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f3d88b1c2faf2fc91d853cd5d4242b5547257070 GIT binary patch literal 58695 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 literal 0 HcmV?d00001 diff --git a/notifications/android/gradle/wrapper/gradle-wrapper.properties b/notifications/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..4e1cc9db6 --- /dev/null +++ b/notifications/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/notifications/android/gradlew b/notifications/android/gradlew new file mode 100755 index 000000000..2fe81a7d9 --- /dev/null +++ b/notifications/android/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/notifications/android/gradlew.bat b/notifications/android/gradlew.bat new file mode 100644 index 000000000..9618d8d96 --- /dev/null +++ b/notifications/android/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/notifications/android/proguard-rules.pro b/notifications/android/proguard-rules.pro new file mode 100644 index 000000000..f1b424510 --- /dev/null +++ b/notifications/android/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/notifications/android/settings.gradle b/notifications/android/settings.gradle new file mode 100644 index 000000000..1e5b8431f --- /dev/null +++ b/notifications/android/settings.gradle @@ -0,0 +1,2 @@ +include ':capacitor-android' +project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') \ No newline at end of file diff --git a/notifications/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java b/notifications/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java new file mode 100644 index 000000000..58020e16c --- /dev/null +++ b/notifications/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.getcapacitor.android; + +import static org.junit.Assert.*; + +import android.content.Context; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals("com.getcapacitor.android", appContext.getPackageName()); + } +} diff --git a/notifications/android/src/main/AndroidManifest.xml b/notifications/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000..fa66aad11 --- /dev/null +++ b/notifications/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/Notifications.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/Notifications.java new file mode 100644 index 000000000..a6c44a35b --- /dev/null +++ b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/Notifications.java @@ -0,0 +1,8 @@ +package com.capacitorjs.plugins.notifications; + +public class Notifications { + + public String echo(String value) { + return value; + } +} diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationsPlugin.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationsPlugin.java new file mode 100644 index 000000000..6ff0b1b43 --- /dev/null +++ b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationsPlugin.java @@ -0,0 +1,22 @@ +package com.capacitorjs.plugins.notifications; + +import com.getcapacitor.JSObject; +import com.getcapacitor.NativePlugin; +import com.getcapacitor.Plugin; +import com.getcapacitor.PluginCall; +import com.getcapacitor.PluginMethod; + +@NativePlugin(name = "Notifications") +public class NotificationsPlugin extends Plugin { + + private Notifications implementation = new Notifications(); + + @PluginMethod + public void echo(PluginCall call) { + String value = call.getString("value"); + + JSObject ret = new JSObject(); + ret.put("value", implementation.echo(value)); + call.resolve(ret); + } +} diff --git a/notifications/android/src/main/res/layout/bridge_layout_main.xml b/notifications/android/src/main/res/layout/bridge_layout_main.xml new file mode 100644 index 000000000..56fec1546 --- /dev/null +++ b/notifications/android/src/main/res/layout/bridge_layout_main.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/notifications/android/src/test/java/com/getcapacitor/ExampleUnitTest.java b/notifications/android/src/test/java/com/getcapacitor/ExampleUnitTest.java new file mode 100644 index 000000000..a0fed0cfb --- /dev/null +++ b/notifications/android/src/test/java/com/getcapacitor/ExampleUnitTest.java @@ -0,0 +1,18 @@ +package com.getcapacitor; + +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} diff --git a/notifications/ios/Plugin.xcodeproj/project.pbxproj b/notifications/ios/Plugin.xcodeproj/project.pbxproj new file mode 100644 index 000000000..247a3bd49 --- /dev/null +++ b/notifications/ios/Plugin.xcodeproj/project.pbxproj @@ -0,0 +1,569 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */; }; + 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */; }; + 2F98D68224C9AAE500613A4C /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F98D68124C9AAE400613A4C /* Notifications.swift */; }; + 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFF88201F53D600D50D53 /* Plugin.framework */; }; + 50ADFF97201F53D600D50D53 /* NotificationsPluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFF96201F53D600D50D53 /* NotificationsPluginTests.swift */; }; + 50ADFF99201F53D600D50D53 /* NotificationsPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 50ADFF8B201F53D600D50D53 /* NotificationsPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFFA52020D75100D50D53 /* Capacitor.framework */; }; + 50ADFFA82020EE4F00D50D53 /* NotificationsPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFFA72020EE4F00D50D53 /* NotificationsPlugin.m */; }; + 50E1A94820377CB70090CE1A /* NotificationsPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E1A94720377CB70090CE1A /* NotificationsPlugin.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 50ADFF93201F53D600D50D53 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 50ADFF7F201F53D600D50D53 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 50ADFF87201F53D600D50D53; + remoteInfo = Plugin; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 2F98D68124C9AAE400613A4C /* Notifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = ""; }; + 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFF88201F53D600D50D53 /* Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFF8B201F53D600D50D53 /* NotificationsPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NotificationsPlugin.h; sourceTree = ""; }; + 50ADFF8C201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 50ADFF91201F53D600D50D53 /* PluginTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PluginTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFF96201F53D600D50D53 /* NotificationsPluginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsPluginTests.swift; sourceTree = ""; }; + 50ADFF98201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 50ADFFA52020D75100D50D53 /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFFA72020EE4F00D50D53 /* NotificationsPlugin.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationsPlugin.m; sourceTree = ""; }; + 50E1A94720377CB70090CE1A /* NotificationsPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsPlugin.swift; sourceTree = ""; }; + 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.debug.xcconfig"; sourceTree = ""; }; + 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.release.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.release.xcconfig"; sourceTree = ""; }; + 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.debug.xcconfig"; sourceTree = ""; }; + F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.release.xcconfig"; sourceTree = ""; }; + F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PluginTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 50ADFF84201F53D600D50D53 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */, + 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8E201F53D600D50D53 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */, + 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 50ADFF7E201F53D600D50D53 = { + isa = PBXGroup; + children = ( + 50ADFF8A201F53D600D50D53 /* Plugin */, + 50ADFF95201F53D600D50D53 /* PluginTests */, + 50ADFF89201F53D600D50D53 /* Products */, + 8C8E7744173064A9F6D438E3 /* Pods */, + A797B9EFA3DCEFEA1FBB66A9 /* Frameworks */, + ); + sourceTree = ""; + }; + 50ADFF89201F53D600D50D53 /* Products */ = { + isa = PBXGroup; + children = ( + 50ADFF88201F53D600D50D53 /* Plugin.framework */, + 50ADFF91201F53D600D50D53 /* PluginTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 50ADFF8A201F53D600D50D53 /* Plugin */ = { + isa = PBXGroup; + children = ( + 50E1A94720377CB70090CE1A /* NotificationsPlugin.swift */, + 2F98D68124C9AAE400613A4C /* Notifications.swift */, + 50ADFF8B201F53D600D50D53 /* NotificationsPlugin.h */, + 50ADFFA72020EE4F00D50D53 /* NotificationsPlugin.m */, + 50ADFF8C201F53D600D50D53 /* Info.plist */, + ); + path = Plugin; + sourceTree = ""; + }; + 50ADFF95201F53D600D50D53 /* PluginTests */ = { + isa = PBXGroup; + children = ( + 50ADFF96201F53D600D50D53 /* NotificationsPluginTests.swift */, + 50ADFF98201F53D600D50D53 /* Info.plist */, + ); + path = PluginTests; + sourceTree = ""; + }; + 8C8E7744173064A9F6D438E3 /* Pods */ = { + isa = PBXGroup; + children = ( + 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */, + 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */, + 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */, + F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + A797B9EFA3DCEFEA1FBB66A9 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 50ADFFA52020D75100D50D53 /* Capacitor.framework */, + 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */, + F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 50ADFF85201F53D600D50D53 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF99201F53D600D50D53 /* NotificationsPlugin.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 50ADFF87201F53D600D50D53 /* Plugin */ = { + isa = PBXNativeTarget; + buildConfigurationList = 50ADFF9C201F53D600D50D53 /* Build configuration list for PBXNativeTarget "Plugin" */; + buildPhases = ( + AB5B3E54B4E897F32C2279DA /* [CP] Check Pods Manifest.lock */, + 50ADFF83201F53D600D50D53 /* Sources */, + 50ADFF84201F53D600D50D53 /* Frameworks */, + 50ADFF85201F53D600D50D53 /* Headers */, + 50ADFF86201F53D600D50D53 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Plugin; + productName = Plugin; + productReference = 50ADFF88201F53D600D50D53 /* Plugin.framework */; + productType = "com.apple.product-type.framework"; + }; + 50ADFF90201F53D600D50D53 /* PluginTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 50ADFF9F201F53D600D50D53 /* Build configuration list for PBXNativeTarget "PluginTests" */; + buildPhases = ( + 0596884F929ED6F1DE134961 /* [CP] Check Pods Manifest.lock */, + 50ADFF8D201F53D600D50D53 /* Sources */, + 50ADFF8E201F53D600D50D53 /* Frameworks */, + 50ADFF8F201F53D600D50D53 /* Resources */, + 8E97F58B69A94C6503FC9C85 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 50ADFF94201F53D600D50D53 /* PBXTargetDependency */, + ); + name = PluginTests; + productName = PluginTests; + productReference = 50ADFF91201F53D600D50D53 /* PluginTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 50ADFF7F201F53D600D50D53 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1160; + ORGANIZATIONNAME = "Max Lynch"; + TargetAttributes = { + 50ADFF87201F53D600D50D53 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + }; + 50ADFF90201F53D600D50D53 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 50ADFF82201F53D600D50D53 /* Build configuration list for PBXProject "Plugin" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 50ADFF7E201F53D600D50D53; + productRefGroup = 50ADFF89201F53D600D50D53 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 50ADFF87201F53D600D50D53 /* Plugin */, + 50ADFF90201F53D600D50D53 /* PluginTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 50ADFF86201F53D600D50D53 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8F201F53D600D50D53 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 0596884F929ED6F1DE134961 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-PluginTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 8E97F58B69A94C6503FC9C85 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Capacitor/Capacitor.framework", + "${BUILT_PRODUCTS_DIR}/CapacitorCordova/Cordova.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Capacitor.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cordova.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + AB5B3E54B4E897F32C2279DA /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Plugin-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 50ADFF83201F53D600D50D53 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 50E1A94820377CB70090CE1A /* NotificationsPlugin.swift in Sources */, + 2F98D68224C9AAE500613A4C /* Notifications.swift in Sources */, + 50ADFFA82020EE4F00D50D53 /* NotificationsPlugin.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8D201F53D600D50D53 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF97201F53D600D50D53 /* NotificationsPluginTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 50ADFF94201F53D600D50D53 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 50ADFF87201F53D600D50D53 /* Plugin */; + targetProxy = 50ADFF93201F53D600D50D53 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 50ADFF9A201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"${BUILT_PRODUCTS_DIR}/Capacitor\"", + "\"${BUILT_PRODUCTS_DIR}/CapacitorCordova\"", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 50ADFF9B201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"${BUILT_PRODUCTS_DIR}/Capacitor\"", + "\"${BUILT_PRODUCTS_DIR}/CapacitorCordova\"", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 50ADFF9D201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Plugin/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)"; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 50ADFF9E201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Plugin/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)"; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 50ADFFA0201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PluginTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.PluginTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 50ADFFA1201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PluginTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.PluginTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 50ADFF82201F53D600D50D53 /* Build configuration list for PBXProject "Plugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFF9A201F53D600D50D53 /* Debug */, + 50ADFF9B201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 50ADFF9C201F53D600D50D53 /* Build configuration list for PBXNativeTarget "Plugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFF9D201F53D600D50D53 /* Debug */, + 50ADFF9E201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 50ADFF9F201F53D600D50D53 /* Build configuration list for PBXNativeTarget "PluginTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFFA0201F53D600D50D53 /* Debug */, + 50ADFFA1201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 50ADFF7F201F53D600D50D53 /* Project object */; +} diff --git a/notifications/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/notifications/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/notifications/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/notifications/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/notifications/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/notifications/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme b/notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme new file mode 100644 index 000000000..303f2621b --- /dev/null +++ b/notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme b/notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme new file mode 100644 index 000000000..3d8c88d25 --- /dev/null +++ b/notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/notifications/ios/Plugin.xcworkspace/contents.xcworkspacedata b/notifications/ios/Plugin.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..afad624ec --- /dev/null +++ b/notifications/ios/Plugin.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/notifications/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/notifications/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/notifications/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/notifications/ios/Plugin/Info.plist b/notifications/ios/Plugin/Info.plist new file mode 100644 index 000000000..1007fd9dd --- /dev/null +++ b/notifications/ios/Plugin/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/notifications/ios/Plugin/Notifications.swift b/notifications/ios/Plugin/Notifications.swift new file mode 100644 index 000000000..f1f916fee --- /dev/null +++ b/notifications/ios/Plugin/Notifications.swift @@ -0,0 +1,7 @@ +import Foundation + +@objc public class Notifications: NSObject { + @objc public func echo(_ value: String) -> String { + return value + } +} diff --git a/notifications/ios/Plugin/NotificationsPlugin.h b/notifications/ios/Plugin/NotificationsPlugin.h new file mode 100644 index 000000000..f2bd9e0bb --- /dev/null +++ b/notifications/ios/Plugin/NotificationsPlugin.h @@ -0,0 +1,10 @@ +#import + +//! Project version number for Plugin. +FOUNDATION_EXPORT double PluginVersionNumber; + +//! Project version string for Plugin. +FOUNDATION_EXPORT const unsigned char PluginVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + diff --git a/notifications/ios/Plugin/NotificationsPlugin.m b/notifications/ios/Plugin/NotificationsPlugin.m new file mode 100644 index 000000000..341cc2230 --- /dev/null +++ b/notifications/ios/Plugin/NotificationsPlugin.m @@ -0,0 +1,8 @@ +#import +#import + +// Define the plugin using the CAP_PLUGIN Macro, and +// each method the plugin supports using the CAP_PLUGIN_METHOD macro. +CAP_PLUGIN(NotificationsPlugin, "Notifications", + CAP_PLUGIN_METHOD(echo, CAPPluginReturnPromise); +) diff --git a/notifications/ios/Plugin/NotificationsPlugin.swift b/notifications/ios/Plugin/NotificationsPlugin.swift new file mode 100644 index 000000000..3e4804fca --- /dev/null +++ b/notifications/ios/Plugin/NotificationsPlugin.swift @@ -0,0 +1,18 @@ +import Foundation +import Capacitor + +/** + * Please read the Capacitor iOS Plugin Development Guide + * here: https://capacitorjs.com/docs/plugins/ios + */ +@objc(NotificationsPlugin) +public class NotificationsPlugin: CAPPlugin { + private let implementation = Notifications() + + @objc func echo(_ call: CAPPluginCall) { + let value = call.getString("value") ?? "" + call.resolve([ + "value": implementation.echo(value) + ]) + } +} diff --git a/notifications/ios/PluginTests/Info.plist b/notifications/ios/PluginTests/Info.plist new file mode 100644 index 000000000..6c40a6cd0 --- /dev/null +++ b/notifications/ios/PluginTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/notifications/ios/PluginTests/NotificationsPluginTests.swift b/notifications/ios/PluginTests/NotificationsPluginTests.swift new file mode 100644 index 000000000..3ebf9ec34 --- /dev/null +++ b/notifications/ios/PluginTests/NotificationsPluginTests.swift @@ -0,0 +1,25 @@ +import XCTest +@testable import Plugin + +class NotificationsTests: XCTestCase { + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testEcho() { + // This is an example of a functional test case for a plugin. + // Use XCTAssert and related functions to verify your tests produce the correct results. + + let implementation = Notifications() + let value = "Hello, World!" + let result = implementation.echo(value) + + XCTAssertEqual(value, result) + } +} diff --git a/notifications/ios/Podfile b/notifications/ios/Podfile new file mode 100644 index 000000000..350751435 --- /dev/null +++ b/notifications/ios/Podfile @@ -0,0 +1,16 @@ +platform :ios, '11.0' + +def capacitor_pods + # Comment the next line if you're not using Swift and don't want to use dynamic frameworks + use_frameworks! + pod 'Capacitor', :path => '../node_modules/@capacitor/ios' + pod 'CapacitorCordova', :path => '../node_modules/@capacitor/ios' +end + +target 'Plugin' do + capacitor_pods +end + +target 'PluginTests' do + capacitor_pods +end diff --git a/notifications/package.json b/notifications/package.json new file mode 100644 index 000000000..d5f47bf40 --- /dev/null +++ b/notifications/package.json @@ -0,0 +1,71 @@ +{ + "name": "@capacitor/notifications", + "version": "0.0.1", + "description": "The Notifications API provides access to native push and local notifications.", + "main": "dist/plugin.js", + "module": "dist/esm/index.js", + "types": "dist/esm/index.d.ts", + "author": "Ionic ", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/ionic-team/capacitor-plugins.git" + }, + "bugs": { + "url": "https://github.com/ionic-team/capacitor-plugins/issues" + }, + "keywords": [ + "capacitor", + "plugin", + "native" + ], + "scripts": { + "verify": "npm run verify:ios && npm run verify:android && npm run verify:web", + "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin && cd ..", + "verify:android": "cd android && ./gradlew clean build test && cd ..", + "verify:web": "npm run build", + "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint", + "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- autocorrect --format", + "eslint": "eslint . --ext ts", + "prettier": "prettier \"**/*.{css,html,ts,js,java}\"", + "swiftlint": "node-swiftlint", + "docgen": "docgen --api NotificationsPlugin --output-readme README.md --output-json dist/docs.json", + "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.js", + "clean": "rimraf ./dist", + "watch": "tsc --watch", + "prepublishOnly": "npm run build" + }, + "devDependencies": { + "@capacitor/android": "^3.0.0-alpha.6", + "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/docgen": "^0.0.10", + "@capacitor/ios": "^3.0.0-alpha.6", + "@ionic/eslint-config": "^0.3.0", + "@ionic/prettier-config": "^1.0.1", + "@ionic/swiftlint-config": "^1.1.2", + "@rollup/plugin-node-resolve": "^9.0.0", + "eslint": "^7.11.0", + "prettier": "^2.1.2", + "prettier-plugin-java": "^0.8.3", + "rimraf": "^3.0.2", + "rollup": "^2.32.0", + "swiftlint": "^1.0.1", + "typescript": "~4.0.3" + }, + "peerDependencies": { + "@capacitor/core": "^3.0.0-alpha.6" + }, + "prettier": "@ionic/prettier-config", + "swiftlint": "@ionic/swiftlint-config", + "eslintConfig": { + "extends": "@ionic/eslint-config/recommended" + }, + "capacitor": { + "ios": { + "src": "ios" + }, + "android": { + "src": "android" + } + } +} diff --git a/notifications/rollup.config.js b/notifications/rollup.config.js new file mode 100644 index 000000000..d58bfbea1 --- /dev/null +++ b/notifications/rollup.config.js @@ -0,0 +1,21 @@ +import nodeResolve from '@rollup/plugin-node-resolve'; + +export default { + input: 'dist/esm/index.js', + output: { + file: 'dist/plugin.js', + format: 'iife', + name: 'capacitorPlugin', // TODO: change this + globals: { + '@capacitor/core': 'capacitorExports', + }, + sourcemap: true, + }, + plugins: [ + nodeResolve({ + // allowlist of dependencies to bundle in + // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly + resolveOnly: ['lodash'], + }), + ], +}; diff --git a/notifications/src/definitions.ts b/notifications/src/definitions.ts new file mode 100644 index 000000000..e1bdc5862 --- /dev/null +++ b/notifications/src/definitions.ts @@ -0,0 +1,9 @@ +declare module '@capacitor/core' { + interface PluginRegistry { + Notifications: NotificationsPlugin; + } +} + +export interface NotificationsPlugin { + echo(options: { value: string }): Promise<{ value: string }>; +} diff --git a/notifications/src/index.ts b/notifications/src/index.ts new file mode 100644 index 000000000..5486b8ce5 --- /dev/null +++ b/notifications/src/index.ts @@ -0,0 +1,18 @@ +import type { PluginImplementations } from '@capacitor/core'; +import { Plugins, registerPlugin } from '@capacitor/core'; + +import type { NotificationsPlugin } from './definitions'; +import { NotificationsWeb } from './web'; + +const implementations: PluginImplementations = { + android: Plugins.Notifications, + ios: Plugins.Notifications, + web: new NotificationsWeb(), +}; + +const Notifications = registerPlugin( + 'Notifications', + implementations, +).getImplementation(); + +export { Notifications }; diff --git a/notifications/src/web.ts b/notifications/src/web.ts new file mode 100644 index 000000000..0492bdd6a --- /dev/null +++ b/notifications/src/web.ts @@ -0,0 +1,14 @@ +import { WebPlugin } from '@capacitor/core'; + +import type { NotificationsPlugin } from './definitions'; + +export class NotificationsWeb extends WebPlugin implements NotificationsPlugin { + constructor() { + super({ name: 'Notifications' }); + } + + async echo(options: { value: string }): Promise<{ value: string }> { + console.log('ECHO', options); + return options; + } +} diff --git a/notifications/tsconfig.json b/notifications/tsconfig.json new file mode 100644 index 000000000..538e088fd --- /dev/null +++ b/notifications/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "allowUnreachableCode": false, + "declaration": true, + "esModuleInterop": true, + "lib": [ + "dom" + ], + "module": "es2015", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "outDir": "dist/esm", + "pretty": true, + "sourceMap": true, + "strict": true, + "target": "es2017" + }, + "files": [ + "src/index.ts" + ] +} From 59eb65f37c84c063211b08ae5cce3a1020f381a1 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Fri, 6 Nov 2020 15:46:49 -0800 Subject: [PATCH 02/60] port local notifications --- notifications/android/build.gradle | 2 + .../plugins/notifications/DateMatch.java | 213 +++++++++ .../notifications/LocalNotification.java | 383 ++++++++++++++++ .../LocalNotificationAttachment.java | 70 +++ .../LocalNotificationManager.java | 427 ++++++++++++++++++ .../LocalNotificationRestoreReceiver.java | 58 +++ .../LocalNotificationSchedule.java | 159 +++++++ .../notifications/NotificationAction.java | 82 ++++ .../NotificationChannelManager.java | 132 ++++++ .../NotificationDismissReceiver.java | 26 ++ .../notifications/NotificationStorage.java | 146 ++++++ .../notifications/NotificationsPlugin.java | 116 ++++- .../TimedNotificationPublisher.java | 51 +++ 13 files changed, 1857 insertions(+), 8 deletions(-) create mode 100644 notifications/android/src/main/java/com/capacitorjs/plugins/notifications/DateMatch.java create mode 100644 notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotification.java create mode 100644 notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationAttachment.java create mode 100644 notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationManager.java create mode 100644 notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationRestoreReceiver.java create mode 100644 notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationSchedule.java create mode 100644 notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationAction.java create mode 100644 notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationChannelManager.java create mode 100644 notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationDismissReceiver.java create mode 100644 notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationStorage.java create mode 100644 notifications/android/src/main/java/com/capacitorjs/plugins/notifications/TimedNotificationPublisher.java diff --git a/notifications/android/build.gradle b/notifications/android/build.gradle index 034dc47f7..f2f9dac80 100644 --- a/notifications/android/build.gradle +++ b/notifications/android/build.gradle @@ -1,5 +1,6 @@ ext { junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.12' + androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.1.0' androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.1' androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.2.0' } @@ -50,6 +51,7 @@ repositories { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':capacitor-android') + implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" testImplementation "junit:junit:$junitVersion" androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/DateMatch.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/DateMatch.java new file mode 100644 index 000000000..a782be9d3 --- /dev/null +++ b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/DateMatch.java @@ -0,0 +1,213 @@ +package com.capacitorjs.plugins.notifications; + +import java.util.Calendar; +import java.util.Date; + +/** + * Class that holds logic for on triggers + * (Specific time) + */ +public class DateMatch { + + private static final String separator = " "; + + private Integer year; + private Integer month; + private Integer day; + private Integer hour; + private Integer minute; + + // Unit used to save the last used unit for a trigger. + // One of the Calendar constants values + private Integer unit = -1; + + public DateMatch() {} + + public Integer getYear() { + return year; + } + + public void setYear(Integer year) { + this.year = year; + } + + public Integer getMonth() { + return month; + } + + public void setMonth(Integer month) { + this.month = month; + } + + public Integer getDay() { + return day; + } + + public void setDay(Integer day) { + this.day = day; + } + + public Integer getHour() { + return hour; + } + + public void setHour(Integer hour) { + this.hour = hour; + } + + public Integer getMinute() { + return minute; + } + + public void setMinute(Integer minute) { + this.minute = minute; + } + + /** + * Gets a calendar instance pointing to the specified date. + * + * @param date The date to point. + */ + private Calendar buildCalendar(Date date) { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.set(Calendar.MILLISECOND, 0); + cal.set(Calendar.SECOND, 0); + return cal; + } + + /** + * Calculates next trigger date for + * + * @param date base date used to calculate trigger + * @return next trigger timestamp + */ + public long nextTrigger(Date date) { + Calendar current = buildCalendar(date); + Calendar next = buildNextTriggerTime(date); + return postponeTriggerIfNeeded(current, next); + } + + /** + * Postpone trigger if first schedule matches the past + */ + private long postponeTriggerIfNeeded(Calendar current, Calendar next) { + if (next.getTimeInMillis() <= current.getTimeInMillis() && unit != -1) { + Integer incrementUnit = -1; + if (unit == Calendar.YEAR || unit == Calendar.MONTH) { + incrementUnit = Calendar.YEAR; + } else if (unit == Calendar.DAY_OF_MONTH) { + incrementUnit = Calendar.MONTH; + } else if (unit == Calendar.HOUR_OF_DAY) { + incrementUnit = Calendar.DAY_OF_MONTH; + } else if (unit == Calendar.MINUTE) { + incrementUnit = Calendar.HOUR_OF_DAY; + } + + if (incrementUnit != -1) { + next.set(incrementUnit, next.get(incrementUnit) + 1); + } + } + return next.getTimeInMillis(); + } + + private Calendar buildNextTriggerTime(Date date) { + Calendar next = buildCalendar(date); + if (year != null) { + next.set(Calendar.YEAR, year); + if (unit == -1) unit = Calendar.YEAR; + } + if (month != null) { + next.set(Calendar.MONTH, month); + if (unit == -1) unit = Calendar.MONTH; + } + if (day != null) { + next.set(Calendar.DAY_OF_MONTH, day); + if (unit == -1) unit = Calendar.DAY_OF_MONTH; + } + if (hour != null) { + next.set(Calendar.HOUR_OF_DAY, hour); + if (unit == -1) unit = Calendar.HOUR_OF_DAY; + } + if (minute != null) { + next.set(Calendar.MINUTE, minute); + if (unit == -1) unit = Calendar.MINUTE; + } + return next; + } + + @Override + public String toString() { + return "DateMatch{" + "year=" + year + ", month=" + month + ", day=" + day + ", hour=" + hour + ", minute=" + minute + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DateMatch dateMatch = (DateMatch) o; + + if (year != null ? !year.equals(dateMatch.year) : dateMatch.year != null) return false; + if (month != null ? !month.equals(dateMatch.month) : dateMatch.month != null) return false; + if (day != null ? !day.equals(dateMatch.day) : dateMatch.day != null) return false; + if (hour != null ? !hour.equals(dateMatch.hour) : dateMatch.hour != null) return false; + return minute != null ? minute.equals(dateMatch.minute) : dateMatch.minute == null; + } + + @Override + public int hashCode() { + int result = year != null ? year.hashCode() : 0; + result = 31 * result + (month != null ? month.hashCode() : 0); + result = 31 * result + (day != null ? day.hashCode() : 0); + result = 31 * result + (hour != null ? hour.hashCode() : 0); + result = 31 * result + (minute != null ? minute.hashCode() : 0); + return result; + } + + /** + * Transform DateMatch object to CronString + * + * @return + */ + public String toMatchString() { + String matchString = year + separator + month + separator + day + separator + hour + separator + minute + separator + unit; + return matchString.replace("null", "*"); + } + + /** + * Create DateMatch object from stored string + * + * @param matchString + * @return + */ + public static DateMatch fromMatchString(String matchString) { + DateMatch date = new DateMatch(); + String[] split = matchString.split(separator); + if (split != null && split.length == 6) { + date.setYear(getValueFromCronElement(split[0])); + date.setMonth(getValueFromCronElement(split[1])); + date.setDay(getValueFromCronElement(split[2])); + date.setHour(getValueFromCronElement(split[3])); + date.setMinute(getValueFromCronElement(split[4])); + date.setUnit(getValueFromCronElement(split[5])); + } + return date; + } + + public static Integer getValueFromCronElement(String token) { + try { + return Integer.parseInt(token); + } catch (NumberFormatException e) { + return null; + } + } + + public Integer getUnit() { + return unit; + } + + public void setUnit(Integer unit) { + this.unit = unit; + } +} diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotification.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotification.java new file mode 100644 index 000000000..1f688bdd9 --- /dev/null +++ b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotification.java @@ -0,0 +1,383 @@ +package com.capacitorjs.plugins.notifications; + +import android.content.ContentResolver; +import android.content.Context; +import com.getcapacitor.JSArray; +import com.getcapacitor.JSObject; +import com.getcapacitor.Logger; +import com.getcapacitor.PluginCall; +import com.getcapacitor.plugin.util.AssetUtil; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Local notification object mapped from json plugin + */ +public class LocalNotification { + + private String title; + private String body; + private Integer id; + private String sound; + private String smallIcon; + private String iconColor; + private String actionTypeId; + private String group; + private boolean groupSummary; + private boolean ongoing; + private boolean autoCancel; + private JSObject extra; + private List attachments; + private LocalNotificationSchedule schedule; + private String channelId; + + private String source; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public LocalNotificationSchedule getSchedule() { + return schedule; + } + + public void setSchedule(LocalNotificationSchedule schedule) { + this.schedule = schedule; + } + + public String getSound(Context context, int defaultSound) { + String soundPath = null; + int resId = AssetUtil.RESOURCE_ID_ZERO_VALUE; + String name = AssetUtil.getResourceBaseName(sound); + if (name != null) { + resId = AssetUtil.getResourceID(context, name, "raw"); + } + if (resId == AssetUtil.RESOURCE_ID_ZERO_VALUE) { + resId = defaultSound; + } + if (resId != AssetUtil.RESOURCE_ID_ZERO_VALUE) { + soundPath = ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.getPackageName() + "/" + resId; + } + return soundPath; + } + + public void setSound(String sound) { + this.sound = sound; + } + + public void setSmallIcon(String smallIcon) { + this.smallIcon = AssetUtil.getResourceBaseName(smallIcon); + } + + public String getIconColor(String globalColor) { + // use the one defined local before trying for a globally defined color + if (iconColor != null) { + return iconColor; + } + + return globalColor; + } + + public void setIconColor(String iconColor) { + this.iconColor = iconColor; + } + + public List getAttachments() { + return attachments; + } + + public void setAttachments(List attachments) { + this.attachments = attachments; + } + + public String getActionTypeId() { + return actionTypeId; + } + + public void setActionTypeId(String actionTypeId) { + this.actionTypeId = actionTypeId; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public JSObject getExtra() { + return extra; + } + + public void setExtra(JSObject extra) { + this.extra = extra; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public boolean isGroupSummary() { + return groupSummary; + } + + public void setGroupSummary(boolean groupSummary) { + this.groupSummary = groupSummary; + } + + public boolean isOngoing() { + return ongoing; + } + + public void setOngoing(boolean ongoing) { + this.ongoing = ongoing; + } + + public boolean isAutoCancel() { + return autoCancel; + } + + public void setAutoCancel(boolean autoCancel) { + this.autoCancel = autoCancel; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + /** + * Build list of the notifications from remote plugin call + */ + public static List buildNotificationList(PluginCall call) { + JSArray notificationArray = call.getArray("notifications"); + if (notificationArray == null) { + call.error("Must provide notifications array as notifications option"); + return null; + } + List resultLocalNotifications = new ArrayList<>(notificationArray.length()); + List notificationsJson; + try { + notificationsJson = notificationArray.toList(); + } catch (JSONException e) { + call.error("Provided notification format is invalid"); + return null; + } + + for (JSONObject jsonNotification : notificationsJson) { + JSObject notification = null; + try { + notification = JSObject.fromJSONObject(jsonNotification); + } catch (JSONException e) { + call.error("Invalid JSON object sent to NotificationPlugin", e); + return null; + } + + try { + LocalNotification activeLocalNotification = buildNotificationFromJSObject(notification); + resultLocalNotifications.add(activeLocalNotification); + } catch (ParseException e) { + call.error("Invalid date format sent to Notification plugin", e); + return null; + } + } + return resultLocalNotifications; + } + + public static LocalNotification buildNotificationFromJSObject(JSObject jsonObject) throws ParseException { + LocalNotification localNotification = new LocalNotification(); + localNotification.setSource(jsonObject.toString()); + localNotification.setId(jsonObject.getInteger("id")); + localNotification.setBody(jsonObject.getString("body")); + localNotification.setActionTypeId(jsonObject.getString("actionTypeId")); + localNotification.setGroup(jsonObject.getString("group")); + localNotification.setSound(jsonObject.getString("sound")); + localNotification.setTitle(jsonObject.getString("title")); + localNotification.setSmallIcon(jsonObject.getString("smallIcon")); + localNotification.setIconColor(jsonObject.getString("iconColor")); + localNotification.setAttachments(LocalNotificationAttachment.getAttachments(jsonObject)); + localNotification.setGroupSummary(jsonObject.getBoolean("groupSummary", false)); + localNotification.setChannelId(jsonObject.getString("channelId")); + localNotification.setSchedule(new LocalNotificationSchedule(jsonObject)); + localNotification.setExtra(jsonObject.getJSObject("extra")); + localNotification.setOngoing(jsonObject.getBoolean("ongoing", false)); + localNotification.setAutoCancel(jsonObject.getBoolean("autoCancel", true)); + + return localNotification; + } + + public static List getLocalNotificationPendingList(PluginCall call) { + List notifications = null; + try { + notifications = call.getArray("notifications").toList(); + } catch (JSONException e) {} + if (notifications == null || notifications.size() == 0) { + call.error("Must provide notifications array as notifications option"); + return null; + } + List notificationsList = new ArrayList<>(notifications.size()); + for (JSONObject notificationToCancel : notifications) { + try { + notificationsList.add(notificationToCancel.getInt("id")); + } catch (JSONException e) {} + } + return notificationsList; + } + + public static JSObject buildLocalNotificationPendingList(List ids) { + JSObject result = new JSObject(); + JSArray jsArray = new JSArray(); + for (String id : ids) { + JSObject notification = new JSObject(); + notification.put("id", id); + jsArray.put(notification); + } + result.put("notifications", jsArray); + return result; + } + + public int getSmallIcon(Context context, int defaultIcon) { + int resId = AssetUtil.RESOURCE_ID_ZERO_VALUE; + + if (smallIcon != null) { + resId = AssetUtil.getResourceID(context, smallIcon, "drawable"); + } + + if (resId == AssetUtil.RESOURCE_ID_ZERO_VALUE) { + resId = defaultIcon; + } + + return resId; + } + + public boolean isScheduled() { + return ( + this.schedule != null && (this.schedule.getOn() != null || this.schedule.getAt() != null || this.schedule.getEvery() != null) + ); + } + + @Override + public String toString() { + return ( + "LocalNotification{" + + "title='" + + title + + '\'' + + ", body='" + + body + + '\'' + + ", id=" + + id + + ", sound='" + + sound + + '\'' + + ", smallIcon='" + + smallIcon + + '\'' + + ", iconColor='" + + iconColor + + '\'' + + ", actionTypeId='" + + actionTypeId + + '\'' + + ", group='" + + group + + '\'' + + ", extra=" + + extra + + ", attachments=" + + attachments + + ", schedule=" + + schedule + + ", groupSummary=" + + groupSummary + + ", ongoing=" + + ongoing + + ", autoCancel=" + + autoCancel + + '}' + ); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + LocalNotification that = (LocalNotification) o; + + if (title != null ? !title.equals(that.title) : that.title != null) return false; + if (body != null ? !body.equals(that.body) : that.body != null) return false; + if (id != null ? !id.equals(that.id) : that.id != null) return false; + if (sound != null ? !sound.equals(that.sound) : that.sound != null) return false; + if (smallIcon != null ? !smallIcon.equals(that.smallIcon) : that.smallIcon != null) return false; + if (iconColor != null ? !iconColor.equals(that.iconColor) : that.iconColor != null) return false; + if (actionTypeId != null ? !actionTypeId.equals(that.actionTypeId) : that.actionTypeId != null) return false; + if (group != null ? !group.equals(that.group) : that.group != null) return false; + if (extra != null ? !extra.equals(that.extra) : that.extra != null) return false; + if (attachments != null ? !attachments.equals(that.attachments) : that.attachments != null) return false; + if (groupSummary != that.groupSummary) return false; + if (ongoing != that.ongoing) return false; + if (autoCancel != that.autoCancel) return false; + return schedule != null ? schedule.equals(that.schedule) : that.schedule == null; + } + + @Override + public int hashCode() { + int result = title != null ? title.hashCode() : 0; + result = 31 * result + (body != null ? body.hashCode() : 0); + result = 31 * result + (id != null ? id.hashCode() : 0); + result = 31 * result + (sound != null ? sound.hashCode() : 0); + result = 31 * result + (smallIcon != null ? smallIcon.hashCode() : 0); + result = 31 * result + (iconColor != null ? iconColor.hashCode() : 0); + result = 31 * result + (actionTypeId != null ? actionTypeId.hashCode() : 0); + result = 31 * result + (group != null ? group.hashCode() : 0); + result = 31 * result + Boolean.hashCode(groupSummary); + result = 31 * result + Boolean.hashCode(ongoing); + result = 31 * result + Boolean.hashCode(autoCancel); + result = 31 * result + (extra != null ? extra.hashCode() : 0); + result = 31 * result + (attachments != null ? attachments.hashCode() : 0); + result = 31 * result + (schedule != null ? schedule.hashCode() : 0); + return result; + } + + public void setExtraFromString(String extraFromString) { + try { + JSONObject jsonObject = new JSONObject(extraFromString); + this.extra = JSObject.fromJSONObject(jsonObject); + } catch (JSONException e) { + Logger.error(Logger.tags("LN"), "Cannot rebuild extra data", e); + } + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } +} diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationAttachment.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationAttachment.java new file mode 100644 index 000000000..586ab555e --- /dev/null +++ b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationAttachment.java @@ -0,0 +1,70 @@ +package com.capacitorjs.plugins.notifications; + +import com.getcapacitor.JSObject; +import java.util.ArrayList; +import java.util.List; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public class LocalNotificationAttachment { + + private String id; + private String url; + private JSONObject options; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public JSONObject getOptions() { + return options; + } + + public void setOptions(JSONObject options) { + this.options = options; + } + + public static List getAttachments(JSObject notification) { + List attachmentsList = new ArrayList<>(); + JSONArray attachments = null; + try { + attachments = notification.getJSONArray("attachments"); + } catch (Exception e) {} + if (attachments != null) { + for (int i = 0; i < attachments.length(); i++) { + LocalNotificationAttachment newAttachment = new LocalNotificationAttachment(); + JSONObject jsonObject = null; + try { + jsonObject = attachments.getJSONObject(i); + } catch (JSONException e) {} + if (jsonObject != null) { + JSObject jsObject = null; + try { + jsObject = JSObject.fromJSONObject(jsonObject); + } catch (JSONException e) {} + newAttachment.setId(jsObject.getString("id")); + newAttachment.setUrl(jsObject.getString("url")); + try { + newAttachment.setOptions(jsObject.getJSONObject("options")); + } catch (JSONException e) {} + attachmentsList.add(newAttachment); + } + } + } + + return attachmentsList; + } +} diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationManager.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationManager.java new file mode 100644 index 000000000..ee338f3f1 --- /dev/null +++ b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationManager.java @@ -0,0 +1,427 @@ +package com.capacitorjs.plugins.notifications; + +import android.app.Activity; +import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.PendingIntent; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.media.AudioAttributes; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; +import androidx.core.app.RemoteInput; +import com.getcapacitor.CapConfig; +import com.getcapacitor.JSObject; +import com.getcapacitor.Logger; +import com.getcapacitor.PluginCall; +import com.getcapacitor.android.R; +import com.getcapacitor.plugin.util.AssetUtil; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Contains implementations for all notification actions + */ +public class LocalNotificationManager { + + private static final String CONFIG_KEY_PREFIX = "plugins.LocalNotifications."; + private static int defaultSoundID = AssetUtil.RESOURCE_ID_ZERO_VALUE; + private static int defaultSmallIconID = AssetUtil.RESOURCE_ID_ZERO_VALUE; + // Action constants + public static final String NOTIFICATION_INTENT_KEY = "LocalNotificationId"; + public static final String NOTIFICATION_OBJ_INTENT_KEY = "LocalNotficationObject"; + public static final String ACTION_INTENT_KEY = "LocalNotificationUserAction"; + public static final String NOTIFICATION_IS_REMOVABLE_KEY = "LocalNotificationRepeating"; + public static final String REMOTE_INPUT_KEY = "LocalNotificationRemoteInput"; + + public static final String DEFAULT_NOTIFICATION_CHANNEL_ID = "default"; + private static final String DEFAULT_PRESS_ACTION = "tap"; + + private Context context; + private Activity activity; + private NotificationStorage storage; + private CapConfig config; + + public LocalNotificationManager(NotificationStorage notificationStorage, Activity activity, Context context, CapConfig config) { + storage = notificationStorage; + this.activity = activity; + this.context = context; + this.config = config; + } + + /** + * Method extecuted when notification is launched by user from the notification bar. + */ + public JSObject handleNotificationActionPerformed(Intent data, NotificationStorage notificationStorage) { + Logger.debug(Logger.tags("LN"), "LocalNotification received: " + data.getDataString()); + int notificationId = data.getIntExtra(LocalNotificationManager.NOTIFICATION_INTENT_KEY, Integer.MIN_VALUE); + if (notificationId == Integer.MIN_VALUE) { + Logger.debug(Logger.tags("LN"), "Activity started without notification attached"); + return null; + } + boolean isRemovable = data.getBooleanExtra(LocalNotificationManager.NOTIFICATION_IS_REMOVABLE_KEY, true); + if (isRemovable) { + notificationStorage.deleteNotification(Integer.toString(notificationId)); + } + JSObject dataJson = new JSObject(); + + Bundle results = RemoteInput.getResultsFromIntent(data); + if (results != null) { + CharSequence input = results.getCharSequence(LocalNotificationManager.REMOTE_INPUT_KEY); + dataJson.put("inputValue", input.toString()); + } + String menuAction = data.getStringExtra(LocalNotificationManager.ACTION_INTENT_KEY); + + dismissVisibleNotification(notificationId); + + dataJson.put("actionId", menuAction); + JSONObject request = null; + try { + String notificationJsonString = data.getStringExtra(LocalNotificationManager.NOTIFICATION_OBJ_INTENT_KEY); + if (notificationJsonString != null) { + request = new JSObject(notificationJsonString); + } + } catch (JSONException e) {} + dataJson.put("notification", request); + return dataJson; + } + + /** + * Create notification channel + */ + public void createNotificationChannel() { + // Create the NotificationChannel, but only on API 26+ because + // the NotificationChannel class is new and not in the support library + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + CharSequence name = "Default"; + String description = "Default"; + int importance = android.app.NotificationManager.IMPORTANCE_DEFAULT; + NotificationChannel channel = new NotificationChannel(DEFAULT_NOTIFICATION_CHANNEL_ID, name, importance); + channel.setDescription(description); + AudioAttributes audioAttributes = new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_ALARM) + .build(); + Uri soundUri = this.getDefaultSoundUrl(context); + if (soundUri != null) { + channel.setSound(soundUri, audioAttributes); + } + // Register the channel with the system; you can't change the importance + // or other notification behaviors after this + android.app.NotificationManager notificationManager = context.getSystemService(android.app.NotificationManager.class); + notificationManager.createNotificationChannel(channel); + } + } + + @Nullable + public JSONArray schedule(PluginCall call, List localNotifications) { + JSONArray ids = new JSONArray(); + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); + + boolean notificationsEnabled = notificationManager.areNotificationsEnabled(); + if (!notificationsEnabled) { + if (call != null) { + call.error("Notifications not enabled on this device"); + } + return null; + } + for (LocalNotification localNotification : localNotifications) { + Integer id = localNotification.getId(); + if (localNotification.getId() == null) { + if (call != null) { + call.error("LocalNotification missing identifier"); + } + return null; + } + dismissVisibleNotification(id); + cancelTimerForNotification(id); + buildNotification(notificationManager, localNotification, call); + ids.put(id); + } + return ids; + } + + // TODO Progressbar support + // TODO System categories (DO_NOT_DISTURB etc.) + // TODO control visibility by flag Notification.VISIBILITY_PRIVATE + // TODO Group notifications (setGroup, setGroupSummary, setNumber) + // TODO use NotificationCompat.MessagingStyle for latest API + // TODO expandable notification NotificationCompat.MessagingStyle + // TODO media style notification support NotificationCompat.MediaStyle + // TODO custom small/large icons + private void buildNotification(NotificationManagerCompat notificationManager, LocalNotification localNotification, PluginCall call) { + String channelId = DEFAULT_NOTIFICATION_CHANNEL_ID; + if (localNotification.getChannelId() != null) { + channelId = localNotification.getChannelId(); + } + NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this.context, channelId) + .setContentTitle(localNotification.getTitle()) + .setContentText(localNotification.getBody()) + .setAutoCancel(localNotification.isAutoCancel()) + .setOngoing(localNotification.isOngoing()) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setGroupSummary(localNotification.isGroupSummary()); + + // support multiline text + mBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(localNotification.getBody())); + + String sound = localNotification.getSound(context, getDefaultSound(context)); + if (sound != null) { + Uri soundUri = Uri.parse(sound); + // Grant permission to use sound + context.grantUriPermission("com.android.systemui", soundUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + mBuilder.setSound(soundUri); + mBuilder.setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS); + } else { + mBuilder.setDefaults(Notification.DEFAULT_ALL); + } + + String group = localNotification.getGroup(); + if (group != null) { + mBuilder.setGroup(group); + } + + // make sure scheduled time is shown instead of display time + if (localNotification.isScheduled() && localNotification.getSchedule().getAt() != null) { + mBuilder.setWhen(localNotification.getSchedule().getAt().getTime()).setShowWhen(true); + } + + mBuilder.setVisibility(NotificationCompat.VISIBILITY_PRIVATE); + mBuilder.setOnlyAlertOnce(true); + + mBuilder.setSmallIcon(localNotification.getSmallIcon(context, getDefaultSmallIcon(context))); + + String iconColor = localNotification.getIconColor(config.getString(CONFIG_KEY_PREFIX + "iconColor")); + if (iconColor != null) { + try { + mBuilder.setColor(Color.parseColor(iconColor)); + } catch (IllegalArgumentException ex) { + if (call != null) { + call.error("Invalid color provided. Must be a hex string (ex: #ff0000"); + } + return; + } + } + + createActionIntents(localNotification, mBuilder); + // notificationId is a unique int for each localNotification that you must define + Notification buildNotification = mBuilder.build(); + if (localNotification.isScheduled()) { + triggerScheduledNotification(buildNotification, localNotification); + } else { + notificationManager.notify(localNotification.getId(), buildNotification); + } + } + + // Create intents for open/dissmis actions + private void createActionIntents(LocalNotification localNotification, NotificationCompat.Builder mBuilder) { + // Open intent + Intent intent = buildIntent(localNotification, DEFAULT_PRESS_ACTION); + + PendingIntent pendingIntent = PendingIntent.getActivity( + context, + localNotification.getId(), + intent, + PendingIntent.FLAG_CANCEL_CURRENT + ); + mBuilder.setContentIntent(pendingIntent); + + // Build action types + String actionTypeId = localNotification.getActionTypeId(); + if (actionTypeId != null) { + NotificationAction[] actionGroup = storage.getActionGroup(actionTypeId); + for (NotificationAction notificationAction : actionGroup) { + // TODO Add custom icons to actions + Intent actionIntent = buildIntent(localNotification, notificationAction.getId()); + PendingIntent actionPendingIntent = PendingIntent.getActivity( + context, + localNotification.getId() + notificationAction.getId().hashCode(), + actionIntent, + PendingIntent.FLAG_CANCEL_CURRENT + ); + NotificationCompat.Action.Builder actionBuilder = new NotificationCompat.Action.Builder( + R.drawable.ic_transparent, + notificationAction.getTitle(), + actionPendingIntent + ); + if (notificationAction.isInput()) { + RemoteInput remoteInput = new RemoteInput.Builder(REMOTE_INPUT_KEY).setLabel(notificationAction.getTitle()).build(); + actionBuilder.addRemoteInput(remoteInput); + } + mBuilder.addAction(actionBuilder.build()); + } + } + + // Dismiss intent + Intent dissmissIntent = new Intent(context, NotificationDismissReceiver.class); + dissmissIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + dissmissIntent.putExtra(NOTIFICATION_INTENT_KEY, localNotification.getId()); + dissmissIntent.putExtra(ACTION_INTENT_KEY, "dismiss"); + LocalNotificationSchedule schedule = localNotification.getSchedule(); + dissmissIntent.putExtra(NOTIFICATION_IS_REMOVABLE_KEY, schedule == null || schedule.isRemovable()); + PendingIntent deleteIntent = PendingIntent.getBroadcast(context, localNotification.getId(), dissmissIntent, 0); + mBuilder.setDeleteIntent(deleteIntent); + } + + @NonNull + private Intent buildIntent(LocalNotification localNotification, String action) { + Intent intent; + if (activity != null) { + intent = new Intent(context, activity.getClass()); + } else { + String packageName = context.getPackageName(); + intent = context.getPackageManager().getLaunchIntentForPackage(packageName); + } + intent.setAction(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.putExtra(NOTIFICATION_INTENT_KEY, localNotification.getId()); + intent.putExtra(ACTION_INTENT_KEY, action); + intent.putExtra(NOTIFICATION_OBJ_INTENT_KEY, localNotification.getSource()); + LocalNotificationSchedule schedule = localNotification.getSchedule(); + intent.putExtra(NOTIFICATION_IS_REMOVABLE_KEY, schedule == null || schedule.isRemovable()); + return intent; + } + + /** + * Build a notification trigger, such as triggering each N seconds, or + * on a certain date "shape" (such as every first of the month) + */ + // TODO support different AlarmManager.RTC modes depending on priority + private void triggerScheduledNotification(Notification notification, LocalNotification request) { + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + LocalNotificationSchedule schedule = request.getSchedule(); + Intent notificationIntent = new Intent(context, TimedNotificationPublisher.class); + notificationIntent.putExtra(NOTIFICATION_INTENT_KEY, request.getId()); + notificationIntent.putExtra(TimedNotificationPublisher.NOTIFICATION_KEY, notification); + PendingIntent pendingIntent = PendingIntent.getBroadcast( + context, + request.getId(), + notificationIntent, + PendingIntent.FLAG_CANCEL_CURRENT + ); + + // Schedule at specific time (with repeating support) + Date at = schedule.getAt(); + if (at != null) { + if (at.getTime() < new Date().getTime()) { + Logger.error(Logger.tags("LN"), "Scheduled time must be *after* current time", null); + return; + } + if (schedule.isRepeating()) { + long interval = at.getTime() - new Date().getTime(); + alarmManager.setRepeating(AlarmManager.RTC, at.getTime(), interval, pendingIntent); + } else { + alarmManager.setExact(AlarmManager.RTC, at.getTime(), pendingIntent); + } + return; + } + + // Schedule at specific intervals + String every = schedule.getEvery(); + if (every != null) { + Long everyInterval = schedule.getEveryInterval(); + if (everyInterval != null) { + long startTime = new Date().getTime() + everyInterval; + alarmManager.setRepeating(AlarmManager.RTC, startTime, everyInterval, pendingIntent); + } + return; + } + + // Cron like scheduler + DateMatch on = schedule.getOn(); + if (on != null) { + long trigger = on.nextTrigger(new Date()); + notificationIntent.putExtra(TimedNotificationPublisher.CRON_KEY, on.toMatchString()); + pendingIntent = PendingIntent.getBroadcast(context, request.getId(), notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT); + alarmManager.setExact(AlarmManager.RTC, trigger, pendingIntent); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + Logger.debug(Logger.tags("LN"), "notification " + request.getId() + " will next fire at " + sdf.format(new Date(trigger))); + } + } + + public void cancel(PluginCall call) { + List notificationsToCancel = LocalNotification.getLocalNotificationPendingList(call); + if (notificationsToCancel != null) { + for (Integer id : notificationsToCancel) { + dismissVisibleNotification(id); + cancelTimerForNotification(id); + storage.deleteNotification(Integer.toString(id)); + } + } + call.success(); + } + + private void cancelTimerForNotification(Integer notificationId) { + Intent intent = new Intent(context, TimedNotificationPublisher.class); + PendingIntent pi = PendingIntent.getBroadcast(context, notificationId, intent, 0); + if (pi != null) { + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + alarmManager.cancel(pi); + } + } + + private void dismissVisibleNotification(int notificationId) { + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this.context); + notificationManager.cancel(notificationId); + } + + public boolean areNotificationsEnabled() { + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); + return notificationManager.areNotificationsEnabled(); + } + + public Uri getDefaultSoundUrl(Context context) { + int soundId = this.getDefaultSound(context); + if (soundId != AssetUtil.RESOURCE_ID_ZERO_VALUE) { + return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.getPackageName() + "/" + soundId); + } + return null; + } + + private int getDefaultSound(Context context) { + if (defaultSoundID != AssetUtil.RESOURCE_ID_ZERO_VALUE) return defaultSoundID; + + int resId = AssetUtil.RESOURCE_ID_ZERO_VALUE; + String soundConfigResourceName = config.getString(CONFIG_KEY_PREFIX + "sound"); + soundConfigResourceName = AssetUtil.getResourceBaseName(soundConfigResourceName); + + if (soundConfigResourceName != null) { + resId = AssetUtil.getResourceID(context, soundConfigResourceName, "raw"); + } + + defaultSoundID = resId; + return resId; + } + + private int getDefaultSmallIcon(Context context) { + if (defaultSmallIconID != AssetUtil.RESOURCE_ID_ZERO_VALUE) return defaultSmallIconID; + + int resId = AssetUtil.RESOURCE_ID_ZERO_VALUE; + String smallIconConfigResourceName = config.getString(CONFIG_KEY_PREFIX + "smallIcon"); + smallIconConfigResourceName = AssetUtil.getResourceBaseName(smallIconConfigResourceName); + + if (smallIconConfigResourceName != null) { + resId = AssetUtil.getResourceID(context, smallIconConfigResourceName, "drawable"); + } + + if (resId == AssetUtil.RESOURCE_ID_ZERO_VALUE) { + resId = android.R.drawable.ic_dialog_info; + } + + defaultSmallIconID = resId; + return resId; + } +} diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationRestoreReceiver.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationRestoreReceiver.java new file mode 100644 index 000000000..a061829b9 --- /dev/null +++ b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationRestoreReceiver.java @@ -0,0 +1,58 @@ +package com.capacitorjs.plugins.notifications; + +import static android.os.Build.VERSION.SDK_INT; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.UserManager; +import com.getcapacitor.CapConfig; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class LocalNotificationRestoreReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + if (SDK_INT >= 24) { + UserManager um = context.getSystemService(UserManager.class); + if (um == null || !um.isUserUnlocked()) return; + } + + NotificationStorage storage = new NotificationStorage(context); + List ids = storage.getSavedNotificationIds(); + + ArrayList notifications = new ArrayList<>(ids.size()); + ArrayList updatedNotifications = new ArrayList<>(); + for (String id : ids) { + LocalNotification notification = storage.getSavedNotification(id); + if (notification == null) { + continue; + } + + LocalNotificationSchedule schedule = notification.getSchedule(); + if (schedule != null) { + Date at = schedule.getAt(); + if (at != null && at.before(new Date())) { + // modify the scheduled date in order to show notifications that would have been delivered while device was off. + long newDateTime = new Date().getTime() + 15 * 1000; + schedule.setAt(new Date(newDateTime)); + notification.setSchedule(schedule); + updatedNotifications.add(notification); + } + } + + notifications.add(notification); + } + + if (updatedNotifications.size() > 0) { + storage.appendNotifications(updatedNotifications); + } + + CapConfig config = new CapConfig(context.getAssets(), null); + LocalNotificationManager localNotificationManager = new LocalNotificationManager(storage, null, context, config); + + localNotificationManager.schedule(null, notifications); + } +} diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationSchedule.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationSchedule.java new file mode 100644 index 000000000..4872a2203 --- /dev/null +++ b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationSchedule.java @@ -0,0 +1,159 @@ +package com.capacitorjs.plugins.notifications; + +import android.text.format.DateUtils; +import com.getcapacitor.JSObject; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class LocalNotificationSchedule { + + public static String JS_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + + private Date at; + private Boolean repeats; + private String every; + private Integer count; + + private DateMatch on; + + public LocalNotificationSchedule(JSObject jsonNotification) throws ParseException { + JSObject schedule = jsonNotification.getJSObject("schedule"); + if (schedule != null) { + // Every specific unit of time (always constant) + buildEveryElement(schedule); + // Count of units of time from every to repeat on + buildCountElement(schedule); + // At specific moment of time (with repeating option) + buildAtElement(schedule); + // Build on - recurring times. For e.g. every 1st day of the month at 8:30. + buildOnElement(schedule); + } + } + + public LocalNotificationSchedule() {} + + private void buildEveryElement(JSObject schedule) { + // 'year'|'month'|'two-weeks'|'week'|'day'|'hour'|'minute'|'second'; + this.every = schedule.getString("every"); + } + + private void buildCountElement(JSObject schedule) { + this.count = schedule.getInteger("count", 1); + } + + private void buildAtElement(JSObject schedule) throws ParseException { + this.repeats = schedule.getBool("repeats"); + String dateString = schedule.getString("at"); + if (dateString != null) { + SimpleDateFormat sdf = new SimpleDateFormat(JS_DATE_FORMAT); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + this.at = sdf.parse(dateString); + } + } + + private void buildOnElement(JSObject schedule) { + JSObject onJson = schedule.getJSObject("on"); + if (onJson != null) { + this.on = new DateMatch(); + on.setYear(onJson.getInteger("year")); + on.setMonth(onJson.getInteger("month")); + on.setDay(onJson.getInteger("day")); + on.setHour(onJson.getInteger("hour")); + on.setMinute(onJson.getInteger("minute")); + } + } + + public DateMatch getOn() { + return on; + } + + public void setOn(DateMatch on) { + this.on = on; + } + + public Date getAt() { + return at; + } + + public void setAt(Date at) { + this.at = at; + } + + public Boolean getRepeats() { + return repeats; + } + + public void setRepeats(Boolean repeats) { + this.repeats = repeats; + } + + public String getEvery() { + return every; + } + + public void setEvery(String every) { + this.every = every; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public boolean isRepeating() { + return Boolean.TRUE.equals(this.repeats); + } + + public boolean isRemovable() { + if (every == null && on == null) { + if (at != null) { + return !isRepeating(); + } else { + return true; + } + } + return false; + } + + /** + * Get constant long value representing specific interval of time (weeks, days etc.) + */ + public Long getEveryInterval() { + switch (every) { + case "year": + return count * DateUtils.YEAR_IN_MILLIS; + case "month": + // This case is just approximation as months have different number of days + return count * 30 * DateUtils.DAY_IN_MILLIS; + case "two-weeks": + return count * 2 * DateUtils.WEEK_IN_MILLIS; + case "week": + return count * DateUtils.WEEK_IN_MILLIS; + case "day": + return count * DateUtils.DAY_IN_MILLIS; + case "hour": + return count * DateUtils.HOUR_IN_MILLIS; + case "minute": + return count * DateUtils.MINUTE_IN_MILLIS; + case "second": + return count * DateUtils.SECOND_IN_MILLIS; + default: + return null; + } + } + + /** + * Get next trigger time based on calendar and current time + * + * @param currentTime - current time that will be used to calculate next trigger + * @return millisecond trigger + */ + public Long getNextOnSchedule(Date currentTime) { + return this.on.nextTrigger(currentTime); + } +} diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationAction.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationAction.java new file mode 100644 index 000000000..c3d22caf1 --- /dev/null +++ b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationAction.java @@ -0,0 +1,82 @@ +package com.capacitorjs.plugins.notifications; + +import com.getcapacitor.JSArray; +import com.getcapacitor.JSObject; +import com.getcapacitor.Logger; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.json.JSONArray; +import org.json.JSONObject; + +/** + * Action types that will be registered for the notifications + */ +public class NotificationAction { + + private String id; + private String title; + private Boolean input; + + public NotificationAction() {} + + public NotificationAction(String id, String title, Boolean input) { + this.id = id; + this.title = title; + this.input = input; + } + + public static Map buildTypes(JSArray types) { + Map actionTypeMap = new HashMap<>(); + try { + List objects = types.toList(); + for (JSONObject obj : objects) { + JSObject jsObject = JSObject.fromJSONObject(obj); + String actionGroupId = jsObject.getString("id"); + if (actionGroupId == null) { + return null; + } + JSONArray actions = jsObject.getJSONArray("actions"); + if (actions != null) { + NotificationAction[] typesArray = new NotificationAction[actions.length()]; + for (int i = 0; i < typesArray.length; i++) { + NotificationAction notificationAction = new NotificationAction(); + JSObject action = JSObject.fromJSONObject(actions.getJSONObject(i)); + notificationAction.setId(action.getString("id")); + notificationAction.setTitle(action.getString("title")); + notificationAction.setInput(action.getBool("input")); + typesArray[i] = notificationAction; + } + actionTypeMap.put(actionGroupId, typesArray); + } + } + } catch (Exception e) { + Logger.error(Logger.tags("LN"), "Error when building action types", e); + } + return actionTypeMap; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public boolean isInput() { + return Boolean.TRUE.equals(input); + } + + public void setInput(Boolean input) { + this.input = input; + } +} diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationChannelManager.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationChannelManager.java new file mode 100644 index 000000000..457dd893e --- /dev/null +++ b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationChannelManager.java @@ -0,0 +1,132 @@ +package com.capacitorjs.plugins.notifications; + +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.ContentResolver; +import android.content.Context; +import android.graphics.Color; +import android.media.AudioAttributes; +import android.net.Uri; +import androidx.core.app.NotificationCompat; +import com.getcapacitor.JSArray; +import com.getcapacitor.JSObject; +import com.getcapacitor.Logger; +import com.getcapacitor.PluginCall; +import java.util.List; + +public class NotificationChannelManager { + + private Context context; + private NotificationManager notificationManager; + + public NotificationChannelManager(Context context) { + this.context = context; + this.notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + } + + public NotificationChannelManager(Context context, NotificationManager manager) { + this.context = context; + this.notificationManager = manager; + } + + private static String CHANNEL_ID = "id"; + private static String CHANNEL_NAME = "name"; + private static String CHANNEL_DESCRIPTION = "description"; + private static String CHANNEL_IMPORTANCE = "importance"; + private static String CHANNEL_VISIBILITY = "visibility"; + private static String CHANNEL_SOUND = "sound"; + private static String CHANNEL_VIBRATE = "vibration"; + private static String CHANNEL_USE_LIGHTS = "lights"; + private static String CHANNEL_LIGHT_COLOR = "lightColor"; + + public void createChannel(PluginCall call) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + JSObject channel = new JSObject(); + channel.put(CHANNEL_ID, call.getString(CHANNEL_ID)); + channel.put(CHANNEL_NAME, call.getString(CHANNEL_NAME)); + channel.put(CHANNEL_DESCRIPTION, call.getString(CHANNEL_DESCRIPTION, "")); + channel.put(CHANNEL_VISIBILITY, call.getInt(CHANNEL_VISIBILITY, NotificationCompat.VISIBILITY_PUBLIC)); + channel.put(CHANNEL_IMPORTANCE, call.getInt(CHANNEL_IMPORTANCE)); + channel.put(CHANNEL_SOUND, call.getString(CHANNEL_SOUND, null)); + channel.put(CHANNEL_VIBRATE, call.getBoolean(CHANNEL_VIBRATE, false)); + channel.put(CHANNEL_USE_LIGHTS, call.getBoolean(CHANNEL_USE_LIGHTS, false)); + channel.put(CHANNEL_LIGHT_COLOR, call.getString(CHANNEL_LIGHT_COLOR, null)); + createChannel(channel); + call.success(); + } else { + call.unavailable(); + } + } + + public void createChannel(JSObject channel) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + NotificationChannel notificationChannel = new NotificationChannel( + channel.getString(CHANNEL_ID), + channel.getString(CHANNEL_NAME), + channel.getInteger(CHANNEL_IMPORTANCE) + ); + notificationChannel.setDescription(channel.getString(CHANNEL_DESCRIPTION)); + notificationChannel.setLockscreenVisibility(channel.getInteger(CHANNEL_VISIBILITY)); + notificationChannel.enableVibration(channel.getBool(CHANNEL_VIBRATE)); + notificationChannel.enableLights(channel.getBool(CHANNEL_USE_LIGHTS)); + String lightColor = channel.getString(CHANNEL_LIGHT_COLOR); + if (lightColor != null) { + try { + notificationChannel.setLightColor(Color.parseColor(lightColor)); + } catch (IllegalArgumentException ex) { + Logger.error(Logger.tags("NotificationChannel"), "Invalid color provided for light color.", null); + } + } + String sound = channel.getString(CHANNEL_SOUND, null); + if (sound != null && !sound.isEmpty()) { + if (sound.contains(".")) { + sound = sound.substring(0, sound.lastIndexOf('.')); + } + AudioAttributes audioAttributes = new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_NOTIFICATION) + .build(); + Uri soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.getPackageName() + "/raw/" + sound); + notificationChannel.setSound(soundUri, audioAttributes); + } + notificationManager.createNotificationChannel(notificationChannel); + } + } + + public void deleteChannel(PluginCall call) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + String channelId = call.getString("id"); + notificationManager.deleteNotificationChannel(channelId); + call.success(); + } else { + call.unavailable(); + } + } + + public void listChannels(PluginCall call) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + List notificationChannels = notificationManager.getNotificationChannels(); + JSArray channels = new JSArray(); + for (NotificationChannel notificationChannel : notificationChannels) { + JSObject channel = new JSObject(); + channel.put(CHANNEL_ID, notificationChannel.getId()); + channel.put(CHANNEL_NAME, notificationChannel.getName()); + channel.put(CHANNEL_DESCRIPTION, notificationChannel.getDescription()); + channel.put(CHANNEL_IMPORTANCE, notificationChannel.getImportance()); + channel.put(CHANNEL_VISIBILITY, notificationChannel.getLockscreenVisibility()); + channel.put(CHANNEL_SOUND, notificationChannel.getSound()); + channel.put(CHANNEL_VIBRATE, notificationChannel.shouldVibrate()); + channel.put(CHANNEL_USE_LIGHTS, notificationChannel.shouldShowLights()); + channel.put(CHANNEL_LIGHT_COLOR, String.format("#%06X", (0xFFFFFF & notificationChannel.getLightColor()))); + Logger.debug(Logger.tags("NotificationChannel"), "visibility " + notificationChannel.getLockscreenVisibility()); + Logger.debug(Logger.tags("NotificationChannel"), "importance " + notificationChannel.getImportance()); + channels.put(channel); + } + JSObject result = new JSObject(); + result.put("channels", channels); + call.success(result); + } else { + call.unavailable(); + } + } +} diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationDismissReceiver.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationDismissReceiver.java new file mode 100644 index 000000000..ddf335f45 --- /dev/null +++ b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationDismissReceiver.java @@ -0,0 +1,26 @@ +package com.capacitorjs.plugins.notifications; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import com.getcapacitor.Logger; + +/** + * Receiver called when notification is dismissed by user + */ +public class NotificationDismissReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + int intExtra = intent.getIntExtra(LocalNotificationManager.NOTIFICATION_INTENT_KEY, Integer.MIN_VALUE); + if (intExtra == Integer.MIN_VALUE) { + Logger.error(Logger.tags("LN"), "Invalid notification dismiss operation", null); + return; + } + boolean isRemovable = intent.getBooleanExtra(LocalNotificationManager.NOTIFICATION_IS_REMOVABLE_KEY, true); + if (isRemovable) { + NotificationStorage notificationStorage = new NotificationStorage(context); + notificationStorage.deleteNotification(Integer.toString(intExtra)); + } + } +} diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationStorage.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationStorage.java new file mode 100644 index 000000000..e66b8ce7d --- /dev/null +++ b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationStorage.java @@ -0,0 +1,146 @@ +package com.capacitorjs.plugins.notifications; + +import android.content.Context; +import android.content.SharedPreferences; +import com.getcapacitor.JSObject; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Class used to abstract storage for notification data + */ +public class NotificationStorage { + + // Key for private preferences + private static final String NOTIFICATION_STORE_ID = "NOTIFICATION_STORE"; + + // Key used to save action types + private static final String ACTION_TYPES_ID = "ACTION_TYPE_STORE"; + + private static final String ID_KEY = "notificationIds"; + + private Context context; + + public NotificationStorage(Context context) { + this.context = context; + } + + /** + * Persist the id of currently scheduled notification + */ + public void appendNotifications(List localNotifications) { + SharedPreferences storage = getStorage(NOTIFICATION_STORE_ID); + SharedPreferences.Editor editor = storage.edit(); + for (LocalNotification request : localNotifications) { + String key = request.getId().toString(); + editor.putString(key, request.getSource()); + } + editor.apply(); + } + + public List getSavedNotificationIds() { + SharedPreferences storage = getStorage(NOTIFICATION_STORE_ID); + Map all = storage.getAll(); + if (all != null) { + return new ArrayList<>(all.keySet()); + } + return new ArrayList<>(); + } + + public JSObject getSavedNotificationAsJSObject(String key) { + SharedPreferences storage = getStorage(NOTIFICATION_STORE_ID); + String notificationString = storage.getString(key, null); + + if (notificationString == null) { + return null; + } + + JSObject jsNotification; + try { + jsNotification = new JSObject(notificationString); + } catch (JSONException ex) { + return null; + } + + return jsNotification; + } + + public LocalNotification getSavedNotification(String key) { + JSObject jsNotification = getSavedNotificationAsJSObject(key); + if (jsNotification == null) { + return null; + } + + LocalNotification notification; + try { + notification = LocalNotification.buildNotificationFromJSObject(jsNotification); + } catch (ParseException ex) { + return null; + } + + return notification; + } + + /** + * Remove the stored notifications + */ + public void deleteNotification(String id) { + SharedPreferences.Editor editor = getStorage(NOTIFICATION_STORE_ID).edit(); + editor.remove(id); + editor.apply(); + } + + /** + * Shared private preferences for the application. + */ + private SharedPreferences getStorage(String key) { + return context.getSharedPreferences(key, Context.MODE_PRIVATE); + } + + /** + * Writes new action types (actions that being displayed in notification) to storage. + * Write will override previous data. + * + * @param typesMap - map with groupId and actionArray assigned to group + */ + public void writeActionGroup(Map typesMap) { + Set typesIds = typesMap.keySet(); + for (String id : typesIds) { + SharedPreferences.Editor editor = getStorage(ACTION_TYPES_ID + id).edit(); + editor.clear(); + NotificationAction[] notificationActions = typesMap.get(id); + editor.putInt("count", notificationActions.length); + for (int i = 0; i < notificationActions.length; i++) { + editor.putString("id" + i, notificationActions[i].getId()); + editor.putString("title" + i, notificationActions[i].getTitle()); + editor.putBoolean("input" + i, notificationActions[i].isInput()); + } + editor.apply(); + } + } + + /** + * Retrieve array of notification actions per ActionTypeId + * + * @param forId - id of the group + */ + public NotificationAction[] getActionGroup(String forId) { + SharedPreferences storage = getStorage(ACTION_TYPES_ID + forId); + int count = storage.getInt("count", 0); + NotificationAction[] actions = new NotificationAction[count]; + for (int i = 0; i < count; i++) { + String id = storage.getString("id" + i, ""); + String title = storage.getString("title" + i, ""); + Boolean input = storage.getBoolean("input" + i, false); + actions[i] = new NotificationAction(id, title, input); + } + return actions; + } +} diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationsPlugin.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationsPlugin.java index 6ff0b1b43..9523df11c 100644 --- a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationsPlugin.java +++ b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationsPlugin.java @@ -1,22 +1,122 @@ package com.capacitorjs.plugins.notifications; +import android.content.Intent; +import com.getcapacitor.JSArray; import com.getcapacitor.JSObject; -import com.getcapacitor.NativePlugin; import com.getcapacitor.Plugin; import com.getcapacitor.PluginCall; import com.getcapacitor.PluginMethod; +import com.getcapacitor.annotation.CapacitorPlugin; +import java.util.List; +import java.util.Map; +import org.json.JSONArray; -@NativePlugin(name = "Notifications") +@CapacitorPlugin(name = "Notifications") public class NotificationsPlugin extends Plugin { - private Notifications implementation = new Notifications(); + private LocalNotificationManager manager; + private NotificationStorage notificationStorage; + private NotificationChannelManager notificationChannelManager; + @Override + public void load() { + super.load(); + notificationStorage = new NotificationStorage(getContext()); + manager = new LocalNotificationManager(notificationStorage, getActivity(), getContext(), this.bridge.getConfig()); + manager.createNotificationChannel(); + notificationChannelManager = new NotificationChannelManager(getActivity()); + } + + @Override + protected void handleOnNewIntent(Intent data) { + super.handleOnNewIntent(data); + if (!Intent.ACTION_MAIN.equals(data.getAction())) { + return; + } + JSObject dataJson = manager.handleNotificationActionPerformed(data, notificationStorage); + if (dataJson != null) { + notifyListeners("localNotificationActionPerformed", dataJson, true); + } + } + + @Override + protected void handleOnActivityResult(int requestCode, int resultCode, Intent data) { + super.handleOnActivityResult(requestCode, resultCode, data); + this.handleOnNewIntent(data); + } + + /** + * Schedule a notification call from JavaScript + * Creates local notification in system. + */ @PluginMethod - public void echo(PluginCall call) { - String value = call.getString("value"); + public void schedule(PluginCall call) { + List localNotifications = LocalNotification.buildNotificationList(call); + if (localNotifications == null) { + return; + } + JSONArray ids = manager.schedule(call, localNotifications); + if (ids != null) { + notificationStorage.appendNotifications(localNotifications); + JSObject result = new JSObject(); + JSArray jsArray = new JSArray(); + for (int i = 0; i < ids.length(); i++) { + try { + JSObject notification = new JSObject().put("id", ids.getString(i)); + jsArray.put(notification); + } catch (Exception ex) {} + } + result.put("notifications", jsArray); + call.success(result); + } + } - JSObject ret = new JSObject(); - ret.put("value", implementation.echo(value)); - call.resolve(ret); + @PluginMethod + public void requestPermission(PluginCall call) { + JSObject result = new JSObject(); + result.put("granted", true); + call.success(result); + } + + @PluginMethod + public void cancel(PluginCall call) { + manager.cancel(call); + } + + @PluginMethod + public void getPending(PluginCall call) { + List ids = notificationStorage.getSavedNotificationIds(); + JSObject result = LocalNotification.buildLocalNotificationPendingList(ids); + call.success(result); + } + + @PluginMethod + public void registerActionTypes(PluginCall call) { + JSArray types = call.getArray("types"); + Map typesArray = NotificationAction.buildTypes(types); + notificationStorage.writeActionGroup(typesArray); + call.success(); + } + + @PluginMethod + public void areEnabled(PluginCall call) { + JSObject data = new JSObject(); + data.put("value", manager.areNotificationsEnabled()); + call.success(data); + } + + @PluginMethod + public void createChannel(PluginCall call) { + notificationChannelManager.createChannel(call); + } + + @PluginMethod + public void deleteChannel(PluginCall call) { + notificationChannelManager.deleteChannel(call); + } + + @PluginMethod + public void listChannels(PluginCall call) { + notificationChannelManager.listChannels(call); } } diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/TimedNotificationPublisher.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/TimedNotificationPublisher.java new file mode 100644 index 000000000..e79807b3b --- /dev/null +++ b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/TimedNotificationPublisher.java @@ -0,0 +1,51 @@ +package com.capacitorjs.plugins.notifications; + +import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import com.getcapacitor.Logger; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Class used to create notification from timer event + * Note: Class is being registered in Android manifest as broadcast receiver + */ +public class TimedNotificationPublisher extends BroadcastReceiver { + + public static String NOTIFICATION_KEY = "NotificationPublisher.notification"; + public static String CRON_KEY = "NotificationPublisher.cron"; + + /** + * Restore and present notification + */ + @Override + public void onReceive(Context context, Intent intent) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + Notification notification = intent.getParcelableExtra(NOTIFICATION_KEY); + int id = intent.getIntExtra(LocalNotificationManager.NOTIFICATION_INTENT_KEY, Integer.MIN_VALUE); + if (id == Integer.MIN_VALUE) { + Logger.error(Logger.tags("LN"), "No valid id supplied", null); + } + notificationManager.notify(id, notification); + rescheduleNotificationIfNeeded(context, intent, id); + } + + private void rescheduleNotificationIfNeeded(Context context, Intent intent, int id) { + String dateString = intent.getStringExtra(CRON_KEY); + if (dateString != null) { + DateMatch date = DateMatch.fromMatchString(dateString); + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + long trigger = date.nextTrigger(new Date()); + Intent clone = (Intent) intent.clone(); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, id, clone, PendingIntent.FLAG_CANCEL_CURRENT); + alarmManager.setExact(AlarmManager.RTC, trigger, pendingIntent); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + Logger.debug(Logger.tags("LN"), "notification " + id + " will next fire at " + sdf.format(new Date(trigger))); + } + } +} From e22456f2855067f29d4795713ea2210e07f53043 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Fri, 6 Nov 2020 16:01:05 -0800 Subject: [PATCH 03/60] rename to local-notifications --- lerna.json | 2 +- .../.eslintignore | 0 .../.gitignore | 0 .../.prettierignore | 0 .../CONTRIBUTING.md | 0 .../CapacitorLocalNotifications.podspec | 2 +- .../README.md | 6 +-- .../android/.gitignore | 0 .../android/build.gradle | 0 .../android/gradle.properties | 0 .../android/gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../android/gradlew | 0 .../android/gradlew.bat | 0 .../android/proguard-rules.pro | 0 .../android/settings.gradle | 0 .../android/ExampleInstrumentedTest.java | 0 .../android/src/main/AndroidManifest.xml | 2 +- .../localnotifications}/DateMatch.java | 2 +- .../LocalNotification.java | 2 +- .../LocalNotificationAttachment.java | 2 +- .../LocalNotificationManager.java | 2 +- .../LocalNotificationRestoreReceiver.java | 2 +- .../LocalNotificationSchedule.java | 2 +- .../LocalNotificationsPlugin.java | 6 +-- .../NotificationAction.java | 2 +- .../NotificationChannelManager.java | 2 +- .../NotificationDismissReceiver.java | 2 +- .../NotificationStorage.java | 2 +- .../TimedNotificationPublisher.java | 2 +- .../main/res/layout/bridge_layout_main.xml | 0 .../com/getcapacitor/ExampleUnitTest.java | 0 .../ios/Plugin.xcodeproj/project.pbxproj | 40 +++++++++--------- .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/xcschemes/Plugin.xcscheme | 0 .../xcschemes/PluginTests.xcscheme | 0 .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../ios/Plugin/Info.plist | 0 .../ios/Plugin/LocalNotifications.swift | 2 +- .../ios/Plugin/LocalNotificationsPlugin.h | 0 .../ios/Plugin/LocalNotificationsPlugin.m | 2 +- .../ios/Plugin/LocalNotificationsPlugin.swift | 6 +-- .../ios/PluginTests/Info.plist | 0 .../LocalNotificationsPluginTests.swift | 4 +- .../ios/Podfile | 0 .../package.json | 6 +-- .../rollup.config.js | 0 .../src/definitions.ts | 4 +- local-notifications/src/index.ts | 18 ++++++++ .../src/web.ts | 8 ++-- .../tsconfig.json | 0 .../plugins/notifications/Notifications.java | 8 ---- notifications/src/index.ts | 18 -------- 55 files changed, 75 insertions(+), 81 deletions(-) rename {notifications => local-notifications}/.eslintignore (100%) rename {notifications => local-notifications}/.gitignore (100%) rename {notifications => local-notifications}/.prettierignore (100%) rename {notifications => local-notifications}/CONTRIBUTING.md (100%) rename notifications/CapacitorNotifications.podspec => local-notifications/CapacitorLocalNotifications.podspec (92%) rename {notifications => local-notifications}/README.md (60%) rename {notifications => local-notifications}/android/.gitignore (100%) rename {notifications => local-notifications}/android/build.gradle (100%) rename {notifications => local-notifications}/android/gradle.properties (100%) rename {notifications => local-notifications}/android/gradle/wrapper/gradle-wrapper.jar (100%) rename {notifications => local-notifications}/android/gradle/wrapper/gradle-wrapper.properties (100%) rename {notifications => local-notifications}/android/gradlew (100%) rename {notifications => local-notifications}/android/gradlew.bat (100%) rename {notifications => local-notifications}/android/proguard-rules.pro (100%) rename {notifications => local-notifications}/android/settings.gradle (100%) rename {notifications => local-notifications}/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java (100%) rename {notifications => local-notifications}/android/src/main/AndroidManifest.xml (58%) rename {notifications/android/src/main/java/com/capacitorjs/plugins/notifications => local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications}/DateMatch.java (99%) rename {notifications/android/src/main/java/com/capacitorjs/plugins/notifications => local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications}/LocalNotification.java (99%) rename {notifications/android/src/main/java/com/capacitorjs/plugins/notifications => local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications}/LocalNotificationAttachment.java (97%) rename {notifications/android/src/main/java/com/capacitorjs/plugins/notifications => local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications}/LocalNotificationManager.java (99%) rename {notifications/android/src/main/java/com/capacitorjs/plugins/notifications => local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications}/LocalNotificationRestoreReceiver.java (97%) rename {notifications/android/src/main/java/com/capacitorjs/plugins/notifications => local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications}/LocalNotificationSchedule.java (98%) rename notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationsPlugin.java => local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java (96%) rename {notifications/android/src/main/java/com/capacitorjs/plugins/notifications => local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications}/NotificationAction.java (97%) rename {notifications/android/src/main/java/com/capacitorjs/plugins/notifications => local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications}/NotificationChannelManager.java (99%) rename {notifications/android/src/main/java/com/capacitorjs/plugins/notifications => local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications}/NotificationDismissReceiver.java (94%) rename {notifications/android/src/main/java/com/capacitorjs/plugins/notifications => local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications}/NotificationStorage.java (98%) rename {notifications/android/src/main/java/com/capacitorjs/plugins/notifications => local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications}/TimedNotificationPublisher.java (97%) rename {notifications => local-notifications}/android/src/main/res/layout/bridge_layout_main.xml (100%) rename {notifications => local-notifications}/android/src/test/java/com/getcapacitor/ExampleUnitTest.java (100%) rename {notifications => local-notifications}/ios/Plugin.xcodeproj/project.pbxproj (89%) rename {notifications => local-notifications}/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename {notifications => local-notifications}/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {notifications => local-notifications}/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme (100%) rename {notifications => local-notifications}/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme (100%) rename {notifications => local-notifications}/ios/Plugin.xcworkspace/contents.xcworkspacedata (100%) rename {notifications => local-notifications}/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {notifications => local-notifications}/ios/Plugin/Info.plist (100%) rename notifications/ios/Plugin/Notifications.swift => local-notifications/ios/Plugin/LocalNotifications.swift (67%) rename notifications/ios/Plugin/NotificationsPlugin.h => local-notifications/ios/Plugin/LocalNotificationsPlugin.h (100%) rename notifications/ios/Plugin/NotificationsPlugin.m => local-notifications/ios/Plugin/LocalNotificationsPlugin.m (81%) rename notifications/ios/Plugin/NotificationsPlugin.swift => local-notifications/ios/Plugin/LocalNotificationsPlugin.swift (71%) rename {notifications => local-notifications}/ios/PluginTests/Info.plist (100%) rename notifications/ios/PluginTests/NotificationsPluginTests.swift => local-notifications/ios/PluginTests/LocalNotificationsPluginTests.swift (88%) rename {notifications => local-notifications}/ios/Podfile (100%) rename {notifications => local-notifications}/package.json (92%) rename {notifications => local-notifications}/rollup.config.js (100%) rename {notifications => local-notifications}/src/definitions.ts (59%) create mode 100644 local-notifications/src/index.ts rename {notifications => local-notifications}/src/web.ts (50%) rename {notifications => local-notifications}/tsconfig.json (100%) delete mode 100644 notifications/android/src/main/java/com/capacitorjs/plugins/notifications/Notifications.java delete mode 100644 notifications/src/index.ts diff --git a/lerna.json b/lerna.json index f338de466..9aa679bb9 100644 --- a/lerna.json +++ b/lerna.json @@ -9,9 +9,9 @@ "dialog", "haptics", "keyboard", + "local-notifications", "motion", "network", - "notifications", "screen-reader", "share", "status-bar", diff --git a/notifications/.eslintignore b/local-notifications/.eslintignore similarity index 100% rename from notifications/.eslintignore rename to local-notifications/.eslintignore diff --git a/notifications/.gitignore b/local-notifications/.gitignore similarity index 100% rename from notifications/.gitignore rename to local-notifications/.gitignore diff --git a/notifications/.prettierignore b/local-notifications/.prettierignore similarity index 100% rename from notifications/.prettierignore rename to local-notifications/.prettierignore diff --git a/notifications/CONTRIBUTING.md b/local-notifications/CONTRIBUTING.md similarity index 100% rename from notifications/CONTRIBUTING.md rename to local-notifications/CONTRIBUTING.md diff --git a/notifications/CapacitorNotifications.podspec b/local-notifications/CapacitorLocalNotifications.podspec similarity index 92% rename from notifications/CapacitorNotifications.podspec rename to local-notifications/CapacitorLocalNotifications.podspec index 544e7a2e2..3b2a96f33 100644 --- a/notifications/CapacitorNotifications.podspec +++ b/local-notifications/CapacitorLocalNotifications.podspec @@ -3,7 +3,7 @@ require 'json' package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) Pod::Spec.new do |s| - s.name = 'CapacitorNotifications' + s.name = 'CapacitorLocalNotifications' s.version = package['version'] s.summary = package['description'] s.license = package['license'] diff --git a/notifications/README.md b/local-notifications/README.md similarity index 60% rename from notifications/README.md rename to local-notifications/README.md index 16dd0c4d8..3ff54ab24 100644 --- a/notifications/README.md +++ b/local-notifications/README.md @@ -1,11 +1,11 @@ -# @capacitor/notifications +# @capacitor/local-notifications -The Notifications API provides access to native push and local notifications. +The Notifications API provides access to native local notifications. ## Install ```bash -npm install @capacitor/notifications +npm install @capacitor/local-notifications npx cap sync ``` diff --git a/notifications/android/.gitignore b/local-notifications/android/.gitignore similarity index 100% rename from notifications/android/.gitignore rename to local-notifications/android/.gitignore diff --git a/notifications/android/build.gradle b/local-notifications/android/build.gradle similarity index 100% rename from notifications/android/build.gradle rename to local-notifications/android/build.gradle diff --git a/notifications/android/gradle.properties b/local-notifications/android/gradle.properties similarity index 100% rename from notifications/android/gradle.properties rename to local-notifications/android/gradle.properties diff --git a/notifications/android/gradle/wrapper/gradle-wrapper.jar b/local-notifications/android/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from notifications/android/gradle/wrapper/gradle-wrapper.jar rename to local-notifications/android/gradle/wrapper/gradle-wrapper.jar diff --git a/notifications/android/gradle/wrapper/gradle-wrapper.properties b/local-notifications/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from notifications/android/gradle/wrapper/gradle-wrapper.properties rename to local-notifications/android/gradle/wrapper/gradle-wrapper.properties diff --git a/notifications/android/gradlew b/local-notifications/android/gradlew similarity index 100% rename from notifications/android/gradlew rename to local-notifications/android/gradlew diff --git a/notifications/android/gradlew.bat b/local-notifications/android/gradlew.bat similarity index 100% rename from notifications/android/gradlew.bat rename to local-notifications/android/gradlew.bat diff --git a/notifications/android/proguard-rules.pro b/local-notifications/android/proguard-rules.pro similarity index 100% rename from notifications/android/proguard-rules.pro rename to local-notifications/android/proguard-rules.pro diff --git a/notifications/android/settings.gradle b/local-notifications/android/settings.gradle similarity index 100% rename from notifications/android/settings.gradle rename to local-notifications/android/settings.gradle diff --git a/notifications/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java b/local-notifications/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java similarity index 100% rename from notifications/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java rename to local-notifications/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java diff --git a/notifications/android/src/main/AndroidManifest.xml b/local-notifications/android/src/main/AndroidManifest.xml similarity index 58% rename from notifications/android/src/main/AndroidManifest.xml rename to local-notifications/android/src/main/AndroidManifest.xml index fa66aad11..ca4513d0c 100644 --- a/notifications/android/src/main/AndroidManifest.xml +++ b/local-notifications/android/src/main/AndroidManifest.xml @@ -1,3 +1,3 @@ + package="com.capacitorjs.plugins.localnotifications"> diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/DateMatch.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/DateMatch.java similarity index 99% rename from notifications/android/src/main/java/com/capacitorjs/plugins/notifications/DateMatch.java rename to local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/DateMatch.java index a782be9d3..f1a131724 100644 --- a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/DateMatch.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/DateMatch.java @@ -1,4 +1,4 @@ -package com.capacitorjs.plugins.notifications; +package com.capacitorjs.plugins.localnotifications; import java.util.Calendar; import java.util.Date; diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotification.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotification.java similarity index 99% rename from notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotification.java rename to local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotification.java index 1f688bdd9..f8056123b 100644 --- a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotification.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotification.java @@ -1,4 +1,4 @@ -package com.capacitorjs.plugins.notifications; +package com.capacitorjs.plugins.localnotifications; import android.content.ContentResolver; import android.content.Context; diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationAttachment.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationAttachment.java similarity index 97% rename from notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationAttachment.java rename to local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationAttachment.java index 586ab555e..8b4ead25c 100644 --- a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationAttachment.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationAttachment.java @@ -1,4 +1,4 @@ -package com.capacitorjs.plugins.notifications; +package com.capacitorjs.plugins.localnotifications; import com.getcapacitor.JSObject; import java.util.ArrayList; diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationManager.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationManager.java similarity index 99% rename from notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationManager.java rename to local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationManager.java index ee338f3f1..76e3ad689 100644 --- a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationManager.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationManager.java @@ -1,4 +1,4 @@ -package com.capacitorjs.plugins.notifications; +package com.capacitorjs.plugins.localnotifications; import android.app.Activity; import android.app.AlarmManager; diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationRestoreReceiver.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationRestoreReceiver.java similarity index 97% rename from notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationRestoreReceiver.java rename to local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationRestoreReceiver.java index a061829b9..06f62ce1a 100644 --- a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationRestoreReceiver.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationRestoreReceiver.java @@ -1,4 +1,4 @@ -package com.capacitorjs.plugins.notifications; +package com.capacitorjs.plugins.localnotifications; import static android.os.Build.VERSION.SDK_INT; diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationSchedule.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationSchedule.java similarity index 98% rename from notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationSchedule.java rename to local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationSchedule.java index 4872a2203..0843db7cd 100644 --- a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/LocalNotificationSchedule.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationSchedule.java @@ -1,4 +1,4 @@ -package com.capacitorjs.plugins.notifications; +package com.capacitorjs.plugins.localnotifications; import android.text.format.DateUtils; import com.getcapacitor.JSObject; diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationsPlugin.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java similarity index 96% rename from notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationsPlugin.java rename to local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java index 9523df11c..7a7039a38 100644 --- a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationsPlugin.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java @@ -1,4 +1,4 @@ -package com.capacitorjs.plugins.notifications; +package com.capacitorjs.plugins.localnotifications; import android.content.Intent; import com.getcapacitor.JSArray; @@ -11,8 +11,8 @@ import java.util.Map; import org.json.JSONArray; -@CapacitorPlugin(name = "Notifications") -public class NotificationsPlugin extends Plugin { +@CapacitorPlugin(name = "LocalNotifications") +public class LocalNotificationsPlugin extends Plugin { private LocalNotificationManager manager; private NotificationStorage notificationStorage; diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationAction.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationAction.java similarity index 97% rename from notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationAction.java rename to local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationAction.java index c3d22caf1..d4bc88fa5 100644 --- a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationAction.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationAction.java @@ -1,4 +1,4 @@ -package com.capacitorjs.plugins.notifications; +package com.capacitorjs.plugins.localnotifications; import com.getcapacitor.JSArray; import com.getcapacitor.JSObject; diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationChannelManager.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationChannelManager.java similarity index 99% rename from notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationChannelManager.java rename to local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationChannelManager.java index 457dd893e..933fa8e69 100644 --- a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationChannelManager.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationChannelManager.java @@ -1,4 +1,4 @@ -package com.capacitorjs.plugins.notifications; +package com.capacitorjs.plugins.localnotifications; import android.app.NotificationChannel; import android.app.NotificationManager; diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationDismissReceiver.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationDismissReceiver.java similarity index 94% rename from notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationDismissReceiver.java rename to local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationDismissReceiver.java index ddf335f45..c29cbb19a 100644 --- a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationDismissReceiver.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationDismissReceiver.java @@ -1,4 +1,4 @@ -package com.capacitorjs.plugins.notifications; +package com.capacitorjs.plugins.localnotifications; import android.content.BroadcastReceiver; import android.content.Context; diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationStorage.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationStorage.java similarity index 98% rename from notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationStorage.java rename to local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationStorage.java index e66b8ce7d..3399d6563 100644 --- a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/NotificationStorage.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationStorage.java @@ -1,4 +1,4 @@ -package com.capacitorjs.plugins.notifications; +package com.capacitorjs.plugins.localnotifications; import android.content.Context; import android.content.SharedPreferences; diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/TimedNotificationPublisher.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/TimedNotificationPublisher.java similarity index 97% rename from notifications/android/src/main/java/com/capacitorjs/plugins/notifications/TimedNotificationPublisher.java rename to local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/TimedNotificationPublisher.java index e79807b3b..a0b983748 100644 --- a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/TimedNotificationPublisher.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/TimedNotificationPublisher.java @@ -1,4 +1,4 @@ -package com.capacitorjs.plugins.notifications; +package com.capacitorjs.plugins.localnotifications; import android.app.AlarmManager; import android.app.Notification; diff --git a/notifications/android/src/main/res/layout/bridge_layout_main.xml b/local-notifications/android/src/main/res/layout/bridge_layout_main.xml similarity index 100% rename from notifications/android/src/main/res/layout/bridge_layout_main.xml rename to local-notifications/android/src/main/res/layout/bridge_layout_main.xml diff --git a/notifications/android/src/test/java/com/getcapacitor/ExampleUnitTest.java b/local-notifications/android/src/test/java/com/getcapacitor/ExampleUnitTest.java similarity index 100% rename from notifications/android/src/test/java/com/getcapacitor/ExampleUnitTest.java rename to local-notifications/android/src/test/java/com/getcapacitor/ExampleUnitTest.java diff --git a/notifications/ios/Plugin.xcodeproj/project.pbxproj b/local-notifications/ios/Plugin.xcodeproj/project.pbxproj similarity index 89% rename from notifications/ios/Plugin.xcodeproj/project.pbxproj rename to local-notifications/ios/Plugin.xcodeproj/project.pbxproj index 247a3bd49..7337bddf3 100644 --- a/notifications/ios/Plugin.xcodeproj/project.pbxproj +++ b/local-notifications/ios/Plugin.xcodeproj/project.pbxproj @@ -9,13 +9,13 @@ /* Begin PBXBuildFile section */ 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */; }; 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */; }; - 2F98D68224C9AAE500613A4C /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F98D68124C9AAE400613A4C /* Notifications.swift */; }; + 2F98D68224C9AAE500613A4C /* LocalNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F98D68124C9AAE400613A4C /* LocalNotifications.swift */; }; 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFF88201F53D600D50D53 /* Plugin.framework */; }; - 50ADFF97201F53D600D50D53 /* NotificationsPluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFF96201F53D600D50D53 /* NotificationsPluginTests.swift */; }; - 50ADFF99201F53D600D50D53 /* NotificationsPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 50ADFF8B201F53D600D50D53 /* NotificationsPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 50ADFF97201F53D600D50D53 /* LocalNotificationsPluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFF96201F53D600D50D53 /* LocalNotificationsPluginTests.swift */; }; + 50ADFF99201F53D600D50D53 /* LocalNotificationsPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 50ADFF8B201F53D600D50D53 /* LocalNotificationsPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFFA52020D75100D50D53 /* Capacitor.framework */; }; - 50ADFFA82020EE4F00D50D53 /* NotificationsPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFFA72020EE4F00D50D53 /* NotificationsPlugin.m */; }; - 50E1A94820377CB70090CE1A /* NotificationsPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E1A94720377CB70090CE1A /* NotificationsPlugin.swift */; }; + 50ADFFA82020EE4F00D50D53 /* LocalNotificationsPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFFA72020EE4F00D50D53 /* LocalNotificationsPlugin.m */; }; + 50E1A94820377CB70090CE1A /* LocalNotificationsPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E1A94720377CB70090CE1A /* LocalNotificationsPlugin.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -29,17 +29,17 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 2F98D68124C9AAE400613A4C /* Notifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = ""; }; + 2F98D68124C9AAE400613A4C /* LocalNotifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalNotifications.swift; sourceTree = ""; }; 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50ADFF88201F53D600D50D53 /* Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 50ADFF8B201F53D600D50D53 /* NotificationsPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NotificationsPlugin.h; sourceTree = ""; }; + 50ADFF8B201F53D600D50D53 /* LocalNotificationsPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LocalNotificationsPlugin.h; sourceTree = ""; }; 50ADFF8C201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50ADFF91201F53D600D50D53 /* PluginTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PluginTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 50ADFF96201F53D600D50D53 /* NotificationsPluginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsPluginTests.swift; sourceTree = ""; }; + 50ADFF96201F53D600D50D53 /* LocalNotificationsPluginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationsPluginTests.swift; sourceTree = ""; }; 50ADFF98201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50ADFFA52020D75100D50D53 /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 50ADFFA72020EE4F00D50D53 /* NotificationsPlugin.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationsPlugin.m; sourceTree = ""; }; - 50E1A94720377CB70090CE1A /* NotificationsPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsPlugin.swift; sourceTree = ""; }; + 50ADFFA72020EE4F00D50D53 /* LocalNotificationsPlugin.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LocalNotificationsPlugin.m; sourceTree = ""; }; + 50E1A94720377CB70090CE1A /* LocalNotificationsPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalNotificationsPlugin.swift; sourceTree = ""; }; 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.debug.xcconfig"; sourceTree = ""; }; 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.release.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.release.xcconfig"; sourceTree = ""; }; 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.debug.xcconfig"; sourceTree = ""; }; @@ -92,10 +92,10 @@ 50ADFF8A201F53D600D50D53 /* Plugin */ = { isa = PBXGroup; children = ( - 50E1A94720377CB70090CE1A /* NotificationsPlugin.swift */, - 2F98D68124C9AAE400613A4C /* Notifications.swift */, - 50ADFF8B201F53D600D50D53 /* NotificationsPlugin.h */, - 50ADFFA72020EE4F00D50D53 /* NotificationsPlugin.m */, + 50E1A94720377CB70090CE1A /* LocalNotificationsPlugin.swift */, + 2F98D68124C9AAE400613A4C /* LocalNotifications.swift */, + 50ADFF8B201F53D600D50D53 /* LocalNotificationsPlugin.h */, + 50ADFFA72020EE4F00D50D53 /* LocalNotificationsPlugin.m */, 50ADFF8C201F53D600D50D53 /* Info.plist */, ); path = Plugin; @@ -104,7 +104,7 @@ 50ADFF95201F53D600D50D53 /* PluginTests */ = { isa = PBXGroup; children = ( - 50ADFF96201F53D600D50D53 /* NotificationsPluginTests.swift */, + 50ADFF96201F53D600D50D53 /* LocalNotificationsPluginTests.swift */, 50ADFF98201F53D600D50D53 /* Info.plist */, ); path = PluginTests; @@ -138,7 +138,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 50ADFF99201F53D600D50D53 /* NotificationsPlugin.h in Headers */, + 50ADFF99201F53D600D50D53 /* LocalNotificationsPlugin.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -306,9 +306,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 50E1A94820377CB70090CE1A /* NotificationsPlugin.swift in Sources */, - 2F98D68224C9AAE500613A4C /* Notifications.swift in Sources */, - 50ADFFA82020EE4F00D50D53 /* NotificationsPlugin.m in Sources */, + 50E1A94820377CB70090CE1A /* LocalNotificationsPlugin.swift in Sources */, + 2F98D68224C9AAE500613A4C /* LocalNotifications.swift in Sources */, + 50ADFFA82020EE4F00D50D53 /* LocalNotificationsPlugin.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -316,7 +316,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 50ADFF97201F53D600D50D53 /* NotificationsPluginTests.swift in Sources */, + 50ADFF97201F53D600D50D53 /* LocalNotificationsPluginTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/notifications/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/local-notifications/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from notifications/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to local-notifications/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/notifications/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/local-notifications/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from notifications/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to local-notifications/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme b/local-notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme similarity index 100% rename from notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme rename to local-notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme diff --git a/notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme b/local-notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme similarity index 100% rename from notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme rename to local-notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme diff --git a/notifications/ios/Plugin.xcworkspace/contents.xcworkspacedata b/local-notifications/ios/Plugin.xcworkspace/contents.xcworkspacedata similarity index 100% rename from notifications/ios/Plugin.xcworkspace/contents.xcworkspacedata rename to local-notifications/ios/Plugin.xcworkspace/contents.xcworkspacedata diff --git a/notifications/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/local-notifications/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from notifications/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to local-notifications/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/notifications/ios/Plugin/Info.plist b/local-notifications/ios/Plugin/Info.plist similarity index 100% rename from notifications/ios/Plugin/Info.plist rename to local-notifications/ios/Plugin/Info.plist diff --git a/notifications/ios/Plugin/Notifications.swift b/local-notifications/ios/Plugin/LocalNotifications.swift similarity index 67% rename from notifications/ios/Plugin/Notifications.swift rename to local-notifications/ios/Plugin/LocalNotifications.swift index f1f916fee..dac1244e9 100644 --- a/notifications/ios/Plugin/Notifications.swift +++ b/local-notifications/ios/Plugin/LocalNotifications.swift @@ -1,6 +1,6 @@ import Foundation -@objc public class Notifications: NSObject { +@objc public class LocalNotifications: NSObject { @objc public func echo(_ value: String) -> String { return value } diff --git a/notifications/ios/Plugin/NotificationsPlugin.h b/local-notifications/ios/Plugin/LocalNotificationsPlugin.h similarity index 100% rename from notifications/ios/Plugin/NotificationsPlugin.h rename to local-notifications/ios/Plugin/LocalNotificationsPlugin.h diff --git a/notifications/ios/Plugin/NotificationsPlugin.m b/local-notifications/ios/Plugin/LocalNotificationsPlugin.m similarity index 81% rename from notifications/ios/Plugin/NotificationsPlugin.m rename to local-notifications/ios/Plugin/LocalNotificationsPlugin.m index 341cc2230..6d2e44203 100644 --- a/notifications/ios/Plugin/NotificationsPlugin.m +++ b/local-notifications/ios/Plugin/LocalNotificationsPlugin.m @@ -3,6 +3,6 @@ // Define the plugin using the CAP_PLUGIN Macro, and // each method the plugin supports using the CAP_PLUGIN_METHOD macro. -CAP_PLUGIN(NotificationsPlugin, "Notifications", +CAP_PLUGIN(LocalNotificationsPlugin, "LocalNotifications", CAP_PLUGIN_METHOD(echo, CAPPluginReturnPromise); ) diff --git a/notifications/ios/Plugin/NotificationsPlugin.swift b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift similarity index 71% rename from notifications/ios/Plugin/NotificationsPlugin.swift rename to local-notifications/ios/Plugin/LocalNotificationsPlugin.swift index 3e4804fca..60084f081 100644 --- a/notifications/ios/Plugin/NotificationsPlugin.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift @@ -5,9 +5,9 @@ import Capacitor * Please read the Capacitor iOS Plugin Development Guide * here: https://capacitorjs.com/docs/plugins/ios */ -@objc(NotificationsPlugin) -public class NotificationsPlugin: CAPPlugin { - private let implementation = Notifications() +@objc(LocalNotificationsPlugin) +public class LocalNotificationsPlugin: CAPPlugin { + private let implementation = LocalNotifications() @objc func echo(_ call: CAPPluginCall) { let value = call.getString("value") ?? "" diff --git a/notifications/ios/PluginTests/Info.plist b/local-notifications/ios/PluginTests/Info.plist similarity index 100% rename from notifications/ios/PluginTests/Info.plist rename to local-notifications/ios/PluginTests/Info.plist diff --git a/notifications/ios/PluginTests/NotificationsPluginTests.swift b/local-notifications/ios/PluginTests/LocalNotificationsPluginTests.swift similarity index 88% rename from notifications/ios/PluginTests/NotificationsPluginTests.swift rename to local-notifications/ios/PluginTests/LocalNotificationsPluginTests.swift index 3ebf9ec34..cf9b14fc7 100644 --- a/notifications/ios/PluginTests/NotificationsPluginTests.swift +++ b/local-notifications/ios/PluginTests/LocalNotificationsPluginTests.swift @@ -1,7 +1,7 @@ import XCTest @testable import Plugin -class NotificationsTests: XCTestCase { +class LocalNotificationsTests: XCTestCase { override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. @@ -16,7 +16,7 @@ class NotificationsTests: XCTestCase { // This is an example of a functional test case for a plugin. // Use XCTAssert and related functions to verify your tests produce the correct results. - let implementation = Notifications() + let implementation = LocalNotifications() let value = "Hello, World!" let result = implementation.echo(value) diff --git a/notifications/ios/Podfile b/local-notifications/ios/Podfile similarity index 100% rename from notifications/ios/Podfile rename to local-notifications/ios/Podfile diff --git a/notifications/package.json b/local-notifications/package.json similarity index 92% rename from notifications/package.json rename to local-notifications/package.json index d5f47bf40..65c8ccdd2 100644 --- a/notifications/package.json +++ b/local-notifications/package.json @@ -1,7 +1,7 @@ { - "name": "@capacitor/notifications", + "name": "@capacitor/local-notifications", "version": "0.0.1", - "description": "The Notifications API provides access to native push and local notifications.", + "description": "The Notifications API provides access to native local notifications.", "main": "dist/plugin.js", "module": "dist/esm/index.js", "types": "dist/esm/index.d.ts", @@ -29,7 +29,7 @@ "eslint": "eslint . --ext ts", "prettier": "prettier \"**/*.{css,html,ts,js,java}\"", "swiftlint": "node-swiftlint", - "docgen": "docgen --api NotificationsPlugin --output-readme README.md --output-json dist/docs.json", + "docgen": "docgen --api LocalNotificationsPlugin --output-readme README.md --output-json dist/docs.json", "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.js", "clean": "rimraf ./dist", "watch": "tsc --watch", diff --git a/notifications/rollup.config.js b/local-notifications/rollup.config.js similarity index 100% rename from notifications/rollup.config.js rename to local-notifications/rollup.config.js diff --git a/notifications/src/definitions.ts b/local-notifications/src/definitions.ts similarity index 59% rename from notifications/src/definitions.ts rename to local-notifications/src/definitions.ts index e1bdc5862..49971843d 100644 --- a/notifications/src/definitions.ts +++ b/local-notifications/src/definitions.ts @@ -1,9 +1,9 @@ declare module '@capacitor/core' { interface PluginRegistry { - Notifications: NotificationsPlugin; + LocalNotifications: LocalNotificationsPlugin; } } -export interface NotificationsPlugin { +export interface LocalNotificationsPlugin { echo(options: { value: string }): Promise<{ value: string }>; } diff --git a/local-notifications/src/index.ts b/local-notifications/src/index.ts new file mode 100644 index 000000000..a4586b49c --- /dev/null +++ b/local-notifications/src/index.ts @@ -0,0 +1,18 @@ +import type { PluginImplementations } from '@capacitor/core'; +import { Plugins, registerPlugin } from '@capacitor/core'; + +import type { LocalNotificationsPlugin } from './definitions'; +import { LocalNotificationsWeb } from './web'; + +const implementations: PluginImplementations = { + android: Plugins.LocalNotifications, + ios: Plugins.LocalNotifications, + web: new LocalNotificationsWeb(), +}; + +const LocalNotifications = registerPlugin( + 'LocalNotifications', + implementations, +).getImplementation(); + +export { LocalNotifications }; diff --git a/notifications/src/web.ts b/local-notifications/src/web.ts similarity index 50% rename from notifications/src/web.ts rename to local-notifications/src/web.ts index 0492bdd6a..1386b925e 100644 --- a/notifications/src/web.ts +++ b/local-notifications/src/web.ts @@ -1,10 +1,12 @@ import { WebPlugin } from '@capacitor/core'; -import type { NotificationsPlugin } from './definitions'; +import type { LocalNotificationsPlugin } from './definitions'; -export class NotificationsWeb extends WebPlugin implements NotificationsPlugin { +export class LocalNotificationsWeb + extends WebPlugin + implements LocalNotificationsPlugin { constructor() { - super({ name: 'Notifications' }); + super({ name: 'LocalNotifications' }); } async echo(options: { value: string }): Promise<{ value: string }> { diff --git a/notifications/tsconfig.json b/local-notifications/tsconfig.json similarity index 100% rename from notifications/tsconfig.json rename to local-notifications/tsconfig.json diff --git a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/Notifications.java b/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/Notifications.java deleted file mode 100644 index a6c44a35b..000000000 --- a/notifications/android/src/main/java/com/capacitorjs/plugins/notifications/Notifications.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.capacitorjs.plugins.notifications; - -public class Notifications { - - public String echo(String value) { - return value; - } -} diff --git a/notifications/src/index.ts b/notifications/src/index.ts deleted file mode 100644 index 5486b8ce5..000000000 --- a/notifications/src/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; - -import type { NotificationsPlugin } from './definitions'; -import { NotificationsWeb } from './web'; - -const implementations: PluginImplementations = { - android: Plugins.Notifications, - ios: Plugins.Notifications, - web: new NotificationsWeb(), -}; - -const Notifications = registerPlugin( - 'Notifications', - implementations, -).getImplementation(); - -export { Notifications }; From f32ab0e9a1c32e7d3798580bc0a706ac2db405bf Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Fri, 6 Nov 2020 16:14:06 -0800 Subject: [PATCH 04/60] fix deprecated API usage --- .../localnotifications/LocalNotificationsPlugin.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java index 7a7039a38..0429ac9c5 100644 --- a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java @@ -67,7 +67,7 @@ public void schedule(PluginCall call) { } catch (Exception ex) {} } result.put("notifications", jsArray); - call.success(result); + call.resolve(result); } } @@ -75,7 +75,7 @@ public void schedule(PluginCall call) { public void requestPermission(PluginCall call) { JSObject result = new JSObject(); result.put("granted", true); - call.success(result); + call.resolve(result); } @PluginMethod @@ -87,7 +87,7 @@ public void cancel(PluginCall call) { public void getPending(PluginCall call) { List ids = notificationStorage.getSavedNotificationIds(); JSObject result = LocalNotification.buildLocalNotificationPendingList(ids); - call.success(result); + call.resolve(result); } @PluginMethod @@ -95,14 +95,14 @@ public void registerActionTypes(PluginCall call) { JSArray types = call.getArray("types"); Map typesArray = NotificationAction.buildTypes(types); notificationStorage.writeActionGroup(typesArray); - call.success(); + call.resolve(); } @PluginMethod public void areEnabled(PluginCall call) { JSObject data = new JSObject(); data.put("value", manager.areNotificationsEnabled()); - call.success(data); + call.resolve(data); } @PluginMethod From 667ef2298a10cd432c092e5a04fb998a7e8be416 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Fri, 6 Nov 2020 16:29:50 -0800 Subject: [PATCH 05/60] port web --- local-notifications/src/definitions.ts | 187 ++++++++++++++++++++++++- local-notifications/src/index.ts | 34 ++++- local-notifications/src/web.ts | 115 ++++++++++++++- 3 files changed, 329 insertions(+), 7 deletions(-) diff --git a/local-notifications/src/definitions.ts b/local-notifications/src/definitions.ts index 49971843d..fc77c5455 100644 --- a/local-notifications/src/definitions.ts +++ b/local-notifications/src/definitions.ts @@ -1,3 +1,5 @@ +import type { PluginListenerHandle } from '@capacitor/core'; + declare module '@capacitor/core' { interface PluginRegistry { LocalNotifications: LocalNotificationsPlugin; @@ -5,5 +7,188 @@ declare module '@capacitor/core' { } export interface LocalNotificationsPlugin { - echo(options: { value: string }): Promise<{ value: string }>; + schedule(options: { + notifications: LocalNotification[]; + }): Promise; + getPending(): Promise; + registerActionTypes(options: { + types: LocalNotificationActionType[]; + }): Promise; + cancel(pending: LocalNotificationPendingList): Promise; + areEnabled(): Promise; + createChannel(channel: NotificationChannel): Promise; + deleteChannel(channel: NotificationChannel): Promise; + listChannels(): Promise; + // TODO + // requestPermission(): Promise; + addListener( + eventName: 'localNotificationReceived', + listenerFunc: (notification: LocalNotification) => void, + ): PluginListenerHandle; + addListener( + eventName: 'localNotificationActionPerformed', + listenerFunc: ( + notificationAction: LocalNotificationActionPerformed, + ) => void, + ): PluginListenerHandle; + + /** + * Remove all native listeners for this plugin + */ + removeAllListeners(): void; +} + +export interface LocalNotificationRequest { + id: string; +} + +export interface LocalNotificationPendingList { + notifications: LocalNotificationRequest[]; +} + +export type LocalNotificationScheduleResult = LocalNotificationPendingList; + +export interface LocalNotificationActionType { + id: string; + actions?: LocalNotificationAction[]; + iosHiddenPreviewsBodyPlaceholder?: string; // >= iOS 11 only + iosCustomDismissAction?: boolean; + iosAllowInCarPlay?: boolean; + iosHiddenPreviewsShowTitle?: boolean; // >= iOS 11 only + iosHiddenPreviewsShowSubtitle?: boolean; // >= iOS 11 only +} + +export interface LocalNotificationAction { + id: string; + title: string; + requiresAuthentication?: boolean; + foreground?: boolean; + destructive?: boolean; + input?: boolean; + inputButtonTitle?: string; + inputPlaceholder?: string; +} + +export interface LocalNotificationAttachment { + id: string; + url: string; + options?: LocalNotificationAttachmentOptions; +} + +export interface LocalNotificationAttachmentOptions { + iosUNNotificationAttachmentOptionsTypeHintKey?: string; + iosUNNotificationAttachmentOptionsThumbnailHiddenKey?: string; + iosUNNotificationAttachmentOptionsThumbnailClippingRectKey?: string; + iosUNNotificationAttachmentOptionsThumbnailTimeKey?: string; +} + +export interface LocalNotification { + title: string; + body: string; + id: number; + schedule?: LocalNotificationSchedule; + /** + * Name of the audio file with extension. + * On iOS the file should be in the app bundle. + * On Android the file should be on res/raw folder. + * Doesn't work on Android version 26+ (Android O and newer), for + * Recommended format is .wav because is supported by both platforms. + */ + sound?: string; + /** + * Android-only: set a custom statusbar icon. + * If set, it overrides default icon from capacitor.config.json + */ + smallIcon?: string; + /** + * Android only: set the color of the notification icon + */ + iconColor?: string; + attachments?: LocalNotificationAttachment[]; + actionTypeId?: string; + extra?: any; + /** + * iOS only: set the thread identifier for notification grouping + */ + threadIdentifier?: string; + /** + * iOS 12+ only: set the summary argument for notification grouping + */ + summaryArgument?: string; + /** + * Android only: set the group identifier for notification grouping, like + * threadIdentifier on iOS. + */ + group?: string; + /** + * Android only: designate this notification as the summary for a group + * (should be used with the `group` property). + */ + groupSummary?: boolean; + /** + * Android only: set the notification channel on which local notification + * will generate. If channel with the given name does not exist then the + * notification will not fire. If not provided, it will use the default channel. + */ + channelId?: string; + /** + * Android only: set the notification ongoing. + * If set to true the notification can't be swiped away. + */ + ongoing?: boolean; + /** + * Android only: set the notification to be removed automatically when the user clicks on it + */ + autoCancel?: boolean; +} + +export interface LocalNotificationSchedule { + at?: Date; + repeats?: boolean; + every?: + | 'year' + | 'month' + | 'two-weeks' + | 'week' + | 'day' + | 'hour' + | 'minute' + | 'second'; + count?: number; + on?: { + year?: number; + month?: number; + day?: number; + hour?: number; + minute?: number; + }; +} + +export interface LocalNotificationActionPerformed { + actionId: string; + inputValue?: string; + notification: LocalNotification; +} + +export interface LocalNotificationEnabledResult { + /** + * Whether the device has Local Notifications enabled or not + */ + value: boolean; +} + +export interface NotificationChannel { + id: string; + name: string; + description?: string; + sound?: string; + importance: 1 | 2 | 3 | 4 | 5; + visibility?: -1 | 0 | 1; + lights?: boolean; + lightColor?: string; + vibration?: boolean; +} + +export interface NotificationChannelList { + channels: NotificationChannel[]; } diff --git a/local-notifications/src/index.ts b/local-notifications/src/index.ts index a4586b49c..6c9aeae18 100644 --- a/local-notifications/src/index.ts +++ b/local-notifications/src/index.ts @@ -1,7 +1,22 @@ import type { PluginImplementations } from '@capacitor/core'; import { Plugins, registerPlugin } from '@capacitor/core'; -import type { LocalNotificationsPlugin } from './definitions'; +import type { + LocalNotification, + LocalNotificationAction, + LocalNotificationActionPerformed, + LocalNotificationActionType, + LocalNotificationAttachment, + LocalNotificationAttachmentOptions, + LocalNotificationEnabledResult, + LocalNotificationPendingList, + LocalNotificationRequest, + LocalNotificationSchedule, + LocalNotificationScheduleResult, + LocalNotificationsPlugin, + NotificationChannel, + NotificationChannelList, +} from './definitions'; import { LocalNotificationsWeb } from './web'; const implementations: PluginImplementations = { @@ -15,4 +30,19 @@ const LocalNotifications = registerPlugin( implementations, ).getImplementation(); -export { LocalNotifications }; +export { + LocalNotification, + LocalNotificationAction, + LocalNotificationActionPerformed, + LocalNotificationActionType, + LocalNotificationAttachment, + LocalNotificationAttachmentOptions, + LocalNotificationEnabledResult, + LocalNotificationPendingList, + LocalNotificationRequest, + LocalNotificationSchedule, + LocalNotificationScheduleResult, + LocalNotifications, + NotificationChannel, + NotificationChannelList, +}; diff --git a/local-notifications/src/web.ts b/local-notifications/src/web.ts index 1386b925e..090587409 100644 --- a/local-notifications/src/web.ts +++ b/local-notifications/src/web.ts @@ -1,16 +1,123 @@ import { WebPlugin } from '@capacitor/core'; -import type { LocalNotificationsPlugin } from './definitions'; +import type { + LocalNotificationsPlugin, + LocalNotificationEnabledResult, + LocalNotificationPendingList, + LocalNotificationActionType, + LocalNotification, + LocalNotificationScheduleResult, + NotificationChannel, + NotificationChannelList, +} from './definitions'; export class LocalNotificationsWeb extends WebPlugin implements LocalNotificationsPlugin { + private pending: LocalNotification[] = []; + constructor() { super({ name: 'LocalNotifications' }); } - async echo(options: { value: string }): Promise<{ value: string }> { - console.log('ECHO', options); - return options; + createChannel(channel: NotificationChannel): Promise { + throw new Error('Feature not available in the browser. ' + channel.id); + } + + deleteChannel(channel: NotificationChannel): Promise { + throw new Error('Feature not available in the browser. ' + channel.id); + } + + listChannels(): Promise { + throw new Error('Feature not available in the browser'); + } + + sendPending(): void { + const toRemove: LocalNotification[] = []; + const now = +new Date(); + this.pending.forEach(localNotification => { + if (localNotification.schedule?.at) { + if (+localNotification.schedule.at <= now) { + this.buildNotification(localNotification); + toRemove.push(localNotification); + } + } + }); + console.log('Sent pending, removing', toRemove); + + this.pending = this.pending.filter( + localNotification => !toRemove.find(ln => ln === localNotification), + ); + } + + // TODO + sendNotification( + localNotification: LocalNotification, + ): any /* Notification */ { + const l = localNotification; + + if (localNotification.schedule?.at) { + const diff = +localNotification.schedule.at - +new Date(); + this.pending.push(l); + setTimeout(() => { + this.sendPending(); + }, diff); + return; + } + + this.buildNotification(localNotification); + } + + buildNotification(localNotification: LocalNotification): Notification { + const l = localNotification; + return new Notification(l.title, { + body: l.body, + }); + } + + schedule(options: { + notifications: LocalNotification[]; + }): Promise { + const notifications: Notification[] = []; + options.notifications.forEach(notification => { + notifications.push(this.sendNotification(notification)); + }); + + return Promise.resolve({ + notifications: options.notifications.map(notification => { + return { id: '' + notification.id }; + }), + }); + } + + getPending(): Promise { + return Promise.resolve({ + notifications: this.pending.map(localNotification => { + return { + id: '' + localNotification.id, + }; + }), + }); + } + + registerActionTypes(_options: { + types: LocalNotificationActionType[]; + }): Promise { + throw new Error('Method not implemented.'); + } + + cancel(pending: LocalNotificationPendingList): Promise { + console.log('Cancel these', pending); + this.pending = this.pending.filter( + localNotification => + !pending.notifications.find(ln => ln.id === '' + localNotification.id), + ); + return Promise.resolve(); + } + + areEnabled(): Promise { + return Promise.resolve({ + value: Notification.permission === 'granted', + }); } } From 27c1afffa3a2718a75b2468900a31fd9355db6cf Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Fri, 6 Nov 2020 17:17:49 -0800 Subject: [PATCH 06/60] implement new permissions --- local-notifications/src/definitions.ts | 23 +++++++++++++--- local-notifications/src/index.ts | 2 ++ local-notifications/src/web.ts | 37 +++++++++++++++++++++++--- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/local-notifications/src/definitions.ts b/local-notifications/src/definitions.ts index fc77c5455..4d8485a03 100644 --- a/local-notifications/src/definitions.ts +++ b/local-notifications/src/definitions.ts @@ -1,4 +1,4 @@ -import type { PluginListenerHandle } from '@capacitor/core'; +import type { PermissionState, PluginListenerHandle } from '@capacitor/core'; declare module '@capacitor/core' { interface PluginRegistry { @@ -6,6 +6,10 @@ declare module '@capacitor/core' { } } +export interface LocalNotificationsPermissionStatus { + display: PermissionState; +} + export interface LocalNotificationsPlugin { schedule(options: { notifications: LocalNotification[]; @@ -19,8 +23,21 @@ export interface LocalNotificationsPlugin { createChannel(channel: NotificationChannel): Promise; deleteChannel(channel: NotificationChannel): Promise; listChannels(): Promise; - // TODO - // requestPermission(): Promise; + + /** + * Check notification permissions + * + * @since 1.0.0 + */ + checkPermissions(): Promise; + + /** + * Request location permissions + * + * @since 1.0.0 + */ + requestPermissions(): Promise; + addListener( eventName: 'localNotificationReceived', listenerFunc: (notification: LocalNotification) => void, diff --git a/local-notifications/src/index.ts b/local-notifications/src/index.ts index 6c9aeae18..643390c70 100644 --- a/local-notifications/src/index.ts +++ b/local-notifications/src/index.ts @@ -14,6 +14,7 @@ import type { LocalNotificationSchedule, LocalNotificationScheduleResult, LocalNotificationsPlugin, + LocalNotificationsPermissionStatus, NotificationChannel, NotificationChannelList, } from './definitions'; @@ -43,6 +44,7 @@ export { LocalNotificationSchedule, LocalNotificationScheduleResult, LocalNotifications, + LocalNotificationsPermissionStatus, NotificationChannel, NotificationChannelList, }; diff --git a/local-notifications/src/web.ts b/local-notifications/src/web.ts index 090587409..bc8ff8ae7 100644 --- a/local-notifications/src/web.ts +++ b/local-notifications/src/web.ts @@ -1,12 +1,14 @@ +import type { PermissionState } from '@capacitor/core'; import { WebPlugin } from '@capacitor/core'; import type { - LocalNotificationsPlugin, + LocalNotification, + LocalNotificationActionType, LocalNotificationEnabledResult, LocalNotificationPendingList, - LocalNotificationActionType, - LocalNotification, LocalNotificationScheduleResult, + LocalNotificationsPermissionStatus, + LocalNotificationsPlugin, NotificationChannel, NotificationChannelList, } from './definitions'; @@ -120,4 +122,33 @@ export class LocalNotificationsWeb value: Notification.permission === 'granted', }); } + + async requestPermissions(): Promise { + const display = this.transformNotificationPermission( + await Notification.requestPermission(), + ); + + return { display }; + } + + async checkPermissions(): Promise { + const display = this.transformNotificationPermission( + Notification.permission, + ); + + return { display }; + } + + protected transformNotificationPermission( + permission: NotificationPermission, + ): PermissionState { + switch (permission) { + case 'granted': + return 'granted'; + case 'denied': + return 'denied'; + default: + return 'prompt'; + } + } } From bd6e3f62dcba5797db2f7f11cc10e0e40e51594f Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 9 Nov 2020 10:56:33 -0800 Subject: [PATCH 07/60] rewrite web --- local-notifications/src/web.ts | 152 +++++++++++++++------------------ 1 file changed, 69 insertions(+), 83 deletions(-) diff --git a/local-notifications/src/web.ts b/local-notifications/src/web.ts index bc8ff8ae7..f88b5ee7a 100644 --- a/local-notifications/src/web.ts +++ b/local-notifications/src/web.ts @@ -3,124 +3,74 @@ import { WebPlugin } from '@capacitor/core'; import type { LocalNotification, - LocalNotificationActionType, LocalNotificationEnabledResult, LocalNotificationPendingList, LocalNotificationScheduleResult, LocalNotificationsPermissionStatus, LocalNotificationsPlugin, - NotificationChannel, NotificationChannelList, } from './definitions'; export class LocalNotificationsWeb extends WebPlugin implements LocalNotificationsPlugin { - private pending: LocalNotification[] = []; + protected pending: LocalNotification[] = []; constructor() { super({ name: 'LocalNotifications' }); } - createChannel(channel: NotificationChannel): Promise { - throw new Error('Feature not available in the browser. ' + channel.id); + async createChannel(): Promise { + throw this.unavailable('Feature not available for web.'); } - deleteChannel(channel: NotificationChannel): Promise { - throw new Error('Feature not available in the browser. ' + channel.id); + async deleteChannel(): Promise { + throw this.unavailable('Feature not available for web.'); } - listChannels(): Promise { - throw new Error('Feature not available in the browser'); + async listChannels(): Promise { + throw this.unavailable('Feature not available for web.'); } - sendPending(): void { - const toRemove: LocalNotification[] = []; - const now = +new Date(); - this.pending.forEach(localNotification => { - if (localNotification.schedule?.at) { - if (+localNotification.schedule.at <= now) { - this.buildNotification(localNotification); - toRemove.push(localNotification); - } - } - }); - console.log('Sent pending, removing', toRemove); - - this.pending = this.pending.filter( - localNotification => !toRemove.find(ln => ln === localNotification), - ); - } - - // TODO - sendNotification( - localNotification: LocalNotification, - ): any /* Notification */ { - const l = localNotification; - - if (localNotification.schedule?.at) { - const diff = +localNotification.schedule.at - +new Date(); - this.pending.push(l); - setTimeout(() => { - this.sendPending(); - }, diff); - return; - } - - this.buildNotification(localNotification); - } - - buildNotification(localNotification: LocalNotification): Notification { - const l = localNotification; - return new Notification(l.title, { - body: l.body, - }); - } - - schedule(options: { + async schedule(options: { notifications: LocalNotification[]; }): Promise { - const notifications: Notification[] = []; - options.notifications.forEach(notification => { - notifications.push(this.sendNotification(notification)); - }); + for (const notification of options.notifications) { + this.sendNotification(notification); + } - return Promise.resolve({ - notifications: options.notifications.map(notification => { - return { id: '' + notification.id }; - }), - }); + return { + notifications: options.notifications.map(notification => ({ + id: notification.id.toString(), + })), + }; } - getPending(): Promise { - return Promise.resolve({ - notifications: this.pending.map(localNotification => { - return { - id: '' + localNotification.id, - }; - }), - }); + async getPending(): Promise { + return { + notifications: this.pending.map(notification => ({ + id: notification.id.toString(), + })), + }; } - registerActionTypes(_options: { - types: LocalNotificationActionType[]; - }): Promise { - throw new Error('Method not implemented.'); + async registerActionTypes(): Promise { + throw this.unavailable('Feature not available for web.'); } - cancel(pending: LocalNotificationPendingList): Promise { - console.log('Cancel these', pending); + async cancel(pending: LocalNotificationPendingList): Promise { this.pending = this.pending.filter( - localNotification => - !pending.notifications.find(ln => ln.id === '' + localNotification.id), + notification => + !pending.notifications.find(n => n.id === notification.id.toString()), ); - return Promise.resolve(); } - areEnabled(): Promise { - return Promise.resolve({ - value: Notification.permission === 'granted', - }); + async areEnabled(): Promise { + const { display } = await this.checkPermissions(); + + return { + value: display === 'granted', + }; } async requestPermissions(): Promise { @@ -151,4 +101,40 @@ export class LocalNotificationsWeb return 'prompt'; } } + + protected sendPending(): void { + const toRemove: LocalNotification[] = []; + const now = new Date().getTime(); + + for (const notification of this.pending) { + if ( + notification.schedule?.at && + notification.schedule.at.getTime() <= now + ) { + this.buildNotification(notification); + toRemove.push(notification); + } + } + + this.pending = this.pending.filter( + notification => !toRemove.find(n => n === notification), + ); + } + + protected sendNotification(notification: LocalNotification): void { + if (notification.schedule?.at) { + const diff = notification.schedule.at.getTime() - new Date().getTime(); + + this.pending.push(notification); + setTimeout(() => { + this.sendPending(); + }, diff); + } + } + + protected buildNotification(notification: LocalNotification): Notification { + return new Notification(notification.title, { + body: notification.body, + }); + } } From 79a711b13a5306b3f72c61c93a6ac819ee8bff18 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 9 Nov 2020 14:44:56 -0800 Subject: [PATCH 08/60] add required xml stuffs --- .../android/src/main/AndroidManifest.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/local-notifications/android/src/main/AndroidManifest.xml b/local-notifications/android/src/main/AndroidManifest.xml index ca4513d0c..33c1430fc 100644 --- a/local-notifications/android/src/main/AndroidManifest.xml +++ b/local-notifications/android/src/main/AndroidManifest.xml @@ -1,3 +1,18 @@ + + + + + + + + + + + + From 6b1f35e01319bdd1b999dab390e3a29a92a89225 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 9 Nov 2020 15:02:06 -0800 Subject: [PATCH 09/60] replace deprecated plugin call methods --- .../plugins/localnotifications/LocalNotification.java | 10 +++++----- .../localnotifications/LocalNotificationManager.java | 8 ++++---- .../localnotifications/NotificationChannelManager.java | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotification.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotification.java index f8056123b..5582392d7 100644 --- a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotification.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotification.java @@ -175,7 +175,7 @@ public void setChannelId(String channelId) { public static List buildNotificationList(PluginCall call) { JSArray notificationArray = call.getArray("notifications"); if (notificationArray == null) { - call.error("Must provide notifications array as notifications option"); + call.reject("Must provide notifications array as notifications option"); return null; } List resultLocalNotifications = new ArrayList<>(notificationArray.length()); @@ -183,7 +183,7 @@ public static List buildNotificationList(PluginCall call) { try { notificationsJson = notificationArray.toList(); } catch (JSONException e) { - call.error("Provided notification format is invalid"); + call.reject("Provided notification format is invalid"); return null; } @@ -192,7 +192,7 @@ public static List buildNotificationList(PluginCall call) { try { notification = JSObject.fromJSONObject(jsonNotification); } catch (JSONException e) { - call.error("Invalid JSON object sent to NotificationPlugin", e); + call.reject("Invalid JSON object sent to NotificationPlugin", e); return null; } @@ -200,7 +200,7 @@ public static List buildNotificationList(PluginCall call) { LocalNotification activeLocalNotification = buildNotificationFromJSObject(notification); resultLocalNotifications.add(activeLocalNotification); } catch (ParseException e) { - call.error("Invalid date format sent to Notification plugin", e); + call.reject("Invalid date format sent to Notification plugin", e); return null; } } @@ -235,7 +235,7 @@ public static List getLocalNotificationPendingList(PluginCall call) { notifications = call.getArray("notifications").toList(); } catch (JSONException e) {} if (notifications == null || notifications.size() == 0) { - call.error("Must provide notifications array as notifications option"); + call.reject("Must provide notifications array as notifications option"); return null; } List notificationsList = new ArrayList<>(notifications.size()); diff --git a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationManager.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationManager.java index 76e3ad689..29e52adc3 100644 --- a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationManager.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationManager.java @@ -133,7 +133,7 @@ public JSONArray schedule(PluginCall call, List localNotifica boolean notificationsEnabled = notificationManager.areNotificationsEnabled(); if (!notificationsEnabled) { if (call != null) { - call.error("Notifications not enabled on this device"); + call.reject("Notifications not enabled on this device"); } return null; } @@ -141,7 +141,7 @@ public JSONArray schedule(PluginCall call, List localNotifica Integer id = localNotification.getId(); if (localNotification.getId() == null) { if (call != null) { - call.error("LocalNotification missing identifier"); + call.reject("LocalNotification missing identifier"); } return null; } @@ -209,7 +209,7 @@ private void buildNotification(NotificationManagerCompat notificationManager, Lo mBuilder.setColor(Color.parseColor(iconColor)); } catch (IllegalArgumentException ex) { if (call != null) { - call.error("Invalid color provided. Must be a hex string (ex: #ff0000"); + call.reject("Invalid color provided. Must be a hex string (ex: #ff0000"); } return; } @@ -361,7 +361,7 @@ public void cancel(PluginCall call) { storage.deleteNotification(Integer.toString(id)); } } - call.success(); + call.resolve(); } private void cancelTimerForNotification(Integer notificationId) { diff --git a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationChannelManager.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationChannelManager.java index 933fa8e69..bf969684e 100644 --- a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationChannelManager.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/NotificationChannelManager.java @@ -52,7 +52,7 @@ public void createChannel(PluginCall call) { channel.put(CHANNEL_USE_LIGHTS, call.getBoolean(CHANNEL_USE_LIGHTS, false)); channel.put(CHANNEL_LIGHT_COLOR, call.getString(CHANNEL_LIGHT_COLOR, null)); createChannel(channel); - call.success(); + call.resolve(); } else { call.unavailable(); } @@ -97,7 +97,7 @@ public void deleteChannel(PluginCall call) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { String channelId = call.getString("id"); notificationManager.deleteNotificationChannel(channelId); - call.success(); + call.resolve(); } else { call.unavailable(); } @@ -124,7 +124,7 @@ public void listChannels(PluginCall call) { } JSObject result = new JSObject(); result.put("channels", channels); - call.success(result); + call.resolve(result); } else { call.unavailable(); } From 712aaa3cf84ea2cb949ab00b2eb2b034f8e00330 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 9 Nov 2020 16:37:55 -0800 Subject: [PATCH 10/60] import ios code --- .../ios/Plugin.xcodeproj/project.pbxproj | 8 +- .../ios/Plugin/LocalNotifications.swift | 7 - .../Plugin/LocalNotificationsDelegate.swift | 176 ++++++ .../ios/Plugin/LocalNotificationsPlugin.m | 14 +- .../ios/Plugin/LocalNotificationsPlugin.swift | 528 +++++++++++++++++- 5 files changed, 710 insertions(+), 23 deletions(-) delete mode 100644 local-notifications/ios/Plugin/LocalNotifications.swift create mode 100644 local-notifications/ios/Plugin/LocalNotificationsDelegate.swift diff --git a/local-notifications/ios/Plugin.xcodeproj/project.pbxproj b/local-notifications/ios/Plugin.xcodeproj/project.pbxproj index 7337bddf3..6dd7c1b23 100644 --- a/local-notifications/ios/Plugin.xcodeproj/project.pbxproj +++ b/local-notifications/ios/Plugin.xcodeproj/project.pbxproj @@ -9,7 +9,7 @@ /* Begin PBXBuildFile section */ 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */; }; 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */; }; - 2F98D68224C9AAE500613A4C /* LocalNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F98D68124C9AAE400613A4C /* LocalNotifications.swift */; }; + 37F524A5255A0D730085E3FD /* LocalNotificationsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37F524A4255A0D730085E3FD /* LocalNotificationsDelegate.swift */; }; 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFF88201F53D600D50D53 /* Plugin.framework */; }; 50ADFF97201F53D600D50D53 /* LocalNotificationsPluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFF96201F53D600D50D53 /* LocalNotificationsPluginTests.swift */; }; 50ADFF99201F53D600D50D53 /* LocalNotificationsPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 50ADFF8B201F53D600D50D53 /* LocalNotificationsPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -29,7 +29,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 2F98D68124C9AAE400613A4C /* LocalNotifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalNotifications.swift; sourceTree = ""; }; + 37F524A4255A0D730085E3FD /* LocalNotificationsDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationsDelegate.swift; sourceTree = ""; }; 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50ADFF88201F53D600D50D53 /* Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50ADFF8B201F53D600D50D53 /* LocalNotificationsPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LocalNotificationsPlugin.h; sourceTree = ""; }; @@ -93,10 +93,10 @@ isa = PBXGroup; children = ( 50E1A94720377CB70090CE1A /* LocalNotificationsPlugin.swift */, - 2F98D68124C9AAE400613A4C /* LocalNotifications.swift */, 50ADFF8B201F53D600D50D53 /* LocalNotificationsPlugin.h */, 50ADFFA72020EE4F00D50D53 /* LocalNotificationsPlugin.m */, 50ADFF8C201F53D600D50D53 /* Info.plist */, + 37F524A4255A0D730085E3FD /* LocalNotificationsDelegate.swift */, ); path = Plugin; sourceTree = ""; @@ -306,8 +306,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 37F524A5255A0D730085E3FD /* LocalNotificationsDelegate.swift in Sources */, 50E1A94820377CB70090CE1A /* LocalNotificationsPlugin.swift in Sources */, - 2F98D68224C9AAE500613A4C /* LocalNotifications.swift in Sources */, 50ADFFA82020EE4F00D50D53 /* LocalNotificationsPlugin.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/local-notifications/ios/Plugin/LocalNotifications.swift b/local-notifications/ios/Plugin/LocalNotifications.swift deleted file mode 100644 index dac1244e9..000000000 --- a/local-notifications/ios/Plugin/LocalNotifications.swift +++ /dev/null @@ -1,7 +0,0 @@ -import Foundation - -@objc public class LocalNotifications: NSObject { - @objc public func echo(_ value: String) -> String { - return value - } -} diff --git a/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift b/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift new file mode 100644 index 000000000..41054a9ee --- /dev/null +++ b/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift @@ -0,0 +1,176 @@ +import Capacitor +import UserNotifications + +public class LocalNotificationsDelegate: NSObject, UNUserNotificationCenterDelegate { + + public weak var bridge: CAPBridge? + // Local list of notification id -> JSObject for storing options + // between notification requets + var notificationRequestLookup = [String: JSObject]() + + override public init() { + super.init() + let center = UNUserNotificationCenter.current() + if center.delegate == nil { + center.delegate = self + } + } + + public func setBridge(bridge: CAPBridge) { + self.bridge = bridge + } + /** + * Request permissions to send notifications + */ + public func requestPermissions(with completion: ((Bool, Error?) -> Void)? = nil) { + let center = UNUserNotificationCenter.current() + center.requestAuthorization(options: [.badge, .alert, .sound]) { (granted, error) in + completion?(granted, error) + } + } + + public func checkPermissions(with completion: ((Bool, Error?) -> Void)? = nil) { + let center = UNUserNotificationCenter.current() + center.requestAuthorization(options: [.badge, .alert, .sound]) { (granted, error) in + completion?(granted, error) + } + } + + /** + * Handle delegate willPresent action when the app is in the foreground. + * This controls how a notification is presented when the app is running, such as + * whether it should stay silent, display a badge, play a sound, or show an alert. + */ + public func userNotificationCenter(_ center: UNUserNotificationCenter, + willPresent notification: UNNotification, + withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + let request = notification.request + var plugin: CAPPlugin + var action = "localNotificationReceived" + var presentationOptions: UNNotificationPresentationOptions = [] + + var notificationData = makeNotificationRequestJSObject(request) + if request.trigger?.isKind(of: UNPushNotificationTrigger.self) ?? false { + plugin = (self.bridge?.getOrLoadPlugin(pluginName: "PushNotifications"))! + let options = plugin.getConfigValue("presentationOptions") as? [String] ?? ["badge"] + + action = "pushNotificationReceived" + if options.contains("alert") { + presentationOptions.update(with: .alert) + } + if options.contains("badge") { + presentationOptions.update(with: .badge) + } + if options.contains("sound") { + presentationOptions.update(with: .sound) + } + notificationData = makePushNotificationRequestJSObject(request) + + } else { + plugin = (self.bridge?.getOrLoadPlugin(pluginName: "LocalNotifications"))! + presentationOptions = [ + .badge, + .sound, + .alert + ] + } + + plugin.notifyListeners(action, data: notificationData) + + if let options = notificationRequestLookup[request.identifier] { + let silent = options["silent"] as? Bool ?? false + if silent { + completionHandler(.init(rawValue:0)) + return + } + } + + completionHandler(presentationOptions) + } + + /** + * Handle didReceive action, called when a notification opens or activates + * the app based on an action. + */ + public func userNotificationCenter(_ center: UNUserNotificationCenter, + didReceive response: UNNotificationResponse, + withCompletionHandler completionHandler: @escaping () -> Void) { + completionHandler() + + var data = JSObject() + + // Get the info for the original notification request + let originalNotificationRequest = response.notification.request + + let actionId = response.actionIdentifier + + // We turn the two default actions (open/dismiss) into generic strings + if actionId == UNNotificationDefaultActionIdentifier { + data["actionId"] = "tap" + } else if actionId == UNNotificationDismissActionIdentifier { + data["actionId"] = "dismiss" + } else { + data["actionId"] = actionId + } + + // If the type of action was for an input type, get the value + if let inputType = response as? UNTextInputNotificationResponse { + data["inputValue"] = inputType.userText + } + + var plugin: CAPPlugin + var action = "localNotificationActionPerformed" + + if originalNotificationRequest.trigger?.isKind(of: UNPushNotificationTrigger.self) ?? false { + plugin = (self.bridge?.getOrLoadPlugin(pluginName: "PushNotifications"))! + data["notification"] = makePushNotificationRequestJSObject(originalNotificationRequest) + action = "pushNotificationActionPerformed" + } else { + data["notification"] = makeNotificationRequestJSObject(originalNotificationRequest) + plugin = (self.bridge?.getOrLoadPlugin(pluginName: "LocalNotifications"))! + } + + plugin.notifyListeners(action, data: data, retainUntilConsumed: true) + } + + /** + * Make a JSObject of pending notifications. + */ + func makePendingNotificationRequestJSObject(_ request: UNNotificationRequest) -> JSObject { + return [ + "id": request.identifier + ] + } + + /** + * Turn a UNNotificationRequest into a JSObject to return back to the client. + */ + func makeNotificationRequestJSObject(_ request: UNNotificationRequest) -> JSObject { + let notificationRequest = notificationRequestLookup[request.identifier] ?? [:] + return [ + "id": request.identifier, + "title": request.content.title, + "sound": notificationRequest["sound"] ?? "", + "body": request.content.body, + "extra": request.content.userInfo as? JSObject ?? [:], + "actionTypeId": request.content.categoryIdentifier, + "attachments": notificationRequest["attachments"] ?? [] + ] + } + + /** + * Turn a UNNotificationRequest into a JSObject to return back to the client. + */ + func makePushNotificationRequestJSObject(_ request: UNNotificationRequest) -> JSObject { + let content = request.content + return [ + "id": request.identifier, + "title": content.title, + "subtitle": content.subtitle, + "body": content.body, + "badge": content.badge ?? 1, + "data": content.userInfo as? JSObject ?? [:] + ] + } + +} diff --git a/local-notifications/ios/Plugin/LocalNotificationsPlugin.m b/local-notifications/ios/Plugin/LocalNotificationsPlugin.m index 6d2e44203..21150d9b6 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsPlugin.m +++ b/local-notifications/ios/Plugin/LocalNotificationsPlugin.m @@ -1,8 +1,16 @@ #import #import -// Define the plugin using the CAP_PLUGIN Macro, and -// each method the plugin supports using the CAP_PLUGIN_METHOD macro. CAP_PLUGIN(LocalNotificationsPlugin, "LocalNotifications", - CAP_PLUGIN_METHOD(echo, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(schedule, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(requestPermissions, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(checkPermissions, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(cancel, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(getPending, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(registerActionTypes, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(areEnabled, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(createChannel, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(deleteChannel, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(listChannels, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(removeAllListeners, CAPPluginReturnNone); ) diff --git a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift index 60084f081..5d8ce72ba 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift @@ -1,18 +1,528 @@ import Foundation import Capacitor +import UserNotifications + +enum LocalNotificationError: LocalizedError { + case contentNoId + case contentNoTitle + case contentNoBody + case triggerConstructionFailed + case triggerRepeatIntervalTooShort + case attachmentNoId + case attachmentNoUrl + case attachmentFileNotFound(path: String) + case attachmentUnableToCreate(String) + + var errorDescription: String? { + switch self { + case .attachmentFileNotFound(path: let path): + return "Unable to find file \(path) for attachment" + default: + return "" + } + } +} -/** - * Please read the Capacitor iOS Plugin Development Guide - * here: https://capacitorjs.com/docs/plugins/ios - */ @objc(LocalNotificationsPlugin) public class LocalNotificationsPlugin: CAPPlugin { - private let implementation = LocalNotifications() - @objc func echo(_ call: CAPPluginCall) { - let value = call.getString("value") ?? "" - call.resolve([ - "value": implementation.echo(value) + let notificationDelegationHandler = LocalNotificationsDelegate() + + // override public init() { + // self.notificationDelegationHandler = LocalNotificationsDelegate() + // } + + /** + * Schedule a notification. + */ + @objc func schedule(_ call: CAPPluginCall) { + guard let notifications = call.getArray("notifications", JSObject.self) else { + call.error("Must provide notifications array as notifications option") + return + } + var ids = [String]() + + for notification in notifications { + guard let identifier = notification["id"] as? Int else { + call.error("Notification missing identifier") + return + } + + // let extra = notification["options"] as? JSObject ?? [:] + + var content: UNNotificationContent + do { + content = try makeNotificationContent(notification) + } catch { + CAPLog.print(error.localizedDescription) + call.error("Unable to make notification", error) + return + } + + var trigger: UNNotificationTrigger? + + do { + if let schedule = notification["schedule"] as? JSObject { + try trigger = handleScheduledNotification(call, schedule) + } + } catch { + call.error("Unable to create notification, trigger failed", error) + return + } + + // Schedule the request. + let request = UNNotificationRequest(identifier: "\(identifier)", content: content, trigger: trigger) + + self.notificationDelegationHandler.notificationRequestLookup[request.identifier] = notification + + let center = UNUserNotificationCenter.current() + center.add(request) { (error: Error?) in + if let theError = error { + CAPLog.print(theError.localizedDescription) + call.error(theError.localizedDescription) + } + } + + ids.append(request.identifier) + } + + let ret = ids.map({ (id) -> JSObject in + return [ + "id": id + ] + }) + call.success([ + "notifications": ret ]) } + + /** + * Request notification permission + */ + @objc func requestPermissions(_ call: CAPPluginCall) { + self.notificationDelegationHandler.requestPermissions { granted, error in + guard error == nil else { + call.error(error!.localizedDescription) + return + } + call.success(["display": granted ? "granted" : "denied"]) + } + } + + @objc func checkPermissions(_ call: CAPPluginCall) { + call.success(["display": "prompt"]) // TODO + } + + /** + * Cancel notifications by id + */ + @objc func cancel(_ call: CAPPluginCall) { + guard let notifications = call.getArray("notifications", JSObject.self), notifications.count > 0 else { + call.error("Must supply notifications to cancel") + return + } + + let ids = notifications.map { $0["id"] as? String ?? "" } + + UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ids) + call.success() + } + + /** + * Get all pending notifications. + */ + @objc func getPending(_ call: CAPPluginCall) { + UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: { (notifications) in + CAPLog.print("num of pending notifications \(notifications.count)") + CAPLog.print(notifications) + + let ret = notifications.compactMap({ [weak self] (notification) -> JSObject? in + return self?.notificationDelegationHandler.makePendingNotificationRequestJSObject(notification) + }) + call.success([ + "notifications": ret + ]) + }) + } + + /** + * Register allowed action types that a notification may present. + */ + @objc func registerActionTypes(_ call: CAPPluginCall) { + guard let types = call.getArray("types", JSObject.self) else { + return + } + + makeActionTypes(types) + + call.success() + } + + /** + * Check if Local Notifications are authorized and enabled + */ + @objc func areEnabled(_ call: CAPPluginCall) { + let center = UNUserNotificationCenter.current() + center.getNotificationSettings { (settings) in + let authorized = settings.authorizationStatus == UNAuthorizationStatus.authorized + let enabled = settings.notificationCenterSetting == UNNotificationSetting.enabled + call.success([ + "value": enabled && authorized + ]) + } + } + + /** + * Build the content for a notification. + */ + func makeNotificationContent(_ notification: JSObject) throws -> UNNotificationContent { + guard let title = notification["title"] as? String else { + throw LocalNotificationError.contentNoTitle + } + guard let body = notification["body"] as? String else { + throw LocalNotificationError.contentNoBody + } + + let extra = notification["extra"] as? JSObject ?? [:] + let content = UNMutableNotificationContent() + content.title = NSString.localizedUserNotificationString(forKey: title, arguments: nil) + content.body = NSString.localizedUserNotificationString(forKey: body, + arguments: nil) + + content.userInfo = extra + if let actionTypeId = notification["actionTypeId"] as? String { + content.categoryIdentifier = actionTypeId + } + + if let threadIdentifier = notification["threadIdentifier"] as? String { + content.threadIdentifier = threadIdentifier + } + + if #available(iOS 12, *), let summaryArgument = notification["summaryArgument"] as? String { + content.summaryArgument = summaryArgument + } + + if let sound = notification["sound"] as? String { + content.sound = UNNotificationSound(named: UNNotificationSoundName(sound)) + } + + if let attachments = notification["attachments"] as? [JSObject] { + content.attachments = try makeAttachments(attachments) + } + + return content + } + + /** + * Build a notification trigger, such as triggering each N seconds, or + * on a certain date "shape" (such as every first of the month) + */ + func handleScheduledNotification(_ call: CAPPluginCall, _ schedule: JSObject) throws -> UNNotificationTrigger? { + var at: Date? + if let dateString = schedule["at"] as? String, let date = CAPPluginCall.jsDateFormatter.date(from: dateString) { + at = date + } + let every = schedule["every"] as? String + let count = schedule["count"] as? Int ?? 1 + let on = schedule["on"] as? JSObject + let repeats = schedule["repeats"] as? Bool ?? false + + // If there's a specific date for this notificiation + if let at = at { + let dateInfo = Calendar.current.dateComponents(in: TimeZone.current, from: at) + + if dateInfo.date! < Date() { + call.error("Scheduled time must be *after* current time") + return nil + } + + let dateInterval = DateInterval(start: Date(), end: dateInfo.date!) + + // Notifications that repeat have to be at least a minute between each other + if repeats && dateInterval.duration < 60 { + throw LocalNotificationError.triggerRepeatIntervalTooShort + } + + return UNTimeIntervalNotificationTrigger(timeInterval: dateInterval.duration, repeats: repeats) + } + + // If this notification should repeat every count of day/month/week/etc. or on a certain + // matching set of date components + if let on = on { + let dateComponents = getDateComponents(on) + return UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true) + } + + if let every = every { + if let repeatDateInterval = getRepeatDateInterval(every, count) { + return UNTimeIntervalNotificationTrigger(timeInterval: repeatDateInterval.duration, repeats: true) + } + } + + return nil + } + + /** + * Given our schedule format, return a DateComponents object + * that only contains the components passed in. + */ + func getDateComponents(_ at: JSObject) -> DateComponents { + //var dateInfo = Calendar.current.dateComponents(in: TimeZone.current, from: Date()) + //dateInfo.calendar = Calendar.current + var dateInfo = DateComponents() + + if let year = at["year"] as? Int { + dateInfo.year = year + } + if let month = at["month"] as? Int { + dateInfo.month = month + } + if let day = at["day"] as? Int { + dateInfo.day = day + } + if let hour = at["hour"] as? Int { + dateInfo.hour = hour + } + if let minute = at["minute"] as? Int { + dateInfo.minute = minute + } + if let second = at["second"] as? Int { + dateInfo.second = second + } + return dateInfo + } + + /** + * Compute the difference between the string representation of a date + * interval and today. For example, if every is "month", then we + * return the interval between today and a month from today. + */ + func getRepeatDateInterval(_ every: String, _ count: Int) -> DateInterval? { + let cal = Calendar.current + let now = Date() + switch every { + case "year": + let newDate = cal.date(byAdding: .year, value: count, to: now)! + return DateInterval(start: now, end: newDate) + case "month": + let newDate = cal.date(byAdding: .month, value: count, to: now)! + return DateInterval(start: now, end: newDate) + case "two-weeks": + let newDate = cal.date(byAdding: .weekOfYear, value: 2 * count, to: now)! + return DateInterval(start: now, end: newDate) + case "week": + let newDate = cal.date(byAdding: .weekOfYear, value: count, to: now)! + return DateInterval(start: now, end: newDate) + case "day": + let newDate = cal.date(byAdding: .day, value: count, to: now)! + return DateInterval(start: now, end: newDate) + case "hour": + let newDate = cal.date(byAdding: .hour, value: count, to: now)! + return DateInterval(start: now, end: newDate) + case "minute": + let newDate = cal.date(byAdding: .minute, value: count, to: now)! + return DateInterval(start: now, end: newDate) + case "second": + let newDate = cal.date(byAdding: .second, value: count, to: now)! + return DateInterval(start: now, end: newDate) + default: + return nil + } + } + + /** + * Make required UNNotificationCategory entries for action types + */ + func makeActionTypes(_ actionTypes: [JSObject]) { + var createdCategories = [UNNotificationCategory]() + + let generalCategory = UNNotificationCategory(identifier: "GENERAL", + actions: [], + intentIdentifiers: [], + options: .customDismissAction) + + createdCategories.append(generalCategory) + for type in actionTypes { + guard let id = type["id"] as? String else { + bridge?.modulePrint(self, "Action type must have an id field") + continue + } + let hiddenBodyPlaceholder = type["iosHiddenPreviewsBodyPlaceholder"] as? String ?? "" + let actions = type["actions"] as? [JSObject] ?? [] + + let newActions = makeActions(actions) + + // Create the custom actions for the TIMER_EXPIRED category. + var newCategory: UNNotificationCategory? + + newCategory = UNNotificationCategory(identifier: id, + actions: newActions, + intentIdentifiers: [], + hiddenPreviewsBodyPlaceholder: hiddenBodyPlaceholder, + options: makeCategoryOptions(type)) + + createdCategories.append(newCategory!) + } + + let center = UNUserNotificationCenter.current() + center.setNotificationCategories(Set(createdCategories)) + } + + /** + * Build the required UNNotificationAction objects for each action type registered. + */ + func makeActions(_ actions: [JSObject]) -> [UNNotificationAction] { + var createdActions = [UNNotificationAction]() + + for action in actions { + guard let id = action["id"] as? String else { + bridge?.modulePrint(self, "Action must have an id field") + continue + } + let title = action["title"] as? String ?? "" + let input = action["input"] as? Bool ?? false + + var newAction: UNNotificationAction + if input { + let inputButtonTitle = action["inputButtonTitle"] as? String + let inputPlaceholder = action["inputPlaceholder"] as? String ?? "" + + if inputButtonTitle != nil { + newAction = UNTextInputNotificationAction(identifier: id, + title: title, + options: makeActionOptions(action), + textInputButtonTitle: inputButtonTitle!, + textInputPlaceholder: inputPlaceholder) + } else { + newAction = UNTextInputNotificationAction(identifier: id, title: title, options: makeActionOptions(action)) + } + } else { + // Create the custom actions for the TIMER_EXPIRED category. + newAction = UNNotificationAction(identifier: id, + title: title, + options: makeActionOptions(action)) + } + createdActions.append(newAction) + } + + return createdActions + } + + /** + * Make options for UNNotificationActions + */ + func makeActionOptions(_ action: JSObject) -> UNNotificationActionOptions { + let foreground = action["foreground"] as? Bool ?? false + let destructive = action["destructive"] as? Bool ?? false + let requiresAuthentication = action["requiresAuthentication"] as? Bool ?? false + + if foreground { + return .foreground + } + if destructive { + return .destructive + } + if requiresAuthentication { + return .authenticationRequired + } + return UNNotificationActionOptions(rawValue: 0) + } + + /** + * Make options for UNNotificationCategoryActions + */ + func makeCategoryOptions(_ type: JSObject) -> UNNotificationCategoryOptions { + let customDismiss = type["iosCustomDismissAction"] as? Bool ?? false + let carPlay = type["iosAllowInCarPlay"] as? Bool ?? false + let hiddenPreviewsShowTitle = type["iosHiddenPreviewsShowTitle"] as? Bool ?? false + let hiddenPreviewsShowSubtitle = type["iosHiddenPreviewsShowSubtitle"] as? Bool ?? false + + if customDismiss { + return .customDismissAction + } + if carPlay { + return .allowInCarPlay + } + + if hiddenPreviewsShowTitle { + return .hiddenPreviewsShowTitle + } + if hiddenPreviewsShowSubtitle { + return .hiddenPreviewsShowSubtitle + } + + return UNNotificationCategoryOptions(rawValue: 0) + } + + /** + * Build the UNNotificationAttachment object for each attachment supplied. + */ + func makeAttachments(_ attachments: [JSObject]) throws -> [UNNotificationAttachment] { + var createdAttachments = [UNNotificationAttachment]() + + for attachment in attachments { + guard let id = attachment["id"] as? String else { + throw LocalNotificationError.attachmentNoId + } + guard let url = attachment["url"] as? String else { + throw LocalNotificationError.attachmentNoUrl + } + guard let urlObject = makeAttachmentUrl(url) else { + throw LocalNotificationError.attachmentFileNotFound(path: url) + } + + let options = attachment["options"] as? JSObject ?? [:] + + do { + let newAttachment = try UNNotificationAttachment(identifier: id, url: urlObject, options: makeAttachmentOptions(options)) + createdAttachments.append(newAttachment) + } catch { + throw LocalNotificationError.attachmentUnableToCreate(error.localizedDescription) + } + } + + return createdAttachments + } + + /** + * Get the internal URL for the attachment URL + */ + func makeAttachmentUrl(_ path: String) -> URL? { + let file = CAPFileManager.get(path: path) + return file?.url + } + + /** + * Build the options for the attachment, if any. (For example: the clipping rectangle to use + * for image attachments) + */ + func makeAttachmentOptions(_ options: JSObject) -> JSObject { + var opts: JSObject = [:] + + if let iosUNNotificationAttachmentOptionsTypeHintKey = options["iosUNNotificationAttachmentOptionsTypeHintKey"] as? String { + opts[UNNotificationAttachmentOptionsTypeHintKey] = iosUNNotificationAttachmentOptionsTypeHintKey + } + if let iosUNNotificationAttachmentOptionsThumbnailHiddenKey = options["iosUNNotificationAttachmentOptionsThumbnailHiddenKey"] as? String { + opts[UNNotificationAttachmentOptionsThumbnailHiddenKey] = iosUNNotificationAttachmentOptionsThumbnailHiddenKey + } + if let iosUNNotificationAttachmentOptionsThumbnailClippingRectKey = options["iosUNNotificationAttachmentOptionsThumbnailClippingRectKey"] as? String { + opts[UNNotificationAttachmentOptionsThumbnailClippingRectKey] = iosUNNotificationAttachmentOptionsThumbnailClippingRectKey + } + if let iosUNNotificationAttachmentOptionsThumbnailTimeKey = options["iosUNNotificationAttachmentOptionsThumbnailTimeKey"] as? String { + opts[UNNotificationAttachmentOptionsThumbnailTimeKey] = iosUNNotificationAttachmentOptionsThumbnailTimeKey + } + return opts + } + + @objc func createChannel(_ call: CAPPluginCall) { + call.unimplemented() + } + + @objc func deleteChannel(_ call: CAPPluginCall) { + call.unimplemented() + } + + @objc func listChannels(_ call: CAPPluginCall) { + call.unimplemented() + } } From 0e6111d55b509c2287c7587a69f679622d17ed6b Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 9 Nov 2020 16:38:29 -0800 Subject: [PATCH 11/60] android permissions (requestPermissions doesn't work) --- .../localnotifications/LocalNotificationsPlugin.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java index 0429ac9c5..cdbea3b11 100644 --- a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java @@ -71,11 +71,11 @@ public void schedule(PluginCall call) { } } - @PluginMethod - public void requestPermission(PluginCall call) { + @Override + public JSObject getPermissionStates() { JSObject result = new JSObject(); - result.put("granted", true); - call.resolve(result); + result.put("display", "granted"); + return result; } @PluginMethod From b41425bbf3d9f19ca530ac788ad2a72d65330bc3 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Fri, 20 Nov 2020 11:39:36 -0800 Subject: [PATCH 12/60] wip --- .../Plugin/LocalNotificationsDelegate.swift | 139 ++++-------------- .../ios/Plugin/LocalNotificationsPlugin.swift | 19 ++- 2 files changed, 38 insertions(+), 120 deletions(-) diff --git a/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift b/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift index 41054a9ee..27bd8b0c4 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift @@ -1,27 +1,14 @@ import Capacitor import UserNotifications -public class LocalNotificationsDelegate: NSObject, UNUserNotificationCenterDelegate { +public class LocalNotificationsDelegate: NSObject, NotificationHandlerProtocol { - public weak var bridge: CAPBridge? + public var plugin: CAPPlugin? + // Local list of notification id -> JSObject for storing options // between notification requets var notificationRequestLookup = [String: JSObject]() - - override public init() { - super.init() - let center = UNUserNotificationCenter.current() - if center.delegate == nil { - center.delegate = self - } - } - - public func setBridge(bridge: CAPBridge) { - self.bridge = bridge - } - /** - * Request permissions to send notifications - */ + public func requestPermissions(with completion: ((Bool, Error?) -> Void)? = nil) { let center = UNUserNotificationCenter.current() center.requestAuthorization(options: [.badge, .alert, .sound]) { (granted, error) in @@ -35,75 +22,34 @@ public class LocalNotificationsDelegate: NSObject, UNUserNotificationCenterDeleg completion?(granted, error) } } + + public func willPresent(notification: UNNotification) -> UNNotificationPresentationOptions { + let notificationData = makeNotificationRequestJSObject(notification.request) - /** - * Handle delegate willPresent action when the app is in the foreground. - * This controls how a notification is presented when the app is running, such as - * whether it should stay silent, display a badge, play a sound, or show an alert. - */ - public func userNotificationCenter(_ center: UNUserNotificationCenter, - willPresent notification: UNNotification, - withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { - let request = notification.request - var plugin: CAPPlugin - var action = "localNotificationReceived" - var presentationOptions: UNNotificationPresentationOptions = [] - - var notificationData = makeNotificationRequestJSObject(request) - if request.trigger?.isKind(of: UNPushNotificationTrigger.self) ?? false { - plugin = (self.bridge?.getOrLoadPlugin(pluginName: "PushNotifications"))! - let options = plugin.getConfigValue("presentationOptions") as? [String] ?? ["badge"] - - action = "pushNotificationReceived" - if options.contains("alert") { - presentationOptions.update(with: .alert) - } - if options.contains("badge") { - presentationOptions.update(with: .badge) - } - if options.contains("sound") { - presentationOptions.update(with: .sound) - } - notificationData = makePushNotificationRequestJSObject(request) - - } else { - plugin = (self.bridge?.getOrLoadPlugin(pluginName: "LocalNotifications"))! - presentationOptions = [ - .badge, - .sound, - .alert - ] - } - - plugin.notifyListeners(action, data: notificationData) - - if let options = notificationRequestLookup[request.identifier] { + self.plugin?.notifyListeners("localNotificationReceived", data: notificationData) + + if let options = notificationRequestLookup[notification.request.identifier] { let silent = options["silent"] as? Bool ?? false if silent { - completionHandler(.init(rawValue:0)) - return + return UNNotificationPresentationOptions.init(rawValue: 0) } } - - completionHandler(presentationOptions) + + return [ + .badge, + .sound, + .alert + ] } - - /** - * Handle didReceive action, called when a notification opens or activates - * the app based on an action. - */ - public func userNotificationCenter(_ center: UNUserNotificationCenter, - didReceive response: UNNotificationResponse, - withCompletionHandler completionHandler: @escaping () -> Void) { - completionHandler() - + + public func didReceive(response: UNNotificationResponse) { var data = JSObject() // Get the info for the original notification request let originalNotificationRequest = response.notification.request - + let actionId = response.actionIdentifier - + // We turn the two default actions (open/dismiss) into generic strings if actionId == UNNotificationDefaultActionIdentifier { data["actionId"] = "tap" @@ -112,34 +58,15 @@ public class LocalNotificationsDelegate: NSObject, UNUserNotificationCenterDeleg } else { data["actionId"] = actionId } - + // If the type of action was for an input type, get the value if let inputType = response as? UNTextInputNotificationResponse { data["inputValue"] = inputType.userText } - var plugin: CAPPlugin - var action = "localNotificationActionPerformed" - - if originalNotificationRequest.trigger?.isKind(of: UNPushNotificationTrigger.self) ?? false { - plugin = (self.bridge?.getOrLoadPlugin(pluginName: "PushNotifications"))! - data["notification"] = makePushNotificationRequestJSObject(originalNotificationRequest) - action = "pushNotificationActionPerformed" - } else { - data["notification"] = makeNotificationRequestJSObject(originalNotificationRequest) - plugin = (self.bridge?.getOrLoadPlugin(pluginName: "LocalNotifications"))! - } - - plugin.notifyListeners(action, data: data, retainUntilConsumed: true) - } - - /** - * Make a JSObject of pending notifications. - */ - func makePendingNotificationRequestJSObject(_ request: UNNotificationRequest) -> JSObject { - return [ - "id": request.identifier - ] + data["notification"] = makeNotificationRequestJSObject(originalNotificationRequest) + + self.plugin?.notifyListeners("localNotificationActionPerformed", data: data, retainUntilConsumed: true) } /** @@ -157,20 +84,4 @@ public class LocalNotificationsDelegate: NSObject, UNUserNotificationCenterDeleg "attachments": notificationRequest["attachments"] ?? [] ] } - - /** - * Turn a UNNotificationRequest into a JSObject to return back to the client. - */ - func makePushNotificationRequestJSObject(_ request: UNNotificationRequest) -> JSObject { - let content = request.content - return [ - "id": request.identifier, - "title": content.title, - "subtitle": content.subtitle, - "body": content.body, - "badge": content.badge ?? 1, - "data": content.userInfo as? JSObject ?? [:] - ] - } - } diff --git a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift index 5d8ce72ba..659751108 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift @@ -25,12 +25,12 @@ enum LocalNotificationError: LocalizedError { @objc(LocalNotificationsPlugin) public class LocalNotificationsPlugin: CAPPlugin { + private let notificationDelegationHandler = LocalNotificationsDelegate() - let notificationDelegationHandler = LocalNotificationsDelegate() - - // override public init() { - // self.notificationDelegationHandler = LocalNotificationsDelegate() - // } + override public func load() { + self.bridge?.userNotificationDelegate.localNotificationHandler = self.notificationDelegationHandler + self.notificationDelegationHandler.plugin = self + } /** * Schedule a notification. @@ -137,7 +137,7 @@ public class LocalNotificationsPlugin: CAPPlugin { CAPLog.print(notifications) let ret = notifications.compactMap({ [weak self] (notification) -> JSObject? in - return self?.notificationDelegationHandler.makePendingNotificationRequestJSObject(notification) + return self?.makePendingNotificationRequestJSObject(notification) }) call.success([ "notifications": ret @@ -513,6 +513,13 @@ public class LocalNotificationsPlugin: CAPPlugin { } return opts } + + func makePendingNotificationRequestJSObject(_ request: UNNotificationRequest) -> JSObject { + return [ + "id": request.identifier + ] + } + @objc func createChannel(_ call: CAPPluginCall) { call.unimplemented() From c28508b37bb855c5140706cc684c743e2fbe240b Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 23 Nov 2020 16:32:57 -0800 Subject: [PATCH 13/60] wip --- .../Plugin/LocalNotificationsDelegate.swift | 20 +++++++++---------- .../ios/Plugin/LocalNotificationsPlugin.swift | 10 ++++++---- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift b/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift index 27bd8b0c4..1b677331a 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift @@ -4,11 +4,11 @@ import UserNotifications public class LocalNotificationsDelegate: NSObject, NotificationHandlerProtocol { public var plugin: CAPPlugin? - + // Local list of notification id -> JSObject for storing options // between notification requets var notificationRequestLookup = [String: JSObject]() - + public func requestPermissions(with completion: ((Bool, Error?) -> Void)? = nil) { let center = UNUserNotificationCenter.current() center.requestAuthorization(options: [.badge, .alert, .sound]) { (granted, error) in @@ -22,34 +22,34 @@ public class LocalNotificationsDelegate: NSObject, NotificationHandlerProtocol { completion?(granted, error) } } - + public func willPresent(notification: UNNotification) -> UNNotificationPresentationOptions { let notificationData = makeNotificationRequestJSObject(notification.request) self.plugin?.notifyListeners("localNotificationReceived", data: notificationData) - + if let options = notificationRequestLookup[notification.request.identifier] { let silent = options["silent"] as? Bool ?? false if silent { return UNNotificationPresentationOptions.init(rawValue: 0) } } - + return [ .badge, .sound, .alert ] } - + public func didReceive(response: UNNotificationResponse) { var data = JSObject() // Get the info for the original notification request let originalNotificationRequest = response.notification.request - + let actionId = response.actionIdentifier - + // We turn the two default actions (open/dismiss) into generic strings if actionId == UNNotificationDefaultActionIdentifier { data["actionId"] = "tap" @@ -58,14 +58,14 @@ public class LocalNotificationsDelegate: NSObject, NotificationHandlerProtocol { } else { data["actionId"] = actionId } - + // If the type of action was for an input type, get the value if let inputType = response as? UNTextInputNotificationResponse { data["inputValue"] = inputType.userText } data["notification"] = makeNotificationRequestJSObject(originalNotificationRequest) - + self.plugin?.notifyListeners("localNotificationActionPerformed", data: data, retainUntilConsumed: true) } diff --git a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift index 659751108..3294da276 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift @@ -488,8 +488,11 @@ public class LocalNotificationsPlugin: CAPPlugin { * Get the internal URL for the attachment URL */ func makeAttachmentUrl(_ path: String) -> URL? { - let file = CAPFileManager.get(path: path) - return file?.url + guard let webURL = URL(string: path) else { + return nil + } + + return bridge?.localURL(fromWebURL: webURL) } /** @@ -513,14 +516,13 @@ public class LocalNotificationsPlugin: CAPPlugin { } return opts } - + func makePendingNotificationRequestJSObject(_ request: UNNotificationRequest) -> JSObject { return [ "id": request.identifier ] } - @objc func createChannel(_ call: CAPPluginCall) { call.unimplemented() } From 164c7745cdde18bcd8096a4a6ef64d47d8286de2 Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Tue, 24 Nov 2020 14:22:50 -0600 Subject: [PATCH 14/60] updated handleOnActivityResult for alpha 7 --- .../java/com/capacitorjs/plugins/share/SharePlugin.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/share/android/src/main/java/com/capacitorjs/plugins/share/SharePlugin.java b/share/android/src/main/java/com/capacitorjs/plugins/share/SharePlugin.java index 565c96761..f719eb4af 100644 --- a/share/android/src/main/java/com/capacitorjs/plugins/share/SharePlugin.java +++ b/share/android/src/main/java/com/capacitorjs/plugins/share/SharePlugin.java @@ -104,11 +104,7 @@ public void share(PluginCall call) { } @Override - protected void handleOnActivityResult(int requestCode, int resultCode, Intent data) { - PluginCall savedCall = getSavedCall(); - if (savedCall == null) { - return; - } + protected void handleOnActivityResult(PluginCall savedCall, int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_CANCELED && !stopped) { savedCall.reject("Share canceled"); } else { From 48eb01628ed0ca8bcfaac28b0f17f2fa88f5d511 Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Tue, 24 Nov 2020 14:27:01 -0600 Subject: [PATCH 15/60] put back null check --- .../main/java/com/capacitorjs/plugins/share/SharePlugin.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/share/android/src/main/java/com/capacitorjs/plugins/share/SharePlugin.java b/share/android/src/main/java/com/capacitorjs/plugins/share/SharePlugin.java index f719eb4af..07732980f 100644 --- a/share/android/src/main/java/com/capacitorjs/plugins/share/SharePlugin.java +++ b/share/android/src/main/java/com/capacitorjs/plugins/share/SharePlugin.java @@ -105,6 +105,10 @@ public void share(PluginCall call) { @Override protected void handleOnActivityResult(PluginCall savedCall, int requestCode, int resultCode, Intent data) { + if (savedCall == null) { + return; + } + if (resultCode == Activity.RESULT_CANCELED && !stopped) { savedCall.reject("Share canceled"); } else { From 0c9f009e9ade6f26d2ee3235073e915c8a367b11 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Tue, 24 Nov 2020 13:19:06 -0800 Subject: [PATCH 16/60] toggle-local --- action-sheet/package.json | 6 +++--- app-launcher/package.json | 6 +++--- app/package.json | 6 +++--- browser/package.json | 6 +++--- clipboard/package.json | 6 +++--- device/package.json | 6 +++--- dialog/package.json | 6 +++--- haptics/package.json | 6 +++--- keyboard/package.json | 6 +++--- motion/package.json | 6 +++--- network/package.json | 6 +++--- screen-reader/package.json | 6 +++--- share/package.json | 6 +++--- status-bar/package.json | 6 +++--- storage/package.json | 6 +++--- text-zoom/package.json | 6 +++--- toast/package.json | 6 +++--- 17 files changed, 51 insertions(+), 51 deletions(-) diff --git a/action-sheet/package.json b/action-sheet/package.json index acc711c19..daba0a9cf 100644 --- a/action-sheet/package.json +++ b/action-sheet/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/app-launcher/package.json b/app-launcher/package.json index 0df643072..f542c93f6 100644 --- a/app-launcher/package.json +++ b/app-launcher/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/app/package.json b/app/package.json index 8da2bf804..d5e0cbdc7 100644 --- a/app/package.json +++ b/app/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/browser/package.json b/browser/package.json index a2180d488..60c0d77fa 100644 --- a/browser/package.json +++ b/browser/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/clipboard/package.json b/clipboard/package.json index f7a1fb082..73c3f41bb 100644 --- a/clipboard/package.json +++ b/clipboard/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/device/package.json b/device/package.json index d78566a4a..bbf9f57b7 100644 --- a/device/package.json +++ b/device/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/dialog/package.json b/dialog/package.json index 7468b9d87..0815cbfed 100644 --- a/dialog/package.json +++ b/dialog/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/haptics/package.json b/haptics/package.json index 5b30e6004..aa0db1086 100644 --- a/haptics/package.json +++ b/haptics/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/keyboard/package.json b/keyboard/package.json index 1f936918f..a6c81e38f 100644 --- a/keyboard/package.json +++ b/keyboard/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/motion/package.json b/motion/package.json index 43dbca981..d3ad18752 100644 --- a/motion/package.json +++ b/motion/package.json @@ -35,10 +35,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@rollup/plugin-node-resolve": "^9.0.0", diff --git a/network/package.json b/network/package.json index 493b66218..58a2036a1 100644 --- a/network/package.json +++ b/network/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/screen-reader/package.json b/screen-reader/package.json index bd644109b..8cb14a585 100644 --- a/screen-reader/package.json +++ b/screen-reader/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/share/package.json b/share/package.json index c44c0463d..394a223a9 100644 --- a/share/package.json +++ b/share/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/status-bar/package.json b/status-bar/package.json index 443328491..17f5a559b 100644 --- a/status-bar/package.json +++ b/status-bar/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/storage/package.json b/storage/package.json index 86a509921..bae842777 100644 --- a/storage/package.json +++ b/storage/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/text-zoom/package.json b/text-zoom/package.json index 7fa498549..0df9aeee3 100644 --- a/text-zoom/package.json +++ b/text-zoom/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/toast/package.json b/toast/package.json index a642e9ac9..24e2e12e4 100644 --- a/toast/package.json +++ b/toast/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", From fbd088786147d7045ac682eb7a8cb265570bab84 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 24 Nov 2020 15:21:09 -0600 Subject: [PATCH 17/60] refactor: update plugin registration from runtime refactor (#93) --- action-sheet/package.json | 5 ++-- action-sheet/rollup.config.js | 11 ++------- action-sheet/src/index.ts | 17 ++++---------- action-sheet/src/web.ts | 4 ---- action-sheet/tsconfig.json | 10 +++----- app-launcher/package.json | 5 ++-- app-launcher/rollup.config.js | 11 ++------- app-launcher/src/index.ts | 17 ++++---------- app-launcher/src/web.ts | 4 ---- app-launcher/tsconfig.json | 10 +++----- app/package.json | 5 ++-- app/rollup.config.js | 11 ++------- app/src/index.ts | 14 ++++------- app/src/web.ts | 2 +- app/tsconfig.json | 10 +++----- browser/package.json | 5 ++-- browser/rollup.config.js | 11 ++------- browser/src/index.ts | 14 ++++------- browser/src/web.ts | 2 +- browser/tsconfig.json | 10 +++----- clipboard/package.json | 5 ++-- clipboard/rollup.config.js | 11 ++------- clipboard/src/index.ts | 17 ++++---------- clipboard/src/web.ts | 6 ----- clipboard/tsconfig.json | 2 +- device/package.json | 5 ++-- device/rollup.config.js | 11 ++------- device/src/index.ts | 14 ++++------- device/src/web.ts | 43 +++++++++++++++++----------------- device/tsconfig.json | 10 +++----- dialog/package.json | 5 ++-- dialog/rollup.config.js | 11 ++------- dialog/src/index.ts | 14 ++++------- dialog/src/web.ts | 4 ---- dialog/tsconfig.json | 10 +++----- haptics/package.json | 5 ++-- haptics/rollup.config.js | 11 ++------- haptics/src/index.ts | 14 ++++------- haptics/src/web.ts | 4 ---- haptics/tsconfig.json | 10 +++----- keyboard/package.json | 5 ++-- keyboard/rollup.config.js | 11 ++------- keyboard/src/index.ts | 13 ++-------- keyboard/tsconfig.json | 10 +++----- motion/package.json | 5 ++-- motion/rollup.config.js | 11 ++------- motion/src/index.ts | 16 ++++--------- motion/src/web.ts | 2 +- motion/tsconfig.json | 10 +++----- network/package.json | 5 ++-- network/rollup.config.js | 11 ++------- network/src/index.ts | 14 ++++------- network/src/web.ts | 4 ---- network/tsconfig.json | 10 +++----- screen-reader/package.json | 5 ++-- screen-reader/rollup.config.js | 11 ++------- screen-reader/src/index.ts | 17 ++++---------- screen-reader/src/web.ts | 4 ---- screen-reader/tsconfig.json | 10 +++----- share/package.json | 5 ++-- share/rollup.config.js | 11 ++------- share/src/index.ts | 14 ++++------- share/src/web.ts | 4 ---- share/tsconfig.json | 10 +++----- status-bar/package.json | 5 ++-- status-bar/rollup.config.js | 11 ++------- status-bar/src/index.ts | 13 ++-------- status-bar/tsconfig.json | 10 +++----- storage/package.json | 5 ++-- storage/rollup.config.js | 11 ++------- storage/src/index.ts | 14 ++++------- storage/src/web.ts | 4 ---- storage/tsconfig.json | 10 +++----- text-zoom/package.json | 5 ++-- text-zoom/rollup.config.js | 11 ++------- text-zoom/src/index.ts | 16 ++++--------- text-zoom/src/ios.ts | 9 +++++-- text-zoom/tsconfig.json | 10 +++----- toast/package.json | 5 ++-- toast/rollup.config.js | 11 ++------- toast/src/index.ts | 14 ++++------- toast/src/web.ts | 20 +++++++--------- toast/tsconfig.json | 10 +++----- 83 files changed, 222 insertions(+), 580 deletions(-) diff --git a/action-sheet/package.json b/action-sheet/package.json index daba0a9cf..a9f04e993 100644 --- a/action-sheet/package.json +++ b/action-sheet/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/action-sheet", "version": "0.1.1", "description": "The Action Sheet API provides access to native Action Sheets, which come up from the bottom of the screen and display actions a user can take.", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/action-sheet/rollup.config.js b/action-sheet/rollup.config.js index 7c37f1d21..796ecfbe0 100644 --- a/action-sheet/rollup.config.js +++ b/action-sheet/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/action-sheet/src/index.ts b/action-sheet/src/index.ts index 69024f9d5..f94c58bcd 100644 --- a/action-sheet/src/index.ts +++ b/action-sheet/src/index.ts @@ -1,19 +1,10 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { ActionSheetPlugin } from './definitions'; import { ActionSheetOptionStyle } from './definitions'; -import { ActionSheetWeb } from './web'; -const implementations: PluginImplementations = { - android: Plugins.ActionSheet, - ios: Plugins.ActionSheet, - web: new ActionSheetWeb(), -}; - -const ActionSheet = registerPlugin( - 'ActionSheet', - implementations, -).getImplementation(); +const ActionSheet = registerPlugin('ActionSheet', { + web: () => import('./web').then(m => new m.ActionSheetWeb()), +}); export { ActionSheet, ActionSheetOptionStyle }; diff --git a/action-sheet/src/web.ts b/action-sheet/src/web.ts index a59656002..094315936 100644 --- a/action-sheet/src/web.ts +++ b/action-sheet/src/web.ts @@ -7,10 +7,6 @@ import type { } from './definitions'; export class ActionSheetWeb extends WebPlugin implements ActionSheetPlugin { - constructor() { - super({ name: 'ActionSheet' }); - } - async showActions(options: ActionSheetOptions): Promise { return new Promise((resolve, _reject) => { let actionSheet: any = document.querySelector('pwa-action-sheet'); diff --git a/action-sheet/tsconfig.json b/action-sheet/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/action-sheet/tsconfig.json +++ b/action-sheet/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/app-launcher/package.json b/app-launcher/package.json index f542c93f6..f437dde00 100644 --- a/app-launcher/package.json +++ b/app-launcher/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/app-launcher", "version": "0.1.0", "description": "The AppLauncher API allows to open other apps", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/app-launcher/rollup.config.js b/app-launcher/rollup.config.js index fb27bda84..3ce1ea5b2 100644 --- a/app-launcher/rollup.config.js +++ b/app-launcher/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/app-launcher/src/index.ts b/app-launcher/src/index.ts index 4407a6d45..82d664db6 100644 --- a/app-launcher/src/index.ts +++ b/app-launcher/src/index.ts @@ -1,18 +1,9 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { AppLauncherPlugin } from './definitions'; -import { AppLauncherWeb } from './web'; -const implementations: PluginImplementations = { - android: Plugins.AppLauncher, - ios: Plugins.AppLauncher, - web: new AppLauncherWeb(), -}; - -const AppLauncher = registerPlugin( - 'AppLauncher', - implementations, -).getImplementation(); +const AppLauncher = registerPlugin('AppLauncher', { + web: () => import('./web').then(m => new m.AppLauncherWeb()), +}); export { AppLauncher }; diff --git a/app-launcher/src/web.ts b/app-launcher/src/web.ts index fed82a58e..efb991ca7 100644 --- a/app-launcher/src/web.ts +++ b/app-launcher/src/web.ts @@ -3,10 +3,6 @@ import { WebPlugin } from '@capacitor/core'; import type { AppLauncherPlugin } from './definitions'; export class AppLauncherWeb extends WebPlugin implements AppLauncherPlugin { - constructor() { - super({ name: 'AppLauncher' }); - } - async canOpenUrl(_options: { url: string }): Promise<{ value: boolean }> { return { value: true }; } diff --git a/app-launcher/tsconfig.json b/app-launcher/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/app-launcher/tsconfig.json +++ b/app-launcher/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/app/package.json b/app/package.json index d5e0cbdc7..d9aab6199 100644 --- a/app/package.json +++ b/app/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/app", "version": "0.0.1", "description": "The App API handles high level App state and events.For example, this API emits events when the app enters and leaves the foreground, handles deeplinks, opens other apps, and manages persisted plugin state.", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/app/rollup.config.js b/app/rollup.config.js index 0073d4f88..b77f7f790 100644 --- a/app/rollup.config.js +++ b/app/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/app/src/index.ts b/app/src/index.ts index 6dafb44aa..aa22d5957 100644 --- a/app/src/index.ts +++ b/app/src/index.ts @@ -1,15 +1,9 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { AppPlugin } from './definitions'; -import { AppWeb } from './web'; -const implementations: PluginImplementations = { - android: Plugins.App, - ios: Plugins.App, - web: new AppWeb(), -}; - -const App = registerPlugin('App', implementations).getImplementation(); +const App = registerPlugin('App', { + web: () => import('./web').then(m => new m.AppWeb()), +}); export { App }; diff --git a/app/src/web.ts b/app/src/web.ts index c1ba46bb2..13d261e29 100644 --- a/app/src/web.ts +++ b/app/src/web.ts @@ -4,7 +4,7 @@ import type { AppInfo, AppPlugin, AppLaunchUrl, AppState } from './definitions'; export class AppWeb extends WebPlugin implements AppPlugin { constructor() { - super({ name: 'App' }); + super(); if (typeof document !== 'undefined') { document.addEventListener( 'visibilitychange', diff --git a/app/tsconfig.json b/app/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/app/tsconfig.json +++ b/app/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/browser/package.json b/browser/package.json index 60c0d77fa..ed8f9ac89 100644 --- a/browser/package.json +++ b/browser/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/browser", "version": "0.1.0", "description": "The Browser API provides the ability to open an in-app browser and subscribe to browser events.", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/browser/rollup.config.js b/browser/rollup.config.js index aca4094ae..807486398 100644 --- a/browser/rollup.config.js +++ b/browser/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/browser/src/index.ts b/browser/src/index.ts index ee74aecea..5883c2ca1 100644 --- a/browser/src/index.ts +++ b/browser/src/index.ts @@ -1,15 +1,9 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { BrowserPlugin } from './definitions'; -import { BrowserWeb } from './web'; -const implementations: PluginImplementations = { - android: Plugins.Browser, - ios: Plugins.Browser, - web: new BrowserWeb(), -}; - -const Browser = registerPlugin('Browser', implementations).getImplementation(); +const Browser = registerPlugin('Browser', { + web: () => import('./web').then(m => new m.BrowserWeb()), +}); export { Browser }; diff --git a/browser/src/web.ts b/browser/src/web.ts index 401066371..efb25c773 100644 --- a/browser/src/web.ts +++ b/browser/src/web.ts @@ -6,7 +6,7 @@ export class BrowserWeb extends WebPlugin implements BrowserPlugin { _lastWindow: Window | null; constructor() { - super({ name: 'Browser' }); + super(); this._lastWindow = null; } diff --git a/browser/tsconfig.json b/browser/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/browser/tsconfig.json +++ b/browser/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/clipboard/package.json b/clipboard/package.json index 73c3f41bb..2dd5b0c12 100644 --- a/clipboard/package.json +++ b/clipboard/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/clipboard", "version": "0.1.1", "description": "The Clipboard API enables copy and pasting to/from the system clipboard.", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/clipboard/rollup.config.js b/clipboard/rollup.config.js index e3e142875..3391fc409 100644 --- a/clipboard/rollup.config.js +++ b/clipboard/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/master/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/clipboard/src/index.ts b/clipboard/src/index.ts index 77ed009bf..eed554b1e 100644 --- a/clipboard/src/index.ts +++ b/clipboard/src/index.ts @@ -1,18 +1,9 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { ClipboardPlugin } from './definitions'; -import { ClipboardWeb } from './web'; -const implementations: PluginImplementations = { - android: Plugins.Clipboard, - ios: Plugins.Clipboard, - web: new ClipboardWeb(), -}; - -const Clipboard = registerPlugin( - 'Clipboard', - implementations, -).getImplementation(); +const Clipboard = registerPlugin('Clipboard', { + web: () => import('./web').then(m => new m.ClipboardWeb()), +}); export { Clipboard }; diff --git a/clipboard/src/web.ts b/clipboard/src/web.ts index 31a259457..72cadf0ab 100644 --- a/clipboard/src/web.ts +++ b/clipboard/src/web.ts @@ -16,12 +16,6 @@ declare global { declare let ClipboardItem: any; export class ClipboardWeb extends WebPlugin implements ClipboardPlugin { - constructor() { - super({ - name: 'Clipboard', - }); - } - async write(options: ClipboardWriteOptions): Promise { if (typeof navigator === 'undefined' || !navigator.clipboard) { throw this.unavailable('Clipboard API not available in this browser'); diff --git a/clipboard/tsconfig.json b/clipboard/tsconfig.json index 28afa0cbe..3bb999d96 100644 --- a/clipboard/tsconfig.json +++ b/clipboard/tsconfig.json @@ -4,7 +4,7 @@ "declaration": true, "esModuleInterop": true, "lib": ["dom"], - "module": "es2015", + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, diff --git a/device/package.json b/device/package.json index bbf9f57b7..952e9c0a5 100644 --- a/device/package.json +++ b/device/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/device", "version": "0.1.1", "description": "The Device API exposes internal information about the device, such as the model and operating system version, along with user information such as unique ids.", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/device/rollup.config.js b/device/rollup.config.js index 0f4826334..119b68367 100644 --- a/device/rollup.config.js +++ b/device/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/device/src/index.ts b/device/src/index.ts index 89633709d..cdb723b87 100644 --- a/device/src/index.ts +++ b/device/src/index.ts @@ -1,15 +1,9 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { DevicePlugin } from './definitions'; -import { DeviceWeb } from './web'; -const implementations: PluginImplementations = { - android: Plugins.Device, - ios: Plugins.Device, - web: new DeviceWeb(), -}; - -const Device = registerPlugin('Device', implementations).getImplementation(); +const Device = registerPlugin('Device', { + web: () => import('./web').then(m => new m.DeviceWeb()), +}); export { Device }; diff --git a/device/src/web.ts b/device/src/web.ts index 30601b50f..0470785b8 100644 --- a/device/src/web.ts +++ b/device/src/web.ts @@ -15,10 +15,6 @@ declare global { } export class DeviceWeb extends WebPlugin implements DevicePlugin { - constructor() { - super({ name: 'Device' }); - } - async getInfo(): Promise { if (typeof navigator === 'undefined' || !navigator.userAgent) { throw this.unavailable('Device API not available in this browser'); @@ -62,15 +58,15 @@ export class DeviceWeb extends WebPlugin implements DevicePlugin { }; } - parseUa(_ua: string): any { + parseUa(ua: string): any { const uaFields: any = {}; - const start = _ua.indexOf('(') + 1; - let end = _ua.indexOf(') AppleWebKit'); - if (_ua.indexOf(') Gecko') !== -1) { - end = _ua.indexOf(') Gecko'); + const start = ua.indexOf('(') + 1; + let end = ua.indexOf(') AppleWebKit'); + if (ua.indexOf(') Gecko') !== -1) { + end = ua.indexOf(') Gecko'); } - const fields = _ua.substring(start, end); - if (_ua.indexOf('Android') !== -1) { + const fields = ua.substring(start, end); + if (ua.indexOf('Android') !== -1) { const tmpFields = fields.replace('; wv', '').split('; ').pop(); if (tmpFields) { uaFields.model = tmpFields.split(' Build')[0]; @@ -81,7 +77,7 @@ export class DeviceWeb extends WebPlugin implements DevicePlugin { if (typeof navigator !== 'undefined' && navigator.oscpu) { uaFields.osVersion = navigator.oscpu; } else { - if (_ua.indexOf('Windows') !== -1) { + if (ua.indexOf('Windows') !== -1) { uaFields.osVersion = fields; } else { const tmpFields = fields.split('; ').pop(); @@ -98,13 +94,13 @@ export class DeviceWeb extends WebPlugin implements DevicePlugin { } } - if (/android/i.test(_ua)) { + if (/android/i.test(ua)) { uaFields.operatingSystem = 'android'; - } else if (/iPad|iPhone|iPod/.test(_ua) && !window.MSStream) { + } else if (/iPad|iPhone|iPod/.test(ua) && !window.MSStream) { uaFields.operatingSystem = 'ios'; - } else if (/Win/.test(_ua)) { + } else if (/Win/.test(ua)) { uaFields.operatingSystem = 'windows'; - } else if (/Mac/i.test(_ua)) { + } else if (/Mac/i.test(ua)) { uaFields.operatingSystem = 'mac'; } else { uaFields.operatingSystem = 'unknown'; @@ -114,14 +110,17 @@ export class DeviceWeb extends WebPlugin implements DevicePlugin { } getUid(): string { - let uid = window.localStorage.getItem('_capuid'); - if (uid) { + if (typeof window !== 'undefined') { + let uid = window.localStorage.getItem('_capuid'); + if (uid) { + return uid; + } + + uid = this.uuid4(); + window.localStorage.setItem('_capuid', uid); return uid; } - - uid = this.uuid4(); - window.localStorage.setItem('_capuid', uid); - return uid; + return this.uuid4(); } uuid4(): string { diff --git a/device/tsconfig.json b/device/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/device/tsconfig.json +++ b/device/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/dialog/package.json b/dialog/package.json index 0815cbfed..d5fb47b4c 100644 --- a/dialog/package.json +++ b/dialog/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/dialog", "version": "0.1.1", "description": "The Dialog API provides methods for triggering native dialog windows for alerts, confirmations, and input prompts", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/dialog/rollup.config.js b/dialog/rollup.config.js index 32ad074e9..a06ce33d8 100644 --- a/dialog/rollup.config.js +++ b/dialog/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/dialog/src/index.ts b/dialog/src/index.ts index 8bd3f0739..07d8919fb 100644 --- a/dialog/src/index.ts +++ b/dialog/src/index.ts @@ -1,15 +1,9 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { DialogPlugin } from './definitions'; -import { DialogWeb } from './web'; -const implementations: PluginImplementations = { - android: Plugins.Dialog, - ios: Plugins.Dialog, - web: new DialogWeb(), -}; - -const Dialog = registerPlugin('Dialog', implementations).getImplementation(); +const Dialog = registerPlugin('Dialog', { + web: () => import('./web').then(m => new m.DialogWeb()), +}); export { Dialog }; diff --git a/dialog/src/web.ts b/dialog/src/web.ts index b19b5f464..accbb6ea5 100644 --- a/dialog/src/web.ts +++ b/dialog/src/web.ts @@ -10,10 +10,6 @@ import type { } from './definitions'; export class DialogWeb extends WebPlugin implements DialogPlugin { - constructor() { - super({ name: 'Dialog' }); - } - async alert(options: AlertOptions): Promise { window.alert(options.message); } diff --git a/dialog/tsconfig.json b/dialog/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/dialog/tsconfig.json +++ b/dialog/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/haptics/package.json b/haptics/package.json index aa0db1086..d51ed9482 100644 --- a/haptics/package.json +++ b/haptics/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/haptics", "version": "0.0.3", "description": "The Haptics API provides physical feedback to the user through touch or vibration.", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/haptics/rollup.config.js b/haptics/rollup.config.js index 4b48f9b36..5ba497b3a 100644 --- a/haptics/rollup.config.js +++ b/haptics/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/haptics/src/index.ts b/haptics/src/index.ts index 031eeb84e..d4f93d4e0 100644 --- a/haptics/src/index.ts +++ b/haptics/src/index.ts @@ -1,16 +1,10 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { HapticsPlugin } from './definitions'; import { HapticsImpactStyle, HapticsNotificationType } from './definitions'; -import { HapticsWeb } from './web'; -const implementations: PluginImplementations = { - android: Plugins.Haptics, - ios: Plugins.Haptics, - web: new HapticsWeb(), -}; - -const Haptics = registerPlugin('Haptics', implementations).getImplementation(); +const Haptics = registerPlugin('Haptics', { + web: () => import('./web').then(m => new m.HapticsWeb()), +}); export { Haptics, HapticsImpactStyle, HapticsNotificationType }; diff --git a/haptics/src/web.ts b/haptics/src/web.ts index 04599799b..58c2aad75 100644 --- a/haptics/src/web.ts +++ b/haptics/src/web.ts @@ -9,10 +9,6 @@ import type { import { HapticsImpactStyle, HapticsNotificationType } from './definitions'; export class HapticsWeb extends WebPlugin implements HapticsPlugin { - constructor() { - super({ name: 'Haptics' }); - } - selectionStarted = false; async impact(options?: HapticsImpactOptions): Promise { diff --git a/haptics/tsconfig.json b/haptics/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/haptics/tsconfig.json +++ b/haptics/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/keyboard/package.json b/keyboard/package.json index a6c81e38f..b820048c2 100644 --- a/keyboard/package.json +++ b/keyboard/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/keyboard", "version": "0.1.0", "description": "The Keyboard API provides keyboard display and visibility control, along with event tracking when the keyboard shows and hides.", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/keyboard/rollup.config.js b/keyboard/rollup.config.js index bfa200b37..39ad037ab 100644 --- a/keyboard/rollup.config.js +++ b/keyboard/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/keyboard/src/index.ts b/keyboard/src/index.ts index 26d0b4283..80d6f9d59 100644 --- a/keyboard/src/index.ts +++ b/keyboard/src/index.ts @@ -1,17 +1,8 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { KeyboardPlugin } from './definitions'; import { KeyboardResize, KeyboardStyle } from './definitions'; -const implementations: PluginImplementations = { - android: Plugins.Keyboard, - ios: Plugins.Keyboard, -}; - -const Keyboard = registerPlugin( - 'Keyboard', - implementations, -).getImplementation(); +const Keyboard = registerPlugin('Keyboard'); export { Keyboard, KeyboardResize, KeyboardStyle }; diff --git a/keyboard/tsconfig.json b/keyboard/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/keyboard/tsconfig.json +++ b/keyboard/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/motion/package.json b/motion/package.json index d3ad18752..d23343918 100644 --- a/motion/package.json +++ b/motion/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/motion", "version": "0.0.3", "description": "The Motion API tracks accelerometer and device orientation (compass heading, etc.)", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "dist/" ], @@ -41,7 +41,6 @@ "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/motion/rollup.config.js b/motion/rollup.config.js index 73de8ed69..498a39962 100644 --- a/motion/rollup.config.js +++ b/motion/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/motion/src/index.ts b/motion/src/index.ts index 2baa4ed76..804fc2623 100644 --- a/motion/src/index.ts +++ b/motion/src/index.ts @@ -1,17 +1,11 @@ -import type { PluginImplementations } from '@capacitor/core'; import { registerPlugin } from '@capacitor/core'; import type { MotionPlugin } from './definitions'; -import { MotionWeb } from './web'; -const plugin = new MotionWeb(); - -const implementations: PluginImplementations = { - android: plugin, - ios: plugin, - web: plugin, -}; - -const Motion = registerPlugin('Motion', implementations).getImplementation(); +const Motion = registerPlugin('Motion', { + android: () => import('./web').then(m => new m.MotionWeb()), + ios: () => import('./web').then(m => new m.MotionWeb()), + web: () => import('./web').then(m => new m.MotionWeb()), +}); export { Motion }; diff --git a/motion/src/web.ts b/motion/src/web.ts index c879c2197..51fa1fb08 100644 --- a/motion/src/web.ts +++ b/motion/src/web.ts @@ -4,7 +4,7 @@ import type { MotionPlugin } from './definitions'; export class MotionWeb extends WebPlugin implements MotionPlugin { constructor() { - super({ name: 'Motion' }); + super(); this.registerWindowListener('devicemotion', 'accel'); this.registerWindowListener('deviceorientation', 'orientation'); } diff --git a/motion/tsconfig.json b/motion/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/motion/tsconfig.json +++ b/motion/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/network/package.json b/network/package.json index 58a2036a1..fb495f5cb 100644 --- a/network/package.json +++ b/network/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/network", "version": "0.1.1", "description": "The Network API provides network and connectivity information.", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/network/rollup.config.js b/network/rollup.config.js index 86d93fdf7..2cb53fd62 100644 --- a/network/rollup.config.js +++ b/network/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/network/src/index.ts b/network/src/index.ts index dc508056d..b3880d24b 100644 --- a/network/src/index.ts +++ b/network/src/index.ts @@ -1,16 +1,10 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { NetworkPlugin } from './definitions'; import { NetworkStatus } from './definitions'; -import { NetworkWeb } from './web'; -const implementations: PluginImplementations = { - android: Plugins.Network, - ios: Plugins.Network, - web: new NetworkWeb(), -}; - -const Network = registerPlugin('Network', implementations).getImplementation(); +const Network = registerPlugin('Network', { + web: () => import('./web').then(m => new m.NetworkWeb()), +}); export { Network, NetworkStatus }; diff --git a/network/src/web.ts b/network/src/web.ts index 8d1f90f0e..2b5b390dc 100644 --- a/network/src/web.ts +++ b/network/src/web.ts @@ -58,10 +58,6 @@ function translatedConnection(): NetworkStatusConnectionType { } export class NetworkWeb extends WebPlugin implements NetworkPlugin { - constructor() { - super({ name: 'Network' }); - } - async getStatus(): Promise { if (!window.navigator) { throw this.unavailable( diff --git a/network/tsconfig.json b/network/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/network/tsconfig.json +++ b/network/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/screen-reader/package.json b/screen-reader/package.json index 8cb14a585..17df1819a 100644 --- a/screen-reader/package.json +++ b/screen-reader/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/screen-reader", "version": "0.0.3", "description": "The Screen Reader API provides access to TalkBack/VoiceOver/etc. and provides simple text-to-speech capabilities for visual accessibility.", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/screen-reader/rollup.config.js b/screen-reader/rollup.config.js index b89cb0452..68674e1e1 100644 --- a/screen-reader/rollup.config.js +++ b/screen-reader/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/screen-reader/src/index.ts b/screen-reader/src/index.ts index a2fa249e6..41a2f5960 100644 --- a/screen-reader/src/index.ts +++ b/screen-reader/src/index.ts @@ -1,18 +1,9 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { ScreenReaderPlugin } from './definitions'; -import { ScreenReaderWeb } from './web'; -const implementations: PluginImplementations = { - android: Plugins.ScreenReader, - ios: Plugins.ScreenReader, - web: new ScreenReaderWeb(), -}; - -const ScreenReader = registerPlugin( - 'ScreenReader', - implementations, -).getImplementation(); +const ScreenReader = registerPlugin('ScreenReader', { + web: () => import('./web').then(m => new m.ScreenReaderWeb()), +}); export { ScreenReader }; diff --git a/screen-reader/src/web.ts b/screen-reader/src/web.ts index 8b17d30c6..eb53ae580 100644 --- a/screen-reader/src/web.ts +++ b/screen-reader/src/web.ts @@ -6,10 +6,6 @@ import type { } from './definitions'; export class ScreenReaderWeb extends WebPlugin implements ScreenReaderPlugin { - constructor() { - super({ name: 'ScreenReader' }); - } - async isEnabled(): Promise { throw this.unavailable('This feature is not available in the browser.'); } diff --git a/screen-reader/tsconfig.json b/screen-reader/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/screen-reader/tsconfig.json +++ b/screen-reader/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/share/package.json b/share/package.json index 394a223a9..c59ac9f03 100644 --- a/share/package.json +++ b/share/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/share", "version": "0.1.1", "description": "The Share API provides methods for sharing content in any sharing-enabled apps the user may have installed.", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/share/rollup.config.js b/share/rollup.config.js index 0005c016e..98fe8d2e6 100644 --- a/share/rollup.config.js +++ b/share/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/share/src/index.ts b/share/src/index.ts index 39eba1d3e..a04f92737 100644 --- a/share/src/index.ts +++ b/share/src/index.ts @@ -1,15 +1,9 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { SharePlugin } from './definitions'; -import { ShareWeb } from './web'; -const implementations: PluginImplementations = { - android: Plugins.Share, - ios: Plugins.Share, - web: new ShareWeb(), -}; - -const Share = registerPlugin('Share', implementations).getImplementation(); +const Share = registerPlugin('Share', { + web: () => import('./web').then(m => new m.ShareWeb()), +}); export { Share }; diff --git a/share/src/web.ts b/share/src/web.ts index 4bc150e40..e38298743 100644 --- a/share/src/web.ts +++ b/share/src/web.ts @@ -3,10 +3,6 @@ import { WebPlugin } from '@capacitor/core'; import type { ShareOptions, SharePlugin, ShareResult } from './definitions'; export class ShareWeb extends WebPlugin implements SharePlugin { - constructor() { - super({ name: 'Share' }); - } - async share(options: ShareOptions): Promise { if (typeof navigator === 'undefined' || !navigator.share) { throw this.unavailable('Share API not available in this browser'); diff --git a/share/tsconfig.json b/share/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/share/tsconfig.json +++ b/share/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/status-bar/package.json b/status-bar/package.json index 17f5a559b..5b3d126c6 100644 --- a/status-bar/package.json +++ b/status-bar/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/status-bar", "version": "0.1.0", "description": "The StatusBar API Provides methods for configuring the style of the Status Bar, along with showing or hiding it.", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/status-bar/rollup.config.js b/status-bar/rollup.config.js index fef10d602..f972075b8 100644 --- a/status-bar/rollup.config.js +++ b/status-bar/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/status-bar/src/index.ts b/status-bar/src/index.ts index 0b6e64e65..c96aaad7a 100644 --- a/status-bar/src/index.ts +++ b/status-bar/src/index.ts @@ -1,17 +1,8 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { StatusBarPlugin } from './definitions'; import { StatusBarStyle } from './definitions'; -const implementations: PluginImplementations = { - android: Plugins.StatusBar, - ios: Plugins.StatusBar, -}; - -const StatusBar = registerPlugin( - 'StatusBar', - implementations, -).getImplementation(); +const StatusBar = registerPlugin('StatusBar'); export { StatusBar, StatusBarStyle }; diff --git a/status-bar/tsconfig.json b/status-bar/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/status-bar/tsconfig.json +++ b/status-bar/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/storage/package.json b/storage/package.json index bae842777..df50008b2 100644 --- a/storage/package.json +++ b/storage/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/storage", "version": "0.0.3", "description": "The Storage API provides a simple key/value persistent store for lightweight data.", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/storage/rollup.config.js b/storage/rollup.config.js index ee56b60ea..d7225e1d1 100644 --- a/storage/rollup.config.js +++ b/storage/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/storage/src/index.ts b/storage/src/index.ts index 42bea24ef..fd6a0d7de 100644 --- a/storage/src/index.ts +++ b/storage/src/index.ts @@ -1,15 +1,9 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { StoragePlugin } from './definitions'; -import { StorageWeb } from './web'; -const implementations: PluginImplementations = { - android: Plugins.Storage, - ios: Plugins.Storage, - web: new StorageWeb(), -}; - -const Storage = registerPlugin('Storage', implementations).getImplementation(); +const Storage = registerPlugin('Storage', { + web: () => import('./web').then(m => new m.StorageWeb()), +}); export { Storage }; diff --git a/storage/src/web.ts b/storage/src/web.ts index 767f04945..9d7dda6f8 100644 --- a/storage/src/web.ts +++ b/storage/src/web.ts @@ -14,10 +14,6 @@ import type { export class StorageWeb extends WebPlugin implements StoragePlugin { private group = 'CapacitorStorage'; - constructor() { - super({ name: 'Storage' }); - } - public async configure({ group }: ConfigureOptions): Promise { if (typeof group === 'string') { this.group = group; diff --git a/storage/tsconfig.json b/storage/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/storage/tsconfig.json +++ b/storage/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/text-zoom/package.json b/text-zoom/package.json index 0df9aeee3..03f96eb9d 100644 --- a/text-zoom/package.json +++ b/text-zoom/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/text-zoom", "version": "0.0.3", "description": "The Text Zoom API provides the ability to change Web View text size for visual accessibility.", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/text-zoom/rollup.config.js b/text-zoom/rollup.config.js index 81e9df7ba..4ce2a0966 100644 --- a/text-zoom/rollup.config.js +++ b/text-zoom/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/text-zoom/src/index.ts b/text-zoom/src/index.ts index 0d96ac77e..05a530587 100644 --- a/text-zoom/src/index.ts +++ b/text-zoom/src/index.ts @@ -1,17 +1,9 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { TextZoomPlugin } from './definitions'; -import { TextZoomIOS } from './ios'; -const implementations: PluginImplementations = { - android: Plugins.TextZoom, - ios: new TextZoomIOS(), -}; - -const TextZoom = registerPlugin( - 'TextZoom', - implementations, -).getImplementation(); +const TextZoom = registerPlugin('TextZoom', { + ios: import('./ios').then(m => new m.TextZoomIOS()), +}); export { TextZoom }; diff --git a/text-zoom/src/ios.ts b/text-zoom/src/ios.ts index cbae0d0b0..2a8851c1d 100644 --- a/text-zoom/src/ios.ts +++ b/text-zoom/src/ios.ts @@ -22,11 +22,16 @@ export class TextZoomIOS implements TextZoomPlugin { } getRaw(): string { - return document.body.style.webkitTextSizeAdjust || '100%'; + if (typeof document !== 'undefined') { + return document.body.style.webkitTextSizeAdjust || '100%'; + } + return '100%'; } setRaw(value: string): void { - document.body.style.webkitTextSizeAdjust = value; + if (typeof document !== 'undefined') { + document.body.style.webkitTextSizeAdjust = value; + } } textSizePercentageToNumber(percentage: string): number { diff --git a/text-zoom/tsconfig.json b/text-zoom/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/text-zoom/tsconfig.json +++ b/text-zoom/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } diff --git a/toast/package.json b/toast/package.json index 24e2e12e4..01d2a9b22 100644 --- a/toast/package.json +++ b/toast/package.json @@ -2,9 +2,9 @@ "name": "@capacitor/toast", "version": "0.1.1", "description": "The Toast API provides a notification pop up for displaying important information to a user. Just like real toast!", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", "files": [ "android/src/main/", "android/build.gradle", @@ -50,7 +50,6 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", "prettier": "~2.2.0", "prettier-plugin-java": "~1.0.0", diff --git a/toast/rollup.config.js b/toast/rollup.config.js index 649ec8574..1ab9be4e9 100644 --- a/toast/rollup.config.js +++ b/toast/rollup.config.js @@ -1,5 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { @@ -10,12 +8,7 @@ export default { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/toast/src/index.ts b/toast/src/index.ts index c9926fea1..76bc2da60 100644 --- a/toast/src/index.ts +++ b/toast/src/index.ts @@ -1,15 +1,9 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { ToastPlugin } from './definitions'; -import { ToastWeb } from './web'; -const implementations: PluginImplementations = { - android: Plugins.Toast, - ios: Plugins.Toast, - web: new ToastWeb(), -}; - -const Toast = registerPlugin('Toast', implementations).getImplementation(); +const Toast = registerPlugin('Toast', { + web: () => import('./web').then(m => new m.ToastWeb()), +}); export { Toast }; diff --git a/toast/src/web.ts b/toast/src/web.ts index 3fe89dff0..8e42bc9c8 100644 --- a/toast/src/web.ts +++ b/toast/src/web.ts @@ -3,18 +3,16 @@ import { WebPlugin } from '@capacitor/core'; import type { ToastPlugin, ToastShowOptions } from './definitions'; export class ToastWeb extends WebPlugin implements ToastPlugin { - constructor() { - super({ name: 'Toast' }); - } - async show(options: ToastShowOptions): Promise { - let duration = 2000; - if (options.duration) { - duration = options.duration === 'long' ? 3500 : 2000; + if (typeof document !== 'undefined') { + let duration = 2000; + if (options.duration) { + duration = options.duration === 'long' ? 3500 : 2000; + } + const toast = document.createElement('pwa-toast') as any; + toast.duration = duration; + toast.message = options.text; + document.body.appendChild(toast); } - const toast = document.createElement('pwa-toast') as any; - toast.duration = duration; - toast.message = options.text; - document.body.appendChild(toast); } } diff --git a/toast/tsconfig.json b/toast/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/toast/tsconfig.json +++ b/toast/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } From eb92b74b9c21ce582d4cee5d0e7707f75b1f66b2 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Tue, 24 Nov 2020 13:31:55 -0800 Subject: [PATCH 18/60] registerPlugin --- local-notifications/package.json | 31 ++++++++++++++++++---------- local-notifications/rollup.config.js | 13 +++--------- local-notifications/src/index.ts | 18 ++++++---------- local-notifications/tsconfig.json | 10 +++------ 4 files changed, 32 insertions(+), 40 deletions(-) diff --git a/local-notifications/package.json b/local-notifications/package.json index 65c8ccdd2..55612e391 100644 --- a/local-notifications/package.json +++ b/local-notifications/package.json @@ -2,9 +2,16 @@ "name": "@capacitor/local-notifications", "version": "0.0.1", "description": "The Notifications API provides access to native local notifications.", - "main": "dist/plugin.js", - "module": "dist/esm/index.js", + "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", + "files": [ + "android/src/main/", + "android/build.gradle", + "dist/", + "ios/Plugin/", + "CapacitorLocalNotifications.podspec" + ], "author": "Ionic ", "license": "MIT", "repository": { @@ -36,19 +43,18 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.6", - "@capacitor/core": "^3.0.0-alpha.6", + "@capacitor/android": "file:../../capacitor/android", + "@capacitor/core": "file:../../capacitor/core", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.6", + "@capacitor/ios": "file:../../capacitor/ios", "@ionic/eslint-config": "^0.3.0", - "@ionic/prettier-config": "^1.0.1", + "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@rollup/plugin-node-resolve": "^9.0.0", "eslint": "^7.11.0", - "prettier": "^2.1.2", - "prettier-plugin-java": "^0.8.3", - "rimraf": "^3.0.2", - "rollup": "^2.32.0", + "prettier": "~2.2.0", + "prettier-plugin-java": "~1.0.0", + "rimraf": "^3.0.0", + "rollup": "^2.29.0", "swiftlint": "^1.0.1", "typescript": "~4.0.3" }, @@ -67,5 +73,8 @@ "android": { "src": "android" } + }, + "publishConfig": { + "access": "public" } } diff --git a/local-notifications/rollup.config.js b/local-notifications/rollup.config.js index d58bfbea1..f262b11ca 100644 --- a/local-notifications/rollup.config.js +++ b/local-notifications/rollup.config.js @@ -1,21 +1,14 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; - export default { input: 'dist/esm/index.js', output: { file: 'dist/plugin.js', format: 'iife', - name: 'capacitorPlugin', // TODO: change this + name: 'capacitorLocalNotifications', globals: { '@capacitor/core': 'capacitorExports', }, sourcemap: true, + inlineDynamicImports: true, }, - plugins: [ - nodeResolve({ - // allowlist of dependencies to bundle in - // @see https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve#resolveonly - resolveOnly: ['lodash'], - }), - ], + external: ['@capacitor/core'], }; diff --git a/local-notifications/src/index.ts b/local-notifications/src/index.ts index 643390c70..8b7e8af51 100644 --- a/local-notifications/src/index.ts +++ b/local-notifications/src/index.ts @@ -1,5 +1,4 @@ -import type { PluginImplementations } from '@capacitor/core'; -import { Plugins, registerPlugin } from '@capacitor/core'; +import { registerPlugin } from '@capacitor/core'; import type { LocalNotification, @@ -18,18 +17,13 @@ import type { NotificationChannel, NotificationChannelList, } from './definitions'; -import { LocalNotificationsWeb } from './web'; -const implementations: PluginImplementations = { - android: Plugins.LocalNotifications, - ios: Plugins.LocalNotifications, - web: new LocalNotificationsWeb(), -}; - -const LocalNotifications = registerPlugin( +const LocalNotifications = registerPlugin( 'LocalNotifications', - implementations, -).getImplementation(); + { + web: () => import('./web').then(m => new m.LocalNotificationsWeb()), + }, +); export { LocalNotification, diff --git a/local-notifications/tsconfig.json b/local-notifications/tsconfig.json index 538e088fd..3bb999d96 100644 --- a/local-notifications/tsconfig.json +++ b/local-notifications/tsconfig.json @@ -3,10 +3,8 @@ "allowUnreachableCode": false, "declaration": true, "esModuleInterop": true, - "lib": [ - "dom" - ], - "module": "es2015", + "lib": ["dom"], + "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -17,7 +15,5 @@ "strict": true, "target": "es2017" }, - "files": [ - "src/index.ts" - ] + "files": ["src/index.ts"] } From 3833819e16559d83e797ab5000cd02e7345224db Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Tue, 24 Nov 2020 13:53:15 -0800 Subject: [PATCH 19/60] use new permission annotation --- .../java/com/capacitorjs/plugins/network/NetworkPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/android/src/main/java/com/capacitorjs/plugins/network/NetworkPlugin.java b/network/android/src/main/java/com/capacitorjs/plugins/network/NetworkPlugin.java index 4ec0c54a6..5af658047 100644 --- a/network/android/src/main/java/com/capacitorjs/plugins/network/NetworkPlugin.java +++ b/network/android/src/main/java/com/capacitorjs/plugins/network/NetworkPlugin.java @@ -10,7 +10,7 @@ import com.getcapacitor.annotation.CapacitorPlugin; import com.getcapacitor.annotation.Permission; -@CapacitorPlugin(name = "Network", permissions = { @Permission(permission = Manifest.permission.ACCESS_NETWORK_STATE) }) +@CapacitorPlugin(name = "Network", permissions = { @Permission(strings = { Manifest.permission.ACCESS_NETWORK_STATE }) }) public class NetworkPlugin extends Plugin { private Network implementation; From ac7478ae86247b974fe5caf50d1ea7d82b530d36 Mon Sep 17 00:00:00 2001 From: Dan Imhoff Date: Tue, 24 Nov 2020 13:58:37 -0800 Subject: [PATCH 20/60] refactor(android): use new permission annotation (#113) --- .../java/com/capacitorjs/plugins/network/NetworkPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/android/src/main/java/com/capacitorjs/plugins/network/NetworkPlugin.java b/network/android/src/main/java/com/capacitorjs/plugins/network/NetworkPlugin.java index 4ec0c54a6..5af658047 100644 --- a/network/android/src/main/java/com/capacitorjs/plugins/network/NetworkPlugin.java +++ b/network/android/src/main/java/com/capacitorjs/plugins/network/NetworkPlugin.java @@ -10,7 +10,7 @@ import com.getcapacitor.annotation.CapacitorPlugin; import com.getcapacitor.annotation.Permission; -@CapacitorPlugin(name = "Network", permissions = { @Permission(permission = Manifest.permission.ACCESS_NETWORK_STATE) }) +@CapacitorPlugin(name = "Network", permissions = { @Permission(strings = { Manifest.permission.ACCESS_NETWORK_STATE }) }) public class NetworkPlugin extends Plugin { private Network implementation; From 84c4403fb204d94f48b728eae207b38f4947c1aa Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Tue, 24 Nov 2020 18:44:02 -0600 Subject: [PATCH 21/60] refactor(android): updated share plugin handleOnActivityResult signature (#111) --- .../main/java/com/capacitorjs/plugins/share/SharePlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/android/src/main/java/com/capacitorjs/plugins/share/SharePlugin.java b/share/android/src/main/java/com/capacitorjs/plugins/share/SharePlugin.java index 565c96761..07732980f 100644 --- a/share/android/src/main/java/com/capacitorjs/plugins/share/SharePlugin.java +++ b/share/android/src/main/java/com/capacitorjs/plugins/share/SharePlugin.java @@ -104,11 +104,11 @@ public void share(PluginCall call) { } @Override - protected void handleOnActivityResult(int requestCode, int resultCode, Intent data) { - PluginCall savedCall = getSavedCall(); + protected void handleOnActivityResult(PluginCall savedCall, int requestCode, int resultCode, Intent data) { if (savedCall == null) { return; } + if (resultCode == Activity.RESULT_CANCELED && !stopped) { savedCall.reject("Share canceled"); } else { From de8e45e35d1e05f77a75384b495613983855047b Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 25 Nov 2020 10:06:01 -0800 Subject: [PATCH 22/60] java permission updates --- .../LocalNotificationsPlugin.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java index cdbea3b11..8936c5d93 100644 --- a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java @@ -7,11 +7,12 @@ import com.getcapacitor.PluginCall; import com.getcapacitor.PluginMethod; import com.getcapacitor.annotation.CapacitorPlugin; +import com.getcapacitor.annotation.Permission; import java.util.List; import java.util.Map; import org.json.JSONArray; -@CapacitorPlugin(name = "LocalNotifications") +@CapacitorPlugin(name = "LocalNotifications", permissions = @Permission(strings = {}, alias = "display")) public class LocalNotificationsPlugin extends Plugin { private LocalNotificationManager manager; @@ -40,8 +41,8 @@ protected void handleOnNewIntent(Intent data) { } @Override - protected void handleOnActivityResult(int requestCode, int resultCode, Intent data) { - super.handleOnActivityResult(requestCode, resultCode, data); + protected void handleOnActivityResult(PluginCall savedCall, int requestCode, int resultCode, Intent data) { + super.handleOnActivityResult(savedCall, requestCode, resultCode, data); this.handleOnNewIntent(data); } @@ -71,13 +72,6 @@ public void schedule(PluginCall call) { } } - @Override - public JSObject getPermissionStates() { - JSObject result = new JSObject(); - result.put("display", "granted"); - return result; - } - @PluginMethod public void cancel(PluginCall call) { manager.cancel(call); From 9375aa7fb7479b8bc7eb927045a9255d009b5bcb Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 25 Nov 2020 10:07:42 -0800 Subject: [PATCH 23/60] ios delegate updates --- .../ios/Plugin/LocalNotificationsPlugin.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift index 3294da276..d42a74c5e 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift @@ -28,7 +28,7 @@ public class LocalNotificationsPlugin: CAPPlugin { private let notificationDelegationHandler = LocalNotificationsDelegate() override public func load() { - self.bridge?.userNotificationDelegate.localNotificationHandler = self.notificationDelegationHandler + self.bridge?.notificationRouter.localNotificationHandler = self.notificationDelegationHandler self.notificationDelegationHandler.plugin = self } @@ -99,7 +99,7 @@ public class LocalNotificationsPlugin: CAPPlugin { /** * Request notification permission */ - @objc func requestPermissions(_ call: CAPPluginCall) { + @objc override public func requestPermissions(_ call: CAPPluginCall) { self.notificationDelegationHandler.requestPermissions { granted, error in guard error == nil else { call.error(error!.localizedDescription) @@ -109,7 +109,7 @@ public class LocalNotificationsPlugin: CAPPlugin { } } - @objc func checkPermissions(_ call: CAPPluginCall) { + @objc override public func checkPermissions(_ call: CAPPluginCall) { call.success(["display": "prompt"]) // TODO } From f7a9a2e8747ddc83c33326f0c7daf7ec7136adb2 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 2 Dec 2020 14:38:38 -0800 Subject: [PATCH 24/60] toggle-local --- action-sheet/package.json | 6 +++--- app-launcher/package.json | 6 +++--- app/package.json | 6 +++--- browser/package.json | 6 +++--- clipboard/package.json | 6 +++--- device/package.json | 6 +++--- dialog/package.json | 6 +++--- haptics/package.json | 6 +++--- keyboard/package.json | 6 +++--- local-notifications/package.json | 6 +++--- motion/package.json | 6 +++--- network/package.json | 6 +++--- screen-reader/package.json | 6 +++--- share/package.json | 6 +++--- status-bar/package.json | 6 +++--- storage/package.json | 6 +++--- text-zoom/package.json | 6 +++--- toast/package.json | 6 +++--- 18 files changed, 54 insertions(+), 54 deletions(-) diff --git a/action-sheet/package.json b/action-sheet/package.json index a9f04e993..b5e050caa 100644 --- a/action-sheet/package.json +++ b/action-sheet/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/app-launcher/package.json b/app-launcher/package.json index f437dde00..8f8b7c3cd 100644 --- a/app-launcher/package.json +++ b/app-launcher/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/app/package.json b/app/package.json index d9aab6199..bd5a44350 100644 --- a/app/package.json +++ b/app/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/browser/package.json b/browser/package.json index ed8f9ac89..3478ba557 100644 --- a/browser/package.json +++ b/browser/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/clipboard/package.json b/clipboard/package.json index 2dd5b0c12..2128b3fd7 100644 --- a/clipboard/package.json +++ b/clipboard/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/device/package.json b/device/package.json index 952e9c0a5..e54be4cdd 100644 --- a/device/package.json +++ b/device/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/dialog/package.json b/dialog/package.json index d5fb47b4c..a3ce1c3c5 100644 --- a/dialog/package.json +++ b/dialog/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/haptics/package.json b/haptics/package.json index d51ed9482..46b1c54f8 100644 --- a/haptics/package.json +++ b/haptics/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/keyboard/package.json b/keyboard/package.json index b820048c2..4539d6c86 100644 --- a/keyboard/package.json +++ b/keyboard/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/local-notifications/package.json b/local-notifications/package.json index 55612e391..9679955da 100644 --- a/local-notifications/package.json +++ b/local-notifications/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/motion/package.json b/motion/package.json index d23343918..57b0f2e10 100644 --- a/motion/package.json +++ b/motion/package.json @@ -35,10 +35,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "eslint": "^7.11.0", diff --git a/network/package.json b/network/package.json index fb495f5cb..fc03b4728 100644 --- a/network/package.json +++ b/network/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/screen-reader/package.json b/screen-reader/package.json index 17df1819a..b5b0f9b51 100644 --- a/screen-reader/package.json +++ b/screen-reader/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/share/package.json b/share/package.json index c59ac9f03..17f302477 100644 --- a/share/package.json +++ b/share/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/status-bar/package.json b/status-bar/package.json index 5b3d126c6..27ed01d41 100644 --- a/status-bar/package.json +++ b/status-bar/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/storage/package.json b/storage/package.json index df50008b2..75a27b12c 100644 --- a/storage/package.json +++ b/storage/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/text-zoom/package.json b/text-zoom/package.json index 03f96eb9d..af38da11e 100644 --- a/text-zoom/package.json +++ b/text-zoom/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", diff --git a/toast/package.json b/toast/package.json index 01d2a9b22..c7752c599 100644 --- a/toast/package.json +++ b/toast/package.json @@ -43,10 +43,10 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "file:../../capacitor/android", - "@capacitor/core": "file:../../capacitor/core", + "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "file:../../capacitor/ios", + "@capacitor/ios": "^3.0.0-alpha.7", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", From a267d1ffd295c8a9ddb9792c2ea1192ef455bbbb Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 2 Dec 2020 14:48:26 -0800 Subject: [PATCH 25/60] build docs --- local-notifications/README.md | 395 +++++++++++++++++++++++++++++++++- 1 file changed, 392 insertions(+), 3 deletions(-) diff --git a/local-notifications/README.md b/local-notifications/README.md index 3ff54ab24..300393355 100644 --- a/local-notifications/README.md +++ b/local-notifications/README.md @@ -11,9 +11,398 @@ npx cap sync ## API - + + +* [`schedule(...)`](#schedule) +* [`getPending()`](#getpending) +* [`registerActionTypes(...)`](#registeractiontypes) +* [`cancel(...)`](#cancel) +* [`areEnabled()`](#areenabled) +* [`createChannel(...)`](#createchannel) +* [`deleteChannel(...)`](#deletechannel) +* [`listChannels()`](#listchannels) +* [`checkPermissions()`](#checkpermissions) +* [`requestPermissions()`](#requestpermissions) +* [`addListener(...)`](#addlistener) +* [`addListener(...)`](#addlistener) +* [`removeAllListeners()`](#removealllisteners) +* [Interfaces](#interfaces) + + - - + + +### schedule(...) + +```typescript +schedule(options: { notifications: LocalNotification[]; }) => Promise +``` + +| Param | Type | +| ------------- | ---------------------------------------------------- | +| **`options`** | { notifications: LocalNotification[]; } | + +**Returns:** Promise<LocalNotificationPendingList> + +-------------------- + + +### getPending() + +```typescript +getPending() => Promise +``` + +**Returns:** Promise<LocalNotificationPendingList> + +-------------------- + + +### registerActionTypes(...) + +```typescript +registerActionTypes(options: { types: LocalNotificationActionType[]; }) => Promise +``` + +| Param | Type | +| ------------- | ------------------------------------------------------ | +| **`options`** | { types: LocalNotificationActionType[]; } | + +-------------------- + + +### cancel(...) + +```typescript +cancel(pending: LocalNotificationPendingList) => Promise +``` + +| Param | Type | +| ------------- | ------------------------------------------------------------------------------------- | +| **`pending`** | LocalNotificationPendingList | + +-------------------- + + +### areEnabled() + +```typescript +areEnabled() => Promise +``` + +**Returns:** Promise<LocalNotificationEnabledResult> + +-------------------- + + +### createChannel(...) + +```typescript +createChannel(channel: NotificationChannel) => Promise +``` + +| Param | Type | +| ------------- | ------------------------------------------------------------------- | +| **`channel`** | NotificationChannel | + +-------------------- + + +### deleteChannel(...) + +```typescript +deleteChannel(channel: NotificationChannel) => Promise +``` + +| Param | Type | +| ------------- | ------------------------------------------------------------------- | +| **`channel`** | NotificationChannel | + +-------------------- + + +### listChannels() + +```typescript +listChannels() => Promise +``` + +**Returns:** Promise<NotificationChannelList> + +-------------------- + + +### checkPermissions() + +```typescript +checkPermissions() => Promise +``` + +Check notification permissions + +**Returns:** Promise<LocalNotificationsPermissionStatus> + +**Since:** 1.0.0 + +-------------------- + + +### requestPermissions() + +```typescript +requestPermissions() => Promise +``` + +Request location permissions + +**Returns:** Promise<LocalNotificationsPermissionStatus> + +**Since:** 1.0.0 + +-------------------- + + +### addListener(...) + +```typescript +addListener(eventName: 'localNotificationReceived', listenerFunc: (notification: LocalNotification) => void) => PluginListenerHandle +``` + +| Param | Type | +| ------------------ | ------------------------------------------------------------------------------------------ | +| **`eventName`** | "localNotificationReceived" | +| **`listenerFunc`** | (notification: LocalNotification) => void | + +**Returns:** PluginListenerHandle + +-------------------- + + +### addListener(...) + +```typescript +addListener(eventName: 'localNotificationActionPerformed', listenerFunc: (notificationAction: LocalNotificationActionPerformed) => void) => PluginListenerHandle +``` + +| Param | Type | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------ | +| **`eventName`** | "localNotificationActionPerformed" | +| **`listenerFunc`** | (notificationAction: LocalNotificationActionPerformed) => void | + +**Returns:** PluginListenerHandle + +-------------------- + + +### removeAllListeners() + +```typescript +removeAllListeners() => void +``` + +Remove all native listeners for this plugin + +-------------------- + + +### Interfaces + + +#### LocalNotificationPendingList + +| Prop | Type | +| ------------------- | --------------------------------------- | +| **`notifications`** | LocalNotificationRequest[] | + + +#### LocalNotificationRequest + +| Prop | Type | +| -------- | ------------------- | +| **`id`** | string | + + +#### LocalNotification + +| Prop | Type | Description | +| ---------------------- | ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **`title`** | string | | +| **`body`** | string | | +| **`id`** | number | | +| **`schedule`** | LocalNotificationSchedule | | +| **`sound`** | string | Name of the audio file with extension. On iOS the file should be in the app bundle. On Android the file should be on res/raw folder. Doesn't work on Android version 26+ (Android O and newer), for Recommended format is .wav because is supported by both platforms. | +| **`smallIcon`** | string | Android-only: set a custom statusbar icon. If set, it overrides default icon from capacitor.config.json | +| **`iconColor`** | string | Android only: set the color of the notification icon | +| **`attachments`** | LocalNotificationAttachment[] | | +| **`actionTypeId`** | string | | +| **`extra`** | any | | +| **`threadIdentifier`** | string | iOS only: set the thread identifier for notification grouping | +| **`summaryArgument`** | string | iOS 12+ only: set the summary argument for notification grouping | +| **`group`** | string | Android only: set the group identifier for notification grouping, like threadIdentifier on iOS. | +| **`groupSummary`** | boolean | Android only: designate this notification as the summary for a group (should be used with the `group` property). | +| **`channelId`** | string | Android only: set the notification channel on which local notification will generate. If channel with the given name does not exist then the notification will not fire. If not provided, it will use the default channel. | +| **`ongoing`** | boolean | Android only: set the notification ongoing. If set to true the notification can't be swiped away. | +| **`autoCancel`** | boolean | Android only: set the notification to be removed automatically when the user clicks on it | + + +#### LocalNotificationSchedule + +| Prop | Type | +| ------------- | -------------------------------------------------------------------------------------------------- | +| **`at`** | Date | +| **`repeats`** | boolean | +| **`every`** | "year" \| "month" \| "two-weeks" \| "week" \| "day" \| "hour" \| "minute" \| "second" | +| **`count`** | number | +| **`on`** | { year?: number; month?: number; day?: number; hour?: number; minute?: number; } | + + +#### Date + +Enables basic storage and retrieval of dates and times. + +| Method | Signature | Description | +| ---------------------- | ------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | +| **toString** | () => string | Returns a string representation of a date. The format of the string depends on the locale. | +| **toDateString** | () => string | Returns a date as a string value. | +| **toTimeString** | () => string | Returns a time as a string value. | +| **toLocaleString** | () => string | Returns a value as a string value appropriate to the host environment's current locale. | +| **toLocaleDateString** | () => string | Returns a date as a string value appropriate to the host environment's current locale. | +| **toLocaleTimeString** | () => string | Returns a time as a string value appropriate to the host environment's current locale. | +| **valueOf** | () => number | Returns the stored time value in milliseconds since midnight, January 1, 1970 UTC. | +| **getTime** | () => number | Gets the time value in milliseconds. | +| **getFullYear** | () => number | Gets the year, using local time. | +| **getUTCFullYear** | () => number | Gets the year using Universal Coordinated Time (UTC). | +| **getMonth** | () => number | Gets the month, using local time. | +| **getUTCMonth** | () => number | Gets the month of a Date object using Universal Coordinated Time (UTC). | +| **getDate** | () => number | Gets the day-of-the-month, using local time. | +| **getUTCDate** | () => number | Gets the day-of-the-month, using Universal Coordinated Time (UTC). | +| **getDay** | () => number | Gets the day of the week, using local time. | +| **getUTCDay** | () => number | Gets the day of the week using Universal Coordinated Time (UTC). | +| **getHours** | () => number | Gets the hours in a date, using local time. | +| **getUTCHours** | () => number | Gets the hours value in a Date object using Universal Coordinated Time (UTC). | +| **getMinutes** | () => number | Gets the minutes of a Date object, using local time. | +| **getUTCMinutes** | () => number | Gets the minutes of a Date object using Universal Coordinated Time (UTC). | +| **getSeconds** | () => number | Gets the seconds of a Date object, using local time. | +| **getUTCSeconds** | () => number | Gets the seconds of a Date object using Universal Coordinated Time (UTC). | +| **getMilliseconds** | () => number | Gets the milliseconds of a Date, using local time. | +| **getUTCMilliseconds** | () => number | Gets the milliseconds of a Date object using Universal Coordinated Time (UTC). | +| **getTimezoneOffset** | () => number | Gets the difference in minutes between the time on the local computer and Universal Coordinated Time (UTC). | +| **setTime** | (time: number) => number | Sets the date and time value in the Date object. | +| **setMilliseconds** | (ms: number) => number | Sets the milliseconds value in the Date object using local time. | +| **setUTCMilliseconds** | (ms: number) => number | Sets the milliseconds value in the Date object using Universal Coordinated Time (UTC). | +| **setSeconds** | (sec: number, ms?: number \| undefined) => number | Sets the seconds value in the Date object using local time. | +| **setUTCSeconds** | (sec: number, ms?: number \| undefined) => number | Sets the seconds value in the Date object using Universal Coordinated Time (UTC). | +| **setMinutes** | (min: number, sec?: number \| undefined, ms?: number \| undefined) => number | Sets the minutes value in the Date object using local time. | +| **setUTCMinutes** | (min: number, sec?: number \| undefined, ms?: number \| undefined) => number | Sets the minutes value in the Date object using Universal Coordinated Time (UTC). | +| **setHours** | (hours: number, min?: number \| undefined, sec?: number \| undefined, ms?: number \| undefined) => number | Sets the hour value in the Date object using local time. | +| **setUTCHours** | (hours: number, min?: number \| undefined, sec?: number \| undefined, ms?: number \| undefined) => number | Sets the hours value in the Date object using Universal Coordinated Time (UTC). | +| **setDate** | (date: number) => number | Sets the numeric day-of-the-month value of the Date object using local time. | +| **setUTCDate** | (date: number) => number | Sets the numeric day of the month in the Date object using Universal Coordinated Time (UTC). | +| **setMonth** | (month: number, date?: number \| undefined) => number | Sets the month value in the Date object using local time. | +| **setUTCMonth** | (month: number, date?: number \| undefined) => number | Sets the month value in the Date object using Universal Coordinated Time (UTC). | +| **setFullYear** | (year: number, month?: number \| undefined, date?: number \| undefined) => number | Sets the year of the Date object using local time. | +| **setUTCFullYear** | (year: number, month?: number \| undefined, date?: number \| undefined) => number | Sets the year value in the Date object using Universal Coordinated Time (UTC). | +| **toUTCString** | () => string | Returns a date converted to a string using Universal Coordinated Time (UTC). | +| **toISOString** | () => string | Returns a date as a string value in ISO format. | +| **toJSON** | (key?: any) => string | Used by the JSON.stringify method to enable the transformation of an object's data for JavaScript Object Notation (JSON) serialization. | + + +#### LocalNotificationAttachment + +| Prop | Type | +| ------------- | ------------------------------------------------------------------------------------------------- | +| **`id`** | string | +| **`url`** | string | +| **`options`** | LocalNotificationAttachmentOptions | + + +#### LocalNotificationAttachmentOptions + +| Prop | Type | +| ---------------------------------------------------------------- | ------------------- | +| **`iosUNNotificationAttachmentOptionsTypeHintKey`** | string | +| **`iosUNNotificationAttachmentOptionsThumbnailHiddenKey`** | string | +| **`iosUNNotificationAttachmentOptionsThumbnailClippingRectKey`** | string | +| **`iosUNNotificationAttachmentOptionsThumbnailTimeKey`** | string | + + +#### LocalNotificationActionType + +| Prop | Type | +| -------------------------------------- | -------------------------------------- | +| **`id`** | string | +| **`actions`** | LocalNotificationAction[] | +| **`iosHiddenPreviewsBodyPlaceholder`** | string | +| **`iosCustomDismissAction`** | boolean | +| **`iosAllowInCarPlay`** | boolean | +| **`iosHiddenPreviewsShowTitle`** | boolean | +| **`iosHiddenPreviewsShowSubtitle`** | boolean | + + +#### LocalNotificationAction + +| Prop | Type | +| ---------------------------- | -------------------- | +| **`id`** | string | +| **`title`** | string | +| **`requiresAuthentication`** | boolean | +| **`foreground`** | boolean | +| **`destructive`** | boolean | +| **`input`** | boolean | +| **`inputButtonTitle`** | string | +| **`inputPlaceholder`** | string | + + +#### LocalNotificationEnabledResult + +| Prop | Type | Description | +| ----------- | -------------------- | --------------------------------------------------------- | +| **`value`** | boolean | Whether the device has Local Notifications enabled or not | + + +#### NotificationChannel + +| Prop | Type | +| ----------------- | ---------------------------------- | +| **`id`** | string | +| **`name`** | string | +| **`description`** | string | +| **`sound`** | string | +| **`importance`** | 1 \| 2 \| 5 \| 4 \| 3 | +| **`visibility`** | 0 \| 1 \| -1 | +| **`lights`** | boolean | +| **`lightColor`** | string | +| **`vibration`** | boolean | + + +#### NotificationChannelList + +| Prop | Type | +| -------------- | ---------------------------------- | +| **`channels`** | NotificationChannel[] | + + +#### LocalNotificationsPermissionStatus + +| Prop | Type | +| ------------- | ------------------------------------------------------------------------- | +| **`display`** | "prompt" \| "prompt-with-rationale" \| "granted" \| "denied" | + + +#### PluginListenerHandle + +| Prop | Type | +| ------------ | -------------------------- | +| **`remove`** | () => void | + + +#### LocalNotificationActionPerformed + +| Prop | Type | +| ------------------ | --------------------------------------------------------------- | +| **`actionId`** | string | +| **`inputValue`** | string | +| **`notification`** | LocalNotification | + From e97578b6885f48c81ed667c08b9d8bb58baba6e1 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 2 Dec 2020 15:03:20 -0800 Subject: [PATCH 26/60] update gradle --- local-notifications/android/build.gradle | 2 +- .../android/gradle/wrapper/gradle-wrapper.jar | Bin 58695 -> 58910 bytes .../gradle/wrapper/gradle-wrapper.properties | 2 +- local-notifications/android/gradlew | 2 ++ local-notifications/android/gradlew.bat | 4 ++++ 5 files changed, 8 insertions(+), 2 deletions(-) diff --git a/local-notifications/android/build.gradle b/local-notifications/android/build.gradle index f2f9dac80..be79102f6 100644 --- a/local-notifications/android/build.gradle +++ b/local-notifications/android/build.gradle @@ -11,7 +11,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.1' + classpath 'com.android.tools.build:gradle:4.1.1' } } diff --git a/local-notifications/android/gradle/wrapper/gradle-wrapper.jar b/local-notifications/android/gradle/wrapper/gradle-wrapper.jar index f3d88b1c2faf2fc91d853cd5d4242b5547257070..62d4c053550b91381bbd28b1afc82d634bf73a8a 100644 GIT binary patch delta 12524 zcmY*pqN z|ERss*;ReIR@Lg&r_R7IT-GRDbpR4NMKNcwFgy&*A|eco1PnBHRth?SBneiw6$UT&^LVJ0?Naa`KfV(>F!Ez}~S z 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 delta 12163 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 diff --git a/local-notifications/android/gradle/wrapper/gradle-wrapper.properties b/local-notifications/android/gradle/wrapper/gradle-wrapper.properties index 4e1cc9db6..186b71557 100644 --- a/local-notifications/android/gradle/wrapper/gradle-wrapper.properties +++ b/local-notifications/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/local-notifications/android/gradlew b/local-notifications/android/gradlew index 2fe81a7d9..fbd7c5158 100755 --- a/local-notifications/android/gradlew +++ b/local-notifications/android/gradlew @@ -82,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -129,6 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/local-notifications/android/gradlew.bat b/local-notifications/android/gradlew.bat index 9618d8d96..5093609d5 100644 --- a/local-notifications/android/gradlew.bat +++ b/local-notifications/android/gradlew.bat @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -81,6 +84,7 @@ set CMD_LINE_ARGS=%* set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% From 999c931a77111abba8a04b8060b6eaa334a28989 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 2 Dec 2020 15:05:18 -0800 Subject: [PATCH 27/60] remove registry --- local-notifications/src/definitions.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/local-notifications/src/definitions.ts b/local-notifications/src/definitions.ts index 4d8485a03..19294a61e 100644 --- a/local-notifications/src/definitions.ts +++ b/local-notifications/src/definitions.ts @@ -1,11 +1,5 @@ import type { PermissionState, PluginListenerHandle } from '@capacitor/core'; -declare module '@capacitor/core' { - interface PluginRegistry { - LocalNotifications: LocalNotificationsPlugin; - } -} - export interface LocalNotificationsPermissionStatus { display: PermissionState; } From 8adc9a7da5b702843575280e61342a9d303cfc26 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 2 Dec 2020 15:05:39 -0800 Subject: [PATCH 28/60] remove constructor --- local-notifications/src/web.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/local-notifications/src/web.ts b/local-notifications/src/web.ts index f88b5ee7a..3de45aee7 100644 --- a/local-notifications/src/web.ts +++ b/local-notifications/src/web.ts @@ -16,10 +16,6 @@ export class LocalNotificationsWeb implements LocalNotificationsPlugin { protected pending: LocalNotification[] = []; - constructor() { - super({ name: 'LocalNotifications' }); - } - async createChannel(): Promise { throw this.unavailable('Feature not available for web.'); } From b93bcc19ba52082d0bb4a08aab8edb8ba7aef4ab Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 2 Dec 2020 17:57:41 -0800 Subject: [PATCH 29/60] docs --- local-notifications/README.md | 378 ++++++++----- local-notifications/src/definitions.ts | 732 ++++++++++++++++++++++--- local-notifications/src/index.ts | 37 +- local-notifications/src/web.ts | 46 +- 4 files changed, 915 insertions(+), 278 deletions(-) diff --git a/local-notifications/README.md b/local-notifications/README.md index 300393355..785ca2b05 100644 --- a/local-notifications/README.md +++ b/local-notifications/README.md @@ -36,14 +36,18 @@ npx cap sync ### schedule(...) ```typescript -schedule(options: { notifications: LocalNotification[]; }) => Promise +schedule(options: ScheduleOptions) => Promise ``` -| Param | Type | -| ------------- | ---------------------------------------------------- | -| **`options`** | { notifications: LocalNotification[]; } | +Schedule one or more local notifications. -**Returns:** Promise<LocalNotificationPendingList> +| Param | Type | +| ------------- | ----------------------------------------------------------- | +| **`options`** | ScheduleOptions | + +**Returns:** Promise<ScheduleResult> + +**Since:** 1.0.0 -------------------- @@ -51,10 +55,14 @@ schedule(options: { notifications: LocalNotification[]; }) => Promise Promise +getPending() => Promise ``` -**Returns:** Promise<LocalNotificationPendingList> +Get a list of pending notifications. + +**Returns:** Promise<PendingResult> + +**Since:** 1.0.0 -------------------- @@ -62,12 +70,18 @@ getPending() => Promise ### registerActionTypes(...) ```typescript -registerActionTypes(options: { types: LocalNotificationActionType[]; }) => Promise +registerActionTypes(options: RegisterActionTypesOptions) => Promise ``` -| Param | Type | -| ------------- | ------------------------------------------------------ | -| **`options`** | { types: LocalNotificationActionType[]; } | +Register actions to take when notifications are displayed. + +Only available for iOS and Android. + +| Param | Type | +| ------------- | --------------------------------------------------------------------------------- | +| **`options`** | RegisterActionTypesOptions | + +**Since:** 1.0.0 -------------------- @@ -75,12 +89,16 @@ registerActionTypes(options: { types: LocalNotificationActionType[]; }) => Promi ### cancel(...) ```typescript -cancel(pending: LocalNotificationPendingList) => Promise +cancel(options: CancelOptions) => Promise ``` -| Param | Type | -| ------------- | ------------------------------------------------------------------------------------- | -| **`pending`** | LocalNotificationPendingList | +Cancel pending notifications. + +| Param | Type | +| ------------- | ------------------------------------------------------- | +| **`options`** | CancelOptions | + +**Since:** 1.0.0 -------------------- @@ -88,10 +106,14 @@ cancel(pending: LocalNotificationPendingList) => Promise ### areEnabled() ```typescript -areEnabled() => Promise +areEnabled() => Promise ``` -**Returns:** Promise<LocalNotificationEnabledResult> +Check if notifications are enabled or not. + +**Returns:** Promise<EnabledResult> + +**Since:** 1.0.0 -------------------- @@ -99,12 +121,18 @@ areEnabled() => Promise ### createChannel(...) ```typescript -createChannel(channel: NotificationChannel) => Promise +createChannel(channel: Channel) => Promise ``` -| Param | Type | -| ------------- | ------------------------------------------------------------------- | -| **`channel`** | NotificationChannel | +Create a notification channel. + +Only available for Android. + +| Param | Type | +| ------------- | ------------------------------------------- | +| **`channel`** | Channel | + +**Since:** 1.0.0 -------------------- @@ -112,12 +140,18 @@ createChannel(channel: NotificationChannel) => Promise ### deleteChannel(...) ```typescript -deleteChannel(channel: NotificationChannel) => Promise +deleteChannel(channel: Channel) => Promise ``` -| Param | Type | -| ------------- | ------------------------------------------------------------------- | -| **`channel`** | NotificationChannel | +Delete a notification channel. + +Only available for Android. + +| Param | Type | +| ------------- | ------------------------------------------- | +| **`channel`** | Channel | + +**Since:** 1.0.0 -------------------- @@ -125,10 +159,16 @@ deleteChannel(channel: NotificationChannel) => Promise ### listChannels() ```typescript -listChannels() => Promise +listChannels() => Promise ``` -**Returns:** Promise<NotificationChannelList> +Get a list of notification channels. + +Only available for Android. + +**Returns:** Promise<ListChannelsResult> + +**Since:** 1.0.0 -------------------- @@ -136,12 +176,12 @@ listChannels() => Promise ### checkPermissions() ```typescript -checkPermissions() => Promise +checkPermissions() => Promise ``` -Check notification permissions +Check permission to display local notifications. -**Returns:** Promise<LocalNotificationsPermissionStatus> +**Returns:** Promise<PermissionStatus> **Since:** 1.0.0 @@ -151,12 +191,12 @@ Check notification permissions ### requestPermissions() ```typescript -requestPermissions() => Promise +requestPermissions() => Promise ``` -Request location permissions +Request permission to display local notifications. -**Returns:** Promise<LocalNotificationsPermissionStatus> +**Returns:** Promise<PermissionStatus> **Since:** 1.0.0 @@ -166,32 +206,40 @@ Request location permissions ### addListener(...) ```typescript -addListener(eventName: 'localNotificationReceived', listenerFunc: (notification: LocalNotification) => void) => PluginListenerHandle +addListener(eventName: 'localNotificationReceived', listenerFunc: (notification: LocalNotificationSchema) => void) => PluginListenerHandle ``` -| Param | Type | -| ------------------ | ------------------------------------------------------------------------------------------ | -| **`eventName`** | "localNotificationReceived" | -| **`listenerFunc`** | (notification: LocalNotification) => void | +Listen for when notifications are displayed. + +| Param | Type | +| ------------------ | ------------------------------------------------------------------------------------------------------ | +| **`eventName`** | "localNotificationReceived" | +| **`listenerFunc`** | (notification: LocalNotificationSchema) => void | **Returns:** PluginListenerHandle +**Since:** 1.0.0 + -------------------- ### addListener(...) ```typescript -addListener(eventName: 'localNotificationActionPerformed', listenerFunc: (notificationAction: LocalNotificationActionPerformed) => void) => PluginListenerHandle +addListener(eventName: 'localNotificationActionPerformed', listenerFunc: (notificationAction: ActionPerformed) => void) => PluginListenerHandle ``` -| Param | Type | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------------ | -| **`eventName`** | "localNotificationActionPerformed" | -| **`listenerFunc`** | (notificationAction: LocalNotificationActionPerformed) => void | +Listen for when an action is performed on a notification. + +| Param | Type | +| ------------------ | -------------------------------------------------------------------------------------------- | +| **`eventName`** | "localNotificationActionPerformed" | +| **`listenerFunc`** | (notificationAction: ActionPerformed) => void | **Returns:** PluginListenerHandle +**Since:** 1.0.0 + -------------------- @@ -201,7 +249,9 @@ addListener(eventName: 'localNotificationActionPerformed', listenerFunc: (notifi removeAllListeners() => void ``` -Remove all native listeners for this plugin +Remove all listeners for this plugin. + +**Since:** 1.0.0 -------------------- @@ -209,52 +259,65 @@ Remove all native listeners for this plugin ### Interfaces -#### LocalNotificationPendingList +#### ScheduleResult + +| Prop | Type | Description | Since | +| ------------------- | ------------------------------------------ | ------------------------------------ | ----- | +| **`notifications`** | LocalNotificationDescriptor[] | The list of scheduled notifications. | 1.0.0 | + + +#### LocalNotificationDescriptor + +The object that describes a local notification. + +| Prop | Type | Description | Since | +| -------- | ------------------- | ---------------------------- | ----- | +| **`id`** | string | The notification identifier. | 1.0.0 | -| Prop | Type | -| ------------------- | --------------------------------------- | -| **`notifications`** | LocalNotificationRequest[] | +#### ScheduleOptions -#### LocalNotificationRequest +| Prop | Type | Description | Since | +| ------------------- | -------------------------------------- | -------------------------------------- | ----- | +| **`notifications`** | LocalNotificationSchema[] | The list of notifications to schedule. | 1.0.0 | -| Prop | Type | -| -------- | ------------------- | -| **`id`** | string | +#### LocalNotificationSchema -#### LocalNotification +| Prop | Type | Description | Since | +| ---------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | +| **`title`** | string | The title of the notification. | 1.0.0 | +| **`body`** | string | The body of the notification, shown below the title. | 1.0.0 | +| **`id`** | number | The notification identifier. | 1.0.0 | +| **`schedule`** | Schedule | Schedule this notification for a later time. | 1.0.0 | +| **`sound`** | string | Name of the audio file to play when this notification is displayed. Include the file extension with the filename. On iOS, the file should be in the app bundle. On Android, the file should be in res/raw folder. Recommended format is `.wav` because is supported by both iOS and Android. Only available for iOS and Android 26+. | 1.0.0 | +| **`smallIcon`** | string | Set a custom status bar icon. If set, this overrides the default icon from Capacitor configuration. Only available for Android. | 1.0.0 | +| **`iconColor`** | string | Set the color of the notification icon. Only available for Android. | 1.0.0 | +| **`attachments`** | Attachment[] | Set attachments for this notification. | 1.0.0 | +| **`actionTypeId`** | string | Associate an action type with this notification. | 1.0.0 | +| **`extra`** | any | Set extra data to store within this notification. | 1.0.0 | +| **`threadIdentifier`** | string | Used to group multiple notifications. Sets `threadIdentifier` on the [`UNMutableNotificationContent`](https://developer.apple.com/documentation/usernotifications/unmutablenotificationcontent). Only available for iOS. | 1.0.0 | +| **`summaryArgument`** | string | The string this notification adds to the category's summary format string. Sets `summaryArgument` on the [`UNMutableNotificationContent`](https://developer.apple.com/documentation/usernotifications/unmutablenotificationcontent). Only available for iOS 12+. | 1.0.0 | +| **`group`** | string | Used to group multiple notifications. Calls `setGroup()` on [`NotificationCompat.Builder`](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder) with the provided value. Only available for Android. | 1.0.0 | +| **`groupSummary`** | boolean | If true, this notification becomes the summary for a group of notifications. Calls `setGroupSummary()` on [`NotificationCompat.Builder`](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder) with the provided value. Only available for Android when using `group`. | 1.0.0 | +| **`channelId`** | string | 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 | +| **`ongoing`** | boolean | If true, the notification can't be swiped away. Calls `setOngoing()` on [`NotificationCompat.Builder`](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder) with the provided value. Only available for Android. | 1.0.0 | +| **`autoCancel`** | boolean | If true, the notification is canceled when the user clicks on it. Calls `setAutoCancel()` on [`NotificationCompat.Builder`](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder) with the provided value. Only available for Android. | 1.0.0 | -| Prop | Type | Description | -| ---------------------- | ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **`title`** | string | | -| **`body`** | string | | -| **`id`** | number | | -| **`schedule`** | LocalNotificationSchedule | | -| **`sound`** | string | Name of the audio file with extension. On iOS the file should be in the app bundle. On Android the file should be on res/raw folder. Doesn't work on Android version 26+ (Android O and newer), for Recommended format is .wav because is supported by both platforms. | -| **`smallIcon`** | string | Android-only: set a custom statusbar icon. If set, it overrides default icon from capacitor.config.json | -| **`iconColor`** | string | Android only: set the color of the notification icon | -| **`attachments`** | LocalNotificationAttachment[] | | -| **`actionTypeId`** | string | | -| **`extra`** | any | | -| **`threadIdentifier`** | string | iOS only: set the thread identifier for notification grouping | -| **`summaryArgument`** | string | iOS 12+ only: set the summary argument for notification grouping | -| **`group`** | string | Android only: set the group identifier for notification grouping, like threadIdentifier on iOS. | -| **`groupSummary`** | boolean | Android only: designate this notification as the summary for a group (should be used with the `group` property). | -| **`channelId`** | string | Android only: set the notification channel on which local notification will generate. If channel with the given name does not exist then the notification will not fire. If not provided, it will use the default channel. | -| **`ongoing`** | boolean | Android only: set the notification ongoing. If set to true the notification can't be swiped away. | -| **`autoCancel`** | boolean | Android only: set the notification to be removed automatically when the user clicks on it | +#### Schedule -#### LocalNotificationSchedule +Represents a schedule for a notification. -| Prop | Type | -| ------------- | -------------------------------------------------------------------------------------------------- | -| **`at`** | Date | -| **`repeats`** | boolean | -| **`every`** | "year" \| "month" \| "two-weeks" \| "week" \| "day" \| "hour" \| "minute" \| "second" | -| **`count`** | number | -| **`on`** | { year?: number; month?: number; day?: number; hour?: number; minute?: number; } | +Use either `at`, `on`, or `every` to schedule notifications. + +| Prop | Type | Description | Since | +| ------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | +| **`at`** | Date | Schedule a notification at a specific date and time. | 1.0.0 | +| **`repeats`** | boolean | Repeat delivery of this notification at the date and time specified by `at`. Only available for iOS and Android. | 1.0.0 | +| **`on`** | { year?: number; month?: number; day?: number; hour?: number; minute?: number; } | Schedule a notification on particular interval(s). This is similar to scheduling [cron](https://en.wikipedia.org/wiki/Cron) jobs. Only available for iOS and Android. | 1.0.0 | +| **`every`** | "year" \| "month" \| "two-weeks" \| "week" \| "day" \| "hour" \| "minute" \| "second" | Schedule a notification on a particular interval. | 1.0.0 | +| **`count`** | number | Limit the number times a notification is delivered by the interval specified by `every`. | 1.0.0 | #### Date @@ -308,86 +371,113 @@ Enables basic storage and retrieval of dates and times. | **toJSON** | (key?: any) => string | Used by the JSON.stringify method to enable the transformation of an object's data for JavaScript Object Notation (JSON) serialization. | -#### LocalNotificationAttachment +#### Attachment + +Represents a notification attachment. + +| Prop | Type | Description | Since | +| ------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----- | +| **`id`** | string | The attachment identifier. | 1.0.0 | +| **`url`** | string | The URL to the attachment. Use the `res` scheme to reference web assets, e.g. `res:///assets/img/icon.png`. Also accepts `file` URLs. | 1.0.0 | +| **`options`** | AttachmentOptions | Attachment options. | 1.0.0 | + + +#### AttachmentOptions + +| Prop | Type | Description | Since | +| ---------------------------------------------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | +| **`iosUNNotificationAttachmentOptionsTypeHintKey`** | string | Sets the `UNNotificationAttachmentOptionsTypeHintKey` key in the hashable options of [`UNNotificationAttachment`](https://developer.apple.com/documentation/usernotifications/unnotificationattachment). Only available for iOS. | 1.0.0 | +| **`iosUNNotificationAttachmentOptionsThumbnailHiddenKey`** | string | Sets the `UNNotificationAttachmentOptionsThumbnailHiddenKey` key in the hashable options of [`UNNotificationAttachment`](https://developer.apple.com/documentation/usernotifications/unnotificationattachment). Only available for iOS. | 1.0.0 | +| **`iosUNNotificationAttachmentOptionsThumbnailClippingRectKey`** | string | Sets the `UNNotificationAttachmentOptionsThumbnailClippingRectKey` key in the hashable options of [`UNNotificationAttachment`](https://developer.apple.com/documentation/usernotifications/unnotificationattachment). Only available for iOS. | 1.0.0 | +| **`iosUNNotificationAttachmentOptionsThumbnailTimeKey`** | string | Sets the `UNNotificationAttachmentOptionsThumbnailTimeKey` key in the hashable options of [`UNNotificationAttachment`](https://developer.apple.com/documentation/usernotifications/unnotificationattachment). Only available for iOS. | 1.0.0 | + + +#### PendingResult + +| Prop | Type | Description | Since | +| ------------------- | ------------------------------------------ | ---------------------------------- | ----- | +| **`notifications`** | LocalNotificationDescriptor[] | The list of pending notifications. | 1.0.0 | + + +#### RegisterActionTypesOptions + +| Prop | Type | Description | Since | +| ----------- | ------------------------- | ------------------------------------- | ----- | +| **`types`** | ActionType[] | The list of action types to register. | 1.0.0 | + -| Prop | Type | -| ------------- | ------------------------------------------------------------------------------------------------- | -| **`id`** | string | -| **`url`** | string | -| **`options`** | LocalNotificationAttachmentOptions | +#### ActionType +A collection of actions. -#### LocalNotificationAttachmentOptions +| Prop | Type | Description | Since | +| -------------------------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | +| **`id`** | string | The ID of the action type. Referenced in notifications by the `actionTypeId` key. | 1.0.0 | +| **`actions`** | Action[] | The list of actions associated with this action type. | 1.0.0 | +| **`iosHiddenPreviewsBodyPlaceholder`** | string | Sets `hiddenPreviewsBodyPlaceholder` of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS 11+. | 1.0.0 | +| **`iosCustomDismissAction`** | boolean | Sets `customDismissAction` in the options of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS. | 1.0.0 | +| **`iosAllowInCarPlay`** | boolean | Sets `allowInCarPlay` in the options of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS. | 1.0.0 | +| **`iosHiddenPreviewsShowTitle`** | boolean | Sets `hiddenPreviewsShowTitle` in the options of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS 11+. | 1.0.0 | +| **`iosHiddenPreviewsShowSubtitle`** | boolean | Sets `hiddenPreviewsShowSubtitle` in the options of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS 11+. | 1.0.0 | -| Prop | Type | -| ---------------------------------------------------------------- | ------------------- | -| **`iosUNNotificationAttachmentOptionsTypeHintKey`** | string | -| **`iosUNNotificationAttachmentOptionsThumbnailHiddenKey`** | string | -| **`iosUNNotificationAttachmentOptionsThumbnailClippingRectKey`** | string | -| **`iosUNNotificationAttachmentOptionsThumbnailTimeKey`** | string | +#### Action -#### LocalNotificationActionType +An action that can be taken when a notification is displayed. -| Prop | Type | -| -------------------------------------- | -------------------------------------- | -| **`id`** | string | -| **`actions`** | LocalNotificationAction[] | -| **`iosHiddenPreviewsBodyPlaceholder`** | string | -| **`iosCustomDismissAction`** | boolean | -| **`iosAllowInCarPlay`** | boolean | -| **`iosHiddenPreviewsShowTitle`** | boolean | -| **`iosHiddenPreviewsShowSubtitle`** | boolean | +| Prop | Type | Description | Since | +| ---------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | +| **`id`** | string | The action identifier. Referenced in the `'localNotificationActionPerformed'` event as `actionId`. | 1.0.0 | +| **`title`** | string | The title text to display for this action. | 1.0.0 | +| **`requiresAuthentication`** | boolean | Sets `authenticationRequired` in the options of the [`UNNotificationAction`](https://developer.apple.com/documentation/usernotifications/unnotificationaction). Only available for iOS. | 1.0.0 | +| **`foreground`** | boolean | Sets `foreground` in the options of the [`UNNotificationAction`](https://developer.apple.com/documentation/usernotifications/unnotificationaction). Only available for iOS. | 1.0.0 | +| **`destructive`** | boolean | Sets `destructive` in the options of the [`UNNotificationAction`](https://developer.apple.com/documentation/usernotifications/unnotificationaction). Only available for iOS. | 1.0.0 | +| **`input`** | boolean | Use a `UNTextInputNotificationAction` instead of a `UNNotificationAction`. Only available for iOS. | 1.0.0 | +| **`inputButtonTitle`** | string | Sets `textInputButtonTitle` on the [`UNTextInputNotificationAction`](https://developer.apple.com/documentation/usernotifications/untextinputnotificationaction). Only available for iOS when `input` is `true`. | 1.0.0 | +| **`inputPlaceholder`** | string | Sets `textInputPlaceholder` on the [`UNTextInputNotificationAction`](https://developer.apple.com/documentation/usernotifications/untextinputnotificationaction). Only available for iOS when `input` is `true`. | 1.0.0 | -#### LocalNotificationAction +#### CancelOptions -| Prop | Type | -| ---------------------------- | -------------------- | -| **`id`** | string | -| **`title`** | string | -| **`requiresAuthentication`** | boolean | -| **`foreground`** | boolean | -| **`destructive`** | boolean | -| **`input`** | boolean | -| **`inputButtonTitle`** | string | -| **`inputPlaceholder`** | string | +| Prop | Type | Description | Since | +| ------------------- | ------------------------------------------ | ------------------------------------ | ----- | +| **`notifications`** | LocalNotificationDescriptor[] | The list of notifications to cancel. | 1.0.0 | -#### LocalNotificationEnabledResult +#### EnabledResult -| Prop | Type | Description | -| ----------- | -------------------- | --------------------------------------------------------- | -| **`value`** | boolean | Whether the device has Local Notifications enabled or not | +| Prop | Type | Description | Since | +| ----------- | -------------------- | ---------------------------------------------------------- | ----- | +| **`value`** | boolean | Whether or not the device has local notifications enabled. | 1.0.0 | -#### NotificationChannel +#### Channel -| Prop | Type | -| ----------------- | ---------------------------------- | -| **`id`** | string | -| **`name`** | string | -| **`description`** | string | -| **`sound`** | string | -| **`importance`** | 1 \| 2 \| 5 \| 4 \| 3 | -| **`visibility`** | 0 \| 1 \| -1 | -| **`lights`** | boolean | -| **`lightColor`** | string | -| **`vibration`** | boolean | +| Prop | Type | Description | Since | +| ----------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | +| **`id`** | string | The channel identifier. | 1.0.0 | +| **`name`** | string | The channel name. | 1.0.0 | +| **`description`** | string | The channel description. | 1.0.0 | +| **`sound`** | string | The sound that is played for notifications posted to this channel. | 1.0.0 | +| **`importance`** | 1 \| 2 \| 5 \| 4 \| 3 | The level of interruption of notifications posted to this channel. See the `PRIORITY_*` constants of [`NotificationCompat`](https://developer.android.com/reference/androidx/core/app/NotificationCompat) for more information. | 1.0.0 | +| **`visibility`** | 0 \| 1 \| -1 | The visibility level of notifications posted to this channel. See the `VISIBILITY_*` constants of [`NotificationCompat`](https://developer.android.com/reference/androidx/core/app/NotificationCompat) for more information. | 1.0.0 | +| **`lights`** | boolean | Whether or not notifications posted to this channel should display notification lights. | 1.0.0 | +| **`lightColor`** | string | The color of notification lights when using the `lights` option. This can be any value that [`Color.parseColor()`](https://developer.android.com/reference/android/graphics/Color#parseColor(java.lang.String)) expects. | 1.0.0 | +| **`vibration`** | boolean | Whether or not notifications posted to this channel should vibrate the device. | 1.0.0 | -#### NotificationChannelList +#### ListChannelsResult -| Prop | Type | -| -------------- | ---------------------------------- | -| **`channels`** | NotificationChannel[] | +| Prop | Type | Description | Since | +| -------------- | ---------------------- | ---------------------------------- | ----- | +| **`channels`** | Channel[] | The list of notification channels. | 1.0.0 | -#### LocalNotificationsPermissionStatus +#### PermissionStatus -| Prop | Type | -| ------------- | ------------------------------------------------------------------------- | -| **`display`** | "prompt" \| "prompt-with-rationale" \| "granted" \| "denied" | +| Prop | Type | Description | Since | +| ------------- | ------------------------------------------------------------------------- | --------------------------------------------- | ----- | +| **`display`** | "prompt" \| "prompt-with-rationale" \| "granted" \| "denied" | Permission state of displaying notifications. | 1.0.0 | #### PluginListenerHandle @@ -397,12 +487,12 @@ Enables basic storage and retrieval of dates and times. | **`remove`** | () => void | -#### LocalNotificationActionPerformed +#### ActionPerformed -| Prop | Type | -| ------------------ | --------------------------------------------------------------- | -| **`actionId`** | string | -| **`inputValue`** | string | -| **`notification`** | LocalNotification | +| Prop | Type | Description | Since | +| ------------------ | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ----- | +| **`actionId`** | string | The identifier of the performed action. | 1.0.0 | +| **`inputValue`** | string | The value entered by the user on the notification. Only available on iOS for notifications with `input` set to `true`. | 1.0.0 | +| **`notification`** | LocalNotificationSchema | The original notification schema. | 1.0.0 | diff --git a/local-notifications/src/definitions.ts b/local-notifications/src/definitions.ts index 19294a61e..31319b164 100644 --- a/local-notifications/src/definitions.ts +++ b/local-notifications/src/definitions.ts @@ -1,161 +1,634 @@ import type { PermissionState, PluginListenerHandle } from '@capacitor/core'; -export interface LocalNotificationsPermissionStatus { - display: PermissionState; -} - export interface LocalNotificationsPlugin { - schedule(options: { - notifications: LocalNotification[]; - }): Promise; - getPending(): Promise; - registerActionTypes(options: { - types: LocalNotificationActionType[]; - }): Promise; - cancel(pending: LocalNotificationPendingList): Promise; - areEnabled(): Promise; - createChannel(channel: NotificationChannel): Promise; - deleteChannel(channel: NotificationChannel): Promise; - listChannels(): Promise; + /** + * Schedule one or more local notifications. + * + * @since 1.0.0 + */ + schedule(options: ScheduleOptions): Promise; /** - * Check notification permissions + * Get a list of pending notifications. * * @since 1.0.0 */ - checkPermissions(): Promise; + getPending(): Promise; /** - * Request location permissions + * Register actions to take when notifications are displayed. + * + * Only available for iOS and Android. * * @since 1.0.0 */ - requestPermissions(): Promise; + registerActionTypes(options: RegisterActionTypesOptions): Promise; + /** + * Cancel pending notifications. + * + * @since 1.0.0 + */ + cancel(options: CancelOptions): Promise; + + /** + * Check if notifications are enabled or not. + * + * @deprecated Use `checkPermissions()` to check if the user has allowed + * notifications to be displayed. + * @since 1.0.0 + */ + areEnabled(): Promise; + + /** + * Create a notification channel. + * + * Only available for Android. + * + * @since 1.0.0 + */ + createChannel(channel: Channel): Promise; + + /** + * Delete a notification channel. + * + * Only available for Android. + * + * @since 1.0.0 + */ + deleteChannel(channel: Channel): Promise; + + /** + * Get a list of notification channels. + * + * Only available for Android. + * + * @since 1.0.0 + */ + listChannels(): Promise; + + /** + * Check permission to display local notifications. + * + * @since 1.0.0 + */ + checkPermissions(): Promise; + + /** + * Request permission to display local notifications. + * + * @since 1.0.0 + */ + requestPermissions(): Promise; + + /** + * Listen for when notifications are displayed. + * + * @since 1.0.0 + */ addListener( eventName: 'localNotificationReceived', - listenerFunc: (notification: LocalNotification) => void, + listenerFunc: (notification: LocalNotificationSchema) => void, ): PluginListenerHandle; + + /** + * Listen for when an action is performed on a notification. + * + * @since 1.0.0 + */ addListener( eventName: 'localNotificationActionPerformed', - listenerFunc: ( - notificationAction: LocalNotificationActionPerformed, - ) => void, + listenerFunc: (notificationAction: ActionPerformed) => void, ): PluginListenerHandle; /** - * Remove all native listeners for this plugin + * Remove all listeners for this plugin. + * + * @since 1.0.0 */ removeAllListeners(): void; } -export interface LocalNotificationRequest { +/** + * The object that describes a local notification. + * + * @since 1.0.0 + */ +export interface LocalNotificationDescriptor { + /** + * The notification identifier. + * + * @since 1.0.0 + */ id: string; } -export interface LocalNotificationPendingList { - notifications: LocalNotificationRequest[]; +export interface ScheduleOptions { + /** + * The list of notifications to schedule. + * + * @since 1.0.0 + */ + notifications: LocalNotificationSchema[]; +} + +export interface ScheduleResult { + /** + * The list of scheduled notifications. + * + * @since 1.0.0 + */ + notifications: LocalNotificationDescriptor[]; +} + +export interface PendingResult { + /** + * The list of pending notifications. + * + * @since 1.0.0 + */ + notifications: LocalNotificationDescriptor[]; +} + +export interface RegisterActionTypesOptions { + /** + * The list of action types to register. + * + * @since 1.0.0 + */ + types: ActionType[]; } -export type LocalNotificationScheduleResult = LocalNotificationPendingList; +export interface CancelOptions { + /** + * The list of notifications to cancel. + * + * @since 1.0.0 + */ + notifications: LocalNotificationDescriptor[]; +} -export interface LocalNotificationActionType { +/** + * A collection of actions. + * + * @since 1.0.0 + */ +export interface ActionType { + /** + * The ID of the action type. + * + * Referenced in notifications by the `actionTypeId` key. + * + * @since 1.0.0 + */ id: string; - actions?: LocalNotificationAction[]; + + /** + * The list of actions associated with this action type. + * + * @since 1.0.0 + */ + actions?: Action[]; + + /** + * Sets `hiddenPreviewsBodyPlaceholder` of the + * [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). + * + * Only available for iOS 11+. + * + * @since 1.0.0 + */ iosHiddenPreviewsBodyPlaceholder?: string; // >= iOS 11 only + + /** + * Sets `customDismissAction` in the options of the + * [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). + * + * Only available for iOS. + * + * @since 1.0.0 + */ iosCustomDismissAction?: boolean; + + /** + * Sets `allowInCarPlay` in the options of the + * [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). + * + * Only available for iOS. + * + * @since 1.0.0 + */ iosAllowInCarPlay?: boolean; - iosHiddenPreviewsShowTitle?: boolean; // >= iOS 11 only - iosHiddenPreviewsShowSubtitle?: boolean; // >= iOS 11 only + + /** + * Sets `hiddenPreviewsShowTitle` in the options of the + * [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). + * + * Only available for iOS 11+. + * + * @since 1.0.0 + */ + iosHiddenPreviewsShowTitle?: boolean; + + /** + * Sets `hiddenPreviewsShowSubtitle` in the options of the + * [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). + * + * Only available for iOS 11+. + * + * @since 1.0.0 + */ + iosHiddenPreviewsShowSubtitle?: boolean; } -export interface LocalNotificationAction { +/** + * An action that can be taken when a notification is displayed. + * + * @since 1.0.0 + */ +export interface Action { + /** + * The action identifier. + * + * Referenced in the `'localNotificationActionPerformed'` event as + * `actionId`. + * + * @since 1.0.0 + */ id: string; + + /** + * The title text to display for this action. + * + * @since 1.0.0 + */ title: string; + + /** + * Sets `authenticationRequired` in the options of the + * [`UNNotificationAction`](https://developer.apple.com/documentation/usernotifications/unnotificationaction). + * + * Only available for iOS. + * + * @since 1.0.0 + */ requiresAuthentication?: boolean; + + /** + * Sets `foreground` in the options of the + * [`UNNotificationAction`](https://developer.apple.com/documentation/usernotifications/unnotificationaction). + * + * Only available for iOS. + * + * @since 1.0.0 + */ foreground?: boolean; + + /** + * Sets `destructive` in the options of the + * [`UNNotificationAction`](https://developer.apple.com/documentation/usernotifications/unnotificationaction). + * + * Only available for iOS. + * + * @since 1.0.0 + */ destructive?: boolean; + + /** + * Use a `UNTextInputNotificationAction` instead of a `UNNotificationAction`. + * + * Only available for iOS. + * + * @since 1.0.0 + */ input?: boolean; + + /** + * Sets `textInputButtonTitle` on the + * [`UNTextInputNotificationAction`](https://developer.apple.com/documentation/usernotifications/untextinputnotificationaction). + * + * Only available for iOS when `input` is `true`. + * + * @since 1.0.0 + */ inputButtonTitle?: string; + + /** + * Sets `textInputPlaceholder` on the + * [`UNTextInputNotificationAction`](https://developer.apple.com/documentation/usernotifications/untextinputnotificationaction). + * + * Only available for iOS when `input` is `true`. + * + * @since 1.0.0 + */ inputPlaceholder?: string; } -export interface LocalNotificationAttachment { +/** + * Represents a notification attachment. + * + * @since 1.0.0 + */ +export interface Attachment { + /** + * The attachment identifier. + * + * @since 1.0.0 + */ id: string; + + /** + * The URL to the attachment. + * + * Use the `res` scheme to reference web assets, e.g. + * `res:///assets/img/icon.png`. Also accepts `file` URLs. + * + * @since 1.0.0 + */ url: string; - options?: LocalNotificationAttachmentOptions; + + /** + * Attachment options. + * + * @since 1.0.0 + */ + options?: AttachmentOptions; } -export interface LocalNotificationAttachmentOptions { +export interface AttachmentOptions { + /** + * Sets the `UNNotificationAttachmentOptionsTypeHintKey` key in the hashable + * options of + * [`UNNotificationAttachment`](https://developer.apple.com/documentation/usernotifications/unnotificationattachment). + * + * Only available for iOS. + * + * @since 1.0.0 + */ iosUNNotificationAttachmentOptionsTypeHintKey?: string; + + /** + * Sets the `UNNotificationAttachmentOptionsThumbnailHiddenKey` key in the + * hashable options of + * [`UNNotificationAttachment`](https://developer.apple.com/documentation/usernotifications/unnotificationattachment). + * + * Only available for iOS. + * + * @since 1.0.0 + */ iosUNNotificationAttachmentOptionsThumbnailHiddenKey?: string; + + /** + * Sets the `UNNotificationAttachmentOptionsThumbnailClippingRectKey` key in + * the hashable options of + * [`UNNotificationAttachment`](https://developer.apple.com/documentation/usernotifications/unnotificationattachment). + * + * Only available for iOS. + * + * @since 1.0.0 + */ iosUNNotificationAttachmentOptionsThumbnailClippingRectKey?: string; + + /** + * Sets the `UNNotificationAttachmentOptionsThumbnailTimeKey` key in the + * hashable options of + * [`UNNotificationAttachment`](https://developer.apple.com/documentation/usernotifications/unnotificationattachment). + * + * Only available for iOS. + * + * @since 1.0.0 + */ iosUNNotificationAttachmentOptionsThumbnailTimeKey?: string; } -export interface LocalNotification { +export interface LocalNotificationSchema { + /** + * The title of the notification. + * + * @since 1.0.0 + */ title: string; + + /** + * The body of the notification, shown below the title. + * + * @since 1.0.0 + */ body: string; + + /** + * The notification identifier. + * + * @since 1.0.0 + */ id: number; - schedule?: LocalNotificationSchedule; + /** - * Name of the audio file with extension. - * On iOS the file should be in the app bundle. - * On Android the file should be on res/raw folder. - * Doesn't work on Android version 26+ (Android O and newer), for - * Recommended format is .wav because is supported by both platforms. + * Schedule this notification for a later time. + * + * @since 1.0.0 + */ + schedule?: Schedule; + + /** + * Name of the audio file to play when this notification is displayed. + * + * Include the file extension with the filename. + * + * On iOS, the file should be in the app bundle. + * On Android, the file should be in res/raw folder. + * + * Recommended format is `.wav` because is supported by both iOS and Android. + * + * Only available for iOS and Android 26+. + * + * @since 1.0.0 */ sound?: string; + /** - * Android-only: set a custom statusbar icon. - * If set, it overrides default icon from capacitor.config.json + * Set a custom status bar icon. + * + * If set, this overrides the default icon from Capacitor configuration. + * + * Only available for Android. + * + * @since 1.0.0 */ smallIcon?: string; + /** - * Android only: set the color of the notification icon + * Set the color of the notification icon. + * + * Only available for Android. + * + * @since 1.0.0 */ iconColor?: string; - attachments?: LocalNotificationAttachment[]; + + /** + * Set attachments for this notification. + * + * @since 1.0.0 + */ + attachments?: Attachment[]; + + /** + * Associate an action type with this notification. + * + * @since 1.0.0 + */ actionTypeId?: string; + + /** + * Set extra data to store within this notification. + * + * @since 1.0.0 + */ extra?: any; + /** - * iOS only: set the thread identifier for notification grouping + * Used to group multiple notifications. + * + * Sets `threadIdentifier` on the + * [`UNMutableNotificationContent`](https://developer.apple.com/documentation/usernotifications/unmutablenotificationcontent). + * + * Only available for iOS. + * + * @since 1.0.0 */ threadIdentifier?: string; + /** - * iOS 12+ only: set the summary argument for notification grouping + * The string this notification adds to the category's summary format string. + * + * Sets `summaryArgument` on the + * [`UNMutableNotificationContent`](https://developer.apple.com/documentation/usernotifications/unmutablenotificationcontent). + * + * Only available for iOS 12+. + * + * @since 1.0.0 */ summaryArgument?: string; + /** - * Android only: set the group identifier for notification grouping, like - * threadIdentifier on iOS. + * Used to group multiple notifications. + * + * Calls `setGroup()` on + * [`NotificationCompat.Builder`](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder) + * with the provided value. + * + * Only available for Android. + * + * @since 1.0.0 */ group?: string; + /** - * Android only: designate this notification as the summary for a group - * (should be used with the `group` property). + * If true, this notification becomes the summary for a group of + * notifications. + * + * Calls `setGroupSummary()` on + * [`NotificationCompat.Builder`](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder) + * with the provided value. + * + * Only available for Android when using `group`. + * + * @since 1.0.0 */ groupSummary?: boolean; + /** - * Android only: set the notification channel on which local notification - * will generate. If channel with the given name does not exist then the - * notification will not fire. If not provided, it will use the default channel. + * 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+. + * + * @since 1.0.0 */ channelId?: string; + /** - * Android only: set the notification ongoing. - * If set to true the notification can't be swiped away. + * If true, the notification can't be swiped away. + * + * Calls `setOngoing()` on + * [`NotificationCompat.Builder`](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder) + * with the provided value. + * + * Only available for Android. + * + * @since 1.0.0 */ ongoing?: boolean; + /** - * Android only: set the notification to be removed automatically when the user clicks on it + * If true, the notification is canceled when the user clicks on it. + * + * Calls `setAutoCancel()` on + * [`NotificationCompat.Builder`](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder) + * with the provided value. + * + * Only available for Android. + * + * @since 1.0.0 */ autoCancel?: boolean; } -export interface LocalNotificationSchedule { +/** + * Represents a schedule for a notification. + * + * Use either `at`, `on`, or `every` to schedule notifications. + * + * @since 1.0.0 + */ +export interface Schedule { + /** + * Schedule a notification at a specific date and time. + * + * @since 1.0.0 + */ at?: Date; + + /** + * Repeat delivery of this notification at the date and time specified by + * `at`. + * + * Only available for iOS and Android. + * + * @since 1.0.0 + */ repeats?: boolean; + + /** + * Schedule a notification on particular interval(s). + * + * This is similar to scheduling [cron](https://en.wikipedia.org/wiki/Cron) + * jobs. + * + * Only available for iOS and Android. + * + * @since 1.0.0 + */ + on?: { + year?: number; + month?: number; + day?: number; + hour?: number; + minute?: number; + }; + + /** + * Schedule a notification on a particular interval. + * + * @since 1.0.0 + */ every?: | 'year' | 'month' @@ -165,41 +638,146 @@ export interface LocalNotificationSchedule { | 'hour' | 'minute' | 'second'; + + /** + * Limit the number times a notification is delivered by the interval + * specified by `every`. + * + * @since 1.0.0 + */ count?: number; - on?: { - year?: number; - month?: number; - day?: number; - hour?: number; - minute?: number; - }; } -export interface LocalNotificationActionPerformed { +export interface ListChannelsResult { + /** + * The list of notification channels. + * + * @since 1.0.0 + */ + channels: Channel[]; +} + +export interface PermissionStatus { + /** + * Permission state of displaying notifications. + * + * @since 1.0.0 + */ + display: PermissionState; +} + +export interface ActionPerformed { + /** + * The identifier of the performed action. + * + * @since 1.0.0 + */ actionId: string; + + /** + * The value entered by the user on the notification. + * + * Only available on iOS for notifications with `input` set to `true`. + * + * @since 1.0.0 + */ inputValue?: string; - notification: LocalNotification; + + /** + * The original notification schema. + * + * @since 1.0.0 + */ + notification: LocalNotificationSchema; } -export interface LocalNotificationEnabledResult { +/** + * @deprecated + */ +export interface EnabledResult { /** - * Whether the device has Local Notifications enabled or not + * Whether or not the device has local notifications enabled. + * + * @since 1.0.0 */ value: boolean; } -export interface NotificationChannel { +export interface Channel { + /** + * The channel identifier. + * + * @since 1.0.0 + */ id: string; + + /** + * The channel name. + * + * @since 1.0.0 + */ name: string; + + /** + * The channel description. + * + * @since 1.0.0 + */ description?: string; + + /** + * The sound that is played for notifications posted to this channel. + * + * @since 1.0.0 + */ sound?: string; + + /** + * The level of interruption of notifications posted to this channel. + * + * See the `PRIORITY_*` constants of + * [`NotificationCompat`](https://developer.android.com/reference/androidx/core/app/NotificationCompat) + * for more information. + * + * @since 1.0.0 + */ importance: 1 | 2 | 3 | 4 | 5; + + /** + * The visibility level of notifications posted to this channel. + * + * See the `VISIBILITY_*` constants of + * [`NotificationCompat`](https://developer.android.com/reference/androidx/core/app/NotificationCompat) + * for more information. + * + * @since 1.0.0 + */ visibility?: -1 | 0 | 1; + + /** + * Whether or not notifications posted to this channel should display + * notification lights. + * + * @since 1.0.0 + */ lights?: boolean; + + /** + * The color of notification lights when using the `lights` option. + * + * This can be any value that + * [`Color.parseColor()`](https://developer.android.com/reference/android/graphics/Color#parseColor(java.lang.String)) + * expects. + * + * @since 1.0.0 + */ lightColor?: string; - vibration?: boolean; -} -export interface NotificationChannelList { - channels: NotificationChannel[]; + /** + * Whether or not notifications posted to this channel should vibrate the + * device. + * + * @since 1.0.0 + */ + vibration?: boolean; } diff --git a/local-notifications/src/index.ts b/local-notifications/src/index.ts index 8b7e8af51..14423d4ca 100644 --- a/local-notifications/src/index.ts +++ b/local-notifications/src/index.ts @@ -1,22 +1,6 @@ import { registerPlugin } from '@capacitor/core'; -import type { - LocalNotification, - LocalNotificationAction, - LocalNotificationActionPerformed, - LocalNotificationActionType, - LocalNotificationAttachment, - LocalNotificationAttachmentOptions, - LocalNotificationEnabledResult, - LocalNotificationPendingList, - LocalNotificationRequest, - LocalNotificationSchedule, - LocalNotificationScheduleResult, - LocalNotificationsPlugin, - LocalNotificationsPermissionStatus, - NotificationChannel, - NotificationChannelList, -} from './definitions'; +import type { LocalNotificationsPlugin } from './definitions'; const LocalNotifications = registerPlugin( 'LocalNotifications', @@ -25,20 +9,5 @@ const LocalNotifications = registerPlugin( }, ); -export { - LocalNotification, - LocalNotificationAction, - LocalNotificationActionPerformed, - LocalNotificationActionType, - LocalNotificationAttachment, - LocalNotificationAttachmentOptions, - LocalNotificationEnabledResult, - LocalNotificationPendingList, - LocalNotificationRequest, - LocalNotificationSchedule, - LocalNotificationScheduleResult, - LocalNotifications, - LocalNotificationsPermissionStatus, - NotificationChannel, - NotificationChannelList, -}; +export * from './definitions'; +export { LocalNotifications }; diff --git a/local-notifications/src/web.ts b/local-notifications/src/web.ts index 3de45aee7..0faebafca 100644 --- a/local-notifications/src/web.ts +++ b/local-notifications/src/web.ts @@ -2,35 +2,33 @@ import type { PermissionState } from '@capacitor/core'; import { WebPlugin } from '@capacitor/core'; import type { - LocalNotification, - LocalNotificationEnabledResult, - LocalNotificationPendingList, - LocalNotificationScheduleResult, - LocalNotificationsPermissionStatus, + EnabledResult, + ListChannelsResult, + LocalNotificationSchema, LocalNotificationsPlugin, - NotificationChannelList, + PermissionStatus, + ScheduleOptions, + ScheduleResult, } from './definitions'; export class LocalNotificationsWeb extends WebPlugin implements LocalNotificationsPlugin { - protected pending: LocalNotification[] = []; + protected pending: LocalNotificationSchema[] = []; async createChannel(): Promise { - throw this.unavailable('Feature not available for web.'); + throw this.unimplemented('Not implemented on web.'); } async deleteChannel(): Promise { - throw this.unavailable('Feature not available for web.'); + throw this.unimplemented('Not implemented on web.'); } - async listChannels(): Promise { - throw this.unavailable('Feature not available for web.'); + async listChannels(): Promise { + throw this.unimplemented('Not implemented on web.'); } - async schedule(options: { - notifications: LocalNotification[]; - }): Promise { + async schedule(options: ScheduleOptions): Promise { for (const notification of options.notifications) { this.sendNotification(notification); } @@ -42,7 +40,7 @@ export class LocalNotificationsWeb }; } - async getPending(): Promise { + async getPending(): Promise { return { notifications: this.pending.map(notification => ({ id: notification.id.toString(), @@ -51,17 +49,17 @@ export class LocalNotificationsWeb } async registerActionTypes(): Promise { - throw this.unavailable('Feature not available for web.'); + throw this.unimplemented('Not implemented on web.'); } - async cancel(pending: LocalNotificationPendingList): Promise { + async cancel(pending: ScheduleResult): Promise { this.pending = this.pending.filter( notification => !pending.notifications.find(n => n.id === notification.id.toString()), ); } - async areEnabled(): Promise { + async areEnabled(): Promise { const { display } = await this.checkPermissions(); return { @@ -69,7 +67,7 @@ export class LocalNotificationsWeb }; } - async requestPermissions(): Promise { + async requestPermissions(): Promise { const display = this.transformNotificationPermission( await Notification.requestPermission(), ); @@ -77,7 +75,7 @@ export class LocalNotificationsWeb return { display }; } - async checkPermissions(): Promise { + async checkPermissions(): Promise { const display = this.transformNotificationPermission( Notification.permission, ); @@ -99,7 +97,7 @@ export class LocalNotificationsWeb } protected sendPending(): void { - const toRemove: LocalNotification[] = []; + const toRemove: LocalNotificationSchema[] = []; const now = new Date().getTime(); for (const notification of this.pending) { @@ -117,7 +115,7 @@ export class LocalNotificationsWeb ); } - protected sendNotification(notification: LocalNotification): void { + protected sendNotification(notification: LocalNotificationSchema): void { if (notification.schedule?.at) { const diff = notification.schedule.at.getTime() - new Date().getTime(); @@ -128,7 +126,9 @@ export class LocalNotificationsWeb } } - protected buildNotification(notification: LocalNotification): Notification { + protected buildNotification( + notification: LocalNotificationSchema, + ): Notification { return new Notification(notification.title, { body: notification.body, }); From cfccf05ce3ae2f5ea8006adbfd11c8b0ed720860 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 2 Dec 2020 18:01:20 -0800 Subject: [PATCH 30/60] rename listeners --- local-notifications/README.md | 10 +++++----- .../localnotifications/LocalNotificationsPlugin.java | 2 +- .../ios/Plugin/LocalNotificationsDelegate.swift | 4 ++-- local-notifications/src/definitions.ts | 7 +++---- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/local-notifications/README.md b/local-notifications/README.md index 785ca2b05..c99c4fa8f 100644 --- a/local-notifications/README.md +++ b/local-notifications/README.md @@ -206,14 +206,14 @@ Request permission to display local notifications. ### addListener(...) ```typescript -addListener(eventName: 'localNotificationReceived', listenerFunc: (notification: LocalNotificationSchema) => void) => PluginListenerHandle +addListener(eventName: 'received', listenerFunc: (notification: LocalNotificationSchema) => void) => PluginListenerHandle ``` Listen for when notifications are displayed. | Param | Type | | ------------------ | ------------------------------------------------------------------------------------------------------ | -| **`eventName`** | "localNotificationReceived" | +| **`eventName`** | "received" | | **`listenerFunc`** | (notification: LocalNotificationSchema) => void | **Returns:** PluginListenerHandle @@ -226,14 +226,14 @@ Listen for when notifications are displayed. ### addListener(...) ```typescript -addListener(eventName: 'localNotificationActionPerformed', listenerFunc: (notificationAction: ActionPerformed) => void) => PluginListenerHandle +addListener(eventName: 'actionPerformed', listenerFunc: (notificationAction: ActionPerformed) => void) => PluginListenerHandle ``` Listen for when an action is performed on a notification. | Param | Type | | ------------------ | -------------------------------------------------------------------------------------------- | -| **`eventName`** | "localNotificationActionPerformed" | +| **`eventName`** | "actionPerformed" | | **`listenerFunc`** | (notificationAction: ActionPerformed) => void | **Returns:** PluginListenerHandle @@ -427,7 +427,7 @@ An action that can be taken when a notification is displayed. | Prop | Type | Description | Since | | ---------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | -| **`id`** | string | The action identifier. Referenced in the `'localNotificationActionPerformed'` event as `actionId`. | 1.0.0 | +| **`id`** | string | The action identifier. Referenced in the `'actionPerformed'` event as `actionId`. | 1.0.0 | | **`title`** | string | The title text to display for this action. | 1.0.0 | | **`requiresAuthentication`** | boolean | Sets `authenticationRequired` in the options of the [`UNNotificationAction`](https://developer.apple.com/documentation/usernotifications/unnotificationaction). Only available for iOS. | 1.0.0 | | **`foreground`** | boolean | Sets `foreground` in the options of the [`UNNotificationAction`](https://developer.apple.com/documentation/usernotifications/unnotificationaction). Only available for iOS. | 1.0.0 | diff --git a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java index 8936c5d93..2983c99bc 100644 --- a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationsPlugin.java @@ -36,7 +36,7 @@ protected void handleOnNewIntent(Intent data) { } JSObject dataJson = manager.handleNotificationActionPerformed(data, notificationStorage); if (dataJson != null) { - notifyListeners("localNotificationActionPerformed", dataJson, true); + notifyListeners("actionPerformed", dataJson, true); } } diff --git a/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift b/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift index 1b677331a..2ec1b1f13 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift @@ -26,7 +26,7 @@ public class LocalNotificationsDelegate: NSObject, NotificationHandlerProtocol { public func willPresent(notification: UNNotification) -> UNNotificationPresentationOptions { let notificationData = makeNotificationRequestJSObject(notification.request) - self.plugin?.notifyListeners("localNotificationReceived", data: notificationData) + self.plugin?.notifyListeners("received", data: notificationData) if let options = notificationRequestLookup[notification.request.identifier] { let silent = options["silent"] as? Bool ?? false @@ -66,7 +66,7 @@ public class LocalNotificationsDelegate: NSObject, NotificationHandlerProtocol { data["notification"] = makeNotificationRequestJSObject(originalNotificationRequest) - self.plugin?.notifyListeners("localNotificationActionPerformed", data: data, retainUntilConsumed: true) + self.plugin?.notifyListeners("actionPerformed", data: data, retainUntilConsumed: true) } /** diff --git a/local-notifications/src/definitions.ts b/local-notifications/src/definitions.ts index 31319b164..7b8dd1397 100644 --- a/local-notifications/src/definitions.ts +++ b/local-notifications/src/definitions.ts @@ -87,7 +87,7 @@ export interface LocalNotificationsPlugin { * @since 1.0.0 */ addListener( - eventName: 'localNotificationReceived', + eventName: 'received', listenerFunc: (notification: LocalNotificationSchema) => void, ): PluginListenerHandle; @@ -97,7 +97,7 @@ export interface LocalNotificationsPlugin { * @since 1.0.0 */ addListener( - eventName: 'localNotificationActionPerformed', + eventName: 'actionPerformed', listenerFunc: (notificationAction: ActionPerformed) => void, ): PluginListenerHandle; @@ -250,8 +250,7 @@ export interface Action { /** * The action identifier. * - * Referenced in the `'localNotificationActionPerformed'` event as - * `actionId`. + * Referenced in the `'actionPerformed'` event as `actionId`. * * @since 1.0.0 */ From 680da7e57f85e787f3c2ef5f6add59c129c22234 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 2 Dec 2020 18:06:53 -0800 Subject: [PATCH 31/60] deleted files --- local-notifications/CONTRIBUTING.md | 42 ------------------- .../main/res/layout/bridge_layout_main.xml | 15 ------- 2 files changed, 57 deletions(-) delete mode 100644 local-notifications/CONTRIBUTING.md delete mode 100644 local-notifications/android/src/main/res/layout/bridge_layout_main.xml diff --git a/local-notifications/CONTRIBUTING.md b/local-notifications/CONTRIBUTING.md deleted file mode 100644 index 019c41eef..000000000 --- a/local-notifications/CONTRIBUTING.md +++ /dev/null @@ -1,42 +0,0 @@ -# Contributing - -This guide provides instructions for contributing to this Capacitor plugin. - -## Developing - -### Local Setup - -1. Fork and clone the repo. -1. Install the dependencies. - - ```shell - npm install - ``` - -1. Install SwiftLint if you're on macOS. - - ```shell - brew install swiftlint - ``` - -### Scripts - -#### `npm run build` - -Build the plugin web assets and generate plugin API documentation using [`@capacitor/docgen`](https://github.com/ionic-team/capacitor-docgen). - -It will compile the TypeScript code from `src/` into ESM JavaScript in `dist/esm/`. These files are used in apps with bundlers when your plugin is imported. - -Then, Rollup will bundle the code into a single file at `dist/plugin.js`. This file is used in apps without bundlers by including it as a script in `index.html`. - -#### `npm run verify` - -Build and validate the web and native projects. - -This is useful to run in CI to verify that the plugin builds for all platforms. - -#### `npm run lint` / `npm run fmt` - -Check formatting and code quality, autoformat/autofix if possible. - -This template is integrated with ESLint, Prettier, and SwiftLint. Using these tools is completely optional, but the [Capacitor Community](https://github.com/capacitor-community/) strives to have consistent code style and structure for easier cooperation. diff --git a/local-notifications/android/src/main/res/layout/bridge_layout_main.xml b/local-notifications/android/src/main/res/layout/bridge_layout_main.xml deleted file mode 100644 index 56fec1546..000000000 --- a/local-notifications/android/src/main/res/layout/bridge_layout_main.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - From ec453c9344935e2b892703fe6d05855141d86f81 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 3 Dec 2020 11:37:17 -0800 Subject: [PATCH 32/60] add license --- local-notifications/LICENSE | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 local-notifications/LICENSE diff --git a/local-notifications/LICENSE b/local-notifications/LICENSE new file mode 100644 index 000000000..6652495cb --- /dev/null +++ b/local-notifications/LICENSE @@ -0,0 +1,23 @@ +Copyright 2020-present Ionic +https://ionic.io + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From 59880880db2fbe1a001aac9719dd2836fb6861ad Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Fri, 4 Dec 2020 16:28:53 -0800 Subject: [PATCH 33/60] add types for config files --- local-notifications/package.json | 1 + local-notifications/src/definitions.ts | 42 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/local-notifications/package.json b/local-notifications/package.json index 9679955da..6bc4d9e09 100644 --- a/local-notifications/package.json +++ b/local-notifications/package.json @@ -44,6 +44,7 @@ }, "devDependencies": { "@capacitor/android": "^3.0.0-alpha.7", + "@capacitor/cli": "^3.0.0-alpha.7", "@capacitor/core": "^3.0.0-alpha.7", "@capacitor/docgen": "^0.0.10", "@capacitor/ios": "^3.0.0-alpha.7", diff --git a/local-notifications/src/definitions.ts b/local-notifications/src/definitions.ts index 7b8dd1397..4c420d6b0 100644 --- a/local-notifications/src/definitions.ts +++ b/local-notifications/src/definitions.ts @@ -1,5 +1,47 @@ +/// + import type { PermissionState, PluginListenerHandle } from '@capacitor/core'; +declare module '@capacitor/cli' { + export interface PluginsConfig { + LocalNotifications?: { + /** + * Set the default status bar icon for notifications. + * + * Icons should be placed in your app's `res/drawable` folder. The value for + * this option should be the drawable resource ID, which is the filename + * without an extension. + * + * Only available for Android. + * + * @since 1.0.0 + */ + smallIcon?: string; + + /** + * Set the default color of status bar icons for notifications. + * + * Only available for Android. + * + * @since 1.0.0 + */ + iconColor?: string; + + /** + * Set the default notification sound for notifications. + * + * On Android 26+ it sets the default channel sound and can't be + * changed unless the app is uninstalled. + * + * Only available for Android. + * + * @since 1.0.0 + */ + sound?: string; + }; + } +} + export interface LocalNotificationsPlugin { /** * Schedule one or more local notifications. From 81e1a32883034fe03b02212cb860af48becbd8c5 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Fri, 4 Dec 2020 16:29:05 -0800 Subject: [PATCH 34/60] more info about smallIcon --- local-notifications/src/definitions.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/local-notifications/src/definitions.ts b/local-notifications/src/definitions.ts index 4c420d6b0..10cc498fd 100644 --- a/local-notifications/src/definitions.ts +++ b/local-notifications/src/definitions.ts @@ -490,7 +490,12 @@ export interface LocalNotificationSchema { /** * Set a custom status bar icon. * - * If set, this overrides the default icon from Capacitor configuration. + * If set, this overrides the `smallIcon` option from Capacitor + * configuration. + * + * Icons should be placed in your app's `res/drawable` folder. The value for + * this option should be the drawable resource ID, which is the filename + * without an extension. * * Only available for Android. * From c10823a7541e0e7085783c388c61b694bd3cb0a1 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Fri, 4 Dec 2020 17:23:09 -0800 Subject: [PATCH 35/60] better description --- local-notifications/README.md | 2 +- local-notifications/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/local-notifications/README.md b/local-notifications/README.md index c99c4fa8f..7b7397e8b 100644 --- a/local-notifications/README.md +++ b/local-notifications/README.md @@ -1,6 +1,6 @@ # @capacitor/local-notifications -The Notifications API provides access to native local notifications. +The Local Notifications API provides a way to schedule device notifications locally (i.e. without a server sending push notifications). ## Install diff --git a/local-notifications/package.json b/local-notifications/package.json index 6bc4d9e09..8f81555ca 100644 --- a/local-notifications/package.json +++ b/local-notifications/package.json @@ -1,7 +1,7 @@ { "name": "@capacitor/local-notifications", "version": "0.0.1", - "description": "The Notifications API provides access to native local notifications.", + "description": "The Local Notifications API provides a way to schedule device notifications locally (i.e. without a server sending push notifications).", "main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", "unpkg": "dist/plugin.js", From 0d6853fd93913aae501df30bc8bab379fab845cd Mon Sep 17 00:00:00 2001 From: Dan Imhoff Date: Mon, 7 Dec 2020 13:56:26 -0800 Subject: [PATCH 36/60] Update local-notifications/ios/PluginTests/LocalNotificationsPluginTests.swift Co-authored-by: jcesarmobile --- .../PluginTests/LocalNotificationsPluginTests.swift | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/local-notifications/ios/PluginTests/LocalNotificationsPluginTests.swift b/local-notifications/ios/PluginTests/LocalNotificationsPluginTests.swift index cf9b14fc7..8eec98b1a 100644 --- a/local-notifications/ios/PluginTests/LocalNotificationsPluginTests.swift +++ b/local-notifications/ios/PluginTests/LocalNotificationsPluginTests.swift @@ -11,15 +11,4 @@ class LocalNotificationsTests: XCTestCase { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } - - func testEcho() { - // This is an example of a functional test case for a plugin. - // Use XCTAssert and related functions to verify your tests produce the correct results. - - let implementation = LocalNotifications() - let value = "Hello, World!" - let result = implementation.echo(value) - - XCTAssertEqual(value, result) - } } From 5ed43bf90b5de3d56c21ef8ecae9fd24c4f85ea1 Mon Sep 17 00:00:00 2001 From: Dan Imhoff Date: Mon, 7 Dec 2020 13:56:40 -0800 Subject: [PATCH 37/60] Update local-notifications/package.json Co-authored-by: jcesarmobile --- local-notifications/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local-notifications/package.json b/local-notifications/package.json index 8f81555ca..475966f77 100644 --- a/local-notifications/package.json +++ b/local-notifications/package.json @@ -60,7 +60,7 @@ "typescript": "~4.0.3" }, "peerDependencies": { - "@capacitor/core": "^3.0.0-alpha.6" + "@capacitor/core": "^3.0.0-alpha.7" }, "prettier": "@ionic/prettier-config", "swiftlint": "@ionic/swiftlint-config", From 51d879f4c5846a7f8f5cb7b6bffb52de5bc53b09 Mon Sep 17 00:00:00 2001 From: Dan Imhoff Date: Mon, 7 Dec 2020 13:57:00 -0800 Subject: [PATCH 38/60] Update local-notifications/android/src/main/AndroidManifest.xml Co-authored-by: jcesarmobile --- local-notifications/android/src/main/AndroidManifest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/local-notifications/android/src/main/AndroidManifest.xml b/local-notifications/android/src/main/AndroidManifest.xml index 33c1430fc..ff9a54bee 100644 --- a/local-notifications/android/src/main/AndroidManifest.xml +++ b/local-notifications/android/src/main/AndroidManifest.xml @@ -15,4 +15,6 @@ + + From 67f9d78b97007fc77413f40f9ff08f400e71e2c9 Mon Sep 17 00:00:00 2001 From: Dan Imhoff Date: Mon, 7 Dec 2020 13:57:11 -0800 Subject: [PATCH 39/60] Update local-notifications/src/definitions.ts Co-authored-by: jcesarmobile --- local-notifications/src/definitions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local-notifications/src/definitions.ts b/local-notifications/src/definitions.ts index 10cc498fd..5c1743f6b 100644 --- a/local-notifications/src/definitions.ts +++ b/local-notifications/src/definitions.ts @@ -266,7 +266,7 @@ export interface ActionType { * Sets `hiddenPreviewsShowTitle` in the options of the * [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). * - * Only available for iOS 11+. + * Only available for iOS. * * @since 1.0.0 */ From 53f0ba7369195b698d271bc40731862c1ef8faea Mon Sep 17 00:00:00 2001 From: Dan Imhoff Date: Mon, 7 Dec 2020 13:57:18 -0800 Subject: [PATCH 40/60] Update local-notifications/src/definitions.ts Co-authored-by: jcesarmobile --- local-notifications/src/definitions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local-notifications/src/definitions.ts b/local-notifications/src/definitions.ts index 5c1743f6b..853da8bac 100644 --- a/local-notifications/src/definitions.ts +++ b/local-notifications/src/definitions.ts @@ -236,7 +236,7 @@ export interface ActionType { * Sets `hiddenPreviewsBodyPlaceholder` of the * [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). * - * Only available for iOS 11+. + * Only available for iOS. * * @since 1.0.0 */ From 53b39a95de994f165a5bfef1d795846df52d489e Mon Sep 17 00:00:00 2001 From: Dan Imhoff Date: Mon, 7 Dec 2020 13:57:25 -0800 Subject: [PATCH 41/60] Update local-notifications/src/definitions.ts Co-authored-by: jcesarmobile --- local-notifications/src/definitions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local-notifications/src/definitions.ts b/local-notifications/src/definitions.ts index 853da8bac..fac5c51ab 100644 --- a/local-notifications/src/definitions.ts +++ b/local-notifications/src/definitions.ts @@ -240,7 +240,7 @@ export interface ActionType { * * @since 1.0.0 */ - iosHiddenPreviewsBodyPlaceholder?: string; // >= iOS 11 only + iosHiddenPreviewsBodyPlaceholder?: string; /** * Sets `customDismissAction` in the options of the From 1b86dd6ac28263b8023e184c7cd32708a2182afd Mon Sep 17 00:00:00 2001 From: Dan Imhoff Date: Mon, 7 Dec 2020 13:57:33 -0800 Subject: [PATCH 42/60] Update local-notifications/src/definitions.ts Co-authored-by: jcesarmobile --- local-notifications/src/definitions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local-notifications/src/definitions.ts b/local-notifications/src/definitions.ts index fac5c51ab..2414d652b 100644 --- a/local-notifications/src/definitions.ts +++ b/local-notifications/src/definitions.ts @@ -276,7 +276,7 @@ export interface ActionType { * Sets `hiddenPreviewsShowSubtitle` in the options of the * [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). * - * Only available for iOS 11+. + * Only available for iOS. * * @since 1.0.0 */ From 9313e6c1eeda70a9e4ddae29071f8e7738e815f9 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 7 Dec 2020 13:54:17 -0800 Subject: [PATCH 43/60] handle checkPermissions --- .../ios/Plugin/LocalNotificationsDelegate.swift | 6 +++--- .../ios/Plugin/LocalNotificationsPlugin.swift | 17 ++++++++++++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift b/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift index 2ec1b1f13..1030b9bdd 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift @@ -16,10 +16,10 @@ public class LocalNotificationsDelegate: NSObject, NotificationHandlerProtocol { } } - public func checkPermissions(with completion: ((Bool, Error?) -> Void)? = nil) { + public func checkPermissions(with completion: ((UNAuthorizationStatus) -> Void)? = nil) { let center = UNUserNotificationCenter.current() - center.requestAuthorization(options: [.badge, .alert, .sound]) { (granted, error) in - completion?(granted, error) + center.getNotificationSettings { settings in + completion?(settings.authorizationStatus) } } diff --git a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift index d42a74c5e..df5c01a76 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift @@ -110,7 +110,22 @@ public class LocalNotificationsPlugin: CAPPlugin { } @objc override public func checkPermissions(_ call: CAPPluginCall) { - call.success(["display": "prompt"]) // TODO + self.notificationDelegationHandler.checkPermissions { status in + let permission: String + + switch status { + case .authorized, .ephemeral, .provisional: + permission = "granted" + case .denied: + permission = "denied" + case .notDetermined: + permission = "prompt" + @unknown default: + permission = "prompt" + } + + call.success(["display": permission]) + } } /** From 908e6fec578f8ff79b2cb155c6e31142d929e33d Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 7 Dec 2020 13:58:25 -0800 Subject: [PATCH 44/60] regen docs --- local-notifications/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/local-notifications/README.md b/local-notifications/README.md index 7b7397e8b..3ea264f31 100644 --- a/local-notifications/README.md +++ b/local-notifications/README.md @@ -291,7 +291,7 @@ The object that describes a local notification. | **`id`** | number | The notification identifier. | 1.0.0 | | **`schedule`** | Schedule | Schedule this notification for a later time. | 1.0.0 | | **`sound`** | string | Name of the audio file to play when this notification is displayed. Include the file extension with the filename. On iOS, the file should be in the app bundle. On Android, the file should be in res/raw folder. Recommended format is `.wav` because is supported by both iOS and Android. Only available for iOS and Android 26+. | 1.0.0 | -| **`smallIcon`** | string | Set a custom status bar icon. If set, this overrides the default icon from Capacitor configuration. Only available for Android. | 1.0.0 | +| **`smallIcon`** | string | Set a custom status bar icon. If set, this overrides the `smallIcon` option from Capacitor configuration. Icons should be placed in your app's `res/drawable` folder. The value for this option should be the drawable resource ID, which is the filename without an extension. Only available for Android. | 1.0.0 | | **`iconColor`** | string | Set the color of the notification icon. Only available for Android. | 1.0.0 | | **`attachments`** | Attachment[] | Set attachments for this notification. | 1.0.0 | | **`actionTypeId`** | string | Associate an action type with this notification. | 1.0.0 | @@ -410,15 +410,15 @@ Represents a notification attachment. A collection of actions. -| Prop | Type | Description | Since | -| -------------------------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | -| **`id`** | string | The ID of the action type. Referenced in notifications by the `actionTypeId` key. | 1.0.0 | -| **`actions`** | Action[] | The list of actions associated with this action type. | 1.0.0 | -| **`iosHiddenPreviewsBodyPlaceholder`** | string | Sets `hiddenPreviewsBodyPlaceholder` of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS 11+. | 1.0.0 | -| **`iosCustomDismissAction`** | boolean | Sets `customDismissAction` in the options of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS. | 1.0.0 | -| **`iosAllowInCarPlay`** | boolean | Sets `allowInCarPlay` in the options of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS. | 1.0.0 | -| **`iosHiddenPreviewsShowTitle`** | boolean | Sets `hiddenPreviewsShowTitle` in the options of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS 11+. | 1.0.0 | -| **`iosHiddenPreviewsShowSubtitle`** | boolean | Sets `hiddenPreviewsShowSubtitle` in the options of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS 11+. | 1.0.0 | +| Prop | Type | Description | Since | +| -------------------------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | +| **`id`** | string | The ID of the action type. Referenced in notifications by the `actionTypeId` key. | 1.0.0 | +| **`actions`** | Action[] | The list of actions associated with this action type. | 1.0.0 | +| **`iosHiddenPreviewsBodyPlaceholder`** | string | Sets `hiddenPreviewsBodyPlaceholder` of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS. | 1.0.0 | +| **`iosCustomDismissAction`** | boolean | Sets `customDismissAction` in the options of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS. | 1.0.0 | +| **`iosAllowInCarPlay`** | boolean | Sets `allowInCarPlay` in the options of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS. | 1.0.0 | +| **`iosHiddenPreviewsShowTitle`** | boolean | Sets `hiddenPreviewsShowTitle` in the options of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS. | 1.0.0 | +| **`iosHiddenPreviewsShowSubtitle`** | boolean | Sets `hiddenPreviewsShowSubtitle` in the options of the [`UNNotificationCategory`](https://developer.apple.com/documentation/usernotifications/unnotificationcategory). Only available for iOS. | 1.0.0 | #### Action From 25286323d9cbfbacdfcce8d448cc2565d9674ede Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 7 Dec 2020 14:06:54 -0800 Subject: [PATCH 45/60] replace deprecated methods --- .../ios/Plugin/LocalNotificationsPlugin.swift | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift index df5c01a76..98fd3cafa 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift @@ -37,14 +37,14 @@ public class LocalNotificationsPlugin: CAPPlugin { */ @objc func schedule(_ call: CAPPluginCall) { guard let notifications = call.getArray("notifications", JSObject.self) else { - call.error("Must provide notifications array as notifications option") + call.reject("Must provide notifications array as notifications option") return } var ids = [String]() for notification in notifications { guard let identifier = notification["id"] as? Int else { - call.error("Notification missing identifier") + call.reject("Notification missing identifier") return } @@ -55,7 +55,7 @@ public class LocalNotificationsPlugin: CAPPlugin { content = try makeNotificationContent(notification) } catch { CAPLog.print(error.localizedDescription) - call.error("Unable to make notification", error) + call.reject("Unable to make notification", nil, error) return } @@ -66,7 +66,7 @@ public class LocalNotificationsPlugin: CAPPlugin { try trigger = handleScheduledNotification(call, schedule) } } catch { - call.error("Unable to create notification, trigger failed", error) + call.reject("Unable to create notification, trigger failed", nil, error) return } @@ -79,7 +79,7 @@ public class LocalNotificationsPlugin: CAPPlugin { center.add(request) { (error: Error?) in if let theError = error { CAPLog.print(theError.localizedDescription) - call.error(theError.localizedDescription) + call.reject(theError.localizedDescription) } } @@ -91,7 +91,7 @@ public class LocalNotificationsPlugin: CAPPlugin { "id": id ] }) - call.success([ + call.resolve([ "notifications": ret ]) } @@ -102,10 +102,10 @@ public class LocalNotificationsPlugin: CAPPlugin { @objc override public func requestPermissions(_ call: CAPPluginCall) { self.notificationDelegationHandler.requestPermissions { granted, error in guard error == nil else { - call.error(error!.localizedDescription) + call.reject(error!.localizedDescription) return } - call.success(["display": granted ? "granted" : "denied"]) + call.resolve(["display": granted ? "granted" : "denied"]) } } @@ -124,7 +124,7 @@ public class LocalNotificationsPlugin: CAPPlugin { permission = "prompt" } - call.success(["display": permission]) + call.resolve(["display": permission]) } } @@ -133,14 +133,14 @@ public class LocalNotificationsPlugin: CAPPlugin { */ @objc func cancel(_ call: CAPPluginCall) { guard let notifications = call.getArray("notifications", JSObject.self), notifications.count > 0 else { - call.error("Must supply notifications to cancel") + call.reject("Must supply notifications to cancel") return } let ids = notifications.map { $0["id"] as? String ?? "" } UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ids) - call.success() + call.resolve() } /** @@ -154,7 +154,7 @@ public class LocalNotificationsPlugin: CAPPlugin { let ret = notifications.compactMap({ [weak self] (notification) -> JSObject? in return self?.makePendingNotificationRequestJSObject(notification) }) - call.success([ + call.resolve([ "notifications": ret ]) }) @@ -170,7 +170,7 @@ public class LocalNotificationsPlugin: CAPPlugin { makeActionTypes(types) - call.success() + call.resolve() } /** @@ -181,7 +181,7 @@ public class LocalNotificationsPlugin: CAPPlugin { center.getNotificationSettings { (settings) in let authorized = settings.authorizationStatus == UNAuthorizationStatus.authorized let enabled = settings.notificationCenterSetting == UNNotificationSetting.enabled - call.success([ + call.resolve([ "value": enabled && authorized ]) } @@ -247,7 +247,7 @@ public class LocalNotificationsPlugin: CAPPlugin { let dateInfo = Calendar.current.dateComponents(in: TimeZone.current, from: at) if dateInfo.date! < Date() { - call.error("Scheduled time must be *after* current time") + call.reject("Scheduled time must be *after* current time") return nil } @@ -359,7 +359,7 @@ public class LocalNotificationsPlugin: CAPPlugin { createdCategories.append(generalCategory) for type in actionTypes { guard let id = type["id"] as? String else { - bridge?.modulePrint(self, "Action type must have an id field") + CAPLog.print("⚡️ ", self.pluginId, "-", "Action type must have an id field") continue } let hiddenBodyPlaceholder = type["iosHiddenPreviewsBodyPlaceholder"] as? String ?? "" @@ -391,7 +391,7 @@ public class LocalNotificationsPlugin: CAPPlugin { for action in actions { guard let id = action["id"] as? String else { - bridge?.modulePrint(self, "Action must have an id field") + CAPLog.print("⚡️ ", self.pluginId, "-", "Action must have an id field") continue } let title = action["title"] as? String ?? "" From 72c08160d7294fa4c1b062d002769e8ff38c65fa Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 7 Dec 2020 15:05:07 -0800 Subject: [PATCH 46/60] export deprecated types --- local-notifications/src/definitions.ts | 80 ++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/local-notifications/src/definitions.ts b/local-notifications/src/definitions.ts index 2414d652b..230efacc4 100644 --- a/local-notifications/src/definitions.ts +++ b/local-notifications/src/definitions.ts @@ -89,7 +89,7 @@ export interface LocalNotificationsPlugin { * * @since 1.0.0 */ - createChannel(channel: Channel): Promise; + createChannel(channel: NotificationChannel): Promise; /** * Delete a notification channel. @@ -98,7 +98,7 @@ export interface LocalNotificationsPlugin { * * @since 1.0.0 */ - deleteChannel(channel: Channel): Promise; + deleteChannel(channel: NotificationChannel): Promise; /** * Get a list of notification channels. @@ -700,7 +700,7 @@ export interface ListChannelsResult { * * @since 1.0.0 */ - channels: Channel[]; + channels: NotificationChannel[]; } export interface PermissionStatus { @@ -749,7 +749,7 @@ export interface EnabledResult { value: boolean; } -export interface Channel { +export interface NotificationChannel { /** * The channel identifier. * @@ -827,3 +827,75 @@ export interface Channel { */ vibration?: boolean; } + +/** + * @deprecated Use `LocalNotificationDescriptor`. + * @since 1.0.0 + */ +export type LocalNotificationRequest = LocalNotificationDescriptor; + +/** + * @deprecated Use `ScheduleResult`. + * @since 1.0.0 + */ +export type LocalNotificationScheduleResult = ScheduleResult; + +/** + * @deprecated Use `PendingResult`. + * @since 1.0.0 + */ +export type LocalNotificationPendingList = PendingResult; + +/** + * @deprecated Use `ActionType`. + * @since 1.0.0 + */ +export type LocalNotificationActionType = ActionType; + +/** + * @deprecated Use `Action`. + * @since 1.0.0 + */ +export type LocalNotificationAction = Action; + +/** + * @deprecated Use `EnabledResult`. + * @since 1.0.0 + */ +export type LocalNotificationEnabledResult = EnabledResult; + +/** + * @deprecated Use `ListChannelsResult`. + * @since 1.0.0 + */ +export type NotificationChannelList = ListChannelsResult; + +/** + * @deprecated Use `Attachment`. + * @since 1.0.0 + */ +export type LocalNotificationAttachment = Attachment; + +/** + * @deprecated Use `AttachmentOptions`. + * @since 1.0.0 + */ +export type LocalNotificationAttachmentOptions = AttachmentOptions; + +/** + * @deprecated Use `LocalNotificationSchema`. + * @since 1.0.0 + */ +export type LocalNotification = LocalNotificationSchema; + +/** + * @deprecated Use `Schedule`. + * @since 1.0.0 + */ +export type LocalNotificationSchedule = Schedule; + +/** + * @deprecated Use `ActionPerformed`. + * @since 1.0.0 + */ +export type LocalNotificationActionPerformed = ActionPerformed; From 65b9dbfa228580e5f444471be74a3324aa1ee2bc Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 7 Dec 2020 15:21:23 -0800 Subject: [PATCH 47/60] regen docs --- local-notifications/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/local-notifications/README.md b/local-notifications/README.md index 3ea264f31..3aa0cecd8 100644 --- a/local-notifications/README.md +++ b/local-notifications/README.md @@ -121,16 +121,16 @@ Check if notifications are enabled or not. ### createChannel(...) ```typescript -createChannel(channel: Channel) => Promise +createChannel(channel: NotificationChannel) => Promise ``` Create a notification channel. Only available for Android. -| Param | Type | -| ------------- | ------------------------------------------- | -| **`channel`** | Channel | +| Param | Type | +| ------------- | ------------------------------------------------------------------- | +| **`channel`** | NotificationChannel | **Since:** 1.0.0 @@ -140,16 +140,16 @@ Only available for Android. ### deleteChannel(...) ```typescript -deleteChannel(channel: Channel) => Promise +deleteChannel(channel: NotificationChannel) => Promise ``` Delete a notification channel. Only available for Android. -| Param | Type | -| ------------- | ------------------------------------------- | -| **`channel`** | Channel | +| Param | Type | +| ------------- | ------------------------------------------------------------------- | +| **`channel`** | NotificationChannel | **Since:** 1.0.0 @@ -451,7 +451,7 @@ An action that can be taken when a notification is displayed. | **`value`** | boolean | Whether or not the device has local notifications enabled. | 1.0.0 | -#### Channel +#### NotificationChannel | Prop | Type | Description | Since | | ----------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | @@ -468,9 +468,9 @@ An action that can be taken when a notification is displayed. #### ListChannelsResult -| Prop | Type | Description | Since | -| -------------- | ---------------------- | ---------------------------------- | ----- | -| **`channels`** | Channel[] | The list of notification channels. | 1.0.0 | +| Prop | Type | Description | Since | +| -------------- | ---------------------------------- | ---------------------------------- | ----- | +| **`channels`** | NotificationChannel[] | The list of notification channels. | 1.0.0 | #### PermissionStatus From e2e07cd186bea136692d245442b2f6ff51ce91fb Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Tue, 15 Dec 2020 17:44:31 -0800 Subject: [PATCH 48/60] update to alpha.10 --- local-notifications/package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/local-notifications/package.json b/local-notifications/package.json index 475966f77..dd447c2bd 100644 --- a/local-notifications/package.json +++ b/local-notifications/package.json @@ -43,11 +43,11 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.7", - "@capacitor/cli": "^3.0.0-alpha.7", - "@capacitor/core": "^3.0.0-alpha.7", + "@capacitor/android": "^3.0.0-alpha.10", + "@capacitor/cli": "^3.0.0-alpha.9", + "@capacitor/core": "^3.0.0-alpha.9", "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.7", + "@capacitor/ios": "^3.0.0-alpha.9", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", @@ -60,7 +60,7 @@ "typescript": "~4.0.3" }, "peerDependencies": { - "@capacitor/core": "^3.0.0-alpha.7" + "@capacitor/core": "^3.0.0-alpha.9" }, "prettier": "@ionic/prettier-config", "swiftlint": "@ionic/swiftlint-config", From 7bd26dcc14bdbc778c81c11e57c4d7e12d2ace7a Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 28 Dec 2020 13:46:08 -0800 Subject: [PATCH 49/60] update to alpha.11 --- local-notifications/README.md | 29 +++++++++++++++++++---------- local-notifications/package.json | 8 ++++---- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/local-notifications/README.md b/local-notifications/README.md index 3aa0cecd8..90cf7e813 100644 --- a/local-notifications/README.md +++ b/local-notifications/README.md @@ -23,10 +23,11 @@ npx cap sync * [`listChannels()`](#listchannels) * [`checkPermissions()`](#checkpermissions) * [`requestPermissions()`](#requestpermissions) -* [`addListener(...)`](#addlistener) -* [`addListener(...)`](#addlistener) +* [`addListener('received', ...)`](#addlistenerreceived-) +* [`addListener('actionPerformed', ...)`](#addlisteneractionperformed-) * [`removeAllListeners()`](#removealllisteners) * [Interfaces](#interfaces) +* [Type Aliases](#type-aliases) @@ -203,7 +204,7 @@ Request permission to display local notifications. -------------------- -### addListener(...) +### addListener('received', ...) ```typescript addListener(eventName: 'received', listenerFunc: (notification: LocalNotificationSchema) => void) => PluginListenerHandle @@ -213,7 +214,7 @@ Listen for when notifications are displayed. | Param | Type | | ------------------ | ------------------------------------------------------------------------------------------------------ | -| **`eventName`** | "received" | +| **`eventName`** | 'received' | | **`listenerFunc`** | (notification: LocalNotificationSchema) => void | **Returns:** PluginListenerHandle @@ -223,7 +224,7 @@ Listen for when notifications are displayed. -------------------- -### addListener(...) +### addListener('actionPerformed', ...) ```typescript addListener(eventName: 'actionPerformed', listenerFunc: (notificationAction: ActionPerformed) => void) => PluginListenerHandle @@ -233,7 +234,7 @@ Listen for when an action is performed on a notification. | Param | Type | | ------------------ | -------------------------------------------------------------------------------------------- | -| **`eventName`** | "actionPerformed" | +| **`eventName`** | 'actionPerformed' | | **`listenerFunc`** | (notificationAction: ActionPerformed) => void | **Returns:** PluginListenerHandle @@ -316,7 +317,7 @@ Use either `at`, `on`, or `every` to schedule notifications. | **`at`** | Date | Schedule a notification at a specific date and time. | 1.0.0 | | **`repeats`** | boolean | Repeat delivery of this notification at the date and time specified by `at`. Only available for iOS and Android. | 1.0.0 | | **`on`** | { year?: number; month?: number; day?: number; hour?: number; minute?: number; } | Schedule a notification on particular interval(s). This is similar to scheduling [cron](https://en.wikipedia.org/wiki/Cron) jobs. Only available for iOS and Android. | 1.0.0 | -| **`every`** | "year" \| "month" \| "two-weeks" \| "week" \| "day" \| "hour" \| "minute" \| "second" | Schedule a notification on a particular interval. | 1.0.0 | +| **`every`** | 'year' \| 'month' \| 'two-weeks' \| 'week' \| 'day' \| 'hour' \| 'minute' \| 'second' | Schedule a notification on a particular interval. | 1.0.0 | | **`count`** | number | Limit the number times a notification is delivered by the interval specified by `every`. | 1.0.0 | @@ -475,9 +476,9 @@ An action that can be taken when a notification is displayed. #### PermissionStatus -| Prop | Type | Description | Since | -| ------------- | ------------------------------------------------------------------------- | --------------------------------------------- | ----- | -| **`display`** | "prompt" \| "prompt-with-rationale" \| "granted" \| "denied" | Permission state of displaying notifications. | 1.0.0 | +| Prop | Type | Description | Since | +| ------------- | ----------------------------------------------------------- | --------------------------------------------- | ----- | +| **`display`** | PermissionState | Permission state of displaying notifications. | 1.0.0 | #### PluginListenerHandle @@ -495,4 +496,12 @@ An action that can be taken when a notification is displayed. | **`inputValue`** | string | The value entered by the user on the notification. Only available on iOS for notifications with `input` set to `true`. | 1.0.0 | | **`notification`** | LocalNotificationSchema | The original notification schema. | 1.0.0 | + +### Type Aliases + + +#### PermissionState + +'prompt' | 'prompt-with-rationale' | 'granted' | 'denied' + diff --git a/local-notifications/package.json b/local-notifications/package.json index dd447c2bd..f46a696a9 100644 --- a/local-notifications/package.json +++ b/local-notifications/package.json @@ -43,11 +43,11 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.10", - "@capacitor/cli": "^3.0.0-alpha.9", + "@capacitor/android": "^3.0.0-alpha.11", + "@capacitor/cli": "^3.0.0-alpha.11", "@capacitor/core": "^3.0.0-alpha.9", - "@capacitor/docgen": "^0.0.10", - "@capacitor/ios": "^3.0.0-alpha.9", + "@capacitor/docgen": "0.0.14", + "@capacitor/ios": "^3.0.0-alpha.11", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", From 02ad6a9db45a8a3923d4b9e3b166aae0beed4567 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 28 Dec 2020 13:50:36 -0800 Subject: [PATCH 50/60] use loadDefault --- .../localnotifications/LocalNotificationRestoreReceiver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationRestoreReceiver.java b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationRestoreReceiver.java index 06f62ce1a..e365942bc 100644 --- a/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationRestoreReceiver.java +++ b/local-notifications/android/src/main/java/com/capacitorjs/plugins/localnotifications/LocalNotificationRestoreReceiver.java @@ -50,7 +50,7 @@ public void onReceive(Context context, Intent intent) { storage.appendNotifications(updatedNotifications); } - CapConfig config = new CapConfig(context.getAssets(), null); + CapConfig config = CapConfig.loadDefault(context); LocalNotificationManager localNotificationManager = new LocalNotificationManager(storage, null, context, config); localNotificationManager.schedule(null, notifications); From 86952285ca4aa3d817d43a8ffc58ed431c75b405 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 7 Jan 2021 10:36:39 -0800 Subject: [PATCH 51/60] add plugin to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a75ca4962..5a551f85c 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ This repository contains the official Capacitor plugins maintained by the Capaci | [`@capacitor/geolocation`](https://capacitorjs.com/docs/v3/apis/geolocation) | [`./geolocation`](./geolocation) | [![npm badge](https://img.shields.io/npm/v/@capacitor/geolocation?style=flat-square)](https://www.npmjs.com/package/@capacitor/geolocation) | [`@capacitor/haptics`](https://capacitorjs.com/docs/v3/apis/haptics) | [`./haptics`](./haptics) | [![npm badge](https://img.shields.io/npm/v/@capacitor/haptics?style=flat-square)](https://www.npmjs.com/package/@capacitor/haptics) | [`@capacitor/keyboard`](https://capacitorjs.com/docs/v3/apis/keyboard) | [`./keyboard`](./keyboard) | [![npm badge](https://img.shields.io/npm/v/@capacitor/keyboard?style=flat-square)](https://www.npmjs.com/package/@capacitor/keyboard) +| [`@capacitor/local-notifications`](https://capacitorjs.com/docs/v3/apis/local-notifications) | [`./local-notifications`](./local-notifications) | [![npm badge](https://img.shields.io/npm/v/@capacitor/local-notifications?style=flat-square)](https://www.npmjs.com/package/@capacitor/local-notifications) | [`@capacitor/motion`](https://capacitorjs.com/docs/v3/apis/motion) | [`./motion`](./motion) | [![npm badge](https://img.shields.io/npm/v/@capacitor/motion?style=flat-square)](https://www.npmjs.com/package/@capacitor/motion) | [`@capacitor/network`](https://capacitorjs.com/docs/v3/apis/network) | [`./network`](./network) | [![npm badge](https://img.shields.io/npm/v/@capacitor/network?style=flat-square)](https://www.npmjs.com/package/@capacitor/network) | [`@capacitor/screen-reader`](https://capacitorjs.com/docs/v3/apis/screen-reader) | [`./screen-reader`](./screen-reader) | [![npm badge](https://img.shields.io/npm/v/@capacitor/screen-reader?style=flat-square)](https://www.npmjs.com/package/@capacitor/screen-reader) From 23501c87b82a3167580a09637055b345c488c056 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Fri, 8 Jan 2021 13:41:53 -0600 Subject: [PATCH 52/60] Fixing schedule date parsing, adding more useful fields to pendingNotification JS object --- .../ios/Plugin/LocalNotificationsPlugin.swift | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift index 98fd3cafa..1c5346460 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift @@ -234,8 +234,8 @@ public class LocalNotificationsPlugin: CAPPlugin { */ func handleScheduledNotification(_ call: CAPPluginCall, _ schedule: JSObject) throws -> UNNotificationTrigger? { var at: Date? - if let dateString = schedule["at"] as? String, let date = CAPPluginCall.jsDateFormatter.date(from: dateString) { - at = date + if let scheduleDate = schedule["at"] as? NSDate { + at = scheduleDate as Date } let every = schedule["every"] as? String let count = schedule["count"] as? Int ?? 1 @@ -533,9 +533,19 @@ public class LocalNotificationsPlugin: CAPPlugin { } func makePendingNotificationRequestJSObject(_ request: UNNotificationRequest) -> JSObject { - return [ - "id": request.identifier + var jsObject: JSObject = [ + "id": request.identifier, + "title": request.content.title, + "repeats": request.trigger?.repeats ?? false, ] + + if let trigger = request.trigger as? UNTimeIntervalNotificationTrigger { + let interval = trigger.timeInterval + let scheduleDate = Date(timeIntervalSinceNow: interval) + jsObject["schedule"] = ISO8601DateFormatter().string(from: scheduleDate) + } + + return jsObject } @objc func createChannel(_ call: CAPPluginCall) { From 8cb9d836c3ea7b030a4c067e4ca1933b03794d46 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Tue, 12 Jan 2021 12:30:23 -0600 Subject: [PATCH 53/60] Renaming NotificationChannel to Channel and adding Channel docs --- local-notifications/src/definitions.ts | 52 +++++++++++++++----------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/local-notifications/src/definitions.ts b/local-notifications/src/definitions.ts index 230efacc4..17e8984f0 100644 --- a/local-notifications/src/definitions.ts +++ b/local-notifications/src/definitions.ts @@ -700,7 +700,7 @@ export interface ListChannelsResult { * * @since 1.0.0 */ - channels: NotificationChannel[]; + channels: Channel[]; } export interface PermissionStatus { @@ -749,7 +749,7 @@ export interface EnabledResult { value: boolean; } -export interface NotificationChannel { +export interface Channel { /** * The channel identifier. * @@ -758,76 +758,84 @@ export interface NotificationChannel { id: string; /** - * The channel name. + * The human-friendly name of this channel (presented to the user). * * @since 1.0.0 */ name: string; /** - * The channel description. + * The description of this channel (presented to the user). * * @since 1.0.0 */ description?: string; /** - * The sound that is played for notifications posted to this channel. + * The sound that should be played for notifications posted to this channel. + * + * Notification channels with an importance of at least `3` should have a + * sound. + * + * The file name of a sound file should be specified relative to the android + * app `res/raw` directory. * * @since 1.0.0 + * @example "jingle.wav" */ sound?: string; /** - * The level of interruption of notifications posted to this channel. - * - * See the `PRIORITY_*` constants of - * [`NotificationCompat`](https://developer.android.com/reference/androidx/core/app/NotificationCompat) - * for more information. + * The level of interruption for notifications posted to this channel. * * @since 1.0.0 */ importance: 1 | 2 | 3 | 4 | 5; /** - * The visibility level of notifications posted to this channel. + * The visibility of notifications posted to this channel. * - * See the `VISIBILITY_*` constants of - * [`NotificationCompat`](https://developer.android.com/reference/androidx/core/app/NotificationCompat) - * for more information. + * This setting is for whether notifications posted to this channel appear on + * the lockscreen or not, and if so, whether they appear in a redacted form. * * @since 1.0.0 */ visibility?: -1 | 0 | 1; /** - * Whether or not notifications posted to this channel should display - * notification lights. + * Whether notifications posted to this channel should display notification + * lights, on devices that support it. * * @since 1.0.0 */ lights?: boolean; /** - * The color of notification lights when using the `lights` option. + * The light color for notifications posted to this channel. + * + * Only supported if lights are enabled on this channel and the device + * supports it. * - * This can be any value that - * [`Color.parseColor()`](https://developer.android.com/reference/android/graphics/Color#parseColor(java.lang.String)) - * expects. + * Supported color formats are `#RRGGBB` and `#RRGGBBAA`. * * @since 1.0.0 */ lightColor?: string; /** - * Whether or not notifications posted to this channel should vibrate the - * device. + * Whether notifications posted to this channel should vibrate. * * @since 1.0.0 */ vibration?: boolean; } +/** + * @deprecated Use 'Channel`. + * @since 1.0.0 + */ +export type NotificationChannel = Channel; + /** * @deprecated Use `LocalNotificationDescriptor`. * @since 1.0.0 From 5990beb1824f40496a982406662a6d0462884838 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Tue, 12 Jan 2021 16:17:05 -0600 Subject: [PATCH 54/60] Updating target iOS version --- .../CapacitorLocalNotifications.podspec | 2 +- .../ios/Plugin.xcodeproj/project.pbxproj | 12 +++++++----- .../xcshareddata/xcschemes/Plugin.xcscheme | 2 +- .../xcshareddata/xcschemes/PluginTests.xcscheme | 2 +- local-notifications/ios/Podfile | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/local-notifications/CapacitorLocalNotifications.podspec b/local-notifications/CapacitorLocalNotifications.podspec index 3b2a96f33..5f674e205 100644 --- a/local-notifications/CapacitorLocalNotifications.podspec +++ b/local-notifications/CapacitorLocalNotifications.podspec @@ -11,7 +11,7 @@ Pod::Spec.new do |s| s.author = package['author'] s.source = { :git => package['repository']['url'], :tag => s.version.to_s } s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}' - s.ios.deployment_target = '11.0' + s.ios.deployment_target = '12.0' s.dependency 'Capacitor' s.swift_version = '5.1' end diff --git a/local-notifications/ios/Plugin.xcodeproj/project.pbxproj b/local-notifications/ios/Plugin.xcodeproj/project.pbxproj index 6dd7c1b23..5696b5618 100644 --- a/local-notifications/ios/Plugin.xcodeproj/project.pbxproj +++ b/local-notifications/ios/Plugin.xcodeproj/project.pbxproj @@ -191,7 +191,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1160; + LastUpgradeCheck = 1230; ORGANIZATIONNAME = "Max Lynch"; TargetAttributes = { 50ADFF87201F53D600D50D53 = { @@ -356,6 +356,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -386,7 +387,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -422,6 +423,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -446,7 +448,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -469,7 +471,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Plugin/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)"; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; @@ -494,7 +496,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Plugin/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)"; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; diff --git a/local-notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme b/local-notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme index 303f2621b..901886c9b 100644 --- a/local-notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme +++ b/local-notifications/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme @@ -1,6 +1,6 @@ Date: Tue, 12 Jan 2021 16:17:57 -0600 Subject: [PATCH 55/60] Renaming LocalNotificationDelegate to LocalNotificationHandler --- local-notifications/ios/Plugin.xcodeproj/project.pbxproj | 8 ++++---- ...ionsDelegate.swift => LocalNotificationsHandler.swift} | 4 ++-- .../ios/Plugin/LocalNotificationsPlugin.swift | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename local-notifications/ios/Plugin/{LocalNotificationsDelegate.swift => LocalNotificationsHandler.swift} (96%) diff --git a/local-notifications/ios/Plugin.xcodeproj/project.pbxproj b/local-notifications/ios/Plugin.xcodeproj/project.pbxproj index 5696b5618..027ff9b47 100644 --- a/local-notifications/ios/Plugin.xcodeproj/project.pbxproj +++ b/local-notifications/ios/Plugin.xcodeproj/project.pbxproj @@ -9,7 +9,7 @@ /* Begin PBXBuildFile section */ 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */; }; 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */; }; - 37F524A5255A0D730085E3FD /* LocalNotificationsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37F524A4255A0D730085E3FD /* LocalNotificationsDelegate.swift */; }; + 37F524A5255A0D730085E3FD /* LocalNotificationsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37F524A4255A0D730085E3FD /* LocalNotificationsHandler.swift */; }; 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFF88201F53D600D50D53 /* Plugin.framework */; }; 50ADFF97201F53D600D50D53 /* LocalNotificationsPluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFF96201F53D600D50D53 /* LocalNotificationsPluginTests.swift */; }; 50ADFF99201F53D600D50D53 /* LocalNotificationsPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 50ADFF8B201F53D600D50D53 /* LocalNotificationsPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -29,7 +29,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 37F524A4255A0D730085E3FD /* LocalNotificationsDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationsDelegate.swift; sourceTree = ""; }; + 37F524A4255A0D730085E3FD /* LocalNotificationsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationsHandler.swift; sourceTree = ""; }; 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50ADFF88201F53D600D50D53 /* Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50ADFF8B201F53D600D50D53 /* LocalNotificationsPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LocalNotificationsPlugin.h; sourceTree = ""; }; @@ -96,7 +96,7 @@ 50ADFF8B201F53D600D50D53 /* LocalNotificationsPlugin.h */, 50ADFFA72020EE4F00D50D53 /* LocalNotificationsPlugin.m */, 50ADFF8C201F53D600D50D53 /* Info.plist */, - 37F524A4255A0D730085E3FD /* LocalNotificationsDelegate.swift */, + 37F524A4255A0D730085E3FD /* LocalNotificationsHandler.swift */, ); path = Plugin; sourceTree = ""; @@ -306,7 +306,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 37F524A5255A0D730085E3FD /* LocalNotificationsDelegate.swift in Sources */, + 37F524A5255A0D730085E3FD /* LocalNotificationsHandler.swift in Sources */, 50E1A94820377CB70090CE1A /* LocalNotificationsPlugin.swift in Sources */, 50ADFFA82020EE4F00D50D53 /* LocalNotificationsPlugin.m in Sources */, ); diff --git a/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift b/local-notifications/ios/Plugin/LocalNotificationsHandler.swift similarity index 96% rename from local-notifications/ios/Plugin/LocalNotificationsDelegate.swift rename to local-notifications/ios/Plugin/LocalNotificationsHandler.swift index 1030b9bdd..427a5d5bf 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsDelegate.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsHandler.swift @@ -1,9 +1,9 @@ import Capacitor import UserNotifications -public class LocalNotificationsDelegate: NSObject, NotificationHandlerProtocol { +public class LocalNotificationsHandler: NSObject, NotificationHandlerProtocol { - public var plugin: CAPPlugin? + public weak var plugin: CAPPlugin? // Local list of notification id -> JSObject for storing options // between notification requets diff --git a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift index 1c5346460..09dd85a50 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift @@ -25,7 +25,7 @@ enum LocalNotificationError: LocalizedError { @objc(LocalNotificationsPlugin) public class LocalNotificationsPlugin: CAPPlugin { - private let notificationDelegationHandler = LocalNotificationsDelegate() + private let notificationDelegationHandler = LocalNotificationsHandler() override public func load() { self.bridge?.notificationRouter.localNotificationHandler = self.notificationDelegationHandler From 7ac72256fcdb8e1565808d5f1eb63f1871a0fd0c Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Tue, 12 Jan 2021 16:28:06 -0600 Subject: [PATCH 56/60] Running docgen --- local-notifications/README.md | 47 +++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/local-notifications/README.md b/local-notifications/README.md index 90cf7e813..db8149992 100644 --- a/local-notifications/README.md +++ b/local-notifications/README.md @@ -129,9 +129,9 @@ Create a notification channel. Only available for Android. -| Param | Type | -| ------------- | ------------------------------------------------------------------- | -| **`channel`** | NotificationChannel | +| Param | Type | +| ------------- | ------------------------------------------- | +| **`channel`** | Channel | **Since:** 1.0.0 @@ -148,9 +148,9 @@ Delete a notification channel. Only available for Android. -| Param | Type | -| ------------- | ------------------------------------------------------------------- | -| **`channel`** | NotificationChannel | +| Param | Type | +| ------------- | ------------------------------------------- | +| **`channel`** | Channel | **Since:** 1.0.0 @@ -452,26 +452,26 @@ An action that can be taken when a notification is displayed. | **`value`** | boolean | Whether or not the device has local notifications enabled. | 1.0.0 | -#### NotificationChannel +#### Channel -| Prop | Type | Description | Since | -| ----------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | -| **`id`** | string | The channel identifier. | 1.0.0 | -| **`name`** | string | The channel name. | 1.0.0 | -| **`description`** | string | The channel description. | 1.0.0 | -| **`sound`** | string | The sound that is played for notifications posted to this channel. | 1.0.0 | -| **`importance`** | 1 \| 2 \| 5 \| 4 \| 3 | The level of interruption of notifications posted to this channel. See the `PRIORITY_*` constants of [`NotificationCompat`](https://developer.android.com/reference/androidx/core/app/NotificationCompat) for more information. | 1.0.0 | -| **`visibility`** | 0 \| 1 \| -1 | The visibility level of notifications posted to this channel. See the `VISIBILITY_*` constants of [`NotificationCompat`](https://developer.android.com/reference/androidx/core/app/NotificationCompat) for more information. | 1.0.0 | -| **`lights`** | boolean | Whether or not notifications posted to this channel should display notification lights. | 1.0.0 | -| **`lightColor`** | string | The color of notification lights when using the `lights` option. This can be any value that [`Color.parseColor()`](https://developer.android.com/reference/android/graphics/Color#parseColor(java.lang.String)) expects. | 1.0.0 | -| **`vibration`** | boolean | Whether or not notifications posted to this channel should vibrate the device. | 1.0.0 | +| Prop | Type | Description | Since | +| ----------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | +| **`id`** | string | The channel identifier. | 1.0.0 | +| **`name`** | string | The human-friendly name of this channel (presented to the user). | 1.0.0 | +| **`description`** | string | The description of this channel (presented to the user). | 1.0.0 | +| **`sound`** | string | The sound that should be played for notifications posted to this channel. Notification channels with an importance of at least `3` should have a sound. The file name of a sound file should be specified relative to the android app `res/raw` directory. | 1.0.0 | +| **`importance`** | 1 \| 2 \| 5 \| 4 \| 3 | The level of interruption for notifications posted to this channel. | 1.0.0 | +| **`visibility`** | 0 \| 1 \| -1 | The visibility of notifications posted to this channel. This setting is for whether notifications posted to this channel appear on the lockscreen or not, and if so, whether they appear in a redacted form. | 1.0.0 | +| **`lights`** | boolean | Whether notifications posted to this channel should display notification lights, on devices that support it. | 1.0.0 | +| **`lightColor`** | string | The light color for notifications posted to this channel. Only supported if lights are enabled on this channel and the device supports it. Supported color formats are `#RRGGBB` and `#RRGGBBAA`. | 1.0.0 | +| **`vibration`** | boolean | Whether notifications posted to this channel should vibrate. | 1.0.0 | #### ListChannelsResult -| Prop | Type | Description | Since | -| -------------- | ---------------------------------- | ---------------------------------- | ----- | -| **`channels`** | NotificationChannel[] | The list of notification channels. | 1.0.0 | +| Prop | Type | Description | Since | +| -------------- | ---------------------- | ---------------------------------- | ----- | +| **`channels`** | Channel[] | The list of notification channels. | 1.0.0 | #### PermissionStatus @@ -500,6 +500,11 @@ An action that can be taken when a notification is displayed. ### Type Aliases +#### NotificationChannel + +Channel + + #### PermissionState 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied' From f7d3b5bcd634e61d4d8556f3ad7c6ab7c2f7bac1 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Tue, 12 Jan 2021 16:30:48 -0600 Subject: [PATCH 57/60] fmt --- .../ios/Plugin/LocalNotificationsPlugin.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift index 09dd85a50..67cea9096 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift @@ -282,8 +282,8 @@ public class LocalNotificationsPlugin: CAPPlugin { * that only contains the components passed in. */ func getDateComponents(_ at: JSObject) -> DateComponents { - //var dateInfo = Calendar.current.dateComponents(in: TimeZone.current, from: Date()) - //dateInfo.calendar = Calendar.current + // var dateInfo = Calendar.current.dateComponents(in: TimeZone.current, from: Date()) + // dateInfo.calendar = Calendar.current var dateInfo = DateComponents() if let year = at["year"] as? Int { @@ -536,15 +536,15 @@ public class LocalNotificationsPlugin: CAPPlugin { var jsObject: JSObject = [ "id": request.identifier, "title": request.content.title, - "repeats": request.trigger?.repeats ?? false, + "repeats": request.trigger?.repeats ?? false ] - + if let trigger = request.trigger as? UNTimeIntervalNotificationTrigger { let interval = trigger.timeInterval - let scheduleDate = Date(timeIntervalSinceNow: interval) + let scheduleDate = Date(timeIntervalSinceNow: interval) jsObject["schedule"] = ISO8601DateFormatter().string(from: scheduleDate) } - + return jsObject } From 5ecd63e9018135ff9f716f52178c7391608f57c3 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Tue, 12 Jan 2021 17:12:55 -0600 Subject: [PATCH 58/60] Disabling additional pendingNotification fields (for now) --- .../ios/Plugin/LocalNotificationsPlugin.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift index 67cea9096..008100722 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift @@ -533,17 +533,17 @@ public class LocalNotificationsPlugin: CAPPlugin { } func makePendingNotificationRequestJSObject(_ request: UNNotificationRequest) -> JSObject { - var jsObject: JSObject = [ - "id": request.identifier, - "title": request.content.title, - "repeats": request.trigger?.repeats ?? false + let jsObject: JSObject = [ + "id": request.identifier + // "title": request.content.title, + // "repeats": request.trigger?.repeats ?? false ] - if let trigger = request.trigger as? UNTimeIntervalNotificationTrigger { - let interval = trigger.timeInterval - let scheduleDate = Date(timeIntervalSinceNow: interval) - jsObject["schedule"] = ISO8601DateFormatter().string(from: scheduleDate) - } + // if let trigger = request.trigger as? UNTimeIntervalNotificationTrigger { + // let interval = trigger.timeInterval + // let scheduleDate = Date(timeIntervalSinceNow: interval) + // jsObject["schedule"] = ISO8601DateFormatter().string(from: scheduleDate) + // } return jsObject } From f6a120f009abbad5fc2344164ae09bfe444ced7e Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Tue, 12 Jan 2021 17:29:49 -0600 Subject: [PATCH 59/60] Removing additional pendingNotification fields --- .../ios/Plugin/LocalNotificationsPlugin.swift | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift index 008100722..588ae3e14 100644 --- a/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift +++ b/local-notifications/ios/Plugin/LocalNotificationsPlugin.swift @@ -533,19 +533,9 @@ public class LocalNotificationsPlugin: CAPPlugin { } func makePendingNotificationRequestJSObject(_ request: UNNotificationRequest) -> JSObject { - let jsObject: JSObject = [ + return [ "id": request.identifier - // "title": request.content.title, - // "repeats": request.trigger?.repeats ?? false ] - - // if let trigger = request.trigger as? UNTimeIntervalNotificationTrigger { - // let interval = trigger.timeInterval - // let scheduleDate = Date(timeIntervalSinceNow: interval) - // jsObject["schedule"] = ISO8601DateFormatter().string(from: scheduleDate) - // } - - return jsObject } @objc func createChannel(_ call: CAPPluginCall) { From 583a9791b2129c6fe621ea751a041f41144f5b01 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Tue, 12 Jan 2021 19:59:04 -0800 Subject: [PATCH 60/60] update to alpha.13 --- local-notifications/package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/local-notifications/package.json b/local-notifications/package.json index f46a696a9..67281aa03 100644 --- a/local-notifications/package.json +++ b/local-notifications/package.json @@ -43,11 +43,11 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@capacitor/android": "^3.0.0-alpha.11", - "@capacitor/cli": "^3.0.0-alpha.11", - "@capacitor/core": "^3.0.0-alpha.9", + "@capacitor/android": "^3.0.0-alpha.12", + "@capacitor/cli": "^3.0.0-alpha.13", + "@capacitor/core": "^3.0.0-alpha.12", "@capacitor/docgen": "0.0.14", - "@capacitor/ios": "^3.0.0-alpha.11", + "@capacitor/ios": "^3.0.0-alpha.13", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", @@ -60,7 +60,7 @@ "typescript": "~4.0.3" }, "peerDependencies": { - "@capacitor/core": "^3.0.0-alpha.9" + "@capacitor/core": "^3.0.0-alpha.12" }, "prettier": "@ionic/prettier-config", "swiftlint": "@ionic/swiftlint-config",