Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(android): Ti.Media.previewImage() fails to display in-memory blobs as of 9.1.0 #12271

Merged
merged 4 commits into from
Nov 20, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -561,64 +561,18 @@ private void deleteTiTempFiles()
} finally {
// If failed to move existing folder to "trash", then do a blocking delete. (Should never happen.)
if (!wasTrashed) {
tryDeleteTree(nextDir);
TiFileHelper.getInstance().tryDeleteTree(nextDir);
}
}
}

// Async delete the "trash" directory tree.
Thread thread = new Thread(() -> {
tryDeleteTree(trashDir);
TiFileHelper.getInstance().tryDeleteTree(trashDir);
});
thread.start();
}

/**
* Recursively deletes the given directory tree.
* Will never throw an exception and will return the result as a boolean instead.
* @param file Reference to a file or directory. Can be null.
* @return
* Returns true if successfully deleted all files and folders under given directory tree.
* Returns false if at least 1 deletion failed or if given a null argument.
*/
private boolean tryDeleteTree(File file)
{
boolean wasSuccessful = false;
try {
wasSuccessful = deleteTree(file);
} catch (Throwable ex) {
Log.e(TAG, "Failed to delete directory tree: " + file, ex);
}
return wasSuccessful;
}

/**
* Recursively deletes the given directory tree.
* @param file Reference to a directory or a single file. Can be null, in which case this method no-ops.
* @return
* Returns true if successfully deleted all files and folders under given directory tree.
* Returns false if at least 1 deletion failed or if given a null argument.
* @exception SecurityException Thrown if don't have permission to delete at least 1 file in the tree.
*/
private boolean deleteTree(File file) throws SecurityException
{
// Validate argument.
if (file == null) {
return false;
}

// If given a directory, then recursively delete the entire tree.
boolean wasDeleted = true;
if (file.isDirectory()) {
for (File nextFile : file.listFiles()) {
wasDeleted = deleteTree(nextFile) && wasDeleted;
}
}

// Delete the given directory/file.
return (wasDeleted && file.delete());
}

public void setRootActivity(TiRootActivity rootActivity)
{
this.rootActivity = new WeakReference<TiRootActivity>(rootActivity);
Expand Down Expand Up @@ -1038,7 +992,6 @@ public void dispose()
{
TiActivityWindows.dispose();
TiActivitySupportHelpers.dispose();
TiFileHelper.getInstance().destroyTempFiles();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not destroy temp files here now? I assume because this no longer an active API and this just happens automatically implicitly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed TiFileHelper to use the same temp directory we expose in JavaScript. This temp directory gets deleted on app startup and every time the JS runtime terminates. See the deleteTiTempFiles() method calls below...
https://github.com/appcelerator/titanium_mobile/blob/master/android/titanium/src/java/org/appcelerator/titanium/TiApplication.java#L363-L384

Also, the old TiFileHelper code wasn't deleting temp files if the app was forced-quit, which happens all the time by the Android OS while the app is backgrounded. This needed to be fixed.

}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,8 @@
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

Expand Down Expand Up @@ -60,8 +55,6 @@ public class TiFileHelper
private SoftReference<Context> softContext;
private TiNinePatchHelper nph;

private ArrayList<File> tempFiles = new ArrayList<File>();

private static HashSet<String> resourcePathCache;
private static HashSet<String> foundResourcePathCache;
private static HashSet<String> notFoundResourcePathCache;
Expand Down Expand Up @@ -414,7 +407,7 @@ public void deployFromAssets(File dest) throws IOException
walkAssets(am, "", paths);

// TODO clean old dir
wipeDirectoryTree(dest);
deleteTree(dest);

// copy from assets to dest dir
BufferedInputStream bis = null;
Expand Down Expand Up @@ -469,7 +462,7 @@ public void deployFromAssets(File dest) throws IOException

public void deployFromZip(File fname, File dest) throws IOException
{
wipeDirectoryTree(dest);
deleteTree(dest);

ZipInputStream zis = null;
ZipEntry ze = null;
Expand Down Expand Up @@ -535,91 +528,79 @@ public void deployFromZip(File fname, File dest) throws IOException
}
}

public void wipeDirectoryTree(File path)
/**
* Recursively deletes the given directory tree.
* Will never throw an exception and will return the result as a boolean instead.
* @param file Reference to a file or directory. Can be null.
* @return
* Returns true if successfully deleted all files and folders under given directory tree.
* Returns false if at least 1 deletion failed or if given a null argument.
*/
public boolean tryDeleteTree(File file)
{
TreeSet<String> dirs = new TreeSet<String>(new Comparator<String>() {
public int compare(String o1, String o2)
{
return o1.compareTo(o2) * -1;
}
});

wipeDirectoryTree(path, dirs);

Iterator<String> d = dirs.iterator();
while (d.hasNext()) {
String fn = d.next();
File f = new File(fn);
Log.d(TAG, "Deleting Dir: " + f.getAbsolutePath(), Log.DEBUG_MODE);
f.delete();
boolean wasSuccessful = false;
jquick-axway marked this conversation as resolved.
Show resolved Hide resolved
try {
wasSuccessful = deleteTree(file);
} catch (Throwable ex) {
Log.e(TAG, "Failed to delete directory tree: " + file, ex);
}
return wasSuccessful;
}

public File getTempFile(String suffix, boolean destroyOnExit) throws IOException
/**
* Recursively deletes the given directory tree.
* @param file Reference to a directory or a single file. Can be null, in which case this method no-ops.
* @return
* Returns true if successfully deleted all files and folders under given directory tree.
* Returns false if at least 1 deletion failed or if given a null argument.
* @exception SecurityException Thrown if don't have permission to delete at least 1 file in the tree.
*/
public boolean deleteTree(File file) throws SecurityException
{
File result = null;
Context context = softContext.get();
// Validate argument.
if (file == null) {
return false;
}

if (context != null) {
result = getTempFile(context.getCacheDir(), suffix, destroyOnExit);
// If given a directory, then recursively delete the entire tree.
boolean wasDeleted = true;
if (file.isDirectory()) {
for (File nextFile : file.listFiles()) {
wasDeleted = deleteTree(nextFile) && wasDeleted;
}
}
return result;

// Delete the given directory/file.
return (wasDeleted && file.delete());
}

public File getTempFile(File dir, String suffix, boolean destroyOnExit) throws IOException
public File getTempFile(String suffix, boolean destroyOnExit) throws IOException
{
if (!dir.exists()) {
dir.mkdirs();
}
final File result = new File(dir.getPath() + "/tia" + Math.abs(new Random().nextLong()) + suffix);

if (destroyOnExit) {
tempFiles.add(result);
}
return result;
TiApplication tiApp = TiApplication.getInstance();
File parentDir = destroyOnExit ? tiApp.getTiTempDir() : tiApp.getCacheDir();
return File.createTempFile("tia", suffix, parentDir);
}

public File getTempFileFromInputStream(InputStream is, String suffix, boolean destroyOnExit)
{
try {
File tempFile = getTempFile(suffix, destroyOnExit);

if (tempFile.exists()) {
try (FileOutputStream os = new FileOutputStream(tempFile)) {
byte[] bytes = new byte[1024];
int length;
FileOutputStream os = new FileOutputStream(tempFile);

while ((length = is.read(bytes)) != -1) {
os.write(bytes, 0, length);
}
os.close();
}
return tempFile;

} catch (FileNotFoundException e) {
Log.w(TAG, "Could not find temp file: " + suffix);
} catch (IOException e) {
} catch (Exception e) {
Log.w(TAG, "Error occurred while creating output stream from temp file: " + suffix);
}
return null;
}

public void destroyOnExit(File file)
{
tempFiles.add(file);
}

// Destroys all temporary files that have been created.
// This is called when the application is exited/destroyed.
public void destroyTempFiles()
{
for (File tempFile : tempFiles) {
tempFile.delete();
}

tempFiles.clear();
}

/**
* Creates/retrieves a data directory in which the application can place its own custom data files.
* @param privateStorage determines the location of the data directory. If this is true, the location is internal(app-data://),
Expand All @@ -640,24 +621,6 @@ public File getDataDirectory(boolean privateStorage)
return f;
}

private void wipeDirectoryTree(File path, SortedSet<String> dirs)
{
File[] files = path.listFiles();
if (files != null) {
int len = files.length;
for (int i = 0; i < len; i++) {
File f = files[i];
if (f.isDirectory()) {
dirs.add(f.getAbsolutePath());
wipeDirectoryTree(f, dirs);
} else {
Log.d(TAG, "Deleting File: " + f.getAbsolutePath(), Log.DEBUG_MODE);
f.delete();
}
}
}
}

private void walkAssets(AssetManager am, String path, ArrayList<String> paths) throws IOException
{
if (titaniumPath(path)) {
Expand Down