From b8c088cc70887ab054f7cd32a8fc14a186b3ee71 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Wed, 19 Jun 2024 09:33:45 +0800 Subject: [PATCH] Upgrade RN as well as update or replace other dependencies (#8011) * start upgrade to RN 74 * migrate react-native-fs to expo-file-system * exclude expo-asset module * fix database manager remove directory * fix: android network helper * include expo on android * temporarily disable android dep lock * replace react-native-create-thumbnail with expo-video-thumnails * update patches file version * fix android build on 74 * create local library to replace MattermostManaged, Notifications and SplitView modules with new arch support * migrate app to use new mattermost-rnutils library * remove unused flipper class for android unsigned * fix mattermost-rnutils android foldedObserver lifecycle * use mattermost-rnutils on Android * use mattermost-rnutils on iOS * path react-native-navigation to not crash when activity is not NavigationActivity * create local library for android share extension with new arch support * Replace ShareModule with @mattermost/rnshare library * remove ShareModule from android native code * update react-intl * update nodejs to 20.x.x npm to 10.x.x and dev deps * update @gorhom/bottom-sheet * use MattermostShare conditionally based on the platform * update @react-native-camera-roll/camera-roll * remove unused react-native-calendars * fix metro config * fix terminate session race condition * remove unused analytics * replace react-native-device-info with expo-application and expo-device * update @react-native-clipboard/clipboard * update @react-native-community/datetimepicker * update @react-native-community/netinfo * update @sentry/react-native * update react-native-document-picker * update react-native-gesture-handler * update react-native-share * update react-native-svg and react-native-svg-transformer * update react-native-vector-icons * update babel * update react-native-shadow-2 * update semver * remove react-native-svg-transformer and convert svg files to svg components * fix @mattermost/rnshare new-arch build on android * remove react-native-create-thumbnail resolution in build.gradle * create @mattermost/hardware-keyboard library to replace hw-keyboard-events * fix hardware-keyboard library * fix rnutils library * create @mattermost/keyboard-tracker library * replace react-native-keyboard-tracking-view with @mattermost/keyboard-tracker * fix: rnutils to not crash on lateinit context * fix: rnutils delete database * revert changes to session_manager * Removed react-native-webview and added expo-web-browser instead With expo-web-browser we no longer need the webview for SSO login the SSO login is now done by using "custom Chrome tabs" on Android and ASWebAuthenticationSession on iOS * remove patch for react-native-keyboard-tracking-view * replace react-native-linear-gradient with expo-linear-gradient * replace react-native-in-app-review with expo-store-review * fix: shared group database directory on ios * replace react-native-fast-image with expo-image * remove unusued type def for react-native-keyboard-tracking-view * replace react-native-elements and react-native-button with @rneui * node version * update sizzling methods * fix tests using jest-expo * replace jail-monkey with expo-device * update babel deps * update typescript eslint * update rn and expo * react-native-document-picker @react-native-camera-roll/camera-roll @react-native-community/datetimepicker react-native-reanimated react-native-safe-area-context * update patches * update @sentry/react-native * upgrade react-native-navigation * update expo & expo-image * upgrade to working version of @sentry/react-native * update node, cocoapods, bundler, fastlane versions * @testing-library/react-native and eslint-plugin-jest * fix: FloatingTextInput causing a crash with reanimated * update sentry, localize, @types/lodash and uuid * fix floating text input label * update react-native-video * fix: cannot calculate shadow efficiently on some components * fix: reduce motion warning for bottomSheet * fix: shadow on YouTube component * update react-native-webrtc expo and @typescript-eslint * audit fix * fix swizzling bad merge * temp use of github dependency for @mattermost libraries * feedback review * feedback review * npm audit fix * update bundle deps * update @mattermost/react-native-turbo-log * update deps --- .eslintrc.json | 2 +- .github/workflows/build-android-beta.yml | 2 +- .github/workflows/build-android-release.yml | 2 +- .github/workflows/build-ios-beta.yml | 2 +- .github/workflows/build-ios-release.yml | 2 +- .github/workflows/build-pr.yml | 2 +- .github/workflows/ci.yml | 2 +- .gitignore | 3 + .node-version | 2 +- .nvmrc | 2 +- .solidarity | 2 +- ios/Gemfile => Gemfile | 2 +- ios/Gemfile.lock => Gemfile.lock | 41 +- NOTICE.txt | 357 - android/app/build.gradle | 13 +- android/app/src/main/AndroidManifest.xml | 2 +- .../com/mattermost/helpers/Credentials.java | 48 - .../helpers/CustomPushNotificationHelper.java | 4 +- .../java/com/mattermost/helpers/Network.java | 8 +- .../helpers/NotificationHelper.java | 312 - .../com/mattermost/helpers/RealPathUtil.java | 274 - .../rnbeta/CustomPushNotification.kt | 6 +- .../com/mattermost/rnbeta/MainActivity.kt | 39 +- .../com/mattermost/rnbeta/MainApplication.kt | 183 +- .../rnbeta/MattermostManagedModule.java | 263 - .../rnbeta/NotificationDismissService.java | 4 +- .../rnbeta/NotificationsModule.java | 79 - .../com/mattermost/rnbeta/SplitViewModule.kt | 74 - .../com/mattermost/share/ShareActivity.java | 20 - .../com/mattermost/share/ShareModule.java | 259 - .../java/com/mattermost/share/ShareUtils.java | 115 - .../res/drawable/rn_edit_text_material.xml | 3 +- .../flipper/ReactNativeFlipper.java | 19 - android/build.gradle | 26 +- android/buildscript-gradle.lockfile | 141 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/gradlew | 14 +- android/gradlew.bat | 20 +- android/settings.gradle | 4 +- app/client/rest/base.ts | 3 - app/client/rest/channels.ts | 26 - app/client/rest/files.ts | 1 - app/client/rest/integrations.ts | 5 - app/client/rest/posts.ts | 36 - app/client/rest/preferences.ts | 2 - app/client/rest/teams.ts | 18 - app/client/rest/users.ts | 43 - .../expanded_announcement_banner.tsx | 6 +- app/components/app_version/index.tsx | 6 +- .../app_slash_suggestion.tsx | 3 - .../slash_suggestion/slash_suggestion.tsx | 3 - .../slash_suggestion_item.tsx | 8 +- app/components/button/index.tsx | 8 +- app/components/emoji/emoji.tsx | 7 +- app/components/files/document_file.tsx | 7 +- app/components/files/file.tsx | 4 +- app/components/files/image_file.tsx | 10 +- app/components/files/video_file.tsx | 20 +- .../files_search/file_options/icon.tsx | 4 +- .../file_options/tablet_options.tsx | 3 +- app/components/files_search/file_result.tsx | 21 +- .../floating_text_input_label/index.tsx | 16 +- .../markdown/markdown_image/index.tsx | 4 +- .../markdown/markdown_table/index.tsx | 2 +- .../markdown/markdown_table_image/index.tsx | 2 +- app/components/option_item/option_icon.tsx | 4 +- app/components/post_draft/archived/index.tsx | 6 +- app/components/post_draft/post_draft.tsx | 2 +- .../post_draft/post_input/post_input.tsx | 52 +- .../post_draft/uploads/upload_item/index.tsx | 2 +- .../post_list/post/avatar/avatar.tsx | 4 +- .../button_binding/index.tsx | 7 +- .../content/image_preview/image_preview.tsx | 2 +- .../attachment_author.test.tsx.snap | 98 +- .../attachment_footer.test.tsx.snap | 52 +- .../action_button/index.tsx | 7 +- .../message_attachments/attachment_author.tsx | 4 +- .../message_attachments/attachment_footer.tsx | 4 +- .../attachment_image/index.tsx | 2 +- .../attachment_thumbnail.tsx | 6 +- .../opengraph/opengraph_image/index.tsx | 10 +- .../post/body/content/youtube/index.tsx | 45 +- .../post/body/content/youtube/youtube.svg | 4 - .../body/content/youtube/youtube_logo.tsx | 24 + app/components/post_list/post/body/index.tsx | 2 +- .../post/body/message/show_more_button.tsx | 2 +- .../post_list/scroll_to_end_view.tsx | 4 +- app/components/profile_picture/image.tsx | 12 +- app/components/profile_picture/index.tsx | 4 +- app/components/progressive_image/index.tsx | 115 +- .../progressive_image/thumbnail.tsx | 44 - app/components/search/index.tsx | 18 +- app/components/slide_up_panel_item/index.tsx | 10 +- .../team_list/team_item/team_icon.tsx | 4 +- app/components/toast/index.tsx | 4 +- .../tutorial_highlight/long_press.tsx | 2 +- .../long_press_illustration.svg | 22 - .../long_press_illustration.tsx | 85 + .../__snapshots__/index.test.tsx.snap | 228 +- app/constants/device.ts | 4 +- app/context/device/index.test.tsx | 16 +- app/context/device/index.tsx | 9 +- app/database/manager/__mocks__/index.ts | 8 +- app/database/manager/index.ts | 35 +- app/hooks/device.ts | 35 +- app/hooks/keyboard_tracking.ts | 7 +- app/init/credentials.ts | 4 - app/init/managed_app.ts | 12 +- app/init/push_notifications.ts | 8 +- app/managers/analytics.ts | 198 - app/managers/global_event_handler.ts | 24 +- app/managers/network_manager.ts | 5 +- app/managers/session_manager.ts | 14 +- app/notifications/index.ts | 15 - app/products/calls/components/emoji_list.tsx | 2 +- .../calls/screens/call_screen/call_screen.tsx | 6 +- app/products/calls/types/calls.ts | 12 - app/products/calls/utils.ts | 16 +- app/screens/apps_form/apps_form_component.tsx | 4 +- app/screens/bottom_sheet/index.tsx | 3 +- app/screens/channel/channel.tsx | 2 +- .../intro/direct_channel/group/group.tsx | 4 +- .../title/group_message/avatars/avatars.tsx | 4 +- .../edit_profile/components/panel_item.tsx | 4 +- app/screens/edit_server/form.tsx | 6 +- app/screens/forgot_password/inbox.svg | 71 - app/screens/forgot_password/inbox.tsx | 214 + app/screens/forgot_password/index.tsx | 8 +- app/screens/gallery/footer/avatar/index.tsx | 4 +- .../footer/download_with_action/index.tsx | 16 +- app/screens/gallery/gallery.tsx | 8 +- .../gallery/image_renderer/transformer.tsx | 8 +- app/screens/gallery/index.tsx | 5 +- .../gallery/lightbox_swipeout/index.tsx | 10 +- app/screens/gallery/pager/index.tsx | 85 +- app/screens/gallery/video_renderer/error.tsx | 4 +- app/screens/gallery/video_renderer/index.tsx | 72 +- .../threads_list/end_of_list.tsx | 2 +- .../illustrations/search_hint.svg | 20 - .../illustrations/search_hint.tsx | 64 + app/screens/home/index.tsx | 27 +- .../search/initial/modifiers/modifier.tsx | 15 +- .../home/search/results/header_button.tsx | 6 +- app/screens/home/search/search.tsx | 23 +- app/screens/home/tab_bar/home.tsx | 4 +- app/screens/in_app_notification/icon.tsx | 10 +- app/screens/invite/summary.tsx | 6 +- app/screens/login/form.tsx | 10 +- app/screens/login/sso_options.tsx | 5 +- app/screens/mfa/index.tsx | 6 +- app/screens/mfa/mfa.svg | 10 - app/screens/mfa/shield.tsx | 48 + app/screens/pinned_messages/empty/empty.svg | 9 - app/screens/pinned_messages/empty/empty.tsx | 44 + app/screens/pinned_messages/empty/index.tsx | 2 +- app/screens/review_app/index.tsx | 6 +- app/screens/server/form.tsx | 7 +- app/screens/settings/about/about.tsx | 8 +- app/screens/settings/advanced/index.tsx | 6 +- .../report_problem/report_problem.tsx | 11 +- app/screens/snack_bar/index.tsx | 51 +- app/screens/sso/index.tsx | 38 +- app/screens/sso/sso.test.tsx | 54 +- ...edirect_url.tsx => sso_authentication.tsx} | 89 +- .../sso/sso_with_redirect_url.test.tsx | 50 - app/screens/sso/sso_with_webview.tsx | 256 - app/screens/thread/thread.tsx | 2 +- app/screens/user_profile/title/avatar.tsx | 9 +- app/utils/file/file_picker/index.ts | 21 +- app/utils/file/index.ts | 79 +- app/utils/gallery/index.ts | 5 +- app/utils/general/index.ts | 4 +- app/utils/helpers.ts | 15 +- app/utils/mattermost_managed.ts | 25 +- app/utils/reviews.ts | 6 +- assets/base/i18n/en.json | 9 +- babel.config.js | 1 - fastlane/Gemfile.lock | 104 +- ios/Mattermost.xcodeproj/project.pbxproj | 417 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- ios/Mattermost/AppDelegate.h | 11 +- ios/Mattermost/AppDelegate.mm | 108 +- ...tificationEventHandler+HandleReplyAction.h | 0 ...tificationEventHandler+HandleReplyAction.m | 1 - .../SDWebImageDownloaderOperation+Swizzle.h | 0 .../SDWebImageDownloaderOperation+Swizzle.m | 6 +- .../{ => Helpers}/NotificationHelper.swift | 25 +- ios/Mattermost/Info.plist | 2 +- ios/Mattermost/Mattermost-Bridging-Header.h | 1 + ios/Mattermost/MattermostManaged.h | 15 - ios/Mattermost/MattermostManaged.m | 179 - ios/Mattermost/Modules/SplitViewModule.m | 11 - ios/Mattermost/Modules/SplitViewModule.swift | 85 - ios/Mattermost/NotificationsModule.h | 6 - ios/Mattermost/NotificationsModule.m | 32 - ios/Mattermost/UIImage+vImageScaling.h | 16 - ios/Mattermost/UIImage+vImageScaling.m | 48 - ios/Mattermost/{ => Utils}/RuntimeUtils.swift | 0 ios/Podfile | 36 +- ios/Podfile.lock | 1523 +- ios/PrivacyInfo.xcprivacy | 36 +- jest.config.js | 14 +- .../hardware-keyboard/android/build.gradle | 58 + .../android/src/main/AndroidManifest.xml | 3 + .../MattermostHardwareKeyboardImpl.kt | 46 + .../MattermostHardwareKeyboardPackage.kt | 30 + .../MattermostHardwareKeyboardModule.kt | 17 + .../MattermostHardwareKeyboardModule.kt | 21 + .../ios/MattermostHardwareKeyboard.h | 14 + .../ios/MattermostHardwareKeyboard.mm | 60 + .../MattermostHardwareKeyboardEvents.swift | 15 + .../MattermostHardwareKeyboardWrapper.swift | 52 + .../mattermost-hardware-keyboard.podspec | 39 + .../hardware-keyboard/package.json | 18 + .../src/NativeMattermostHardwareKeyboard.ts | 10 + .../hardware-keyboard/src/index.tsx | 59 + .../Extensions/UIResponder+FirstResponder.h | 6 + .../Extensions/UIResponder+FirstResponder.m | 17 + .../ios/MattermostKeyboardTrackerView.h | 17 + .../ios/MattermostKeyboardTrackerView.mm | 169 + .../MattermostKeyboardTrackerViewManager.mm | 127 + .../ios/Views/KeyboardTrackingView.h | 50 + .../ios/Views/KeyboardTrackingView.m | 622 + .../ios/Views/ObservingInputAccessoryView.h | 36 + .../ios/Views/ObservingInputAccessoryView.m | 172 + .../mattermost-keyboard-tracker.podspec | 23 + .../@mattermost/keyboard-tracker/package.json | 16 + ...rmostKeyboardTrackerViewNativeComponent.ts | 70 + .../keyboard-tracker/src/index.tsx | 64 + .../@mattermost/rnshare/android/build.gradle | 59 + .../android/src/main/AndroidManifest.xml | 8 + .../mattermost/rnshare/MattermostShareImpl.kt | 150 + .../rnshare/MattermostSharePackage.kt | 33 + .../com/mattermost/rnshare/ShareActivity.kt | 9 + .../java/com/mattermost/rnshare/ShareUtils.kt | 108 + .../com/mattermost/rnshare/ShareWorker.kt | 153 + .../mattermost/rnshare/helpers/Extensions.kt | 39 + .../rnshare/helpers/RealPathUtil.kt | 220 + .../src/newarch/MattermostShareModule.kt | 23 + .../src/oldarch/MattermostShareModule.kt | 31 + libraries/@mattermost/rnshare/package.json | 19 + .../rnshare/src/NativeMattermostShare.ts | 36 + libraries/@mattermost/rnshare/src/index.tsx | 32 + .../@mattermost/rnutils/android/build.gradle | 64 + .../android/src/main/AndroidManifest.xml | 9 + .../mattermost/rnutils/RNUtilsModuleImpl.kt | 109 + .../com/mattermost/rnutils/RNUtilsPackage.kt | 30 + .../com/mattermost/rnutils/enums/Events.kt | 6 + .../rnutils/helpers}/FoldableObserver.kt | 29 +- .../rnutils/helpers/NotificationHelper.kt | 296 + .../rnutils/helpers/Notifications.kt | 47 + .../rnutils/helpers/RealPathUtil.kt | 225 + .../rnutils/helpers/SaveDataTask.kt | 146 + .../mattermost/rnutils/helpers/SplitView.kt | 61 + .../android/src/newarch/RNUtilsModule.kt | 65 + .../android/src/oldarch/RNUtilsModule.kt | 86 + .../Extensions/RNUtilsWrapper+Events.swift | 12 + .../ios/Managers/NotificationManager.swift | 117 + .../ios/Managers/OrientationManager.swift | 16 + .../ios/Protocols/OrientationLockable.swift | 5 + .../ios/Protocols/RNUtilsDelegate.swift | 5 + libraries/@mattermost/rnutils/ios/RNUtils.h | 15 + libraries/@mattermost/rnutils/ios/RNUtils.mm | 204 + .../rnutils/ios/RNUtilsWrapper.swift | 224 + .../rnutils/mattermost-rnutils.podspec | 39 + libraries/@mattermost/rnutils/package.json | 18 + .../@mattermost/rnutils/src/NativeRNUtils.ts | 62 + libraries/@mattermost/rnutils/src/index.tsx | 30 + metro.config.js | 13 +- package-lock.json | 11937 +++++++++++++--- package.json | 193 +- patches/@gorhom+bottom-sheet+4.6.1.patch | 40 - patches/@nozbe+watermelondb+0.27.1.patch | 15 + ...act-native-community+netinfo+11.3.2.patch} | 0 patches/@rneui+base+4.0.0-rc.8.patch | 66 + ...00.1.patch => @sentry+utils+7.117.0.patch} | 0 ...es+react-syntax-highlighter+15.5.13.patch} | 0 patches/expo-image+1.12.12.patch | 22 + patches/expo-modules-core+1.12.15.patch | 10 + patches/jest-expo+51.0.2.patch | 15 + patches/react-native-button+3.1.0.patch | 36 - .../react-native-create-thumbnail+1.6.4.patch | 113 - .../react-native-device-info+10.13.1.patch | 17 - patches/react-native-elements+3.4.3.patch | 93 - patches/react-native-fast-image+8.6.3.patch | 108 - ...react-native-hw-keyboard-event+0.0.4.patch | 66 - ...-native-keyboard-tracking-view+5.7.0.patch | 494 - ...h => react-native-navigation+7.40.0.patch} | 172 +- ...act-native-safe-area-context+4.10.5.patch} | 0 ....0.patch => react-native-svg+15.3.0.patch} | 0 patches/react-native-video+5.2.1.patch | 199 - patches/react-native-video+6.2.0.patch | 17 + patches/react-native-webrtc+124.0.1.patch | 143 + scripts/build.sh | 4 +- scripts/preinstall.sh | 6 +- .../components/channel_item/avatar/avatar.tsx | 4 +- .../content_view/attachments/multiple.tsx | 2 + .../content_view/attachments/single.tsx | 2 + .../content_view/attachments/thumbnail.tsx | 10 +- .../content_view/link_preview/index.tsx | 6 +- .../components/header/post_button.tsx | 22 +- share_extension/index.tsx | 15 +- share_extension/screens/share.tsx | 5 +- share_extension/state/index.ts | 15 +- share_extension/utils/index.ts | 2 + test/file_transformer.js | 12 - test/setup.ts | 112 +- tsconfig.json | 2 - types/components/emoji.ts | 8 +- types/global/share_extension.d.ts | 39 - types/global/styles.ts | 3 +- types/global/svg.d.ts | 7 - .../react-native-keyboard-tracking-view.d.ts | 27 - 313 files changed, 17912 insertions(+), 9050 deletions(-) rename ios/Gemfile => Gemfile (54%) rename ios/Gemfile.lock => Gemfile.lock (82%) delete mode 100644 android/app/src/main/java/com/mattermost/helpers/Credentials.java delete mode 100644 android/app/src/main/java/com/mattermost/helpers/NotificationHelper.java delete mode 100644 android/app/src/main/java/com/mattermost/helpers/RealPathUtil.java delete mode 100644 android/app/src/main/java/com/mattermost/rnbeta/MattermostManagedModule.java delete mode 100644 android/app/src/main/java/com/mattermost/rnbeta/NotificationsModule.java delete mode 100644 android/app/src/main/java/com/mattermost/rnbeta/SplitViewModule.kt delete mode 100644 android/app/src/main/java/com/mattermost/share/ShareActivity.java delete mode 100644 android/app/src/main/java/com/mattermost/share/ShareModule.java delete mode 100644 android/app/src/main/java/com/mattermost/share/ShareUtils.java delete mode 100644 android/app/src/unsigned/java/com/mattermost/flipper/ReactNativeFlipper.java delete mode 100644 app/components/post_list/post/body/content/youtube/youtube.svg create mode 100644 app/components/post_list/post/body/content/youtube/youtube_logo.tsx delete mode 100644 app/components/progressive_image/thumbnail.tsx delete mode 100644 app/components/tutorial_highlight/long_press_illustration.svg create mode 100644 app/components/tutorial_highlight/long_press_illustration.tsx delete mode 100644 app/managers/analytics.ts delete mode 100644 app/notifications/index.ts delete mode 100644 app/screens/forgot_password/inbox.svg create mode 100644 app/screens/forgot_password/inbox.tsx delete mode 100644 app/screens/global_threads/threads_list/illustrations/search_hint.svg create mode 100644 app/screens/global_threads/threads_list/illustrations/search_hint.tsx delete mode 100644 app/screens/mfa/mfa.svg create mode 100644 app/screens/mfa/shield.tsx delete mode 100644 app/screens/pinned_messages/empty/empty.svg create mode 100644 app/screens/pinned_messages/empty/empty.tsx rename app/screens/sso/{sso_with_redirect_url.tsx => sso_authentication.tsx} (67%) delete mode 100644 app/screens/sso/sso_with_redirect_url.test.tsx delete mode 100644 app/screens/sso/sso_with_webview.tsx rename ios/Mattermost/{ => Extensions}/RNNotificationEventHandler+HandleReplyAction.h (100%) rename ios/Mattermost/{ => Extensions}/RNNotificationEventHandler+HandleReplyAction.m (99%) rename ios/Mattermost/{ => Extensions}/SDWebImageDownloaderOperation+Swizzle.h (100%) rename ios/Mattermost/{ => Extensions}/SDWebImageDownloaderOperation+Swizzle.m (100%) rename ios/Mattermost/{ => Helpers}/NotificationHelper.swift (76%) delete mode 100644 ios/Mattermost/MattermostManaged.h delete mode 100644 ios/Mattermost/MattermostManaged.m delete mode 100644 ios/Mattermost/Modules/SplitViewModule.m delete mode 100644 ios/Mattermost/Modules/SplitViewModule.swift delete mode 100644 ios/Mattermost/NotificationsModule.h delete mode 100644 ios/Mattermost/NotificationsModule.m delete mode 100644 ios/Mattermost/UIImage+vImageScaling.h delete mode 100644 ios/Mattermost/UIImage+vImageScaling.m rename ios/Mattermost/{ => Utils}/RuntimeUtils.swift (100%) create mode 100644 libraries/@mattermost/hardware-keyboard/android/build.gradle create mode 100644 libraries/@mattermost/hardware-keyboard/android/src/main/AndroidManifest.xml create mode 100644 libraries/@mattermost/hardware-keyboard/android/src/main/java/com/mattermost/hardware/keyboard/MattermostHardwareKeyboardImpl.kt create mode 100644 libraries/@mattermost/hardware-keyboard/android/src/main/java/com/mattermost/hardware/keyboard/MattermostHardwareKeyboardPackage.kt create mode 100644 libraries/@mattermost/hardware-keyboard/android/src/newarch/MattermostHardwareKeyboardModule.kt create mode 100644 libraries/@mattermost/hardware-keyboard/android/src/oldarch/MattermostHardwareKeyboardModule.kt create mode 100644 libraries/@mattermost/hardware-keyboard/ios/MattermostHardwareKeyboard.h create mode 100644 libraries/@mattermost/hardware-keyboard/ios/MattermostHardwareKeyboard.mm create mode 100644 libraries/@mattermost/hardware-keyboard/ios/MattermostHardwareKeyboardEvents.swift create mode 100644 libraries/@mattermost/hardware-keyboard/ios/MattermostHardwareKeyboardWrapper.swift create mode 100644 libraries/@mattermost/hardware-keyboard/mattermost-hardware-keyboard.podspec create mode 100644 libraries/@mattermost/hardware-keyboard/package.json create mode 100644 libraries/@mattermost/hardware-keyboard/src/NativeMattermostHardwareKeyboard.ts create mode 100644 libraries/@mattermost/hardware-keyboard/src/index.tsx create mode 100644 libraries/@mattermost/keyboard-tracker/ios/Extensions/UIResponder+FirstResponder.h create mode 100644 libraries/@mattermost/keyboard-tracker/ios/Extensions/UIResponder+FirstResponder.m create mode 100644 libraries/@mattermost/keyboard-tracker/ios/MattermostKeyboardTrackerView.h create mode 100644 libraries/@mattermost/keyboard-tracker/ios/MattermostKeyboardTrackerView.mm create mode 100644 libraries/@mattermost/keyboard-tracker/ios/ViewManagers/MattermostKeyboardTrackerViewManager.mm create mode 100644 libraries/@mattermost/keyboard-tracker/ios/Views/KeyboardTrackingView.h create mode 100644 libraries/@mattermost/keyboard-tracker/ios/Views/KeyboardTrackingView.m create mode 100644 libraries/@mattermost/keyboard-tracker/ios/Views/ObservingInputAccessoryView.h create mode 100644 libraries/@mattermost/keyboard-tracker/ios/Views/ObservingInputAccessoryView.m create mode 100644 libraries/@mattermost/keyboard-tracker/mattermost-keyboard-tracker.podspec create mode 100644 libraries/@mattermost/keyboard-tracker/package.json create mode 100644 libraries/@mattermost/keyboard-tracker/src/MattermostKeyboardTrackerViewNativeComponent.ts create mode 100644 libraries/@mattermost/keyboard-tracker/src/index.tsx create mode 100644 libraries/@mattermost/rnshare/android/build.gradle create mode 100644 libraries/@mattermost/rnshare/android/src/main/AndroidManifest.xml create mode 100644 libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/MattermostShareImpl.kt create mode 100644 libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/MattermostSharePackage.kt create mode 100644 libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/ShareActivity.kt create mode 100644 libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/ShareUtils.kt create mode 100644 libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/ShareWorker.kt create mode 100644 libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/helpers/Extensions.kt create mode 100644 libraries/@mattermost/rnshare/android/src/main/java/com/mattermost/rnshare/helpers/RealPathUtil.kt create mode 100644 libraries/@mattermost/rnshare/android/src/newarch/MattermostShareModule.kt create mode 100644 libraries/@mattermost/rnshare/android/src/oldarch/MattermostShareModule.kt create mode 100644 libraries/@mattermost/rnshare/package.json create mode 100644 libraries/@mattermost/rnshare/src/NativeMattermostShare.ts create mode 100644 libraries/@mattermost/rnshare/src/index.tsx create mode 100644 libraries/@mattermost/rnutils/android/build.gradle create mode 100644 libraries/@mattermost/rnutils/android/src/main/AndroidManifest.xml create mode 100644 libraries/@mattermost/rnutils/android/src/main/java/com/mattermost/rnutils/RNUtilsModuleImpl.kt create mode 100644 libraries/@mattermost/rnutils/android/src/main/java/com/mattermost/rnutils/RNUtilsPackage.kt create mode 100644 libraries/@mattermost/rnutils/android/src/main/java/com/mattermost/rnutils/enums/Events.kt rename {android/app/src/main/java/com/mattermost/rnbeta => libraries/@mattermost/rnutils/android/src/main/java/com/mattermost/rnutils/helpers}/FoldableObserver.kt (80%) create mode 100644 libraries/@mattermost/rnutils/android/src/main/java/com/mattermost/rnutils/helpers/NotificationHelper.kt create mode 100644 libraries/@mattermost/rnutils/android/src/main/java/com/mattermost/rnutils/helpers/Notifications.kt create mode 100644 libraries/@mattermost/rnutils/android/src/main/java/com/mattermost/rnutils/helpers/RealPathUtil.kt create mode 100644 libraries/@mattermost/rnutils/android/src/main/java/com/mattermost/rnutils/helpers/SaveDataTask.kt create mode 100644 libraries/@mattermost/rnutils/android/src/main/java/com/mattermost/rnutils/helpers/SplitView.kt create mode 100644 libraries/@mattermost/rnutils/android/src/newarch/RNUtilsModule.kt create mode 100644 libraries/@mattermost/rnutils/android/src/oldarch/RNUtilsModule.kt create mode 100644 libraries/@mattermost/rnutils/ios/Extensions/RNUtilsWrapper+Events.swift create mode 100644 libraries/@mattermost/rnutils/ios/Managers/NotificationManager.swift create mode 100644 libraries/@mattermost/rnutils/ios/Managers/OrientationManager.swift create mode 100644 libraries/@mattermost/rnutils/ios/Protocols/OrientationLockable.swift create mode 100644 libraries/@mattermost/rnutils/ios/Protocols/RNUtilsDelegate.swift create mode 100644 libraries/@mattermost/rnutils/ios/RNUtils.h create mode 100644 libraries/@mattermost/rnutils/ios/RNUtils.mm create mode 100644 libraries/@mattermost/rnutils/ios/RNUtilsWrapper.swift create mode 100644 libraries/@mattermost/rnutils/mattermost-rnutils.podspec create mode 100644 libraries/@mattermost/rnutils/package.json create mode 100644 libraries/@mattermost/rnutils/src/NativeRNUtils.ts create mode 100644 libraries/@mattermost/rnutils/src/index.tsx delete mode 100644 patches/@gorhom+bottom-sheet+4.6.1.patch rename patches/{@react-native-community+netinfo+11.3.1.patch => @react-native-community+netinfo+11.3.2.patch} (100%) create mode 100644 patches/@rneui+base+4.0.0-rc.8.patch rename patches/{@sentry+utils+7.100.1.patch => @sentry+utils+7.117.0.patch} (100%) rename patches/{@types+react-syntax-highlighter+15.5.11.patch => @types+react-syntax-highlighter+15.5.13.patch} (100%) create mode 100644 patches/expo-image+1.12.12.patch create mode 100644 patches/expo-modules-core+1.12.15.patch create mode 100644 patches/jest-expo+51.0.2.patch delete mode 100644 patches/react-native-button+3.1.0.patch delete mode 100644 patches/react-native-create-thumbnail+1.6.4.patch delete mode 100644 patches/react-native-device-info+10.13.1.patch delete mode 100644 patches/react-native-elements+3.4.3.patch delete mode 100644 patches/react-native-fast-image+8.6.3.patch delete mode 100644 patches/react-native-hw-keyboard-event+0.0.4.patch delete mode 100644 patches/react-native-keyboard-tracking-view+5.7.0.patch rename patches/{react-native-navigation+7.39.1.patch => react-native-navigation+7.40.0.patch} (50%) rename patches/{react-native-safe-area-context+4.9.0.patch => react-native-safe-area-context+4.10.5.patch} (100%) rename patches/{react-native-svg+15.1.0.patch => react-native-svg+15.3.0.patch} (100%) delete mode 100644 patches/react-native-video+5.2.1.patch create mode 100644 patches/react-native-video+6.2.0.patch create mode 100644 patches/react-native-webrtc+124.0.1.patch delete mode 100644 test/file_transformer.js delete mode 100644 types/global/share_extension.d.ts delete mode 100644 types/global/svg.d.ts delete mode 100644 types/modules/react-native-keyboard-tracking-view.d.ts diff --git a/.eslintrc.json b/.eslintrc.json index 229d31473..b4435ff30 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -69,7 +69,7 @@ "newlines-between": "always", "pathGroups": [ { - "pattern": "{@(@actions|@app|@assets|@calls|@client|@components|@constants|@context|@database|@helpers|@hooks|@init|@managers|@queries|@screens|@selectors|@share|@store|@telemetry|@typings|@test|@utils)/**,@(@constants|@i18n|@notifications|@store|@websocket)}", + "pattern": "{@(@actions|@app|@assets|@calls|@client|@components|@constants|@context|@database|@helpers|@hooks|@init|@managers|@queries|@screens|@selectors|@share|@store|@telemetry|@typings|@test|@utils)/**,@(@constants|@i18n|@store|@websocket)}", "group": "external", "position": "after" }, diff --git a/.github/workflows/build-android-beta.yml b/.github/workflows/build-android-beta.yml index 2b9f80912..e24d6c663 100644 --- a/.github/workflows/build-android-beta.yml +++ b/.github/workflows/build-android-beta.yml @@ -8,7 +8,7 @@ on: - build-beta-android-[0-9]+ env: - NODE_VERSION: 18.7.0 + NODE_VERSION: 20.13.1 TERM: xterm jobs: diff --git a/.github/workflows/build-android-release.yml b/.github/workflows/build-android-release.yml index d570d66c2..cfdefb84b 100644 --- a/.github/workflows/build-android-release.yml +++ b/.github/workflows/build-android-release.yml @@ -8,7 +8,7 @@ on: - build-release-android-[0-9]+ env: - NODE_VERSION: 18.7.0 + NODE_VERSION: 20.13.1 TERM: xterm jobs: diff --git a/.github/workflows/build-ios-beta.yml b/.github/workflows/build-ios-beta.yml index 4c71a3108..da4d289bd 100644 --- a/.github/workflows/build-ios-beta.yml +++ b/.github/workflows/build-ios-beta.yml @@ -9,7 +9,7 @@ on: - build-beta-sim-[0-9]+ env: - NODE_VERSION: 18.7.0 + NODE_VERSION: 20.13.1 TERM: xterm jobs: diff --git a/.github/workflows/build-ios-release.yml b/.github/workflows/build-ios-release.yml index 0143b5c89..ad1009e08 100644 --- a/.github/workflows/build-ios-release.yml +++ b/.github/workflows/build-ios-release.yml @@ -9,7 +9,7 @@ on: - build-release-sim-[0-9]+ env: - NODE_VERSION: 18.7.0 + NODE_VERSION: 20.13.1 TERM: xterm jobs: diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index e8b01b65d..98757478d 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -6,7 +6,7 @@ on: - labeled env: - NODE_VERSION: 18.7.0 + NODE_VERSION: 20.13.1 TERM: xterm jobs: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57e9448c2..b9cecf7ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ on: pull_request: env: - NODE_VERSION: 18.7.0 + NODE_VERSION: 20.13.1 TERM: xterm jobs: diff --git a/.gitignore b/.gitignore index 600fc0dfa..9b208b47c 100644 --- a/.gitignore +++ b/.gitignore @@ -115,3 +115,6 @@ launch.json # Temporary files created by Metro to check the health of the file watcher .metro-health-check* + +libraries/**/**/build +libraries/**/**/.build \ No newline at end of file diff --git a/.node-version b/.node-version index aacb51810..297d47ba0 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -18.17 +20.13.1 \ No newline at end of file diff --git a/.nvmrc b/.nvmrc index aacb51810..f203ab89b 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.17 +20.13.1 diff --git a/.solidarity b/.solidarity index f7aa6366b..61768427b 100644 --- a/.solidarity +++ b/.solidarity @@ -39,7 +39,7 @@ { "rule": "cli", "binary": "pod", - "semver": "1.14.3", + "semver": "1.15.2", "platform": "darwin" } ], diff --git a/ios/Gemfile b/Gemfile similarity index 54% rename from ios/Gemfile rename to Gemfile index 8c4e3a146..813f0e00d 100644 --- a/ios/Gemfile +++ b/Gemfile @@ -1,3 +1,3 @@ source "https://rubygems.org" -gem "cocoapods", "1.14.3" +gem "cocoapods", "1.15.2" diff --git a/ios/Gemfile.lock b/Gemfile.lock similarity index 82% rename from ios/Gemfile.lock rename to Gemfile.lock index d4d156e90..4751c61aa 100644 --- a/ios/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,11 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.6) + CFPropertyList (3.0.7) + base64 + nkf rexml - activesupport (7.1.2) + activesupport (7.1.3.4) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) @@ -13,19 +15,19 @@ GEM minitest (>= 5.1) mutex_m tzinfo (~> 2.0) - addressable (2.8.5) + addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) atomos (0.1.3) base64 (0.2.0) - bigdecimal (3.1.4) + bigdecimal (3.1.8) claide (1.1.0) - cocoapods (1.14.3) + cocoapods (1.15.2) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.14.3) + cocoapods-core (= 1.15.2) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 2.1, < 3.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -40,7 +42,7 @@ GEM nap (~> 1.0) ruby-macho (>= 2.3.0, < 3.0) xcodeproj (>= 1.23.0, < 2.0) - cocoapods-core (1.14.3) + cocoapods-core (1.15.2) activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) @@ -60,36 +62,37 @@ GEM netrc (~> 0.11) cocoapods-try (1.2.0) colored2 (3.1.2) - concurrent-ruby (1.2.2) + concurrent-ruby (1.3.3) connection_pool (2.4.1) - drb (2.2.0) - ruby2_keywords + drb (2.2.1) escape (0.0.4) ethon (0.16.0) ffi (>= 1.15.0) - ffi (1.16.3) + ffi (1.17.0) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) httpclient (2.8.3) - i18n (1.14.1) + i18n (1.14.5) concurrent-ruby (~> 1.0) - json (2.6.3) - minitest (5.20.0) + json (2.7.2) + minitest (5.23.1) molinillo (0.8.0) mutex_m (0.2.0) nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) + nkf (0.2.0) public_suffix (4.0.7) - rexml (3.2.6) + rexml (3.2.9) + strscan ruby-macho (2.5.1) - ruby2_keywords (0.0.5) + strscan (3.1.0) typhoeus (1.4.1) ethon (>= 0.9.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - xcodeproj (1.23.0) + xcodeproj (1.24.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) @@ -101,7 +104,7 @@ PLATFORMS ruby DEPENDENCIES - cocoapods (= 1.14.3) + cocoapods (= 1.15.2) BUNDLED WITH - 2.3.26 + 2.5.11 diff --git a/NOTICE.txt b/NOTICE.txt index 441354b1e..13569ac5c 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1163,42 +1163,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- - -## jail-monkey - -This product contains 'jail-monkey' by Gant Laborde. - -A React Native module for identifying jail-broken, rooted, or mock locations on iOS and Android - -* HOMEPAGE: - * https://github.com/GantMan/jail-monkey#readme - -* LICENSE: MIT - -MIT License - -Copyright (c) 2017 Gant Laborde - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --- ## mime-db @@ -1526,77 +1490,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- - -## react-native-button - -This product contains 'react-native-button' by James Ide. - -A button for React apps - -* HOMEPAGE: - * https://github.com/ide/react-native-button - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2015 James Ide - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - ---- - -## react-native-calendars - -This product contains a modified version of 'react-native-calendars' by Wix.com. - -React Native Calendar Components - -* HOMEPAGE: - * https://github.com/wix/react-native-calendars#readme - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2017 Wix.com - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - --- ## react-native-create-thumbnail @@ -1633,42 +1526,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- - -## react-native-device-info - -This product contains a modified version of 'react-native-device-info' by Rebecca Hughes. - -Get device information using react-native - -* HOMEPAGE: - * https://github.com/react-native-community/react-native-device-info#readme - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2015 Rebecca Hughes - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --- ## react-native-document-picker @@ -1741,40 +1598,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- - -## react-native-elements - -This product contains 'react-native-elements' by React Native Elements. - -Cross Platform React Native UI Toolkit - -* HOMEPAGE: - * https://github.com/react-native-elements/react-native-elements - -* LICENSE: The MIT License (MIT) - -Copyright (c) 2016 Nader Dabit - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --- ## react-native-exception-handler @@ -1881,42 +1704,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- - -## react-native-fs - -This product contains 'react-native-fs' by Hagen Hübel. - -Native filesystem access for react-native - -* HOMEPAGE: - * https://github.com/itinance/react-native-fs - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2015 Johannes Lumpe - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --- ## react-native-gesture-handler @@ -2049,42 +1836,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- - -## react-native-in-app-review - -This product contains 'react-native-in-app-review' by Mina Samir Shafik. - -react native in app review, to rate on Play store, App Store, Generally, the in-app review flow (see figure 1 for play store, figure 2 for ios) can be triggered at any time throughout the user journey of your app. During the flow, the user has the ability - -* HOMEPAGE: - * https://github.com/MinaSamir11/react-native-in-app-review#readme - -* LICENSE: MIT - -MIT License - -Copyright (c) 2020 Mina Samir - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --- ## react-native-incall-manager @@ -2151,42 +1902,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- - -## react-native-keyboard-tracking-view - -This product contains a modified version of 'react-native-keyboard-tracking-view' by Artal Druk. - -React Native UI component which tracks the keyboard - -* HOMEPAGE: - * https://github.com/wix/react-native-keyboard-tracking-view - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2016 Wix.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --- ## react-native-keychain @@ -2223,42 +1938,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- - -## react-native-linear-gradient - -This product contains 'react-native-linear-gradient' by Brent Vatne. - -A element for React Native - -* HOMEPAGE: - * https://github.com/react-native-community/react-native-linear-gradient#readme - -* LICENSE: MIT - -MIT License - -Copyright (c) 2016 React Native Community - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --- ## react-native-localize @@ -2848,42 +2527,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- - -## react-native-webview - -This product contains a modified version of 'react-native-webview' by Jamon Holmgren. - -React Native WebView component for iOS, Android, and Windows 10 (coming soon) - -* HOMEPAGE: - * https://github.com/react-native-community/react-native-webview#readme - -* LICENSE: MIT - -MIT License - -Copyright (c) 2015-present, Facebook, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --- ## react-syntax-highlighter diff --git a/android/app/build.gradle b/android/app/build.gradle index 4ac525a5d..488d3a6c6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -166,6 +166,7 @@ android { matchingFallbacks = ['release'] } } + // applicationVariants are e.g. debug, release applicationVariants.all { variant -> variant.outputs.each { output -> @@ -190,7 +191,6 @@ repositories { dependencies { // The version of react-native is set by the React Native Gradle Plugin implementation("com.facebook.react:react-android") - implementation("com.facebook.react:flipper-integration") if (hermesEnabled.toBoolean()) { implementation("com.facebook.react:hermes-android") @@ -198,14 +198,9 @@ dependencies { implementation jscFlavor } - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' - implementation 'io.reactivex.rxjava3:rxjava:3.1.6' - implementation 'io.reactivex.rxjava3:rxandroid:3.0.2' - implementation 'androidx.window:window-core:1.1.0' - implementation 'androidx.window:window-rxjava3:1.0.0' - implementation 'androidx.window:window:1.0.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1' implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.8.0' + implementation 'com.google.android.material:material:1.12.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation "com.google.firebase:firebase-messaging:$firebaseVersion" @@ -247,7 +242,7 @@ configurations.all { // Run this once to be able to run the application with BUCK // puts all compile dependencies into folder libs for BUCK to use -task copyDownloadableDepsToLibs(type: Copy) { +tasks.register('copyDownloadableDepsToLibs', Copy) { from configurations.implementation into 'libs' } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 3b44cb982..227ee36dc 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -94,7 +94,7 @@ android:exported="false" /> inputMap = new HashMap<>(); - saveMap(context, inputMap); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - public static int getNotificationId(Bundle notification) { - final String postId = notification.getString("post_id"); - final String channelId = notification.getString("channel_id"); - - int notificationId = CustomPushNotificationHelper.MESSAGE_NOTIFICATION_ID; - if (postId != null) { - notificationId = postId.hashCode(); - } else if (channelId != null) { - notificationId = channelId.hashCode(); - } - - return notificationId; - } - - public static StatusBarNotification[] getDeliveredNotifications(Context context) { - final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - return notificationManager.getActiveNotifications(); - } - - public static boolean addNotificationToPreferences(Context context, int notificationId, Bundle notification) { - try { - boolean createSummary = true; - final String serverUrl = notification.getString("server_url"); - final String channelId = notification.getString("channel_id"); - final String rootId = notification.getString("root_id"); - final boolean isCRTEnabled = notification.containsKey("is_crt_enabled") && notification.getString("is_crt_enabled").equals("true"); - - final boolean isThreadNotification = isCRTEnabled && !android.text.TextUtils.isEmpty(rootId); - final String groupId = isThreadNotification ? rootId : channelId; - - Map notificationsPerServer = loadMap(context); - JSONObject notificationsInServer = notificationsPerServer.get(serverUrl); - if (notificationsInServer == null) { - notificationsInServer = new JSONObject(); - } - - JSONObject notificationsInGroup = notificationsInServer.optJSONObject(groupId); - if (notificationsInGroup == null) { - notificationsInGroup = new JSONObject(); - } - - if (notificationsInGroup.length() > 0) { - createSummary = false; - } - - notificationsInGroup.put(String.valueOf(notificationId), false); - - if (createSummary) { - // Add the summary notification id as well - notificationsInGroup.put(String.valueOf(notificationId + 1), true); - } - notificationsInServer.put(groupId, notificationsInGroup); - notificationsPerServer.put(serverUrl, notificationsInServer); - saveMap(context, notificationsPerServer); - - return createSummary; - } catch(Exception e) { - e.printStackTrace(); - return false; - } - } - - public static void dismissNotification(Context context, Bundle notification) { - final boolean isCRTEnabled = notification.containsKey("is_crt_enabled") && notification.getString("is_crt_enabled").equals("true"); - final String serverUrl = notification.getString("server_url"); - final String channelId = notification.getString("channel_id"); - final String rootId = notification.getString("root_id"); - - int notificationId = getNotificationId(notification); - - if (!android.text.TextUtils.isEmpty(serverUrl) && !android.text.TextUtils.isEmpty(channelId)) { - boolean isThreadNotification = isCRTEnabled && !android.text.TextUtils.isEmpty(rootId); - String notificationIdStr = String.valueOf(notificationId); - String groupId = isThreadNotification ? rootId : channelId; - - Map notificationsPerServer = loadMap(context); - JSONObject notificationsInServer = notificationsPerServer.get(serverUrl); - if (notificationsInServer == null) { - return; - } - - JSONObject notificationsInGroup = notificationsInServer.optJSONObject(groupId); - if (notificationsInGroup == null) { - return; - } - boolean isSummary = notificationsInGroup.optBoolean(notificationIdStr); - notificationsInGroup.remove(notificationIdStr); - - NotificationManager notificationManager = context.getSystemService(NotificationManager.class); - notificationManager.cancel(notificationId); - StatusBarNotification[] statusNotifications = getDeliveredNotifications(context); - boolean hasMore = false; - - for (final StatusBarNotification status : statusNotifications) { - Bundle bundle = status.getNotification().extras; - if (isThreadNotification) { - hasMore = bundle.containsKey("root_id") && bundle.getString("root_id").equals(rootId); - } else { - hasMore = bundle.containsKey("channel_id") && bundle.getString("channel_id").equals(channelId); - } - if (hasMore) break; - } - - if (!hasMore || isSummary) { - notificationsInServer.remove(groupId); - } else { - try { - notificationsInServer.put(groupId, notificationsInGroup); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - notificationsPerServer.put(serverUrl, notificationsInServer); - saveMap(context, notificationsPerServer); - } - } - - public static void removeChannelNotifications(Context context, String serverUrl, String channelId) { - NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); - Map notificationsPerServer = loadMap(context); - JSONObject notificationsInServer = notificationsPerServer.get(serverUrl); - - if (notificationsInServer != null) { - notificationsInServer.remove(channelId); - notificationsPerServer.put(serverUrl, notificationsInServer); - saveMap(context, notificationsPerServer); - } - - StatusBarNotification[] notifications = getDeliveredNotifications(context); - for (StatusBarNotification sbn:notifications) { - Notification n = sbn.getNotification(); - Bundle bundle = n.extras; - String cId = bundle.getString("channel_id"); - String rootId = bundle.getString("root_id"); - boolean isCRTEnabled = bundle.containsKey("is_crt_enabled") && bundle.getString("is_crt_enabled").equals("true"); - boolean skipThreadNotification = isCRTEnabled && !android.text.TextUtils.isEmpty(rootId); - if (Objects.equals(cId, channelId) && !skipThreadNotification) { - notificationManager.cancel(sbn.getId()); - } - } - } - - public static void removeThreadNotifications(Context context, String serverUrl, String threadId) { - NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); - Map notificationsPerServer = loadMap(context); - JSONObject notificationsInServer = notificationsPerServer.get(serverUrl); - - StatusBarNotification[] notifications = getDeliveredNotifications(context); - for (StatusBarNotification sbn:notifications) { - Notification n = sbn.getNotification(); - Bundle bundle = n.extras; - String rootId = bundle.getString("root_id"); - String postId = bundle.getString("post_id"); - if (Objects.equals(rootId, threadId)) { - notificationManager.cancel(sbn.getId()); - } - - if (Objects.equals(postId, threadId)) { - String channelId = bundle.getString("channel_id"); - int id = sbn.getId(); - if (notificationsInServer != null && channelId != null) { - JSONObject notificationsInChannel = notificationsInServer.optJSONObject(channelId); - if (notificationsInChannel != null) { - notificationsInChannel.remove(String.valueOf(id)); - try { - notificationsInServer.put(channelId, notificationsInChannel); - } catch (JSONException e) { - e.printStackTrace(); - } - } - } - notificationManager.cancel(id); - } - } - - if (notificationsInServer != null) { - notificationsInServer.remove(threadId); - notificationsPerServer.put(serverUrl, notificationsInServer); - saveMap(context, notificationsPerServer); - } - } - - public static void removeServerNotifications(Context context, String serverUrl) { - NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); - Map notificationsPerServer = loadMap(context); - notificationsPerServer.remove(serverUrl); - saveMap(context, notificationsPerServer); - StatusBarNotification[] notifications = getDeliveredNotifications(context); - for (StatusBarNotification sbn:notifications) { - Notification n = sbn.getNotification(); - Bundle bundle = n.extras; - String url = bundle.getString("server_url"); - if (Objects.equals(url, serverUrl)) { - notificationManager.cancel(sbn.getId()); - } - } - } - - public static void clearChannelOrThreadNotifications(Context context, Bundle notification) { - final String serverUrl = notification.getString("server_url"); - final String channelId = notification.getString("channel_id"); - final String rootId = notification.getString("root_id"); - if (channelId != null) { - final boolean isCRTEnabled = notification.containsKey("is_crt_enabled") && notification.getString("is_crt_enabled").equals("true"); - // rootId is available only when CRT is enabled & clearing the thread - final boolean isClearThread = isCRTEnabled && !android.text.TextUtils.isEmpty(rootId); - - if (isClearThread) { - removeThreadNotifications(context, serverUrl, rootId); - } else { - removeChannelNotifications(context, serverUrl, channelId); - } - } - } - - - /** - * Map Structure - * - * { serverUrl: { groupId: { notification1: true, notification2: false } } } - * summary notification has a value of true - * - */ - - private static void saveMap(Context context, Map inputMap) { - SharedPreferences pSharedPref = context.getSharedPreferences(PUSH_NOTIFICATIONS, Context.MODE_PRIVATE); - if (pSharedPref != null) { - JSONObject json = new JSONObject(inputMap); - String jsonString = json.toString(); - SharedPreferences.Editor editor = pSharedPref.edit(); - editor.remove(NOTIFICATIONS_IN_GROUP).apply(); - editor.putString(NOTIFICATIONS_IN_GROUP, jsonString); - editor.apply(); - } - } - - private static Map loadMap(Context context) { - Map outputMap = new HashMap<>(); - if (context != null) { - SharedPreferences pSharedPref = context.getSharedPreferences(PUSH_NOTIFICATIONS, Context.MODE_PRIVATE); - try { - if (pSharedPref != null) { - String jsonString = pSharedPref.getString(NOTIFICATIONS_IN_GROUP, (new JSONObject()).toString()); - JSONObject json = new JSONObject(jsonString); - Iterator servers = json.keys(); - - while (servers.hasNext()) { - String serverUrl = servers.next(); - JSONObject notificationGroup = json.getJSONObject(serverUrl); - outputMap.put(serverUrl, notificationGroup); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - return outputMap; - } -} diff --git a/android/app/src/main/java/com/mattermost/helpers/RealPathUtil.java b/android/app/src/main/java/com/mattermost/helpers/RealPathUtil.java deleted file mode 100644 index 1508b40c1..000000000 --- a/android/app/src/main/java/com/mattermost/helpers/RealPathUtil.java +++ /dev/null @@ -1,274 +0,0 @@ -package com.mattermost.helpers; - -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.provider.DocumentsContract; -import android.provider.MediaStore; -import android.provider.OpenableColumns; -import android.content.ContentResolver; -import android.os.Environment; -import android.webkit.MimeTypeMap; -import android.util.Log; -import android.text.TextUtils; - -import android.os.ParcelFileDescriptor; - -import java.io.*; -import java.nio.channels.FileChannel; - -// Class based on DocumentHelper https://gist.github.com/steveevers/a5af24c226f44bb8fdc3 - -public class RealPathUtil { - public static final String CACHE_DIR_NAME = "mmShare"; - 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); - 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] - }; - - String name = getDataColumn(context, contentUri, selection, selectionArgs); - if (!TextUtils.isEmpty(name)) { - return name; - } - - 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 = ""; - - 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); - boolean cacheDirExists = cacheDir.exists(); - if (!cacheDirExists) { - cacheDirExists = cacheDir.mkdirs(); - } - - if (cacheDirExists) { - tmpFile = new File(cacheDir, fileName); - boolean fileCreated = tmpFile.createNewFile(); - - if (fileCreated) { - ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r"); - - try (FileInputStream inputSrc = new FileInputStream(pfd.getFileDescriptor())) { - FileChannel src = inputSrc.getChannel(); - try (FileOutputStream outputDst = new FileOutputStream(tmpFile)) { - FileChannel dst = outputDst.getChannel(); - dst.transferFrom(src, 0, src.size()); - src.close(); - dst.close(); - } - } - - pfd.close(); - } - return tmpFile.getAbsolutePath(); - } - } catch (IOException ex) { - ex.printStackTrace(); - } - - return null; - } - - 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) { - String extension = ""; - if (uri == null) { - return extension; - } - - extension = MimeTypeMap.getFileExtensionFromUrl(uri); - if (!extension.equals("")) { - return extension; - } - - 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()) { - File[] files = fileOrDirectory.listFiles(); - if (files != null) { - for (File child : files) - deleteRecursive(child); - } - } - - if (!fileOrDirectory.delete()) { - Log.i("ReactNative", "Couldn't delete file " + fileOrDirectory.getName()); - } - } - - private static String sanitizeFilename(String filename) { - if (filename == null) { - return null; - } - - File f = new File(filename); - return f.getName(); - } -} diff --git a/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.kt b/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.kt index 192355c6c..f19ee2d20 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.kt +++ b/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.kt @@ -8,10 +8,9 @@ import androidx.core.app.NotificationCompat import com.mattermost.helpers.CustomPushNotificationHelper import com.mattermost.helpers.DatabaseHelper import com.mattermost.helpers.Network -import com.mattermost.helpers.NotificationHelper import com.mattermost.helpers.PushNotificationDataHelper import com.mattermost.helpers.database_extension.getServerUrlForIdentifier -import com.mattermost.share.ShareModule +import com.mattermost.rnutils.helpers.NotificationHelper import com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME import com.wix.reactnativenotifications.core.AppLaunchHelper import com.wix.reactnativenotifications.core.AppLifecycleFacade @@ -104,8 +103,7 @@ class CustomPushNotification( when (type) { CustomPushNotificationHelper.PUSH_TYPE_MESSAGE, CustomPushNotificationHelper.PUSH_TYPE_SESSION -> { - val shareModule = ShareModule.getInstance() - val currentActivityName = shareModule?.currentActivityName ?: "" + val currentActivityName = mAppLifecycleFacade.runningReactContext.currentActivity?.componentName?.className ?: "" Log.i("ReactNative", currentActivityName) if (!mAppLifecycleFacade.isAppVisible() || currentActivityName != "MainActivity") { var createSummary = type == CustomPushNotificationHelper.PUSH_TYPE_MESSAGE diff --git a/android/app/src/main/java/com/mattermost/rnbeta/MainActivity.kt b/android/app/src/main/java/com/mattermost/rnbeta/MainActivity.kt index 720bc5666..049f88542 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/MainActivity.kt +++ b/android/app/src/main/java/com/mattermost/rnbeta/MainActivity.kt @@ -1,15 +1,15 @@ package com.mattermost.rnbeta -import android.os.Bundle - -import android.view.KeyEvent import android.content.res.Configuration - +import android.os.Bundle +import android.view.KeyEvent import com.facebook.react.ReactActivityDelegate -import com.reactnativenavigation.NavigationActivity -import com.github.emilioicai.hwkeyboardevent.HWKeyboardEventModule import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint import com.facebook.react.defaults.DefaultReactActivityDelegate +import com.mattermost.hardware.keyboard.MattermostHardwareKeyboardImpl +import com.mattermost.rnutils.helpers.FoldableObserver +import com.reactnativenavigation.NavigationActivity +import expo.modules.ReactActivityDelegateWrapper class MainActivity : NavigationActivity() { private var HWKeyboardConnected = false @@ -26,7 +26,8 @@ class MainActivity : NavigationActivity() { * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] */ override fun createReactActivityDelegate(): ReactActivityDelegate = - DefaultReactActivityDelegate(this, mainComponentName, DefaultNewArchitectureEntryPoint.fabricEnabled) + ReactActivityDelegateWrapper(this, BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, + DefaultReactActivityDelegate(this, mainComponentName, DefaultNewArchitectureEntryPoint.fabricEnabled)) override fun onCreate(savedInstanceState: Bundle?) { @@ -46,6 +47,11 @@ class MainActivity : NavigationActivity() { foldableObserver.onStop() } + override fun onDestroy() { + super.onDestroy() + foldableObserver.onDestroy() + } + override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) { @@ -60,24 +66,11 @@ class MainActivity : NavigationActivity() { reactGateway.onWindowFocusChanged(hasFocus) } - /* - https://mattermost.atlassian.net/browse/MM-10601 - Required by react-native-hw-keyboard-event - (https://github.com/emilioicai/react-native-hw-keyboard-event) - */ override fun dispatchKeyEvent(event: KeyEvent): Boolean { if (HWKeyboardConnected) { - val keyCode = event.keyCode - val keyAction = event.action - if (keyAction == KeyEvent.ACTION_UP) { - if (keyCode == KeyEvent.KEYCODE_ENTER) { - val keyPressed = if (event.isShiftPressed) "shift-enter" else "enter" - HWKeyboardEventModule.getInstance().keyPressed(keyPressed) - return true - } else if (keyCode == KeyEvent.KEYCODE_K && event.isCtrlPressed) { - HWKeyboardEventModule.getInstance().keyPressed("find-channels") - return true - } + val ok = MattermostHardwareKeyboardImpl.dispatchKeyEvent(event) + if (ok) { + return true } } return super.dispatchKeyEvent(event) diff --git a/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.kt b/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.kt index 6d7693f55..fb323ac48 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.kt +++ b/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.kt @@ -1,151 +1,67 @@ package com.mattermost.rnbeta + +import android.annotation.SuppressLint import android.content.Context +import android.content.res.Configuration import android.os.Bundle import android.util.Log - import com.facebook.react.PackageList import com.facebook.react.ReactHost +import com.facebook.react.ReactInstanceManager import com.facebook.react.ReactNativeHost import com.facebook.react.ReactPackage -import com.facebook.react.TurboReactPackage -import com.facebook.react.bridge.JSIModulePackage -import com.facebook.react.bridge.JSIModuleSpec -import com.facebook.react.bridge.JavaScriptContextHolder -import com.facebook.react.bridge.NativeModule -import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContext +import com.facebook.react.bridge.UiThreadUtil import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost import com.facebook.react.defaults.DefaultReactNativeHost -import com.facebook.react.flipper.ReactNativeFlipper -import com.facebook.react.module.model.ReactModuleInfo -import com.facebook.react.module.model.ReactModuleInfoProvider import com.facebook.react.modules.network.OkHttpClientProvider import com.facebook.soloader.SoLoader - -import com.mattermost.helpers.RealPathUtil import com.mattermost.networkclient.RCTOkHttpClientFactory -import com.mattermost.share.ShareModule - -import com.nozbe.watermelondb.jsi.WatermelonDBJSIPackage - +import com.mattermost.rnshare.helpers.RealPathUtil +import com.nozbe.watermelondb.jsi.JSIInstaller import com.reactnativenavigation.NavigationApplication - import com.wix.reactnativenotifications.RNNotificationsPackage import com.wix.reactnativenotifications.core.AppLaunchHelper import com.wix.reactnativenotifications.core.AppLifecycleFacade import com.wix.reactnativenotifications.core.JsIOHelper import com.wix.reactnativenotifications.core.notification.INotificationsApplication import com.wix.reactnativenotifications.core.notification.IPushNotification - +import expo.modules.ApplicationLifecycleDispatcher +import expo.modules.ReactNativeHostWrapper +import expo.modules.image.okhttp.ExpoImageOkHttpClientGlideModule import java.io.File class MainApplication : NavigationApplication(), INotificationsApplication { - var instance: MainApplication? = null - var sharedExtensionIsOpened = false + private var listenerAdded = false override val reactNativeHost: ReactNativeHost = - object : DefaultReactNativeHost(this) { - override fun getPackages(): List = - PackageList(this).packages.apply { - // Packages that cannot be autolinked yet can be added manually here, for example: - // add(MyReactNativePackage()) - add(RNNotificationsPackage(this@MainApplication)) - add(object : TurboReactPackage() { - override fun getModule( - name: String, - reactContext: ReactApplicationContext - ): NativeModule { - return when (name) { - "MattermostManaged" -> MattermostManagedModule.getInstance( - reactContext - ) - "MattermostShare" -> ShareModule.getInstance(reactContext) - "Notifications" -> NotificationsModule.getInstance( - instance, - reactContext - ) - "SplitView" -> SplitViewModule.getInstance( - reactContext - ) - else -> - throw IllegalArgumentException("Could not find module $name") - } - } + ReactNativeHostWrapper(this, + object : DefaultReactNativeHost(this) { + override fun getPackages(): List = + PackageList(this).packages.apply { + // Packages that cannot be autolinked yet can be added manually here, for example: + // add(MyReactNativePackage()) + add(RNNotificationsPackage(this@MainApplication)) + } - override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { - return ReactModuleInfoProvider { - val map: MutableMap = java.util.HashMap() - map["MattermostManaged"] = ReactModuleInfo( - "MattermostManaged", - "com.mattermost.rnbeta.MattermostManagedModule", - false, - false, - false, - false - ) - map["MattermostShare"] = ReactModuleInfo( - "MattermostShare", - "com.mattermost.share.ShareModule", - false, - false, - false, - false - ) - map["Notifications"] = ReactModuleInfo( - "Notifications", - "com.mattermost.rnbeta.NotificationsModule", - false, - false, - false, - false - ) - map["SplitView"] = ReactModuleInfo( - "SplitView", - "com.mattermost.rnbeta.SplitViewModule", - false, - false, - false, - false - ) - map - } - } - }) - } + override fun getJSMainModuleName(): String = "index" - override fun getJSIModulePackage(): JSIModulePackage { - return JSIModulePackage { reactApplicationContext: ReactApplicationContext?, jsContext: JavaScriptContextHolder? -> - val modules = - mutableListOf>() - modules.addAll( - WatermelonDBJSIPackage().getJSIModules( - reactApplicationContext, - jsContext - ) - ) - modules - } - } + override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG - override fun getJSMainModuleName(): String = "index" - - override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG - - override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED - override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED - } + override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED + override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED + }) override val reactHost: ReactHost - get() = getDefaultReactHost(this.applicationContext, reactNativeHost) + get() = getDefaultReactHost(applicationContext, reactNativeHost) override fun onCreate() { super.onCreate() - instance = this - val context: Context = applicationContext // Delete any previous temp files created by the app - val tempFolder = File(context.cacheDir, RealPathUtil.CACHE_DIR_NAME) + val tempFolder = File(applicationContext.cacheDir, RealPathUtil.CACHE_DIR_NAME) RealPathUtil.deleteTempFiles(tempFolder) Log.i("ReactNative", "Cleaning temp cache " + tempFolder.absolutePath) @@ -157,13 +73,20 @@ class MainApplication : NavigationApplication(), INotificationsApplication { // with a cookie jar defined in APIClientModule and an interceptor to intercept all // requests that originate from React Native's OKHttpClient OkHttpClientProvider.setOkHttpClientFactory(RCTOkHttpClientFactory()) + ExpoImageOkHttpClientGlideModule.okHttpClient = RCTOkHttpClientFactory().createNewNetworkModuleClient() SoLoader.init(this, false) if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { // If you opted-in for the New Architecture, we load the native entry point for this app. - load() + load(bridgelessEnabled = false) } - ReactNativeFlipper.initializeFlipper(this, reactNativeHost.reactInstanceManager) + ApplicationLifecycleDispatcher.onApplicationCreate(this) + registerJSIModules() + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig) } override fun getPushNotification( @@ -180,4 +103,38 @@ class MainApplication : NavigationApplication(), INotificationsApplication { JsIOHelper() ) } + + @SuppressLint("VisibleForTests") + private fun runOnJSQueueThread(action: () -> Unit) { + reactNativeHost.reactInstanceManager.currentReactContext?.runOnJSQueueThread { + action() + } ?: UiThreadUtil.runOnUiThread { + reactNativeHost.reactInstanceManager.currentReactContext?.runOnJSQueueThread { + action() + } + } + } + + @Suppress("DEPRECATION") + private fun registerJSIModules() { + val reactInstanceManager = reactNativeHost.reactInstanceManager + + if (!listenerAdded) { + listenerAdded = true + reactInstanceManager.addReactInstanceEventListener(object : ReactInstanceManager.ReactInstanceEventListener { + override fun onReactContextInitialized(context: ReactContext) { + runOnJSQueueThread { + registerWatermelonJSI(context) + } + } + }) + } + } + + private fun registerWatermelonJSI(context: ReactContext) { + val holder = context.javaScriptContextHolder?.get() + if (holder != null) { + JSIInstaller.install(context, holder) + } + } } diff --git a/android/app/src/main/java/com/mattermost/rnbeta/MattermostManagedModule.java b/android/app/src/main/java/com/mattermost/rnbeta/MattermostManagedModule.java deleted file mode 100644 index 765fc2fe8..000000000 --- a/android/app/src/main/java/com/mattermost/rnbeta/MattermostManagedModule.java +++ /dev/null @@ -1,263 +0,0 @@ -package com.mattermost.rnbeta; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.ParcelFileDescriptor; -import android.text.TextUtils; -import android.webkit.MimeTypeMap; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.FileProvider; - -import com.facebook.react.bridge.ActivityEventListener; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.BaseActivityEventListener; -import com.facebook.react.bridge.GuardedResultAsyncTask; -import com.facebook.react.bridge.Promise; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContextBaseJavaModule; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.modules.core.DeviceEventManagerModule; - -import com.mattermost.helpers.Credentials; -import com.reactlibrary.createthumbnail.CreateThumbnailModule; -import com.mattermost.helpers.RealPathUtil; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.lang.ref.WeakReference; -import java.net.URL; -import java.nio.channels.FileChannel; -import java.util.Objects; - -public class MattermostManagedModule extends ReactContextBaseJavaModule { - private static final String SAVE_EVENT = "MattermostManagedSaveFile"; - private static final Integer SAVE_REQUEST = 38641; - private static MattermostManagedModule instance; - private ReactApplicationContext reactContext; - - private Promise mPickerPromise; - private String fileContent; - - private MattermostManagedModule(ReactApplicationContext reactContext) { - super(reactContext); - this.reactContext = reactContext; - // Let the document provider know you're done by closing the stream. - ActivityEventListener mActivityEventListener = new BaseActivityEventListener() { - @Override - public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) { - if (requestCode == SAVE_REQUEST) { - if (mPickerPromise != null) { - if (resultCode == Activity.RESULT_CANCELED) { - mPickerPromise.reject(SAVE_EVENT, "Save operation cancelled"); - } else if (resultCode == Activity.RESULT_OK) { - Uri uri = intent.getData(); - if (uri == null) { - mPickerPromise.reject(SAVE_EVENT, "No data found"); - } else { - try { - new SaveDataTask(reactContext, fileContent, uri).execute(); - mPickerPromise.resolve(uri.toString()); - } catch (Exception e) { - mPickerPromise.reject(SAVE_EVENT, e.getMessage()); - } - } - } - - mPickerPromise = null; - } else if (resultCode == Activity.RESULT_OK) { - try { - Uri uri = intent.getData(); - if (uri != null) - new SaveDataTask(reactContext, fileContent, uri).execute(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - }; - reactContext.addActivityEventListener(mActivityEventListener); - } - - public static MattermostManagedModule getInstance(ReactApplicationContext reactContext) { - if (instance == null) { - instance = new MattermostManagedModule(reactContext); - } else { - instance.reactContext = reactContext; - } - - return instance; - } - - public static MattermostManagedModule getInstance() { - return instance; - } - - public void sendEvent(String eventName, - @Nullable WritableMap params) { - this.reactContext - .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) - .emit(eventName, params); - } - - @Override - @NonNull - public String getName() { - return "MattermostManaged"; - } - - @ReactMethod - public void getFilePath(String filePath, Promise promise) { - Activity currentActivity = getCurrentActivity(); - WritableMap map = Arguments.createMap(); - - if (currentActivity != null) { - Uri uri = Uri.parse(filePath); - String path = RealPathUtil.getRealPathFromURI(currentActivity, uri); - if (path != null) { - String text = "file://" + path; - map.putString("filePath", text); - } - } - - promise.resolve(map); - } - - @ReactMethod - public void saveFile(String path, final Promise promise) { - Uri contentUri; - String filename = ""; - if(path.startsWith("content://")) { - contentUri = Uri.parse(path); - } else { - File newFile = new File(path); - filename = newFile.getName(); - Activity currentActivity = getCurrentActivity(); - if(currentActivity == null) { - promise.reject(SAVE_EVENT, "Activity doesn't exist"); - return; - } - try { - final String packageName = currentActivity.getPackageName(); - final String authority = packageName + ".provider"; - contentUri = FileProvider.getUriForFile(currentActivity, authority, newFile); - } - catch(IllegalArgumentException e) { - promise.reject(SAVE_EVENT, e.getMessage()); - return; - } - } - - if(contentUri == null) { - promise.reject(SAVE_EVENT, "Invalid file"); - return; - } - - String extension = MimeTypeMap.getFileExtensionFromUrl(path).toLowerCase(); - String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); - if (mimeType == null) { - mimeType = RealPathUtil.getMimeType(path); - } - - Intent intent = new Intent(); - intent.setAction(Intent.ACTION_CREATE_DOCUMENT); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType(mimeType); - intent.putExtra(Intent.EXTRA_TITLE, filename); - - PackageManager pm = Objects.requireNonNull(getCurrentActivity()).getPackageManager(); - if (intent.resolveActivity(pm) != null) { - try { - getCurrentActivity().startActivityForResult(intent, SAVE_REQUEST); - mPickerPromise = promise; - fileContent = path; - } - catch(Exception e) { - promise.reject(SAVE_EVENT, e.getMessage()); - } - } else { - try { - if(mimeType == null) { - throw new Exception("It wasn't possible to detect the type of the file"); - } - throw new Exception("No app associated with this mime type"); - } - catch(Exception e) { - promise.reject(SAVE_EVENT, e.getMessage()); - } - } - } - - @ReactMethod - public void createThumbnail(ReadableMap options, Promise promise) { - try { - WritableMap optionsMap = Arguments.createMap(); - optionsMap.merge(options); - String url = options.hasKey("url") ? options.getString("url") : ""; - URL videoUrl = new URL(url); - String serverUrl = videoUrl.getProtocol() + "://" + videoUrl.getHost() + ":" + videoUrl.getPort(); - String token = Credentials.getCredentialsForServerSync(this.reactContext, serverUrl); - if (!TextUtils.isEmpty(token)) { - WritableMap headers = Arguments.createMap(); - if (optionsMap.hasKey("headers")) { - headers.merge(Objects.requireNonNull(optionsMap.getMap("headers"))); - } - headers.putString("Authorization", "Bearer " + token); - optionsMap.putMap("headers", headers); - } - CreateThumbnailModule thumb = new CreateThumbnailModule(this.reactContext); - thumb.create(optionsMap.copy(), promise); - } catch (Exception e) { - promise.reject("CreateThumbnail_ERROR", e); - } - } - - private static class SaveDataTask extends GuardedResultAsyncTask { - private final WeakReference weakContext; - private final String fromFile; - private final Uri toFile; - - protected SaveDataTask(ReactApplicationContext reactContext, String path, Uri destination) { - super(reactContext.getExceptionHandler()); - weakContext = new WeakReference<>(reactContext.getApplicationContext()); - fromFile = path; - toFile = destination; - } - - @Override - protected Object doInBackgroundGuarded() { - try { - ParcelFileDescriptor pfd = weakContext.get().getContentResolver().openFileDescriptor(toFile, "w"); - File input = new File(this.fromFile); - try (FileInputStream fileInputStream = new FileInputStream(input)) { - try (FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor())) { - FileChannel source = fileInputStream.getChannel(); - FileChannel dest = fileOutputStream.getChannel(); - dest.transferFrom(source, 0, source.size()); - source.close(); - dest.close(); - } - } - pfd.close(); - } catch (Exception e) { - e.printStackTrace(); - } - - return null; - } - - @Override - protected void onPostExecuteGuarded(Object o) { - - } - } -} diff --git a/android/app/src/main/java/com/mattermost/rnbeta/NotificationDismissService.java b/android/app/src/main/java/com/mattermost/rnbeta/NotificationDismissService.java index 3299145ee..6f72fb5ff 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/NotificationDismissService.java +++ b/android/app/src/main/java/com/mattermost/rnbeta/NotificationDismissService.java @@ -6,7 +6,7 @@ import android.app.IntentService; import android.os.Bundle; import android.util.Log; -import com.mattermost.helpers.NotificationHelper; +import com.mattermost.rnutils.helpers.NotificationHelper; import com.wix.reactnativenotifications.core.NotificationIntentAdapter; public class NotificationDismissService extends IntentService { @@ -19,7 +19,7 @@ public class NotificationDismissService extends IntentService { final Context context = getApplicationContext(); final Bundle bundle = NotificationIntentAdapter.extractPendingNotificationDataFromIntent(intent); - NotificationHelper.dismissNotification(context, bundle); + NotificationHelper.INSTANCE.dismissNotification(context, bundle); Log.i("ReactNative", "Dismiss notification"); } } diff --git a/android/app/src/main/java/com/mattermost/rnbeta/NotificationsModule.java b/android/app/src/main/java/com/mattermost/rnbeta/NotificationsModule.java deleted file mode 100644 index 6de0faccd..000000000 --- a/android/app/src/main/java/com/mattermost/rnbeta/NotificationsModule.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.mattermost.rnbeta; - -import android.app.Notification; -import android.content.Context; -import android.os.Bundle; -import android.service.notification.StatusBarNotification; - -import androidx.annotation.NonNull; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContextBaseJavaModule; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.Promise; -import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.WritableMap; -import com.mattermost.helpers.NotificationHelper; - -import java.util.Set; - -public class NotificationsModule extends ReactContextBaseJavaModule { - private static NotificationsModule instance; - private final MainApplication mApplication; - - private NotificationsModule(MainApplication application, ReactApplicationContext reactContext) { - super(reactContext); - mApplication = application; - } - - public static NotificationsModule getInstance(MainApplication application, ReactApplicationContext reactContext) { - if (instance == null) { - instance = new NotificationsModule(application, reactContext); - } - - return instance; - } - - @NonNull - @Override - public String getName() { - return "Notifications"; - } - - @ReactMethod - public void getDeliveredNotifications(final Promise promise) { - Context context = mApplication.getApplicationContext(); - StatusBarNotification[] notifications = NotificationHelper.getDeliveredNotifications(context); - WritableArray result = Arguments.createArray(); - for (StatusBarNotification sbn:notifications) { - WritableMap map = Arguments.createMap(); - Notification n = sbn.getNotification(); - Bundle bundle = n.extras; - Set keys = bundle.keySet(); - for (String key: keys) { - map.putString(key, bundle.getString(key)); - } - result.pushMap(map); - } - promise.resolve(result); - } - - @ReactMethod - public void removeChannelNotifications(String serverUrl, String channelId) { - Context context = mApplication.getApplicationContext(); - NotificationHelper.removeChannelNotifications(context, serverUrl, channelId); - } - - @ReactMethod - public void removeThreadNotifications(String serverUrl, String threadId) { - Context context = mApplication.getApplicationContext(); - NotificationHelper.removeThreadNotifications(context, serverUrl, threadId); - } - - @ReactMethod - public void removeServerNotifications(String serverUrl) { - Context context = mApplication.getApplicationContext(); - NotificationHelper.removeServerNotifications(context, serverUrl); - } -} diff --git a/android/app/src/main/java/com/mattermost/rnbeta/SplitViewModule.kt b/android/app/src/main/java/com/mattermost/rnbeta/SplitViewModule.kt deleted file mode 100644 index 89a99fdea..000000000 --- a/android/app/src/main/java/com/mattermost/rnbeta/SplitViewModule.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.mattermost.rnbeta - -import com.facebook.react.bridge.* -import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter -import com.learnium.RNDeviceInfo.resolver.DeviceTypeResolver - -class SplitViewModule(private var reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { - private var listenerCount = 0 - - companion object { - private var instance: SplitViewModule? = null - - fun getInstance(reactContext: ReactApplicationContext): SplitViewModule { - if (instance == null) { - instance = SplitViewModule(reactContext) - } else { - instance!!.reactContext = reactContext - } - - return instance!! - } - - fun getInstance(): SplitViewModule? { - return instance - } - } - - - override fun getName() = "SplitView" - - private fun sendEvent(params: WritableMap?) { - reactContext - .getJSModule(RCTDeviceEventEmitter::class.java) - .emit("SplitViewChanged", params) - } - - private fun getSplitViewResults(folded: Boolean) : WritableMap? { - if (currentActivity != null) { - val deviceResolver = DeviceTypeResolver(this.reactContext) - val map = Arguments.createMap() - var isSplitView = folded; - if (currentActivity?.isInMultiWindowMode == true) { - isSplitView = FoldableObserver.getInstance()?.isCompactView() == true - } - map.putBoolean("isSplitView", isSplitView) - map.putBoolean("isTablet", deviceResolver.isTablet) - return map - } - - return null - } - - fun setDeviceFolded() { - val map = getSplitViewResults(FoldableObserver.getInstance()?.isDeviceFolded == true) - if (listenerCount > 0) { - sendEvent(map) - } - } - - @ReactMethod(isBlockingSynchronousMethod = true) - fun isRunningInSplitView(): WritableMap? { - return getSplitViewResults(FoldableObserver.getInstance()?.isDeviceFolded == true) - } - - @ReactMethod - fun addListener(eventName: String) { - listenerCount += 1 - } - - @ReactMethod - fun removeListeners(count: Int) { - listenerCount -= count - } -} diff --git a/android/app/src/main/java/com/mattermost/share/ShareActivity.java b/android/app/src/main/java/com/mattermost/share/ShareActivity.java deleted file mode 100644 index 7f9d19cae..000000000 --- a/android/app/src/main/java/com/mattermost/share/ShareActivity.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.mattermost.share; - -import android.os.Bundle; - -import com.facebook.react.ReactActivity; -import com.mattermost.rnbeta.MainApplication; - -public class ShareActivity extends ReactActivity { - @Override - protected String getMainComponentName() { - return "MattermostShare"; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - MainApplication app = (MainApplication) this.getApplication(); - app.setSharedExtensionIsOpened(true); - } -} diff --git a/android/app/src/main/java/com/mattermost/share/ShareModule.java b/android/app/src/main/java/com/mattermost/share/ShareModule.java deleted file mode 100644 index e97f36639..000000000 --- a/android/app/src/main/java/com/mattermost/share/ShareModule.java +++ /dev/null @@ -1,259 +0,0 @@ -package com.mattermost.share; - -import com.facebook.react.bridge.ReactContextBaseJavaModule; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.Promise; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.Arguments; -import com.mattermost.helpers.Credentials; -import com.mattermost.rnbeta.MainApplication; -import com.mattermost.helpers.RealPathUtil; - -import android.app.Activity; -import android.content.Intent; -import android.net.Uri; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.io.File; -import java.util.ArrayList; - -import org.json.JSONArray; -import org.json.JSONObject; -import org.json.JSONException; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.MultipartBody; -import okhttp3.RequestBody; -import okhttp3.Response; - -public class ShareModule extends ReactContextBaseJavaModule { - private final OkHttpClient client = new OkHttpClient(); - public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); - private static ShareModule instance; - private final MainApplication mApplication; - private ReactApplicationContext mReactContext; - private File tempFolder; - - private ShareModule(ReactApplicationContext reactContext) { - super(reactContext); - mReactContext = reactContext; - mApplication = (MainApplication)reactContext.getApplicationContext(); - } - - public static ShareModule getInstance(ReactApplicationContext reactContext) { - if (instance == null) { - instance = new ShareModule(reactContext); - } else { - instance.mReactContext = reactContext; - } - - return instance; - } - - public static ShareModule getInstance() { - return instance; - } - - @NonNull - @Override - public String getName() { - return "MattermostShare"; - } - - @ReactMethod(isBlockingSynchronousMethod = true) - public String getCurrentActivityName() { - Activity currentActivity = getCurrentActivity(); - if (currentActivity != null) { - String activityName = currentActivity.getComponentName().getClassName(); - String[] components = activityName.split("\\."); - return components[components.length - 1]; - } - - return ""; - } - - @ReactMethod - public void clear() { - Activity currentActivity = getCurrentActivity(); - if (currentActivity != null && this.getCurrentActivityName().equals("ShareActivity")) { - Intent intent = currentActivity.getIntent(); - intent.setAction(""); - intent.removeExtra(Intent.EXTRA_TEXT); - intent.removeExtra(Intent.EXTRA_STREAM); - } - } - - @Nullable - @Override - public Map getConstants() { - HashMap constants = new HashMap<>(1); - constants.put("cacheDirName", RealPathUtil.CACHE_DIR_NAME); - constants.put("isOpened", mApplication.getSharedExtensionIsOpened()); - return constants; - } - - @ReactMethod - public void close(ReadableMap data) { - this.clear(); - Activity currentActivity = getCurrentActivity(); - if (currentActivity == null || !this.getCurrentActivityName().equals("ShareActivity")) { - return; - } - - currentActivity.finishAndRemoveTask(); - if (data != null && data.hasKey("serverUrl")) { - ReadableArray files = data.getArray("files"); - String serverUrl = data.getString("serverUrl"); - final String token = Credentials.getCredentialsForServerSync(mReactContext, serverUrl); - JSONObject postData = buildPostObject(data); - - if (files != null && files.size() > 0) { - uploadFiles(serverUrl, token, files, postData); - } else { - try { - post(serverUrl, token, postData); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - mApplication.setSharedExtensionIsOpened(false); - RealPathUtil.deleteTempFiles(this.tempFolder); - } - - @ReactMethod - public void getSharedData(Promise promise) { - promise.resolve(processIntent()); - } - - public WritableArray processIntent() { - String type, action, extra; - WritableArray items = Arguments.createArray(); - Activity currentActivity = getCurrentActivity(); - - if (currentActivity != null) { - this.tempFolder = new File(currentActivity.getCacheDir(), RealPathUtil.CACHE_DIR_NAME); - Intent intent = currentActivity.getIntent(); - action = intent.getAction(); - type = intent.getType(); - extra = intent.getStringExtra(Intent.EXTRA_TEXT); - - if (Intent.ACTION_SEND.equals(action) && "text/plain".equals(type) && extra != null) { - items.pushMap(ShareUtils.getTextItem(extra)); - } else if (Intent.ACTION_SEND.equals(action)) { - if (extra != null) { - items.pushMap(ShareUtils.getTextItem(extra)); - } - Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM); - if (uri != null) { - ReadableMap fileInfo = ShareUtils.getFileItem(currentActivity, uri); - if (fileInfo != null) { - items.pushMap(fileInfo); - } - } - } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) { - if (extra != null) { - items.pushMap(ShareUtils.getTextItem(extra)); - } - - ArrayList uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); - for (Uri uri : uris) { - ReadableMap fileInfo = ShareUtils.getFileItem(currentActivity, uri); - if (fileInfo != null) { - items.pushMap(fileInfo); - } - } - } - } - - return items; - } - - private JSONObject buildPostObject(ReadableMap data) { - JSONObject json = new JSONObject(); - try { - json.put("user_id", data.getString("userId")); - if (data.hasKey("channelId")) { - json.put("channel_id", data.getString("channelId")); - } - if (data.hasKey("message")) { - json.put("message", data.getString("message")); - } - } catch (JSONException e) { - e.printStackTrace(); - } - return json; - } - - private void post(String serverUrl, String token, JSONObject postData) throws IOException { - RequestBody body = RequestBody.create(postData.toString(), JSON); - Request request = new Request.Builder() - .header("Authorization", "BEARER " + token) - .url(serverUrl + "/api/v4/posts") - .post(body) - .build(); - client.newCall(request).execute(); - } - - private void uploadFiles(String serverUrl, String token, ReadableArray files, JSONObject postData) { - try { - MultipartBody.Builder builder = new MultipartBody.Builder() - .setType(MultipartBody.FORM); - - for(int i = 0 ; i < files.size() ; i++) { - ReadableMap file = files.getMap(i); - String mime = file.getString("type"); - String fullPath = file.getString("value"); - if (fullPath != null) { - String filePath = fullPath.replaceFirst("file://", ""); - File fileInfo = new File(filePath); - if (fileInfo.exists() && mime != null) { - final MediaType MEDIA_TYPE = MediaType.parse(mime); - builder.addFormDataPart("files", file.getString("filename"), RequestBody.create(fileInfo, MEDIA_TYPE)); - } - } - } - - builder.addFormDataPart("channel_id", postData.getString("channel_id")); - RequestBody body = builder.build(); - Request request = new Request.Builder() - .header("Authorization", "BEARER " + token) - .url(serverUrl + "/api/v4/files") - .post(body) - .build(); - - try (Response response = client.newCall(request).execute()) { - if (response.isSuccessful()) { - String responseData = Objects.requireNonNull(response.body()).string(); - JSONObject responseJson = new JSONObject(responseData); - JSONArray fileInfoArray = responseJson.getJSONArray("file_infos"); - JSONArray file_ids = new JSONArray(); - for(int i = 0 ; i < fileInfoArray.length() ; i++) { - JSONObject fileInfo = fileInfoArray.getJSONObject(i); - file_ids.put(fileInfo.getString("id")); - } - postData.put("file_ids", file_ids); - post(serverUrl, token, postData); - } - } catch (IOException e) { - e.printStackTrace(); - } - - } catch (JSONException e) { - e.printStackTrace(); - } - } -} diff --git a/android/app/src/main/java/com/mattermost/share/ShareUtils.java b/android/app/src/main/java/com/mattermost/share/ShareUtils.java deleted file mode 100644 index 143326edf..000000000 --- a/android/app/src/main/java/com/mattermost/share/ShareUtils.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.mattermost.share; - -import android.app.Activity; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.media.MediaMetadataRetriever; -import android.net.Uri; -import android.webkit.URLUtil; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableMap; -import com.mattermost.helpers.RealPathUtil; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.UUID; - -public class ShareUtils { - public static ReadableMap getTextItem(String text) { - WritableMap map = Arguments.createMap(); - map.putString("value", text); - map.putString("type", ""); - map.putBoolean("isString", true); - return map; - } - - public static ReadableMap getFileItem(Activity activity, Uri uri) { - WritableMap map = Arguments.createMap(); - String filePath = RealPathUtil.getRealPathFromURI(activity, uri); - if (filePath == null) { - return null; - } - - File file = new File(filePath); - String type = RealPathUtil.getMimeTypeFromUri(activity, uri); - if (type != null) { - if (type.startsWith("image/")) { - BitmapFactory.Options bitMapOption = ShareUtils.getImageDimensions(filePath); - map.putInt("height", bitMapOption.outHeight); - map.putInt("width", bitMapOption.outWidth); - - } else if (type.startsWith("video/")) { - File cacheDir = new File(activity.getCacheDir(), RealPathUtil.CACHE_DIR_NAME); - addVideoThumbnailToMap(cacheDir, activity.getApplicationContext(), map, "file://" + filePath); - } - } else { - type = "application/octet-stream"; - } - - map.putString("value", "file://" + filePath); - map.putDouble("size", (double) file.length()); - map.putString("filename", file.getName()); - map.putString("type", type); - map.putString("extension", RealPathUtil.getExtension(filePath).replaceFirst(".", "")); - map.putBoolean("isString", false); - return map; - } - - public static BitmapFactory.Options getImageDimensions(String filePath) { - BitmapFactory.Options bitMapOption = new BitmapFactory.Options(); - bitMapOption.inJustDecodeBounds=true; - BitmapFactory.decodeFile(filePath, bitMapOption); - return bitMapOption; - } - - private static void addVideoThumbnailToMap(File cacheDir, Context context, WritableMap map, String filePath) { - String fileName = ("thumb-" + UUID.randomUUID().toString()) + ".png"; - OutputStream fOut = null; - - try { - File file = new File(cacheDir, fileName); - Bitmap image = getBitmapAtTime(context, filePath, 1); - if (file.createNewFile()) { - fOut = new FileOutputStream(file); - image.compress(Bitmap.CompressFormat.PNG, 100, fOut); - fOut.flush(); - fOut.close(); - - map.putString("videoThumb", "file://" + file.getAbsolutePath()); - map.putInt("width", image.getWidth()); - map.putInt("height", image.getHeight()); - } - } catch (Exception ignored) { - } - } - - private static Bitmap getBitmapAtTime(Context context, String filePath, int time) { - try { - MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - if (URLUtil.isFileUrl(filePath)) { - String decodedPath; - try { - decodedPath = URLDecoder.decode(filePath, "UTF-8"); - } catch (UnsupportedEncodingException e) { - decodedPath = filePath; - } - - retriever.setDataSource(decodedPath.replace("file://", "")); - } else if (filePath.contains("content://")) { - retriever.setDataSource(context, Uri.parse(filePath)); - } - - Bitmap image = retriever.getFrameAtTime(time * 1000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC); - retriever.release(); - return image; - } catch (Exception e) { - throw new IllegalStateException("File doesn't exist or not supported"); - } - } -} diff --git a/android/app/src/main/res/drawable/rn_edit_text_material.xml b/android/app/src/main/res/drawable/rn_edit_text_material.xml index ab58c0f44..060f7ee17 100644 --- a/android/app/src/main/res/drawable/rn_edit_text_material.xml +++ b/android/app/src/main/res/drawable/rn_edit_text_material.xml @@ -14,7 +14,8 @@ android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material" android:insetRight="@dimen/abc_edit_text_inset_horizontal_material" android:insetTop="@dimen/abc_edit_text_inset_top_material" - android:insetBottom="@dimen/abc_edit_text_inset_bottom_material"> + android:insetBottom="@dimen/abc_edit_text_inset_bottom_material" + >