forked from Ivasoft/mattermost-mobile
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ba6b1d641 | ||
|
|
286f05c3be | ||
|
|
e1329d41a3 | ||
|
|
bc39b38bf4 | ||
|
|
9173752390 | ||
|
|
033241691c | ||
|
|
3a4c9e75bf | ||
|
|
840fda2051 | ||
|
|
afa18fb5d9 | ||
|
|
84de817451 | ||
|
|
93777aefe6 | ||
|
|
825d8fcad3 | ||
|
|
abc2f30ef3 | ||
|
|
0d83ac4c93 | ||
|
|
cf88a2ae8f |
@@ -132,7 +132,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60"
|
||||
versionCode 357
|
||||
versionCode 359
|
||||
versionName "1.44.0"
|
||||
multiDexEnabled = true
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
|
||||
@@ -3,11 +3,9 @@ package com.mattermost.share;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentResolver;
|
||||
import android.os.Environment;
|
||||
import android.webkit.MimeTypeMap;
|
||||
@@ -15,18 +13,18 @@ import android.util.Log;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.Objects;
|
||||
|
||||
// Class based on the steveevers DocumentHelper https://gist.github.com/steveevers/a5af24c226f44bb8fdc3
|
||||
|
||||
public class RealPathUtil {
|
||||
public static String getRealPathFromURI(final Context context, final Uri uri) {
|
||||
|
||||
final boolean isKitKatOrNewer = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||
|
||||
// DocumentProvider
|
||||
if (isKitKatOrNewer && DocumentsContract.isDocumentUri(context, uri)) {
|
||||
if (DocumentsContract.isDocumentUri(context, uri)) {
|
||||
// ExternalStorageProvider
|
||||
if (isExternalStorageDocument(uri)) {
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
@@ -111,6 +109,7 @@ public class RealPathUtil {
|
||||
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
returnCursor.moveToFirst();
|
||||
fileName = sanitizeFilename(returnCursor.getString(nameIndex));
|
||||
returnCursor.close();
|
||||
|
||||
} catch (Exception e) {
|
||||
// just continue to get the filename with the last segment of the path
|
||||
@@ -118,7 +117,7 @@ public class RealPathUtil {
|
||||
|
||||
try {
|
||||
if (TextUtils.isEmpty(fileName)) {
|
||||
fileName = sanitizeFilename(uri.getLastPathSegment().toString().trim());
|
||||
fileName = sanitizeFilename(uri.getLastPathSegment().trim());
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +126,6 @@ public class RealPathUtil {
|
||||
cacheDir.mkdirs();
|
||||
}
|
||||
|
||||
String mimeType = getMimeType(uri.getPath());
|
||||
tmpFile = new File(cacheDir, fileName);
|
||||
tmpFile.createNewFile();
|
||||
|
||||
@@ -234,7 +232,7 @@ public class RealPathUtil {
|
||||
|
||||
private static void deleteRecursive(File fileOrDirectory) {
|
||||
if (fileOrDirectory.isDirectory())
|
||||
for (File child : fileOrDirectory.listFiles())
|
||||
for (File child : Objects.requireNonNull(fileOrDirectory.listFiles()))
|
||||
deleteRecursive(child);
|
||||
|
||||
fileOrDirectory.delete();
|
||||
|
||||
@@ -24,7 +24,7 @@ type Props = {
|
||||
initWebSocket: (additionalOptions: {forceConnection: boolean}) => void;
|
||||
markChannelViewedAndReadOnReconnect: (channelId: string) => void;
|
||||
setCurrentUserStatusOffline: () => void;
|
||||
startPeriodicStatusUpdates: () => void;
|
||||
startPeriodicStatusUpdates: (forceStatusUpdate: boolean) => void;
|
||||
status: string;
|
||||
stopPeriodicStatusUpdates: () => void;
|
||||
}
|
||||
@@ -111,10 +111,7 @@ const NetworkIndicator = ({
|
||||
};
|
||||
|
||||
const handleConnectionChange = ({hasInternet}: ConnectionChangedEvent) => {
|
||||
if (firstRun.current) {
|
||||
firstRun.current = false;
|
||||
handleWebSocket(true);
|
||||
} else {
|
||||
if (!firstRun.current) {
|
||||
if (!hasInternet) {
|
||||
setConnected(false);
|
||||
}
|
||||
@@ -142,7 +139,7 @@ const NetworkIndicator = ({
|
||||
const handleWebSocket = (connect: boolean) => {
|
||||
if (connect) {
|
||||
initWebSocket({forceConnection: true});
|
||||
startPeriodicStatusUpdates();
|
||||
startPeriodicStatusUpdates(true);
|
||||
} else {
|
||||
closeWebSocket(true);
|
||||
stopPeriodicStatusUpdates();
|
||||
@@ -150,6 +147,11 @@ const NetworkIndicator = ({
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
handleWebSocket(true);
|
||||
firstRun.current = false;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const networkListener = networkConnectionListener(handleConnectionChange);
|
||||
return () => networkListener.removeEventListener();
|
||||
@@ -168,7 +170,6 @@ const NetworkIndicator = ({
|
||||
useEffect(() => {
|
||||
const handleAppStateChange = stateChange(async (appState: AppStateStatus) => {
|
||||
const active = appState === 'active';
|
||||
|
||||
handleWebSocket(active);
|
||||
|
||||
if (active) {
|
||||
|
||||
@@ -51,7 +51,10 @@ export default class PostDraft extends PureComponent {
|
||||
|
||||
updateNativeScrollView = (scrollViewNativeID) => {
|
||||
if (this.keyboardTracker?.current) {
|
||||
this.keyboardTracker.current.resetScrollView(scrollViewNativeID);
|
||||
const resetScrollView = requestAnimationFrame(() => {
|
||||
this.keyboardTracker.current.resetScrollView(scrollViewNativeID);
|
||||
cancelAnimationFrame(resetScrollView);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ const Reaction = ({count, emojiName, highlight, onPress, onLongPress, theme}: Re
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
onPress(emojiName, highlight);
|
||||
}, []);
|
||||
}, [highlight]);
|
||||
|
||||
return (
|
||||
<TouchableWithFeedback
|
||||
|
||||
@@ -6,6 +6,7 @@ import {injectIntl, intlShape} from 'react-intl';
|
||||
import {DeviceEventEmitter, FlatList, Platform, StyleSheet, ViewToken} from 'react-native';
|
||||
|
||||
import {DeepLinkTypes, NavigationTypes} from '@constants';
|
||||
import {useResetNativeScrollView} from '@hooks';
|
||||
import {Posts} from '@mm-redux/constants';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
import {getDateForDateLine, isCombinedUserActivityPost, isDateLine, isStartOfNewMessages} from '@mm-redux/utils/post_list';
|
||||
@@ -217,7 +218,7 @@ const PostList = ({
|
||||
{...postProps}
|
||||
/>
|
||||
);
|
||||
}, [postIds]);
|
||||
}, [postIds, theme]);
|
||||
|
||||
const scrollToIndex = useCallback((index: number, animated = true) => {
|
||||
flatListRef.current?.scrollToIndex({
|
||||
@@ -228,6 +229,8 @@ const PostList = ({
|
||||
});
|
||||
}, []);
|
||||
|
||||
useResetNativeScrollView(scrollViewNativeID, postIds);
|
||||
|
||||
useEffect(() => {
|
||||
const scrollToBottom = (screen: string) => {
|
||||
if (screen === location) {
|
||||
|
||||
@@ -2,3 +2,6 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export {default as useDidUpdate} from './did_update';
|
||||
export {usePermanentSidebar, useSplitView} from './permanent_sidebar';
|
||||
export {useResetNativeScrollView} from './reset_native_scrollview';
|
||||
export {useShowMoreAnimatedStyle} from './show_more';
|
||||
|
||||
24
app/hooks/reset_native_scrollview.ts
Normal file
24
app/hooks/reset_native_scrollview.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {useEffect, useRef} from 'react';
|
||||
import {UPDATE_NATIVE_SCROLLVIEW} from '@constants/post_draft';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
|
||||
export const useResetNativeScrollView = (scrollViewNativeID: string | undefined, postIds: string[]) => {
|
||||
const prevPostCount = useRef(postIds.length);
|
||||
|
||||
useEffect(() => {
|
||||
if (scrollViewNativeID) {
|
||||
EventEmitter.emit(UPDATE_NATIVE_SCROLLVIEW, scrollViewNativeID);
|
||||
}
|
||||
}, [scrollViewNativeID]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!prevPostCount.current && postIds.length) {
|
||||
EventEmitter.emit(UPDATE_NATIVE_SCROLLVIEW, scrollViewNativeID);
|
||||
}
|
||||
|
||||
prevPostCount.current = postIds.length;
|
||||
}, [postIds]);
|
||||
};
|
||||
@@ -30,11 +30,6 @@ function loadTranslation(locale) {
|
||||
localeData = require('react-intl/locale-data/de');
|
||||
momentData = require('moment/locale/de');
|
||||
break;
|
||||
case 'en-AU':
|
||||
translations = require('@assets/i18n/en_AU.json');
|
||||
localeData = require('react-intl/locale-data/en');
|
||||
momentData = require('moment/locale/en-au');
|
||||
break;
|
||||
case 'es':
|
||||
translations = require('@assets/i18n/es.json');
|
||||
localeData = require('react-intl/locale-data/es');
|
||||
|
||||
@@ -7,7 +7,6 @@ const languages: Record<string, string> = keyMirror({
|
||||
bg: null,
|
||||
de: null,
|
||||
en: null,
|
||||
'en-AU': null,
|
||||
es: null,
|
||||
fr: null,
|
||||
hu: null,
|
||||
|
||||
@@ -76,7 +76,9 @@ const launchApp = (credentials) => {
|
||||
resetToSelectServer(emmProvider.allowOtherServers);
|
||||
}
|
||||
|
||||
telemetry.startSinceLaunch(credentials ? 'Launch on Channel Screen' : 'Launch on Server Screen');
|
||||
if (!EphemeralStore.appStarted) {
|
||||
telemetry.startSinceLaunch(credentials ? 'Launch on Channel Screen' : 'Launch on Server Screen');
|
||||
}
|
||||
});
|
||||
|
||||
EphemeralStore.appStarted = true;
|
||||
|
||||
@@ -802,29 +802,32 @@ export function searchProfiles(term: string, options: any = {}): ActionFunc {
|
||||
}
|
||||
|
||||
let statusIntervalId: NodeJS.Timeout|null;
|
||||
export function startPeriodicStatusUpdates(): ActionFunc {
|
||||
export function startPeriodicStatusUpdates(forceStatusUpdate = false): ActionFunc {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
if (statusIntervalId) {
|
||||
clearInterval(statusIntervalId);
|
||||
}
|
||||
|
||||
statusIntervalId = setInterval(
|
||||
() => {
|
||||
const {statuses} = getState().entities.users;
|
||||
const getStatusForUsers = () => {
|
||||
const {statuses} = getState().entities.users;
|
||||
|
||||
if (!statuses) {
|
||||
return;
|
||||
}
|
||||
if (!statuses) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userIds = Object.keys(statuses);
|
||||
if (!userIds.length) {
|
||||
return;
|
||||
}
|
||||
const userIds = Object.keys(statuses).filter((u) => u);
|
||||
if (!userIds.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(getStatusesByIds(userIds));
|
||||
},
|
||||
General.STATUS_INTERVAL,
|
||||
);
|
||||
dispatch(getStatusesByIds(userIds));
|
||||
};
|
||||
|
||||
statusIntervalId = setInterval(getStatusForUsers, General.STATUS_INTERVAL);
|
||||
|
||||
if (forceStatusUpdate) {
|
||||
getStatusForUsers();
|
||||
}
|
||||
|
||||
return {data: true};
|
||||
};
|
||||
|
||||
@@ -61,10 +61,7 @@ export default class ChannelIOS extends ChannelBase {
|
||||
renderDraftArea = true;
|
||||
component = (
|
||||
<>
|
||||
<ChannelPostList
|
||||
updateNativeScrollView={this.updateNativeScrollView}
|
||||
registerTypingAnimation={this.registerTypingAnimation}
|
||||
/>
|
||||
<ChannelPostList registerTypingAnimation={this.registerTypingAnimation}/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {General} from '@mm-redux/constants';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
|
||||
import {showModal, showModalOverCurrentContext} from '@actions/navigation';
|
||||
import {UPDATE_NATIVE_SCROLLVIEW, TYPING_VISIBLE} from '@constants/post_draft';
|
||||
import {TYPING_VISIBLE} from '@constants/post_draft';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import PushNotifications from '@init/push_notifications';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
@@ -127,7 +127,6 @@ export default class ChannelBase extends PureComponent {
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
this.props.actions.getChannelStats(this.props.currentChannelId);
|
||||
this.updateNativeScrollView();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -297,10 +296,6 @@ export default class ChannelBase extends PureComponent {
|
||||
});
|
||||
};
|
||||
|
||||
updateNativeScrollView = () => {
|
||||
EventEmitter.emit(UPDATE_NATIVE_SCROLLVIEW, this.props.currentChannelId);
|
||||
};
|
||||
|
||||
render() {
|
||||
// Overriden in channel.android.js and channel.ios.js
|
||||
// but defined here for channel_base.test.js
|
||||
|
||||
@@ -34,7 +34,6 @@ export default class ChannelPostList extends PureComponent {
|
||||
postIds: PropTypes.array,
|
||||
refreshing: PropTypes.bool.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
updateNativeScrollView: PropTypes.func,
|
||||
registerTypingAnimation: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
@@ -62,11 +61,6 @@ export default class ChannelPostList extends PureComponent {
|
||||
if (this.props.channelId !== prevProps.channelId) {
|
||||
this.isLoadingMoreTop = false;
|
||||
}
|
||||
|
||||
if (!prevProps.postIds?.length && this.props.postIds?.length > 0 && this.props.updateNativeScrollView) {
|
||||
// This is needed to re-bind the scrollview natively when getting the first posts
|
||||
this.props.updateNativeScrollView();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
@@ -32,7 +32,7 @@ function makeMapStateToProps() {
|
||||
|
||||
let teammateId;
|
||||
let isTeammateGuest = false;
|
||||
let customStatusEnabled;
|
||||
let customStatusEnabled = false;
|
||||
let customStatus;
|
||||
const isDirectMessage = currentChannel.type === General.DM_CHANNEL;
|
||||
|
||||
|
||||
@@ -171,6 +171,7 @@ export default class PinnedPosts extends PureComponent {
|
||||
ref={this.setListRef}
|
||||
contentContainerStyle={style.sectionList}
|
||||
data={postIds}
|
||||
extraData={theme}
|
||||
keyExtractor={this.keyExtractor}
|
||||
keyboardShouldPersistTaps='always'
|
||||
keyboardDismissMode='interactive'
|
||||
|
||||
@@ -171,6 +171,7 @@ export default class RecentMentions extends PureComponent {
|
||||
ref={this.setListRef}
|
||||
contentContainerStyle={style.sectionList}
|
||||
data={postIds}
|
||||
extraData={theme}
|
||||
keyExtractor={this.keyExtractor}
|
||||
keyboardShouldPersistTaps='always'
|
||||
keyboardDismissMode='interactive'
|
||||
|
||||
@@ -173,6 +173,7 @@ export default class SavedPosts extends PureComponent {
|
||||
ref={this.setListRef}
|
||||
contentContainerStyle={style.sectionList}
|
||||
data={postIds}
|
||||
extraData={theme}
|
||||
keyExtractor={this.keyExtractor}
|
||||
keyboardShouldPersistTaps='always'
|
||||
keyboardDismissMode='interactive'
|
||||
|
||||
@@ -19,12 +19,10 @@ export const PERF_MARKERS = {
|
||||
class Telemetry {
|
||||
metrics: PerfMetric[];
|
||||
currentMetrics: Record<string, PerfMetric>;
|
||||
started: boolean;
|
||||
|
||||
constructor() {
|
||||
this.metrics = [];
|
||||
this.currentMetrics = {};
|
||||
this.started = false;
|
||||
}
|
||||
|
||||
getMetrics = () => {
|
||||
@@ -64,17 +62,14 @@ class Telemetry {
|
||||
}
|
||||
|
||||
startSinceLaunch(extra = '') {
|
||||
if (!this.started) {
|
||||
this.started = true;
|
||||
getTimeSinceStartup().then((endTime) => {
|
||||
this.metrics.push({
|
||||
extra,
|
||||
name: PERF_MARKERS.START_SINCE_LAUNCH,
|
||||
startTime: 0,
|
||||
endTime,
|
||||
});
|
||||
getTimeSinceStartup().then((endTime) => {
|
||||
this.metrics.push({
|
||||
extra,
|
||||
name: PERF_MARKERS.START_SINCE_LAUNCH,
|
||||
startTime: 0,
|
||||
endTime,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
remove(names = []) {
|
||||
|
||||
@@ -322,6 +322,7 @@
|
||||
"mobile.loading_options": "Зареждане на опции ...",
|
||||
"mobile.loading_posts": "Зареждане на съобщения ...",
|
||||
"mobile.login_options.choose_title": "Изберете вашия метод за вход",
|
||||
"mobile.long_post_title": "{channelName} - Публикуване",
|
||||
"mobile.mailTo.error.text": "Неуспешно отваряне клиент за ел. поща.",
|
||||
"mobile.mailTo.error.title": "Грешка",
|
||||
"mobile.managed.blocked_by": "Блокиран от {vendor}",
|
||||
@@ -607,6 +608,8 @@
|
||||
"post_info.del": "Изтрий",
|
||||
"post_info.edit": "Редактирай",
|
||||
"post_info.guest": "ГОСТ",
|
||||
"post_info.message.show_less": "Показване на по-малко",
|
||||
"post_info.message.show_more": "Покажи повече",
|
||||
"post_info.system": "Система",
|
||||
"post_message_view.edited": "(редактирано)",
|
||||
"posts_view.newMsg": "Нови съобщения",
|
||||
|
||||
@@ -288,6 +288,7 @@
|
||||
"mobile.loading_options": "Lade Optionen...",
|
||||
"mobile.loading_posts": "Lade Nachrichten...",
|
||||
"mobile.login_options.choose_title": "Wählen Sie ihre Anmeldemethode",
|
||||
"mobile.long_post_title": "{channelName} - Nachricht",
|
||||
"mobile.mailTo.error.title": "Fehler",
|
||||
"mobile.managed.blocked_by": "Blockiert durch {vendor}",
|
||||
"mobile.managed.exit": "Beenden",
|
||||
@@ -539,6 +540,8 @@
|
||||
"post_info.del": "Löschen",
|
||||
"post_info.edit": "Bearbeiten",
|
||||
"post_info.guest": "GAST",
|
||||
"post_info.message.show_less": "Weniger anzeigen",
|
||||
"post_info.message.show_more": "Mehr anzeigen",
|
||||
"post_info.system": "System",
|
||||
"post_message_view.edited": "(bearbeitet)",
|
||||
"posts_view.newMsg": "Neue Nachrichten",
|
||||
|
||||
@@ -125,15 +125,6 @@
|
||||
"create_comment.addComment": "Add a comment...",
|
||||
"create_post.deactivated": "You are viewing an archived channel with a deactivated user.",
|
||||
"create_post.write": "Write to {channelDisplayName}",
|
||||
"custom_status.failure_message": "Failed to update status. Try again",
|
||||
"custom_status.set_status": "Set a status",
|
||||
"custom_status.suggestions.in_a_meeting": "In a meeting",
|
||||
"custom_status.suggestions.on_a_vacation": "Away on holiday",
|
||||
"custom_status.suggestions.out_for_lunch": "Out for lunch",
|
||||
"custom_status.suggestions.out_sick": "Out sick",
|
||||
"custom_status.suggestions.recent_title": "RECENT",
|
||||
"custom_status.suggestions.title": "SUGGESTIONS",
|
||||
"custom_status.suggestions.working_from_home": "Working from home",
|
||||
"date_separator.today": "Today",
|
||||
"date_separator.yesterday": "Yesterday",
|
||||
"edit_post.editPost": "Edit the post...",
|
||||
@@ -261,8 +252,6 @@
|
||||
"mobile.create_channel.public": "New Public Channel",
|
||||
"mobile.create_post.read_only": "This channel is read-only",
|
||||
"mobile.custom_list.no_results": "No Results",
|
||||
"mobile.custom_status.choose_emoji": "Choose an emoji",
|
||||
"mobile.custom_status.modal_confirm": "Done",
|
||||
"mobile.display_settings.sidebar": "Sidebar",
|
||||
"mobile.display_settings.theme": "Theme",
|
||||
"mobile.document_preview.failed_description": "An error occurred while opening the document. Please make sure you have a {fileType} viewer installed and try again.\n",
|
||||
@@ -333,6 +322,7 @@
|
||||
"mobile.loading_options": "Loading Options...",
|
||||
"mobile.loading_posts": "Loading messages...",
|
||||
"mobile.login_options.choose_title": "Choose your login method",
|
||||
"mobile.long_post_title": "{channelName} - Post",
|
||||
"mobile.mailTo.error.text": "Unable to open an email client.",
|
||||
"mobile.mailTo.error.title": "Error",
|
||||
"mobile.managed.blocked_by": "Blocked by {vendor}",
|
||||
@@ -469,7 +459,7 @@
|
||||
"mobile.reset_status.alert_cancel": "Cancel",
|
||||
"mobile.reset_status.alert_ok": "Ok",
|
||||
"mobile.reset_status.title_ooo": "Disable 'Out Of Office'?",
|
||||
"mobile.retry_message": "Fetching messages failed. Tap here to try again.",
|
||||
"mobile.retry_message": "Refreshing messages failed. Pull up to try again.",
|
||||
"mobile.routes.channelInfo": "Info",
|
||||
"mobile.routes.channelInfo.createdBy": "Created by {creator} on ",
|
||||
"mobile.routes.channelInfo.delete_channel": "Archive Channel",
|
||||
@@ -481,7 +471,6 @@
|
||||
"mobile.routes.channel_members.action_message_confirm": "Are you sure you want to remove the selected members from the channel?",
|
||||
"mobile.routes.code": "{language} Code",
|
||||
"mobile.routes.code.noLanguage": "Code",
|
||||
"mobile.routes.custom_status": "Set a Status",
|
||||
"mobile.routes.edit_profile": "Edit Profile",
|
||||
"mobile.routes.login": "Login",
|
||||
"mobile.routes.loginOptions": "Login Chooser",
|
||||
@@ -619,6 +608,8 @@
|
||||
"post_info.del": "Delete",
|
||||
"post_info.edit": "Edit",
|
||||
"post_info.guest": "GUEST",
|
||||
"post_info.message.show_less": "Show less",
|
||||
"post_info.message.show_more": "Show more",
|
||||
"post_info.system": "System",
|
||||
"post_message_view.edited": "(edited)",
|
||||
"posts_view.newMsg": "New Messages",
|
||||
@@ -677,7 +668,6 @@
|
||||
"user.settings.general.lastName": "Last Name",
|
||||
"user.settings.general.nickname": "Nickname",
|
||||
"user.settings.general.position": "Position",
|
||||
"user.settings.general.status": "Status",
|
||||
"user.settings.general.username": "Username",
|
||||
"user.settings.modal.display": "Display",
|
||||
"user.settings.modal.notifications": "Notifications",
|
||||
|
||||
@@ -322,6 +322,7 @@
|
||||
"mobile.loading_options": "Cargando Opciones...",
|
||||
"mobile.loading_posts": "Cargando Mensajes...",
|
||||
"mobile.login_options.choose_title": "Selecciona un método para iniciar sesión",
|
||||
"mobile.long_post_title": "{channelName} - Mensaje",
|
||||
"mobile.mailTo.error.text": "No se pudo abrir la app de correos electrónicos.",
|
||||
"mobile.mailTo.error.title": "Error",
|
||||
"mobile.managed.blocked_by": "Bloqueado por {vendor}",
|
||||
@@ -607,6 +608,8 @@
|
||||
"post_info.del": "Eliminar",
|
||||
"post_info.edit": "Editar",
|
||||
"post_info.guest": "HUÉSPEDES",
|
||||
"post_info.message.show_less": "Ver menos",
|
||||
"post_info.message.show_more": "Ver más",
|
||||
"post_info.system": "Sistema",
|
||||
"post_message_view.edited": "(editado)",
|
||||
"posts_view.newMsg": "Nuevos Mensajes",
|
||||
|
||||
@@ -310,6 +310,7 @@
|
||||
"mobile.loading_options": "Chargement des paramètres...",
|
||||
"mobile.loading_posts": "Chargement des messages...",
|
||||
"mobile.login_options.choose_title": "Spécifiez votre méthode de connexion",
|
||||
"mobile.long_post_title": "{channelName} - Publication",
|
||||
"mobile.mailTo.error.text": "Impossible d'ouvrir un client de messagerie.",
|
||||
"mobile.mailTo.error.title": "Erreur",
|
||||
"mobile.managed.blocked_by": "Bloqué par {vendor}",
|
||||
@@ -591,6 +592,8 @@
|
||||
"post_info.del": "Supprimer",
|
||||
"post_info.edit": "Modifier",
|
||||
"post_info.guest": "INVITÉ",
|
||||
"post_info.message.show_less": "Afficher moins",
|
||||
"post_info.message.show_more": "Afficher plus",
|
||||
"post_info.system": "Système",
|
||||
"post_message_view.edited": "(édité)",
|
||||
"posts_view.newMsg": "Nouveaux messages",
|
||||
|
||||
@@ -322,6 +322,7 @@
|
||||
"mobile.loading_options": "Beállítások betöltése...",
|
||||
"mobile.loading_posts": "Üzenetek betöltése...",
|
||||
"mobile.login_options.choose_title": "Válassza ki a bejelentkezési módját",
|
||||
"mobile.long_post_title": "{channelName} - Küldés",
|
||||
"mobile.mailTo.error.text": "Nem lehet megnyitni az email kliens-t.",
|
||||
"mobile.mailTo.error.title": "Hiba",
|
||||
"mobile.managed.blocked_by": "Letiltotta: {vendor}",
|
||||
@@ -607,6 +608,8 @@
|
||||
"post_info.del": "Törlés",
|
||||
"post_info.edit": "Szerkesztés",
|
||||
"post_info.guest": "VENDÉG",
|
||||
"post_info.message.show_less": "Kevesebb",
|
||||
"post_info.message.show_more": "Több",
|
||||
"post_info.system": "Rendszer",
|
||||
"post_message_view.edited": "(szerkesztett)",
|
||||
"posts_view.newMsg": "Új üzenetek",
|
||||
|
||||
@@ -273,6 +273,7 @@
|
||||
"mobile.loading_options": "Carimento Opzioni...",
|
||||
"mobile.loading_posts": "Caricamento messaggi...",
|
||||
"mobile.login_options.choose_title": "Seleziona il tuo metodo di login",
|
||||
"mobile.long_post_title": "{channelName} - Pubblicazione",
|
||||
"mobile.managed.blocked_by": "Bloccati da {vendor}",
|
||||
"mobile.managed.exit": "Esci",
|
||||
"mobile.managed.jailbreak": "Dispositivi con jailbreak considerati non sicuri da {vendor}, per favore uscire dall'app.",
|
||||
@@ -532,6 +533,8 @@
|
||||
"post_info.del": "Cancella",
|
||||
"post_info.edit": "Modifica",
|
||||
"post_info.guest": "OSPITE",
|
||||
"post_info.message.show_less": "Mostra di meno",
|
||||
"post_info.message.show_more": "Mostra di più",
|
||||
"post_info.system": "Sistema",
|
||||
"post_message_view.edited": "(modificato)",
|
||||
"posts_view.newMsg": "Nuovo Messaggio",
|
||||
|
||||
@@ -125,15 +125,6 @@
|
||||
"create_comment.addComment": "コメントを追加する...",
|
||||
"create_post.deactivated": "無効化されたユーザーのいるアーカイブされたチャンネルを見ています。",
|
||||
"create_post.write": "{channelDisplayName}へ投稿する",
|
||||
"custom_status.failure_message": "ステータスを更新できませんでした。再度試してみてください",
|
||||
"custom_status.set_status": "ステータスを設定する",
|
||||
"custom_status.suggestions.in_a_meeting": "ミーティング中",
|
||||
"custom_status.suggestions.on_a_vacation": "休暇中",
|
||||
"custom_status.suggestions.out_for_lunch": "ランチ中",
|
||||
"custom_status.suggestions.out_sick": "病欠",
|
||||
"custom_status.suggestions.recent_title": "最新",
|
||||
"custom_status.suggestions.title": "おすすめ",
|
||||
"custom_status.suggestions.working_from_home": "自宅での作業",
|
||||
"date_separator.today": "今日",
|
||||
"date_separator.yesterday": "昨日",
|
||||
"edit_post.editPost": "投稿を編集する...",
|
||||
@@ -261,8 +252,6 @@
|
||||
"mobile.create_channel.public": "新しい公開チャンネル",
|
||||
"mobile.create_post.read_only": "このチャンネルは読み取り専用です",
|
||||
"mobile.custom_list.no_results": "該当するものはありません",
|
||||
"mobile.custom_status.choose_emoji": "絵文字を選択する",
|
||||
"mobile.custom_status.modal_confirm": "完了",
|
||||
"mobile.display_settings.sidebar": "サイドバー",
|
||||
"mobile.display_settings.theme": "テーマ",
|
||||
"mobile.document_preview.failed_description": "文書を開く際にエラーが発生しました。{fileType}ビューワーがインストールされていることを確認し、再度実行してください。\n",
|
||||
@@ -333,6 +322,7 @@
|
||||
"mobile.loading_options": "オプションを読み込んでいます...",
|
||||
"mobile.loading_posts": "メッセージをロードしています...",
|
||||
"mobile.login_options.choose_title": "ログイン方法を選択してください",
|
||||
"mobile.long_post_title": "{channelName} - 投稿",
|
||||
"mobile.mailTo.error.text": "電子メールクライアントを開けませんでした。",
|
||||
"mobile.mailTo.error.title": "エラー",
|
||||
"mobile.managed.blocked_by": "{vendor}によりブロックされました",
|
||||
@@ -469,7 +459,7 @@
|
||||
"mobile.reset_status.alert_cancel": "キャンセル",
|
||||
"mobile.reset_status.alert_ok": "了解",
|
||||
"mobile.reset_status.title_ooo": "外出中を無効化しますか?",
|
||||
"mobile.retry_message": "メッセージを取得できませんでした。再度更新するにはここをタップしてください。",
|
||||
"mobile.retry_message": "メッセージを更新できませんでした。再度更新するには画面を引き上げてください。",
|
||||
"mobile.routes.channelInfo": "情報",
|
||||
"mobile.routes.channelInfo.createdBy": "{creator} によって作成 ",
|
||||
"mobile.routes.channelInfo.delete_channel": "チャンネルをアーカイブする",
|
||||
@@ -481,7 +471,6 @@
|
||||
"mobile.routes.channel_members.action_message_confirm": "本当に選択したメンバーをチャンネルから削除しますか?",
|
||||
"mobile.routes.code": "{language} コード",
|
||||
"mobile.routes.code.noLanguage": "コード",
|
||||
"mobile.routes.custom_status": "ステータスを設定する",
|
||||
"mobile.routes.edit_profile": "プロフィールを編集する",
|
||||
"mobile.routes.login": "ログイン",
|
||||
"mobile.routes.loginOptions": "ログイン選択",
|
||||
@@ -496,7 +485,6 @@
|
||||
"mobile.routes.user_profile": "プロフィール",
|
||||
"mobile.routes.user_profile.edit": "編集する",
|
||||
"mobile.routes.user_profile.local_time": "ローカルタイム",
|
||||
"mobile.routes.user_profile.organization": "ORGANIZATION",
|
||||
"mobile.routes.user_profile.send_message": "メッセージを送信",
|
||||
"mobile.search.after_modifier_description": "指定された日付以降の投稿を探す",
|
||||
"mobile.search.before_modifier_description": "指定された日付以前の投稿を探す",
|
||||
@@ -579,10 +567,8 @@
|
||||
"more_channels.dropdownTitle": "表示する",
|
||||
"more_channels.noMore": "参加できるチャンネルがありません",
|
||||
"more_channels.publicChannels": "公開チャンネル",
|
||||
"more_channels.sharedChannels": "共有チャンネル",
|
||||
"more_channels.showArchivedChannels": "表示: アーカイブチャンネル",
|
||||
"more_channels.showPublicChannels": "表示: 公開チャンネル",
|
||||
"more_channels.showSharedChannels": "表示: 共有チャンネル",
|
||||
"more_channels.title": "他のチャンネル",
|
||||
"msg_typing.areTyping": "{users}と{last}が入力しています...",
|
||||
"msg_typing.isTyping": "{user}が入力しています...",
|
||||
@@ -619,6 +605,8 @@
|
||||
"post_info.del": "削除",
|
||||
"post_info.edit": "編集する",
|
||||
"post_info.guest": "ゲスト",
|
||||
"post_info.message.show_less": "表示を少なくする",
|
||||
"post_info.message.show_more": "さらに表示",
|
||||
"post_info.system": "システム",
|
||||
"post_message_view.edited": "(編集済)",
|
||||
"posts_view.newMsg": "新しいメッセージ",
|
||||
@@ -677,7 +665,6 @@
|
||||
"user.settings.general.lastName": "苗字(ラストネーム)",
|
||||
"user.settings.general.nickname": "ニックネーム",
|
||||
"user.settings.general.position": "役職",
|
||||
"user.settings.general.status": "ステータス",
|
||||
"user.settings.general.username": "ユーザー名",
|
||||
"user.settings.modal.display": "表示",
|
||||
"user.settings.modal.notifications": "通知",
|
||||
|
||||
@@ -286,6 +286,7 @@
|
||||
"mobile.loading_options": "Loading Options...",
|
||||
"mobile.loading_posts": "메시지 불러오는 중...",
|
||||
"mobile.login_options.choose_title": "Choose your login method",
|
||||
"mobile.long_post_title": "{channelName} - Post",
|
||||
"mobile.mailTo.error.title": "에러",
|
||||
"mobile.managed.blocked_by": "Blocked by {vendor}",
|
||||
"mobile.managed.exit": "종료",
|
||||
@@ -556,6 +557,8 @@
|
||||
"post_info.del": "삭제",
|
||||
"post_info.edit": "편집",
|
||||
"post_info.guest": "게스트",
|
||||
"post_info.message.show_less": "감추기",
|
||||
"post_info.message.show_more": "더보기",
|
||||
"post_info.system": "시스템",
|
||||
"post_message_view.edited": "(수정됨)",
|
||||
"posts_view.newMsg": "새로운 메시지",
|
||||
|
||||
@@ -125,15 +125,6 @@
|
||||
"create_comment.addComment": "Voeg commentaar toe...",
|
||||
"create_post.deactivated": "Je bekijkt een gearchiveerd kanaal met een gedeactiveerde gebruiker.",
|
||||
"create_post.write": "Schrijven naar {channelDisplayName}",
|
||||
"custom_status.failure_message": "Het bijwerken van de status is mislukt. Probeer opnieuw",
|
||||
"custom_status.set_status": "Een status instellen",
|
||||
"custom_status.suggestions.in_a_meeting": "In vergadering",
|
||||
"custom_status.suggestions.on_a_vacation": "Op vakantie",
|
||||
"custom_status.suggestions.out_for_lunch": "Lunch",
|
||||
"custom_status.suggestions.out_sick": "Ziek",
|
||||
"custom_status.suggestions.recent_title": "RECENT",
|
||||
"custom_status.suggestions.title": "SUGGESTIES",
|
||||
"custom_status.suggestions.working_from_home": "Thuiswerk",
|
||||
"date_separator.today": "Vandaag",
|
||||
"date_separator.yesterday": "Gisteren",
|
||||
"edit_post.editPost": "Bewerk het bericht...",
|
||||
@@ -261,8 +252,6 @@
|
||||
"mobile.create_channel.public": "Nieuw Publiek kanaal",
|
||||
"mobile.create_post.read_only": "Dit kanaal is alleen-lezen",
|
||||
"mobile.custom_list.no_results": "Geen resultaten",
|
||||
"mobile.custom_status.choose_emoji": "Kies een emoticon",
|
||||
"mobile.custom_status.modal_confirm": "Klaar",
|
||||
"mobile.display_settings.sidebar": "Zijbalk",
|
||||
"mobile.display_settings.theme": "Thema",
|
||||
"mobile.document_preview.failed_description": "Er is een fout opgetreden tijdens het openen van het document. Zorg ervoor dat je een {fileType} - viewer geïnstalleerd en probeer het opnieuw. \n",
|
||||
@@ -333,6 +322,7 @@
|
||||
"mobile.loading_options": "Opties aan het laden...",
|
||||
"mobile.loading_posts": "Berichten aan het laden...",
|
||||
"mobile.login_options.choose_title": "Kies je loginmethode",
|
||||
"mobile.long_post_title": "{channelName} - Bericht",
|
||||
"mobile.mailTo.error.text": "Kan het e-mailprogramma niet openen.",
|
||||
"mobile.mailTo.error.title": "Fout",
|
||||
"mobile.managed.blocked_by": "Geblokkeerd door {vendor}",
|
||||
@@ -469,7 +459,7 @@
|
||||
"mobile.reset_status.alert_cancel": "Annuleren",
|
||||
"mobile.reset_status.alert_ok": "Ok",
|
||||
"mobile.reset_status.title_ooo": "\"Out of Office\" Uitschakelen?",
|
||||
"mobile.retry_message": "Het ophalen van berichten is mislukt. Tik hier om het opnieuw te proberen.",
|
||||
"mobile.retry_message": "Het vernieuwen van berichten is mislukt. Probeer het nog eens.",
|
||||
"mobile.routes.channelInfo": "Info",
|
||||
"mobile.routes.channelInfo.createdBy": "Gemaakt door {creator} op ",
|
||||
"mobile.routes.channelInfo.delete_channel": "Kanaal Archiveren",
|
||||
@@ -481,7 +471,6 @@
|
||||
"mobile.routes.channel_members.action_message_confirm": "Weet je zeker dat je de geselecteerde leden uit het kanaal wilt verwijderen?",
|
||||
"mobile.routes.code": "{language} Code",
|
||||
"mobile.routes.code.noLanguage": "Code",
|
||||
"mobile.routes.custom_status": "Een status instellen",
|
||||
"mobile.routes.edit_profile": "Bewerk Profiel",
|
||||
"mobile.routes.login": "Aanmelden",
|
||||
"mobile.routes.loginOptions": "Aanmeldkiezer",
|
||||
@@ -619,6 +608,8 @@
|
||||
"post_info.del": "Verwijderen",
|
||||
"post_info.edit": "Bewerken",
|
||||
"post_info.guest": "GAST",
|
||||
"post_info.message.show_less": "Toon Minder",
|
||||
"post_info.message.show_more": "Toon Meer",
|
||||
"post_info.system": "Systeem",
|
||||
"post_message_view.edited": "(bewerkt)",
|
||||
"posts_view.newMsg": "Nieuwe Berichten",
|
||||
@@ -677,7 +668,6 @@
|
||||
"user.settings.general.lastName": "Achternaam",
|
||||
"user.settings.general.nickname": "Roepnaam",
|
||||
"user.settings.general.position": "Positie",
|
||||
"user.settings.general.status": "Status",
|
||||
"user.settings.general.username": "Gebruikersnaam",
|
||||
"user.settings.modal.display": "Weergave",
|
||||
"user.settings.modal.notifications": "Meldingen",
|
||||
|
||||
@@ -260,6 +260,7 @@
|
||||
"mobile.loading_options": "Wczytuję Opcje...",
|
||||
"mobile.loading_posts": "Wczytuję Wiadomości...",
|
||||
"mobile.login_options.choose_title": "Wybierz metodę logowania",
|
||||
"mobile.long_post_title": "{channelName} - Wiadomość",
|
||||
"mobile.managed.blocked_by": "Zablokowany przez {vendor}",
|
||||
"mobile.managed.exit": "Wyjście",
|
||||
"mobile.managed.jailbreak": "Urządzenia z Jailbreakiem nie są zaufane przez {vendor}, proszę wyjść z aplikacji.",
|
||||
@@ -502,6 +503,8 @@
|
||||
"post_info.del": "Usuń",
|
||||
"post_info.edit": "Edytuj",
|
||||
"post_info.guest": "GOŚĆ",
|
||||
"post_info.message.show_less": "Pokaż Mniej",
|
||||
"post_info.message.show_more": "Pokaż Więcej",
|
||||
"post_info.system": "System",
|
||||
"post_message_view.edited": "(edytowany)",
|
||||
"posts_view.newMsg": "Nowe Wiadomości",
|
||||
|
||||
@@ -322,6 +322,7 @@
|
||||
"mobile.loading_options": "Carregando Opções...",
|
||||
"mobile.loading_posts": "Carregando Mensagens...",
|
||||
"mobile.login_options.choose_title": "Escolha seu método de login",
|
||||
"mobile.long_post_title": "{channelName} - Post",
|
||||
"mobile.mailTo.error.text": "Não foi possível abrir um cliente de e-mail.",
|
||||
"mobile.mailTo.error.title": "Erro",
|
||||
"mobile.managed.blocked_by": "Bloqueado por {vendor}",
|
||||
@@ -604,6 +605,8 @@
|
||||
"post_info.del": "Deletar",
|
||||
"post_info.edit": "Editar",
|
||||
"post_info.guest": "CONVIDADO",
|
||||
"post_info.message.show_less": "Mostrar menos",
|
||||
"post_info.message.show_more": "Mostrar mais",
|
||||
"post_info.system": "Sistema",
|
||||
"post_message_view.edited": "(editado)",
|
||||
"posts_view.newMsg": "Novas Mensagens",
|
||||
|
||||
@@ -322,6 +322,7 @@
|
||||
"mobile.loading_options": "Încărcare Opțiuni...",
|
||||
"mobile.loading_posts": "Se încarcă mesajele ...",
|
||||
"mobile.login_options.choose_title": "Alegeți metoda dvs. de conectare",
|
||||
"mobile.long_post_title": "{channelName} - Postați",
|
||||
"mobile.mailTo.error.text": "Nu se poate deschide un client de e-mail.",
|
||||
"mobile.mailTo.error.title": "Eroare",
|
||||
"mobile.managed.blocked_by": "Blocat de {vendor}",
|
||||
@@ -604,6 +605,8 @@
|
||||
"post_info.del": "Șterge",
|
||||
"post_info.edit": "Editați",
|
||||
"post_info.guest": "OASPETE",
|
||||
"post_info.message.show_less": "Afișați mai puține",
|
||||
"post_info.message.show_more": "Detalii",
|
||||
"post_info.system": "Sistem",
|
||||
"post_message_view.edited": "(editat)",
|
||||
"posts_view.newMsg": "Mesaje noi",
|
||||
|
||||
@@ -322,6 +322,7 @@
|
||||
"mobile.loading_options": "Загрузка опций...",
|
||||
"mobile.loading_posts": "Загрузка сообщений...",
|
||||
"mobile.login_options.choose_title": "Выберите метод входа",
|
||||
"mobile.long_post_title": "{channelName} - Сообщение",
|
||||
"mobile.mailTo.error.text": "Невозможно открыть почтовый клиент.",
|
||||
"mobile.mailTo.error.title": "Ошибка",
|
||||
"mobile.managed.blocked_by": "Заблокирован {vendor}",
|
||||
@@ -604,6 +605,8 @@
|
||||
"post_info.del": "Удалить",
|
||||
"post_info.edit": "Изменить",
|
||||
"post_info.guest": "ГОСТЬ",
|
||||
"post_info.message.show_less": "Показать меньше",
|
||||
"post_info.message.show_more": "Показать больше",
|
||||
"post_info.system": "Система",
|
||||
"post_message_view.edited": "(отредактировано)",
|
||||
"posts_view.newMsg": "Новые сообщения",
|
||||
|
||||
@@ -125,15 +125,6 @@
|
||||
"create_comment.addComment": "Lägg till en kommentar...",
|
||||
"create_post.deactivated": "Du visar en arkiverad kanal med ett avstängt användarkonto.",
|
||||
"create_post.write": "Skriv i kanalen {channelDisplayName}",
|
||||
"custom_status.failure_message": "Statusuppdateringen misslyckades. Försök igen",
|
||||
"custom_status.set_status": "Ange status",
|
||||
"custom_status.suggestions.in_a_meeting": "I möte",
|
||||
"custom_status.suggestions.on_a_vacation": "På ledighet",
|
||||
"custom_status.suggestions.out_for_lunch": "På lunch",
|
||||
"custom_status.suggestions.out_sick": "Sjuk",
|
||||
"custom_status.suggestions.recent_title": "NYLIGEN",
|
||||
"custom_status.suggestions.title": "FÖRSLAG",
|
||||
"custom_status.suggestions.working_from_home": "Arbetar hemifrån",
|
||||
"date_separator.today": "Idag",
|
||||
"date_separator.yesterday": "Igår",
|
||||
"edit_post.editPost": "Redigera meddelandet...",
|
||||
@@ -261,8 +252,6 @@
|
||||
"mobile.create_channel.public": "Publika kanaler",
|
||||
"mobile.create_post.read_only": "Kanalen är read-only",
|
||||
"mobile.custom_list.no_results": "Inga resultat",
|
||||
"mobile.custom_status.choose_emoji": "Välj en emoji",
|
||||
"mobile.custom_status.modal_confirm": "Klar",
|
||||
"mobile.display_settings.sidebar": "Sidofält",
|
||||
"mobile.display_settings.theme": "Tema",
|
||||
"mobile.document_preview.failed_description": "Ett fel inträffade när dokumentet skulle öppnas. Kontrollera att du har {fileType}-visare installerad och försök igen.\n",
|
||||
@@ -333,6 +322,7 @@
|
||||
"mobile.loading_options": "Hämtar alternativ...",
|
||||
"mobile.loading_posts": "Hämtar meddelanden...",
|
||||
"mobile.login_options.choose_title": "Välj inloggningsmetod",
|
||||
"mobile.long_post_title": "{channelName} - Post",
|
||||
"mobile.mailTo.error.text": "Kunde inte öppna en mejlklient.",
|
||||
"mobile.mailTo.error.title": "Fel",
|
||||
"mobile.managed.blocked_by": "Blockerad av {vendor}",
|
||||
@@ -469,7 +459,7 @@
|
||||
"mobile.reset_status.alert_cancel": "Avbryt",
|
||||
"mobile.reset_status.alert_ok": "Ok",
|
||||
"mobile.reset_status.title_ooo": "Inaktivera \"Ej på kontoret\"?",
|
||||
"mobile.retry_message": "Hämta meddelanden misslyckades. Tryck här för att försöka igen.",
|
||||
"mobile.retry_message": "Misslyckades att ladda om meddelanden. Dra uppåt för att försöka igen.",
|
||||
"mobile.routes.channelInfo": "Info",
|
||||
"mobile.routes.channelInfo.createdBy": "Skapad av {creator} den ",
|
||||
"mobile.routes.channelInfo.delete_channel": "Arkivera kanaler",
|
||||
@@ -481,7 +471,6 @@
|
||||
"mobile.routes.channel_members.action_message_confirm": "Är du säker på att du vill ta bort de valda medlemmarna från kanalen?",
|
||||
"mobile.routes.code": "{language} kod",
|
||||
"mobile.routes.code.noLanguage": "Kod",
|
||||
"mobile.routes.custom_status": "Ange status",
|
||||
"mobile.routes.edit_profile": "Editera profil",
|
||||
"mobile.routes.login": "Logga in",
|
||||
"mobile.routes.loginOptions": "Välj inloggningsmetod",
|
||||
@@ -619,6 +608,8 @@
|
||||
"post_info.del": "Ta bort",
|
||||
"post_info.edit": "Redigera",
|
||||
"post_info.guest": "GÄST",
|
||||
"post_info.message.show_less": "Visa mindre",
|
||||
"post_info.message.show_more": "Visa mer",
|
||||
"post_info.system": "System",
|
||||
"post_message_view.edited": "(redigerad)",
|
||||
"posts_view.newMsg": "Nya meddelanden",
|
||||
@@ -677,7 +668,6 @@
|
||||
"user.settings.general.lastName": "Efternamn",
|
||||
"user.settings.general.nickname": "Alias",
|
||||
"user.settings.general.position": "Position",
|
||||
"user.settings.general.status": "Status",
|
||||
"user.settings.general.username": "Användarnamn",
|
||||
"user.settings.modal.display": "Utseende",
|
||||
"user.settings.modal.notifications": "Meddelanden",
|
||||
|
||||
@@ -125,15 +125,6 @@
|
||||
"create_comment.addComment": "Yorum yazın...",
|
||||
"create_post.deactivated": "Devre dışı bırakılmış bir kullanıcı ile arşivlenmiş bir kanala bakıyorsunuz.",
|
||||
"create_post.write": "{channelDisplayName} kanalına yazın",
|
||||
"custom_status.failure_message": "Durum güncellenemedi. Yeniden deneyin",
|
||||
"custom_status.set_status": "Bir durum ayarlayın",
|
||||
"custom_status.suggestions.in_a_meeting": "Toplantıda",
|
||||
"custom_status.suggestions.on_a_vacation": "Tatilde",
|
||||
"custom_status.suggestions.out_for_lunch": "Öğle yemeğinde",
|
||||
"custom_status.suggestions.out_sick": "Hasta",
|
||||
"custom_status.suggestions.recent_title": "SON KULLANILANLAR",
|
||||
"custom_status.suggestions.title": "ÖNERİLER",
|
||||
"custom_status.suggestions.working_from_home": "Evden çalışıyor",
|
||||
"date_separator.today": "Bugün",
|
||||
"date_separator.yesterday": "Dün",
|
||||
"edit_post.editPost": "İletiyi düzenle...",
|
||||
@@ -261,8 +252,6 @@
|
||||
"mobile.create_channel.public": "Yeni Herkese Açık Kanal",
|
||||
"mobile.create_post.read_only": "Bu kanal salt okunur",
|
||||
"mobile.custom_list.no_results": "Herhangi bir sonuç bulunamadı",
|
||||
"mobile.custom_status.choose_emoji": "Bir emoji seçin",
|
||||
"mobile.custom_status.modal_confirm": "Tamam",
|
||||
"mobile.display_settings.sidebar": "Yan Çubuk",
|
||||
"mobile.display_settings.theme": "Tema",
|
||||
"mobile.document_preview.failed_description": "Belge açılırken bir sorun çıktı. Lütfen {fileType} türündeki dosyalar için bir görüntüleyicinin kurulmuş olduğundan emin olun ve yeniden deneyin.\n",
|
||||
@@ -333,6 +322,7 @@
|
||||
"mobile.loading_options": "Ayarlar Yükleniyor...",
|
||||
"mobile.loading_posts": "İletiler yükleniyor...",
|
||||
"mobile.login_options.choose_title": "Oturum açma yönteminizi seçin",
|
||||
"mobile.long_post_title": "{channelName} - İleti",
|
||||
"mobile.mailTo.error.text": "Bir e-posta istemcisi açılamadı.",
|
||||
"mobile.mailTo.error.title": "Hata",
|
||||
"mobile.managed.blocked_by": "{vendor} tarafından engellenmiş",
|
||||
@@ -469,7 +459,7 @@
|
||||
"mobile.reset_status.alert_cancel": "İptal",
|
||||
"mobile.reset_status.alert_ok": "Tamam",
|
||||
"mobile.reset_status.title_ooo": "\"Ofis Dışında\" devre dışı bırakılsın mı?",
|
||||
"mobile.retry_message": "İletiler alınamadı. Yeniden denemek için buraya dokunun.",
|
||||
"mobile.retry_message": "İletiler yenilenemedi. Yeniden denemek için yukarı çekin.",
|
||||
"mobile.routes.channelInfo": "Bilgiler",
|
||||
"mobile.routes.channelInfo.createdBy": "{creator} tarafından şu zamanda oluşturuldu ",
|
||||
"mobile.routes.channelInfo.delete_channel": "Kanalı Arşivle",
|
||||
@@ -481,7 +471,6 @@
|
||||
"mobile.routes.channel_members.action_message_confirm": "Seçilmiş üyeleri kanaldan çıkarmak istediğinize emin misiniz?",
|
||||
"mobile.routes.code": "{language} Kodu",
|
||||
"mobile.routes.code.noLanguage": "Kod",
|
||||
"mobile.routes.custom_status": "Bir durum ayarlayın",
|
||||
"mobile.routes.edit_profile": "Profili Düzenle",
|
||||
"mobile.routes.login": "Oturum Aç",
|
||||
"mobile.routes.loginOptions": "Oturum Açma Seçici",
|
||||
@@ -619,6 +608,8 @@
|
||||
"post_info.del": "Sil",
|
||||
"post_info.edit": "Düzenle",
|
||||
"post_info.guest": "KONUK",
|
||||
"post_info.message.show_less": "Daha az ayrıntı",
|
||||
"post_info.message.show_more": "Daha çok ayrıntı",
|
||||
"post_info.system": "Sistem",
|
||||
"post_message_view.edited": "(düzenlendi)",
|
||||
"posts_view.newMsg": "Yeni İletiler",
|
||||
@@ -677,7 +668,6 @@
|
||||
"user.settings.general.lastName": "Soyad",
|
||||
"user.settings.general.nickname": "Takma Ad",
|
||||
"user.settings.general.position": "Unvan",
|
||||
"user.settings.general.status": "Durum",
|
||||
"user.settings.general.username": "Kullanıcı Adı",
|
||||
"user.settings.modal.display": "Görünüm",
|
||||
"user.settings.modal.notifications": "Bildirimler",
|
||||
|
||||
@@ -278,6 +278,7 @@
|
||||
"mobile.loading_options": "Параметри завантаження...",
|
||||
"mobile.loading_posts": "Завантаження повідомлень...",
|
||||
"mobile.login_options.choose_title": "Виберіть спосіб входу",
|
||||
"mobile.long_post_title": "{channelName} - повідомлення",
|
||||
"mobile.managed.blocked_by": "Заблоковано {vendor}",
|
||||
"mobile.managed.exit": "Вхід",
|
||||
"mobile.managed.jailbreak": "Пристрої з Jailbroken не є довіреними {vendor}, вийдіть з програми.",
|
||||
@@ -534,6 +535,8 @@
|
||||
"post_info.del": "Видалити",
|
||||
"post_info.edit": "Редагувати",
|
||||
"post_info.guest": "GUEST",
|
||||
"post_info.message.show_less": "Показати менше",
|
||||
"post_info.message.show_more": "Показати більше",
|
||||
"post_info.system": "Система",
|
||||
"post_message_view.edited": "(змінено)",
|
||||
"posts_view.newMsg": "Нові повідомлення",
|
||||
|
||||
@@ -322,6 +322,7 @@
|
||||
"mobile.loading_options": "加载选项中...",
|
||||
"mobile.loading_posts": "正在加载消息...",
|
||||
"mobile.login_options.choose_title": "选择您的登入方式",
|
||||
"mobile.long_post_title": "{channelName} - 消息",
|
||||
"mobile.mailTo.error.text": "无法打开邮件客户端。",
|
||||
"mobile.mailTo.error.title": "错误",
|
||||
"mobile.managed.blocked_by": "被 {vendor} 封锁",
|
||||
@@ -604,6 +605,8 @@
|
||||
"post_info.del": "删除",
|
||||
"post_info.edit": "编辑",
|
||||
"post_info.guest": "游客",
|
||||
"post_info.message.show_less": "显示更少",
|
||||
"post_info.message.show_more": "显示更多",
|
||||
"post_info.system": "系统",
|
||||
"post_message_view.edited": "(已编辑)",
|
||||
"posts_view.newMsg": "新消息",
|
||||
|
||||
@@ -271,6 +271,7 @@
|
||||
"mobile.loading_options": "讀取選項...",
|
||||
"mobile.loading_posts": "正在載入訊息...",
|
||||
"mobile.login_options.choose_title": "選擇登入方式",
|
||||
"mobile.long_post_title": "{channelName} - 訊息",
|
||||
"mobile.managed.blocked_by": "被 {vendor} 阻擋",
|
||||
"mobile.managed.exit": "離開",
|
||||
"mobile.managed.jailbreak": "{vendor} 不信任越獄後的裝置,請關閉應用程式。",
|
||||
@@ -533,6 +534,8 @@
|
||||
"post_info.del": "刪除",
|
||||
"post_info.edit": "編輯",
|
||||
"post_info.guest": "訪客",
|
||||
"post_info.message.show_less": "顯示較少內容",
|
||||
"post_info.message.show_more": "顯示更多",
|
||||
"post_info.system": "系統",
|
||||
"post_message_view.edited": "(被編輯過)",
|
||||
"posts_view.newMsg": "新訊息",
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from '@support/ui/component';
|
||||
import {
|
||||
LoginScreen,
|
||||
LongPostScreen,
|
||||
SelectServerScreen,
|
||||
ThreadScreen,
|
||||
} from '@support/ui/screen';
|
||||
@@ -76,6 +77,14 @@ class ChannelScreen {
|
||||
return this.postList.getNewMessagesDivider();
|
||||
}
|
||||
|
||||
getLongPostItem = (postId, text, postProfileOptions = {}) => {
|
||||
return LongPostScreen.getPost(postId, text, postProfileOptions);
|
||||
}
|
||||
|
||||
getLongPostMessage = () => {
|
||||
return LongPostScreen.getPostMessage();
|
||||
}
|
||||
|
||||
getPostListPostItem = (postId, text, postProfileOptions = {}) => {
|
||||
return this.postList.getPost(postId, text, postProfileOptions);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import EditPostScreen from './edit_post';
|
||||
import EditProfileScreen from './edit_profile';
|
||||
import GeneralSettingsScreen from './general_settings';
|
||||
import LoginScreen from './login';
|
||||
import LongPostScreen from './long_post';
|
||||
import MoreChannelsScreen from './more_channels';
|
||||
import MoreDirectMessagesScreen from './more_direct_messages';
|
||||
import NotificationScreen from './notification';
|
||||
@@ -54,6 +55,7 @@ export {
|
||||
EditProfileScreen,
|
||||
GeneralSettingsScreen,
|
||||
LoginScreen,
|
||||
LongPostScreen,
|
||||
MoreChannelsScreen,
|
||||
MoreDirectMessagesScreen,
|
||||
NotificationScreen,
|
||||
|
||||
60
detox/e2e/support/ui/screen/long_post.js
Normal file
60
detox/e2e/support/ui/screen/long_post.js
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Post} from '@support/ui/component';
|
||||
|
||||
class LongPostScreen {
|
||||
testID = {
|
||||
longPostItem: 'long_post.post',
|
||||
closeLongPostButton: 'close.long_post.button',
|
||||
}
|
||||
|
||||
closeLongPostButton = element(by.id(this.testID.closeLongPostButton));
|
||||
|
||||
getPost = (postId, postMessage, postProfileOptions = {}) => {
|
||||
const {
|
||||
postItem,
|
||||
postItemBlockQuote,
|
||||
postItemEmoji,
|
||||
postItemHeaderDateTime,
|
||||
postItemHeaderDisplayName,
|
||||
postItemHeaderGuestTag,
|
||||
postItemHeaderReply,
|
||||
postItemImage,
|
||||
postItemMessage,
|
||||
postItemProfilePicture,
|
||||
postItemProfilePictureUserStatus,
|
||||
postItemShowLessButton,
|
||||
postItemShowMoreButton,
|
||||
postItemTable,
|
||||
postItemTableExpandButton,
|
||||
postItemThematicBreak,
|
||||
} = Post.getPost(this.testID.longPostItem, postId, postMessage, postProfileOptions);
|
||||
|
||||
return {
|
||||
longPostItem: postItem,
|
||||
longPostItemBlockQuote: postItemBlockQuote,
|
||||
longPostItemEmoji: postItemEmoji,
|
||||
longPostItemHeaderDateTime: postItemHeaderDateTime,
|
||||
longPostItemHeaderDisplayName: postItemHeaderDisplayName,
|
||||
longPostItemHeaderGuestTag: postItemHeaderGuestTag,
|
||||
longPostItemHeaderReply: postItemHeaderReply,
|
||||
longPostItemImage: postItemImage,
|
||||
longPostItemMessage: postItemMessage,
|
||||
longPostItemProfilePicture: postItemProfilePicture,
|
||||
longPostItemProfilePictureUserStatus: postItemProfilePictureUserStatus,
|
||||
longPostItemShowLessButton: postItemShowLessButton,
|
||||
longPostItemShowMoreButton: postItemShowMoreButton,
|
||||
longPostItemTable: postItemTable,
|
||||
longPostItemTableExpandButton: postItemTableExpandButton,
|
||||
longPostItemThematicBreak: postItemThematicBreak,
|
||||
};
|
||||
}
|
||||
|
||||
getPostMessage = () => {
|
||||
return Post.getPostMessage(this.testID.longPostItem);
|
||||
}
|
||||
}
|
||||
|
||||
const longPostScreen = new LongPostScreen();
|
||||
export default longPostScreen;
|
||||
@@ -2,6 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {PostList} from '@support/ui/component';
|
||||
import {LongPostScreen} from '@support/ui/screen';
|
||||
|
||||
class PermalinkScreen {
|
||||
testID = {
|
||||
@@ -17,6 +18,14 @@ class PermalinkScreen {
|
||||
|
||||
postList = new PostList(this.testID.permalinkScreenPrefix);
|
||||
|
||||
getLongPostItem = (postId, text, postProfileOptions = {}) => {
|
||||
return LongPostScreen.getPost(postId, text, postProfileOptions);
|
||||
}
|
||||
|
||||
getLongPostMessage = () => {
|
||||
return LongPostScreen.getPostMessage();
|
||||
}
|
||||
|
||||
getPostListPostItem = (postId, text, postProfileOptions = {}) => {
|
||||
return this.postList.getPost(postId, text, postProfileOptions);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
PostOptions,
|
||||
SendButton,
|
||||
} from '@support/ui/component';
|
||||
import {LongPostScreen} from '@support/ui/screen';
|
||||
import {timeouts, wait} from '@support/utils';
|
||||
|
||||
class ThreadScreen {
|
||||
@@ -45,6 +46,14 @@ class ThreadScreen {
|
||||
|
||||
postList = new PostList(this.testID.threadScreenPrefix);
|
||||
|
||||
getLongPostItem = (postId, text, postProfileOptions = {}) => {
|
||||
return LongPostScreen.getPost(postId, text, postProfileOptions);
|
||||
}
|
||||
|
||||
getLongPostMessage = () => {
|
||||
return LongPostScreen.getPostMessage();
|
||||
}
|
||||
|
||||
getPostListPostItem = (postId, text, postProfileOptions = {}) => {
|
||||
return this.postList.getPost(postId, text, postProfileOptions);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
import {ChannelScreen} from '@support/ui/screen';
|
||||
import {
|
||||
ChannelScreen,
|
||||
LongPostScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {
|
||||
Channel,
|
||||
Post,
|
||||
@@ -20,6 +23,7 @@ import {isAndroid} from '@support/utils';
|
||||
describe('Message Posting', () => {
|
||||
const longMessage = 'The quick brown fox jumps over the lazy dog.'.repeat(30);
|
||||
const {
|
||||
getLongPostItem,
|
||||
getPostListPostItem,
|
||||
goToChannel,
|
||||
postMessage,
|
||||
@@ -58,21 +62,15 @@ describe('Message Posting', () => {
|
||||
it('MM-T3229 should be able to open long post via show more', async () => {
|
||||
// # Open long post via show more
|
||||
const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id);
|
||||
const {
|
||||
postListPostItem,
|
||||
postListPostItemShowLessButton,
|
||||
postListPostItemShowMoreButton,
|
||||
} = await getPostListPostItem(post.id, longMessage);
|
||||
const {postListPostItemShowMoreButton} = await getPostListPostItem(post.id, longMessage);
|
||||
await postListPostItemShowMoreButton.tap();
|
||||
|
||||
// * Verify long post is displayed
|
||||
await expect(postListPostItem).toExist();
|
||||
const {longPostItem} = getLongPostItem(post.id, longMessage);
|
||||
await expect(longPostItem).toExist();
|
||||
|
||||
// # Close long post
|
||||
await postListPostItemShowLessButton.tap();
|
||||
|
||||
// * Verify show more button is displayed again
|
||||
await expect(postListPostItemShowMoreButton).toExist();
|
||||
// # Close long post screen
|
||||
await LongPostScreen.closeLongPostButton.tap();
|
||||
});
|
||||
|
||||
it('MM-T3269 should be able to post a markdown image', async () => {
|
||||
@@ -125,21 +123,19 @@ describe('Message Posting', () => {
|
||||
createAt: lastMessageDate.getTime(),
|
||||
});
|
||||
|
||||
// # Switch channels
|
||||
await goToChannel(townSquareChannel.display_name);
|
||||
await goToChannel(testChannel.display_name);
|
||||
|
||||
// Detox is having trouble scrolling
|
||||
if (isAndroid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// * Verify last message is posted
|
||||
await goToChannel(townSquareChannel.display_name);
|
||||
await goToChannel(testChannel.display_name);
|
||||
const {postListPostItem: lastPostItem} = await getPostListPostItem(lastPost.post.id, lastMessage);
|
||||
await waitFor(lastPostItem).toBeVisible().whileElement(by.id(ChannelScreen.testID.channelPostList)).scroll(1000, 'down');
|
||||
|
||||
// * Verify user can scroll up multiple times until first matching post is seen
|
||||
const {postListPostItem: firstPostItem} = await getPostListPostItem(firstPost.post.id, firstMessage);
|
||||
await waitFor(firstPostItem).toBeVisible().whileElement(by.id(ChannelScreen.testID.channelPostList)).scroll(2000, 'up');
|
||||
await waitFor(firstPostItem).toBeVisible().whileElement(by.id(ChannelScreen.testID.channelPostList)).scroll(1000, 'up');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,8 +7,8 @@ GEM
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.1.1)
|
||||
aws-partitions (1.465.0)
|
||||
aws-sdk-core (3.114.1)
|
||||
aws-partitions (1.467.0)
|
||||
aws-sdk-core (3.114.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.239.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
@@ -16,7 +16,7 @@ GEM
|
||||
aws-sdk-kms (1.43.0)
|
||||
aws-sdk-core (~> 3, >= 3.112.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.96.0)
|
||||
aws-sdk-s3 (1.96.1)
|
||||
aws-sdk-core (~> 3, >= 3.112.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.1)
|
||||
@@ -55,7 +55,7 @@ GEM
|
||||
faraday_middleware (1.0.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.4)
|
||||
fastlane (2.184.1)
|
||||
fastlane (2.185.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
@@ -139,7 +139,7 @@ GEM
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (~> 0.14)
|
||||
highline (2.0.3)
|
||||
http-cookie (1.0.3)
|
||||
http-cookie (1.0.4)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.4.0)
|
||||
|
||||
@@ -912,7 +912,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 357;
|
||||
CURRENT_PROJECT_VERSION = 359;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -954,7 +954,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 357;
|
||||
CURRENT_PROJECT_VERSION = 359;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
ENABLE_BITCODE = NO;
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>357</string>
|
||||
<string>359</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.44.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>357</string>
|
||||
<string>359</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.44.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>357</string>
|
||||
<string>359</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java
|
||||
index 48fb5c1..3872d91 100644
|
||||
index 48fb5c1..5ca1e20 100644
|
||||
--- a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java
|
||||
+++ b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java
|
||||
@@ -3,6 +3,7 @@ package com.imagepicker;
|
||||
@@ -10,15 +10,7 @@ index 48fb5c1..3872d91 100644
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
@@ -49,6 +50,7 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
+import java.util.ArrayList;
|
||||
|
||||
import com.facebook.react.modules.core.PermissionListener;
|
||||
import com.facebook.react.modules.core.PermissionAwareActivity;
|
||||
@@ -69,6 +71,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -69,6 +70,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
public static final int REQUEST_LAUNCH_IMAGE_LIBRARY = 13002;
|
||||
public static final int REQUEST_LAUNCH_VIDEO_LIBRARY = 13003;
|
||||
public static final int REQUEST_LAUNCH_VIDEO_CAPTURE = 13004;
|
||||
@@ -26,11 +18,14 @@ index 48fb5c1..3872d91 100644
|
||||
public static final int REQUEST_PERMISSIONS_FOR_CAMERA = 14001;
|
||||
public static final int REQUEST_PERMISSIONS_FOR_LIBRARY = 14002;
|
||||
|
||||
@@ -266,26 +269,24 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -265,27 +267,22 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
{
|
||||
cameraIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, videoDurationLimit);
|
||||
}
|
||||
}
|
||||
+ else if (pickBoth) {
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
+ } else if (pickBoth) {
|
||||
+ Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
+ this.setImageCaptureUri(takePictureIntent);
|
||||
+ Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
|
||||
@@ -41,9 +36,7 @@ index 48fb5c1..3872d91 100644
|
||||
+ cameraIntent.putExtra(Intent.EXTRA_TITLE, "Choose an action");
|
||||
+ cameraIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
|
||||
+ requestCode = REQUEST_LAUNCH_MIXED_CAPTURE;
|
||||
+ }
|
||||
else
|
||||
{
|
||||
+ } else {
|
||||
requestCode = REQUEST_LAUNCH_IMAGE_CAPTURE;
|
||||
cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
|
||||
@@ -66,23 +59,24 @@ index 48fb5c1..3872d91 100644
|
||||
}
|
||||
|
||||
if (cameraIntent.resolveActivity(reactContext.getPackageManager()) == null)
|
||||
@@ -350,16 +351,19 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -349,17 +346,18 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
requestCode = REQUEST_LAUNCH_VIDEO_LIBRARY;
|
||||
libraryIntent = new Intent(Intent.ACTION_PICK);
|
||||
libraryIntent.setType("video/*");
|
||||
}
|
||||
+ else if (pickBoth) {
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
+ } else if (pickBoth) {
|
||||
+ libraryIntent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
+ libraryIntent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
+ libraryIntent.setType("image/*");
|
||||
+ libraryIntent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {"image/*", "video/*"});
|
||||
+ requestCode = REQUEST_LAUNCH_MIXED_CAPTURE;
|
||||
+ }
|
||||
else
|
||||
{
|
||||
+ } else {
|
||||
requestCode = REQUEST_LAUNCH_IMAGE_LIBRARY;
|
||||
libraryIntent = new Intent(Intent.ACTION_PICK,
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
||||
-
|
||||
|
||||
- if (pickBoth)
|
||||
- {
|
||||
- libraryIntent.setType("image/* video/*");
|
||||
@@ -91,7 +85,7 @@ index 48fb5c1..3872d91 100644
|
||||
}
|
||||
|
||||
if (libraryIntent.resolveActivity(reactContext.getPackageManager()) == null)
|
||||
@@ -385,75 +389,47 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -385,75 +383,42 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,29 +107,10 @@ index 48fb5c1..3872d91 100644
|
||||
- callback = null;
|
||||
- return;
|
||||
- }
|
||||
+ protected String getMimeType(Activity activity, Uri uri) {
|
||||
+ String mimeType = null;
|
||||
+ if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
|
||||
+ ContentResolver cr = activity.getApplicationContext().getContentResolver();
|
||||
+ mimeType = cr.getType(uri);
|
||||
+ } else {
|
||||
+ String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri
|
||||
+ .toString());
|
||||
+ mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
|
||||
+ fileExtension.toLowerCase());
|
||||
+ }
|
||||
+ return mimeType;
|
||||
+ }
|
||||
|
||||
-
|
||||
- Uri uri = null;
|
||||
- switch (requestCode)
|
||||
+ protected void extractImageFromResult(Activity activity, Uri uri, int requestCode) {
|
||||
+ String realPath = getRealPathFromURI(uri);
|
||||
+ String mime = getMimeType(activity, uri);
|
||||
+ final boolean isUrl = !TextUtils.isEmpty(realPath) &&
|
||||
+ Patterns.WEB_URL.matcher(realPath).matches();
|
||||
+ if (realPath == null || isUrl)
|
||||
{
|
||||
- {
|
||||
- case REQUEST_LAUNCH_IMAGE_CAPTURE:
|
||||
- uri = cameraCaptureURI;
|
||||
- break;
|
||||
@@ -172,20 +147,35 @@ index 48fb5c1..3872d91 100644
|
||||
- responseHelper.invokeResponse(callback);
|
||||
- callback = null;
|
||||
- return;
|
||||
-
|
||||
+ protected String getMimeType(Activity activity, Uri uri) {
|
||||
+ String mimeType = null;
|
||||
+ if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
|
||||
+ ContentResolver cr = activity.getApplicationContext().getContentResolver();
|
||||
+ mimeType = cr.getType(uri);
|
||||
+ } else {
|
||||
+ String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri.toString());
|
||||
+ mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
|
||||
+ fileExtension.toLowerCase());
|
||||
+ }
|
||||
+ return mimeType;
|
||||
+ }
|
||||
|
||||
- case REQUEST_LAUNCH_VIDEO_CAPTURE:
|
||||
- final String path = getRealPathFromURI(data.getData());
|
||||
- responseHelper.putString("uri", data.getData().toString());
|
||||
- responseHelper.putString("path", path);
|
||||
- fileScan(reactContext, path);
|
||||
+ try
|
||||
+ {
|
||||
+ protected void extractImageFromResult(Activity activity, Uri uri, int requestCode) {
|
||||
+ String realPath = getRealPathFromURI(uri);
|
||||
+ String mime = getMimeType(activity, uri);
|
||||
+ final boolean isUrl = !TextUtils.isEmpty(realPath) &&
|
||||
+ Patterns.WEB_URL.matcher(realPath).matches();
|
||||
+ if (isUrl) {
|
||||
+ try {
|
||||
+ File file = createFileFromURI(uri);
|
||||
+ realPath = file.getAbsolutePath();
|
||||
+ uri = Uri.fromFile(file);
|
||||
+ }
|
||||
+ catch (Exception e)
|
||||
+ {
|
||||
+ } catch (Exception e) {
|
||||
+ // image not in cache
|
||||
+ responseHelper.putString("error", "Could not read photo");
|
||||
+ responseHelper.putString("uri", uri.toString());
|
||||
@@ -201,7 +191,7 @@ index 48fb5c1..3872d91 100644
|
||||
final ReadExifResult result = readExifInterface(responseHelper, imageConfig);
|
||||
|
||||
if (result.error != null)
|
||||
@@ -461,6 +437,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -461,6 +426,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
removeUselessFiles(requestCode, imageConfig);
|
||||
responseHelper.invokeError(callback, result.error.getMessage());
|
||||
callback = null;
|
||||
@@ -209,7 +199,7 @@ index 48fb5c1..3872d91 100644
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -472,7 +449,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -472,7 +438,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
updatedResultResponse(uri, imageConfig.original.getAbsolutePath());
|
||||
|
||||
// don't create a new file if contraint are respected
|
||||
@@ -218,7 +208,7 @@ index 48fb5c1..3872d91 100644
|
||||
{
|
||||
responseHelper.putInt("width", initialWidth);
|
||||
responseHelper.putInt("height", initialHeight);
|
||||
@@ -481,6 +458,14 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -481,6 +447,13 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
else
|
||||
{
|
||||
imageConfig = getResizedImage(reactContext, this.options, imageConfig, initialWidth, initialHeight, requestCode);
|
||||
@@ -229,11 +219,10 @@ index 48fb5c1..3872d91 100644
|
||||
+ this.options = null;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
if (imageConfig.resized == null)
|
||||
{
|
||||
removeUselessFiles(requestCode, imageConfig);
|
||||
@@ -523,6 +508,64 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -523,6 +496,61 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
this.options = null;
|
||||
}
|
||||
|
||||
@@ -269,15 +258,12 @@ index 48fb5c1..3872d91 100644
|
||||
+ case REQUEST_LAUNCH_IMAGE_CAPTURE:
|
||||
+ extractImageFromResult(activity, cameraCaptureURI, requestCode);
|
||||
+ break;
|
||||
+
|
||||
+ case REQUEST_LAUNCH_IMAGE_LIBRARY:
|
||||
+ extractImageFromResult(activity, data.getData(), requestCode);
|
||||
+ break;
|
||||
+
|
||||
+ case REQUEST_LAUNCH_VIDEO_LIBRARY:
|
||||
+ extractVideoFromResult(data.getData());
|
||||
+ break;
|
||||
+
|
||||
+ case REQUEST_LAUNCH_MIXED_CAPTURE:
|
||||
+ case REQUEST_LAUNCH_VIDEO_CAPTURE:
|
||||
+ if (data == null || data.getData() == null) {
|
||||
@@ -298,7 +284,7 @@ index 48fb5c1..3872d91 100644
|
||||
public void invokeCustomButton(@NonNull final String action)
|
||||
{
|
||||
responseHelper.invokeCustomButton(this.callback, action);
|
||||
@@ -551,7 +594,8 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -551,7 +579,8 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
{
|
||||
return callback == null || (cameraCaptureURI == null && requestCode == REQUEST_LAUNCH_IMAGE_CAPTURE)
|
||||
|| (requestCode != REQUEST_LAUNCH_IMAGE_CAPTURE && requestCode != REQUEST_LAUNCH_IMAGE_LIBRARY
|
||||
@@ -308,7 +294,7 @@ index 48fb5c1..3872d91 100644
|
||||
}
|
||||
|
||||
private void updatedResultResponse(@Nullable final Uri uri,
|
||||
@@ -571,22 +615,23 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -571,22 +600,24 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@NonNull final Callback callback,
|
||||
@NonNull final int requestCode)
|
||||
{
|
||||
@@ -318,21 +304,21 @@ index 48fb5c1..3872d91 100644
|
||||
- .checkSelfPermission(activity, Manifest.permission.CAMERA);
|
||||
-
|
||||
- boolean permissionsGranted = false;
|
||||
-
|
||||
|
||||
+ int selfCheckResult = 0;
|
||||
switch (requestCode) {
|
||||
case REQUEST_PERMISSIONS_FOR_LIBRARY:
|
||||
- permissionsGranted = writePermission == PackageManager.PERMISSION_GRANTED;
|
||||
+ selfCheckResult = ActivityCompat
|
||||
+ .checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
+ .checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
break;
|
||||
case REQUEST_PERMISSIONS_FOR_CAMERA:
|
||||
- permissionsGranted = cameraPermission == PackageManager.PERMISSION_GRANTED && writePermission == PackageManager.PERMISSION_GRANTED;
|
||||
+ selfCheckResult = ActivityCompat
|
||||
+ .checkSelfPermission(activity, Manifest.permission.CAMERA);
|
||||
+ .checkSelfPermission(activity, Manifest.permission.CAMERA);
|
||||
+ if (selfCheckResult == PackageManager.PERMISSION_GRANTED) {
|
||||
+ selfCheckResult = ActivityCompat
|
||||
+ .checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
+ .checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
+ }
|
||||
break;
|
||||
}
|
||||
@@ -341,42 +327,52 @@ index 48fb5c1..3872d91 100644
|
||||
if (!permissionsGranted)
|
||||
{
|
||||
final Boolean dontAskAgain = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) && ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA);
|
||||
@@ -787,4 +832,22 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -787,4 +818,21 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
videoDurationLimit = options.getInt("durationLimit");
|
||||
}
|
||||
}
|
||||
+
|
||||
+ private void setImageCaptureUri(Intent cameraIntent) {
|
||||
+ final File original = createNewFile(reactContext, this.options, false);
|
||||
+ imageConfig = imageConfig.withOriginalFile(original);
|
||||
+ imageConfig = imageConfig.withOriginalFile(original);
|
||||
+
|
||||
+ if (imageConfig.original != null) {
|
||||
+ cameraCaptureURI = RealPathUtil.compatUriFromFile(reactContext, imageConfig.original);
|
||||
+ }else {
|
||||
+ responseHelper.invokeError(callback, "Couldn't get file path for photo");
|
||||
+ return;
|
||||
+ }
|
||||
+ if (cameraCaptureURI == null)
|
||||
+ {
|
||||
+ responseHelper.invokeError(callback, "Couldn't get file path for photo");
|
||||
+ return;
|
||||
+ }
|
||||
+ cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraCaptureURI);
|
||||
+ if (imageConfig.original != null) {
|
||||
+ cameraCaptureURI = RealPathUtil.compatUriFromFile(reactContext, imageConfig.original);
|
||||
+ } else {
|
||||
+ responseHelper.invokeError(callback, "Couldn't get file path for photo");
|
||||
+ return;
|
||||
+ }
|
||||
+ if (cameraCaptureURI == null) {
|
||||
+ responseHelper.invokeError(callback, "Couldn't get file path for photo");
|
||||
+ return;
|
||||
+ }
|
||||
+ cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraCaptureURI);
|
||||
+ }
|
||||
}
|
||||
diff --git a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/utils/RealPathUtil.java b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/utils/RealPathUtil.java
|
||||
index cc90dce..c1cc74d 100644
|
||||
index cc90dce..72ddc92 100644
|
||||
--- a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/utils/RealPathUtil.java
|
||||
+++ b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/utils/RealPathUtil.java
|
||||
@@ -7,16 +7,22 @@ import android.net.Uri;
|
||||
import android.os.Build;
|
||||
@@ -1,201 +1,270 @@
|
||||
package com.imagepicker.utils;
|
||||
|
||||
-import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
-import android.os.Build;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
-import android.content.ContentUris;
|
||||
+import android.provider.OpenableColumns;
|
||||
import android.content.ContentUris;
|
||||
+import android.content.ContentResolver;
|
||||
import android.os.Environment;
|
||||
+import android.os.ParcelFileDescriptor;
|
||||
+import android.webkit.MimeTypeMap;
|
||||
+import android.util.Log;
|
||||
+import android.text.TextUtils;
|
||||
+
|
||||
+import android.os.ParcelFileDescriptor;
|
||||
+
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.FileProvider;
|
||||
@@ -384,93 +380,432 @@ index cc90dce..c1cc74d 100644
|
||||
-import java.io.File;
|
||||
+import java.io.*;
|
||||
+import java.nio.channels.FileChannel;
|
||||
+import java.util.Objects;
|
||||
+
|
||||
+// Class based on the steveevers DocumentHelper https://gist.github.com/steveevers/a5af24c226f44bb8fdc3
|
||||
|
||||
public class RealPathUtil {
|
||||
|
||||
+ public static final String CACHE_DIR_NAME = "mmShare";
|
||||
+ public static final String CACHE_DIR_NAME = "mmShare";
|
||||
+
|
||||
public static @Nullable Uri compatUriFromFile(@NonNull final Context context,
|
||||
@NonNull final File file) {
|
||||
Uri result = null;
|
||||
@@ -58,12 +64,7 @@ public class RealPathUtil {
|
||||
}
|
||||
// DownloadsProvider
|
||||
else if (isDownloadsDocument(uri)) {
|
||||
+ public static @Nullable
|
||||
+ Uri compatUriFromFile(@NonNull final Context context,
|
||||
+ @NonNull final File file) {
|
||||
+ Uri result = null;
|
||||
+ final String packageName = context.getApplicationContext().getPackageName();
|
||||
+ final String authority = packageName + ".provider";
|
||||
+ try {
|
||||
+ result = FileProvider.getUriForFile(context, authority, file);
|
||||
+ }
|
||||
+ catch(IllegalArgumentException e) {
|
||||
+ e.printStackTrace();
|
||||
+ }
|
||||
+ return result;
|
||||
+ }
|
||||
+
|
||||
+ public static String getRealPathFromURI(final Context context, final Uri uri) {
|
||||
+
|
||||
+ // DocumentProvider
|
||||
+ if (DocumentsContract.isDocumentUri(context, uri)) {
|
||||
+ // ExternalStorageProvider
|
||||
+ 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];
|
||||
+ }
|
||||
+ } else if (isDownloadsDocument(uri)) {
|
||||
+ // DownloadsProvider
|
||||
+
|
||||
+ final String id = DocumentsContract.getDocumentId(uri);
|
||||
+ if (!TextUtils.isEmpty(id)) {
|
||||
+ if (id.startsWith("raw:")) {
|
||||
+ return id.replaceFirst("raw:", "");
|
||||
+ }
|
||||
+ try {
|
||||
+ return getPathFromSavingTempFile(context, uri);
|
||||
+ } catch (NumberFormatException e) {
|
||||
+ Log.e("ReactNative", "DownloadsProvider unexpected uri " + uri.toString());
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+ } else if (isMediaDocument(uri)) {
|
||||
+ // MediaProvider
|
||||
+
|
||||
+ 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]
|
||||
+ };
|
||||
+
|
||||
+ if (contentUri != null) {
|
||||
+ return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||
+ } else {
|
||||
+ return getPathFromSavingTempFile(context, uri);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
+ // MediaStore (and general)
|
||||
+
|
||||
+ if (isGooglePhotosUri(uri)) {
|
||||
+ return uri.getLastPathSegment();
|
||||
+ }
|
||||
+
|
||||
+ // Try save to tmp file, and return tmp file path
|
||||
+ return getPathFromSavingTempFile(context, uri);
|
||||
+ } else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||
+ return uri.getPath();
|
||||
+ }
|
||||
+
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ public static String getPathFromSavingTempFile(Context context, final Uri uri) {
|
||||
+ File tmpFile;
|
||||
+ String fileName = null;
|
||||
+
|
||||
+ if (uri == null || uri.isRelative()) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ // Try and get the filename from the Uri
|
||||
+ try {
|
||||
+ Cursor returnCursor =
|
||||
+ context.getContentResolver().query(uri, null, null, null, null);
|
||||
+ int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
+ returnCursor.moveToFirst();
|
||||
+ fileName = sanitizeFilename(returnCursor.getString(nameIndex));
|
||||
+ returnCursor.close();
|
||||
+
|
||||
+ } catch (Exception e) {
|
||||
+ // just continue to get the filename with the last segment of the path
|
||||
+ }
|
||||
+
|
||||
+ try {
|
||||
+ if (TextUtils.isEmpty(fileName)) {
|
||||
+ fileName = sanitizeFilename(uri.getLastPathSegment().trim());
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ File cacheDir = new File(context.getCacheDir(), CACHE_DIR_NAME);
|
||||
+ if (!cacheDir.exists()) {
|
||||
+ cacheDir.mkdirs();
|
||||
+ }
|
||||
+
|
||||
+ tmpFile = new File(cacheDir, fileName);
|
||||
+ tmpFile.createNewFile();
|
||||
+
|
||||
+ ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r");
|
||||
+
|
||||
+ FileChannel src = new FileInputStream(pfd.getFileDescriptor()).getChannel();
|
||||
+ FileChannel dst = new FileOutputStream(tmpFile).getChannel();
|
||||
+ dst.transferFrom(src, 0, src.size());
|
||||
+ src.close();
|
||||
+ dst.close();
|
||||
+ } catch (IOException ex) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ return tmpFile.getAbsolutePath();
|
||||
+ }
|
||||
+
|
||||
+ public static String getDataColumn(Context context, Uri uri, String selection,
|
||||
+ String[] selectionArgs) {
|
||||
+
|
||||
+ Cursor cursor = null;
|
||||
+ final String column = "_data";
|
||||
+ final String[] projection = {
|
||||
+ column
|
||||
+ };
|
||||
+
|
||||
+ try {
|
||||
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
|
||||
+ null);
|
||||
+ if (cursor != null && cursor.moveToFirst()) {
|
||||
+ final int index = cursor.getColumnIndexOrThrow(column);
|
||||
+ return cursor.getString(index);
|
||||
+ }
|
||||
+ } finally {
|
||||
+ if (cursor != null)
|
||||
+ cursor.close();
|
||||
+ }
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ public static boolean isExternalStorageDocument(Uri uri) {
|
||||
+ return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||
+ }
|
||||
+
|
||||
+ public static boolean isDownloadsDocument(Uri uri) {
|
||||
+ return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||
+ }
|
||||
+
|
||||
+ public static boolean isMediaDocument(Uri uri) {
|
||||
+ return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||
+ }
|
||||
+
|
||||
+ public static boolean isGooglePhotosUri(Uri uri) {
|
||||
+ return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||
+ }
|
||||
+
|
||||
+ 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 "";
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ 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";
|
||||
+ }
|
||||
+
|
||||
+ public static String getMimeType(String filePath) {
|
||||
+ File file = new File(filePath);
|
||||
+ return getMimeType(file);
|
||||
+ }
|
||||
+
|
||||
+ public static String getMimeTypeFromUri(final Context context, final Uri uri) {
|
||||
+ try {
|
||||
+ ContentResolver cR = context.getContentResolver();
|
||||
+ return cR.getType(uri);
|
||||
+ } catch (Exception e) {
|
||||
+ return "application/octet-stream";
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static void deleteTempFiles(final File dir) {
|
||||
+ try {
|
||||
+ if (dir.isDirectory()) {
|
||||
+ deleteRecursive(dir);
|
||||
+ }
|
||||
+ } catch (Exception e) {
|
||||
+ // do nothing
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static void deleteRecursive(File fileOrDirectory) {
|
||||
+ if (fileOrDirectory.isDirectory())
|
||||
+ for (File child : Objects.requireNonNull(fileOrDirectory.listFiles()))
|
||||
+ deleteRecursive(child);
|
||||
+
|
||||
+ fileOrDirectory.delete();
|
||||
+ }
|
||||
+
|
||||
+ private static String sanitizeFilename(String filename) {
|
||||
+ if (filename == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
|
||||
- public static @Nullable Uri compatUriFromFile(@NonNull final Context context,
|
||||
- @NonNull final File file) {
|
||||
- Uri result = null;
|
||||
- if (Build.VERSION.SDK_INT < 21) {
|
||||
- result = Uri.fromFile(file);
|
||||
- }
|
||||
- else {
|
||||
- final String packageName = context.getApplicationContext().getPackageName();
|
||||
- final String authority = new StringBuilder(packageName).append(".provider").toString();
|
||||
- try {
|
||||
- result = FileProvider.getUriForFile(context, authority, file);
|
||||
- }
|
||||
- catch(IllegalArgumentException e) {
|
||||
- e.printStackTrace();
|
||||
- }
|
||||
- }
|
||||
- return result;
|
||||
- }
|
||||
-
|
||||
- @SuppressLint("NewApi")
|
||||
- public static @Nullable String getRealPathFromURI(@NonNull final Context context,
|
||||
- @NonNull final Uri uri) {
|
||||
-
|
||||
- final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||
-
|
||||
- // DocumentProvider
|
||||
- if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
|
||||
- // ExternalStorageProvider
|
||||
- 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];
|
||||
- }
|
||||
-
|
||||
- // TODO handle non-primary volumes
|
||||
- }
|
||||
- // DownloadsProvider
|
||||
- else if (isDownloadsDocument(uri)) {
|
||||
-
|
||||
- final String id = DocumentsContract.getDocumentId(uri);
|
||||
- final Uri contentUri = ContentUris.withAppendedId(
|
||||
- Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||
-
|
||||
- return getDataColumn(context, contentUri, null, null);
|
||||
+ return getPathFromSavingTempFile(context, uri);
|
||||
}
|
||||
// MediaProvider
|
||||
else if (isMediaDocument(uri)) {
|
||||
@@ -89,7 +90,7 @@ public class RealPathUtil {
|
||||
}
|
||||
}
|
||||
// MediaStore (and general)
|
||||
- }
|
||||
- // 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())) {
|
||||
+ if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
|
||||
// Return the remote address
|
||||
if (isGooglePhotosUri(uri))
|
||||
@@ -98,7 +99,7 @@ public class RealPathUtil {
|
||||
if (isFileProviderUri(context, uri))
|
||||
return getFileProviderPath(context, uri);
|
||||
|
||||
-
|
||||
- // Return the remote address
|
||||
- if (isGooglePhotosUri(uri))
|
||||
- return uri.getLastPathSegment();
|
||||
-
|
||||
- if (isFileProviderUri(context, uri))
|
||||
- return getFileProviderPath(context, uri);
|
||||
-
|
||||
- return getDataColumn(context, uri, null, null);
|
||||
+ return getPathFromSavingTempFile(context, uri);
|
||||
}
|
||||
// File
|
||||
else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||
@@ -108,6 +109,49 @@ public class RealPathUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
+
|
||||
+ public static String getPathFromSavingTempFile(Context context, final Uri uri) {
|
||||
+ File tmpFile;
|
||||
+ String fileName = null;
|
||||
+
|
||||
+ // Try and get the filename from the Uri
|
||||
+ try {
|
||||
+ Cursor returnCursor =
|
||||
+ context.getContentResolver().query(uri, null, null, null, null);
|
||||
+ int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
+ returnCursor.moveToFirst();
|
||||
+ fileName = returnCursor.getString(nameIndex);
|
||||
+ } catch (Exception e) {
|
||||
+ // just continue to get the filename with the last segment of the path
|
||||
+ }
|
||||
+
|
||||
+ try {
|
||||
+ if (fileName == null) {
|
||||
+ fileName = uri.getLastPathSegment().toString().trim();
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ File cacheDir = new File(context.getCacheDir(), CACHE_DIR_NAME);
|
||||
+ if (!cacheDir.exists()) {
|
||||
+ cacheDir.mkdirs();
|
||||
+ }
|
||||
+
|
||||
+ tmpFile = new File(cacheDir, fileName);
|
||||
+ tmpFile.createNewFile();
|
||||
+
|
||||
+ ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r");
|
||||
+
|
||||
+ FileChannel src = new FileInputStream(pfd.getFileDescriptor()).getChannel();
|
||||
+ FileChannel dst = new FileOutputStream(tmpFile).getChannel();
|
||||
+ dst.transferFrom(src, 0, src.size());
|
||||
+ src.close();
|
||||
+ dst.close();
|
||||
+ } catch (IOException ex) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ return tmpFile.getAbsolutePath();
|
||||
+ }
|
||||
+
|
||||
/**
|
||||
* Get the value of the data column for this Uri. This is useful for
|
||||
* MediaStore Uris, and other file-based ContentProviders.
|
||||
- }
|
||||
- // File
|
||||
- else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||
- return uri.getPath();
|
||||
- }
|
||||
-
|
||||
- return null;
|
||||
- }
|
||||
-
|
||||
- /**
|
||||
- * 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 = "_data";
|
||||
- final String[] projection = {
|
||||
- column
|
||||
- };
|
||||
-
|
||||
- try {
|
||||
- cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
|
||||
- null);
|
||||
- if (cursor != null && cursor.moveToFirst()) {
|
||||
- final int index = cursor.getColumnIndexOrThrow(column);
|
||||
- return cursor.getString(index);
|
||||
- }
|
||||
- } finally {
|
||||
- if (cursor != null)
|
||||
- cursor.close();
|
||||
- }
|
||||
- return null;
|
||||
- }
|
||||
-
|
||||
-
|
||||
- /**
|
||||
- * @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(@NonNull final Uri uri) {
|
||||
- return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||
- }
|
||||
-
|
||||
- /**
|
||||
- * @param context The Application context
|
||||
- * @param uri The Uri is checked by functions
|
||||
- * @return Whether the Uri authority is FileProvider
|
||||
- */
|
||||
- public static boolean isFileProviderUri(@NonNull final Context context,
|
||||
- @NonNull final Uri uri) {
|
||||
- final String packageName = context.getPackageName();
|
||||
- final String authority = new StringBuilder(packageName).append(".provider").toString();
|
||||
- return authority.equals(uri.getAuthority());
|
||||
- }
|
||||
-
|
||||
- /**
|
||||
- * @param context The Application context
|
||||
- * @param uri The Uri is checked by functions
|
||||
- * @return File path or null if file is missing
|
||||
- */
|
||||
- public static @Nullable String getFileProviderPath(@NonNull final Context context,
|
||||
- @NonNull final Uri uri)
|
||||
- {
|
||||
- final File appDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
|
||||
- final File file = new File(appDir, uri.getLastPathSegment());
|
||||
- return file.exists() ? file.toString(): null;
|
||||
- }
|
||||
+ File f = new File(filename);
|
||||
+ return f.getName();
|
||||
+ }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user