├── .github ├── issue_template.md └── pull_request_template.md ├── .gitignore ├── .travis.yml ├── DEMO.md ├── Hints-and-pitfalls.MD ├── LICENSE ├── README.md ├── demo ├── .editorconfig ├── .gitignore ├── App_Resources │ ├── Android │ │ ├── app.gradle │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ └── res │ │ │ ├── drawable-nodpi │ │ │ ├── splash.xml │ │ │ └── web_hi_res_512.png │ │ │ ├── mipmap-hdpi │ │ │ └── icon.png │ │ │ ├── mipmap-mdpi │ │ │ └── icon.png │ │ │ ├── mipmap-xhdpi │ │ │ └── icon.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── icon.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── icon.png │ │ │ ├── values-v21 │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── colors_splash.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ └── iOS │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── 100.png │ │ │ ├── 1024.png │ │ │ ├── 114.png │ │ │ ├── 120.png │ │ │ ├── 144.png │ │ │ ├── 152.png │ │ │ ├── 167.png │ │ │ ├── 180.png │ │ │ ├── 20.png │ │ │ ├── 29.png │ │ │ ├── 40.png │ │ │ ├── 50.png │ │ │ ├── 57.png │ │ │ ├── 58.png │ │ │ ├── 60.png │ │ │ ├── 72.png │ │ │ ├── 76.png │ │ │ ├── 80.png │ │ │ ├── 87.png │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── LaunchScreen.Center.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchScreen-Center.png │ │ │ ├── LaunchScreen-Center@2x.png │ │ │ └── LaunchScreen-Center@3x.png │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ └── build.xcconfig ├── LICENSE ├── angular.json ├── nsconfig.json ├── package-lock.json ├── package.json ├── src │ ├── app.android.css │ ├── app.common.css │ ├── app.ios.css │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── fonts │ │ │ ├── MaterialIcons-Regular.ttf │ │ │ └── README.md │ │ ├── home │ │ │ ├── home-component.css │ │ │ ├── home-routing.module.ts │ │ │ ├── home.component.html │ │ │ ├── home.component.ts │ │ │ └── home.module.ts │ │ ├── in-app-products │ │ │ ├── in-app-products-routing.module.ts │ │ │ ├── in-app-products.component.css │ │ │ ├── in-app-products.component.html │ │ │ ├── in-app-products.component.ts │ │ │ └── in-app-products.module.ts │ │ ├── products.service.ts │ │ └── products │ │ │ ├── products.component-base.ts │ │ │ └── products.model.ts │ ├── fonts │ │ ├── MaterialIcons-Regular.eot │ │ ├── MaterialIcons-Regular.ttf │ │ └── README.md │ ├── main.ts │ ├── package.json │ └── settings │ │ ├── settings.android.ts │ │ ├── settings.ios.ts │ │ └── settings.ts ├── tsconfig.json ├── tsconfig.tns.json ├── tsfmt.json └── webpack.config.js ├── publish ├── pack-customized.sh ├── pack.sh ├── package-lock.json ├── package.json └── publish.sh ├── src ├── .npmignore ├── in-app-purchase.android.ts ├── in-app-purchase.common.ts ├── in-app-purchase.ios.ts ├── index.d.ts ├── package-lock.json ├── package.json ├── platforms │ ├── android │ │ ├── AndroidManifest.xml │ │ ├── README.md │ │ ├── include.gradle │ │ └── nativescript_in_app_purchase.aar │ └── ios │ │ ├── Info.plist │ │ ├── README.md │ │ └── build.xcconfig ├── references.d.ts ├── scripts │ └── build-native.js ├── tsconfig.json └── typings │ ├── billing-android-declarations.d.ts │ └── billing-android.d.ts └── tslint.json /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | ### Make sure to check the demo app(s) for sample usage 2 | 3 | ### Make sure to check the existing issues in this repository 4 | 5 | ### If the demo apps cannot help and there is no issue for your problem, tell us about it 6 | Please, ensure your title is less than 63 characters long and starts with a capital 7 | letter. 8 | 9 | ### Which platform(s) does your issue occur on? 10 | - iOS/Android/Both 11 | - iOS/Android versions 12 | - emulator or device. What type of device? 13 | 14 | ### Please, provide the following version numbers that your issue occurs with: 15 | 16 | - CLI: (run `tns --version` to fetch it) 17 | - Cross-platform modules: (check the 'version' attribute in the 18 | `node_modules/tns-core-modules/package.json` file in your project) 19 | - Runtime(s): (look for the `"tns-android"` and `"tns-ios"` properties in the `package.json` file of your project) 20 | - Plugin(s): (look for the version numbers in the `package.json` file of your 21 | project and paste your dependencies and devDependencies here) 22 | 23 | ### Please, tell us how to recreate the issue in as much detail as possible. 24 | Describe the steps to reproduce it. 25 | 26 | ### Is there any code involved? 27 | - provide a code example to recreate the problem 28 | - (EVEN BETTER) provide a .zip with application or refer to a repository with application where the problem is reproducible. 29 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | ## PR Checklist 11 | 12 | - [ ] The PR title follows our guidelines: https://github.com/NativeScript/NativeScript/blob/master/CONTRIBUTING.md#commit-messages. 13 | - [ ] There is an issue for the bug/feature this PR is for. To avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it. 14 | - [ ] All existing tests are passing 15 | - [ ] Tests for the changes are included 16 | 17 | ## What is the current behavior? 18 | 19 | 20 | ## What is the new behavior? 21 | 22 | 23 | Fixes/Implements/Closes #[Issue Number]. 24 | 25 | 26 | 27 | 36 | 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .idea 3 | .DS_Store 4 | *.esm.json 5 | *.js 6 | *.js.map 7 | *.log 8 | !src/*.d.ts 9 | !src/index.d.ts 10 | !src/references.d.ts 11 | !src/scripts/*.js 12 | !seed-tests/*.js 13 | seed-tests/seed-copy 14 | seed-tests/seed-copy-new-git-repo/**/*.* 15 | !demo/karma.conf.js 16 | !demo/app/tests/*.js 17 | demo/*.d.ts 18 | !demo/references.d.ts 19 | demo/lib 20 | demo/platforms 21 | node_modules 22 | publish/src 23 | publish/package 24 | demo/report/report.html 25 | demo/report/stats.json 26 | !demo-vue/app/app.js 27 | demo-resources/in-app-purchases-json/json-bin.id 28 | demo-resources/in-app-purchases-json/json-bin.id.* 29 | 30 | __android-dts-generator 31 | !create-google-play-billing-typings.js -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | include: 3 | - stage: "Lint" 4 | language: node_js 5 | os: linux 6 | node_js: "10" 7 | script: cd src && npm run ci.tslint 8 | - stage: "WebPack, Build and Test" 9 | os: osx 10 | env: 11 | - WebPack="iOS" 12 | osx_image: xcode10.2 13 | language: node_js 14 | node_js: "10" 15 | jdk: oraclejdk8 16 | script: cd src && npm run build && cd ../demo && npm i && tns build ios --bundle --env.uglify 17 | - language: android 18 | os: linux 19 | env: 20 | - WebPack="Android" 21 | jdk: oraclejdk8 22 | before_install: nvm install 10 23 | script: cd src && npm run build && cd ../demo && npm i && tns build android --bundle --env.uglify --env.snapshot 24 | - language: android 25 | env: 26 | - BuildAndroid="28" 27 | os: linux 28 | jdk: oraclejdk8 29 | before_install: nvm install 10 30 | script: 31 | - cd src && npm i && npm run tsc && cd ../demo && tns build android 32 | - os: osx 33 | env: 34 | - BuildiOS="12" 35 | - Xcode="10.0" 36 | osx_image: xcode10.2 37 | language: node_js 38 | node_js: "10" 39 | jdk: oraclejdk8 40 | script: 41 | - cd src && npm i && npm run tsc && cd ../demo && tns build ios 42 | - os: linux 43 | language: android 44 | dist: precise 45 | sudo: required 46 | jdk: oraclejdk8 47 | before_script: 48 | - echo no | android create avd --force -n test -t android-21 -b armeabi-v7a 49 | - emulator -avd test -no-audio -no-window & 50 | - android-wait-for-emulator 51 | before_install: 52 | - nvm install 10 53 | script: cd src && npm run test.android 54 | - os: osx 55 | language: node_js 56 | node_js: "10" 57 | jdk: oraclejdk8 58 | osx_image: xcode10.2 59 | script: cd src && npm run test.ios 60 | 61 | android: 62 | components: 63 | - tools 64 | - platform-tools 65 | - build-tools-28.0.3 66 | - android-28 67 | - extra-android-m2repository 68 | - sys-img-armeabi-v7a-android-21 69 | 70 | before_install: 71 | - sudo pip install --upgrade pip 72 | - sudo pip install six 73 | 74 | install: 75 | - echo no | npm install -g nativescript 76 | - tns usage-reporting disable 77 | - tns error-reporting disable 78 | -------------------------------------------------------------------------------- /DEMO.md: -------------------------------------------------------------------------------- 1 | # DEMO 2 | 3 | This nativescript plugin contains an app that has to 4 | be published at least to a closed release track for 5 | testint in app purchases. 6 | 7 | 8 | ### Product IDs list 9 | 10 | Since the app stores do not provide a service to request all available products, 11 | the product IDs must be provided by the implementor it self. 12 | This app does request the product IDs remotely via http GET request. 13 | The url to this remote service is defined platform specific files in folder [`./demo/src/settings`](./demo/src/settings). 14 | 15 | - [settings.common.ts](./demo/src/settings/settings.common.ts) (Shared) 16 | - [settings.ts](./demo/src/settings/settings.ts) (Global - _is unused_) 17 | - [settings.android.ts](./demo/src/settings/settings.android.ts) (for Android) 18 | - [settings.ios.ts](./demo/src/settings/settings.ios.ts) (for iOS) 19 | 20 | The result must be a JSON of type [`ProductIDListings`](./demo/src/app/products/products.model.ts) 21 | described in [products.model.ts](./demo/src/app/products/products.model.ts) 22 | 23 | Here an example JSON 24 | ```json 25 | { 26 | "test_level_1": { 27 | "id": "test_level_1", 28 | "consumable": false, 29 | "description": "description is optional" 30 | }, 31 | "test_moneybag_1": { 32 | "id": "test_moneybag_1", 33 | "consumable": true, 34 | "description": "description is optional" 35 | } 36 | } 37 | ``` 38 | 39 | Note: 40 | This demo uses the remote JSON storage [jsonblob.com](https://jsonblob.com) to hold the product lists. 41 | See also [./demo-resources/in-app-purchases-json/README.md](./demo-resources/in-app-purchases-json/README.md) 42 | 43 | ### Build or run demo 44 | 45 | First of all `cd` into `src` directory: 46 | ```bash 47 | $ cd src/ 48 | ``` 49 | 50 | 51 | ## The demo app 52 | 53 | ### Run 54 | 55 | To run the angular demo with android 56 | 57 | ```bash 58 | $ npm run demo.android 59 | ``` 60 | 61 | To debug the angular demo with android 62 | 63 | ```bash 64 | $ npm run demo.android.debug 65 | ``` 66 | 67 | For IOS simply change `android` with `ios` like 68 | 69 | ```bash 70 | $ npm run demo.ios 71 | ``` 72 | 73 | ### Release build 74 | 75 | #### Android 76 | Create release build APK 77 | 78 | ```bash 79 | tns build android --release \ 80 | --key-store-path \ 81 | --key-store-password \ 82 | --key-store-alias \ 83 | --key-store-alias-password \ 84 | --copy-to /app-id.apk 85 | ``` 86 | 87 | In case the app is too big Google Play Store requires to upload an Android App Bundle instead of an APK 88 | 89 | Create release build Android App Bundle 90 | 91 | ```bash 92 | tns build android --release \ 93 | --key-store-path \ 94 | --key-store-password \ 95 | --key-store-alias \ 96 | --key-store-alias-password \ 97 | --aab \ 98 | --copy-to /app-id.aab 99 | ``` 100 | 101 | 102 | ###### Version counter 103 | 104 | You might execute this inside you project dir to automated counting up the version code 105 | 106 | ```bash 107 | node -e "\ 108 | let a='app/App_Resources';try{a=(JSON.parse(require('fs').readFileSync('nsconfig.json'))||{}).appResourcesPath||''}catch(e){};\ 109 | var aManifest=a+'/Android/src/main/AndroidManifest.xml';\ 110 | var fs=require('fs');\ 111 | var xml=fs.readFileSync(aManifest,'utf8');\ 112 | var _xs;var _xv=xml;_xv=_xv.substring(_xs=_xv.indexOf('android:versionCode=\"')+'android:versionCode=\"'.length,_xv.indexOf('\"',_xs)).trim();\ 113 | var newXML=xml.replace(new RegExp('(.*)(android:versionCode=\")([0-9]*)(\")(.*)','g'),'\$1\$2'+'__VERSION__'+'\$4\$5');\ 114 | var version=Number.parseInt(_xv)+1;\ 115 | newXML=newXML.replace('__VERSION__',version);\ 116 | fs.writeFileSync(aManifest,newXML);\ 117 | console.log(version);\ 118 | " 119 | ``` 120 | 121 | here a one line as script for the `package.json` 122 | 123 | ```bash 124 | "bump-version-android" : "node -e \"let a='app\/App_Resources';try{a=(JSON.parse(require('fs').readFileSync('nsconfig.json'))||{}).appResourcesPath||''}catch(e){};var aManifest=a+'\/Android\/src\/main\/AndroidManifest.xml';var fs=require('fs');var xml=fs.readFileSync(aManifest,'utf8');var _xs;var _xv=xml;_xv=_xv.substring(_xs=_xv.indexOf('android:versionCode=\\\"')+'android:versionCode=\\\"'.length,_xv.indexOf('\\\"',_xs)).trim();var newXML=xml.replace(new RegExp('(.*)(android:versionCode=\\\")([0-9]*)(\\\")(.*)','g'),'\\$1\\$2'+'__VERSION__'+'\\$4\\$5');var version=Number.parseInt(_xv)+1;newXML=newXML.replace('__VERSION__',version);fs.writeFileSync(aManifest,newXML);console.log(version);\"" 125 | ``` 126 | 127 | #### IOS 128 | 129 | You might execute this inside you project dir to automated counting up the version code 130 | 131 | ```bash 132 | node -e "\ 133 | let a='app/App_Resources';try{a=(JSON.parse(require('fs').readFileSync('nsconfig.json'))||{}).appResourcesPath||''}catch(e){};\ 134 | var aManifest=a+'/iOS/Info.plist';\ 135 | var fs=require('fs');\ 136 | var xml=fs.readFileSync(aManifest,'utf8');\ 137 | var _xs;var _xv=xml;_xv=_xv.substring(_xs=_xv.indexOf('CFBundleVersion')+'CFBundleVersion'.length,_xv.indexOf('',_xs)).replace('','').replace('','').trim();\ 138 | var newXML=xml.replace(new RegExp('(.*)(CFBundleVersion)([\n\r\t])*()(.*)()',''),'\$1\$2\$3\n\t\$4__VERSION__\$6');\ 139 | var _xva=_xv.split('.');\ 140 | _xva[_xva.length-1]=Number.parseInt(_xva[_xva.length-1])+1;\ 141 | var version=_xva.join('.');\ 142 | newXML=newXML.replace('__VERSION__',version);\ 143 | fs.writeFileSync(aManifest,newXML);\ 144 | console.log(version);" 145 | ``` 146 | 147 | 148 | here a one line as script for the `package.json` 149 | 150 | ```bash 151 | "bump-version-ios" : "node -e \"let a='app\/App_Resources';try{a=(JSON.parse(require('fs').readFileSync('nsconfig.json'))||{}).appResourcesPath||''}catch(e){};var aManifest=a+'\/iOS\/Info.plist';var fs=require('fs');var xml=fs.readFileSync(aManifest,'utf8');var _xs;var _xv=xml;_xv=_xv.substring(_xs=_xv.indexOf('CFBundleVersion<\/key>')+'CFBundleVersion<\/key>'.length,_xv.indexOf('<\/string>',_xs)).replace('','').replace('<\/string>','').trim();var newXML=xml.replace(new RegExp('(.*)(CFBundleVersion<\/key>)([\\n\\r\\t])*()(.*)(<\/string>)',''),'\\$1\\$2\\$3\\n\\t\\$4__VERSION__\\$6');var _xva=_xv.split('.');_xva[_xva.length-1]=Number.parseInt(_xva[_xva.length-1])+1;var version=_xva.join('.');newXML=newXML.replace('__VERSION__',version);fs.writeFileSync(aManifest,newXML);console.log(version);\"" 152 | ``` -------------------------------------------------------------------------------- /Hints-and-pitfalls.MD: -------------------------------------------------------------------------------- 1 | Here some hints on how the **In App Purchase** systems works on the different platforms. 2 | 3 | ## iOS 4 | 5 | ### Restoring 6 | 7 | Restoring will only provide the **non consumable** items. 8 | No subscriptions and no consumables. 9 | 10 | ### Purchased 11 | 12 | When purchasing an item the **IAP API** will provide a notification for the transaction 13 | with the state **PURCHASING**. 14 | The final state **PURCHASED** will be provided after the app has been restarted only. 15 | 16 | ### Items / Subscriptions 17 | 18 | There is no way to differentiate between *Items* and *Subscriptions* on iOS. 19 | Although there is `SKProduct#subscriptionPeriod` which could indicate that the item containing such a period would probably be a subscription unfortunately `SKProduct#subscriptionPeriod` is is not always set nor does contain a value. 20 | I couldn't manage setting up a subscription that contains a subscription period value at the [https://appstoreconnect.apple.com](https://appstoreconnect.apple.com/WebObjects/iTunesConnect.woa/ra/ng/app//addons) . 21 | 22 | 23 | 24 | ## Android 25 | 26 | ### Purchased 27 | 28 | A product is only purchased if it has been confirmed. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright (c) 2015-2019 Progress Software Corporation 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nativescript-in-app-purchase 2 | 3 | NativeScript plugin to handle in app purchases and subscriptions on Android and iOS. 4 | 5 | 6 | 7 | ## Compatibility 8 | 9 | - Version _1.x_ build for _Google Billing Api 2.1.0_ 10 | - Version _2.x_ build for _Google Billing Api 4.x_ 11 | 12 | ## (Optional) Prerequisites / Requirements 13 | 14 | Refer to the mobile ecosystem provider on how to 15 | test in app purchases. 16 | 17 | For [Apple](https://developer.apple.com/in-app-purchase/) 18 | head to [developer.apple.com](https://developer.apple.com/in-app-purchase/) 19 | 20 | For Android [Google Play Store](https://developer.android.com/google/play/billing/billing_testing) head over to [developer.android.com](https://developer.android.com/google/play/billing/billing_testing) 21 | 22 | ## Installation 23 | 24 | Installing the plugin 25 | 26 | ```javascript 27 | tns plugin add nativescript-in-app-purchase 28 | ``` 29 | 30 | ## Usage 31 | 32 | Use this typings definition for Typescript and adding _IntelliSense_ support. 33 | ``` 34 | /// 35 | ``` 36 | 37 | #### Initialization 38 | 39 | First of all it is required to create an instance of `InAppPurchaseManager`. 40 | 41 | ```typescript 42 | import { OnInit } from '@angular/core' 43 | import { InAppPurchaseManager, InAppPurchaseResultCode, InAppPurchaseStateUpdateListener, InAppPurchaseTransactionState, InAppPurchaseType } from 'nativescript-in-app-purchase' 44 | export class Test implements OnInit { 45 | private inAppPurchaseManager: InAppPurchaseManager 46 | constructor() { } 47 | ngOnInit(): void { 48 | const purchaseStateUpdateListener: InAppPurchaseStateUpdateListener = { 49 | onUpdate: (purchaseTransactionState: InAppPurchaseTransactionState): void => { 50 | if (purchaseTransactionState.resultCode === InAppPurchaseResultCode.Purchased) { 51 | // Item has been purchased, sync local items list ... 52 | } 53 | }, 54 | onUpdateHistory: (purchaseTransactionState: InAppPurchaseTransactionState): void => { 55 | if (purchaseTransactionState.resultCode === InAppPurchaseResultCode.Restored) { 56 | // Item has been restored, sync local items list ... 57 | } 58 | } 59 | } 60 | InAppPurchaseManager.bootStrapInstance(purchaseStateUpdateListener).then(inAppPurchaseManager => { 61 | this.inAppPurchaseManager = inAppPurchaseManager 62 | }) 63 | } 64 | } 65 | ``` 66 | 67 | #### Product list 68 | 69 | Get the list of in app products. 70 | To retrieve the list of in app products you must query a known amount of product IDs. 71 | 72 | ```typescript 73 | // additional imports required 74 | import { InAppPurchaseType, InAppListProductsResult, InAppProduct } from 'nativescript-in-app-purchase' 75 | 76 | // query products 77 | queryProducts() { 78 | const myProductIds = ['product_1', 'product_2'] 79 | // For subscriptions change to `InAppPurchaseType.Subscription` 80 | const myProductType = InAppPurchaseType.InAppPurchase 81 | 82 | this.inAppPurchaseManager.list(myProductIds, myProductType) 83 | .then((result: InAppListProductsResult) => { 84 | const products: InAppProduct[] = result.products 85 | for (const product of products) { 86 | // get the products ... 87 | console.log(product.title, product) 88 | } 89 | }) 90 | } 91 | ``` 92 | 93 | #### Buy a product 94 | 95 | When buying a product the result `InAppOrderResult` is only related to the order transaction it self. 96 | The purchase state of the product will be called on the `InAppPurchaseStateUpdateListener#onUpdate` method. 97 | This is where you have to **confirm the purchase** to finish the whole purchasing transaction. 98 | The App Store and Google Play Store **will automatically refund orders** that haven't been confirmed. 99 | 100 | Buying a product 101 | ```typescript 102 | // additional imports required 103 | import { InAppOrderResult } from 'nativescript-in-app-purchase' 104 | 105 | // by product 106 | buy() { 107 | const myProducts: InAppProduct[] = []//... 108 | 109 | const productToBuy: InAppProduct = myProducts[0] 110 | this.inAppPurchaseManager.order(productToBuy) 111 | .then((result: InAppOrderResult) => { 112 | if (result.success) { 113 | // order has been processed 114 | // ... expecting confirmation ... 115 | // handle confirmation in `InAppPurchaseStateUpdateListener.onUpdate(...)` 116 | } 117 | }) 118 | } 119 | ``` 120 | 121 | Confirming a product 122 | ```typescript 123 | // additional imports required 124 | import { InAppOrderConfirmResult } from 'nativescript-in-app-purchase' 125 | 126 | ngOnInit(): void { 127 | const purchaseStateUpdateListener: InAppPurchaseStateUpdateListener = { 128 | onUpdate: (purchaseTransactionState: InAppPurchaseTransactionState): void => { 129 | if (purchaseTransactionState.resultCode === InAppPurchaseResultCode.Purchased) { 130 | // Item has been purchased, sync local items list ... 131 | this.confirmOrder(purchaseTransactionState) 132 | } 133 | }, 134 | onUpdateHistory: (purchaseTransactionState: InAppPurchaseTransactionState): void => { 135 | if (purchaseTransactionState.resultCode === InAppPurchaseResultCode.Restored) { 136 | // Item has been restored, sync local items list ... 137 | } 138 | } 139 | } 140 | // ... 141 | } 142 | 143 | confirmOrder(purchaseTransactionState: InAppPurchaseTransactionState) { 144 | const isConsumable = (productId: string): boolean => { 145 | /* determine if is consumable and can be purchased more then once */ 146 | return false } 147 | 148 | // only purchased products can be confirmed 149 | if (purchaseTransactionState.resultCode === InAppPurchaseResultCode.Purchased) { 150 | const consumable: boolean = isConsumable(purchaseTransactionState.productIdentifier) 151 | this.inAppPurchaseManager.orderConfirm(purchaseTransactionState, consumable) 152 | .then((result: InAppOrderConfirmResult) => { 153 | if (result.success) { 154 | // order confirmation has been processed 155 | } 156 | }) 157 | 158 | } 159 | } 160 | 161 | ``` 162 | 163 | #### Restore purchases 164 | 165 | Restore purchases will get you all items the user already purchased. 166 | The purchase state of the restored product will be called on the `InAppPurchaseStateUpdateListener#onUpdateHistory` method. 167 | 168 | ```typescript 169 | // additional imports required 170 | import { InAppOrderHistoryResult } from 'nativescript-in-app-purchase' 171 | 172 | restoreProducts() { 173 | this.inAppPurchaseManager.purchaseHistory() 174 | .then((result: InAppOrderHistoryResult) => { 175 | if (result.success) { 176 | // purchase history requested 177 | // handle it in `InAppPurchaseStateUpdateListener.onUpdateHistory(...)` 178 | } 179 | }) 180 | } 181 | ``` 182 | 183 | ## API 184 | 185 | 186 | - `list(productIds: string[], productType?: InAppPurchaseType): Promise` 187 | List all products 188 | - `order(product: InAppProduct): Promise` 189 | Order a product 190 | - `orderConfirm(purchaseTransaction: InAppPurchaseTransactionState, consumable: boolean): Promise` 191 | Confirm the buy of a product to make it final 192 | - `purchaseHistory(): Promise` 193 | Load user's owned products 194 | - `canMakePayment(): boolean` 195 | Check wether billing is enabled or not 196 | - `static bootStrapInstance(purchaseStateUpdateListener?: InAppPurchaseStateUpdateListener): Promise` 197 | Create a new instance of the in app purchase manager 198 | - `getStoreReceipt(): string` 199 | Returns the application's Base64 encoded store receipt for the currently logged 200 | in iOS App Store user. For Android, this function always returns `undefined`. 201 | - `refreshStoreReceipt(): Promise` 202 | On iOS, request a new receipt if the receipt is invalid or missing. 203 | On Android, the promise just completes. 204 | - `shutdown()` 205 | Close connection to the underlying OS billing API 206 | 207 | ## DEMO App 208 | 209 | There is a demo angular app project included. 210 | Checkout this repo and read the [DEMO Readme](./DEMO.md) 211 | 212 | ## License 213 | 214 | Apache License Version 2.0, January 2020 215 | 216 | ## Donation 217 | 218 | Donate with Bitcoin 219 | **[3GFxvCK4nnTvHcLpVtFDQhdjANzRGBV6G6](bitcoin:3GFxvCK4nnTvHcLpVtFDQhdjANzRGBV6G6)** 220 | [![Open in Wallet](https://chart.apis.google.com/chart?chs=200x200&cht=qr&chld=L&chl=bitcoin%3A3GFxvCK4nnTvHcLpVtFDQhdjANzRGBV6G6)](bitcoin:3GFxvCK4nnTvHcLpVtFDQhdjANzRGBV6G6) 221 | **[Open in Wallet](bitcoin:3GFxvCK4nnTvHcLpVtFDQhdjANzRGBV6G6)** -------------------------------------------------------------------------------- /demo/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | charset = utf-8 8 | 9 | [*.json] 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [*.ts] 14 | indent_style = space 15 | indent_size = 4 16 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | # NativeScript 2 | hooks/ 3 | node_modules/ 4 | platforms/ 5 | 6 | # NativeScript Template 7 | *.js.map 8 | *.js 9 | !webpack.config.js 10 | 11 | # Logs 12 | logs 13 | *.log 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | 18 | # General 19 | .DS_Store 20 | .AppleDouble 21 | .LSOverride 22 | .idea 23 | .cloud 24 | .project 25 | tmp/ 26 | typings/ 27 | 28 | # Visual Studio Code 29 | .vscode/* 30 | !.vscode/settings.json 31 | !.vscode/tasks.json 32 | !.vscode/launch.json 33 | !.vscode/extensions.json 34 | 35 | 36 | 37 | 38 | !hooks/after-prepare.js 39 | !hooks/README-update-apple-bundle-id.md 40 | !hooks/update-apple-bundle-id-module.js -------------------------------------------------------------------------------- /demo/App_Resources/Android/app.gradle: -------------------------------------------------------------------------------- 1 | // Add your native dependencies here: 2 | 3 | // Uncomment to add recyclerview-v7 dependency 4 | //dependencies { 5 | // implementation 'com.android.support:recyclerview-v7:+' 6 | //} 7 | 8 | // If you want to add something to be applied before applying plugins' include.gradle files 9 | // e.g. project.ext.googlePlayServicesVersion = "15.0.1" 10 | // create a file named before-plugins.gradle in the current directory and place it there 11 | 12 | android { 13 | defaultConfig { 14 | minSdkVersion 17 15 | generatedDensities = [] 16 | } 17 | aaptOptions { 18 | additionalParameters "--no-version-vectors" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /demo/App_Resources/Android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 14 | 15 | 16 | 17 | 23 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /demo/App_Resources/Android/src/main/res/drawable-nodpi/splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /demo/App_Resources/Android/src/main/res/drawable-nodpi/web_hi_res_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/Android/src/main/res/drawable-nodpi/web_hi_res_512.png -------------------------------------------------------------------------------- /demo/App_Resources/Android/src/main/res/mipmap-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/Android/src/main/res/mipmap-hdpi/icon.png -------------------------------------------------------------------------------- /demo/App_Resources/Android/src/main/res/mipmap-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/Android/src/main/res/mipmap-mdpi/icon.png -------------------------------------------------------------------------------- /demo/App_Resources/Android/src/main/res/mipmap-xhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/Android/src/main/res/mipmap-xhdpi/icon.png -------------------------------------------------------------------------------- /demo/App_Resources/Android/src/main/res/mipmap-xxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/Android/src/main/res/mipmap-xxhdpi/icon.png -------------------------------------------------------------------------------- /demo/App_Resources/Android/src/main/res/mipmap-xxxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/Android/src/main/res/mipmap-xxxhdpi/icon.png -------------------------------------------------------------------------------- /demo/App_Resources/Android/src/main/res/values-v21/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3d5afe 4 | -------------------------------------------------------------------------------- /demo/App_Resources/Android/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 14 | 15 | 16 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /demo/App_Resources/Android/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #F5F5F5 4 | #757575 5 | #33B5E5 6 | #272734 7 | -------------------------------------------------------------------------------- /demo/App_Resources/Android/src/main/res/values/colors_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #2196F3 4 | #E91E63 5 | -------------------------------------------------------------------------------- /demo/App_Resources/Android/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | In App Purchase DEMO 4 | @string/app_name 5 | -------------------------------------------------------------------------------- /demo/App_Resources/Android/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | 23 | 24 | 25 | 32 | 33 | 35 | 36 | 37 | 42 | 43 | 45 | 46 | -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/100.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/114.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/120.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/144.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/152.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/167.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/180.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/20.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/29.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/40.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/50.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/57.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/58.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/60.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/72.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/76.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/80.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/87.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | {"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"72x72","expected-size":"72","filename":"72.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"50x50","expected-size":"100","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"50x50","expected-size":"50","filename":"50.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"72x72","expected-size":"144","filename":"144.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"}]} -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchScreen-Center.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchScreen-Center@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchScreen-Center@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@3x.png -------------------------------------------------------------------------------- /demo/App_Resources/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | In App Purchase DEMO 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | In App Purchase DEMO 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.29 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiresFullScreen 28 | 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /demo/App_Resources/iOS/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /demo/App_Resources/iOS/build.xcconfig: -------------------------------------------------------------------------------- 1 | // You can add custom settings here 2 | // for example you can uncomment the following line to force distribution code signing 3 | // CODE_SIGN_IDENTITY = iPhone Distribution 4 | // To build for device with XCode 8 you need to specify your development team. 5 | // DEVELOPMENT_TEAM = YOUR_TEAM_ID; 6 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 7 | -------------------------------------------------------------------------------- /demo/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright (c) 2015-2019 Progress Software Corporation 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /demo/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "cli": { 6 | "defaultCollection": "@nativescript/schematics" 7 | }, 8 | "projects": { 9 | "blank": { 10 | "root": "", 11 | "sourceRoot": "src", 12 | "projectType": "application", 13 | "prefix": "ns" 14 | } 15 | }, 16 | "defaultProject": "blank" 17 | } 18 | -------------------------------------------------------------------------------- /demo/nsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "appResourcesPath": "App_Resources", 3 | "appPath": "src" 4 | } 5 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "in-app-purchase-demo", 3 | "displayName": "In App Purchase DEMO", 4 | "nativescript": { 5 | "id-android": "de.superfusion.mobile.nativescript_in_app_purchase.dev.demo", 6 | "id-ios": "de.superfusion.mobile.nativescript-in-app-purchase.dev.demo", 7 | "id": "de.superfusion.mobile.nativescript_in_app_purchase.dev.demo", 8 | "tns-android": { 9 | "version": "6.2.0", 10 | "$_version_bump": 10029 11 | }, 12 | "tns-ios": { 13 | "version": "6.2.0", 14 | "$_version_bump": "1.29" 15 | } 16 | }, 17 | "description": "NativeScript Application", 18 | "license": "SEE LICENSE IN ", 19 | "repository": "", 20 | "dependencies": { 21 | "@angular/animations": "~8.2.0", 22 | "@angular/common": "~8.2.0", 23 | "@angular/compiler": "~8.2.0", 24 | "@angular/core": "~8.2.0", 25 | "@angular/forms": "~8.2.0", 26 | "@angular/platform-browser": "~8.2.0", 27 | "@angular/platform-browser-dynamic": "~8.2.0", 28 | "@angular/router": "~8.2.0", 29 | "@nativescript/theme": "~2.2.0", 30 | "nativescript-angular": "~8.20.0", 31 | "nativescript-in-app-purchase": "file:../src", 32 | "reflect-metadata": "~0.1.12", 33 | "rxjs": "^6.4.0", 34 | "tns-core-modules": "~6.2.0", 35 | "zone.js": "~0.9.1" 36 | }, 37 | "devDependencies": { 38 | "@angular/compiler-cli": "~8.2.0", 39 | "@ngtools/webpack": "~8.2.0", 40 | "nativescript-dev-webpack": "~1.3.0", 41 | "typescript": "~3.5.3" 42 | }, 43 | "gitHead": "a6fec076a20f898feabb4466a2c411158c18a100", 44 | "readme": "NativeScript Application", 45 | "scripts": { 46 | "bump-version-android": "node -e \"let a='app/App_Resources';try{a=(JSON.parse(require('fs').readFileSync('nsconfig.json'))||{}).appResourcesPath||''}catch(e){};var aManifest=a+'/Android/src/main/AndroidManifest.xml';var fs=require('fs');var xml=fs.readFileSync(aManifest,'utf8');var _xs;var _xv=xml;_xv=_xv.substring(_xs=_xv.indexOf('android:versionCode=\\\"')+'android:versionCode=\\\"'.length,_xv.indexOf('\\\"',_xs)).trim();var newXML=xml.replace(new RegExp('(.*)(android:versionCode=\\\")([0-9]*)(\\\")(.*)','g'),'\\$1\\$2'+'__VERSION__'+'\\$4\\$5');var version=Number.parseInt(_xv)+1;newXML=newXML.replace('__VERSION__',version);fs.writeFileSync(aManifest,newXML);console.log(version);\"", 47 | "bump-version-ios": "node -e \"let a='app/App_Resources';try{a=(JSON.parse(require('fs').readFileSync('nsconfig.json'))||{}).appResourcesPath||''}catch(e){};var aManifest=a+'/iOS/Info.plist';var fs=require('fs');var xml=fs.readFileSync(aManifest,'utf8');var _xs;var _xv=xml;_xv=_xv.substring(_xs=_xv.indexOf('CFBundleVersion')+'CFBundleVersion'.length,_xv.indexOf('',_xs)).replace('','').replace('','').trim();var newXML=xml.replace(new RegExp('(.*)(CFBundleVersion)([\\n\\r\\t])*()(.*)()',''),'\\$1\\$2\\$3\\n\\t\\$4__VERSION__\\$6');var _xva=_xv.split('.');_xva[_xva.length-1]=Number.parseInt(_xva[_xva.length-1])+1;var version=_xva.join('.');newXML=newXML.replace('__VERSION__',version);fs.writeFileSync(aManifest,newXML);console.log(version);\"", 48 | "bump-version": "npm run bump-version-android && npm run bump-version-ios" 49 | } 50 | } -------------------------------------------------------------------------------- /demo/src/app.android.css: -------------------------------------------------------------------------------- 1 | @import "./app.common.css"; 2 | 3 | .action-bar-icons { 4 | font-size: 10dp; 5 | } 6 | 7 | .custom-action-bar { 8 | color: white; 9 | } -------------------------------------------------------------------------------- /demo/src/app.common.css: -------------------------------------------------------------------------------- 1 | @import "~@nativescript/theme/css/core.css"; 2 | @import "~@nativescript/theme/css/blue.css"; 3 | 4 | /* Place any CSS rules you want to apply on both iOS and Android here. 5 | This is where the vast majority of your CSS code goes. */ 6 | .material-icons { 7 | font-family: "Material Icons", "MaterialIcons-Regular"; 8 | } -------------------------------------------------------------------------------- /demo/src/app.ios.css: -------------------------------------------------------------------------------- 1 | @import "./app.common.css"; -------------------------------------------------------------------------------- /demo/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { Routes } from "@angular/router"; 3 | import { NativeScriptRouterModule } from "nativescript-angular/router"; 4 | 5 | const routes: Routes = [ 6 | { path: "", redirectTo: "/home", pathMatch: "full" }, 7 | { path: "home", loadChildren: () => import("~/app/home/home.module").then((m) => m.HomeModule) }, 8 | { path: "in-app-products", loadChildren: () => import("~/app/in-app-products/in-app-products.module").then((m) => m.InAppProductsModule) } 9 | ]; 10 | 11 | @NgModule({ 12 | imports: [NativeScriptRouterModule.forRoot(routes)], 13 | exports: [NativeScriptRouterModule] 14 | }) 15 | export class AppRoutingModule { } 16 | -------------------------------------------------------------------------------- /demo/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "ns-app", 5 | templateUrl: "app.component.html" 6 | }) 7 | export class AppComponent { } 8 | -------------------------------------------------------------------------------- /demo/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core"; 2 | import { NativeScriptModule } from "nativescript-angular/nativescript.module"; 3 | 4 | import { AppRoutingModule } from "./app-routing.module"; 5 | import { AppComponent } from "./app.component"; 6 | 7 | import { HttpClientModule } from "@angular/common/http"; 8 | 9 | @NgModule({ 10 | bootstrap: [ 11 | AppComponent 12 | ], 13 | imports: [ 14 | NativeScriptModule, 15 | AppRoutingModule, 16 | HttpClientModule 17 | ], 18 | declarations: [ 19 | AppComponent 20 | ], 21 | schemas: [ 22 | NO_ERRORS_SCHEMA 23 | ] 24 | }) 25 | export class AppModule { } 26 | -------------------------------------------------------------------------------- /demo/src/app/fonts/MaterialIcons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/src/app/fonts/MaterialIcons-Regular.ttf -------------------------------------------------------------------------------- /demo/src/app/fonts/README.md: -------------------------------------------------------------------------------- 1 | # Material Design Icons 2 | 3 | Origin: https://github.com/google/material-design-icons 4 | 5 | 6 | Use this with nativescript 7 | 8 | visit the page https://material.io/resources/icons/ and find your icons name, 9 | then find here the corresponding unicode and create an xml entity of it. 10 | 11 | e.g 12 | If the icon needed is `contact_mail` 13 | and the code is `e0d0` then the 14 | xml entity would be `` 15 | 16 | ``` 17 | 3d_rotation e84d 18 | ac_unit eb3b 19 | access_alarm e190 20 | access_alarms e191 21 | access_time e192 22 | accessibility e84e 23 | accessible e914 24 | account_balance e84f 25 | account_balance_wallet e850 26 | account_box e851 27 | account_circle e853 28 | adb e60e 29 | add e145 30 | add_a_photo e439 31 | add_alarm e193 32 | add_alert e003 33 | add_box e146 34 | add_circle e147 35 | add_circle_outline e148 36 | add_location e567 37 | add_shopping_cart e854 38 | add_to_photos e39d 39 | add_to_queue e05c 40 | adjust e39e 41 | airline_seat_flat e630 42 | airline_seat_flat_angled e631 43 | airline_seat_individual_suite e632 44 | airline_seat_legroom_extra e633 45 | airline_seat_legroom_normal e634 46 | airline_seat_legroom_reduced e635 47 | airline_seat_recline_extra e636 48 | airline_seat_recline_normal e637 49 | airplanemode_active e195 50 | airplanemode_inactive e194 51 | airplay e055 52 | airport_shuttle eb3c 53 | alarm e855 54 | alarm_add e856 55 | alarm_off e857 56 | alarm_on e858 57 | album e019 58 | all_inclusive eb3d 59 | all_out e90b 60 | android e859 61 | announcement e85a 62 | apps e5c3 63 | archive e149 64 | arrow_back e5c4 65 | arrow_downward e5db 66 | arrow_drop_down e5c5 67 | arrow_drop_down_circle e5c6 68 | arrow_drop_up e5c7 69 | arrow_forward e5c8 70 | arrow_upward e5d8 71 | art_track e060 72 | aspect_ratio e85b 73 | assessment e85c 74 | assignment e85d 75 | assignment_ind e85e 76 | assignment_late e85f 77 | assignment_return e860 78 | assignment_returned e861 79 | assignment_turned_in e862 80 | assistant e39f 81 | assistant_photo e3a0 82 | attach_file e226 83 | attach_money e227 84 | attachment e2bc 85 | audiotrack e3a1 86 | autorenew e863 87 | av_timer e01b 88 | backspace e14a 89 | backup e864 90 | battery_alert e19c 91 | battery_charging_full e1a3 92 | battery_full e1a4 93 | battery_std e1a5 94 | battery_unknown e1a6 95 | beach_access eb3e 96 | beenhere e52d 97 | block e14b 98 | bluetooth e1a7 99 | bluetooth_audio e60f 100 | bluetooth_connected e1a8 101 | bluetooth_disabled e1a9 102 | bluetooth_searching e1aa 103 | blur_circular e3a2 104 | blur_linear e3a3 105 | blur_off e3a4 106 | blur_on e3a5 107 | book e865 108 | bookmark e866 109 | bookmark_border e867 110 | border_all e228 111 | border_bottom e229 112 | border_clear e22a 113 | border_color e22b 114 | border_horizontal e22c 115 | border_inner e22d 116 | border_left e22e 117 | border_outer e22f 118 | border_right e230 119 | border_style e231 120 | border_top e232 121 | border_vertical e233 122 | branding_watermark e06b 123 | brightness_1 e3a6 124 | brightness_2 e3a7 125 | brightness_3 e3a8 126 | brightness_4 e3a9 127 | brightness_5 e3aa 128 | brightness_6 e3ab 129 | brightness_7 e3ac 130 | brightness_auto e1ab 131 | brightness_high e1ac 132 | brightness_low e1ad 133 | brightness_medium e1ae 134 | broken_image e3ad 135 | brush e3ae 136 | bubble_chart e6dd 137 | bug_report e868 138 | build e869 139 | burst_mode e43c 140 | business e0af 141 | business_center eb3f 142 | cached e86a 143 | cake e7e9 144 | call e0b0 145 | call_end e0b1 146 | call_made e0b2 147 | call_merge e0b3 148 | call_missed e0b4 149 | call_missed_outgoing e0e4 150 | call_received e0b5 151 | call_split e0b6 152 | call_to_action e06c 153 | camera e3af 154 | camera_alt e3b0 155 | camera_enhance e8fc 156 | camera_front e3b1 157 | camera_rear e3b2 158 | camera_roll e3b3 159 | cancel e5c9 160 | card_giftcard e8f6 161 | card_membership e8f7 162 | card_travel e8f8 163 | casino eb40 164 | cast e307 165 | cast_connected e308 166 | center_focus_strong e3b4 167 | center_focus_weak e3b5 168 | change_history e86b 169 | chat e0b7 170 | chat_bubble e0ca 171 | chat_bubble_outline e0cb 172 | check e5ca 173 | check_box e834 174 | check_box_outline_blank e835 175 | check_circle e86c 176 | chevron_left e5cb 177 | chevron_right e5cc 178 | child_care eb41 179 | child_friendly eb42 180 | chrome_reader_mode e86d 181 | class e86e 182 | clear e14c 183 | clear_all e0b8 184 | close e5cd 185 | closed_caption e01c 186 | cloud e2bd 187 | cloud_circle e2be 188 | cloud_done e2bf 189 | cloud_download e2c0 190 | cloud_off e2c1 191 | cloud_queue e2c2 192 | cloud_upload e2c3 193 | code e86f 194 | collections e3b6 195 | collections_bookmark e431 196 | color_lens e3b7 197 | colorize e3b8 198 | comment e0b9 199 | compare e3b9 200 | compare_arrows e915 201 | computer e30a 202 | confirmation_number e638 203 | contact_mail e0d0 204 | contact_phone e0cf 205 | contacts e0ba 206 | content_copy e14d 207 | content_cut e14e 208 | content_paste e14f 209 | control_point e3ba 210 | control_point_duplicate e3bb 211 | copyright e90c 212 | create e150 213 | create_new_folder e2cc 214 | credit_card e870 215 | crop e3be 216 | crop_16_9 e3bc 217 | crop_3_2 e3bd 218 | crop_5_4 e3bf 219 | crop_7_5 e3c0 220 | crop_din e3c1 221 | crop_free e3c2 222 | crop_landscape e3c3 223 | crop_original e3c4 224 | crop_portrait e3c5 225 | crop_rotate e437 226 | crop_square e3c6 227 | dashboard e871 228 | data_usage e1af 229 | date_range e916 230 | dehaze e3c7 231 | delete e872 232 | delete_forever e92b 233 | delete_sweep e16c 234 | description e873 235 | desktop_mac e30b 236 | desktop_windows e30c 237 | details e3c8 238 | developer_board e30d 239 | developer_mode e1b0 240 | device_hub e335 241 | devices e1b1 242 | devices_other e337 243 | dialer_sip e0bb 244 | dialpad e0bc 245 | directions e52e 246 | directions_bike e52f 247 | directions_boat e532 248 | directions_bus e530 249 | directions_car e531 250 | directions_railway e534 251 | directions_run e566 252 | directions_subway e533 253 | directions_transit e535 254 | directions_walk e536 255 | disc_full e610 256 | dns e875 257 | do_not_disturb e612 258 | do_not_disturb_alt e611 259 | do_not_disturb_off e643 260 | do_not_disturb_on e644 261 | dock e30e 262 | domain e7ee 263 | done e876 264 | done_all e877 265 | donut_large e917 266 | donut_small e918 267 | drafts e151 268 | drag_handle e25d 269 | drive_eta e613 270 | dvr e1b2 271 | edit e3c9 272 | edit_location e568 273 | eject e8fb 274 | email e0be 275 | enhanced_encryption e63f 276 | equalizer e01d 277 | error e000 278 | error_outline e001 279 | euro_symbol e926 280 | ev_station e56d 281 | event e878 282 | event_available e614 283 | event_busy e615 284 | event_note e616 285 | event_seat e903 286 | exit_to_app e879 287 | expand_less e5ce 288 | expand_more e5cf 289 | explicit e01e 290 | explore e87a 291 | exposure e3ca 292 | exposure_neg_1 e3cb 293 | exposure_neg_2 e3cc 294 | exposure_plus_1 e3cd 295 | exposure_plus_2 e3ce 296 | exposure_zero e3cf 297 | extension e87b 298 | face e87c 299 | fast_forward e01f 300 | fast_rewind e020 301 | favorite e87d 302 | favorite_border e87e 303 | featured_play_list e06d 304 | featured_video e06e 305 | feedback e87f 306 | fiber_dvr e05d 307 | fiber_manual_record e061 308 | fiber_new e05e 309 | fiber_pin e06a 310 | fiber_smart_record e062 311 | file_download e2c4 312 | file_upload e2c6 313 | filter e3d3 314 | filter_1 e3d0 315 | filter_2 e3d1 316 | filter_3 e3d2 317 | filter_4 e3d4 318 | filter_5 e3d5 319 | filter_6 e3d6 320 | filter_7 e3d7 321 | filter_8 e3d8 322 | filter_9 e3d9 323 | filter_9_plus e3da 324 | filter_b_and_w e3db 325 | filter_center_focus e3dc 326 | filter_drama e3dd 327 | filter_frames e3de 328 | filter_hdr e3df 329 | filter_list e152 330 | filter_none e3e0 331 | filter_tilt_shift e3e2 332 | filter_vintage e3e3 333 | find_in_page e880 334 | find_replace e881 335 | fingerprint e90d 336 | first_page e5dc 337 | fitness_center eb43 338 | flag e153 339 | flare e3e4 340 | flash_auto e3e5 341 | flash_off e3e6 342 | flash_on e3e7 343 | flight e539 344 | flight_land e904 345 | flight_takeoff e905 346 | flip e3e8 347 | flip_to_back e882 348 | flip_to_front e883 349 | folder e2c7 350 | folder_open e2c8 351 | folder_shared e2c9 352 | folder_special e617 353 | font_download e167 354 | format_align_center e234 355 | format_align_justify e235 356 | format_align_left e236 357 | format_align_right e237 358 | format_bold e238 359 | format_clear e239 360 | format_color_fill e23a 361 | format_color_reset e23b 362 | format_color_text e23c 363 | format_indent_decrease e23d 364 | format_indent_increase e23e 365 | format_italic e23f 366 | format_line_spacing e240 367 | format_list_bulleted e241 368 | format_list_numbered e242 369 | format_paint e243 370 | format_quote e244 371 | format_shapes e25e 372 | format_size e245 373 | format_strikethrough e246 374 | format_textdirection_l_to_r e247 375 | format_textdirection_r_to_l e248 376 | format_underlined e249 377 | forum e0bf 378 | forward e154 379 | forward_10 e056 380 | forward_30 e057 381 | forward_5 e058 382 | free_breakfast eb44 383 | fullscreen e5d0 384 | fullscreen_exit e5d1 385 | functions e24a 386 | g_translate e927 387 | gamepad e30f 388 | games e021 389 | gavel e90e 390 | gesture e155 391 | get_app e884 392 | gif e908 393 | golf_course eb45 394 | gps_fixed e1b3 395 | gps_not_fixed e1b4 396 | gps_off e1b5 397 | grade e885 398 | gradient e3e9 399 | grain e3ea 400 | graphic_eq e1b8 401 | grid_off e3eb 402 | grid_on e3ec 403 | group e7ef 404 | group_add e7f0 405 | group_work e886 406 | hd e052 407 | hdr_off e3ed 408 | hdr_on e3ee 409 | hdr_strong e3f1 410 | hdr_weak e3f2 411 | headset e310 412 | headset_mic e311 413 | healing e3f3 414 | hearing e023 415 | help e887 416 | help_outline e8fd 417 | high_quality e024 418 | highlight e25f 419 | highlight_off e888 420 | history e889 421 | home e88a 422 | hot_tub eb46 423 | hotel e53a 424 | hourglass_empty e88b 425 | hourglass_full e88c 426 | http e902 427 | https e88d 428 | image e3f4 429 | image_aspect_ratio e3f5 430 | import_contacts e0e0 431 | import_export e0c3 432 | important_devices e912 433 | inbox e156 434 | indeterminate_check_box e909 435 | info e88e 436 | info_outline e88f 437 | input e890 438 | insert_chart e24b 439 | insert_comment e24c 440 | insert_drive_file e24d 441 | insert_emoticon e24e 442 | insert_invitation e24f 443 | insert_link e250 444 | insert_photo e251 445 | invert_colors e891 446 | invert_colors_off e0c4 447 | iso e3f6 448 | keyboard e312 449 | keyboard_arrow_down e313 450 | keyboard_arrow_left e314 451 | keyboard_arrow_right e315 452 | keyboard_arrow_up e316 453 | keyboard_backspace e317 454 | keyboard_capslock e318 455 | keyboard_hide e31a 456 | keyboard_return e31b 457 | keyboard_tab e31c 458 | keyboard_voice e31d 459 | kitchen eb47 460 | label e892 461 | label_outline e893 462 | landscape e3f7 463 | language e894 464 | laptop e31e 465 | laptop_chromebook e31f 466 | laptop_mac e320 467 | laptop_windows e321 468 | last_page e5dd 469 | launch e895 470 | layers e53b 471 | layers_clear e53c 472 | leak_add e3f8 473 | leak_remove e3f9 474 | lens e3fa 475 | library_add e02e 476 | library_books e02f 477 | library_music e030 478 | lightbulb_outline e90f 479 | line_style e919 480 | line_weight e91a 481 | linear_scale e260 482 | link e157 483 | linked_camera e438 484 | list e896 485 | live_help e0c6 486 | live_tv e639 487 | local_activity e53f 488 | local_airport e53d 489 | local_atm e53e 490 | local_bar e540 491 | local_cafe e541 492 | local_car_wash e542 493 | local_convenience_store e543 494 | local_dining e556 495 | local_drink e544 496 | local_florist e545 497 | local_gas_station e546 498 | local_grocery_store e547 499 | local_hospital e548 500 | local_hotel e549 501 | local_laundry_service e54a 502 | local_library e54b 503 | local_mall e54c 504 | local_movies e54d 505 | local_offer e54e 506 | local_parking e54f 507 | local_pharmacy e550 508 | local_phone e551 509 | local_pizza e552 510 | local_play e553 511 | local_post_office e554 512 | local_printshop e555 513 | local_see e557 514 | local_shipping e558 515 | local_taxi e559 516 | location_city e7f1 517 | location_disabled e1b6 518 | location_off e0c7 519 | location_on e0c8 520 | location_searching e1b7 521 | lock e897 522 | lock_open e898 523 | lock_outline e899 524 | looks e3fc 525 | looks_3 e3fb 526 | looks_4 e3fd 527 | looks_5 e3fe 528 | looks_6 e3ff 529 | looks_one e400 530 | looks_two e401 531 | loop e028 532 | loupe e402 533 | low_priority e16d 534 | loyalty e89a 535 | mail e158 536 | mail_outline e0e1 537 | map e55b 538 | markunread e159 539 | markunread_mailbox e89b 540 | memory e322 541 | menu e5d2 542 | merge_type e252 543 | message e0c9 544 | mic e029 545 | mic_none e02a 546 | mic_off e02b 547 | mms e618 548 | mode_comment e253 549 | mode_edit e254 550 | monetization_on e263 551 | money_off e25c 552 | monochrome_photos e403 553 | mood e7f2 554 | mood_bad e7f3 555 | more e619 556 | more_horiz e5d3 557 | more_vert e5d4 558 | motorcycle e91b 559 | mouse e323 560 | move_to_inbox e168 561 | movie e02c 562 | movie_creation e404 563 | movie_filter e43a 564 | multiline_chart e6df 565 | music_note e405 566 | music_video e063 567 | my_location e55c 568 | nature e406 569 | nature_people e407 570 | navigate_before e408 571 | navigate_next e409 572 | navigation e55d 573 | near_me e569 574 | network_cell e1b9 575 | network_check e640 576 | network_locked e61a 577 | network_wifi e1ba 578 | new_releases e031 579 | next_week e16a 580 | nfc e1bb 581 | no_encryption e641 582 | no_sim e0cc 583 | not_interested e033 584 | note e06f 585 | note_add e89c 586 | notifications e7f4 587 | notifications_active e7f7 588 | notifications_none e7f5 589 | notifications_off e7f6 590 | notifications_paused e7f8 591 | offline_pin e90a 592 | ondemand_video e63a 593 | opacity e91c 594 | open_in_browser e89d 595 | open_in_new e89e 596 | open_with e89f 597 | pages e7f9 598 | pageview e8a0 599 | palette e40a 600 | pan_tool e925 601 | panorama e40b 602 | panorama_fish_eye e40c 603 | panorama_horizontal e40d 604 | panorama_vertical e40e 605 | panorama_wide_angle e40f 606 | party_mode e7fa 607 | pause e034 608 | pause_circle_filled e035 609 | pause_circle_outline e036 610 | payment e8a1 611 | people e7fb 612 | people_outline e7fc 613 | perm_camera_mic e8a2 614 | perm_contact_calendar e8a3 615 | perm_data_setting e8a4 616 | perm_device_information e8a5 617 | perm_identity e8a6 618 | perm_media e8a7 619 | perm_phone_msg e8a8 620 | perm_scan_wifi e8a9 621 | person e7fd 622 | person_add e7fe 623 | person_outline e7ff 624 | person_pin e55a 625 | person_pin_circle e56a 626 | personal_video e63b 627 | pets e91d 628 | phone e0cd 629 | phone_android e324 630 | phone_bluetooth_speaker e61b 631 | phone_forwarded e61c 632 | phone_in_talk e61d 633 | phone_iphone e325 634 | phone_locked e61e 635 | phone_missed e61f 636 | phone_paused e620 637 | phonelink e326 638 | phonelink_erase e0db 639 | phonelink_lock e0dc 640 | phonelink_off e327 641 | phonelink_ring e0dd 642 | phonelink_setup e0de 643 | photo e410 644 | photo_album e411 645 | photo_camera e412 646 | photo_filter e43b 647 | photo_library e413 648 | photo_size_select_actual e432 649 | photo_size_select_large e433 650 | photo_size_select_small e434 651 | picture_as_pdf e415 652 | picture_in_picture e8aa 653 | picture_in_picture_alt e911 654 | pie_chart e6c4 655 | pie_chart_outlined e6c5 656 | pin_drop e55e 657 | place e55f 658 | play_arrow e037 659 | play_circle_filled e038 660 | play_circle_outline e039 661 | play_for_work e906 662 | playlist_add e03b 663 | playlist_add_check e065 664 | playlist_play e05f 665 | plus_one e800 666 | poll e801 667 | polymer e8ab 668 | pool eb48 669 | portable_wifi_off e0ce 670 | portrait e416 671 | power e63c 672 | power_input e336 673 | power_settings_new e8ac 674 | pregnant_woman e91e 675 | present_to_all e0df 676 | print e8ad 677 | priority_high e645 678 | public e80b 679 | publish e255 680 | query_builder e8ae 681 | question_answer e8af 682 | queue e03c 683 | queue_music e03d 684 | queue_play_next e066 685 | radio e03e 686 | radio_button_checked e837 687 | radio_button_unchecked e836 688 | rate_review e560 689 | receipt e8b0 690 | recent_actors e03f 691 | record_voice_over e91f 692 | redeem e8b1 693 | redo e15a 694 | refresh e5d5 695 | remove e15b 696 | remove_circle e15c 697 | remove_circle_outline e15d 698 | remove_from_queue e067 699 | remove_red_eye e417 700 | remove_shopping_cart e928 701 | reorder e8fe 702 | repeat e040 703 | repeat_one e041 704 | replay e042 705 | replay_10 e059 706 | replay_30 e05a 707 | replay_5 e05b 708 | reply e15e 709 | reply_all e15f 710 | report e160 711 | report_problem e8b2 712 | restaurant e56c 713 | restaurant_menu e561 714 | restore e8b3 715 | restore_page e929 716 | ring_volume e0d1 717 | room e8b4 718 | room_service eb49 719 | rotate_90_degrees_ccw e418 720 | rotate_left e419 721 | rotate_right e41a 722 | rounded_corner e920 723 | router e328 724 | rowing e921 725 | rss_feed e0e5 726 | rv_hookup e642 727 | satellite e562 728 | save e161 729 | scanner e329 730 | schedule e8b5 731 | school e80c 732 | screen_lock_landscape e1be 733 | screen_lock_portrait e1bf 734 | screen_lock_rotation e1c0 735 | screen_rotation e1c1 736 | screen_share e0e2 737 | sd_card e623 738 | sd_storage e1c2 739 | search e8b6 740 | security e32a 741 | select_all e162 742 | send e163 743 | sentiment_dissatisfied e811 744 | sentiment_neutral e812 745 | sentiment_satisfied e813 746 | sentiment_very_dissatisfied e814 747 | sentiment_very_satisfied e815 748 | settings e8b8 749 | settings_applications e8b9 750 | settings_backup_restore e8ba 751 | settings_bluetooth e8bb 752 | settings_brightness e8bd 753 | settings_cell e8bc 754 | settings_ethernet e8be 755 | settings_input_antenna e8bf 756 | settings_input_component e8c0 757 | settings_input_composite e8c1 758 | settings_input_hdmi e8c2 759 | settings_input_svideo e8c3 760 | settings_overscan e8c4 761 | settings_phone e8c5 762 | settings_power e8c6 763 | settings_remote e8c7 764 | settings_system_daydream e1c3 765 | settings_voice e8c8 766 | share e80d 767 | shop e8c9 768 | shop_two e8ca 769 | shopping_basket e8cb 770 | shopping_cart e8cc 771 | short_text e261 772 | show_chart e6e1 773 | shuffle e043 774 | signal_cellular_4_bar e1c8 775 | signal_cellular_connected_no_internet_4_bar e1cd 776 | signal_cellular_no_sim e1ce 777 | signal_cellular_null e1cf 778 | signal_cellular_off e1d0 779 | signal_wifi_4_bar e1d8 780 | signal_wifi_4_bar_lock e1d9 781 | signal_wifi_off e1da 782 | sim_card e32b 783 | sim_card_alert e624 784 | skip_next e044 785 | skip_previous e045 786 | slideshow e41b 787 | slow_motion_video e068 788 | smartphone e32c 789 | smoke_free eb4a 790 | smoking_rooms eb4b 791 | sms e625 792 | sms_failed e626 793 | snooze e046 794 | sort e164 795 | sort_by_alpha e053 796 | spa eb4c 797 | space_bar e256 798 | speaker e32d 799 | speaker_group e32e 800 | speaker_notes e8cd 801 | speaker_notes_off e92a 802 | speaker_phone e0d2 803 | spellcheck e8ce 804 | star e838 805 | star_border e83a 806 | star_half e839 807 | stars e8d0 808 | stay_current_landscape e0d3 809 | stay_current_portrait e0d4 810 | stay_primary_landscape e0d5 811 | stay_primary_portrait e0d6 812 | stop e047 813 | stop_screen_share e0e3 814 | storage e1db 815 | store e8d1 816 | store_mall_directory e563 817 | straighten e41c 818 | streetview e56e 819 | strikethrough_s e257 820 | style e41d 821 | subdirectory_arrow_left e5d9 822 | subdirectory_arrow_right e5da 823 | subject e8d2 824 | subscriptions e064 825 | subtitles e048 826 | subway e56f 827 | supervisor_account e8d3 828 | surround_sound e049 829 | swap_calls e0d7 830 | swap_horiz e8d4 831 | swap_vert e8d5 832 | swap_vertical_circle e8d6 833 | switch_camera e41e 834 | switch_video e41f 835 | sync e627 836 | sync_disabled e628 837 | sync_problem e629 838 | system_update e62a 839 | system_update_alt e8d7 840 | tab e8d8 841 | tab_unselected e8d9 842 | tablet e32f 843 | tablet_android e330 844 | tablet_mac e331 845 | tag_faces e420 846 | tap_and_play e62b 847 | terrain e564 848 | text_fields e262 849 | text_format e165 850 | textsms e0d8 851 | texture e421 852 | theaters e8da 853 | thumb_down e8db 854 | thumb_up e8dc 855 | thumbs_up_down e8dd 856 | time_to_leave e62c 857 | timelapse e422 858 | timeline e922 859 | timer e425 860 | timer_10 e423 861 | timer_3 e424 862 | timer_off e426 863 | title e264 864 | toc e8de 865 | today e8df 866 | toll e8e0 867 | tonality e427 868 | touch_app e913 869 | toys e332 870 | track_changes e8e1 871 | traffic e565 872 | train e570 873 | tram e571 874 | transfer_within_a_station e572 875 | transform e428 876 | translate e8e2 877 | trending_down e8e3 878 | trending_flat e8e4 879 | trending_up e8e5 880 | tune e429 881 | turned_in e8e6 882 | turned_in_not e8e7 883 | tv e333 884 | unarchive e169 885 | undo e166 886 | unfold_less e5d6 887 | unfold_more e5d7 888 | update e923 889 | usb e1e0 890 | verified_user e8e8 891 | vertical_align_bottom e258 892 | vertical_align_center e259 893 | vertical_align_top e25a 894 | vibration e62d 895 | video_call e070 896 | video_label e071 897 | video_library e04a 898 | videocam e04b 899 | videocam_off e04c 900 | videogame_asset e338 901 | view_agenda e8e9 902 | view_array e8ea 903 | view_carousel e8eb 904 | view_column e8ec 905 | view_comfy e42a 906 | view_compact e42b 907 | view_day e8ed 908 | view_headline e8ee 909 | view_list e8ef 910 | view_module e8f0 911 | view_quilt e8f1 912 | view_stream e8f2 913 | view_week e8f3 914 | vignette e435 915 | visibility e8f4 916 | visibility_off e8f5 917 | voice_chat e62e 918 | voicemail e0d9 919 | volume_down e04d 920 | volume_mute e04e 921 | volume_off e04f 922 | volume_up e050 923 | vpn_key e0da 924 | vpn_lock e62f 925 | wallpaper e1bc 926 | warning e002 927 | watch e334 928 | watch_later e924 929 | wb_auto e42c 930 | wb_cloudy e42d 931 | wb_incandescent e42e 932 | wb_iridescent e436 933 | wb_sunny e430 934 | wc e63d 935 | web e051 936 | web_asset e069 937 | weekend e16b 938 | whatshot e80e 939 | widgets e1bd 940 | wifi e63e 941 | wifi_lock e1e1 942 | wifi_tethering e1e2 943 | work e8f9 944 | wrap_text e25b 945 | youtube_searched_for e8fa 946 | zoom_in e8ff 947 | zoom_out e900 948 | zoom_out_map e56b 949 | ``` -------------------------------------------------------------------------------- /demo/src/app/home/home-component.css: -------------------------------------------------------------------------------- 1 | .home-panel{ 2 | padding:10; 3 | } 4 | 5 | .home-panel-bottom-button{ 6 | margin-bottom: 40; 7 | } -------------------------------------------------------------------------------- /demo/src/app/home/home-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { Routes } from "@angular/router"; 3 | import { NativeScriptRouterModule } from "nativescript-angular/router"; 4 | 5 | import { HomeComponent } from "./home.component"; 6 | 7 | const routes: Routes = [ 8 | { path: "", component: HomeComponent } 9 | ]; 10 | 11 | @NgModule({ 12 | imports: [NativeScriptRouterModule.forChild(routes)], 13 | exports: [NativeScriptRouterModule] 14 | }) 15 | export class HomeRoutingModule { } 16 | -------------------------------------------------------------------------------- /demo/src/app/home/home.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 19 | 20 | 27 | 28 | 29 | 40 | -------------------------------------------------------------------------------- /demo/src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "Home", 5 | templateUrl: "./home.component.html", 6 | styleUrls:['./home-component.css'] 7 | }) 8 | export class HomeComponent implements OnInit { 9 | 10 | constructor() { 11 | // Use the component constructor to inject providers. 12 | } 13 | 14 | ngOnInit(): void { 15 | // Init your component properties here. 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /demo/src/app/home/home.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core"; 2 | import { NativeScriptCommonModule } from "nativescript-angular/common"; 3 | 4 | import { HomeRoutingModule } from "./home-routing.module"; 5 | import { HomeComponent } from "./home.component"; 6 | 7 | @NgModule({ 8 | imports: [ 9 | NativeScriptCommonModule, 10 | HomeRoutingModule 11 | ], 12 | declarations: [ 13 | HomeComponent 14 | ], 15 | schemas: [ 16 | NO_ERRORS_SCHEMA 17 | ] 18 | }) 19 | export class HomeModule { } 20 | -------------------------------------------------------------------------------- /demo/src/app/in-app-products/in-app-products-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes } from '@angular/router'; 3 | import { NativeScriptRouterModule } from 'nativescript-angular/router'; 4 | import { InAppProductsComponent } from './in-app-products.component'; 5 | 6 | 7 | const routes: Routes = [ 8 | { path: "", component: InAppProductsComponent } 9 | ] 10 | 11 | @NgModule({ 12 | imports: [NativeScriptRouterModule.forChild(routes)], 13 | exports: [NativeScriptRouterModule] 14 | }) 15 | export class InAppProductsRoutingModule { } 16 | -------------------------------------------------------------------------------- /demo/src/app/in-app-products/in-app-products.component.css: -------------------------------------------------------------------------------- 1 | /* Add mobile styles for the component here. */ 2 | .product_list-image--empty{ 3 | color:#DDD; 4 | } 5 | 6 | .product_list-sub--empty{ 7 | color:#aaa; 8 | } 9 | 10 | .mat-icon--big { 11 | font-size:150; 12 | } -------------------------------------------------------------------------------- /demo/src/app/in-app-products/in-app-products.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 28 | 34 | 41 | 42 | 47 | 48 | 49 | 50 | 51 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 69 | 70 | 83 | 84 | 85 | 86 | 87 | 88 | 95 | 101 | 108 | 109 | 114 | 115 | 116 | 117 | 118 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 135 | 141 | 148 | 149 | 154 | 155 | 156 | 157 | 158 | 159 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 173 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 203 | 204 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 215 | 216 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 227 | 228 | 230 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | -------------------------------------------------------------------------------- /demo/src/app/in-app-products/in-app-products.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild, ElementRef } from '@angular/core'; 2 | import { InAppPurchaseType } from 'nativescript-in-app-purchase'; 3 | import { ProductsService } from '../products.service'; 4 | import { ProductsComponentBase } from '../products/products.component-base'; 5 | import { ProductItem, PurchaseItem } from '../products/products.model'; 6 | import { BottomNavigation, TabStrip } from 'tns-core-modules/ui' 7 | import * as utils from "tns-core-modules/utils/utils" 8 | 9 | @Component({ 10 | selector: 'ns-in-app-products', 11 | templateUrl: './in-app-products.component.html', 12 | styleUrls: ['./in-app-products.component.css'], 13 | providers: [ProductsService] 14 | }) 15 | export class InAppProductsComponent extends ProductsComponentBase { 16 | 17 | selectedIndex: number 18 | productsItems_InAppPurchase: ProductItem[] = null 19 | productsItems_Subscription: ProductItem[] = null 20 | purchaseItems_Restored: PurchaseItem[] = null 21 | 22 | productsItems_InAppPurchaseMap: { [key: string]: ProductItem } = {} 23 | productsItems_SubscriptionMap: { [key: string]: ProductItem } = {} 24 | 25 | @ViewChild('buttomNavigation', { static: false }) 26 | buttomNavigation: ElementRef 27 | @ViewChild('tabStrip', { static: false }) 28 | tabStrip: ElementRef 29 | 30 | get purchasedProductsMap_keys() { return Object.keys(this.purchasedProductsMap || {}) } 31 | get purchasedProductsMap_values() { return Object['values'](this.purchasedProductsMap || {}) } 32 | 33 | openProductIDsUrlInBrowser() { 34 | utils.openUrl(this.productsService.productIDsUrl) 35 | } 36 | 37 | protected loadingRestoredProduct() { 38 | this.purchaseItems_Restored = [] 39 | } 40 | 41 | protected loadingRestoredProductDone(purchaseItem: PurchaseItem) { 42 | console.log('loadingRestoredProductDone', purchaseItem) 43 | if (purchaseItem) { 44 | this.purchaseItems_Restored = this.purchaseItems_Restored || [] 45 | this.purchaseItems_Restored.push(purchaseItem) 46 | } 47 | } 48 | 49 | protected loadingProductsDone(type: InAppPurchaseType, productItems: ProductItem[]) { 50 | console.log('loadingProductsDone', type, 'items:', productItems) 51 | if (InAppPurchaseType.Subscription === type) { 52 | this.productsItems_Subscription = [].concat(productItems || []) 53 | this.productsItems_Subscription.forEach(item => { this.productsItems_SubscriptionMap[item.id] = item }) 54 | } else if (InAppPurchaseType.InAppPurchase === type) { 55 | this.productsItems_InAppPurchase = [].concat(productItems || []) 56 | this.productsItems_InAppPurchase.forEach(item => { this.productsItems_InAppPurchaseMap[item.id] = item }) 57 | } 58 | } 59 | 60 | protected loadingProductsError(type: InAppPurchaseType, error: any) { 61 | console.error('loadingProductsError', type, 'error:', error) 62 | if (InAppPurchaseType.Subscription === type) { 63 | this.productsItems_Subscription = [] 64 | } else if (InAppPurchaseType.InAppPurchase === type) { 65 | this.productsItems_InAppPurchase = [] 66 | } 67 | alert('Error loading items:\n' + JSON.stringify(error, null, 2)) 68 | } 69 | 70 | selectedIndexChanged(selectedIndex) { 71 | setTimeout( 72 | /** User timeout to prevent: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. */ 73 | () => { 74 | console.log('selectedIndexChanged', selectedIndex) 75 | if (0 === selectedIndex) { 76 | if (null == this.productsItems_InAppPurchase) { 77 | this.toggleLoadingProducts(InAppPurchaseType.InAppPurchase, true) 78 | } 79 | } else if (1 === selectedIndex) { 80 | if (null == this.productsItems_Subscription) { 81 | this.toggleLoadingProducts(InAppPurchaseType.Subscription, true) 82 | } 83 | } else if (2 === selectedIndex) { 84 | if (null == this.purchaseItems_Restored) { 85 | this.restoreProducts() 86 | } 87 | } 88 | }) 89 | } 90 | 91 | doBuyProduct(productItem: ProductItem) { 92 | if (!productItem.options.consumable && productItem.purchased) { 93 | alert('Product already purchased.\nProduct is not consumable and cant\'t be bought a second time.') 94 | return 95 | } 96 | this.confirmBuy(productItem) 97 | } 98 | 99 | tappedOnRestoreEmptyScreen() { 100 | if (!this.restoring) 101 | if (this.purchaseItems_Restored) { 102 | /** 103 | * Already tried to load restored pruducts but none were returned. 104 | * So invite the user to buy something. 105 | */ 106 | this.openShopTab() 107 | } else { 108 | /** 109 | * Load purchased products 110 | */ 111 | this.restoreProducts() 112 | } 113 | } 114 | 115 | openShopTab() { 116 | /** 117 | * TODO: 118 | * On iOS this does only change the tab but not the 119 | * selected tab-strip-item. 120 | * This seems like a bug in nativescript's ui widget 121 | */ 122 | this.buttomNavigation.nativeElement.selectedIndex = 0 123 | } 124 | 125 | showInfo() { 126 | alert(`In app purchase testing. 127 | 128 | To reset values simply navigate back. 129 | `) 130 | } 131 | 132 | showInfoPurchased(item){ 133 | alert(` 134 | ID : ${item.productId} 135 | Price : ${item.product.price} 136 | Date : ${new Date(item.purchaseTime)} 137 | Order : ${item.purchaseId} 138 | `) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /demo/src/app/in-app-products/in-app-products.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; 2 | 3 | import { InAppProductsRoutingModule } from './in-app-products-routing.module'; 4 | import { NativeScriptCommonModule } from 'nativescript-angular/common'; 5 | import { InAppProductsComponent } from './in-app-products.component'; 6 | 7 | 8 | @NgModule({ 9 | declarations: [InAppProductsComponent], 10 | imports: [ 11 | InAppProductsRoutingModule, 12 | NativeScriptCommonModule 13 | ], 14 | schemas: [NO_ERRORS_SCHEMA] 15 | }) 16 | export class InAppProductsModule { } 17 | -------------------------------------------------------------------------------- /demo/src/app/products.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { Injectable, OnDestroy } from '@angular/core'; 3 | import { InAppOrderResult, InAppProduct, InAppPurchaseManager, InAppPurchaseStateUpdateListener, InAppPurchaseType, InAppOrderHistoryResult, InAppListProductsResult } from 'nativescript-in-app-purchase'; 4 | import { from, Observable, Subscription } from 'rxjs'; 5 | import { flatMap, map } from 'rxjs/operators'; 6 | import { ProductID, ProductIDListings, ProductItem } from './products/products.model'; 7 | import { URL_PRODUCTS } from '~/settings/settings'; 8 | 9 | @Injectable() 10 | export class ProductsService implements OnDestroy { 11 | productIDsUrl = URL_PRODUCTS 12 | 13 | _productDefinitions: ProductIDListings 14 | get productDefinitions(): ProductIDListings { return this._productDefinitions || {} } 15 | set productDefinitions(value: ProductIDListings) { this._productDefinitions = value } 16 | 17 | sub_loadProductIDs: Subscription 18 | 19 | constructor(private httpClient: HttpClient) { } 20 | 21 | loadingProductItems(selectedProductType: InAppPurchaseType, inAppPurchaseStateUpdateListener: InAppPurchaseStateUpdateListener): Observable { 22 | console.log('loadingProductItems(...)', selectedProductType) 23 | return this.loadProductIDs() 24 | .pipe(map(productIds => productIds)) 25 | .pipe(flatMap(productIds => this.loadingProductsByProductIds(productIds, selectedProductType, inAppPurchaseStateUpdateListener), 26 | (productIds, result) => { 27 | if (!result.success) { 28 | throw result 29 | } 30 | const products = result.products 31 | const productItems: ProductItem[] = [] 32 | for (const inAppProduct of products) { 33 | productItems.push({ 34 | id: inAppProduct.productId, 35 | product: inAppProduct, 36 | options: productIds[inAppProduct.productId] 37 | }) 38 | } 39 | return productItems 40 | } 41 | )) 42 | } 43 | 44 | orderProduct(productItem: ProductItem, inAppPurchaseStateUpdateListener: InAppPurchaseStateUpdateListener): Observable { 45 | return from(InAppPurchaseManager.bootStrapInstance(inAppPurchaseStateUpdateListener)) 46 | .pipe(flatMap(purchaseManager => from(purchaseManager.order(productItem.product)))) 47 | } 48 | 49 | loadingProductsByProductIds(productIds: ProductIDListings, productType: InAppPurchaseType, inAppPurchaseStateUpdateListener: InAppPurchaseStateUpdateListener): Observable { 50 | console.log('loadingProductsByProductIds(...)', productIds) 51 | return new Observable(observer => { 52 | InAppPurchaseManager.bootStrapInstance(inAppPurchaseStateUpdateListener) 53 | .then(purchaseManager => { 54 | purchaseManager 55 | .list(Object.keys(productIds), productType) 56 | .then(result => { 57 | console.log('loadingProductsByProductIds(...) - result', result, 'for ids', productIds) 58 | observer.next(result) 59 | }).catch(reason => { 60 | observer.error(reason) 61 | }) 62 | }).catch(reason => { 63 | observer.error(reason) 64 | }) 65 | }) 66 | } 67 | 68 | loadProductIDs(): Observable { 69 | return new Observable(observer => { 70 | this.sub_loadProductIDs = this.httpClient.get(this.productIDsUrl) 71 | .subscribe((result) => { 72 | const productDefinitions = Array.isArray(result) ? convert_prod_ids_to_map(result as ProductID[]) : result as ProductIDListings 73 | console.log('loadProducts(...) - httpClient.get result:', result) 74 | observer.next(this.getDefaultProdIsIfNecessary(productDefinitions)) 75 | }, (error) => { 76 | console.error('loadProducts(...) - httpClient.get', error) 77 | observer.next(this.getDefaultProdIsIfNecessary()) 78 | }) 79 | }) 80 | } 81 | 82 | restoreProducts(inAppPurchaseStateUpdateListener: InAppPurchaseStateUpdateListener): Observable { 83 | return from(InAppPurchaseManager.bootStrapInstance(inAppPurchaseStateUpdateListener)) 84 | .pipe(flatMap(purchaseManager => from(purchaseManager.purchaseHistory()))) 85 | } 86 | 87 | private getDefaultProdIsIfNecessary(productDefinitions?: ProductIDListings): ProductIDListings { 88 | if (!productDefinitions) { 89 | return this._productDefinitions = defaultProductDefinitions 90 | } 91 | return this._productDefinitions = productDefinitions 92 | } 93 | 94 | ngOnDestroy(): void { 95 | if (this.sub_loadProductIDs) { 96 | this.sub_loadProductIDs.unsubscribe() 97 | this.sub_loadProductIDs = null 98 | } 99 | } 100 | 101 | } 102 | 103 | /* --- test --- */ 104 | const testProducts_google_play: ProductID[] = [{ 105 | "id": "android.test.purchased", 106 | "consumable": false 107 | }, { 108 | "id": "android.test.canceled", 109 | "consumable": false 110 | }, { 111 | "id": "android.test.item_unavailable", 112 | "consumable": false 113 | }] 114 | const convert_prod_ids_to_map = (prod_ids: ProductID[]) => { 115 | const prod_ids_map: ProductIDListings = {} 116 | prod_ids.forEach(item => { prod_ids_map[item.id] = item }) 117 | return prod_ids_map 118 | } 119 | const defaultProductDefinitions: ProductIDListings = convert_prod_ids_to_map(testProducts_google_play) -------------------------------------------------------------------------------- /demo/src/app/products/products.component-base.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy, OnInit, AfterViewInit, NgZone } from '@angular/core'; 2 | import { InAppOrderResult, InAppPurchaseManager, InAppPurchaseResultCode, InAppPurchaseStateUpdateListener, InAppPurchaseTransactionState, InAppPurchaseType, InAppProduct } from 'nativescript-in-app-purchase'; 3 | import { from, Subscription } from 'rxjs'; 4 | import { confirm, ConfirmOptions } from "tns-core-modules/ui/dialogs"; 5 | import { ProductItem, ProductItemMap, PurchaseItem, PurchasedProductsMap } from './products.model'; 6 | import { ProductsService } from '../products.service'; 7 | import { map, flatMap, concatMap } from 'rxjs/operators'; 8 | 9 | export abstract class ProductsComponentBase implements OnInit, AfterViewInit, OnDestroy { 10 | 11 | productItemsMap: ProductItemMap 12 | purchasedProductsMap: PurchasedProductsMap 13 | 14 | loading: boolean 15 | loadingMessage: string 16 | restoring: boolean 17 | 18 | sub_loading: Subscription 19 | sub_restoring: Subscription 20 | 21 | inAppPurchaseStateUpdateListener: InAppPurchaseStateUpdateListener 22 | 23 | constructor(private ngZone: NgZone, protected productsService: ProductsService) { } 24 | 25 | ngOnInit() { 26 | 27 | } 28 | 29 | ngAfterViewInit(): void { 30 | this._init() 31 | } 32 | 33 | private _init() { 34 | this.inAppPurchaseStateUpdateListener = { 35 | onUpdate: (purchaseTransactionState: InAppPurchaseTransactionState) => { 36 | console.log(purchaseTransactionState) 37 | alert('Purchase update:\n' + JSON.stringify(purchaseTransactionState, null, 2)) 38 | from(InAppPurchaseManager.bootStrapInstance(this.inAppPurchaseStateUpdateListener)) 39 | .subscribe((purchaseManager: InAppPurchaseManager) => { 40 | this.confirmPurchaseTransaction(purchaseTransactionState, purchaseManager) 41 | if (purchaseTransactionState.resultCode === InAppPurchaseResultCode.Purchased) { 42 | 43 | } 44 | }) 45 | }, 46 | onUpdateHistory: (purchaseTransactionState: InAppPurchaseTransactionState) => { 47 | console.log('Restored item:', purchaseTransactionState.productIdentifier, purchaseTransactionState) 48 | if (InAppPurchaseResultCode.Restored == purchaseTransactionState.resultCode 49 | || InAppPurchaseResultCode.Purchased == purchaseTransactionState.resultCode 50 | ) { 51 | // update product 'owned' state 52 | this.productItemsMap = this.productItemsMap || {} as ProductItemMap 53 | const product = this.productItemsMap[purchaseTransactionState.productIdentifier] 54 | // console.log('Find restored product from identifier', `this.productItemsMap[${purchaseTransactionState.productIdentifier}], found:`, product, 'in', this.productItemsMap) 55 | if (product) { 56 | product.purchased = (product.purchased || 0) + 1 57 | } 58 | const purchaseItem: PurchaseItem = { 59 | productId: purchaseTransactionState.productIdentifier, 60 | purchaseId: purchaseTransactionState.purchaseId, 61 | purchaseTime: purchaseTransactionState.purchaseTime, 62 | product: product ? product.product : null, 63 | options: product ? product.options : null 64 | } 65 | 66 | /** only if not already present */ 67 | if (this.updatePurchasedMap(purchaseItem)) { 68 | /** notify listener */ 69 | this.loadingRestoredProductDone(purchaseItem) 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | updatePurchasedMap(purchaseItem: PurchaseItem): PurchaseItem { 77 | this.purchasedProductsMap = this.purchasedProductsMap || {} 78 | this.purchasedProductsMap[purchaseItem.productId] = this.purchasedProductsMap[purchaseItem.productId] || [] 79 | const found = this.purchasedProductsMap[purchaseItem.productId].filter(item => item.productId === purchaseItem.productId) 80 | if (!(found || []).length) { 81 | this.purchasedProductsMap[purchaseItem.productId].push(purchaseItem) 82 | return purchaseItem 83 | } 84 | return null 85 | } 86 | 87 | private __updateStateLoading(active: boolean) { 88 | this.loading = active 89 | } 90 | 91 | private __updateStateRestore(active: boolean) { 92 | // ngZone is required for iOS only 93 | this.ngZone.run(() => { 94 | this.restoring = active 95 | }) 96 | } 97 | 98 | /** 99 | * 100 | * @param event 101 | * @param type Either 'InAppPurchase' (default) or 'Subscription'. 102 | * @param force Force reloading even if a request is ongoing.
103 | * Without `force` a running request would be stopped.
104 | */ 105 | toggleLoadingProducts(type?: InAppPurchaseType, force?: boolean) { 106 | let _inAppPurchaseType = type === InAppPurchaseType.Subscription ? InAppPurchaseType.Subscription : InAppPurchaseType.InAppPurchase 107 | if (force && this.loading) { 108 | console.log('toggleLoadingProducts(event)', 'force stop loading...') 109 | this.unsubscribeLoadingProducts() 110 | } 111 | if (this.loading) { 112 | console.log('toggleLoadingProducts(event)', 'stop loading...') 113 | this.unsubscribeLoadingProducts() 114 | } else { 115 | console.log('toggleLoadingProducts(event)', 'loading products...') 116 | this.__updateStateLoading(true) 117 | this.loadingMessage = null 118 | 119 | this.sub_loading = this.productsService.loadingProductItems(_inAppPurchaseType, this.inAppPurchaseStateUpdateListener) 120 | .subscribe((products) => { 121 | this.addProducts(products) 122 | this.__updateStateLoading(false) 123 | this.loadingProductsDone(_inAppPurchaseType, products) 124 | }, 125 | (error) => { 126 | console.error('toggleLoadingProducts - loadingProductsByProductIds', error) 127 | this.__updateStateLoading(false) 128 | this.loadingMessage = error 129 | /** notify listener */ 130 | this.loadingProductsError(_inAppPurchaseType, error) 131 | }, 132 | () => { this.__updateStateLoading(false) }) 133 | } 134 | } 135 | 136 | private addProducts(productItems: ProductItem[]) { 137 | this.productItemsMap = this.productItemsMap || {} as ProductItemMap 138 | (productItems || []).map(item => this.productItemsMap[item.id] = item) 139 | } 140 | 141 | protected abstract loadingProductsDone(type: InAppPurchaseType, productItems: ProductItem[]) 142 | protected abstract loadingProductsError(type: InAppPurchaseType, error: any) 143 | protected abstract loadingRestoredProduct() 144 | protected abstract loadingRestoredProductDone(purchaseItem: PurchaseItem) 145 | 146 | confirmBuy(productItem: ProductItem) { 147 | const options: ConfirmOptions = { 148 | title: 'Confirm buy', 149 | message: 'Are you sure you want to buy this product?', 150 | okButtonText: 'Yes', 151 | cancelButtonText: 'No', 152 | neutralButtonText: 'Cancel', 153 | cancelable: true 154 | } as ConfirmOptions 155 | 156 | confirm(options).then((confirmed) => { 157 | if (confirmed) { 158 | this.productsService.orderProduct(productItem, this.inAppPurchaseStateUpdateListener) 159 | .subscribe((result: InAppOrderResult) => { 160 | console.log('confirmBuy - order:', result) 161 | if (!result.success) { 162 | alert('Failed to buy\n' + JSON.stringify(result, null, 2)) 163 | } 164 | }, (error) => { 165 | console.error('confirmBuy - order:', error) 166 | alert('Error buying:\n' + error) 167 | }, () => { }) 168 | } 169 | }) 170 | } 171 | 172 | private confirmPurchaseTransaction(purchaseTransactionState: InAppPurchaseTransactionState, purchaseManager: InAppPurchaseManager) { 173 | if (purchaseTransactionState.resultCode === InAppPurchaseResultCode.Purchased) { 174 | const _confirmOrder = (purchaseManager: InAppPurchaseManager, purchaseTransactionState: InAppPurchaseTransactionState, productItem: ProductItem) => { 175 | const consumable: boolean = productItem.options.consumable 176 | purchaseManager.orderConfirm(purchaseTransactionState, consumable) 177 | .then((result) => { 178 | if (!result.success) { 179 | console.log('Couldn\'t confirm product with id:', productItem.id, result) 180 | alert('Couldn\'t confirm product with id:' + productItem.id + '\n' + JSON.stringify(result, null, 2)) 181 | } 182 | }) 183 | .catch((error) => { 184 | console.error('Error confirming product with id:', productItem.id, error) 185 | alert('Error confirming product with id:\n' + productItem.id + '\n' + error) 186 | }) 187 | } 188 | 189 | if (this.productItemsMap) 190 | if (this.productItemsMap[purchaseTransactionState.productIdentifier]) { 191 | _confirmOrder(purchaseManager, purchaseTransactionState, this.productItemsMap[purchaseTransactionState.productIdentifier]) 192 | } else { 193 | console.log('Can\'t find product with id:', purchaseTransactionState.productIdentifier) 194 | console.log('Reload product with id:', purchaseTransactionState.productIdentifier) 195 | purchaseManager.list([purchaseTransactionState.productIdentifier]) 196 | .then((result) => { 197 | const inAppProducts = result.products 198 | if ((inAppProducts || []).length) { 199 | // add into product item list 200 | const productItem: ProductItem = { 201 | id: inAppProducts[0].productId, 202 | product: inAppProducts[0], 203 | options: this.productsService.productDefinitions[inAppProducts[0].productId] 204 | } 205 | this.addProducts([productItem]) 206 | _confirmOrder(purchaseManager, purchaseTransactionState, productItem) 207 | } else { 208 | console.log('Can\'t find and load product with id:', purchaseTransactionState.productIdentifier) 209 | alert('Can\'t find and load product with id:\n' + purchaseTransactionState.productIdentifier) 210 | } 211 | }) 212 | .catch((error) => { 213 | console.error('Error confirming product with id:', purchaseTransactionState.productIdentifier, error) 214 | alert('Error confirming product with id:\n' + purchaseTransactionState.productIdentifier + '\n' + error) 215 | }); 216 | } 217 | } 218 | } 219 | 220 | restoreProducts() { 221 | /** notify listener */ 222 | this.loadingRestoredProduct() 223 | if (this.restoring) { 224 | this.unsubscribeRestoringProducts() 225 | } else { 226 | this.__updateStateRestore(true) 227 | 228 | this.unsubscribeLoadingProducts() 229 | 230 | this.sub_loading = 231 | // 1. Load product IDs 232 | this.productsService.loadingProductItems(InAppPurchaseType.InAppPurchase, this.inAppPurchaseStateUpdateListener) 233 | // 2. Load products of type 'In App Purchase' and 'Subscription' 234 | .pipe(concatMap((productItems_inApp) => this.productsService.loadingProductItems(InAppPurchaseType.Subscription, this.inAppPurchaseStateUpdateListener), 235 | (productItems_inApp, productItems_sub) => { 236 | const all: ProductItem[] = [] 237 | .concat(productItems_inApp) 238 | .concat(productItems_sub) 239 | 240 | this.addProducts(productItems_inApp) 241 | /** notify listener */ 242 | this.loadingProductsDone(InAppPurchaseType.InAppPurchase, productItems_inApp) 243 | 244 | this.addProducts(productItems_sub) 245 | /** notify listener */ 246 | this.loadingProductsDone(InAppPurchaseType.Subscription, productItems_sub) 247 | return all 248 | })) 249 | // 3. Notify listener for every product 250 | .subscribe(all => { 251 | this.sub_restoring = this.productsService.restoreProducts(this.inAppPurchaseStateUpdateListener) 252 | .subscribe((result) => { 253 | this.__updateStateRestore(false) 254 | }, (error) => { 255 | this.__updateStateRestore(false) 256 | console.error('restoreProducts - ERROR ', error) 257 | alert('Failed restoring products/n' + error) 258 | }, () => { 259 | this.__updateStateRestore(false) 260 | }) 261 | }, (error) => { 262 | this.__updateStateRestore(false) 263 | console.error('loadingProductItems - ERROR ', error) 264 | alert('Failed loading products/n' + error) 265 | }, () => { 266 | this.__updateStateRestore(false) 267 | }) 268 | } 269 | } 270 | 271 | getProductDefinitions() { 272 | return this.productsService.productDefinitions 273 | } 274 | 275 | unsubscribeRestoringProducts() { 276 | if (this.sub_restoring) { 277 | this.sub_restoring.unsubscribe() 278 | this.sub_restoring = null 279 | } 280 | this.restoring = false 281 | this.__updateStateRestore(false) 282 | } 283 | 284 | unsubscribeLoadingProducts() { 285 | if (this.sub_loading) { 286 | this.sub_loading.unsubscribe() 287 | this.sub_loading = null 288 | } 289 | this.loading = false 290 | this.loadingMessage = null 291 | this.__updateStateLoading(false) 292 | } 293 | 294 | ngOnDestroy(): void { 295 | this.unsubscribeLoadingProducts() 296 | this.unsubscribeRestoringProducts() 297 | this.productsService.ngOnDestroy() 298 | } 299 | 300 | } -------------------------------------------------------------------------------- /demo/src/app/products/products.model.ts: -------------------------------------------------------------------------------- 1 | import { InAppProduct } from 'nativescript-in-app-purchase' 2 | export interface ProductItemMap { 3 | [key: string]: ProductItem 4 | } 5 | export interface ProductItem { 6 | id: string 7 | product: InAppProduct 8 | options: ProductID 9 | /** 10 | * The amount how often this product has been purchased.
11 | * Will be and remain one if the product is not cosumable and has been bought once.
12 | * Will incread for consumable products that get bought. 13 | */ 14 | purchased?: number 15 | } 16 | /** 17 | * The product listings 18 | */ 19 | export interface ProductIDListings { 20 | /** the product-id */ 21 | [key: string]: ProductID 22 | } 23 | /** 24 | * Representation of on product 25 | */ 26 | export interface ProductID { 27 | /** the product-id */ 28 | id: string 29 | /** set wheter the item is comsumable or one time buy */ 30 | consumable: boolean 31 | /** any info about the product item */ 32 | description?: string 33 | } 34 | 35 | export interface PurchaseItem { 36 | productId: string 37 | purchaseId: string 38 | purchaseTime: number 39 | product?: InAppProduct 40 | options?: ProductID 41 | } 42 | 43 | export interface PurchasedProductsMap { 44 | [key: string]: PurchaseItem[] 45 | } -------------------------------------------------------------------------------- /demo/src/fonts/MaterialIcons-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/src/fonts/MaterialIcons-Regular.eot -------------------------------------------------------------------------------- /demo/src/fonts/MaterialIcons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/demo/src/fonts/MaterialIcons-Regular.ttf -------------------------------------------------------------------------------- /demo/src/fonts/README.md: -------------------------------------------------------------------------------- 1 | # Material Design Icons 2 | 3 | Origin: https://github.com/google/material-design-icons 4 | 5 | 6 | Use this with nativescript 7 | 8 | visit the page https://material.io/resources/icons/ and find your icons name, 9 | then find here the corresponding unicode and create an xml entity of it. 10 | 11 | e.g 12 | If the icon needed is `contact_mail` 13 | and the code is `e0d0` then the 14 | xml entity would be `` 15 | 16 | ``` 17 | 3d_rotation e84d 18 | ac_unit eb3b 19 | access_alarm e190 20 | access_alarms e191 21 | access_time e192 22 | accessibility e84e 23 | accessible e914 24 | account_balance e84f 25 | account_balance_wallet e850 26 | account_box e851 27 | account_circle e853 28 | adb e60e 29 | add e145 30 | add_a_photo e439 31 | add_alarm e193 32 | add_alert e003 33 | add_box e146 34 | add_circle e147 35 | add_circle_outline e148 36 | add_location e567 37 | add_shopping_cart e854 38 | add_to_photos e39d 39 | add_to_queue e05c 40 | adjust e39e 41 | airline_seat_flat e630 42 | airline_seat_flat_angled e631 43 | airline_seat_individual_suite e632 44 | airline_seat_legroom_extra e633 45 | airline_seat_legroom_normal e634 46 | airline_seat_legroom_reduced e635 47 | airline_seat_recline_extra e636 48 | airline_seat_recline_normal e637 49 | airplanemode_active e195 50 | airplanemode_inactive e194 51 | airplay e055 52 | airport_shuttle eb3c 53 | alarm e855 54 | alarm_add e856 55 | alarm_off e857 56 | alarm_on e858 57 | album e019 58 | all_inclusive eb3d 59 | all_out e90b 60 | android e859 61 | announcement e85a 62 | apps e5c3 63 | archive e149 64 | arrow_back e5c4 65 | arrow_downward e5db 66 | arrow_drop_down e5c5 67 | arrow_drop_down_circle e5c6 68 | arrow_drop_up e5c7 69 | arrow_forward e5c8 70 | arrow_upward e5d8 71 | art_track e060 72 | aspect_ratio e85b 73 | assessment e85c 74 | assignment e85d 75 | assignment_ind e85e 76 | assignment_late e85f 77 | assignment_return e860 78 | assignment_returned e861 79 | assignment_turned_in e862 80 | assistant e39f 81 | assistant_photo e3a0 82 | attach_file e226 83 | attach_money e227 84 | attachment e2bc 85 | audiotrack e3a1 86 | autorenew e863 87 | av_timer e01b 88 | backspace e14a 89 | backup e864 90 | battery_alert e19c 91 | battery_charging_full e1a3 92 | battery_full e1a4 93 | battery_std e1a5 94 | battery_unknown e1a6 95 | beach_access eb3e 96 | beenhere e52d 97 | block e14b 98 | bluetooth e1a7 99 | bluetooth_audio e60f 100 | bluetooth_connected e1a8 101 | bluetooth_disabled e1a9 102 | bluetooth_searching e1aa 103 | blur_circular e3a2 104 | blur_linear e3a3 105 | blur_off e3a4 106 | blur_on e3a5 107 | book e865 108 | bookmark e866 109 | bookmark_border e867 110 | border_all e228 111 | border_bottom e229 112 | border_clear e22a 113 | border_color e22b 114 | border_horizontal e22c 115 | border_inner e22d 116 | border_left e22e 117 | border_outer e22f 118 | border_right e230 119 | border_style e231 120 | border_top e232 121 | border_vertical e233 122 | branding_watermark e06b 123 | brightness_1 e3a6 124 | brightness_2 e3a7 125 | brightness_3 e3a8 126 | brightness_4 e3a9 127 | brightness_5 e3aa 128 | brightness_6 e3ab 129 | brightness_7 e3ac 130 | brightness_auto e1ab 131 | brightness_high e1ac 132 | brightness_low e1ad 133 | brightness_medium e1ae 134 | broken_image e3ad 135 | brush e3ae 136 | bubble_chart e6dd 137 | bug_report e868 138 | build e869 139 | burst_mode e43c 140 | business e0af 141 | business_center eb3f 142 | cached e86a 143 | cake e7e9 144 | call e0b0 145 | call_end e0b1 146 | call_made e0b2 147 | call_merge e0b3 148 | call_missed e0b4 149 | call_missed_outgoing e0e4 150 | call_received e0b5 151 | call_split e0b6 152 | call_to_action e06c 153 | camera e3af 154 | camera_alt e3b0 155 | camera_enhance e8fc 156 | camera_front e3b1 157 | camera_rear e3b2 158 | camera_roll e3b3 159 | cancel e5c9 160 | card_giftcard e8f6 161 | card_membership e8f7 162 | card_travel e8f8 163 | casino eb40 164 | cast e307 165 | cast_connected e308 166 | center_focus_strong e3b4 167 | center_focus_weak e3b5 168 | change_history e86b 169 | chat e0b7 170 | chat_bubble e0ca 171 | chat_bubble_outline e0cb 172 | check e5ca 173 | check_box e834 174 | check_box_outline_blank e835 175 | check_circle e86c 176 | chevron_left e5cb 177 | chevron_right e5cc 178 | child_care eb41 179 | child_friendly eb42 180 | chrome_reader_mode e86d 181 | class e86e 182 | clear e14c 183 | clear_all e0b8 184 | close e5cd 185 | closed_caption e01c 186 | cloud e2bd 187 | cloud_circle e2be 188 | cloud_done e2bf 189 | cloud_download e2c0 190 | cloud_off e2c1 191 | cloud_queue e2c2 192 | cloud_upload e2c3 193 | code e86f 194 | collections e3b6 195 | collections_bookmark e431 196 | color_lens e3b7 197 | colorize e3b8 198 | comment e0b9 199 | compare e3b9 200 | compare_arrows e915 201 | computer e30a 202 | confirmation_number e638 203 | contact_mail e0d0 204 | contact_phone e0cf 205 | contacts e0ba 206 | content_copy e14d 207 | content_cut e14e 208 | content_paste e14f 209 | control_point e3ba 210 | control_point_duplicate e3bb 211 | copyright e90c 212 | create e150 213 | create_new_folder e2cc 214 | credit_card e870 215 | crop e3be 216 | crop_16_9 e3bc 217 | crop_3_2 e3bd 218 | crop_5_4 e3bf 219 | crop_7_5 e3c0 220 | crop_din e3c1 221 | crop_free e3c2 222 | crop_landscape e3c3 223 | crop_original e3c4 224 | crop_portrait e3c5 225 | crop_rotate e437 226 | crop_square e3c6 227 | dashboard e871 228 | data_usage e1af 229 | date_range e916 230 | dehaze e3c7 231 | delete e872 232 | delete_forever e92b 233 | delete_sweep e16c 234 | description e873 235 | desktop_mac e30b 236 | desktop_windows e30c 237 | details e3c8 238 | developer_board e30d 239 | developer_mode e1b0 240 | device_hub e335 241 | devices e1b1 242 | devices_other e337 243 | dialer_sip e0bb 244 | dialpad e0bc 245 | directions e52e 246 | directions_bike e52f 247 | directions_boat e532 248 | directions_bus e530 249 | directions_car e531 250 | directions_railway e534 251 | directions_run e566 252 | directions_subway e533 253 | directions_transit e535 254 | directions_walk e536 255 | disc_full e610 256 | dns e875 257 | do_not_disturb e612 258 | do_not_disturb_alt e611 259 | do_not_disturb_off e643 260 | do_not_disturb_on e644 261 | dock e30e 262 | domain e7ee 263 | done e876 264 | done_all e877 265 | donut_large e917 266 | donut_small e918 267 | drafts e151 268 | drag_handle e25d 269 | drive_eta e613 270 | dvr e1b2 271 | edit e3c9 272 | edit_location e568 273 | eject e8fb 274 | email e0be 275 | enhanced_encryption e63f 276 | equalizer e01d 277 | error e000 278 | error_outline e001 279 | euro_symbol e926 280 | ev_station e56d 281 | event e878 282 | event_available e614 283 | event_busy e615 284 | event_note e616 285 | event_seat e903 286 | exit_to_app e879 287 | expand_less e5ce 288 | expand_more e5cf 289 | explicit e01e 290 | explore e87a 291 | exposure e3ca 292 | exposure_neg_1 e3cb 293 | exposure_neg_2 e3cc 294 | exposure_plus_1 e3cd 295 | exposure_plus_2 e3ce 296 | exposure_zero e3cf 297 | extension e87b 298 | face e87c 299 | fast_forward e01f 300 | fast_rewind e020 301 | favorite e87d 302 | favorite_border e87e 303 | featured_play_list e06d 304 | featured_video e06e 305 | feedback e87f 306 | fiber_dvr e05d 307 | fiber_manual_record e061 308 | fiber_new e05e 309 | fiber_pin e06a 310 | fiber_smart_record e062 311 | file_download e2c4 312 | file_upload e2c6 313 | filter e3d3 314 | filter_1 e3d0 315 | filter_2 e3d1 316 | filter_3 e3d2 317 | filter_4 e3d4 318 | filter_5 e3d5 319 | filter_6 e3d6 320 | filter_7 e3d7 321 | filter_8 e3d8 322 | filter_9 e3d9 323 | filter_9_plus e3da 324 | filter_b_and_w e3db 325 | filter_center_focus e3dc 326 | filter_drama e3dd 327 | filter_frames e3de 328 | filter_hdr e3df 329 | filter_list e152 330 | filter_none e3e0 331 | filter_tilt_shift e3e2 332 | filter_vintage e3e3 333 | find_in_page e880 334 | find_replace e881 335 | fingerprint e90d 336 | first_page e5dc 337 | fitness_center eb43 338 | flag e153 339 | flare e3e4 340 | flash_auto e3e5 341 | flash_off e3e6 342 | flash_on e3e7 343 | flight e539 344 | flight_land e904 345 | flight_takeoff e905 346 | flip e3e8 347 | flip_to_back e882 348 | flip_to_front e883 349 | folder e2c7 350 | folder_open e2c8 351 | folder_shared e2c9 352 | folder_special e617 353 | font_download e167 354 | format_align_center e234 355 | format_align_justify e235 356 | format_align_left e236 357 | format_align_right e237 358 | format_bold e238 359 | format_clear e239 360 | format_color_fill e23a 361 | format_color_reset e23b 362 | format_color_text e23c 363 | format_indent_decrease e23d 364 | format_indent_increase e23e 365 | format_italic e23f 366 | format_line_spacing e240 367 | format_list_bulleted e241 368 | format_list_numbered e242 369 | format_paint e243 370 | format_quote e244 371 | format_shapes e25e 372 | format_size e245 373 | format_strikethrough e246 374 | format_textdirection_l_to_r e247 375 | format_textdirection_r_to_l e248 376 | format_underlined e249 377 | forum e0bf 378 | forward e154 379 | forward_10 e056 380 | forward_30 e057 381 | forward_5 e058 382 | free_breakfast eb44 383 | fullscreen e5d0 384 | fullscreen_exit e5d1 385 | functions e24a 386 | g_translate e927 387 | gamepad e30f 388 | games e021 389 | gavel e90e 390 | gesture e155 391 | get_app e884 392 | gif e908 393 | golf_course eb45 394 | gps_fixed e1b3 395 | gps_not_fixed e1b4 396 | gps_off e1b5 397 | grade e885 398 | gradient e3e9 399 | grain e3ea 400 | graphic_eq e1b8 401 | grid_off e3eb 402 | grid_on e3ec 403 | group e7ef 404 | group_add e7f0 405 | group_work e886 406 | hd e052 407 | hdr_off e3ed 408 | hdr_on e3ee 409 | hdr_strong e3f1 410 | hdr_weak e3f2 411 | headset e310 412 | headset_mic e311 413 | healing e3f3 414 | hearing e023 415 | help e887 416 | help_outline e8fd 417 | high_quality e024 418 | highlight e25f 419 | highlight_off e888 420 | history e889 421 | home e88a 422 | hot_tub eb46 423 | hotel e53a 424 | hourglass_empty e88b 425 | hourglass_full e88c 426 | http e902 427 | https e88d 428 | image e3f4 429 | image_aspect_ratio e3f5 430 | import_contacts e0e0 431 | import_export e0c3 432 | important_devices e912 433 | inbox e156 434 | indeterminate_check_box e909 435 | info e88e 436 | info_outline e88f 437 | input e890 438 | insert_chart e24b 439 | insert_comment e24c 440 | insert_drive_file e24d 441 | insert_emoticon e24e 442 | insert_invitation e24f 443 | insert_link e250 444 | insert_photo e251 445 | invert_colors e891 446 | invert_colors_off e0c4 447 | iso e3f6 448 | keyboard e312 449 | keyboard_arrow_down e313 450 | keyboard_arrow_left e314 451 | keyboard_arrow_right e315 452 | keyboard_arrow_up e316 453 | keyboard_backspace e317 454 | keyboard_capslock e318 455 | keyboard_hide e31a 456 | keyboard_return e31b 457 | keyboard_tab e31c 458 | keyboard_voice e31d 459 | kitchen eb47 460 | label e892 461 | label_outline e893 462 | landscape e3f7 463 | language e894 464 | laptop e31e 465 | laptop_chromebook e31f 466 | laptop_mac e320 467 | laptop_windows e321 468 | last_page e5dd 469 | launch e895 470 | layers e53b 471 | layers_clear e53c 472 | leak_add e3f8 473 | leak_remove e3f9 474 | lens e3fa 475 | library_add e02e 476 | library_books e02f 477 | library_music e030 478 | lightbulb_outline e90f 479 | line_style e919 480 | line_weight e91a 481 | linear_scale e260 482 | link e157 483 | linked_camera e438 484 | list e896 485 | live_help e0c6 486 | live_tv e639 487 | local_activity e53f 488 | local_airport e53d 489 | local_atm e53e 490 | local_bar e540 491 | local_cafe e541 492 | local_car_wash e542 493 | local_convenience_store e543 494 | local_dining e556 495 | local_drink e544 496 | local_florist e545 497 | local_gas_station e546 498 | local_grocery_store e547 499 | local_hospital e548 500 | local_hotel e549 501 | local_laundry_service e54a 502 | local_library e54b 503 | local_mall e54c 504 | local_movies e54d 505 | local_offer e54e 506 | local_parking e54f 507 | local_pharmacy e550 508 | local_phone e551 509 | local_pizza e552 510 | local_play e553 511 | local_post_office e554 512 | local_printshop e555 513 | local_see e557 514 | local_shipping e558 515 | local_taxi e559 516 | location_city e7f1 517 | location_disabled e1b6 518 | location_off e0c7 519 | location_on e0c8 520 | location_searching e1b7 521 | lock e897 522 | lock_open e898 523 | lock_outline e899 524 | looks e3fc 525 | looks_3 e3fb 526 | looks_4 e3fd 527 | looks_5 e3fe 528 | looks_6 e3ff 529 | looks_one e400 530 | looks_two e401 531 | loop e028 532 | loupe e402 533 | low_priority e16d 534 | loyalty e89a 535 | mail e158 536 | mail_outline e0e1 537 | map e55b 538 | markunread e159 539 | markunread_mailbox e89b 540 | memory e322 541 | menu e5d2 542 | merge_type e252 543 | message e0c9 544 | mic e029 545 | mic_none e02a 546 | mic_off e02b 547 | mms e618 548 | mode_comment e253 549 | mode_edit e254 550 | monetization_on e263 551 | money_off e25c 552 | monochrome_photos e403 553 | mood e7f2 554 | mood_bad e7f3 555 | more e619 556 | more_horiz e5d3 557 | more_vert e5d4 558 | motorcycle e91b 559 | mouse e323 560 | move_to_inbox e168 561 | movie e02c 562 | movie_creation e404 563 | movie_filter e43a 564 | multiline_chart e6df 565 | music_note e405 566 | music_video e063 567 | my_location e55c 568 | nature e406 569 | nature_people e407 570 | navigate_before e408 571 | navigate_next e409 572 | navigation e55d 573 | near_me e569 574 | network_cell e1b9 575 | network_check e640 576 | network_locked e61a 577 | network_wifi e1ba 578 | new_releases e031 579 | next_week e16a 580 | nfc e1bb 581 | no_encryption e641 582 | no_sim e0cc 583 | not_interested e033 584 | note e06f 585 | note_add e89c 586 | notifications e7f4 587 | notifications_active e7f7 588 | notifications_none e7f5 589 | notifications_off e7f6 590 | notifications_paused e7f8 591 | offline_pin e90a 592 | ondemand_video e63a 593 | opacity e91c 594 | open_in_browser e89d 595 | open_in_new e89e 596 | open_with e89f 597 | pages e7f9 598 | pageview e8a0 599 | palette e40a 600 | pan_tool e925 601 | panorama e40b 602 | panorama_fish_eye e40c 603 | panorama_horizontal e40d 604 | panorama_vertical e40e 605 | panorama_wide_angle e40f 606 | party_mode e7fa 607 | pause e034 608 | pause_circle_filled e035 609 | pause_circle_outline e036 610 | payment e8a1 611 | people e7fb 612 | people_outline e7fc 613 | perm_camera_mic e8a2 614 | perm_contact_calendar e8a3 615 | perm_data_setting e8a4 616 | perm_device_information e8a5 617 | perm_identity e8a6 618 | perm_media e8a7 619 | perm_phone_msg e8a8 620 | perm_scan_wifi e8a9 621 | person e7fd 622 | person_add e7fe 623 | person_outline e7ff 624 | person_pin e55a 625 | person_pin_circle e56a 626 | personal_video e63b 627 | pets e91d 628 | phone e0cd 629 | phone_android e324 630 | phone_bluetooth_speaker e61b 631 | phone_forwarded e61c 632 | phone_in_talk e61d 633 | phone_iphone e325 634 | phone_locked e61e 635 | phone_missed e61f 636 | phone_paused e620 637 | phonelink e326 638 | phonelink_erase e0db 639 | phonelink_lock e0dc 640 | phonelink_off e327 641 | phonelink_ring e0dd 642 | phonelink_setup e0de 643 | photo e410 644 | photo_album e411 645 | photo_camera e412 646 | photo_filter e43b 647 | photo_library e413 648 | photo_size_select_actual e432 649 | photo_size_select_large e433 650 | photo_size_select_small e434 651 | picture_as_pdf e415 652 | picture_in_picture e8aa 653 | picture_in_picture_alt e911 654 | pie_chart e6c4 655 | pie_chart_outlined e6c5 656 | pin_drop e55e 657 | place e55f 658 | play_arrow e037 659 | play_circle_filled e038 660 | play_circle_outline e039 661 | play_for_work e906 662 | playlist_add e03b 663 | playlist_add_check e065 664 | playlist_play e05f 665 | plus_one e800 666 | poll e801 667 | polymer e8ab 668 | pool eb48 669 | portable_wifi_off e0ce 670 | portrait e416 671 | power e63c 672 | power_input e336 673 | power_settings_new e8ac 674 | pregnant_woman e91e 675 | present_to_all e0df 676 | print e8ad 677 | priority_high e645 678 | public e80b 679 | publish e255 680 | query_builder e8ae 681 | question_answer e8af 682 | queue e03c 683 | queue_music e03d 684 | queue_play_next e066 685 | radio e03e 686 | radio_button_checked e837 687 | radio_button_unchecked e836 688 | rate_review e560 689 | receipt e8b0 690 | recent_actors e03f 691 | record_voice_over e91f 692 | redeem e8b1 693 | redo e15a 694 | refresh e5d5 695 | remove e15b 696 | remove_circle e15c 697 | remove_circle_outline e15d 698 | remove_from_queue e067 699 | remove_red_eye e417 700 | remove_shopping_cart e928 701 | reorder e8fe 702 | repeat e040 703 | repeat_one e041 704 | replay e042 705 | replay_10 e059 706 | replay_30 e05a 707 | replay_5 e05b 708 | reply e15e 709 | reply_all e15f 710 | report e160 711 | report_problem e8b2 712 | restaurant e56c 713 | restaurant_menu e561 714 | restore e8b3 715 | restore_page e929 716 | ring_volume e0d1 717 | room e8b4 718 | room_service eb49 719 | rotate_90_degrees_ccw e418 720 | rotate_left e419 721 | rotate_right e41a 722 | rounded_corner e920 723 | router e328 724 | rowing e921 725 | rss_feed e0e5 726 | rv_hookup e642 727 | satellite e562 728 | save e161 729 | scanner e329 730 | schedule e8b5 731 | school e80c 732 | screen_lock_landscape e1be 733 | screen_lock_portrait e1bf 734 | screen_lock_rotation e1c0 735 | screen_rotation e1c1 736 | screen_share e0e2 737 | sd_card e623 738 | sd_storage e1c2 739 | search e8b6 740 | security e32a 741 | select_all e162 742 | send e163 743 | sentiment_dissatisfied e811 744 | sentiment_neutral e812 745 | sentiment_satisfied e813 746 | sentiment_very_dissatisfied e814 747 | sentiment_very_satisfied e815 748 | settings e8b8 749 | settings_applications e8b9 750 | settings_backup_restore e8ba 751 | settings_bluetooth e8bb 752 | settings_brightness e8bd 753 | settings_cell e8bc 754 | settings_ethernet e8be 755 | settings_input_antenna e8bf 756 | settings_input_component e8c0 757 | settings_input_composite e8c1 758 | settings_input_hdmi e8c2 759 | settings_input_svideo e8c3 760 | settings_overscan e8c4 761 | settings_phone e8c5 762 | settings_power e8c6 763 | settings_remote e8c7 764 | settings_system_daydream e1c3 765 | settings_voice e8c8 766 | share e80d 767 | shop e8c9 768 | shop_two e8ca 769 | shopping_basket e8cb 770 | shopping_cart e8cc 771 | short_text e261 772 | show_chart e6e1 773 | shuffle e043 774 | signal_cellular_4_bar e1c8 775 | signal_cellular_connected_no_internet_4_bar e1cd 776 | signal_cellular_no_sim e1ce 777 | signal_cellular_null e1cf 778 | signal_cellular_off e1d0 779 | signal_wifi_4_bar e1d8 780 | signal_wifi_4_bar_lock e1d9 781 | signal_wifi_off e1da 782 | sim_card e32b 783 | sim_card_alert e624 784 | skip_next e044 785 | skip_previous e045 786 | slideshow e41b 787 | slow_motion_video e068 788 | smartphone e32c 789 | smoke_free eb4a 790 | smoking_rooms eb4b 791 | sms e625 792 | sms_failed e626 793 | snooze e046 794 | sort e164 795 | sort_by_alpha e053 796 | spa eb4c 797 | space_bar e256 798 | speaker e32d 799 | speaker_group e32e 800 | speaker_notes e8cd 801 | speaker_notes_off e92a 802 | speaker_phone e0d2 803 | spellcheck e8ce 804 | star e838 805 | star_border e83a 806 | star_half e839 807 | stars e8d0 808 | stay_current_landscape e0d3 809 | stay_current_portrait e0d4 810 | stay_primary_landscape e0d5 811 | stay_primary_portrait e0d6 812 | stop e047 813 | stop_screen_share e0e3 814 | storage e1db 815 | store e8d1 816 | store_mall_directory e563 817 | straighten e41c 818 | streetview e56e 819 | strikethrough_s e257 820 | style e41d 821 | subdirectory_arrow_left e5d9 822 | subdirectory_arrow_right e5da 823 | subject e8d2 824 | subscriptions e064 825 | subtitles e048 826 | subway e56f 827 | supervisor_account e8d3 828 | surround_sound e049 829 | swap_calls e0d7 830 | swap_horiz e8d4 831 | swap_vert e8d5 832 | swap_vertical_circle e8d6 833 | switch_camera e41e 834 | switch_video e41f 835 | sync e627 836 | sync_disabled e628 837 | sync_problem e629 838 | system_update e62a 839 | system_update_alt e8d7 840 | tab e8d8 841 | tab_unselected e8d9 842 | tablet e32f 843 | tablet_android e330 844 | tablet_mac e331 845 | tag_faces e420 846 | tap_and_play e62b 847 | terrain e564 848 | text_fields e262 849 | text_format e165 850 | textsms e0d8 851 | texture e421 852 | theaters e8da 853 | thumb_down e8db 854 | thumb_up e8dc 855 | thumbs_up_down e8dd 856 | time_to_leave e62c 857 | timelapse e422 858 | timeline e922 859 | timer e425 860 | timer_10 e423 861 | timer_3 e424 862 | timer_off e426 863 | title e264 864 | toc e8de 865 | today e8df 866 | toll e8e0 867 | tonality e427 868 | touch_app e913 869 | toys e332 870 | track_changes e8e1 871 | traffic e565 872 | train e570 873 | tram e571 874 | transfer_within_a_station e572 875 | transform e428 876 | translate e8e2 877 | trending_down e8e3 878 | trending_flat e8e4 879 | trending_up e8e5 880 | tune e429 881 | turned_in e8e6 882 | turned_in_not e8e7 883 | tv e333 884 | unarchive e169 885 | undo e166 886 | unfold_less e5d6 887 | unfold_more e5d7 888 | update e923 889 | usb e1e0 890 | verified_user e8e8 891 | vertical_align_bottom e258 892 | vertical_align_center e259 893 | vertical_align_top e25a 894 | vibration e62d 895 | video_call e070 896 | video_label e071 897 | video_library e04a 898 | videocam e04b 899 | videocam_off e04c 900 | videogame_asset e338 901 | view_agenda e8e9 902 | view_array e8ea 903 | view_carousel e8eb 904 | view_column e8ec 905 | view_comfy e42a 906 | view_compact e42b 907 | view_day e8ed 908 | view_headline e8ee 909 | view_list e8ef 910 | view_module e8f0 911 | view_quilt e8f1 912 | view_stream e8f2 913 | view_week e8f3 914 | vignette e435 915 | visibility e8f4 916 | visibility_off e8f5 917 | voice_chat e62e 918 | voicemail e0d9 919 | volume_down e04d 920 | volume_mute e04e 921 | volume_off e04f 922 | volume_up e050 923 | vpn_key e0da 924 | vpn_lock e62f 925 | wallpaper e1bc 926 | warning e002 927 | watch e334 928 | watch_later e924 929 | wb_auto e42c 930 | wb_cloudy e42d 931 | wb_incandescent e42e 932 | wb_iridescent e436 933 | wb_sunny e430 934 | wc e63d 935 | web e051 936 | web_asset e069 937 | weekend e16b 938 | whatshot e80e 939 | widgets e1bd 940 | wifi e63e 941 | wifi_lock e1e1 942 | wifi_tethering e1e2 943 | work e8f9 944 | wrap_text e25b 945 | youtube_searched_for e8fa 946 | zoom_in e8ff 947 | zoom_out e900 948 | zoom_out_map e56b 949 | ``` -------------------------------------------------------------------------------- /demo/src/main.ts: -------------------------------------------------------------------------------- 1 | // this import should be first in order to load some required settings (like globals and reflect-metadata) 2 | import { platformNativeScriptDynamic } from "nativescript-angular/platform"; 3 | 4 | import { AppModule } from "./app/app.module"; 5 | 6 | platformNativeScriptDynamic().bootstrapModule(AppModule); 7 | -------------------------------------------------------------------------------- /demo/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "main.js", 3 | "android": { 4 | "v8Flags": "--expose_gc", 5 | "markingMode": "none" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /demo/src/settings/settings.android.ts: -------------------------------------------------------------------------------- 1 | /** This file is created automatically via ths script `./upload-jsonblob.com.sh` */ 2 | /** Manual modifications may be overridden. */ 3 | /** Define the platform specific URL providing the product list. */ 4 | export const URL_PRODUCTS = 'https://www.json-generator.com/api/json/get/clbrXUjpbC?indent=2' 5 | -------------------------------------------------------------------------------- /demo/src/settings/settings.ios.ts: -------------------------------------------------------------------------------- 1 | /** This file is created automatically via ths script `./upload-jsonblob.com.sh` */ 2 | /** Manual modifications may be overridden. */ 3 | /** Define the platform specific URL providing the product list. */ 4 | export const URL_PRODUCTS = 'https://www.json-generator.com/api/json/get/cjMZxRudbC?indent=2' 5 | -------------------------------------------------------------------------------- /demo/src/settings/settings.ts: -------------------------------------------------------------------------------- 1 | /** This file is created automatically via ths script `./upload-jsonblob.com.sh` */ 2 | /** Manual modifications may be overridden. */ 3 | /** Define the platform specific URL providing the product list. */ 4 | export const URL_PRODUCTS = 'https://www.json-generator.com/api/json/get/bUwovcXSNu?indent=2' 5 | -------------------------------------------------------------------------------- /demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es5", 5 | "experimentalDecorators": true, 6 | "emitDecoratorMetadata": true, 7 | "noEmitHelpers": true, 8 | "noEmitOnError": true, 9 | "skipLibCheck": true, 10 | "lib": [ 11 | "es6", 12 | "dom", 13 | "es2015.iterable" 14 | ], 15 | "baseUrl": ".", 16 | "paths": { 17 | "~/*": [ 18 | "src/*" 19 | ], 20 | "*": [ 21 | "./node_modules/*" 22 | ] 23 | } 24 | }, 25 | "exclude": [ 26 | "node_modules", 27 | "platforms", 28 | "../src/node_modules" 29 | ], 30 | "include": [ 31 | "../src", 32 | "**/*" 33 | ] 34 | } -------------------------------------------------------------------------------- /demo/tsconfig.tns.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "module": "esNext", 5 | "moduleResolution": "node" 6 | } 7 | } -------------------------------------------------------------------------------- /demo/tsfmt.json: -------------------------------------------------------------------------------- 1 | { 2 | "indentSize": 4, 3 | "tabSize": 4 4 | } 5 | -------------------------------------------------------------------------------- /demo/webpack.config.js: -------------------------------------------------------------------------------- 1 | const { join, relative, resolve, sep, dirname } = require("path"); 2 | 3 | const webpack = require("webpack"); 4 | const nsWebpack = require("nativescript-dev-webpack"); 5 | const nativescriptTarget = require("nativescript-dev-webpack/nativescript-target"); 6 | const { nsReplaceBootstrap } = require("nativescript-dev-webpack/transformers/ns-replace-bootstrap"); 7 | const { nsReplaceLazyLoader } = require("nativescript-dev-webpack/transformers/ns-replace-lazy-loader"); 8 | const { nsSupportHmrNg } = require("nativescript-dev-webpack/transformers/ns-support-hmr-ng"); 9 | const { getMainModulePath } = require("nativescript-dev-webpack/utils/ast-utils"); 10 | const { getNoEmitOnErrorFromTSConfig } = require("nativescript-dev-webpack/utils/tsconfig-utils"); 11 | const CleanWebpackPlugin = require("clean-webpack-plugin"); 12 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 13 | const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); 14 | const { NativeScriptWorkerPlugin } = require("nativescript-worker-loader/NativeScriptWorkerPlugin"); 15 | const TerserPlugin = require("terser-webpack-plugin"); 16 | const { getAngularCompilerPlugin } = require("nativescript-dev-webpack/plugins/NativeScriptAngularCompilerPlugin"); 17 | const hashSalt = Date.now().toString(); 18 | 19 | module.exports = env => { 20 | // Add your custom Activities, Services and other Android app components here. 21 | const appComponents = [ 22 | "tns-core-modules/ui/frame", 23 | "tns-core-modules/ui/frame/activity", 24 | ]; 25 | 26 | const platform = env && (env.android && "android" || env.ios && "ios"); 27 | if (!platform) { 28 | throw new Error("You need to provide a target platform!"); 29 | } 30 | 31 | const AngularCompilerPlugin = getAngularCompilerPlugin(platform); 32 | const projectRoot = __dirname; 33 | 34 | // Default destination inside platforms//... 35 | const dist = resolve(projectRoot, nsWebpack.getAppPath(platform, projectRoot)); 36 | 37 | const { 38 | // The 'appPath' and 'appResourcesPath' values are fetched from 39 | // the nsconfig.json configuration file. 40 | appPath = "src", 41 | appResourcesPath = "App_Resources", 42 | 43 | // You can provide the following flags when running 'tns run android|ios' 44 | aot, // --env.aot 45 | snapshot, // --env.snapshot, 46 | production, // --env.production 47 | uglify, // --env.uglify 48 | report, // --env.report 49 | sourceMap, // --env.sourceMap 50 | hiddenSourceMap, // --env.hiddenSourceMap 51 | hmr, // --env.hmr, 52 | unitTesting, // --env.unitTesting 53 | verbose, // --env.verbose 54 | snapshotInDocker, // --env.snapshotInDocker 55 | skipSnapshotTools, // --env.skipSnapshotTools 56 | compileSnapshot // --env.compileSnapshot 57 | } = env; 58 | 59 | const useLibs = compileSnapshot; 60 | const isAnySourceMapEnabled = !!sourceMap || !!hiddenSourceMap; 61 | const externals = nsWebpack.getConvertedExternals(env.externals); 62 | const appFullPath = resolve(projectRoot, appPath); 63 | const appResourcesFullPath = resolve(projectRoot, appResourcesPath); 64 | const tsConfigName = "tsconfig.tns.json"; 65 | const entryModule = `${nsWebpack.getEntryModule(appFullPath, platform)}.ts`; 66 | const entryPath = `.${sep}${entryModule}`; 67 | const entries = { bundle: entryPath }; 68 | const areCoreModulesExternal = Array.isArray(env.externals) && env.externals.some(e => e.indexOf("tns-core-modules") > -1); 69 | if (platform === "ios" && !areCoreModulesExternal) { 70 | entries["tns_modules/tns-core-modules/inspector_modules"] = "inspector_modules"; 71 | }; 72 | 73 | const ngCompilerTransformers = []; 74 | const additionalLazyModuleResources = []; 75 | if (aot) { 76 | ngCompilerTransformers.push(nsReplaceBootstrap); 77 | } 78 | 79 | if (hmr) { 80 | ngCompilerTransformers.push(nsSupportHmrNg); 81 | } 82 | 83 | // when "@angular/core" is external, it's not included in the bundles. In this way, it will be used 84 | // directly from node_modules and the Angular modules loader won't be able to resolve the lazy routes 85 | // fixes https://github.com/NativeScript/nativescript-cli/issues/4024 86 | if (env.externals && env.externals.indexOf("@angular/core") > -1) { 87 | const appModuleRelativePath = getMainModulePath(resolve(appFullPath, entryModule), tsConfigName); 88 | if (appModuleRelativePath) { 89 | const appModuleFolderPath = dirname(resolve(appFullPath, appModuleRelativePath)); 90 | // include the lazy loader inside app module 91 | ngCompilerTransformers.push(nsReplaceLazyLoader); 92 | // include the new lazy loader path in the allowed ones 93 | additionalLazyModuleResources.push(appModuleFolderPath); 94 | } 95 | } 96 | 97 | const ngCompilerPlugin = new AngularCompilerPlugin({ 98 | hostReplacementPaths: nsWebpack.getResolver([platform, "tns"]), 99 | platformTransformers: ngCompilerTransformers.map(t => t(() => ngCompilerPlugin, resolve(appFullPath, entryModule), projectRoot)), 100 | mainPath: join(appFullPath, entryModule), 101 | tsConfigPath: join(__dirname, tsConfigName), 102 | skipCodeGeneration: !aot, 103 | sourceMap: !!isAnySourceMapEnabled, 104 | additionalLazyModuleResources: additionalLazyModuleResources 105 | }); 106 | 107 | let sourceMapFilename = nsWebpack.getSourceMapFilename(hiddenSourceMap, __dirname, dist); 108 | 109 | const itemsToClean = [`${dist}/**/*`]; 110 | if (platform === "android") { 111 | itemsToClean.push(`${join(projectRoot, "platforms", "android", "app", "src", "main", "assets", "snapshots")}`); 112 | itemsToClean.push(`${join(projectRoot, "platforms", "android", "app", "build", "configurations", "nativescript-android-snapshot")}`); 113 | } 114 | 115 | const noEmitOnErrorFromTSConfig = getNoEmitOnErrorFromTSConfig(join(projectRoot, tsConfigName)); 116 | 117 | nsWebpack.processAppComponents(appComponents, platform); 118 | const config = { 119 | mode: production ? "production" : "development", 120 | context: appFullPath, 121 | externals, 122 | watchOptions: { 123 | ignored: [ 124 | appResourcesFullPath, 125 | // Don't watch hidden files 126 | "**/.*", 127 | ] 128 | }, 129 | target: nativescriptTarget, 130 | entry: entries, 131 | output: { 132 | pathinfo: false, 133 | path: dist, 134 | sourceMapFilename, 135 | libraryTarget: "commonjs2", 136 | filename: "[name].js", 137 | globalObject: "global", 138 | hashSalt 139 | }, 140 | resolve: { 141 | extensions: [".ts", ".js", ".scss", ".css"], 142 | // Resolve {N} system modules from tns-core-modules 143 | modules: [ 144 | resolve(__dirname, "node_modules/tns-core-modules"), 145 | resolve(__dirname, "node_modules"), 146 | "node_modules/tns-core-modules", 147 | "node_modules", 148 | ], 149 | alias: { 150 | '~': appFullPath 151 | }, 152 | symlinks: true 153 | }, 154 | resolveLoader: { 155 | symlinks: false 156 | }, 157 | node: { 158 | // Disable node shims that conflict with NativeScript 159 | "http": false, 160 | "timers": false, 161 | "setImmediate": false, 162 | "fs": "empty", 163 | "__dirname": false, 164 | }, 165 | devtool: hiddenSourceMap ? "hidden-source-map" : (sourceMap ? "inline-source-map" : "none"), 166 | optimization: { 167 | runtimeChunk: "single", 168 | noEmitOnErrors: noEmitOnErrorFromTSConfig, 169 | splitChunks: { 170 | cacheGroups: { 171 | vendor: { 172 | name: "vendor", 173 | chunks: "all", 174 | test: (module, chunks) => { 175 | const moduleName = module.nameForCondition ? module.nameForCondition() : ''; 176 | return /[\\/]node_modules[\\/]/.test(moduleName) || 177 | appComponents.some(comp => comp === moduleName); 178 | }, 179 | enforce: true, 180 | }, 181 | } 182 | }, 183 | minimize: !!uglify, 184 | minimizer: [ 185 | new TerserPlugin({ 186 | parallel: true, 187 | cache: true, 188 | sourceMap: isAnySourceMapEnabled, 189 | terserOptions: { 190 | output: { 191 | comments: false, 192 | semicolons: !isAnySourceMapEnabled 193 | }, 194 | compress: { 195 | // The Android SBG has problems parsing the output 196 | // when these options are enabled 197 | 'collapse_vars': platform !== "android", 198 | sequences: platform !== "android", 199 | } 200 | } 201 | }) 202 | ], 203 | }, 204 | module: { 205 | rules: [ 206 | { 207 | include: join(appFullPath, entryPath), 208 | use: [ 209 | // Require all Android app components 210 | platform === "android" && { 211 | loader: "nativescript-dev-webpack/android-app-components-loader", 212 | options: { modules: appComponents } 213 | }, 214 | 215 | { 216 | loader: "nativescript-dev-webpack/bundle-config-loader", 217 | options: { 218 | angular: true, 219 | loadCss: !snapshot, // load the application css if in debug mode 220 | unitTesting, 221 | appFullPath, 222 | projectRoot, 223 | ignoredFiles: nsWebpack.getUserDefinedEntries(entries, platform) 224 | } 225 | }, 226 | ].filter(loader => !!loader) 227 | }, 228 | 229 | { test: /\.html$|\.xml$/, use: "raw-loader" }, 230 | 231 | { 232 | test: /[\/|\\]app\.css$/, 233 | use: [ 234 | "nativescript-dev-webpack/style-hot-loader", 235 | { 236 | loader: "nativescript-dev-webpack/css2json-loader", 237 | options: { useForImports: true } 238 | } 239 | ] 240 | }, 241 | { 242 | test: /[\/|\\]app\.scss$/, 243 | use: [ 244 | "nativescript-dev-webpack/style-hot-loader", 245 | { 246 | loader: "nativescript-dev-webpack/css2json-loader", 247 | options: { useForImports: true } 248 | }, 249 | "sass-loader" 250 | ] 251 | }, 252 | 253 | // Angular components reference css files and their imports using raw-loader 254 | { test: /\.css$/, exclude: /[\/|\\]app\.css$/, use: "raw-loader" }, 255 | { test: /\.scss$/, exclude: /[\/|\\]app\.scss$/, use: ["raw-loader", "resolve-url-loader", "sass-loader"] }, 256 | 257 | { 258 | test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/, 259 | use: [ 260 | "nativescript-dev-webpack/moduleid-compat-loader", 261 | "nativescript-dev-webpack/lazy-ngmodule-hot-loader", 262 | "@ngtools/webpack", 263 | ] 264 | }, 265 | 266 | // Mark files inside `@angular/core` as using SystemJS style dynamic imports. 267 | // Removing this will cause deprecation warnings to appear. 268 | { 269 | test: /[\/\\]@angular[\/\\]core[\/\\].+\.js$/, 270 | parser: { system: true }, 271 | }, 272 | ], 273 | }, 274 | plugins: [ 275 | // Define useful constants like TNS_WEBPACK 276 | new webpack.DefinePlugin({ 277 | "global.TNS_WEBPACK": "true", 278 | "process": "global.process", 279 | }), 280 | // Remove all files from the out dir. 281 | new CleanWebpackPlugin(itemsToClean, { verbose: !!verbose }), 282 | // Copy assets to out dir. Add your own globs as needed. 283 | new CopyWebpackPlugin([ 284 | { from: { glob: "fonts/**" } }, 285 | { from: { glob: "**/*.jpg" } }, 286 | { from: { glob: "**/*.png" } }, 287 | ], { ignore: [`${relative(appPath, appResourcesFullPath)}/**`] }), 288 | new nsWebpack.GenerateNativeScriptEntryPointsPlugin("bundle"), 289 | // For instructions on how to set up workers with webpack 290 | // check out https://github.com/nativescript/worker-loader 291 | new NativeScriptWorkerPlugin(), 292 | ngCompilerPlugin, 293 | // Does IPC communication with the {N} CLI to notify events when running in watch mode. 294 | new nsWebpack.WatchStateLoggerPlugin(), 295 | ], 296 | }; 297 | 298 | if (report) { 299 | // Generate report files for bundles content 300 | config.plugins.push(new BundleAnalyzerPlugin({ 301 | analyzerMode: "static", 302 | openAnalyzer: false, 303 | generateStatsFile: true, 304 | reportFilename: resolve(projectRoot, "report", `report.html`), 305 | statsFilename: resolve(projectRoot, "report", `stats.json`), 306 | })); 307 | } 308 | 309 | if (snapshot) { 310 | config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ 311 | chunk: "vendor", 312 | angular: true, 313 | requireModules: [ 314 | "reflect-metadata", 315 | "@angular/platform-browser", 316 | "@angular/core", 317 | "@angular/common", 318 | "@angular/router", 319 | "nativescript-angular/platform-static", 320 | "nativescript-angular/router", 321 | ], 322 | projectRoot, 323 | webpackConfig: config, 324 | snapshotInDocker, 325 | skipSnapshotTools, 326 | useLibs 327 | })); 328 | } 329 | 330 | if (hmr) { 331 | config.plugins.push(new webpack.HotModuleReplacementPlugin()); 332 | } 333 | 334 | return config; 335 | }; 336 | -------------------------------------------------------------------------------- /publish/pack-customized.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SOURCE_DIR=../src; 4 | TO_SOURCE_DIR=src; 5 | PACK_DIR=package; 6 | ROOT_DIR=..; 7 | PUBLISH=--publish 8 | 9 | install(){ 10 | npm i 11 | } 12 | 13 | pack() { 14 | 15 | echo 'Clearing /src and /package...' 16 | node_modules/.bin/rimraf "$TO_SOURCE_DIR" 17 | node_modules/.bin/rimraf "$PACK_DIR" 18 | 19 | # copy src 20 | echo 'Copying src...' 21 | node_modules/.bin/ncp "$SOURCE_DIR" "$TO_SOURCE_DIR" 22 | 23 | # copy README & LICENSE to src 24 | echo 'Copying README and LICENSE to /src...' 25 | node_modules/.bin/ncp "$ROOT_DIR"/LICENSE "$TO_SOURCE_DIR"/LICENSE 26 | node_modules/.bin/ncp "$ROOT_DIR"/README.md "$TO_SOURCE_DIR"/README.md 27 | node_modules/.bin/ncp "$ROOT_DIR"/DEMO.md "$TO_SOURCE_DIR"/DEMO.md 28 | 29 | # compile package and copy files required by npm 30 | echo 'Building /src...' 31 | cd "$TO_SOURCE_DIR" 32 | node_modules/.bin/tsc 33 | cd .. 34 | 35 | echo 'Creating package...' 36 | # create package dir 37 | mkdir "$PACK_DIR" 38 | 39 | # create the package 40 | cd "$PACK_DIR" 41 | npm pack ../"$TO_SOURCE_DIR" 42 | 43 | # delete source directory used to create the package 44 | cd .. 45 | node_modules/.bin/rimraf "$TO_SOURCE_DIR" 46 | } 47 | 48 | install && pack -------------------------------------------------------------------------------- /publish/pack.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SOURCE_DIR=../src; 4 | TO_SOURCE_DIR=src; 5 | PACK_DIR=package; 6 | ROOT_DIR=..; 7 | PUBLISH=--publish 8 | 9 | install(){ 10 | npm i 11 | } 12 | 13 | pack() { 14 | 15 | echo 'Clearing /src and /package...' 16 | node_modules/.bin/rimraf "$TO_SOURCE_DIR" 17 | node_modules/.bin/rimraf "$PACK_DIR" 18 | 19 | # copy src 20 | echo 'Copying src...' 21 | node_modules/.bin/ncp "$SOURCE_DIR" "$TO_SOURCE_DIR" 22 | 23 | # copy README & LICENSE to src 24 | echo 'Copying README and LICENSE to /src...' 25 | node_modules/.bin/ncp "$ROOT_DIR"/LICENSE "$TO_SOURCE_DIR"/LICENSE 26 | node_modules/.bin/ncp "$ROOT_DIR"/README.md "$TO_SOURCE_DIR"/README.md 27 | 28 | # compile package and copy files required by npm 29 | echo 'Building /src...' 30 | cd "$TO_SOURCE_DIR" 31 | node_modules/.bin/tsc 32 | cd .. 33 | 34 | echo 'Creating package...' 35 | # create package dir 36 | mkdir "$PACK_DIR" 37 | 38 | # create the package 39 | cd "$PACK_DIR" 40 | npm pack ../"$TO_SOURCE_DIR" 41 | 42 | # delete source directory used to create the package 43 | cd .. 44 | node_modules/.bin/rimraf "$TO_SOURCE_DIR" 45 | } 46 | 47 | install && pack -------------------------------------------------------------------------------- /publish/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nativescript-publish", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "nativescript-publish", 9 | "version": "1.0.0", 10 | "devDependencies": { 11 | "ncp": "^2.0.0", 12 | "rimraf": "^2.5.0" 13 | } 14 | }, 15 | "node_modules/balanced-match": { 16 | "version": "1.0.0", 17 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 18 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 19 | "dev": true 20 | }, 21 | "node_modules/brace-expansion": { 22 | "version": "1.1.11", 23 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 24 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 25 | "dev": true, 26 | "dependencies": { 27 | "balanced-match": "^1.0.0", 28 | "concat-map": "0.0.1" 29 | } 30 | }, 31 | "node_modules/concat-map": { 32 | "version": "0.0.1", 33 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 34 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 35 | "dev": true 36 | }, 37 | "node_modules/fs.realpath": { 38 | "version": "1.0.0", 39 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 40 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 41 | "dev": true 42 | }, 43 | "node_modules/glob": { 44 | "version": "7.1.6", 45 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 46 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 47 | "dev": true, 48 | "dependencies": { 49 | "fs.realpath": "^1.0.0", 50 | "inflight": "^1.0.4", 51 | "inherits": "2", 52 | "minimatch": "^3.0.4", 53 | "once": "^1.3.0", 54 | "path-is-absolute": "^1.0.0" 55 | }, 56 | "engines": { 57 | "node": "*" 58 | }, 59 | "funding": { 60 | "url": "https://github.com/sponsors/isaacs" 61 | } 62 | }, 63 | "node_modules/inflight": { 64 | "version": "1.0.6", 65 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 66 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 67 | "dev": true, 68 | "dependencies": { 69 | "once": "^1.3.0", 70 | "wrappy": "1" 71 | } 72 | }, 73 | "node_modules/inherits": { 74 | "version": "2.0.4", 75 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 76 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 77 | "dev": true 78 | }, 79 | "node_modules/minimatch": { 80 | "version": "3.0.4", 81 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 82 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 83 | "dev": true, 84 | "dependencies": { 85 | "brace-expansion": "^1.1.7" 86 | }, 87 | "engines": { 88 | "node": "*" 89 | } 90 | }, 91 | "node_modules/ncp": { 92 | "version": "2.0.0", 93 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", 94 | "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", 95 | "dev": true, 96 | "bin": { 97 | "ncp": "bin/ncp" 98 | } 99 | }, 100 | "node_modules/once": { 101 | "version": "1.4.0", 102 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 103 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 104 | "dev": true, 105 | "dependencies": { 106 | "wrappy": "1" 107 | } 108 | }, 109 | "node_modules/path-is-absolute": { 110 | "version": "1.0.1", 111 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 112 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 113 | "dev": true, 114 | "engines": { 115 | "node": ">=0.10.0" 116 | } 117 | }, 118 | "node_modules/rimraf": { 119 | "version": "2.7.1", 120 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 121 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 122 | "dev": true, 123 | "dependencies": { 124 | "glob": "^7.1.3" 125 | }, 126 | "bin": { 127 | "rimraf": "bin.js" 128 | } 129 | }, 130 | "node_modules/wrappy": { 131 | "version": "1.0.2", 132 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 133 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 134 | "dev": true 135 | } 136 | }, 137 | "dependencies": { 138 | "balanced-match": { 139 | "version": "1.0.0", 140 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 141 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 142 | "dev": true 143 | }, 144 | "brace-expansion": { 145 | "version": "1.1.11", 146 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 147 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 148 | "dev": true, 149 | "requires": { 150 | "balanced-match": "^1.0.0", 151 | "concat-map": "0.0.1" 152 | } 153 | }, 154 | "concat-map": { 155 | "version": "0.0.1", 156 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 157 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 158 | "dev": true 159 | }, 160 | "fs.realpath": { 161 | "version": "1.0.0", 162 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 163 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 164 | "dev": true 165 | }, 166 | "glob": { 167 | "version": "7.1.6", 168 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 169 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 170 | "dev": true, 171 | "requires": { 172 | "fs.realpath": "^1.0.0", 173 | "inflight": "^1.0.4", 174 | "inherits": "2", 175 | "minimatch": "^3.0.4", 176 | "once": "^1.3.0", 177 | "path-is-absolute": "^1.0.0" 178 | } 179 | }, 180 | "inflight": { 181 | "version": "1.0.6", 182 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 183 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 184 | "dev": true, 185 | "requires": { 186 | "once": "^1.3.0", 187 | "wrappy": "1" 188 | } 189 | }, 190 | "inherits": { 191 | "version": "2.0.4", 192 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 193 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 194 | "dev": true 195 | }, 196 | "minimatch": { 197 | "version": "3.0.4", 198 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 199 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 200 | "dev": true, 201 | "requires": { 202 | "brace-expansion": "^1.1.7" 203 | } 204 | }, 205 | "ncp": { 206 | "version": "2.0.0", 207 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", 208 | "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", 209 | "dev": true 210 | }, 211 | "once": { 212 | "version": "1.4.0", 213 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 214 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 215 | "dev": true, 216 | "requires": { 217 | "wrappy": "1" 218 | } 219 | }, 220 | "path-is-absolute": { 221 | "version": "1.0.1", 222 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 223 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 224 | "dev": true 225 | }, 226 | "rimraf": { 227 | "version": "2.7.1", 228 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 229 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 230 | "dev": true, 231 | "requires": { 232 | "glob": "^7.1.3" 233 | } 234 | }, 235 | "wrappy": { 236 | "version": "1.0.2", 237 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 238 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 239 | "dev": true 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /publish/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nativescript-publish", 3 | "version": "1.0.0", 4 | "description": "Publish helper", 5 | "devDependencies": { 6 | "ncp": "^2.0.0", 7 | "rimraf": "^2.5.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /publish/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PACK_DIR=package; 4 | 5 | publish() { 6 | cd $PACK_DIR 7 | echo 'Publishing to npm...' 8 | npm publish *.tgz --access public 9 | } 10 | 11 | ./pack-customized.sh && publish -------------------------------------------------------------------------------- /src/.npmignore: -------------------------------------------------------------------------------- 1 | *.map 2 | *.ts 3 | !*.d.ts 4 | tsconfig.json 5 | scripts/* 6 | platforms/android/* 7 | !platforms/android/include.gradle 8 | !platforms/android/*.aar 9 | !platforms/android/*.jar 10 | .DS_Store -------------------------------------------------------------------------------- /src/in-app-purchase.common.ts: -------------------------------------------------------------------------------- 1 | export class InAppPurchaseManagerBase implements InAppPurchaseUpdateNotifier { 2 | protected constructor(protected purchaseStateUpdateListener: InAppPurchaseStateUpdateListener) { } 3 | 4 | notifyOnUpdate(purchaseTransactionState: InAppPurchaseTransactionState) { 5 | this.purchaseStateUpdateListener.onUpdate(purchaseTransactionState) 6 | } 7 | 8 | notifyOnUpdateHistory(purchaseTransactionState: InAppPurchaseTransactionState) { 9 | this.purchaseStateUpdateListener.onUpdateHistory(purchaseTransactionState) 10 | } 11 | 12 | } 13 | 14 | 15 | 16 | import { InAppPurchaseStateUpdateListener, InAppPurchaseUpdateNotifier, InAppPurchaseType, InAppPurchaseResultCode } from "." 17 | export class InAppProduct { 18 | productId: string 19 | title: string 20 | description?: string 21 | /** 22 | * Returns formatted price of the item, including its currency sign. 23 | */ 24 | price?: string 25 | /** 26 | * Returns price in micro-units, where 1,000,000 micro-units equal one unit of the currency.
27 | * For example, if price is "€7.99", price_amount_micros is "7990000" 28 | */ 29 | priceMicro?: number 30 | /** 31 | * The price currency code ISO-4271 32 | */ 33 | priceCurrencyCode: string 34 | type?: InAppPurchaseType 35 | /** 36 | * Android Only.
37 | * Get the url the the icon for this item. 38 | */ 39 | android_iconUrl?: string 40 | 41 | /** 42 | * 43 | * @param nativeValue The original OS specific product entity.
44 | * On Android it is type com.android.billingclient.api.SkuDetails
45 | * On IOS it is type SKProduct 46 | */ 47 | constructor(public nativeValue: any) { } 48 | } 49 | 50 | export class InAppPurchaseTransactionState { 51 | resultCode: InAppPurchaseResultCode 52 | productIdentifier: string 53 | purchaseId: string 54 | purchaseTime: number 55 | /** 56 | * 57 | * @param nativeValue The underlying OS specific purchase transaction.
58 | * May be undefined.
59 | * On Android it is type com.android.billingclient.api.Purchase
60 | * On IOS it is type SKPaymentTransaction 61 | */ 62 | constructor(public nativeValue: any) { } 63 | } -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | export declare interface InAppPurchaseStateUpdateListener { 2 | /** 3 | * Listener to get notified when a purchase gets updated 4 | * @param purchaseTransactionState 5 | */ 6 | onUpdate(purchaseTransactionState: InAppPurchaseTransactionState): void 7 | /** 8 | * Listener to get notified when restoring the uses already purchased items 9 | * @param purchaseTransactionState 10 | */ 11 | onUpdateHistory(purchaseTransactionState: InAppPurchaseTransactionState): void 12 | } 13 | 14 | export declare interface InAppPurchaseUpdateNotifier { 15 | notifyOnUpdate(purchaseTransactionState: InAppPurchaseTransactionState): void 16 | notifyOnUpdateHistory(purchaseTransactionState: InAppPurchaseTransactionState): void 17 | } 18 | 19 | export declare class InAppPurchaseManager { 20 | /** 21 | * 22 | * @param purchaseUpdateListener 23 | */ 24 | init(purchaseUpdateListener?: InAppPurchaseStateUpdateListener): Promise 25 | /** 26 | * 27 | * @param productIds 28 | * @param productType 29 | */ 30 | list(productIds: string[], productType?: InAppPurchaseType): Promise 31 | /** 32 | * The intention to buy a product 33 | * @param product Must be an instance provided via Purchage#list(...) 34 | */ 35 | order(product: InAppProduct): Promise 36 | /** 37 | * Every bought product must be confirmed/aknowledged 38 | * @param purchaseTransaction Must cons an instance provided via Purchage#list(...) 39 | * @param consumable Set to true if the product is consumable and can be bought more than once.
40 | * Set to false if the product can be bought only once. 41 | */ 42 | orderConfirm(purchaseTransaction: InAppPurchaseTransactionState, consumable: boolean): Promise 43 | /** 44 | * Query already bought products 45 | */ 46 | purchaseHistory(): Promise 47 | /** 48 | * 49 | */ 50 | canMakePayment(): boolean 51 | /** 52 | * Returns the application's Base64 encoded store receipt for the currently 53 | * logged in iOS App Store user. This is necessary for checking subscription 54 | * status under iOS when hitting the ["verifyReceipt" Web Service Endpoint][1] 55 | * in order to send a receipt to the App Store for verification. 56 | * 57 | * For Android, this function always returns `undefined`. 58 | * 59 | * [1]:https://developer.apple.com/documentation/appstorereceipts/verifyreceipt 60 | */ 61 | getStoreReceipt(): string 62 | /** 63 | * On iOS, requests to refresh the store receipt, which represents the user's 64 | * transactions with your app. It refreshes the store receipt for the 65 | * currently logged in AppStore user. Use this API to request a new receipt if 66 | * the receipt is invalid or missing. On iOS, it returns a completed promise 67 | * once the refresh is complete. 68 | * 69 | * On Android, the promise just always completes. 70 | */ 71 | refreshStoreReceipt(): Promise 72 | /** 73 | * 74 | * @param purchaseStateUpdateListener Get notified every time an update on a purchas occures.
75 | * This listener is mandatory to manually confirm/aknowledge/finish any buy via the method
76 | * InAppPurchaseManager#orderConfirm.
77 | */ 78 | static bootStrapInstance(purchaseStateUpdateListener?: InAppPurchaseStateUpdateListener): Promise 79 | /** 80 | * Shut down any connection to the underlying billing api.
81 | * Calling this method may make this instance unusable. 82 | */ 83 | shutdown() 84 | } 85 | 86 | export declare class InAppProduct { 87 | productId: string 88 | title: string 89 | description?: string 90 | /** 91 | * Returns formatted price of the item, including its currency sign. 92 | */ 93 | price?: string 94 | /** 95 | * Returns price in micro-units, where 1,000,000 micro-units equal one unit of the currency.
96 | * For example, if price is "€7.99", price_amount_micros is "7990000" 97 | */ 98 | priceMicro?: number 99 | /** 100 | * The price currency code ISO-4271 101 | */ 102 | priceCurrencyCode: string 103 | type?: InAppPurchaseType 104 | /** 105 | * Android Only.
106 | * Get the url the the icon for this item. 107 | */ 108 | android_iconUrl?: string 109 | 110 | /** 111 | * 112 | * @param nativeValue The original OS specific product entity 113 | */ 114 | constructor(public nativeValue: any) 115 | } 116 | 117 | export declare class InAppPurchaseTransactionState { 118 | resultCode: InAppPurchaseResultCode 119 | productIdentifier: any 120 | purchaseId: string 121 | purchaseTime: number 122 | /** 123 | * 124 | * @param nativeValue The underlying OS specific purchase transaction.
125 | * May be undefined 126 | */ 127 | constructor(public nativeValue: any) 128 | } 129 | 130 | export declare interface IInAppPurchaseResult { 131 | success: boolean 132 | message?: string 133 | nativeResult?: { 134 | code?: string 135 | codeText?: string 136 | response?: any 137 | } 138 | } 139 | 140 | export declare interface InAppListProductsResult extends IInAppPurchaseResult { 141 | products: InAppProduct[] 142 | } 143 | 144 | export declare interface InAppOrderResult extends IInAppPurchaseResult { 145 | } 146 | 147 | export declare interface InAppOrderConfirmResult extends IInAppPurchaseResult { 148 | } 149 | 150 | export declare interface InAppOrderHistoryResult extends IInAppPurchaseResult { 151 | } 152 | 153 | declare const enum InAppPurchaseType { 154 | InAppPurchase = 'InAppPurchase', 155 | Subscription = 'Subscription' 156 | } 157 | 158 | declare const enum InAppPurchaseResultCode { 159 | /** 160 | * A purchase transaction is ongoing 161 | */ 162 | Purchasing = 'Purchasing', 163 | /** 164 | * Item has been purchased successfully 165 | */ 166 | Purchased = 'Purchased', 167 | /** 168 | * An purchasin transaction has failed 169 | */ 170 | Failed = 'Failed', 171 | /** 172 | * A already bought item has this state when it was queried from the api 173 | */ 174 | Restored = 'Restored', 175 | /** 176 | * User does not have permissions to buy but requested parental approval (iOS only) 177 | */ 178 | Deferred = 'Deferred' 179 | } -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nativescript-in-app-purchase", 3 | "version": "2.1.0", 4 | "description": "NativeScript plugin to handle in app purchases and subscriptions on Android and iOS.", 5 | "main": "in-app-purchase", 6 | "typings": "index.d.ts", 7 | "nativescript": { 8 | "platforms": { 9 | "android": "6.0.0", 10 | "ios": "6.0.1" 11 | } 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/daniele-pecora/nativescript-in-app-purchase.git" 16 | }, 17 | "scripts": { 18 | "tsc": "npm i && tsc", 19 | "build": "npm run tsc && npm run build.native", 20 | "build.native": "node scripts/build-native.js", 21 | "tslint": "cd .. && tslint \"**/*.ts\" --config tslint.json --exclude \"**/node_modules/**\"", 22 | "ci.tslint": "npm i && tslint '**/*.ts' --config '../tslint.json' --exclude '**/node_modules/**' --exclude '**/platforms/**'", 23 | "prepack": "npm run build.native", 24 | "demo.ios": "npm i && cd ../demo && tns run ios", 25 | "demo.ios.debug": "npm i && cd ../demo && tns debug ios", 26 | "demo.android": "npm i && cd ../demo && tns run android", 27 | "demo.android.debug": "npm i && cd ../demo && tns debug android", 28 | "demo.reset": "cd ../demo && npx rimraf -- hooks node_modules platforms package-lock.json", 29 | "plugin.prepare": "npm run build && cd ../demo && tns plugin remove nativescript-in-app-purchase && tns plugin add ../src && cd ../demo && tns plugin remove nativescript-in-app-purchase && tns plugin add ../src", 30 | "clean": "npm run demo.reset && npx rimraf -- node_modules package-lock.json && npm i" 31 | }, 32 | "keywords": [ 33 | "NativeScript", 34 | "JavaScript", 35 | "Android", 36 | "iOS" 37 | ], 38 | "author": { 39 | "name": "Superfusion Mobile", 40 | "email": "superfusion.mobile@googlemail.com" 41 | }, 42 | "bugs": { 43 | "url": "https://github.com/daniele-pecora/nativescript-in-app-purchase/issues" 44 | }, 45 | "license": "Apache-2.0", 46 | "homepage": "https://github.com/daniele-pecora/nativescript-in-app-purchase", 47 | "devDependencies": { 48 | "tns-core-modules": "^6.0.0", 49 | "tns-platform-declarations": "^6.0.0", 50 | "typescript": "~3.4.5", 51 | "prompt": "^1.0.0", 52 | "rimraf": "^2.6.3", 53 | "tslint": "^5.12.1", 54 | "semver": "^5.6.0" 55 | }, 56 | "dependencies": {}, 57 | "bootstrapper": "nativescript-plugin-seed" 58 | } 59 | -------------------------------------------------------------------------------- /src/platforms/android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/platforms/android/README.md: -------------------------------------------------------------------------------- 1 | # Android permissions and dependencies 2 | 3 | * (Optional) Use AndroidManifest.xml to describe any permissions, features or other configuration specifics required or used by your plugin for Android. 4 | NOTE: The NativeScript CLI will not resolve any contradicting or duplicate entries during the merge. After the plugin is installed, you need to manually resolve such issues. 5 | 6 | * (Optional) Use include.gradle configuration to describe any native dependencies. If there are no such, this file can be removed. For more information, see the [include.gradle Specification](http://docs.nativescript.org/plugins/plugins#includegradle-specification) 7 | 8 | 9 | [Read more about nativescript plugins](http://docs.nativescript.org/plugins/plugins) -------------------------------------------------------------------------------- /src/platforms/android/include.gradle: -------------------------------------------------------------------------------- 1 | /* Include.gradle configuration: http://docs.nativescript.org/plugins/plugins#includegradle-specification */ 2 | 3 | android { 4 | 5 | } 6 | 7 | dependencies { 8 | // Describe plugin native Android dependencies like 9 | // implementation "groupName:pluginName:ver" 10 | // EXAMPLE: implementation "com.facebook.fresco:fresco:0.9.0+" 11 | 12 | def billing_version = "4.0.0" 13 | 14 | implementation "com.android.billingclient:billing:$billing_version" 15 | } -------------------------------------------------------------------------------- /src/platforms/android/nativescript_in_app_purchase.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/src/platforms/android/nativescript_in_app_purchase.aar -------------------------------------------------------------------------------- /src/platforms/ios/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/platforms/ios/README.md: -------------------------------------------------------------------------------- 1 | # iOS permissions and dependencies 2 | 3 | 4 | * (Optional) Use Info.plist to describe any permissions, features or other configuration specifics required or used by your plugin for iOS. 5 | NOTE: The NativeScript CLI will not resolve any contradicting or duplicate entries during the merge. After the plugin is installed, you need to manually resolve such issues. 6 | * (Optional) Use build.xcconfig configuration to describe any plugin the native dependencies. If there are no such, this file can be removed. For more information, see the [build.xcconfig Specification section](http://docs.nativescript.org/plugins/plugins#buildxcconfig-specification). 7 | 8 | 9 | [Read more about nativescript plugins](http://docs.nativescript.org/plugins/plugins) -------------------------------------------------------------------------------- /src/platforms/ios/build.xcconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daniele-pecora/nativescript-in-app-purchase/cc9a0ceccd964727648fa3019bf87064547907de/src/platforms/ios/build.xcconfig -------------------------------------------------------------------------------- /src/references.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /src/scripts/build-native.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process'); 2 | const semver = require('semver'); 3 | 4 | exec('tns --version', (err, stdout, stderr) => { 5 | if (err) { 6 | // node couldn't execute the command 7 | console.log(`tns --version err: ${err}`); 8 | return; 9 | } 10 | console.log('******stdout', `${stdout}`) 11 | // In case the current Node.js version is not supported by CLI, a warning in `tns --version` output is shown. 12 | // Sample output: 13 | // 14 | /*Support for Node.js ^8.0.0 is deprecated and will be removed in one of the next releases of NativeScript. Please, upgrade to the latest Node.js LTS version. 15 | 16 | 6.0.0 17 | */ 18 | // Extract the actual version (6.0.0) from it. 19 | 20 | /** 21 | * We must ignore this warning here: 22 | * "You are using the deprecated nsconfig.json file. Just be aware that NativeScript now has an improved nativescript.config.(js|ts) file for when you're ready to upgrade this project. Error while loading nativescript-cloud is: Default commands should be required before child commands" 23 | * and some ANSI styling commands 24 | */ 25 | const versionString = (`${stdout}`.split('\n').filter(item => item.match(new RegExp('^(\\x1B\\[39m)?[0-9].*$', '')))[0]) 26 | .replace('\x1B\[39m', '') 27 | const tnsVersion = semver.major((`${versionString}`.match(/^(?:\d+\.){2}\d+.*?$/m) || [])[0]); 28 | 29 | // execute 'tns plugin build' for {N} version > 4. This command builds .aar in platforms/android folder. 30 | if (tnsVersion >= 4) { 31 | console.log(`executing 'tns plugin build'`); 32 | exec('tns plugin build', (err, stdout, stderr) => { 33 | if (err) { 34 | // node couldn't execute the command 35 | console.log(`${err}`); 36 | return; 37 | } 38 | }); 39 | } 40 | }); 41 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "removeComments": true, 7 | "noLib": false, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "skipLibCheck": true, 11 | "lib": ["es6", "dom"], 12 | "sourceMap": true, 13 | "pretty": true, 14 | "allowUnreachableCode": false, 15 | "allowUnusedLabels": false, 16 | "noEmitHelpers": true, 17 | "noEmitOnError": false, 18 | "noImplicitAny": false, 19 | "noImplicitReturns": true, 20 | "noImplicitUseStrict": false, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "exclude": [ 24 | "node_modules" 25 | ], 26 | "compileOnSave": false 27 | } 28 | -------------------------------------------------------------------------------- /src/typings/billing-android-declarations.d.ts: -------------------------------------------------------------------------------- 1 | declare module androidNative { export class Array { constructor(); length: number; [index: number]: T; } } 2 | 3 | import globalAndroid = android; 4 | 5 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "indent": [ 9 | true, 10 | "spaces" 11 | ], 12 | "no-duplicate-variable": true, 13 | "no-eval": true, 14 | "no-internal-module": true, 15 | "no-trailing-whitespace": true, 16 | "no-var-keyword": true, 17 | "one-line": [ 18 | true, 19 | "check-open-brace", 20 | "check-whitespace" 21 | ], 22 | "quotemark": [ 23 | false, 24 | "double" 25 | ], 26 | "semicolon": [ 27 | true, 28 | "always" 29 | ], 30 | "triple-equals": [ 31 | true, 32 | "allow-null-check" 33 | ], 34 | "typedef-whitespace": [ 35 | true, 36 | { 37 | "call-signature": "nospace", 38 | "index-signature": "nospace", 39 | "parameter": "nospace", 40 | "property-declaration": "nospace", 41 | "variable-declaration": "nospace" 42 | } 43 | ], 44 | "variable-name": [ 45 | true, 46 | "ban-keywords" 47 | ], 48 | "whitespace": [ 49 | true, 50 | "check-branch", 51 | "check-decl", 52 | "check-operator", 53 | "check-separator", 54 | "check-type" 55 | ] 56 | } 57 | } --------------------------------------------------------------------------------