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

Choose alternative download location in settings (external SD) #117

Closed
primaclip opened this issue Jun 13, 2018 · 30 comments
Closed

Choose alternative download location in settings (external SD) #117

primaclip opened this issue Jun 13, 2018 · 30 comments

Comments

@primaclip
Copy link

Hi,
and thanks for the great app!!!

Is there a way to change the download folder/location? My internal memory is very limited, so I'd like to download TV shows to a folder an the external SD card.

Thanks again!

@cemrich
Copy link
Member

cemrich commented Jun 17, 2018

There is currently no way to change the download location. I tried to avoid implementing a file chooser. However, your use case does look like a common one. So I will leave this issue open as enhancement.

@cemrich cemrich changed the title How to choose alternative download location (external SD)? Choose alternative download location in settings (external SD) Jun 17, 2018
@rugk
Copy link

rugk commented Jun 17, 2018

Instead of a file chooser, you could just offer two had-coded paths. One points to internal memory, the other to a fixed path (e.g. /emulated/0/zapp or, or maybe additionally, a more "hidden" one in that Android dir: /emulated/0/Android/<appID>/…), so you only need to offer a radio selection then.

@cyberbeat
Copy link

This would be a great addition. I also have little internal memory and a big 32 GB sd card. I also agree with the proposal from the comment before.

@cemrich
Copy link
Member

cemrich commented May 18, 2019

File handling on Android is insane: I found Context.getExternalMediaDirs() to enumerate all valid media download locations (including sd cards and other removable storage) but encountered an issue with the download manager on my phone.

For future reference:

I'll retry in a month or so.

@cemrich
Copy link
Member

cemrich commented Jul 25, 2019

Android 9 on the Pocophone has a similar issue. DownloadManager is not able to access the external SD despite read and write permissions correctly set. I have no idea what is wrong with this SD card thingy.

Shouldn't it be possible to transfer Zapp as an app to an external SD card? Does this solve the problem?

@alexanderadam
Copy link

alexanderadam commented Sep 30, 2019

Shouldn't it be possible to transfer Zapp as an app to an external SD card? Does this solve the problem?

There are two problems with this

  1. It doesn't allow me to move Zapp in the app settings. I can do it with other apps though. Could it be that it must be marked somehow to support Adoptable Storage?
  2. Even if I could move the app: the downloads doesn't seem to count as app data in my case. So it probably would just move the app itself (and probably the settings/data?) but not the downloads.

So did you check for the READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permission in your example (if not this might help)?

And if I read this properly it should be possible to use the integrated system dialogue from Android (since Android 5.0) with ACTION_OPEN_DOCUMENT_TREE. In my opinion this should be the best option because it introduces no additional libraries (you can also see it in this video).

Otherwise there are some libraries:

PS: I'm not an Android developer so I have no idea whether anything I wrote actually makes sense.
PPS: It might be a good feature for Hacktoberfest. 😉

@cemrich
Copy link
Member

cemrich commented Oct 2, 2019

  • check why zapp cannot be moved to external storage
  • check if zapp downloads can be marked as application data
  • check if uri returned by ACTION_OPEN_DOCUMENT_TREE works with download manager (across device restarts)

ACTION_OPEN_DOCUMENT_TREE might actually work, because it does return a content uri instad of an absolute path.

@cemrich
Copy link
Member

cemrich commented Oct 3, 2019

check why zapp cannot be moved to external storage

Installation on external storage has to be enabled inside the manifest: https://developer.android.com/guide/topics/data/install-location.html
This is no viable option for Zapp because it has to use a service for playback which would be killed when the device is connected to the computer as mass storage device.

@cemrich
Copy link
Member

cemrich commented Oct 4, 2019

Android Q has restricted file handling even further. Additionally DownloadManager is not able to download to any content: uri but to file: uri only. So uris returned by ACTION_OPEN_DOCUMENT_TREE cannot be used for download manager. This is very inconvenient as I see no way of changing the content uri in a file one.

I am thinking about using ExoPlayer for downloading content. This would play back downloaded media from disk automatically. But downloaded shows will not be saved as files and not readable by any other application (which is quire a big limitation...).

@alexanderadam
Copy link

Additionally DownloadManager is not able to download to any content: uri but to file: uri only.

Is this not solving the problem?

@cemrich
Copy link
Member

cemrich commented Oct 4, 2019

I tried similar code snippets without success. Finding up to date answers is Quote difficult.

@alexanderadam
Copy link

alexanderadam commented Oct 5, 2019

And would it be possible just to use a fixed path, like suggested by @rugk earlier?

Something like

File sdCard = Environment.getExternalStorageDirectory();
File dir = new File (sdCard.getAbsolutePath() + "/zapp_downloads");
dir.mkdirs();

File file = new File(dir, "video.mp4");
 Uri uri = Uri.fromFile(file);

 DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadURL));
 request.setTitle("video");
 request.setDescription("video");
 request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
 request.setDestinationUri(uri);
 request.allowScanningByMediaScanner();

 downloadID = downloadManager.enqueue(request);
 downloadProgressView.show(downloadID, new DownloadProgressView.DownloadStatusListener() {
     @Override
     public void downloadFailed(int reason) {
         Log.d(TAG, "downloadFailed" + reason);
     }

     @Override
     public void downloadSuccessful() {
         Log.d(TAG, "downloadSuccessful");
     }

     @Override
     public void downloadCancelled() {
       Log.d(TAG, "downloadCancelled");
     }
 });
 downloadButton.setVisibility(View.GONE);

PS: I took this from stackoverflow. 😉

@alexanderadam
Copy link

alexanderadam commented Feb 13, 2020

Is there anything I could help here, except from doing some Java? 🤔

@cemrich I've put a bounty on it, in case this changes anything. 😉
EDIT: I transferred the money on another way as discussed via email. For others: Do not use BountySource! Use any other of the mentioned possibilities.

@cemrich
Copy link
Member

cemrich commented Apr 30, 2020

And would it be possible just to use a fixed path, like suggested by @rugk earlier?

getExternalStorageDirectory() is deprecated and did never work for all device models. At the moment I see no solution to this (seemingly simple and common) problem, that is not:

  1. hacky
  2. using another download manager involving a lot of work as all libraries seem to be outdated

What might work: Downloading the files in some sort of cache/temp directory and copying it over once the download is complete. This however is a UX nightmare and a lot of work to implement. The download manager will notify the user with a wrong (temp) path and zapp has to listen to finished downloads.

As it stands now I will not implement this feature to avoid hacks, compatibility issues, UX fails and a LOT of work for a small feature.

If you know another application that handles this issue gracefully on all Android versions I may be able to look into it.

@alexanderadam
Copy link

alexanderadam commented May 1, 2020

@cemrich I just created a StackOverflow question for it. Maybe someone comes up with an good idea.

Someone suggested:

Use ACTION_CREATE_DOCUMENT and allow the user to choose where your app should download the user's content.

And ACTION_CREATE_DOCUMENT brought me to these examples on GitHub:

Furthermore the application RCX seems also support SD cards when using SAF according to the docs.

Does this help?

@cemrich
Copy link
Member

cemrich commented May 1, 2020

@alexanderadam ACTION_CREATE_DOCUMENT does return a content:// uri (as does ACTION_OPEN_DOCUMENT_TREE ) which is not compatible with the download manager.

NewPipe does support SD cards (with SAF enabled, which might break some devices?) but implements its own download manager.

You StackOverflow question does not mention Download manager which is the main limiting factor here, because it does require (old style) file:// urls.

@alexanderadam
Copy link

alexanderadam commented May 3, 2020

I see, I wasn't able to understand the issue here. To be honest: I'm still not understanding.

So to clarify this: you need a certain representation of the download path for the download manager? How is it called?

Does File#getCanonicalPath / File#getAbsolutePath help?

Or setting a destination URI on the Download Manager via DownloadManager.Request#setDestinationUri which explicitely can "be a file URI to a path on external storage"?

Or maybe DownloadManager.Request#setDestinationInExternalPublicDir

Or does this StackOverflow thread help?

@rugk
Copy link

rugk commented May 3, 2020

I've seen other apps that open the Android file picker, ask the user to select the sdcard there and press okay, and then they have access to it. Don't ask me which API level that is though.

@alexanderadam
Copy link

alexanderadam commented May 4, 2020

I've seen other apps that open the Android file picker, ask the user to select the sdcard there and press okay, and then they have access to it.

But did they use the default Download Manager, too?

Because from what I understand so far, other download manager libraries (i.e. Fetch, novoda's download manager, okdownload or PRDownloader) don't have this problem anyway (apart from having additional features like support for long running downloads, checksum validation, resuming, etc.).

EDIT: It seems that bountysource had issues but it shows the bounty now. Maybe someone else would also like to at least sponsor a coffee or something similar for the person who solves this issue?

EDIT 2: I transferred the money on another way as discussed via email. For others: Do not use BountySource! Use any other of the mentioned possibilities.

@rugk
Copy link

rugk commented May 4, 2020

I guess no, at least they were not intended for downloading things, maybe just saving files AFAIK. But TBH I don't know… 🤷

@cemrich
Copy link
Member

cemrich commented May 4, 2020

@alexanderadam Most apps seem to use download manager libraries. I am hesitant to use these because these libraries tend to be deprecated/unmaintained pretty quickly. However, fetch seems to be actively maintained for a long period now, so I'll consider using it.

The problem lies within how Android handles its file system. Newer Android versions try to abstract away the file system to restrict and decouple apps and increase security. As a result apps don't work with old school file paths any more (like file:///sdcard/Movies/Zapp/download.mp4), but with abstracted content uris (like content://media/external/audio/media/710). These can't be converted in any meaningful, non-hacky way. Most Android apis can work with the newer content urls (which are the right way to go). But DownloadManager seems to require the old school file paths.

@alexanderadam
Copy link

fetch seems to be actively maintained for a long period now, so I'll consider using it.

This would probably fix other issues, too.
Because currently I'm using Zapp on a device that is low on internal space. The native download manager will simply create broken files if I add a few files to the download queue and I'm running out of space. It's not even complaining or giving a warning. Those files might even have a similar size but it seems that it's content isn't fully written. This means a MP4 might seem good and you can start it but you can't see the second half of it. So if I want to download a few files I have to:

  1. add a few files to the queue
  2. wait until they are finished
  3. check whether they are fine (because this corruption thing can even happen with very few files if I chosed 'too high' quality and some files were too big)
  4. move them to SD card
  5. move on with 1. for the next files

So I hope this makes it understandable how painful it is to use Zapp on a device that is low on internal storage. 😉

I guess that a proper download library would not only just fix the external space issue but might also give possibilities for error handling (i.e. low on space) and add checksum checks/resuming.

@cemrich
Copy link
Member

cemrich commented May 30, 2020

A quick update: I am still working on it, but due to this crazy pandemic-childcare-situation progress is rather slow.

Fetch is usable. I need to fix some quirks regarding notifications but otherwise it integrates nicely.

Also I think I found a solution to switch between primary storage and sdcard on all supported Android versions (which is insane, given these APIs are all over the place and changing constantly). Both storage locations will be fixed to the apps own external (media) directory for ease of use and to avoid configuration clutter. Bonus points: Zapp does not require its storage permission any more.

I simplified the ui and need to re-add some features like quality selection. It will take a while, but an in-app download progress indicator is worth the wait :)

@alexanderadam
Copy link

Oh wow, this sounds wonderful! Thank you so much for working on this!

@cemrich
Copy link
Member

cemrich commented Aug 11, 2020

I just released a beta version to test this feature on a wide variety of devices. To install it, you have to uninstall any Zapp version from F-Droid first. Please let me know if you experience any issues!

@alexanderadam
Copy link

alexanderadam commented Aug 11, 2020

Wow, the download progress bar looks very nice! ❤️
Also the download worked perfectly for me.

Just two things:

I had issues downloading some videos and had to hit Retry download twice.

Screenshot_20200811-162052_Zapp

  1. Could it be that there's a tight timeout setting that could be increased? I don't know whether it ran into timeout or the problem was something else? Is it possible to see what the issue was?
  2. I guess resuming is not possible, right?

It was not very clear where I could find the video (I found it on the SD card in the Movies directory). Maybe it would be nice to have it mentioned somewhere (in case I didn't miss this information). Or even better a Open destination button or so?

It's just a small detail, however. 😉
The feature itself works great! Thank you sooooooo much!

@cemrich
Copy link
Member

cemrich commented Aug 12, 2020

@alex1702 I added the raw error codes as shown in #197. I use the default Fetch timeouts which should be fine for most use cases and are certainly way better than my guesses.

Pause / Resume is supported (small action buttons inside the download notification) but I think Fetch cleans up any file parts after an error event.

There is no fool proof way of opening up a directory in Android. Do you think it is too hard to find? I tried to add it to every media collection I could find :)

@alexanderadam
Copy link

alexanderadam commented Aug 12, 2020

I added the raw error codes as shown in #197.

Nice! Is there a APK that has the error codes patch integrated?

You didn't had to push the Retry button in your tests?

There is no fool proof way of opening up a directory in Android. Do you think it is too hard to find?

It's no big issue in any case. Would it be easier to write the path (i.e. a small text below that says The video will be located in /foo/bar/baz)?

@cemrich
Copy link
Member

cemrich commented Aug 14, 2020

Nice! Is there a APK that has the error codes patch integrated?

Not yet. I'll release another beta before releasing.

You didn't had to push the Retry button in your tests?

Not even once. But I've very stable wifi here. I'll test again with an emulator.

Would it be easier to write the path

There is no path that has any meaning to the user. It will look something like content://media/external/audio/media/710 because google decided to abstract away the file system. Maybe I can open the movies directory at least in some higher Android versions.

@cemrich
Copy link
Member

cemrich commented Sep 2, 2020

@alexanderadam Sorry, I forgot to mention the new Release. Version 2.5.0 is available on Github and F-Droid. I did not find a clean way to open the download directory, so I'll skip this feature. However, another user reported sporadic errors during download with a lot of retries. I opened #204 to continue investigating. Could you please test if you are also experiencing an "Unknown io error"? Also, which version of Android are you using?

I'll close this issue. Feel free to open new ones for more detailed error reports.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants