├── www └── InAppUpdateManager.js ├── package.json ├── types └── index.d.ts ├── LICENSE ├── README.md ├── plugin.xml └── src ├── ios └── InAppUpdateManager.swift └── android └── InAppUpdateManager.java /www/InAppUpdateManager.js: -------------------------------------------------------------------------------- 1 | var exec = require('cordova/exec'); 2 | 3 | exports.checkForImmediateUpdate = function( success, error ) { 4 | exec(success, error, "InAppUpdateManager", "immediate"); 5 | }; 6 | 7 | exports.isUpdateAvailable = function( success, error) { 8 | exec(success, error, "InAppUpdateManager", "isUpdateAvailable"); 9 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-inappupdatemanager", 3 | "version": "1.0.0", 4 | "description": "A cordova plugin to support in-app updates in Android and IOS.", 5 | "cordova": { 6 | "id": "cordova-plugin-inappupdatemanager", 7 | "platforms": [] 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/phiamo/cordova-plugin-inappupdatemanager.git" 12 | }, 13 | "keywords": [ 14 | "ecosystem:cordova" 15 | ], 16 | "contributors": [ 17 | "Partha Roy" 18 | ], 19 | "author": "Philipp A. Mohrenweiser", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/phiamo/cordova-plugin-inappupdatemanager/issues" 23 | }, 24 | "homepage": "https://github.com/phiamo/cordova-plugin-inappupdatemanager#readme", 25 | "types": "./types/index.d.ts" 26 | } -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for cordova-plugin-inappupdatemanager v1.0.0 2 | // Project: https://www.npmjs.com/package/cordova-plugin-inappupdatemanager 3 | // Definitions by: Philipp A. Mohrenweiser 4 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped 5 | // 6 | // Licensed under the MIT license. 7 | 8 | interface CordovaPlugins { 9 | /** 10 | * cordova-plugin-inappupdatemanager interface 11 | */ 12 | InAppUpdateManager: InAppUpdateManager.InAppUpdateManager; 13 | } 14 | 15 | /** 16 | * Keep the type global namespace clean by using a module 17 | */ 18 | declare module InAppUpdateManager { 19 | interface InAppUpdateManager { 20 | /** 21 | * @example cordova.plugins.InAppUpdateManager.checkForImmediateUpdate() 22 | */ 23 | checkForImmediateUpdate(success: Function, error: Function): void; 24 | 25 | isUpdateAvailable(success: Function, error: Function): void; 26 | } 27 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Sunbird 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cordova-plugin-inappupdatemanager 2 | A cordova plugin to support in-app updates in Android and IOS. 3 | 4 | ## Supported Platforms 5 | 6 | ### Android 7 | - devices running Android 5.0 (API level 21) or higher, 8 | - Play Core library 1.5.0 or higher 9 | 10 | ### IOS 11 | - since 11.3 12 | 13 | ## Installation 14 | 15 | ``` 16 | cordova plugin add https://github.com/phiamo/cordova-plugin-inappupdatemanager 17 | ``` 18 | 19 | ## How to Use 20 | 21 | Vanilla JS 22 | ``` 23 | //after device is ready 24 | var fail = function (message) { 25 | alert(message) 26 | } 27 | var success = function (data) { 28 | console.log("succes"); 29 | } 30 | cordova.plugins.InAppUpdateManager.checkForImmediateUpdate( success, fail ); 31 | ``` 32 | 33 | Typescript 34 | TODO: @types 35 | 36 | ## Result 37 | 38 | ![screenshot](./screenshot/downloadplugin.gif) 39 | 40 | _**If this plugin helps your project then don't forget to ⭐ star the repo.**_ 41 | 42 | ## Contributing 43 | 44 | 1. Fork it 45 | 2. Create your feature branch (`git checkout -b my-new-feature`) 46 | 3. Commit your changes (`git commit -am 'Add some feature'`) 47 | 4. Push to the branch (`git push origin my-new-feature`) 48 | 5. Create new Pull Request 49 | 50 | 51 | ## Credits 52 | Initial credits go to Partha Roy whose repository and code from 53 | https://github.com/iamparthaonline/cordova-in-app-update-android 54 | was the basis to get this done -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | InAppUpdateManager 5 | A cordova plugin to support in-app updates in Android and IOS. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/ios/InAppUpdateManager.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | enum VersionError: Error { 4 | case invalidBundleInfo, invalidResponse 5 | } 6 | 7 | class LookupResult: Decodable { 8 | var results: [AppInfo] 9 | } 10 | 11 | class AppInfo: Decodable { 12 | var version: String 13 | var trackViewUrl: String 14 | } 15 | 16 | @objc(InAppUpdateManager) class InAppUpdateManager : CDVPlugin { 17 | @objc(immediate:) 18 | func immediate(command: CDVInvokedUrlCommand) { 19 | let pluginResult = CDVPluginResult( 20 | status: CDVCommandStatus_OK 21 | ) 22 | 23 | DispatchQueue.global().async { 24 | self.checkVersion(force: false) 25 | } 26 | 27 | 28 | self.commandDelegate!.send( 29 | pluginResult, 30 | callbackId: command.callbackId 31 | ) 32 | } 33 | 34 | private func checkVersion(force: Bool) { 35 | let info = Bundle.main.infoDictionary 36 | if let currentVersion = info?["CFBundleShortVersionString"] as? String { 37 | _ = getAppInfo { (info, error) in 38 | if let appStoreAppVersion = info?.version{ 39 | if let error = error { 40 | print("error getting app store version: ", error) 41 | } else if appStoreAppVersion == currentVersion { 42 | print("Already on the last app version: ",currentVersion) 43 | } else if appStoreAppVersion.compare(currentVersion, options: .numeric) == .orderedDescending { 44 | print("Needs update: AppStore Version: \(appStoreAppVersion) > Current version: ", currentVersion) 45 | DispatchQueue.main.async { 46 | let topController: UIViewController = UIApplication.shared.keyWindow!.rootViewController! 47 | topController.showAppUpdateAlert(Version: (info?.version)!, Force: force, AppURL: (info?.trackViewUrl)!) 48 | } 49 | } 50 | else { 51 | print("Lower Version Online: \(appStoreAppVersion) ... probabaly we currently develop a new one?", currentVersion) 52 | } 53 | } 54 | } 55 | } 56 | } 57 | 58 | private func getAppInfo(completion: @escaping (AppInfo?, Error?) -> Void) -> URLSessionDataTask? { 59 | guard let identifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String, 60 | let url = URL(string: "https://itunes.apple.com/lookup?bundleId=\(identifier)") else { 61 | DispatchQueue.main.async { 62 | completion(nil, VersionError.invalidBundleInfo) 63 | } 64 | return nil 65 | } 66 | let task = URLSession.shared.dataTask(with: url) { (data, response, error) in 67 | do { 68 | if let error = error { throw error } 69 | guard let data = data else { throw VersionError.invalidResponse } 70 | 71 | let result = try JSONDecoder().decode(LookupResult.self, from: data) 72 | guard let info = result.results.first else { throw VersionError.invalidResponse } 73 | 74 | completion(info, nil) 75 | } catch { 76 | completion(nil, error) 77 | } 78 | } 79 | task.resume() 80 | return task 81 | } 82 | } 83 | 84 | extension UIViewController { 85 | @objc fileprivate func showAppUpdateAlert( Version : String, Force: Bool, AppURL: String) { 86 | let appName = Bundle.appName() 87 | print("Going to URL", AppURL) 88 | 89 | let alertTitle = "New Version" 90 | let alertMessage = "\(appName) Version \(Version) is available on AppStore." 91 | 92 | let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert) 93 | 94 | if !Force { 95 | let notNowButton = UIAlertAction(title: "Not Now", style: .default) 96 | alertController.addAction(notNowButton) 97 | } 98 | 99 | let updateButton = UIAlertAction(title: "Update", style: .default) { (action:UIAlertAction) in 100 | guard let url = URL(string: AppURL) else { 101 | return 102 | } 103 | if #available(iOS 10.0, *) { 104 | UIApplication.shared.open(url, options: [:], completionHandler: nil) 105 | } else { 106 | UIApplication.shared.openURL(url) 107 | } 108 | } 109 | 110 | alertController.addAction(updateButton) 111 | self.present(alertController, animated: true, completion: nil) 112 | } 113 | } 114 | extension Bundle { 115 | static func appName() -> String { 116 | guard let dictionary = Bundle.main.infoDictionary else { 117 | return "" 118 | } 119 | if let version : String = dictionary["CFBundleName"] as? String { 120 | return version 121 | } else { 122 | return "" 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/android/InAppUpdateManager.java: -------------------------------------------------------------------------------- 1 | package InAppUpdateManager; 2 | 3 | import android.content.Context; 4 | import android.content.IntentSender; 5 | 6 | import com.google.android.play.core.appupdate.AppUpdateInfo; 7 | import com.google.android.play.core.appupdate.AppUpdateManager; 8 | import com.google.android.play.core.appupdate.AppUpdateManagerFactory; 9 | import com.google.android.play.core.install.model.AppUpdateType; 10 | import com.google.android.play.core.install.model.UpdateAvailability; 11 | import com.google.android.play.core.tasks.Task; 12 | 13 | import org.apache.cordova.CallbackContext; 14 | import org.apache.cordova.CordovaPlugin; 15 | import org.json.JSONArray; 16 | 17 | /** 18 | * This class echoes a string called from JavaScript. 19 | */ 20 | public class InAppUpdateManager extends CordovaPlugin { 21 | 22 | public static final int REQUEST_CODE = 108108; 23 | protected AppUpdateManager appUpdateManager; 24 | 25 | @Override 26 | public boolean execute(String action, JSONArray args, 27 | CallbackContext callbackContext) { 28 | if (action.equals("immediate")) { 29 | 30 | Context context = cordova.getActivity().getApplicationContext(); 31 | this.startUpdateCheck(context); 32 | return true; 33 | } 34 | 35 | if (action.equals("isUpdateAvailable")) { 36 | Context context = cordova.getActivity().getApplicationContext(); 37 | this.isUpdateAvailable(context, callbackContext); 38 | return true; 39 | } 40 | return false; 41 | } 42 | 43 | private void isUpdateAvailable(Context context, CallbackContext callbackContext) { 44 | appUpdateManager = AppUpdateManagerFactory.create(context); 45 | Task appUpdateInfoTask = appUpdateManager.getAppUpdateInfo(); 46 | 47 | appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> { 48 | if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) { 49 | int availableCodes = appUpdateInfo.availableVersionCode(); 50 | callbackContext.success(availableCodes); 51 | } 52 | }); 53 | } 54 | 55 | private void startUpdateCheck(Context context) { 56 | // Creates instance of the manager. 57 | appUpdateManager = AppUpdateManagerFactory.create(context); 58 | 59 | // Returns an intent object that you use to check for an update. 60 | Task appUpdateInfoTask = appUpdateManager.getAppUpdateInfo(); 61 | 62 | // Checks that the platform will allow the specified type of update. 63 | appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> { 64 | if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE 65 | // For a flexible update, use AppUpdateType.FLEXIBLE 66 | && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) { 67 | // Request the update. 68 | 69 | try { 70 | appUpdateManager.startUpdateFlowForResult( 71 | // Pass the intent that is returned by 'getAppUpdateInfo()'. 72 | appUpdateInfo, 73 | // Or 'AppUpdateType.FLEXIBLE' for flexible updates. 74 | AppUpdateType.IMMEDIATE, 75 | // The current activity making the update request. 76 | cordova.getActivity(), 77 | // Include a request code to later monitor this update request. 78 | REQUEST_CODE); 79 | } catch (IntentSender.SendIntentException e) { 80 | e.printStackTrace(); 81 | } 82 | } 83 | }); 84 | } 85 | 86 | // Checks that the update is not stalled during 'onResume()'. 87 | // However, you should execute this check at all entry points into the app. 88 | @Override 89 | public void onResume(boolean multitaskin) { 90 | super.onResume(multitaskin); 91 | 92 | appUpdateManager 93 | .getAppUpdateInfo() 94 | .addOnSuccessListener( 95 | appUpdateInfo -> { 96 | if (appUpdateInfo.updateAvailability() 97 | == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) { 98 | // If an in-app update is already running, resume the update. 99 | try { 100 | appUpdateManager.startUpdateFlowForResult( 101 | appUpdateInfo, 102 | AppUpdateType.IMMEDIATE, 103 | cordova.getActivity(), 104 | REQUEST_CODE); 105 | } catch (IntentSender.SendIntentException e) { 106 | e.printStackTrace(); 107 | } 108 | } 109 | }); 110 | } 111 | } --------------------------------------------------------------------------------