diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 84364f2ef7ff..38855f1595a0 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -91,6 +91,16 @@ + + + + + + + + + + createNativeModules( List modules = new ArrayList<>(); modules.add(new StartupTimer(reactContext)); + modules.add(new ShareActionHandlerModule(reactContext)); return modules; } diff --git a/android/app/src/main/java/com/expensify/chat/MainActivity.kt b/android/app/src/main/java/com/expensify/chat/MainActivity.kt index 935ba8c8825f..0969a2c9b4d3 100644 --- a/android/app/src/main/java/com/expensify/chat/MainActivity.kt +++ b/android/app/src/main/java/com/expensify/chat/MainActivity.kt @@ -1,18 +1,21 @@ package com.expensify.chat -import expo.modules.ReactActivityDelegateWrapper - +import android.content.Intent import android.content.pm.ActivityInfo import android.os.Bundle +import android.util.Log import android.view.KeyEvent import android.view.View import android.view.WindowInsets import com.expensify.chat.bootsplash.BootSplash +import com.expensify.chat.intentHandler.ImageIntentHandler +import com.expensify.chat.intentHandler.IntentHandlerFactory import com.expensify.reactnativekeycommand.KeyCommandModule import com.facebook.react.ReactActivity import com.facebook.react.ReactActivityDelegate import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled import com.facebook.react.defaults.DefaultReactActivityDelegate +import expo.modules.ReactActivityDelegateWrapper class MainActivity : ReactActivity() { /** @@ -34,6 +37,9 @@ class MainActivity : ReactActivity() { override fun onCreate(savedInstanceState: Bundle?) { BootSplash.init(this) super.onCreate(null) + Log.i("ImageIntentHandler", "On create") + + if (resources.getBoolean(R.bool.portrait_only)) { requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT } @@ -50,6 +56,21 @@ class MainActivity : ReactActivity() { defaultInsets.systemWindowInsetBottom ) } + + if (intent != null) { + handleIntent(intent) + } + } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + setIntent(intent) // Must store the new intent unless getIntent() will return the old one + handleIntent(intent) + } + + private fun handleIntent(intent: Intent) { + val intentHandler = IntentHandlerFactory.getIntentHandler(this, intent.type) + intentHandler?.handle(intent) } /** diff --git a/android/app/src/main/java/com/expensify/chat/MainApplication.kt b/android/app/src/main/java/com/expensify/chat/MainApplication.kt index 2362af009979..a6e762c82e63 100644 --- a/android/app/src/main/java/com/expensify/chat/MainApplication.kt +++ b/android/app/src/main/java/com/expensify/chat/MainApplication.kt @@ -29,6 +29,7 @@ class MainApplication : MultiDexApplication(), ReactApplication { add(BootSplashPackage()) add(ExpensifyAppPackage()) add(RNTextInputResetPackage()) +// add(ShareExtensionHandlerPackage()) } override fun getJSMainModuleName() = ".expo/.virtual-metro-entry" diff --git a/android/app/src/main/java/com/expensify/chat/ShareActionHandlerModule.kt b/android/app/src/main/java/com/expensify/chat/ShareActionHandlerModule.kt new file mode 100644 index 000000000000..b6e6bf212362 --- /dev/null +++ b/android/app/src/main/java/com/expensify/chat/ShareActionHandlerModule.kt @@ -0,0 +1,34 @@ +package com.expensify.chat + +import android.content.Context +import android.util.Log +import com.expensify.chat.intentHandler.IntentHandlerConstants +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.Callback +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod + +class ShareActionHandlerModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { + override fun getName() = "ShareActionHandlerModule" + + @ReactMethod + fun processFiles(callback: Callback) { + try { + val sharedPreferences = reactApplicationContext.getSharedPreferences(IntentHandlerConstants.preferencesFile, Context.MODE_PRIVATE) + val fileSet = sharedPreferences.getStringSet(IntentHandlerConstants.fileArrayProperty, setOf()) + val fileArray: ArrayList = ArrayList(fileSet) + + val resultArray = Arguments.createArray() + for (file in fileArray) { + resultArray.pushString(file) + } + + callback.invoke(resultArray) + } catch (exception: Exception) { + Log.e("ImageIntentHandler", exception.toString()) + callback.invoke(exception.toString(), null) + } + } + +} \ No newline at end of file diff --git a/android/app/src/main/java/com/expensify/chat/image/ImageUtils.kt b/android/app/src/main/java/com/expensify/chat/image/ImageUtils.kt new file mode 100644 index 000000000000..1fa176f04dac --- /dev/null +++ b/android/app/src/main/java/com/expensify/chat/image/ImageUtils.kt @@ -0,0 +1,114 @@ +package com.expensify.chat.image + +import android.content.Context +import android.net.Uri +import android.os.Environment +import android.util.Log +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream + + +object ImageUtils { + private const val tag = "ImageUtils" + private const val maxPictureWidth = 2400 + private const val optimalPictureSize = maxPictureWidth * maxPictureWidth + private const val defaultImageExtension = ".jpg" + + private fun getUniqueImageFilePrefix(): String { + return System.currentTimeMillis().toString() + } + + /** + * Checks if external storage is available. + * + * @return true if external storage is available, mounted and writable, false otherwise. + */ + private fun isExternalStorageAvailable(): Boolean { + // Make sure the media storage is mounted (MEDIA_MOUNTED implies writable) + val state: String = Environment.getExternalStorageState() + if (state != Environment.MEDIA_MOUNTED) { + Log.w(tag, "External storage requested but not available" ) + return false + } + return true + } + + /** + * Synchronous method + * + * @param context + * @param imageUri + * @param destinationFile + * @throws IOException + */ + @Throws(IOException::class) + fun saveImageFromMediaProviderUri(imageUri: Uri?, destinationFile: File?, context: Context) { + val inputStream: InputStream? = imageUri?.let { context.contentResolver.openInputStream(it) } + val outputStream: OutputStream = FileOutputStream(destinationFile) + inputStream?.use { input -> + outputStream.use { output -> + input.copyTo(output) + } + } + } + + /** + * Creates an image file into the internal or external storage. + * + * @param context + * @return + * @throws IOException + */ + @Throws(IOException::class) + fun createImageFile(context: Context): File { + + // Create an image file name + val file: File = File.createTempFile( + getUniqueImageFilePrefix(), + defaultImageExtension, + getPhotoDirectory(context) + ) + Log.i(tag, "Created a temporary file for the photo at" + file.absolutePath) + return file + } + + /** + * Determines where the photo directory is based on if we can use external storage or not + * + * @param context + * @return File the directory + */ + private fun getPhotoDirectory(context: Context): File? { + val photoDirectory: File = if (isExternalStorageAvailable()) File( + context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "Expensify" + ) else File(context.filesDir.absolutePath, "Expensify") + if (!photoDirectory.exists()) { + photoDirectory.mkdir() + } + return photoDirectory + } + + /** + * Copy the given Uri to storage + * + * @param uri + * @param context + * @return The absolute path of the image + */ + fun copyUriToStorage(uri: Uri?, context: Context): String? { + var resultingPath: String? = null + try { + val imageFile: File = createImageFile(context) + saveImageFromMediaProviderUri(uri, imageFile, context) + resultingPath = imageFile.absolutePath + Log.i("ImageIntentHandler", "save image$resultingPath") + + } catch (ex: IOException) { + Log.e(tag, "Couldn't save image from intent", ex) + } + return resultingPath + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/expensify/chat/intentHandler/AbstractIntentHandler.kt b/android/app/src/main/java/com/expensify/chat/intentHandler/AbstractIntentHandler.kt new file mode 100644 index 000000000000..a58c687bee56 --- /dev/null +++ b/android/app/src/main/java/com/expensify/chat/intentHandler/AbstractIntentHandler.kt @@ -0,0 +1,5 @@ +package com.expensify.chat.intentHandler + +abstract class AbstractIntentHandler: IntentHandler { + override fun onCompleted() {} +} \ No newline at end of file diff --git a/android/app/src/main/java/com/expensify/chat/intentHandler/ImageIntentHandler.kt b/android/app/src/main/java/com/expensify/chat/intentHandler/ImageIntentHandler.kt new file mode 100644 index 000000000000..14d8a6f163c4 --- /dev/null +++ b/android/app/src/main/java/com/expensify/chat/intentHandler/ImageIntentHandler.kt @@ -0,0 +1,87 @@ +package com.expensify.chat.intentHandler + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.util.Log +import com.expensify.chat.image.ImageUtils.copyUriToStorage + + +object IntentHandlerConstants { + const val preferencesFile = "shareActionHandler" + const val fileArrayProperty = "filePaths" +} + + +class ImageIntentHandler(private val context: Context) : AbstractIntentHandler() { + override fun handle(intent: Intent?): Boolean { + Log.i("ImageIntentHandler", "Handle intent" + intent.toString()) + if (intent == null) { + return false + } + + val action: String? = intent.action + val type: String = intent.type ?: return false + + if(!type.startsWith("image/")) return false + + when(action) { + Intent.ACTION_SEND -> { + Log.i("ImageIntentHandler", "Handle receive single image") + handleSingleImageIntent(intent, context) + onCompleted() + return true + } + Intent.ACTION_SEND_MULTIPLE -> { + handleMultipleImagesIntent(intent, context) + onCompleted() + return true + } + } + return false + } + + private fun handleSingleImageIntent(intent: Intent, context: Context) { + (intent.getParcelableExtra(Intent.EXTRA_STREAM))?.let { imageUri -> + + Log.i("ImageIntentHandler", "handleSingleImageIntent$imageUri") + // Update UI to reflect image being shared + if (imageUri == null) { + return + } + + val fileArrayList: ArrayList = ArrayList() + val resultingPath: String? = copyUriToStorage(imageUri, context) + if (resultingPath != null) { + fileArrayList.add(resultingPath) + val sharedPreferences = context.getSharedPreferences(IntentHandlerConstants.preferencesFile, Context.MODE_PRIVATE) + val editor = sharedPreferences.edit() + editor.putStringSet(IntentHandlerConstants.fileArrayProperty, fileArrayList.toSet()) + editor.apply() + } + } + } + + private fun handleMultipleImagesIntent(intent: Intent, context: Context) { + + val resultingImagePaths = ArrayList() + + (intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM))?.let { imageUris -> + for (imageUri in imageUris) { + val resultingPath: String? = copyUriToStorage(imageUri, context) + if (resultingPath != null) { + resultingImagePaths.add(resultingPath) + } + } + } +// Yapl.getInstance().callIntentCallback(resultingImagePaths) + } + + override fun onCompleted() { + val uri: Uri = Uri.parse("new-expensify://share/root") + val deepLinkIntent = Intent(Intent.ACTION_VIEW, uri) + deepLinkIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + context.startActivity(deepLinkIntent) + } + +} \ No newline at end of file diff --git a/android/app/src/main/java/com/expensify/chat/intentHandler/IntentHandler.kt b/android/app/src/main/java/com/expensify/chat/intentHandler/IntentHandler.kt new file mode 100644 index 000000000000..6cd93dff276a --- /dev/null +++ b/android/app/src/main/java/com/expensify/chat/intentHandler/IntentHandler.kt @@ -0,0 +1,8 @@ +package com.expensify.chat.intentHandler + +import android.content.Intent + +interface IntentHandler { + fun handle(intent: Intent?): Boolean + fun onCompleted() +} \ No newline at end of file diff --git a/android/app/src/main/java/com/expensify/chat/intentHandler/IntentHandlerFactory.kt b/android/app/src/main/java/com/expensify/chat/intentHandler/IntentHandlerFactory.kt new file mode 100644 index 000000000000..c89af09e2b50 --- /dev/null +++ b/android/app/src/main/java/com/expensify/chat/intentHandler/IntentHandlerFactory.kt @@ -0,0 +1,14 @@ +package com.expensify.chat.intentHandler + +import android.content.Context + +object IntentHandlerFactory { + fun getIntentHandler(context: Context, mimeType: String?): IntentHandler? { + if (mimeType == null) return null + return when { + mimeType.startsWith("image/") -> ImageIntentHandler(context) + // Add other cases like video/*, application/pdf etc. + else -> null + } + } +} \ No newline at end of file diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index 317b79c19c0d..8fc9e8ae99c4 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -39,11 +39,13 @@ BDB853621F354EBB84E619C2 /* ExpensifyNewKansas-MediumItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = D2AFB39EC1D44BF9B91D3227 /* ExpensifyNewKansas-MediumItalic.otf */; }; DD79042B2792E76D004484B4 /* RCTBootSplash.m in Sources */ = {isa = PBXBuildFile; fileRef = DD79042A2792E76D004484B4 /* RCTBootSplash.m */; }; DDCB2E57F334C143AC462B43 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D20D83B0E39BA6D21761E72 /* ExpoModulesProvider.swift */; }; - E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */ = {isa = PBXBuildFile; }; + E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; E5647A4A2B8E0CE40047156F /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5647A492B8E0CE40047156F /* ShareViewController.swift */; }; E5647A4D2B8E0CE40047156F /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E5647A4B2B8E0CE40047156F /* MainInterface.storyboard */; }; E5647A512B8E0CE40047156F /* ShareMenuExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = E5647A472B8E0CE20047156F /* ShareMenuExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - E5CB2F182B7F834000B63003 /* (null) in Embed Foundation Extensions */ = {isa = PBXBuildFile; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + E5647A5B2B9203B20047156F /* RCTShareActionHandlerModule.m in Sources */ = {isa = PBXBuildFile; fileRef = E5647A5A2B9203B10047156F /* RCTShareActionHandlerModule.m */; }; + E5647A5C2B9203B20047156F /* RCTShareActionHandlerModule.m in Sources */ = {isa = PBXBuildFile; fileRef = E5647A5A2B9203B10047156F /* RCTShareActionHandlerModule.m */; }; + E5CB2F182B7F834000B63003 /* BuildFile in Embed Foundation Extensions */ = {isa = PBXBuildFile; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; }; ED222ED90E074A5481A854FA /* ExpensifyNeue-BoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8B28D84EF339436DBD42A203 /* ExpensifyNeue-BoldItalic.otf */; }; F0C450EA2705020500FD2970 /* colors.json in Resources */ = {isa = PBXBuildFile; fileRef = F0C450E92705020500FD2970 /* colors.json */; }; @@ -82,7 +84,7 @@ dstSubfolderSpec = 13; files = ( 7FD73CA22B23CE9500420AF3 /* NotificationServiceExtension.appex in Embed Foundation Extensions */, - E5CB2F182B7F834000B63003 /* (null) in Embed Foundation Extensions */, + E5CB2F182B7F834000B63003 /* BuildFile in Embed Foundation Extensions */, E5647A512B8E0CE40047156F /* ShareMenuExtension.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; @@ -150,6 +152,8 @@ E5647A492B8E0CE40047156F /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; E5647A4C2B8E0CE40047156F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; E5647A4E2B8E0CE40047156F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E5647A592B9202B50047156F /* RCTShareActionHandlerModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTShareActionHandlerModule.h; sourceTree = ""; }; + E5647A5A2B9203B10047156F /* RCTShareActionHandlerModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTShareActionHandlerModule.m; sourceTree = ""; }; E704648954784DDFBAADF568 /* ExpensifyMono-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyMono-Regular.otf"; path = "../assets/fonts/native/ExpensifyMono-Regular.otf"; sourceTree = ""; }; E750C93A45B47BDC5149C5AA /* Pods-NewExpensify-NewExpensifyTests.debugdevelopment.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.debugdevelopment.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.debugdevelopment.xcconfig"; sourceTree = ""; }; E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = ""; }; @@ -173,8 +177,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */, - E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */, + E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, + E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, 5B8996A7D8B007ECC41919E1 /* libPods-NewExpensify.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -280,6 +284,8 @@ 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( + E5647A5A2B9203B10047156F /* RCTShareActionHandlerModule.m */, + E5647A592B9202B50047156F /* RCTShareActionHandlerModule.h */, 374FB8D528A133A7000D84EF /* OriginImageRequestHandler.h */, 374FB8D628A133FE000D84EF /* OriginImageRequestHandler.mm */, F0C450E92705020500FD2970 /* colors.json */, @@ -890,6 +896,7 @@ 0CDA8E35287DD650004ECBEC /* AppDelegate.mm in Sources */, 7041848626A8E47D00E09F4D /* RCTStartupTimer.m in Sources */, 7F5E81F06BCCF61AD02CEA06 /* ExpoModulesProvider.swift in Sources */, + E5647A5C2B9203B20047156F /* RCTShareActionHandlerModule.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -897,6 +904,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E5647A5B2B9203B20047156F /* RCTShareActionHandlerModule.m in Sources */, 18D050E0262400AF000D658B /* BridgingFile.swift in Sources */, 0F5E5350263B73FD004CA14F /* EnvironmentChecker.m in Sources */, 374FB8D728A133FE000D84EF /* OriginImageRequestHandler.mm in Sources */, @@ -2351,6 +2359,7 @@ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = com.expensify.chat.dev.ShareMenuExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2434,6 +2443,7 @@ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = com.expensify.chat.dev.ShareMenuExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2517,6 +2527,7 @@ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = com.expensify.chat.dev.ShareMenuExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2594,6 +2605,7 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.expensify.chat.dev.ShareMenuExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2670,6 +2682,7 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.expensify.chat.dev.ShareMenuExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2746,6 +2759,7 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.expensify.chat.dev.ShareMenuExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; diff --git a/ios/RCTShareActionHandlerModule.h b/ios/RCTShareActionHandlerModule.h new file mode 100644 index 000000000000..ee8d186edb0d --- /dev/null +++ b/ios/RCTShareActionHandlerModule.h @@ -0,0 +1,16 @@ +// +// RCTShareActionHandlerModule.h +// NewExpensify +// +// Created by Bartek Krasoń on 01/03/2024. +// + +#ifndef RCTShareActionHandlerModule_h +#define RCTShareActionHandlerModule_h + +#import + +@interface RCTShareActionHandlerModule : NSObject +@end + +#endif /* RCTShareActionHandlerModule_h */ diff --git a/ios/RCTShareExtensionHandlerModule.m b/ios/RCTShareActionHandlerModule.m similarity index 79% rename from ios/RCTShareExtensionHandlerModule.m rename to ios/RCTShareActionHandlerModule.m index 569e0da8b71a..269faec4f0a4 100644 --- a/ios/RCTShareExtensionHandlerModule.m +++ b/ios/RCTShareActionHandlerModule.m @@ -1,22 +1,21 @@ // -// RCTShareExtensionHandlerModule.m +// RCTShareActionHandlerModule.m // NewExpensify // -// Created by Bartek Krasoń on 23/02/2024. +// Created by Bartek Krasoń on 01/03/2024. // #import -#import "ios:NewExpensify:RCTShareExtensionHandlerModule.h" +#import "RCTShareActionHandlerModule.h" #import NSString *const ShareExtensionGroupIdentifier = @"group.com.new-expensify"; NSString *const ShareExtensionFilesKey = @"ShareFiles"; NSString *const ShareImageFileExtension = @".png"; -@implementation RCTShareExtensionHandlerModule +@implementation RCTShareActionHandlerModule -// To export a module named RCTCalendarModule -RCT_EXPORT_MODULE(RCTShareExtensionHandlerModule); +RCT_EXPORT_MODULE(RCTShareActionHandlerModule); RCT_EXPORT_METHOD(processFiles:(RCTResponseSenderBlock)callback) { @@ -38,10 +37,10 @@ @implementation RCTShareExtensionHandlerModule NSArray *imageSrcPath = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:sharedImagesFolderPath error:&error]; if (imageSrcPath.count == 0) { - NSLog(@"handleShareExtension Failed to find images in 'sharedImagesFolderPath'"); + NSLog(@"handleShareAction Failed to find images in 'sharedImagesFolderPath'"); return; } - NSLog(@"handleShareExtension shared %lu images", imageSrcPath.count); + NSLog(@"handleShareAction shared %lu images", imageSrcPath.count); NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; @@ -49,14 +48,14 @@ @implementation RCTShareExtensionHandlerModule for (int i = 0; i < imageSrcPath.count; i++) { if (imageSrcPath[i] == NULL) { - NSLog(@"handleShareExtension Invalid image in position %d, imageSrcPath[i] is nil", i); + NSLog(@"handleShareAction Invalid image in position %d, imageSrcPath[i] is nil", i); continue; } - NSLog(@"handleShareExtension Valid image in position %d", i); + NSLog(@"handleShareAction Valid image in position %d", i); NSString *srcImageAbsolutePath = [sharedImagesFolderPath stringByAppendingPathComponent:imageSrcPath[i]]; UIImage *smartScanImage = [[UIImage alloc] initWithContentsOfFile:srcImageAbsolutePath]; if (smartScanImage == NULL) { - NSLog(@"handleShareExtension Failed to load image %@", srcImageAbsolutePath); + NSLog(@"handleShareAction Failed to load image %@", srcImageAbsolutePath); continue; } diff --git a/ios/ios:NewExpensify:RCTShareExtensionHandlerModule.h b/ios/ios:NewExpensify:RCTShareExtensionHandlerModule.h deleted file mode 100644 index 161bb3496766..000000000000 --- a/ios/ios:NewExpensify:RCTShareExtensionHandlerModule.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// ios:NewExpensify:RCTShareExtensionHandlerModule.h -// NewExpensify -// -// Created by Bartek Krasoń on 23/02/2024. -// - -#ifndef ios_NewExpensify_RCTShareExtensionHandlerModule_h -#define ios_NewExpensify_RCTShareExtensionHandlerModule_h - -#import -@interface RCTShareExtensionHandlerModule : NSObject -@end - -#endif /* ios_NewExpensify_RCTShareExtensionHandlerModule_h */ diff --git a/src/modules/ShareActionHandlerModule.ts b/src/modules/ShareActionHandlerModule.ts new file mode 100644 index 000000000000..34add7ae6e6a --- /dev/null +++ b/src/modules/ShareActionHandlerModule.ts @@ -0,0 +1,9 @@ +import {NativeModules} from 'react-native'; + +const {ShareActionHandlerModule} = NativeModules; + +type ShareActionHandlerType = { + processFiles(callback: (array: string[]) => void): void; +}; + +export default ShareActionHandlerModule as ShareActionHandlerType; diff --git a/src/modules/ShareExtensionHandlerModule.ts b/src/modules/ShareExtensionHandlerModule.ts deleted file mode 100644 index a283189ca9c6..000000000000 --- a/src/modules/ShareExtensionHandlerModule.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {NativeModules} from 'react-native'; - -const {ShareExtensionHandlerModule} = NativeModules; - -type ShareExtensionHandlerType = { - processFiles(callback: (array: string[]) => void): void; -}; - -export default ShareExtensionHandlerModule as ShareExtensionHandlerType; diff --git a/src/pages/share/ShareRootPage.tsx b/src/pages/share/ShareRootPage.tsx index b482d15a1030..028e7439a0df 100644 --- a/src/pages/share/ShareRootPage.tsx +++ b/src/pages/share/ShareRootPage.tsx @@ -1,6 +1,6 @@ import React, {useCallback, useEffect, useRef} from 'react'; import type {AppStateStatus} from 'react-native'; -import {AppState, View} from 'react-native'; +import {AppState, Platform, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -15,7 +15,7 @@ import Navigation from '@navigation/Navigation'; import OnyxTabNavigator, {TopTab} from '@navigation/OnyxTabNavigator'; import MoneyRequestParticipantsSelector from '@pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector'; import CONST from '@src/CONST'; -import ShareExtensionHandlerModule from '@src/modules/ShareExtensionHandlerModule'; +import ShareActionHandlerModule from '@src/modules/ShareActionHandlerModule'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Report} from '@src/types/onyx'; @@ -39,11 +39,14 @@ function ShareRootPage({selectedTab, iou}: ShareRootPageProps) { const handleAppStateChange = (nextAppState: AppStateStatus) => { if (appState.current.match(/inactive|background/) && nextAppState === 'active') { - ShareExtensionHandlerModule?.processFiles((processedFiles) => { + console.log('PROCESSED FILES ATTEMPT'); + + ShareActionHandlerModule.processFiles((processedFiles) => { // eslint-disable-next-line no-console console.log('PROCESSED FILES ', processedFiles); }); } + console.log('AppState', nextAppState); appState.current = nextAppState; // eslint-disable-next-line no-console @@ -51,10 +54,10 @@ function ShareRootPage({selectedTab, iou}: ShareRootPageProps) { }; useEffect(() => { - const appStateSubscription = AppState.addEventListener('change', handleAppStateChange); + const changeSubscription = AppState.addEventListener('change', handleAppStateChange); return () => { - appStateSubscription.remove(); + changeSubscription.remove(); }; }, []); @@ -65,12 +68,13 @@ function ShareRootPage({selectedTab, iou}: ShareRootPageProps) { const goToNextStep = useCallback(() => { // const nextStepIOUType = numberOfParticipants.current === 1 ? CONST.IOU.TYPE.REQUEST : CONST.IOU.TYPE.SPLIT; const nextStepIOUType = CONST.IOU.TYPE.REQUEST; - IOU.startMoneyRequest_temporaryForRefactor(optimisticReportID, false, CONST.IOU.REQUEST_TYPE.SCAN); + // IOU.startMoneyRequest_temporaryForRefactor(optimisticReportID, false, CONST.IOU.REQUEST_TYPE.SCAN); IOU.setMoneyRequestTag(transactionID, ''); - IOU.resetMoneyRequestCategory_temporaryForRefactor(transactionID); + // IOU.resetMoneyRequestCategory_temporaryForRefactor(transactionID); Navigation.navigate(ROUTES.SHARE_SCAN_CONFIRM.getRoute(nextStepIOUType, transactionID, selectedReportID.current || optimisticReportID)); }, [transactionID, optimisticReportID]); + console.log('share root page'); return ( ({ - selectedTab: { - key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`, - }, - // @ts-expect-error To fix - iou: { - key: ONYXKEYS.IOU, - }, -})(ShareRootPage); +export default ShareRootPage;