-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Share extension Android and iOS implementation #54354
Draft
Guccio163
wants to merge
120
commits into
Expensify:main
Choose a base branch
from
software-mansion-labs:brtqkr/wire-up-share-extension-ios
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,299
−10
Draft
Changes from 116 commits
Commits
Show all changes
120 commits
Select commit
Hold shift + click to select a range
410748b
setup share
BrtqKr 3cbb982
wire up groups, update group id
BrtqKr 38ec094
update app group id
BrtqKr b5e1641
fix single image share
BrtqKr 7f82783
Merge remote-tracking branch 'origin/main' into brtqkr/wire-up-share-…
BrtqKr 357b878
add tabs layout, wire up transaction setup
BrtqKr 5871fa0
initial onyx setup
BrtqKr 4562d4e
add mime type handling in the ios native module return object
BrtqKr 06ac232
attachment wip
BrtqKr 3c6946c
wire up share attachment, wire up message wip
BrtqKr f7d16af
add text
BrtqKr 7b13b38
Submit Details Page in progress
filip-solecki b447164
Merge branch 'main' into brtqkr/wire-up-share-extension-ios
filip-solecki 47e83df
Fix navigation
filip-solecki ee33b1e
Merge branch 'main' into brtqkr/wire-up-share-extension-ios
filip-solecki 3dd5657
Clear code
filip-solecki f1b5f27
Support Android images sharing and preparation for other types
filip-solecki 7dd0798
Handle text and other file types sharing on iOS
filip-solecki 333b5a1
Handle text sharing for Android devices
filip-solecki 82579c1
shareDetailsPage wip
Guccio163 ea0b0b8
Message visible
Guccio163 7c3ee53
textInput research
Guccio163 9cc6950
Merge branch 'main' of github.com:software-mansion-labs/expensify-app…
Guccio163 c73447f
Fix text files sharing on Android
filip-solecki a3022b9
clicking on users works without properly getting text and alternateText
Guccio163 ccb7120
working redirect, not working creating mult-people chats
Guccio163 caaac2e
Merge branch 'main' of github.com:software-mansion-labs/expensify-app…
Guccio163 83a92cc
adding users and creating chats fixed from main
Guccio163 b14584d
data extraction cleanup
Guccio163 1b07248
Merge branch 'brtqkr/wire-up-share-extension-ios' into Guccio163/shar…
Guccio163 a021fab
Merge branch 'main' into brtqkr/wire-up-share-extension-ios
filip-solecki 9cdeebb
attachment visualising
Guccio163 de34abf
Merge branch 'brtqkr/wire-up-share-extension-ios' of github.com:softw…
Guccio163 c639945
share working, styling attachment to do
Guccio163 5f21f49
target details in submit added
Guccio163 68db412
Finish Submit flow, Fix description issue, close Modal after finishin…
filip-solecki 35325d6
Merge branch 'brtqkr/wire-up-share-extension-ios' of github.com:softw…
Guccio163 286189a
Merge branch 'brtqkr/wire-up-share-extension-ios' into Guccio163/shar…
Guccio163 4fd6101
merge message
Guccio163 b457bab
Fix file uriPath on iOS and block sending file while sharing plain text
filip-solecki 1930925
Handle URL sharing on iOS
filip-solecki 43d8f08
Attachment Modal working for video and image, waiting for ldf
Guccio163 08c85e9
Fix PDF sharing for iOS
filip-solecki 89bfbc4
Fix files handling on iOS
filip-solecki 7dd929f
display pdf, image and video thumbnails, lacks aspectRatio
Guccio163 b960843
files and text working on ios, android failing with pdf
Guccio163 c4b3d65
return aspectRatio for videos on iOS
filip-solecki c354af4
return aspectRatio for videos on Android
filip-solecki 1bbe686
PDF without thumbnail, opening Modal
Guccio163 ecca4b4
remove PDF opening Modal, left-aligned thumbnails
Guccio163 20cc5c1
wrapping in SafeAreaView
Guccio163 ff3127e
Cleanup and Submit fix
filip-solecki 5f6b097
cleanup
Guccio163 a42a984
small restructure
Guccio163 b4c7481
unknown user mail display
Guccio163 f04e90f
Merge branch 'main' of github.com:software-mansion-labs/expensify-app…
Guccio163 f5c754c
requestMoney params fixing
Guccio163 f4a9288
dismissModal after submit working
Guccio163 021a098
Merge branch 'main' of github.com:software-mansion-labs/expensify-app…
Guccio163 fd90d8a
Merge branch 'main' of github.com:software-mansion-labs/expensify-app…
Guccio163 71e3475
unfinished alert when incorrect file format and redirect to home
Guccio163 eb0590a
unsupported file format alert and redirect working
Guccio163 c67b558
Merge branch 'main' of github.com:software-mansion-labs/expensify-app…
Guccio163 2276bb6
JS cleanup without SubmitDetailsPage
Guccio163 f8a33c2
SubmitDetailsPage with onSuccess comments, yet to change
Guccio163 d68cf92
Merge branch 'main' of github.com:software-mansion-labs/expensify-app…
Guccio163 08c7040
Clear Android files
filip-solecki 11a8a52
Fix shared file's name
filip-solecki 05e21ae
Remove redundant Log
filip-solecki 9d1bf2d
Submit flow working
Guccio163 8e60c27
Merge branch 'brtqkr/wire-up-share-extension-ios' of github.com:softw…
Guccio163 bfe2682
Fix flicering Tabs
filip-solecki 769908d
Fix submit request description
filip-solecki 8f309af
Accept txt mimeType
filip-solecki e23dc6e
Fix file handling for iOS
filip-solecki f9ec294
Merge branch 'main' into brtqkr/wire-up-share-extension-ios
filip-solecki 10bacbf
Fix TS error in Routes
filip-solecki 4132cab
Fix TS in Report file
filip-solecki 3770da6
Fix TS in ReportDetailsPage
filip-solecki 6a61f98
Fix changed files ESLint check
filip-solecki 5a048eb
Fix ESLint in dismissModalWithReport
filip-solecki ab547b5
Clean native files
filip-solecki b697ec8
Do not pass anonymous function causing rerenders
filip-solecki 9d50aad
Add empty line in android files
filip-solecki 813f5ac
rename intenthandler
filip-solecki 869dc76
Merge branch 'main' into brtqkr/wire-up-share-extension-ios
filip-solecki f74f3da
create function for saving to shared
filip-solecki 6ed8ad1
Merge branch 'main' into brtqkr/wire-up-share-extension-ios
filip-solecki e4c96ec
Remove redundant if statement
filip-solecki 776446e
Remove redundant type and memoization
filip-solecki b8ddb52
Remove redundant object creation
filip-solecki 63bfe9e
Merge branch 'main' into brtqkr/wire-up-share-extension-ios
filip-solecki 55ef8fd
Check if files folder is not empty while deleting files
filip-solecki 3bb13ca
Reuse getInternalStorageDirectory function
filip-solecki b92ada3
Remove lateinit usage
filip-solecki 5301ebd
Replace set to NULL with remove
filip-solecki bd4d916
Create reusable function for saving files
filip-solecki 3fa8535
Merge branch 'main' into brtqkr/wire-up-share-extension-ios
filip-solecki a8af760
Merge branch 'main' into brtqkr/wire-up-share-extension-ios
filip-solecki 443f25e
Make RCTSahreActionHandlerModule more readable
filip-solecki 693cc99
shareTab and submitTab changes before merging both tabs
Guccio163 a77dbaa
shareTabs duplicate code extracting and onFinish defaulting
Guccio163 e6f5ef1
Update comment to Onyx key
filip-solecki 4460ebf
Fix snyk by using newest gson
filip-solecki f561b7e
Fix Onyx naming
filip-solecki 1e5c644
Merge branch 'main' into brtqkr/wire-up-share-extension-ios
filip-solecki 72c4acc
Fix TS
filip-solecki 8146a84
Correct 'source' comment
filip-solecki aab0c4d
Fix typing in translations files
filip-solecki a4f49f5
Correct getOptimisticChatReport description
filip-solecki 48fba1c
Fix ID typing
filip-solecki 951a392
comments and ShareTab conditions
Guccio163 2594819
Merge branch 'brtqkr/wire-up-share-extension-ios' of github.com:softw…
Guccio163 bc618ff
get rid of onFinish from participants selector
Guccio163 d649547
Merge branch 'main' into brtqkr/wire-up-share-extension-ios
filip-solecki 92a46e8
get rid of useMemo and rename variable
filip-solecki 18defbf
Remove assertion from ShareActionHandlerModule
blazejkustra a012e2b
Rename ShareActionModule elements
filip-solecki 12d4d7b
Fix changed files eslint
filip-solecki 721e615
Fix default value for inexistent reportID
filip-solecki File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
android/app/src/main/java/com/expensify/chat/ShareActionHandlerModule.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package com.expensify.chat | ||
|
||
import android.content.Context | ||
import android.graphics.BitmapFactory | ||
import android.media.MediaMetadataRetriever | ||
import com.expensify.chat.intenthandler.IntentHandlerConstants | ||
import com.facebook.react.bridge.Callback | ||
import com.facebook.react.bridge.ReactApplicationContext | ||
import com.facebook.react.bridge.ReactContextBaseJavaModule | ||
import android.util.Log | ||
import com.facebook.react.bridge.ReactMethod | ||
import org.json.JSONObject | ||
import java.io.File | ||
|
||
class ShareActionHandlerModule(reactContext: ReactApplicationContext) : | ||
ReactContextBaseJavaModule(reactContext) { | ||
|
||
override fun getName(): String { | ||
return "ShareActionHandlerModule" | ||
} | ||
|
||
@ReactMethod | ||
fun processFiles(callback: Callback) { | ||
try { | ||
val sharedPreferences = reactApplicationContext.getSharedPreferences( | ||
IntentHandlerConstants.preferencesFile, | ||
Context.MODE_PRIVATE | ||
) | ||
|
||
val shareObjectString = sharedPreferences.getString(IntentHandlerConstants.shareObjectProperty, null) | ||
if (shareObjectString == null) { | ||
callback.invoke("No data found", null) | ||
return | ||
} | ||
|
||
val shareObject = JSONObject(shareObjectString) | ||
val content = shareObject.optString("content") | ||
val mimeType = shareObject.optString("mimeType") | ||
val fileUriPath = "file://$content" | ||
val timestamp = System.currentTimeMillis() | ||
|
||
val file = File(content) | ||
if (!file.exists()) { | ||
val textObject = JSONObject().apply { | ||
put("id", "text") | ||
put("content", content) | ||
put("mimeType", "txt") | ||
put("processedAt", timestamp) | ||
} | ||
callback.invoke(textObject.toString()) | ||
return | ||
} | ||
|
||
val identifier = file.name | ||
var aspectRatio = 0.0f | ||
|
||
if (mimeType.startsWith("image/")) { | ||
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } | ||
BitmapFactory.decodeFile(content, options) | ||
aspectRatio = if (options.outHeight != 0) options.outWidth.toFloat() / options.outHeight else 1.0f | ||
} else if (mimeType.startsWith("video/")) { | ||
val retriever = MediaMetadataRetriever() | ||
try { | ||
retriever.setDataSource(content) | ||
val videoWidth = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)?.toFloatOrNull() ?: 1f | ||
val videoHeight = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toFloatOrNull() ?: 1f | ||
if (videoHeight != 0f) aspectRatio = videoWidth / videoHeight | ||
} catch (e: Exception) { | ||
Log.e("ShareActionHandlerModule", "Error retrieving video metadata: ${e.message}") | ||
} finally { | ||
retriever.release() | ||
} | ||
} | ||
|
||
val fileData = JSONObject().apply { | ||
put("id", identifier) | ||
put("content", fileUriPath) | ||
put("mimeType", mimeType) | ||
put("processedAt", timestamp) | ||
put("aspectRatio", aspectRatio) | ||
} | ||
|
||
callback.invoke(fileData.toString()) | ||
|
||
} catch (e: Exception) { | ||
callback.invoke(e.toString(), null) | ||
} | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
android/app/src/main/java/com/expensify/chat/intentHandler/AbstractIntentHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.expensify.chat.intenthandler | ||
|
||
import android.content.Context | ||
import com.expensify.chat.utils.FileUtils.clearInternalStorageDirectory | ||
|
||
abstract class AbstractIntentHandler: IntentHandler { | ||
override fun onCompleted() {} | ||
|
||
protected fun clearTemporaryFiles(context: Context) { | ||
// Clear data present in the shared preferences | ||
val sharedPreferences = context.getSharedPreferences(IntentHandlerConstants.preferencesFile, Context.MODE_PRIVATE) | ||
val editor = sharedPreferences.edit() | ||
editor.clear() | ||
editor.apply() | ||
|
||
// Clear leftover temporary files from previous share attempts | ||
clearInternalStorageDirectory(context) | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
android/app/src/main/java/com/expensify/chat/intentHandler/FileIntentHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package com.expensify.chat.intenthandler | ||
|
||
import android.content.Context | ||
import android.content.Intent | ||
import android.net.Uri | ||
import com.expensify.chat.utils.FileUtils | ||
|
||
class FileIntentHandler(private val context: Context) : AbstractIntentHandler() { | ||
override fun handle(intent: Intent): Boolean { | ||
super.clearTemporaryFiles(context) | ||
when(intent.action) { | ||
Intent.ACTION_SEND -> { | ||
handleSingleFileIntent(intent, context) | ||
onCompleted() | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
private fun handleSingleFileIntent(intent: Intent, context: Context) { | ||
(intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM))?.let { fileUri -> | ||
val resultingPath: String? = FileUtils.copyUriToStorage(fileUri, context) | ||
|
||
if (resultingPath != null) { | ||
val shareFileObject = ShareFileObject(resultingPath, intent.type) | ||
|
||
val sharedPreferences = context.getSharedPreferences(IntentHandlerConstants.preferencesFile, Context.MODE_PRIVATE) | ||
val editor = sharedPreferences.edit() | ||
editor.putString(IntentHandlerConstants.shareObjectProperty, shareFileObject.toString()) | ||
editor.apply() | ||
} | ||
} | ||
} | ||
|
||
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) | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
android/app/src/main/java/com/expensify/chat/intentHandler/IntentHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.expensify.chat.intenthandler | ||
|
||
import android.content.Intent | ||
|
||
object IntentHandlerConstants { | ||
const val preferencesFile = "shareActionHandler" | ||
const val shareObjectProperty = "shareObject" | ||
} | ||
interface IntentHandler { | ||
fun handle(intent: Intent): Boolean | ||
fun onCompleted() | ||
} |
15 changes: 15 additions & 0 deletions
15
android/app/src/main/java/com/expensify/chat/intentHandler/IntentHandlerFactory.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.expensify.chat.intenthandler | ||
|
||
import android.content.Context | ||
|
||
object IntentHandlerFactory { | ||
fun getIntentHandler(context: Context, mimeType: String?, rest: String?): IntentHandler? { | ||
if (mimeType == null) return null | ||
|
||
return when { | ||
mimeType.matches(Regex("(image|application|audio|video)/.*")) -> FileIntentHandler(context) | ||
mimeType.startsWith("text/") -> TextIntentHandler(context) | ||
else -> throw UnsupportedOperationException("Unsupported MIME type: $mimeType") | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
android/app/src/main/java/com/expensify/chat/intentHandler/ShareFileObject.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.expensify.chat.intenthandler | ||
|
||
import com.google.gson.Gson | ||
|
||
data class ShareFileObject(val content: String, val mimeType: String?) { | ||
override fun toString(): String { | ||
return Gson().toJson(this) | ||
} | ||
} |
76 changes: 76 additions & 0 deletions
76
android/app/src/main/java/com/expensify/chat/intentHandler/TextIntentHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package com.expensify.chat.intenthandler | ||
|
||
import android.content.Context | ||
import android.content.Intent | ||
import android.net.Uri | ||
import com.expensify.chat.utils.FileUtils | ||
|
||
|
||
class TextIntentHandler(private val context: Context) : AbstractIntentHandler() { | ||
override fun handle(intent: Intent): Boolean { | ||
super.clearTemporaryFiles(context) | ||
when(intent.action) { | ||
Intent.ACTION_SEND -> { | ||
handleTextIntent(intent, context) | ||
onCompleted() | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
private fun handleTextIntent(intent: Intent, context: Context) { | ||
when { | ||
intent.type == "text/plain" -> { | ||
val extras = intent.extras | ||
if (extras != null) { | ||
when { | ||
extras.containsKey(Intent.EXTRA_STREAM) -> { | ||
handleTextFileIntent(intent, context) | ||
} | ||
extras.containsKey(Intent.EXTRA_TEXT) -> { | ||
handleTextPlainIntent(intent, context) | ||
} | ||
else -> { | ||
throw UnsupportedOperationException("Unknown text/plain content") | ||
} | ||
} | ||
} | ||
} | ||
Regex("text/.*").matches(intent.type ?: "") -> handleTextFileIntent(intent, context) | ||
else -> throw UnsupportedOperationException("Unsupported MIME type: ${intent.type}") | ||
} | ||
} | ||
|
||
private fun saveToSharedPreferences(key: String, value: String) { | ||
val sharedPreferences = context.getSharedPreferences(IntentHandlerConstants.preferencesFile, Context.MODE_PRIVATE) | ||
val editor = sharedPreferences.edit() | ||
editor.putString(key, value) | ||
editor.apply() | ||
} | ||
|
||
private fun handleTextFileIntent(intent: Intent, context: Context) { | ||
(intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM))?.let { fileUri -> | ||
val resultingPath: String? = FileUtils.copyUriToStorage(fileUri, context) | ||
if (resultingPath != null) { | ||
val shareFileObject = ShareFileObject(resultingPath, intent.type) | ||
saveToSharedPreferences(IntentHandlerConstants.shareObjectProperty, shareFileObject.toString()) | ||
} | ||
} | ||
} | ||
|
||
private fun handleTextPlainIntent(intent: Intent, context: Context) { | ||
var intentTextContent = intent.getStringExtra(Intent.EXTRA_TEXT) | ||
if(intentTextContent != null) { | ||
val shareFileObject = ShareFileObject(intentTextContent, intent.type) | ||
saveToSharedPreferences(IntentHandlerConstants.shareObjectProperty, shareFileObject.toString()) | ||
} | ||
} | ||
|
||
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) | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to add library for handling json for this line? Wouldn't it be easier just to do something like:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might work partially, but in case we don't want to add this library we have to handle a few more issues like escaping eg.
Hello "world"
is not a valid JSON string and we have to convert it toHello \"world\"
and there are more similar cases that need to be handled one by oneThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And what about using
JSONObject
fromorg.json.JSONObject
like here