3

I'm working on auto update module in react native. For android version, app should download apk of the new version and installs it. It's possible to download the apk by using react-native-fs, but how can i install the apk? Or being more general, how can i run external files in the react native app?

Meysam Izadmehr
  • 3,103
  • 17
  • 25
  • Have you seen the code-push framework ? It's a tool to do auto updates on react-native apps. If your final goal is to do auto update, maybe this tool can help you. – Gui Herzog Jan 03 '17 at 08:09
  • yes, but i think it's just to update JS bundle. I want to install the new version of the app, so native codes updated too. – Meysam Izadmehr Jan 03 '17 at 10:02

3 Answers3

3

Nope. You can't in generic case (may be this possible on rooted devices).

It is google market (or vendor market) task. It should work out of the box when you push new version of app to market, of course users can opt in auto update.

However you can launch install dialog, see: Install Application programmatically on Android

To download file the application can use react-native-fs module.

To open Intent with explicit data type application/vnd.android.package-archive one can use react-native-mime-intent.

Community
  • 1
  • 1
  • thanks for your answer. i just want to launch install dialog. can you explain more about opening intent by linking? – Meysam Izadmehr Jan 04 '17 at 14:09
  • I have never done this for apk types, but think it should be as simple as: Linking.openUrl('file://path-to-apk.apk').then(...).catch(...) –  Jan 04 '17 at 15:24
  • I test `Linking.openUrl('file://path-to-apk.apk')` but it's not working. It's not open apk with installer, just want to open it. In android, it's possible to send _application/vnd.android.package-archive_ as argument to the intent, how can i do it in react native? – Meysam Izadmehr Jan 07 '17 at 05:42
  • Ok, so you need write some native code, or use already existent third-party package. Fast search suggest something like: https://github.com/veeloinc/react-native-mime-intent –  Jan 07 '17 at 05:49
  • thanks, now i can use _application/vnd.android.package-archive_. Right now i'm getting error : _There was a problem parsing package_, do you have any idea? – Meysam Izadmehr Jan 07 '17 at 08:08
  • No, see adb logcat for additional info... It is very android specific. Can you install downloaded package by hand from phone (perhaps from file manager)? May be permissions problem? –  Jan 07 '17 at 08:18
  • @MeysamIzadmehr did you solved the issue? Were you able to launch the installer? – user567 Nov 16 '17 at 10:05
3

I know some time has passed, but @vovkasm answer is outdated for a long time. You can achieve what you want with rn-fetch-blob Thats what I did in my app - download .apk from url and than auto-run installation once the download is completed, that updates the app for new version. It provides both: downloading and opening the file. Keep in mind that for android 8+ this requires additional signed permission for REQUEST_INSTALL_PACKAGES in manifest, but except that all works properly.

Simple example of opening the .apk with action view intent once its downloaded:

RNFetchBlob.android.actionViewIntent(
      resource.path(), //path where the file is downloaded
      "application/vnd.android.package-archive",
    )

Also as @vovkasm wrote, it is still possible to download files using react-native-fs, not sure about reading .apk tho.

What I have noticed is that react-native-fs handles no connection errors and download progress, which doesn't work on rn-fetch-blob, but on the other hand rn-fetch-blob supports download manager while react-native-fs doesnt... You can always mix both depending on what you need :)

Community
  • 1
  • 1
M. Wojcik
  • 2,301
  • 3
  • 23
  • 31
-2

Yes, you can do it. https://github.com/mikehardy/react-native-update-apk

1-Create Updater.js root directory and edit fileProviderAuthority as your package name + .provider

        import {Alert} from 'react-native';
        import * as UpdateAPK from 'rn-update-apk';
        
        export default (onStart, onComp) => {
          const update = new UpdateAPK.UpdateAPK({
            iosAppId: '1104809018',
            apkVersionUrl: 'https://example.com/apk/test-version.json',
//Must have test version json file and apk file to the remote server. You can find the contents of the Json file in the repo on the link and examine it.
            fileProviderAuthority: 'com.lorien.provider',
        
            needUpdateApp: (needUpdate) => {
              needUpdate(true);
        
            },
        
            forceUpdateApp: () => {
              console.log('forceUpdateApp callback called');
            },
        
            notNeedUpdateApp: () => {
              console.log('notNeedUpdateApp callback called');
            },
        
            downloadApkStart: () => {
              console.log('downloadApkStart callback called');
            },
        
            downloadApkProgress: (progress) => {
              console.log(`downloadApkProgress callback called - ${progress}%...`);
              onStart(progress);
            },
        
            downloadApkEnd: () => {
              console.log('downloadApkEnd callback called');
              onComp();
            },
            onError: (err) => {
              console.log('onError callback called', err);
              Alert.alert('There was an error', err.message);
            },
          });
        
              update.checkUpdate();
            };
    

2- Add libraries and add files

    npm install rn-update-apk 
(You may need to replace it if you get an error. Find the library in react_modules and change this line as import change tandroidx.core.content.FileProvider)
    npm install react-native-fs
    npm install react-native-exit-app
    npm install react-native-simple-dialogs
    
    Add files
    
    go android>app>src>main>res
    create a folder named xml
    
    and create a js file named filepaths.xml in xml folder like this:
    
        <?xml version="1.0" encoding="utf-8"?>
        <paths xmlns:android="http://schemas.android.com/apk/res/android">
          <cache-path name="cache" path="/" /> 
        </paths>
    

3- Edit Android Manifest like this: (focus the xmlns line, permissions and provider tag)

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.lorien"
      xmlns:tools="http://schemas.android.com/tools">
    
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
    
        <application
          android:name=".MainApplication"
          android:label="@string/app_name"
          android:icon="@mipmap/ic_launcher"
          android:roundIcon="@mipmap/ic_launcher_round"
          android:allowBackup="false"
          android:usesCleartextTraffic="true"
          android:theme="@style/AppTheme">
          <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
          </activity>
          <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
          <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <!-- you might need the tools:replace thing to workaround rn-fetch-blob or other definitions of provider -->
            <!-- just make sure if you "replace" here that you include all the paths you are replacing *plus* the cache path we use -->
            <meta-data tools:replace="android:resource"
              android:name="android.support.FILE_PROVIDER_PATHS"
              android:resource="@xml/filepaths" />
          </provider>
        </application>
    
    </manifest>
    

4- Using and call the UPDATER in main js:

        import { Dialog } from 'react-native-simple-dialogs';
        import RNExitApp from 'react-native-exit-app';
        import Updater from '../Updater';
        import { ActivityIndicator, Alert } from 'react-native';
          constructor(props) {
            super(props);
            this.state = {
              progressVisibility: false,
              downloadVisibility: false,
              downloadBar: "",
            }
          }
          componentDidMount() {
            console.disableYellowBox = true;
            const self = this;
        
            function onStart(progress) {
              self.setState({downloadVisibility: true})
              self.setState({downloadBar: progress})
            }
        
            function onComp() {
              self.setState({downloadVisibility: false})
              Alert.alert('Dikkat!','Gelen güncellemeyi yüklemeden işlem yapamazsınız. Yükleme işlemini başlatmadıysanız, uygulamayı kapatıp açarak tekrar deneyiniz.',[{ text: "Kapat", onPress: () => RNExitApp.exitApp() }])
            }
            Updater(onStart, onComp);
          }
sakiM
  • 4,832
  • 5
  • 27
  • 33