Fix Android sharing files without extension (#1646)
This commit is contained in:
parent
8a152ac1e7
commit
aebe22f58d
4 changed files with 89 additions and 4 deletions
|
|
@ -7,6 +7,7 @@ import android.os.Build;
|
|||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentResolver;
|
||||
import android.os.Environment;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
|
|
@ -98,6 +99,7 @@ public class RealPathUtil {
|
|||
cacheDir.mkdirs();
|
||||
}
|
||||
|
||||
String mimeType = getMimeType(uri.getPath());
|
||||
tmpFile = File.createTempFile("tmp", fileName, cacheDir);
|
||||
|
||||
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r");
|
||||
|
|
@ -182,6 +184,11 @@ public class RealPathUtil {
|
|||
return getMimeType(file);
|
||||
}
|
||||
|
||||
public static String getMimeTypeFromUri(final Context context, final Uri uri) {
|
||||
ContentResolver cR = context.getContentResolver();
|
||||
return cR.getType(uri);
|
||||
}
|
||||
|
||||
public static void deleteTempFiles(final File dir) {
|
||||
try {
|
||||
if (dir.isDirectory()) {
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ public class ShareModule extends ReactContextBaseJavaModule {
|
|||
map = Arguments.createMap();
|
||||
text = "file://" + filePath;
|
||||
map.putString("value", text);
|
||||
map.putString("type", RealPathUtil.getMimeType(filePath));
|
||||
map.putString("type", RealPathUtil.getMimeTypeFromUri(currentActivity, uri));
|
||||
items.pushMap(map);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@
|
|||
|
||||
import {Platform} from 'react-native';
|
||||
import RNFetchBlob from 'react-native-fetch-blob';
|
||||
import mimeDB from 'mime-db';
|
||||
|
||||
import {lookupMimeType} from 'mattermost-redux/utils/file_utils';
|
||||
|
||||
import {DeviceTypes} from 'app/constants/';
|
||||
|
||||
const EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/;
|
||||
const {DOCUMENTS_PATH, IMAGES_PATH, VIDEOS_PATH} = DeviceTypes;
|
||||
const DEFAULT_SERVER_MAX_FILE_SIZE = 50 * 1024 * 1024;// 50 Mb
|
||||
|
||||
|
|
@ -32,6 +34,9 @@ const SUPPORTED_VIDEO_FORMAT = Platform.select({
|
|||
android: ['video/3gpp', 'video/x-matroska', 'video/mp4', 'video/webm'],
|
||||
});
|
||||
|
||||
const types = {};
|
||||
const extensions = {};
|
||||
|
||||
export function generateId() {
|
||||
// Implementation taken from http://stackoverflow.com/a/2117523
|
||||
let id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
|
||||
|
|
@ -144,3 +149,69 @@ export const isVideo = (file) => {
|
|||
|
||||
return SUPPORTED_VIDEO_FORMAT.includes(mime);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the default extension for a MIME type.
|
||||
*
|
||||
* @param {string} type
|
||||
* @return {boolean|string}
|
||||
*/
|
||||
|
||||
export function getExtensionFromMime(type) {
|
||||
if (!Object.keys(extensions).length) {
|
||||
populateMaps();
|
||||
}
|
||||
|
||||
if (!type || typeof type !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: use media-typer
|
||||
const match = EXTRACT_TYPE_REGEXP.exec(type);
|
||||
|
||||
// get extensions
|
||||
const exts = match && extensions[match[1].toLowerCase()];
|
||||
|
||||
if (!exts || !exts.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return exts[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the extensions and types maps.
|
||||
* @private
|
||||
*/
|
||||
|
||||
function populateMaps() {
|
||||
// source preference (least -> most)
|
||||
const preference = ['nginx', 'apache', undefined, 'iana']; //eslint-disable-line no-undefined
|
||||
|
||||
Object.keys(mimeDB).forEach((type) => {
|
||||
const mime = mimeDB[type];
|
||||
const exts = mime.extensions;
|
||||
|
||||
if (!exts || !exts.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
extensions[type] = exts;
|
||||
|
||||
for (let i = 0; i < exts.length; i++) {
|
||||
const extension = exts[i];
|
||||
|
||||
if (types[extension]) {
|
||||
const from = preference.indexOf(mimeDB[types[extension]].source);
|
||||
const to = preference.indexOf(mime.source);
|
||||
|
||||
if (types[extension] !== 'application/octet-stream' &&
|
||||
(from > to || (from === to && types[extension].substr(0, 12) === 'application/'))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
types[extension] = type;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {NavigationActions} from 'react-navigation';
|
|||
import TouchableItem from 'react-navigation/src/views/TouchableItem';
|
||||
import PropTypes from 'prop-types';
|
||||
import {intlShape} from 'react-intl';
|
||||
|
||||
import {
|
||||
Image,
|
||||
NativeModules,
|
||||
|
|
@ -25,6 +26,7 @@ import {getFormattedFileSize, lookupMimeType} from 'mattermost-redux/utils/file_
|
|||
|
||||
import PaperPlane from 'app/components/paper_plane';
|
||||
import mattermostManaged from 'app/mattermost_managed';
|
||||
import {getExtensionFromMime} from 'app/utils/file';
|
||||
import {emptyFunction} from 'app/utils/general';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
|
@ -278,15 +280,20 @@ export default class ExtensionPost extends PureComponent {
|
|||
break;
|
||||
default: {
|
||||
const fullPath = item.value;
|
||||
const filename = fullPath.replace(/^.*[\\/]/, '');
|
||||
const extension = filename.split('.').pop();
|
||||
const fileSize = await RNFetchBlob.fs.stat(fullPath);
|
||||
let filename = fullPath.replace(/^.*[\\/]/, '');
|
||||
let extension = filename.split('.').pop();
|
||||
if (extension === filename) {
|
||||
extension = getExtensionFromMime(item.type);
|
||||
filename = `${filename}.${extension}`;
|
||||
}
|
||||
|
||||
totalSize += fileSize.size;
|
||||
files.push({
|
||||
extension,
|
||||
filename,
|
||||
fullPath,
|
||||
mimeType: lookupMimeType(filename.toLowerCase()),
|
||||
mimeType: item.type || lookupMimeType(filename.toLowerCase()),
|
||||
size: getFormattedFileSize(fileSize),
|
||||
type: item.type,
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue