How to install .xapk file from local storage programmatically in Flutter? - java

I want to implement this functionality;
A Button when pressed will install .xapk file from local storage with the following path.
String _apkFilePath = '/storage/emulated/0/Download/filename.xapk';

Duplicate of: I am making an .XAPK installer application in flutter. with open_file package I can open normal .apk installation dialogue but how can I install .XAPK
If you're still trying to install an .xapk file, I'm sharing a piece of code that helped me. I'm using the packages:
archive (for all the extraction as zip logic)
device_apps (to open the Settings app in case you don't have the required permissions)
open_filex (to open the apk file with the Android intent)
package_archive_info (to get the information from the .apk package)
path_provider (to get Directories and paths)
permission_handler (to ask for permissions to install)
and file_picker since I initiate the method with a picked file using that package.
abstract class XapkInstaller {
static install({required PlatformFile file}) async {
late List<FileSystemEntity> allFiles, apkFiles;
late PackageArchiveInfo appInfo;
late String appPackageName;
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
String appName = file.path.toString().split("/").last.replaceAll(".apklis", "");
String zipFilePath = "${tempDir.path.replaceAll('/$appName.apklis', '')}/$appName.zip";
// this function convert xapk in zip file and moves in appname_zip directory
_moveFile(File(file.path.toString()), zipFilePath);
final bytes = File(zipFilePath).readAsBytesSync();
final archive = ZipDecoder().decodeBytes(bytes);
// Extract the contents of the Zip archive to disk app cache.
for (final file in archive) {
final String filename = file.name;
if (file.isFile) {
final data = file.content as List<int>;
File("${tempDir.path}/$appName/$filename")
..createSync(recursive: true)
..writeAsBytesSync(data);
} else {
Directory(tempPath).create(recursive: true);
}
}
final Directory myDir = Directory("${tempDir.path}/$appName");
allFiles = myDir.listSync(recursive: true, followLinks: true);
apkFiles = allFiles.where((element) => element.path.endsWith('.apk')).toList();
for (int x = 0; x < apkFiles.length; x++) {
final String filePath = apkFiles[x].path;
try {
appInfo = await PackageArchiveInfo.fromPath(filePath);
appPackageName = appInfo.packageName;
} catch (e) {
appInfo = PackageArchiveInfo(appName: "", packageName: "", version: "", buildNumber: "");
}
if (appInfo.appName.isNotEmpty) {
try {
// moving obb file to android/obb folder
_moveObbToAndroidDir(allFiles, appPackageName);
// showing popup to install app
if (await Permission.requestInstallPackages.request().isGranted) {
await OpenFilex.open(filePath);
} else {
DeviceApps.openAppSettings(appInfo.packageName);
}
} catch (e) {
//catch error in installing
}
}
}
// clearing cache file after installing xapk
Future.delayed(const Duration(seconds: 180), () {
tempDir.deleteSync(recursive: true);
tempDir.create();
});
}
static _moveObbToAndroidDir(List<FileSystemEntity> allFiles, String appPackageName) async {
for (int x = 0; x < allFiles.length; x++) {
final fileExtension = allFiles[x].path.split("/").last.split(".").last;
if (fileExtension == "obb") {
String filepath = allFiles[x].path;
String obbFileName = filepath.split("/").last.split(".").first;
String obbDirPath = "/Android/obb/$appPackageName";
// creating the directory inside android/obb folder to place obb files
if (!Directory(obbDirPath).existsSync()) {
Directory(obbDirPath).createSync();
}
// rename path should also contains filename i.e. whole path with filename and extension
final String renamePath = "$obbDirPath/$obbFileName.obb";
try {
// syncronus copying
File(filepath).copySync(renamePath);
} on FileSystemException {
// in case of exception copying asyncronushly
await File(filepath).copy(renamePath);
}
}
}
}
static Future<File> _moveFile(File sourceFile, String newPath) async {
try {
// prefer using rename as it is probably faster
return await sourceFile.rename(newPath);
} on FileSystemException catch (e) {
// if rename fails, copy the source file and then delete it
final newFile = await sourceFile.copy(newPath);
await sourceFile.delete();
return newFile;
}
}
}
I've tried it and it works, so remember to update the permissions on the AndroidManifest file and you're all set.

Related

.git/objects/pack directory is not empty while deleting directory created by Jgit

I have cloned a directory in my container using Jgit code for reference
public UpdateProjectDto pushIntoGit(String id, String Name, GitDTO gitDTO, File archiveFolder, boolean upgradeProject) {
log.info("Pushing {} in git repo {} ", Name, gitDTO.getRepoUrl());
final GitConfiguration gitConfiguration = findConfigurationByUrl(gitDTO.getRepoUrl());
String latestCommitId;
File Dir = null;
File destination = null;
UpdateProjectDto updateProjectDto = new UpdateProjectDto();
final UsernamePasswordCredentialsProvider usernamePasswordCredentialsProvider = new UsernamePasswordCredentialsProvider(gitConfiguration.getUserName(), gitConfiguration.getPassword());
final CloneCommand cloneCommand = Git.cloneRepository();
cloneCommand.setURI(gitDTO.getRepoUrl());
cloneCommand.setCredentialsProvider(usernamePasswordCredentialsProvider);
try {
final Git gitRepo = cloneCommand.call();
final String repoName = gitUtils.getRepoName(gitDTO.getRepoUrl());
if (gitDTO.getLastCommitId().equals(getLastCommitId(gitRepo))) {
removeGitFolder(archiveFolder);
Dir = new File(String.format("temp/%s", Name));
Dir.mkdir();
FileUtils.copyDirectory(archiveFolder, Dir);
FileUtils.deleteDirectory(archiveFolder);
destination = new File(gitRepo.getRepository().getDirectory().getParent());
FileUtils.copyDirectory(Dir.getParentFile(), destination);
FileUtils.deleteDirectory(Dir);
gitRepo.add().addFilepattern(".").call();
gitRepo.commit().setMessage(String.format(" %s pushed into git", Name)).call();
final PushCommand push = gitRepo.push();
push.setRemote(Constants.ORIGIN);
push.add(Constants.MASTER);
push.setCredentialsProvider(usernamePasswordCredentialsProvider);
push.call();
log.info("Git Push successful for {} in git repo {} ", Name, gitDTO.getRepoUrl());
latestCommitId = getLastCommitId(gitRepo);
if (!upgradeProject) {
updateProjectDto = workspacesServiceClient.addLatestCommitIdToProject(gitDTO.getProjectId(), latestCommitId, Collections.singletonList(id), ProjectEventType.CREATED);
} else {
updateProjectDto.setCommitId(latestCommitId);
updateProjectDto.setUserName(gitDTO.getUsername());
}
log.info(String.format("UpdateProjectDto: %s, repository name: %s", updateProjectDto.toString(), repoName));
final File projectDirectoryForUser = gitUtils.getProjectDirectoryFromRepoName(updateProjectDto.getUserName(), repoName);
if (!projectDirectoryForUser.exists()) {
FileUtils.deleteDirectory(new File(repoName));
throw new internalServerError(String.format(ExceptionMessage.GIT_DIRECTORY_NOT_FOUND, repoName));
}
updateGitFolder(projectDirectoryForUser, gitConfiguration);
FileUtils.deleteDirectory(destination);
gitRepo.getRepository().close();
gitRepo.close();
} else {
FileUtils.deleteDirectory(new File(repoName));
throw new internalServerError("Perform git pull before adding any");
}
} catch (GitAPIException | IOException e) {
log.error("Failed to push into git due to {}", ExceptionUtils.getStackTrace(e));
throw new internalServerError(String.format(ExceptionMessage.FAILED_TO_PUSH_INTO_GIT,));
} finally {
FileUtils.deleteQuietly(archiveFolder);
FileUtils.deleteQuietly(xxx);
FileUtils.deleteQuietly(destination);
}
return updateProjectDto;
}
I was using try with resources in clone command and expecting gitRepo object to auto close , but after seeing other posts I understood that we need to close repository also , so I did those changes as well. but still I see my directory is not able to delete.
When I try to delete directory using rm-rf command
tt-798f4978f4-f5pwb:/workspace/pqr/projects/username/workbench$ lsof +D lsof78
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 7 xyz 61r REG 0,44 44988 9572259 lsof78/.git/objects/pack/pack-d4f595d6d76d88356465eac2d60c7ff849255f67.pack
java 7 xyz 65r REG 0,44 226 9572244 lsof78/.git/objects/pack/pack-9c6ee15199d5fb8e65cad42f87428dacd5249d43.pack
This is the error
rm: cannot remove 'lsof78/.git/objects/pack': Directory not empty

Rename files on SMB using smbj fails

I'm strugging to rename files with content to SMB using com.hierynomus.smbj.
Files are generated with content, but after renaming content is gone.
Following implementation renames files:
public void rename(String pathFrom, String pathTo) {
pathFrom = formatPath(pathFrom);
pathTo = formatPath(pathTo);
Set<SMB2ShareAccess> shareAccessSet = new HashSet<>();
shareAccessSet.add(SMB2ShareAccess.FILE_SHARE_READ);
shareAccessSet.add(SMB2ShareAccess.FILE_SHARE_WRITE);
shareAccessSet.add(SMB2ShareAccess.FILE_SHARE_DELETE);
Set<FileAttributes> fileAttributes = new HashSet<>();
fileAttributes.add(FileAttributes.FILE_ATTRIBUTE_NORMAL);
Set<SMB2CreateOptions> createOptions = new HashSet<>();
SMB2CreateDisposition smb2CreateDisposition = SMB2CreateDisposition.FILE_OVERWRITE_IF;
if (isFolder(pathFrom)) {
createOptions.add(SMB2CreateOptions.FILE_DIRECTORY_FILE);
smb2CreateDisposition = SMB2CreateDisposition.FILE_OPEN_IF;
}
else if (isFile(pathFrom)) {
createOptions.add(SMB2CreateOptions.FILE_NON_DIRECTORY_FILE);
}
else {
throw new IllegalArgumentException("Path '" + pathFrom + "' can't be resolved to file nor directory");
}
try (DiskEntry file = this.smbShare.open(pathFrom, of(AccessMask.MAXIMUM_ALLOWED), fileAttributes, shareAccessSet,
smb2CreateDisposition, createOptions)) {
file.rename(pathTo, true);
}
}
Maybe I messed up the attributes and options in smbShare.open-function?
Hm.. I messed up with properties of SMB2CreateDisposition.
The solution: smb2CreateDisposition = SMB2CreateDisposition.FILE_OPEN_IF;
instead of smb2CreateDisposition = SMB2CreateDisposition.FILE_OVERWRITE_IF;
The former opens (1.) or creates (2.) the file in question.
The latter overwrites the existing file.

AWS S3 upload file name mismatch

I am able to upload multiple files to s3 bucket at once. However there is a mismatch in the file name the one I provided and uploaded file. I am interested in file name as I need to generate cloud front signed url based on that.
File generation code
final String fileName = System.currentTimeMillis() + pictureData.getFileName();
final File file = new File(fileName); //fileName is -> 1594125913522_image1.png
writeByteArrayToFile(img, file);
AWS file upload code
public void uploadMultipleFiles(final List<File> files) {
final TransferManager transferManager = TransferManagerBuilder.standard().withS3Client(amazonS3).build();
try {
final MultipleFileUpload xfer = transferManager.uploadFileList(bucketName, null, new File("."), files);
xfer.waitForCompletion();
} catch (InterruptedException exception) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("InterruptedException occurred=>" + exception);
}
} catch (AmazonServiceException exception) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("AmazonServiceException occurred =>" + exception);
}
throw exception;
}
}
Uploaded file name is 94125913522_image1.png. As you can see first two characters disappeared. What am I missing here. I am not able to figure out. Kindly advice.
private static void writeByteArrayToFile(final byte[] byteArray, final File file) {
try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(Paths.get(file.getName())))) {
outputStream.write(byteArray);
} catch (IOException exception) {
throw new FileIllegalStateException("Error while writing image to file", exception);
}
}
The reason of the problem
You lose the first two charecters of the file names because of the third argument of this method:
transferManager.uploadFileList(bucketName, null, new File("."), files);
What happens in this case
So, what is the third argument:
/**
...
* #param directory
* The common parent directory of files to upload. The keys
* of the files in the list of files are constructed relative to
* this directory and the virtualDirectoryKeyPrefix.
...
*/
public MultipleFileUpload uploadFileList(... , File directory, ...){...}
And how will it be used:
...
int startingPosition = directory.getAbsolutePath().length();
if (!(directory.getAbsolutePath().endsWith(File.separator)))
startingPosition++;
...
String key = f.getAbsolutePath().substring(startingPosition)...
Thus, the directory variable is used to define a starting index to trim file paths to get file keys.
When you pass new File(".") as a directory, the parent directory for your files will be {your_path}.
But this is a directory, and you need to work with files inside it. So the common part, retrieved from your directory file, is {your_path}./
That is 2 symbols more than you actually need. And for this reason this method trims the 2 extra characters - an extra shift of two characters when trimming the file path.
The solution
If you only need to work with the current directory, you can pass the current directory as follows:
MultipleFileUpload upload = transferManager.uploadFileList(bucketName, "",
System.getProperty("user.dir"), files);
But if you start working with external sources, it won't work. So you can use this code, which creates one MultipleFileUpload per group of files from one directory.
private final String PATH_SEPARATOR = File.separator;
private String bucketName;
private TransferManager transferManager;
public void uploadMultipleFiles(String prefix, List<File> filesToUpload){
Map<File, List<File>> multipleUploadArguments =
getMultipleUploadArguments(filesToUpload);
for (Map.Entry<File, List<File>> multipleUploadArgument:
multipleUploadArguments.entrySet()){
try{
MultipleFileUpload upload = transferManager.uploadFileList(
bucketName, prefix,
multipleUploadArgument.getKey(),
multipleUploadArgument.getValue()
);
upload.waitForCompletion();
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}
private Map<File, List<File>> getMultipleUploadArguments(List<File> filesToUpload){
return filesToUpload.stream()
.collect(Collectors.groupingBy(this::getDirectoryPathForFile));
}
private File getDirectoryPathForFile(File file){
String filePath = file.getAbsolutePath();
String directoryPath = filePath.substring(0, filePath.lastIndexOf(PATH_SEPARATOR));
return new File(directoryPath);
}

Using static resources inside a jar library (to be used in Android)

I am developing a plain java library (jar), which contains some static files, which I put to src/main/resources. These static files are used to execute an algorithm and return processed data to the user.
public String getStringFromFile(String fileName) {
String text = "";
try {
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource(fileName).getFile());
Scanner scanner = new Scanner(file);
text = scanner.useDelimiter("\\A").next();
scanner.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return text;
}
So far so good. However, when I try to use this library/method in an Android project I get:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getFile()' on a null object reference
I want my static resource files to be published with the library. Am I doing something wrong? Any thoughts?
Ok. I think I have solved this. Following this article I created a res directory in the root of my jar module (on the same level as the src directory) and put my files there (helloworld.json). Then added this to build.gradle:
jar {
into('resourcez') {
from 'res'
}
}
Using this helper function (inside the jar lib) and getResourceAsStream() I get the contents of my resource files:
public String getModelFromStream(String fileName) {
final String classpath = "resourcez/";
ClassLoader classLoader = DexiModelLoader.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream(fileName);
if (resourceAsStream == null)
resourceAsStream = classLoader.getResourceAsStream(classpath + fileName);
if (resourceAsStream == null)
return "error";
Scanner s = new Scanner(resourceAsStream).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
After this I simply call getStringFromStream("helloworld.json") or getStringFromStream("resourcez/helloworld.json") in my android app and voilĂ !

Can't load a BufferedImage

I have a form with that code:
public Form()
{
initComponents();
try
{
File file= new File("avatar.jpg");
BufferedImage image= ImageIO.read(file);
}
catch (IOException ex)
{
System.out.println("Failed to load image");
}
}
The problem is that the code always throws the IOException and enters in the catch block.
So the file isn't read.
I have created the project with Netbeans 7.2, and the directory looks like this:
What's the problem? Maybe the file shouldn't be there but in the father directory? Or what?
Is your image being packaged within your jar? to find this out, extract you jar file like you would an ordinary zip file and check if the image is anywhere there (normally located by jarname\packagename\filename. If so then you'll need to extract your image as a resource using getResourceAsStream().
It would be something like:
public class Test {
private static final String absName = "/yourpackage/yourimage.jpg";
public static void main(String[] args) {
Class c=null;
try {
c = Class.forName("yourpackage.Test");//pkg is the package name in which the resource lies
} catch (Exception ex) {
// This should not happen.
}
InputStream s = c.getResourceAsStream(absName);
// do something with it.
}
public InputStream getResourceAsStream(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader();
if (cl==null) {
return ClassLoader.getSystemResourceAsStream(name); // A system class.
}
return cl.getResourceAsStream(name);
}
public java.net.URL getResource(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader();
if (cl==null) {
return ClassLoader.getSystemResource(name); // A system class.
}
return cl.getResource(name);
}
private String resolveName(String name) {
if (name == null) {
return name;
}
if (!name.startsWith("/")) {
Class c = this;
while (c.isArray()) {
c = c.getComponentType();
}
String baseName = c.getName();
int index = baseName.lastIndexOf('.');
if (index != -1) {
name = baseName.substring(0, index).replace('.', '/') + "/" + name;
}
} else {
name = name.substring(1);
}
return name;
}
}
Reference:
Accessing Resources
It looks like you have a namespace of poker.*
It all depends on where the jvm is initialized from.
Where is your main? Is it in /Users/ramy/NetBeansProjects/Poker/src?
Also, I suggest you use getResource() for all of your file loading needs, especially inside jars.
this.getClass().getResource("/resource/buttons1.png")
or
this.getClass().getResourceAsStream("/resource/TX_Jello2.ttf")
You can find out where your programs default path is by doing the following:
System.getProperty("user.dir");
Without seeing the error I would say the most likely cause is it can't find the file. So I suggest you replace "avatar.jpg" in the File constructor with the absolute file path to it. e.g.
File file = new File("INSERT_PATH_TO_FILE/avatar.jpg");
You cannot assume the image will be "there" because the relative path between your .java and the image seems ok.
Accessing a resource depends of your "kind" of project (Web, standalone....). In your case, you can try to get the image from your classpath
final File inputFile = new ClassPathResource("....").getFile();
final BufferedImage inputImg = ImageIO.read(inputFile);

Categories

Resources