chronogolf / nativescript-store-update

Apache License 2.0
19 stars 13 forks source link

this is unexpected #30

Open gyansoft786 opened 4 years ago

gyansoft786 commented 4 years ago

you only parsing html to get version name. But in real version code should be compared. You should use native api provided by google. read more https://developer.android.com/guide/playcore/in-app-updates

jeremypele commented 4 years ago

@gyansoft786 You have every right to suggest improvements but you have also every right to remain polite. The solution was the most effective one more than 2 years ago, we do not use this plugin anymore though do not maintain it.

No matter if the solution pleases you or not, you should keep in mind that this code is provided to you for free and that some people spent personal time to build it. Feel free to fork it and improve the code yourself

gyansoft786 commented 4 years ago

@jeremypele i apologize for my words. I really did't mean that. However i'll contribute to this repo in near future. Happy coding ;-)

abhayastudios commented 4 years ago

@gyansoft786 I agree it is the proper way to handle this, but the downside of using the Play Core library is that it only works for builds signed by Google (or at least it needs to be the same signature as available in the play store), i.e. not easy to test locally.

If it helps anyone, for now I implemented it as follows for my purposes. Maybe at some point we can properly integrate it in this plugin:

/*
   Check App Stores for new version and prompt user to update if newer version found

   Android Notes
   *************

   Play Core Libraries are needed with minimum SDK 21. Add below to app.gradle file.gradle:

     android { defaultConfig { minSdkVersion 21 } }
     dependencies { implementation 'com.google.android.play:core:1.7.2' }

   This will fail if not signed by Google app signing: https://stackoverflow.com/a/61270305/918269
   It can be tested with Internal App Sharing: https://stackoverflow.com/a/59266778/918269
*/
import { Injectable } from '@angular/core';
import { ad } from '@nativescript/core/utils/utils';
import { android } from '@nativescript/core//application';
import { isAndroid, isIOS } from '@nativescript/core/platform';
import * as appversion from "nativescript-appversion";

declare const com:any;

@Injectable()
export class AppStoreUpdateProvider {
  private androidAppUpdateManager;
  private androidAppUpdateType;
  private androidContext;
  private androidUpdateAvailability;
  private appVersionCode; // version code of running app

  constructor() {
    this.appVersionCode = appversion.getVersionCodeSync();

    if (isAndroid) {
      // see enum: https://developer.android.com/reference/com/google/android/play/core/install/model/UpdateAvailability
      this.androidUpdateAvailability = com.google.android.play.core.install.model.UpdateAvailability;        
      // see enum: https://developer.android.com/reference/com/google/android/play/core/install/model/AppUpdateType
      this.androidAppUpdateType = com.google.android.play.core.install.model.AppUpdateType;
      this.androidContext = ad.getApplicationContext();
      this.androidAppUpdateManager = com.google.android.play.core.appupdate.AppUpdateManagerFactory.create(this.androidContext);
    }
  }

  public checkStoreUpdate() : Promise<any> {
    if (isAndroid) { return this.checkAndroidUpdate(); }
    if (isIOS) { return this.checkIOSUpdate(); }
  }

  public doStoreUpdate() {
    if (isAndroid) { return this.doAndroidUpdate(); }
    if (isIOS) { return this.doIOSUpdate(); }
  }

  private checkIOSUpdate() {
    return new Promise((resolve, reject) => {
      console.log('AppStoreUpdateProvider.checkIOSUpdate: not yet implemented!')
      resolve()
    });
  }

  private doIOSUpdate() {
    console.log('AppStoreUpdateProvider.doIOSUpdate: not yet implemented!')
  }

  private checkAndroidUpdate() {
    return new Promise((resolve, reject) => {
      try {
        const appUpdateInfoTask = this.androidAppUpdateManager.getAppUpdateInfo();
        const onSuccessListener = new com.google.android.play.core.tasks.OnSuccessListener({
          onSuccess: (AppUpdateInfo: any) => {
            resolve({
              availableVersionCode:       AppUpdateInfo.availableVersionCode(),
              bytesDownloaded:            AppUpdateInfo.bytesDownloaded(),
              clientVersionStalenessDays: AppUpdateInfo.clientVersionStalenessDays(),
              installStatus:              AppUpdateInfo.installStatus(),
              packageName:                AppUpdateInfo.packageName(),
              totalBytesToDownload:       AppUpdateInfo.totalBytesToDownload(),
              updateAvailability:         AppUpdateInfo.updateAvailability(),
              updateAvailable:            AppUpdateInfo.updateAvailability()===this.androidUpdateAvailability.UPDATE_AVAILABLE,
              updateFlexibleAllowed:      AppUpdateInfo.isUpdateTypeAllowed(this.androidAppUpdateType.FLEXIBLE),
              updateImmediateAllowed:     AppUpdateInfo.isUpdateTypeAllowed(this.androidAppUpdateType.IMMEDIATE),
              updateInProgress:           AppUpdateInfo.updateAvailability()===this.androidUpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS,
              updatePriority:             AppUpdateInfo.updatePriority(),
            })
          }
        });

        const onFailureListener = new com.google.android.play.core.tasks.OnFailureListener({
          onFailure: (AppUpdateInfo: any) => {
            reject({
              code: AppUpdateInfo.getErrorCode(),
              message: AppUpdateInfo.getMessage(),
            });
          }
        });

        appUpdateInfoTask.addOnSuccessListener(onSuccessListener);
        appUpdateInfoTask.addOnFailureListener(onFailureListener);
      } catch (error) {
        reject(`checkAndroidUpdate: ${error}`);
      }
    });
  }

  public doAndroidUpdate() {
    const REQUEST_CODE = 32000; // random code (positive signed 16 bit) for monitoring update status (for FLEXIBLE)

    try {
        const appUpdateInfoTask = this.androidAppUpdateManager.getAppUpdateInfo();
        const onSuccessListener = new com.google.android.play.core.tasks.OnSuccessListener({
          onSuccess: (AppUpdateInfo: any) => {
            if (AppUpdateInfo.updateAvailability()===this.androidUpdateAvailability.UPDATE_AVAILABLE) {
              this.androidAppUpdateManager.startUpdateFlowForResult(
                AppUpdateInfo,
                this.androidAppUpdateType.IMMEDIATE, // or FLEXIBLE
                android.foregroundActivity,          // current activity making the update request
                REQUEST_CODE,
              );
            }
          }
        });

        const onFailureListener = new com.google.android.play.core.tasks.OnFailureListener({
          onFailure: (AppUpdateInfo: any) => {
            console.log('doAndroidUpdate: onFailure');
          }
        });

        appUpdateInfoTask.addOnSuccessListener(onSuccessListener);
        appUpdateInfoTask.addOnFailureListener(onFailureListener);
    } catch (error) {
      console.log(`doAndroidUpdate: ${error}`);
    }
  }
}

Then call it from my main.ts :

private checkStoreUpdate() {
    this.appStore.checkStoreUpdate()
    .then((result) => {
      if (result.updateAvailable) {
        console.log(`checkStoreUpdate: version ${result.availableVersionCode} is available!`)
        this.dialog.show(options)
        .then((doStoreUpdate:boolean) => {
          if (doStoreUpdate) { this.appStore.doStoreUpdate(); }
        });
      }
    })
    .catch((error) => {
      if (isAndroid && error.code===-3) { console.log('checkStoreUpdate: cannot check on build not signed by Google!'); }
      else { console.log(`checkStoreUpdate: ${error.message}`); }
    });