The body of the

Upgrades are the most basic function of an app, because it is rare for an app to be released without post-maintenance!


Upgrades to native apps are common, but now hybrid apps are popular. For the project, I implemented a simple upgrade based on the Ionic framework, with a forced or non-forced update depending on the server-side return.

Plug-in installation

File (access to file)

ionic cordova plugin add cordova-plugin-file
npm install --save @ionic-native/file

File Transfer(File Transfer)

ionic cordova plugin add cordova-plugin-file-transfer
npm install --save @ionic-native/file-transfer

App Version(used to get the Version number)

ionic cordova plugin add cordova-plugin-app-version
npm install --save @ionic-native/app-version

UID (Obtain device identification, mainly used for gray level upgrade, only upgrade is not used)

ionic cordova plugin add cordova-plugin-uid
npm install --save @ionic-native/uid

File Opener(open the downloaded APK File)

ionic cordova plugin add cordova-plugin-file-opener2
npm install --save @ionic-native/file-opener

Android Permissions Get Runtime Permissions

ionic cordova plugin add cordova-plugin-android-permissions
npm install --save @ionic-native/android-permissions

Upgrade services

Create the nativeService.ts upgrade service in the SRC /app folder.

/** * Created by LLCN on 11-29. */ import {Injectable} from '@angular/core'; import {Platform, AlertController} from 'ionic-angular'; import {AppVersion} from '@ionic-native/app-version'; import {File} from '@ionic-native/file'; import {FileTransfer, FileTransferObject} from "@ionic-native/file-transfer"; import {FileOpener} from '@ionic-native/file-opener'; import {Uid} from "@ionic-native/uid"; import {AndroidPermissions} from "@ionic-native/android-permissions"; import {ToastController} from 'ionic-angular'; import {HttpClient} from "@angular/common/http"; @Injectable() export class NativeService { constructor(private http: HttpClient, private platform: Platform, private alertCtrl: AlertController, private transfer: FileTransfer, private appVersion: AppVersion, private file: File, private fileOpener: FileOpener, private uid: Uid, private toastCtrl: ToastController, private androidPermissions: AndroidPermissions) {} /** * Check if an app needs to be upgraded */ DetectionUpgrade () { (this.getversionNumber ()); // If the versionNumber is not the same as the versionNumber (this.getversionNumber ()); // If the versionNumber is not the same as the versionNumber ()) This.getVersionNumber (). Then ((version) => {this.getimei (). Then ((IMEI) => {// Get the version this.getimei () Body = {tag: 'update', data: {type: "chcnav", terminal: IMEI, version: version}} // const url = 'xxx.xxx.xxx'; Subscribe (res => {if (res as any).status > 0) && ((res as any).subscribe(res as any).subscribe(res as any).> any).data.version ! == version)) { let apkUrl = (res as any).data.path; If (res as any).data.force_update) {this.alertctrl.create ({title: = ""); // If (res as any).data.force_update) {this.alertctrl.create ({title: =" "); 'Upgrade tip ', subTitle:' Found new version, do you want to upgrade now? ', enableBackdropDismiss: false, buttons: [{text: 'settle ', handler: () => { this.storagePermissions().then(res => { if (res) { this.downloadApp(apkUrl); } }) } }] }).present(); } else {this.alertTCTRL. create({title: 'Upgrade alert ', subTitle:' Upgrade alert '); ', enableBackdropDismiss: false, buttons: [{text: 'cancel'}, {text: 'settle ', handler: () => { // this.downloadApp(apkUrl); this.storagePermissions().then(res => { if (res) { this.downloadApp(apkUrl); } }) }  }] }).present(); }}}, the error = > {})})})} / download and install the app * * * * / downloadApp (url: any) {let options; Options = {title: 'Download progress ', subTitle:' Currently downloaded: 0%', enableBackdropDismiss: false } let alert = this.alertCtrl.create(options); alert.present(); const fileTransfer: FileTransferObject = this.transfer.create(); console.log(this.file.externalRootDirectory) const apk = this.file.externalRootDirectory + 'android.apk'; Then (() => {this.FileOpener. Open (apk, fileOpener) {this.FileOpener. 'application/vnd.android.package-archive').then(() => { }).catch(e => { console.log('Error opening file' + e) }); }). Catch (err => {this.toastCtrl.create({message: 'Failed to store apk, please check if you have turned off storage permission! ', duration: 3000, position: 'bottom' }).present(); }); fileTransfer.onProgress((event: ProgressEvent) => { let num = Math.floor(event.loaded / event.total * 100); let title = document.getElementsByClassName('alert-sub-title')[0]; if (num === 100) { // alert.dismiss(); Title && (tit.innerHTML = 'Download complete, install complete '); } else {title && (tit.innerHTML = 'Downloaded:' + num + '%'); }}); } /** * getVersionNumber();} /** * getVersionNumber();} /** * getVersionNumber(); Promise<string> { return new Promise((resolve) => { this.appVersion.getVersionNumber().then((value: string) => { resolve(value); }).catch(err => { console.log('getVersionNumber:' + err); }); }); } / acquire imei number * * * * / async getImei () {const {hasPermission} = await this. AndroidPermissions. CheckPermission ( this.androidPermissions.PERMISSION.READ_PHONE_STATE ); if (! hasPermission) { const result = await this.androidPermissions.requestPermission( this.androidPermissions.PERMISSION.READ_PHONE_STATE ); if (! result.hasPermission) { // throw new Error('Permissions required'); this.platform.exitApp(); Return (); return (); return; } return this.uid. iMei} /** * Storage runtime permission ** APK download request storage permission ** / async StoragePermissions () {const {hasPermission} = await this.androidPermissions.checkPermission( this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE ); if (! hasPermission) { const result = await this.androidPermissions.requestPermission( this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE ); if (! Result.hasPermission) {// throw new Error(' Storage permission denied '); this.platform.exitApp(); App} return true; // return true; } return true; }}

use

Inject the required service in app.module.ts

import {File} from "@ionic-native/file";
import {FileTransfer, FileTransferObject} from '@ionic-native/file-transfer';
import {AppVersion} from '@ionic-native/app-version';
import {AndroidPermissions} from '@ionic-native/android-permissions/';
import {Uid} from '@ionic-native/uid';
import {NativeService} from './NativeService'
import {FileOpener} from "@ionic-native/file-opener";
  providers: [
    FileTransfer,
    File,
    NativeService,
    AppVersion,
    Uid,
    AndroidPermissions,
    FileOpener,
    FileTransferObject,
  ]

use

On the app.com ponent. Ts is used

constructor(private nativeService: NativeService,...) { platform.ready().then(() => { ... this.nativeService.detectionUpgrade(); . }); }

Pay attention to the problem

  1. After Android upgrade, opening APK through FileOpener does not appear to complete the open button

Cause: FileOpenEner2 plug-in issue

Treatment method:

Find the Android source code under Platforms, find the FileOpener Java class, and add the following code:

The directory for commonly: IO. Making. Pwlin. Cordova. Plugins. Fileopener2;

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
private void _open(String fileArg, String contentType, Boolean openWithDefault, CallbackContext callbackContext) throws JSONException { String fileName = ""; try { CordovaResourceApi resourceApi = webView.getResourceApi(); Uri fileUri = resourceApi.remapUri(Uri.parse(fileArg)); fileName = this.stripFileProtocol(fileUri.toString()); } catch (Exception e) { fileName = fileArg; } File file = new File(fileName); if (file.exists()) { try { Uri path = Uri.fromFile(file); Intent intent = new Intent(Intent.ACTION_VIEW); if ((Build.VERSION.SDK_INT >= 23 && ! contentType.equals("application/vnd.android.package-archive")) || ((Build.VERSION.SDK_INT == 24 || Build.VERSION.SDK_INT  == 25) && contentType.equals("application/vnd.android.package-archive"))) { Context context = cordova.getActivity().getApplicationContext(); path = FileProvider.getUriForFile(context, cordova.getActivity().getPackageName() + ".opener.provider", file); intent.setDataAndType(path, contentType); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); / / here / / intent. SetFlags (intent. FLAG_ACTIVITY_CLEAR_TOP); List<ResolveInfo> infoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo resolveInfo : infoList) { String packageName = resolveInfo.activityInfo.packageName; context.grantUriPermission(packageName, path, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); } } else { intent.setDataAndType(path, contentType); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } / / / here @ see * * * * / if at http://stackoverflow.com/questions/14321376/open-an-activity-from-a-cordovaplugin (openWithDefault) { cordova.getActivity().startActivity(intent); } else { cordova.getActivity().startActivity(Intent.createChooser(intent, "Open File in..." )); } callbackContext.success(); } catch (android.content.ActivityNotFoundException e) { JSONObject errorObj = new JSONObject(); errorObj.put("status", PluginResult.Status.ERROR.ordinal()); errorObj.put("message", "Activity not found: " + e.getMessage()); callbackContext.error(errorObj); } } else { JSONObject errorObj = new JSONObject(); errorObj.put("status", PluginResult.Status.ERROR.ordinal()); errorObj.put("message", "File not found"); callbackContext.error(errorObj); }}

If you have to change the platform every time you rebuild it, you can also change it directly inside the plug-in. It was modified when the plug-in was installed.

  1. Android8 cannot automatically open installer (permission denied)

Because of Android8 permissions, APK download can not be automatically opened after the installation program, so the platform targetSDKVersion must be modified.

Modify latformsandroidappsrcmainAndroidManifest. Inside the XML targetSdkVersion value for 23. (so have to add a platform, revised and compiled)

  1. error: resource android:attr/fontVariationSettings resource android:attr/ttcIndex not found.

Solution a: in/platforms/android/build gradle and/platforms/android/app/build. Add the following code gradle.

Configurations. All {resolutionStrategy {force 'com. Android. Support: support - v4:27.1.0'}}

Solution 2: Download (Recommended)

Install the cordova-android-support-gradle-release plugin

ionic cordova plugin add cordova-android-support-gradle-release --fetch