android recursive mutex fix, android file path resolver fix
This commit is contained in:
@@ -53,6 +53,7 @@ typedef void (*fnDebugMessageCallback)(GLDEBUGPROC callback, const void* userPar
|
|||||||
EGLDisplay g_display = EGL_NO_DISPLAY;
|
EGLDisplay g_display = EGL_NO_DISPLAY;
|
||||||
EGLContext g_context = EGL_NO_CONTEXT;
|
EGLContext g_context = EGL_NO_CONTEXT;
|
||||||
std::recursive_mutex mutex;
|
std::recursive_mutex mutex;
|
||||||
|
int mutex_count = 0;
|
||||||
|
|
||||||
jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
|
jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
|
||||||
{
|
{
|
||||||
@@ -127,7 +128,9 @@ int GetUnicodeChar(struct android_app* app, int eventType, int keyCode, int meta
|
|||||||
void android_async_lock(struct engine* engine)
|
void android_async_lock(struct engine* engine)
|
||||||
{
|
{
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
eglMakeCurrent(engine->display, engine->surface, engine->surface, engine->context);
|
if (mutex_count == 0)
|
||||||
|
eglMakeCurrent(engine->display, engine->surface, engine->surface, engine->context);
|
||||||
|
mutex_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void android_async_swap(struct engine* engine)
|
void android_async_swap(struct engine* engine)
|
||||||
@@ -137,7 +140,9 @@ void android_async_swap(struct engine* engine)
|
|||||||
|
|
||||||
void android_async_unlock(struct engine* engine)
|
void android_async_unlock(struct engine* engine)
|
||||||
{
|
{
|
||||||
eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
mutex_count--;
|
||||||
|
if (mutex_count == 0)
|
||||||
|
eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
592
android/src/main/java/com/omixlab/panopainter/FileUtils.java
Normal file
592
android/src/main/java/com/omixlab/panopainter/FileUtils.java
Normal file
@@ -0,0 +1,592 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 OpenIntents.org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* ULR: https://github.com/coltoscosmin/FileUtils/blob/master/FileUtils.java
|
||||||
|
*/
|
||||||
|
package com.omixlab.panopainter;
|
||||||
|
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.DatabaseUtils;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.provider.OpenableColumns;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
public class FileUtils {
|
||||||
|
public static final String DOCUMENTS_DIR = "documents";
|
||||||
|
// configured android:authorities in AndroidManifest (https://developer.android.com/reference/android/support/v4/content/FileProvider)
|
||||||
|
public static final String AUTHORITY = "YOUR_AUTHORITY.provider";
|
||||||
|
public static final String HIDDEN_PREFIX = ".";
|
||||||
|
/**
|
||||||
|
* TAG for log messages.
|
||||||
|
*/
|
||||||
|
static final String TAG = "FileUtils";
|
||||||
|
private static final boolean DEBUG = false; // Set to true to enable logging
|
||||||
|
/**
|
||||||
|
* File and folder comparator. TODO Expose sorting option method
|
||||||
|
*/
|
||||||
|
public static Comparator<File> sComparator = new Comparator<File>() {
|
||||||
|
@Override
|
||||||
|
public int compare(File f1, File f2) {
|
||||||
|
// Sort alphabetically by lower case, which is much cleaner
|
||||||
|
return f1.getName().toLowerCase().compareTo(
|
||||||
|
f2.getName().toLowerCase());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* File (not directories) filter.
|
||||||
|
*/
|
||||||
|
public static FileFilter sFileFilter = new FileFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File file) {
|
||||||
|
final String fileName = file.getName();
|
||||||
|
// Return files only (not directories) and skip hidden files
|
||||||
|
return file.isFile() && !fileName.startsWith(HIDDEN_PREFIX);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Folder (directories) filter.
|
||||||
|
*/
|
||||||
|
public static FileFilter sDirFilter = new FileFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File file) {
|
||||||
|
final String fileName = file.getName();
|
||||||
|
// Return directories only and skip hidden directories
|
||||||
|
return file.isDirectory() && !fileName.startsWith(HIDDEN_PREFIX);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private FileUtils() {
|
||||||
|
} //private constructor to enforce Singleton pattern
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the extension of a file name, like ".png" or ".jpg".
|
||||||
|
*
|
||||||
|
* @param uri
|
||||||
|
* @return Extension including the dot("."); "" if there is no extension;
|
||||||
|
* null if uri was null.
|
||||||
|
*/
|
||||||
|
public static String getExtension(String uri) {
|
||||||
|
if (uri == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dot = uri.lastIndexOf(".");
|
||||||
|
if (dot >= 0) {
|
||||||
|
return uri.substring(dot);
|
||||||
|
} else {
|
||||||
|
// No extension.
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether the URI is a local one.
|
||||||
|
*/
|
||||||
|
public static boolean isLocal(String url) {
|
||||||
|
if (url != null && !url.startsWith("http://") && !url.startsWith("https://")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True if Uri is a MediaStore Uri.
|
||||||
|
* @author paulburke
|
||||||
|
*/
|
||||||
|
public static boolean isMediaUri(Uri uri) {
|
||||||
|
return "media".equalsIgnoreCase(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert File into Uri.
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* @return uri
|
||||||
|
*/
|
||||||
|
public static Uri getUri(File file) {
|
||||||
|
if (file != null) {
|
||||||
|
return Uri.fromFile(file);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the path only (without file name).
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static File getPathWithoutFilename(File file) {
|
||||||
|
if (file != null) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
// no file to be split off. Return everything
|
||||||
|
return file;
|
||||||
|
} else {
|
||||||
|
String filename = file.getName();
|
||||||
|
String filepath = file.getAbsolutePath();
|
||||||
|
|
||||||
|
// Construct path without file name.
|
||||||
|
String pathwithoutname = filepath.substring(0,
|
||||||
|
filepath.length() - filename.length());
|
||||||
|
if (pathwithoutname.endsWith("/")) {
|
||||||
|
pathwithoutname = pathwithoutname.substring(0, pathwithoutname.length() - 1);
|
||||||
|
}
|
||||||
|
return new File(pathwithoutname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The MIME type for the given file.
|
||||||
|
*/
|
||||||
|
public static String getMimeType(File file) {
|
||||||
|
|
||||||
|
String extension = getExtension(file.getName());
|
||||||
|
|
||||||
|
if (extension.length() > 0)
|
||||||
|
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.substring(1));
|
||||||
|
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The MIME type for the give Uri.
|
||||||
|
*/
|
||||||
|
public static String getMimeType(Context context, Uri uri) {
|
||||||
|
File file = new File(getPath(context, uri));
|
||||||
|
return getMimeType(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uri The Uri to check.
|
||||||
|
* @return Whether the Uri authority is local.
|
||||||
|
*/
|
||||||
|
public static boolean isLocalStorageDocument(Uri uri) {
|
||||||
|
return AUTHORITY.equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uri The Uri to check.
|
||||||
|
* @return Whether the Uri authority is ExternalStorageProvider.
|
||||||
|
*/
|
||||||
|
public static boolean isExternalStorageDocument(Uri uri) {
|
||||||
|
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uri The Uri to check.
|
||||||
|
* @return Whether the Uri authority is DownloadsProvider.
|
||||||
|
*/
|
||||||
|
public static boolean isDownloadsDocument(Uri uri) {
|
||||||
|
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uri The Uri to check.
|
||||||
|
* @return Whether the Uri authority is MediaProvider.
|
||||||
|
*/
|
||||||
|
public static boolean isMediaDocument(Uri uri) {
|
||||||
|
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uri The Uri to check.
|
||||||
|
* @return Whether the Uri authority is Google Photos.
|
||||||
|
*/
|
||||||
|
public static boolean isGooglePhotosUri(Uri uri) {
|
||||||
|
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the data column for this Uri. This is useful for
|
||||||
|
* MediaStore Uris, and other file-based ContentProviders.
|
||||||
|
*
|
||||||
|
* @param context The context.
|
||||||
|
* @param uri The Uri to query.
|
||||||
|
* @param selection (Optional) Filter used in the query.
|
||||||
|
* @param selectionArgs (Optional) Selection arguments used in the query.
|
||||||
|
* @return The value of the _data column, which is typically a file path.
|
||||||
|
*/
|
||||||
|
public static String getDataColumn(Context context, Uri uri, String selection,
|
||||||
|
String[] selectionArgs) {
|
||||||
|
|
||||||
|
Cursor cursor = null;
|
||||||
|
final String column = MediaStore.Files.FileColumns.DATA;
|
||||||
|
final String[] projection = {
|
||||||
|
column
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
|
||||||
|
null);
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
if (DEBUG)
|
||||||
|
DatabaseUtils.dumpCursor(cursor);
|
||||||
|
|
||||||
|
final int column_index = cursor.getColumnIndexOrThrow(column);
|
||||||
|
return cursor.getString(column_index);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a file path from a Uri. This will get the the path for Storage Access
|
||||||
|
* Framework Documents, as well as the _data field for the MediaStore and
|
||||||
|
* other file-based ContentProviders.<br>
|
||||||
|
* <br>
|
||||||
|
* Callers should check whether the path is local before assuming it
|
||||||
|
* represents a local file.
|
||||||
|
*
|
||||||
|
* @param context The context.
|
||||||
|
* @param uri The Uri to query.
|
||||||
|
* @see #isLocal(String)
|
||||||
|
* @see #getFile(Context, Uri)
|
||||||
|
*/
|
||||||
|
public static String getPath(final Context context, final Uri uri) {
|
||||||
|
|
||||||
|
if (DEBUG)
|
||||||
|
Log.d(TAG + " File -",
|
||||||
|
"Authority: " + uri.getAuthority() +
|
||||||
|
", Fragment: " + uri.getFragment() +
|
||||||
|
", Port: " + uri.getPort() +
|
||||||
|
", Query: " + uri.getQuery() +
|
||||||
|
", Scheme: " + uri.getScheme() +
|
||||||
|
", Host: " + uri.getHost() +
|
||||||
|
", Segments: " + uri.getPathSegments().toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||||
|
|
||||||
|
// DocumentProvider
|
||||||
|
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
|
||||||
|
// LocalStorageProvider
|
||||||
|
if (isLocalStorageDocument(uri)) {
|
||||||
|
// The path is the id
|
||||||
|
return DocumentsContract.getDocumentId(uri);
|
||||||
|
}
|
||||||
|
// ExternalStorageProvider
|
||||||
|
else if (isExternalStorageDocument(uri)) {
|
||||||
|
final String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
final String[] split = docId.split(":");
|
||||||
|
final String type = split[0];
|
||||||
|
|
||||||
|
if ("primary".equalsIgnoreCase(type)) {
|
||||||
|
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// DownloadsProvider
|
||||||
|
else if (isDownloadsDocument(uri)) {
|
||||||
|
|
||||||
|
final String id = DocumentsContract.getDocumentId(uri);
|
||||||
|
|
||||||
|
if (id != null && id.startsWith("raw:")) {
|
||||||
|
return id.substring(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] contentUriPrefixesToTry = new String[]{
|
||||||
|
"content://downloads/public_downloads",
|
||||||
|
"content://downloads/my_downloads"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String contentUriPrefix : contentUriPrefixesToTry) {
|
||||||
|
Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id));
|
||||||
|
try {
|
||||||
|
String path = getDataColumn(context, contentUri, null, null);
|
||||||
|
if (path != null) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// path could not be retrieved using ContentResolver, therefore copy file to accessible cache using streams
|
||||||
|
String fileName = getFileName(context, uri);
|
||||||
|
File cacheDir = getDocumentCacheDir(context);
|
||||||
|
File file = generateFileName(fileName, cacheDir);
|
||||||
|
String destinationPath = null;
|
||||||
|
if (file != null) {
|
||||||
|
destinationPath = file.getAbsolutePath();
|
||||||
|
saveFileFromUri(context, uri, destinationPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return destinationPath;
|
||||||
|
}
|
||||||
|
// MediaProvider
|
||||||
|
else if (isMediaDocument(uri)) {
|
||||||
|
final String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
final String[] split = docId.split(":");
|
||||||
|
final String type = split[0];
|
||||||
|
|
||||||
|
Uri contentUri = null;
|
||||||
|
if ("image".equals(type)) {
|
||||||
|
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if ("video".equals(type)) {
|
||||||
|
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if ("audio".equals(type)) {
|
||||||
|
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String selection = "_id=?";
|
||||||
|
final String[] selectionArgs = new String[]{
|
||||||
|
split[1]
|
||||||
|
};
|
||||||
|
|
||||||
|
return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// MediaStore (and general)
|
||||||
|
else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
|
||||||
|
// Return the remote address
|
||||||
|
if (isGooglePhotosUri(uri))
|
||||||
|
return uri.getLastPathSegment();
|
||||||
|
|
||||||
|
return getDataColumn(context, uri, null, null);
|
||||||
|
}
|
||||||
|
// File
|
||||||
|
else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
return uri.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert Uri into File, if possible.
|
||||||
|
*
|
||||||
|
* @return file A local file that the Uri was pointing to, or null if the
|
||||||
|
* Uri is unsupported or pointed to a remote resource.
|
||||||
|
* @author paulburke
|
||||||
|
* @see #getPath(Context, Uri)
|
||||||
|
*/
|
||||||
|
public static File getFile(Context context, Uri uri) {
|
||||||
|
if (uri != null) {
|
||||||
|
String path = getPath(context, uri);
|
||||||
|
if (path != null && isLocal(path)) {
|
||||||
|
return new File(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the file size in a human-readable string.
|
||||||
|
*
|
||||||
|
* @param size
|
||||||
|
* @return
|
||||||
|
* @author paulburke
|
||||||
|
*/
|
||||||
|
public static String getReadableFileSize(int size) {
|
||||||
|
final int BYTES_IN_KILOBYTES = 1024;
|
||||||
|
final DecimalFormat dec = new DecimalFormat("###.#");
|
||||||
|
final String KILOBYTES = " KB";
|
||||||
|
final String MEGABYTES = " MB";
|
||||||
|
final String GIGABYTES = " GB";
|
||||||
|
float fileSize = 0;
|
||||||
|
String suffix = KILOBYTES;
|
||||||
|
|
||||||
|
if (size > BYTES_IN_KILOBYTES) {
|
||||||
|
fileSize = size / BYTES_IN_KILOBYTES;
|
||||||
|
if (fileSize > BYTES_IN_KILOBYTES) {
|
||||||
|
fileSize = fileSize / BYTES_IN_KILOBYTES;
|
||||||
|
if (fileSize > BYTES_IN_KILOBYTES) {
|
||||||
|
fileSize = fileSize / BYTES_IN_KILOBYTES;
|
||||||
|
suffix = GIGABYTES;
|
||||||
|
} else {
|
||||||
|
suffix = MEGABYTES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return String.valueOf(dec.format(fileSize) + suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getDownloadsDir() {
|
||||||
|
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getDocumentCacheDir(Context context) {
|
||||||
|
File dir = new File(context.getCacheDir(), DOCUMENTS_DIR);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
dir.mkdirs();
|
||||||
|
}
|
||||||
|
logDir(context.getCacheDir());
|
||||||
|
logDir(dir);
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logDir(File dir) {
|
||||||
|
if(!DEBUG) return;
|
||||||
|
Log.d(TAG, "Dir=" + dir);
|
||||||
|
File[] files = dir.listFiles();
|
||||||
|
for (File file : files) {
|
||||||
|
Log.d(TAG, "File=" + file.getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File generateFileName(String name, File directory) {
|
||||||
|
if (name == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
File file = new File(directory, name);
|
||||||
|
|
||||||
|
if (file.exists()) {
|
||||||
|
String fileName = name;
|
||||||
|
String extension = "";
|
||||||
|
int dotIndex = name.lastIndexOf('.');
|
||||||
|
if (dotIndex > 0) {
|
||||||
|
fileName = name.substring(0, dotIndex);
|
||||||
|
extension = name.substring(dotIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
while (file.exists()) {
|
||||||
|
index++;
|
||||||
|
name = fileName + '(' + index + ')' + extension;
|
||||||
|
file = new File(directory, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!file.createNewFile()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
logDir(directory);
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void saveFileFromUri(Context context, Uri uri, String destinationPath) {
|
||||||
|
InputStream is = null;
|
||||||
|
BufferedOutputStream bos = null;
|
||||||
|
try {
|
||||||
|
is = context.getContentResolver().openInputStream(uri);
|
||||||
|
bos = new BufferedOutputStream(new FileOutputStream(destinationPath, false));
|
||||||
|
byte[] buf = new byte[1024];
|
||||||
|
is.read(buf);
|
||||||
|
do {
|
||||||
|
bos.write(buf);
|
||||||
|
} while (is.read(buf) != -1);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (is != null) is.close();
|
||||||
|
if (bos != null) bos.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] readBytesFromFile(String filePath) {
|
||||||
|
|
||||||
|
FileInputStream fileInputStream = null;
|
||||||
|
byte[] bytesArray = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
File file = new File(filePath);
|
||||||
|
bytesArray = new byte[(int) file.length()];
|
||||||
|
|
||||||
|
//read file into bytes[]
|
||||||
|
fileInputStream = new FileInputStream(file);
|
||||||
|
fileInputStream.read(bytesArray);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (fileInputStream != null) {
|
||||||
|
try {
|
||||||
|
fileInputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytesArray;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File createTempImageFile(Context context, String fileName) throws IOException {
|
||||||
|
// Create an image file name
|
||||||
|
File storageDir = new File(context.getCacheDir(), DOCUMENTS_DIR);
|
||||||
|
return File.createTempFile(fileName, ".jpg", storageDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFileName(Context context, Uri uri) {
|
||||||
|
String mimeType = context.getContentResolver().getType(uri);
|
||||||
|
String filename = null;
|
||||||
|
|
||||||
|
if (mimeType == null && context != null) {
|
||||||
|
String path = getPath(context, uri);
|
||||||
|
if (path == null) {
|
||||||
|
filename = getName(uri.toString());
|
||||||
|
} else {
|
||||||
|
File file = new File(path);
|
||||||
|
filename = file.getName();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Cursor returnCursor = context.getContentResolver().query(uri, null, null, null, null);
|
||||||
|
if (returnCursor != null) {
|
||||||
|
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||||
|
returnCursor.moveToFirst();
|
||||||
|
filename = returnCursor.getString(nameIndex);
|
||||||
|
returnCursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getName(String filename) {
|
||||||
|
if (filename == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int index = filename.lastIndexOf('/');
|
||||||
|
return filename.substring(index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -135,10 +135,11 @@ public class MainActivity extends NativeActivity {
|
|||||||
//String path = myFile.getAbsolutePath();
|
//String path = myFile.getAbsolutePath();
|
||||||
String path = null;
|
String path = null;
|
||||||
try {
|
try {
|
||||||
path = PathUtil.getPath(this, uri);
|
path = FileUtils.getPath(this, uri);
|
||||||
Log.v("PanoPainterJava", "pick selected " + path);
|
Log.v("PanoPainterJava", "pick selected " + path);
|
||||||
pickFileCallback(path);
|
if (path != null)
|
||||||
} catch (URISyntaxException e) {
|
pickFileCallback(path);
|
||||||
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import android.os.Build;
|
|||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.provider.DocumentsContract;
|
import android.provider.DocumentsContract;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
@@ -60,6 +61,7 @@ public class PathUtil {
|
|||||||
return cursor.getString(column_index);
|
return cursor.getString(column_index);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
Log.v("PanoPainterJava", "PathUtil error: " + e.getMessage());
|
||||||
}
|
}
|
||||||
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||||
return uri.getPath();
|
return uri.getPath();
|
||||||
|
|||||||
@@ -88,6 +88,7 @@
|
|||||||
<!--brush-presets panel-->
|
<!--brush-presets panel-->
|
||||||
<layout id="tpl-panel-brush-preset">
|
<layout id="tpl-panel-brush-preset">
|
||||||
<node width="220" margin="0 0 10 0" rtl="ltr">
|
<node width="220" margin="0 0 10 0" rtl="ltr">
|
||||||
|
<scroll id="brushes" color=".4" pad="5 20 5 5" dir="row" wrap="1" flood-events="1" grow="1" height="1"/>
|
||||||
<border height="40" color=".5" dir="row" align="center" flood-events="1">
|
<border height="40" color=".5" dir="row" align="center" flood-events="1">
|
||||||
<button-custom id="btn-add" thickness="1" color="0 0" border-color=".0" shrink="1" margin="0 2 0 5">
|
<button-custom id="btn-add" thickness="1" color="0 0" border-color=".0" shrink="1" margin="0 2 0 5">
|
||||||
<icon width="30" icon="add"/>
|
<icon width="30" icon="add"/>
|
||||||
@@ -106,7 +107,6 @@
|
|||||||
<icon width="30" icon="bin_closed"/>
|
<icon width="30" icon="bin_closed"/>
|
||||||
</button-custom>
|
</button-custom>
|
||||||
</border>
|
</border>
|
||||||
<scroll id="brushes" color=".4" pad="5 20 5 5" dir="row" wrap="1" flood-events="1" grow="1" height="1"/>
|
|
||||||
</node>
|
</node>
|
||||||
</layout>
|
</layout>
|
||||||
<!--brush-presets-item icon-->
|
<!--brush-presets-item icon-->
|
||||||
@@ -168,13 +168,13 @@
|
|||||||
<node height="20" justify="center"><text text="Size" font-face="arial" font-size="11"/></node>
|
<node height="20" justify="center"><text text="Size" font-face="arial" font-size="11"/></node>
|
||||||
<node height="20" justify="center"><text text="Flow" font-face="arial" font-size="11"/></node>
|
<node height="20" justify="center"><text text="Flow" font-face="arial" font-size="11"/></node>
|
||||||
<node height="20" justify="center"><text text="Opacity" font-face="arial" font-size="11"/></node>
|
<node height="20" justify="center"><text text="Opacity" font-face="arial" font-size="11"/></node>
|
||||||
<node height="20" justify="center"><text text="Spacing" font-face="arial" font-size="11"/></node>
|
<node height="20" justify="center" margin="5 0 0 0"><text text="Spacing" font-face="arial" font-size="11"/></node>
|
||||||
<node height="20" justify="center"><text text="Angle" font-face="arial" font-size="11"/></node>
|
<node height="20" justify="center"><text text="Angle" font-face="arial" font-size="11"/></node>
|
||||||
<node height="20" justify="center"><text text="Mixer" font-face="arial" font-size="11"/></node>
|
<node height="30" justify="center" margin="5 0 0 0"><text text="Stencil" font-face="arial" font-size="11"/></node>
|
||||||
<node height="20" justify="center"><text text="Stencil" font-face="arial" font-size="11"/></node>
|
<node height="20" justify="center" margin="5 0 0 0"><text text="Mixer" font-face="arial" font-size="11"/></node>
|
||||||
<node height="20" justify="center"><text text="Wet" font-face="arial" font-size="11"/></node>
|
<node height="20" justify="center"><text text="Wet" font-face="arial" font-size="11"/></node>
|
||||||
<node height="20" justify="center"><text text="Noise" font-face="arial" font-size="11"/></node>
|
<node height="20" justify="center"><text text="Noise" font-face="arial" font-size="11"/></node>
|
||||||
<node height="20" justify="center"><text text="Hue" font-face="arial" font-size="11"/></node>
|
<node height="20" justify="center" margin="5 0 0 0"><text text="Hue" font-face="arial" font-size="11"/></node>
|
||||||
<node height="20" justify="center"><text text="Sat" font-face="arial" font-size="11"/></node>
|
<node height="20" justify="center"><text text="Sat" font-face="arial" font-size="11"/></node>
|
||||||
<node height="20" justify="center"><text text="Val" font-face="arial" font-size="11"/></node>
|
<node height="20" justify="center"><text text="Val" font-face="arial" font-size="11"/></node>
|
||||||
</node>
|
</node>
|
||||||
@@ -200,23 +200,23 @@
|
|||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
<node height="20" pad="1" width="100%"><slider-h id="tip-opacity" value="1"/></node>
|
<node height="20" pad="1" width="100%"><slider-h id="tip-opacity" value="1"/></node>
|
||||||
<node height="20" pad="1" width="100%"><slider-h id="tip-spacing" value=".25"/></node>
|
<node height="20" pad="1" width="100%" margin="5 0 0 0"><slider-h id="tip-spacing" value=".25"/></node>
|
||||||
<node height="20" pad="1" width="100%" dir="row">
|
<node height="20" pad="1" width="100%" dir="row">
|
||||||
<slider-h id="tip-angle" width="1" grow="1"/>
|
<slider-h id="tip-angle" width="1" grow="1"/>
|
||||||
<node width="20" pad="0" margin="0 0 0 2">
|
<node width="20" pad="0" margin="0 0 0 2">
|
||||||
<checkbox id="tip-angle-follow"></checkbox>
|
<checkbox id="tip-angle-follow"></checkbox>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
<node height="20" pad="1" width="100%"><slider-h id="tip-mix"/></node>
|
<node height="30" pad="1" width="100%" dir="row" margin="5 0 0 0">
|
||||||
<node height="20" pad="1" width="100%" dir="row">
|
|
||||||
<slider-h id="tip-stencil" width="1" grow="1"/>
|
<slider-h id="tip-stencil" width="1" grow="1"/>
|
||||||
<node width="20" pad="0" margin="0 0 0 2">
|
<node width="50" pad="0" margin="0 0 0 5">
|
||||||
<button id="tip-stencil-load" text="..." height="20"></button>
|
<button id="tip-stencil-load" text="Load" height="30"></button>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
|
<node height="20" pad="1" width="100%" margin="5 0 0 0"><slider-h id="tip-mix"/></node>
|
||||||
<node height="20" pad="1" width="100%"><slider-h id="tip-wet"/></node>
|
<node height="20" pad="1" width="100%"><slider-h id="tip-wet"/></node>
|
||||||
<node height="20" pad="1" width="100%"><slider-h id="tip-noise"/></node>
|
<node height="20" pad="1" width="100%"><slider-h id="tip-noise"/></node>
|
||||||
<node height="20" pad="1" width="100%" dir="row">
|
<node height="20" pad="1" width="100%" dir="row" margin="5 0 0 0">
|
||||||
<slider-h id="tip-hue" width="1" grow="1"/>
|
<slider-h id="tip-hue" width="1" grow="1"/>
|
||||||
<node width="20" pad="0" margin="0 0 0 2">
|
<node width="20" pad="0" margin="0 0 0 2">
|
||||||
<checkbox id="tip-hue-pressure"></checkbox>
|
<checkbox id="tip-hue-pressure"></checkbox>
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ std::string Asset::absolute(const std::string& path)
|
|||||||
|
|
||||||
bool Asset::is_asset(const std::string & path)
|
bool Asset::is_asset(const std::string & path)
|
||||||
{
|
{
|
||||||
return path.find("data/") != std::string::npos;
|
return path.substr(0, 5) == "data/";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Asset::open(const char* path)
|
bool Asset::open(const char* path)
|
||||||
|
|||||||
Reference in New Issue
Block a user