How to install a downloaded .apk with React Native Expo? - android

I need to install downloaded .apk file from within the Expo app (it's for update functionality). This is my code:
import React from "react";
import { Button, View } from "react-native";
import * as FileSystem from "expo-file-system";
import { startActivityAsync } from "expo-intent-launcher";
export function Updater() {
async function updateApk() {
const uri = "https://expo.dev/artifacts/eas/my_apk_name.apk";
const localUri = FileSystem.documentDirectory + "test.apk";
try {
await FileSystem.downloadAsync(uri, localUri);
await startActivityAsync("android.intent.action.INSTALL_PACKAGE", {
data: localUri,
flags: 1,
});
} catch (error) {
alert(`Error during installing APK: ${error}`);
}
}
return (
<View>
<Button title="Reset APK" onPress={updateApk} />
</View>
);
}
It downloads the file, stores it, but then there is an error during startActivityAsync:
Encountered an exception while calling native method:
Exception occurred while executing exported method startActivity on module ExpoIntentLauncher:
file://data/user/0/com.my.app.id/files/test.apk exposed beyond app through Intent.getData()
I tried passing uri first to FileSystem.getContentUriAsync() but then there is no error, the intent result is 0 but nothing happens.
My permissions in app.json:
"permissions": [
"READ_EXTERNAL_STORAGE",
"WRITE_EXTERNAL_STORAGE",
"CAMERA"
]
Do I need any additional permissions to get it to work? Or is it completely impossible with Expo? Maybe I should save the file to different location to be able to use this intent?
I also tried android.intent.action.VIEW with no luck.
I test it on Android 13, on physical device. App is built with EAS.

Maybe you can use this command to build release build.
expo:build android
For that you have to signup in Expo's website.
After that you can get apk in Expo's server.

I finally got it to work. The funny part is that I got the answer from the AI that is now banned here. But I just tested this solution on a real android device and it works. Anyway there are two changes needed:
REQUEST_INSTALL_PACKAGES must be added to app.json file.
Uri for intent must be a content uri so: localUri = await FileSystem.getContentUriAsync(localUri)

Related

How to add values to app.json for android in expo managed workflow?

I need to add those to Android files:
android:usesCleartextTraffic="true" and <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
But I'm using managed workflow and I don't know how to add those lines to app.json file.
I did this plugin which seems to work:
const { createRunOncePlugin, withAndroidManifest } = require('#expo/config-plugins');
const withAndroidManifestHavingBetterSecuritySettings = config => {
return withAndroidManifest(config, config => {
const androidManifest = config.modResults.manifest;
const mainApplication = androidManifest.application[0];
if(process.env.CHANNEL !== 'dev') {
androidManifest.$ = {
...androidManifest.$,
'xmlns:tools': 'http://schemas.android.com/tools',
};
mainApplication.$['tools:replace'] = 'android:usesCleartextTraffic';
mainApplication.$['android:usesCleartextTraffic'] = 'false';
}
return config;
});
};
module.exports = createRunOncePlugin(
withAndroidManifestHavingBetterSecuritySettings,
'withAndroidManifestHavingBetterSecuritySettings',
'1.0.0'
);
I had many issues related to merging of AndroidManifest files when "developmentClient": true in my eas.json file (related to me dev eas profile). I believe that it's related to the fact that the debug/AndroidManifest is a higher priority manifest than main/AndroidManifest (not sure though). So my solution was not to ignore the changes when building the dev profile. Hardening security settings in development builds do not seem useful anyhow.
So I struggled with this problem for a while now and the only solution I could come up with was setting the minimum sdk version of the android app from 21 to 28. This is not ideal as my application now does not support old android devices, but doing this defaults the usesClearTextTraffic flag to false.
If your app works fine while developing in expo, but after generating the APK some functions don't work, try this. In my case the APK crashed on login, but building in development with expo was working fine. The problem was that traffic is encrypted so that's why I ended up here trying to set clear text traffic. The problem in my case was with expoPushToken, in the APK it throws an exception I wasn't catching (building with expo worked fine as I said before, no exception). So, if the exception happens just catch it and set the token to empty string.
So, I had this:
import * as Notifications from "expo-notifications";
export async function getDevicePushTokenForAPP() {
const pushToken = await Notifications.getExpoPushTokenAsync();
return pushToken.data;
}
So then, I added the try and catch:
export async function getDevicePushTokenForAPP() {
try {
const pushToken = await Notifications.getExpoPushTokenAsync();
return pushToken.data;
} catch (e) {
return "";
}
}
Now if you build the APK again (expo build:android) it should work fine, in my case login worked. But please note this is for testing purposes only, I needed the APK to quickly show it to the client. (Note that you will need the bundle, not the apk, when uploading to the Playstore). This is a quick fix for you to test the APK; but with no token, push notifications won't work. The final fix is to add firebase to your project, it's mandatory now, so add firebase and with the firebase unique ID, your push notification will work in your APK.
My conclusion is that expo uses its own ID to communicate with firebase, that's why it works while developing but the APK doesn't go through expo and tries to connect to firebase directly, but crashes because there's no ID.
You should update your app.json like that:
"android": {
"usesCleartextTraffic": true,
uses-permission android:name
},

How to open/install another APK file from inside React Native App built using expo

I have an android application built using expo (Managed Flow). I am trying to install/open a downloaded apk file from inside the application without success.
const fileUri = ...
FileSystem.getContentUriAsync(fileUri).then(cUri => {
console.log(cUri);
IntentLauncher.startActivityAsync('android.intent.action.VIEW', {
data: cUri,
flags: 1,
type: 'application/vnd.android.package-archive'
});
});
I also tried:
const fileUri = ...
Linking.openURL(fileUri);
I need help. Thanks.

How can I generate an AndroidManifest.xml from an existing react-native project?

I already have a react-native app created and I wanna access the user's geolocation. Well, I don't have any Android's folder and I need to add some permissions in AndroidManifest.xml. How can I tie this into my existing project?
Obs: I am using expo and npm.
AndroidManifest.xml not possible with an Expo project. In order to add this permission, you would have to use the Bare workflow
If not you can eject your project from expo or create a new project with react-native-cli and move your source code to it.
Hope this helps you. Feel free for doubts.
Expo has evolved quite a bit in recent years and you can now (Expo SDK 41+) use config plugins to change your AndroidManifest.xml and still remain in the managed workflow. You will lose the ability to use the Expo Go App, but you can use EAS builds to build a custom dev client, which is basically a version of Expo Go tailored to your project.
The docs provide an example of how to use the withAndroidManifest mod to modify permissions. Note that you can run expo prebuild --no-install to test the effects of your plugin, but be sure to delete the generated native folders before running the EAS build, otherwise EAS will assume bare workflow.
The below codes worked for me!
state = {
isLoading:false,
location:null,
errorMessage:null,
isDevice:"",
}
async componentDidMount() {
if (Platform.OS === 'android' && !Constants.isDevice) {
this.setState({
errorMessage:"oops, this will not work on sketch in an android emulator.Try it on your device!"
});
} else {
this._getLocationAsync();
}
}
_getLocationAsync = async () => {
let { status} = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
this.setState({
setError:'Permission to access location was denied'
});
}
let location = await Location.getCurrentPositionAsync({});
this.setState({
location
})
console.log(location)
}
It is possible to edit AndroidManifest.xml with apktool, followed by using apksigner to re-sign the apk with credentials from expo credentials:manager.
To edit the existing manifest:
apktool decode myapp-original.apk
vim myapp-original/AndroidManifest.xml
To re-build a new signed apk:
apktool build myapp-original -o myapp.apk
expo credentials:manager -p android
/path/toAndroid/Sdk/build-tools/$VERSION/apksigner sign -ks #username__myapp.bak.jks myapp.apk

How can I open apk file I just download from react-native Webview?

I am trying to use react-native-webview to download Android apk file. It could download apk file actually!
But How can I know when file is download success and get file path or automatically install the apk file I just download?
is there any good ideal to make it happen? very thanks
Once downloaded to your device path, you can try this with two modules.
You must have two modules installed and available. react-native-file-viewer and react-native-document-picker
Example
import FileViewer from 'react-native-file-viewer';
import { DocumentPicker, DocumentPickerUtil } from 'react-native-document-picker';
DocumentPicker.show({
filetype: [DocumentPickerUtil.allFiles()],
}, (error, res) => {
if(res) {
FileViewer.open(res.uri)
.then(() => {
// success
})
.catch(_err => {
// error
});
}
});

Error while trying to access files in the app

I'm building a react-native app that uses tensorflow to recognize images, I'm following the steps in this tutorial.
I did everything according to the explanations, including the part of "Fetching files". I created the assets folder and put the files in it (the path is correct).
But when I run this code:
const tfImageRecognition = new TfImageRecognition({
model: require('./assets/tensorflow_inception_graph.pb'),
labels: require('./assets/tensorflow_labels.txt'),
});
The app gives the following error:
I already tried to create a new project, I imported the react-native-tensorflow import { TfImageRecognition } from 'react-native-tensorflow';, I updated the cache, I deleted the folder node_modules and also I created the file "rn-cli.config.js" that is requested in the tutorial to give access to the files in the assets folder. Any idea how to fix this?
I'm using expo to run the app on mobile (android).
npm: 5.51
expo: 51.4.0
react-native: 0.54.0
react-native-cli: 2.0.1
This problem didn't occur with me. Try react-native start --reset-cache and then run the app again.
There is a better way to this.
import model from './assets/tensorflow_inception_graph.pb';
import labels from './assets/tensorflow_labels.txt';
const tfImageRecognition = new TfImageRecognition({
model,
labels
});
Restart your server.
I tried the same example as you mentioned I got accessed Image and Text.
I stored files inside assets in the same directory. Can you share code to produce an error that you faced?
async recognizeImage() {
try {
const tfImageRecognition = new TfImageRecognition({
model:require('./assets/tensorflow_inception_graph.pb'),
labels: require('./assets/tensorflow_labels.txt')
})
const results = await tfImageRecognition.recognize({
image: this.image
})
const resultText = `Name: ${results[0].name} - Confidence: ${results[0].confidence}`
this.setState({result: resultText})
await tfImageRecognition.close()
} catch(err) {
alert(err)
}
}
As you mentioned your using expo then I'm assuming that run npm eject already. As react-native-tensorflow this library require native changes
You must add extensions in your rn-cli.config.js, in order to require tensorflow_inception_graph.pb and tensorflow_labels.txt
module.exports = {
getAssetExts() {
return ['pb', 'txt']
}
}
Replace ./ with so ../ , so final code will be -
model: require('../assets/tensorflow_inception_graph.pb'),
labels: require('../assets/tensorflow_labels.txt')

Categories

Resources