Skip to content
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

[Connect SDK] Enable downloads in the Connect SDK #9583

Merged
merged 14 commits into from
Nov 15, 2024

Conversation

simond-stripe
Copy link
Collaborator

@simond-stripe simond-stripe commented Nov 10, 2024

Summary

Adds a download listener so that when the user attempts to download a file via the webhook, it downloads in the background and shows a toast when it's done.

Note that we don't need storage permissions because the downloading is delegated to a system service that has higher permissions: https://developer.android.com/reference/android/app/DownloadManager.Request#setDestinationUri(android.net.Uri)

Motivation

Certain embedded components, such as the Payouts Component, provide the user the ability to download files.

Testing

  • Added tests
  • Modified tests
  • Manually verified

Screenshots

demo-1731262880.mp4

@simond-stripe simond-stripe requested a review from a team as a code owner November 10, 2024 17:34
Copy link
Contributor

github-actions bot commented Nov 10, 2024

Diffuse output:

OLD: identity-example-release-base.apk (signature: V1, V2)
NEW: identity-example-release-pr.apk (signature: V1, V2)

          │          compressed          │         uncompressed         
          ├───────────┬───────────┬──────┼───────────┬───────────┬──────
 APK      │ old       │ new       │ diff │ old       │ new       │ diff 
──────────┼───────────┼───────────┼──────┼───────────┼───────────┼──────
      dex │     2 MiB │     2 MiB │  0 B │   4.1 MiB │   4.1 MiB │  0 B 
     arsc │     1 MiB │     1 MiB │  0 B │     1 MiB │     1 MiB │  0 B 
 manifest │   2.3 KiB │   2.3 KiB │  0 B │     8 KiB │     8 KiB │  0 B 
      res │ 301.8 KiB │ 301.8 KiB │  0 B │ 455.5 KiB │ 455.5 KiB │  0 B 
   native │   6.2 MiB │   6.2 MiB │  0 B │  15.8 MiB │  15.8 MiB │  0 B 
    asset │   7.1 KiB │   7.1 KiB │  0 B │   6.9 KiB │   6.9 KiB │  0 B 
    other │  90.2 KiB │  90.2 KiB │ +6 B │ 170.3 KiB │ 170.3 KiB │  0 B 
──────────┼───────────┼───────────┼──────┼───────────┼───────────┼──────
    total │   9.6 MiB │   9.6 MiB │ +6 B │  21.5 MiB │  21.5 MiB │  0 B 

 DEX     │ old   │ new   │ diff      
─────────┼───────┼───────┼───────────
   files │     1 │     1 │ 0         
 strings │ 19966 │ 19966 │ 0 (+0 -0) 
   types │  6188 │  6188 │ 0 (+0 -0) 
 classes │  4979 │  4979 │ 0 (+0 -0) 
 methods │ 29759 │ 29759 │ 0 (+0 -0) 
  fields │ 17526 │ 17526 │ 0 (+0 -0) 

 ARSC    │ old  │ new  │ diff 
─────────┼──────┼──────┼──────
 configs │  164 │  164 │  0   
 entries │ 3622 │ 3622 │  0
APK
   compressed    │  uncompressed   │                        
──────────┬──────┼──────────┬──────┤                        
 size     │ diff │ size     │ diff │ path                   
──────────┼──────┼──────────┼──────┼────────────────────────
 28.4 KiB │ +8 B │ 62.9 KiB │  0 B │ ∆ META-INF/CERT.SF     
  1.2 KiB │ -1 B │  1.2 KiB │  0 B │ ∆ META-INF/CERT.RSA    
 25.3 KiB │ -1 B │ 62.8 KiB │  0 B │ ∆ META-INF/MANIFEST.MF 
──────────┼──────┼──────────┼──────┼────────────────────────
 54.9 KiB │ +6 B │  127 KiB │  0 B │ (total)

@simond-stripe simond-stripe force-pushed the simond/enable-donwloading-connect branch from ae703b3 to eeb1657 Compare November 10, 2024 19:16
@simond-stripe simond-stripe force-pushed the simond/update-api-to-views branch from 0f24247 to 4c9c1b9 Compare November 11, 2024 18:17
@simond-stripe simond-stripe requested review from a team as code owners November 11, 2024 18:17
Base automatically changed from simond/update-api-to-views to master November 11, 2024 19:00
@simond-stripe simond-stripe force-pushed the simond/enable-donwloading-connect branch from 7f250fc to e667558 Compare November 11, 2024 19:02
}

private fun showErrorToast() {
MainScope().launch {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd pass in the main scope like you're doing with io Scope to make it more testable

context.getSystemService(Context.DOWNLOAD_SERVICE) as? DownloadManager,
private val ioScope: CoroutineScope = CoroutineScope(Dispatchers.IO),
// methods below exposed for mocking in tests
private val getDownloadManagerRequest: (Uri) -> DownloadManager.Request = { uri ->

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

passing in all these lambdas feels a bit strange here. is there some kind of interface you can use here to abstract away the OS level functionality? that way you can just mock the interface.

This will likely make the code a lot easier to follow too

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe abstracting away and creating components for the following? Then this service could call StripeDownloadManager and you could mock all of these for your tests

StripeDownloadManager:

  • startDownload(url, ua, ...,) : DownloadMetadata
  • getDownloadProgress(Id): DownloadStatus

DownloadMetadata:

  • id
  • fileName

ToastManager

  • showToast(string)

val downloadId = downloadManager.enqueue(request)

// Monitor the download progress and show a toast when done
val query = DownloadManager.Query().setFilterById(downloadId)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this static method feels like it should be behind some kind of interface if we want this class to be testable

Copy link

@seankenkeremath-stripe seankenkeremath-stripe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the interface for the DownloadListener can be cleaned up a bit here. It doesn't have to be what i'm suggesting, but I think it's worth taking a stab at. Let me know what you think

@simond-stripe
Copy link
Collaborator Author

I think the interface for the DownloadListener can be cleaned up a bit here. It doesn't have to be what i'm suggesting, but I think it's worth taking a stab at. Let me know what you think

Good point, I think abstracting these behind an interface is a good idea.

@seankenkeremath-stripe I've updated the code and I find it's more idiomatic and easier to read now, but still pretty lightweight - let me know what you think

}

private fun showErrorToast() {
mainScope.launch {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you may actually just want to make the toast manager call mainScope so you don't need to worry about it here. it will never be called on any other thread anyway

/**
* Returns a [DownloadManager.Request] for the given URI.
*/
fun getDownloadRequest(uri: String): DownloadManager.Request

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this need to be part of the interface? sort of feels like an implementation detail

/**
* Returns a best-guess file name for the given URL, content disposition, and MIME type.
*/
fun getFileName(url: String, contentDisposition: String? = null, mimeType: String? = null): String

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same with this one, with both of these it seems like the end result is just that you want the file name, and i'm wondering if you could just return that along with the ID when you start the DL request

Copy link

@seankenkeremath-stripe seankenkeremath-stripe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! A few non-blocking suggestions

Copy link

@seankenkeremath-stripe seankenkeremath-stripe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice 👍

@simond-stripe simond-stripe merged commit 06beaa8 into master Nov 15, 2024
14 checks passed
@simond-stripe simond-stripe deleted the simond/enable-donwloading-connect branch November 15, 2024 19:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants