podverse / podverse-rn

Podverse mobile app written in React Native for iOS, Android, and F-Droid
https://podverse.fm/about
GNU Affero General Public License v3.0
218 stars 36 forks source link

Improve offline experience #2117

Closed mitchdowney closed 7 months ago

mitchdowney commented 10 months ago

There are a variety of bug reports like this...but creating a new one as we really need to nail down offline experience or the app leads to a frustrating UX for people.

i think i mentioned the internet. data issues before. i am in a train.. and not having singal stalled opening the application. i use this apo as an offline app. download when docked only. so.. the issues that prevent me feom using it are very annoying more so than others. im not mentionening the failure to download data that should not be attempted o begin with when wifi is off. and never reattempted again automatically.

Android v4.15.3

lovegaoshi commented 10 months ago

my android 14, airplane mode does not stall podverse from a cold start does PV even have redownload attempts? previously i mistook fail to cache -> RNTP loaded web url as reattempts but its just working as intended.

lovegaoshi commented 9 months ago

is this the issue to keep track of PV's android external download problem? anyways here is my snippet to access media:

        val query = this.contentResolver.query(
          MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
          arrayOf(
            MediaStore.Audio.Media._ID,
            MediaStore.Audio.Media.RELATIVE_PATH,
            MediaStore.Audio.Media.DISPLAY_NAME
            ), null,null, null)
        query?.use { cursor ->
          Log.d("RNTP", cursor.count.toString())
          val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID)
          val pathColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.RELATIVE_PATH)
          val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME)
            while (cursor.moveToNext()) {
              Log.d("RNTP", cursor.getString(idColumn) + cursor.getString(pathColumn) + cursor.getString(nameColumn))
            }
        }

which gives:

---------------------------- PROCESS STARTED (14000) for package com.noxplay.noxplayer ----------------------------
2024-02-21 12:14:41.706 14000-14000 RNTP                    com.noxplay.noxplayer                D  3
2024-02-21 12:14:41.706 14000-14000 RNTP                    com.noxplay.noxplayer                D  1000000033Music/Free_Test.mp3
2024-02-21 12:14:41.706 14000-14000 RNTP                    com.noxplay.noxplayer                D  1000000034Music/Free_Test_Data_100KB_MP3.mp3
2024-02-21 12:14:41.706 14000-14000 RNTP                    com.noxplay.noxplayer                D  1000000035Music/Gggg.mp3

the id column can be added to the path via https://developer.android.com/reference/android/content/ContentUris#appendId(android.net.Uri.Builder,%20long)

I'm planning to add a local playback myself so I should have something ready for PV as well. though note file addition/deletion needs to be broadcasted as https://stackoverflow.com/questions/3300137/how-can-i-refresh-mediastore-on-android

media content can be resolved as: content://media/external/audio/media/1000000037

whereContentUris.appendId(Uri.Builder().path(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.path), cursor.getLong(idColumn)).build().toString()gives /external/audio/media/1000000037

~I'm not sure how to get specifically from SD cards. MediaStore doesnt seem to distinguish. my plan is to use folderpicker to get the relative path, and use relative path to filter MediaStore.Audio.Media.RELATIVE_PATH column.~ doesnt matter anyways as if PV is set up correctly, it only shows files created by PV.

The other solution is MANAGE_EXTERNAL_STORAGE which google will heavily scrutinize for apps on google play.

mitchdowney commented 9 months ago

Thanks so much @lovegaoshi. Unfortunately I'm struggling with how I am supposed to use this...it seems like it is a code fragment and I will need to combine it with the other info and links you provided.

I noticed something odd. react-native-scoped-storage has a java function named getRealPathFromURI, which has a lot of code to it, but it doesn't look like it is used anywhere, nor is it exported as a RN function. I tried moving it into a custom module...but I keep getting undefined returned when I try to use it with a URI like: content://com.android.externalstorage.documents/tree/primary%3APodcasts/document/primary%3APodcasts/00ABibM3S.mp3

I put my WIP changes in this file in case you want to see it.

Question: for the changes you are proposing, should it go in a custom module, or elsewhere? Is this something apps should have to write themselves, or is this something ideally RNTP would have a built-in way to handle?

Sorry I didn't have luck figuring this out. Maybe if @kreonjr and I pair he can help me get on track.

lovegaoshi commented 9 months ago

getRealPathFromURI doesnt work in the way you think it would; it accepts an external/something/something/something URI, not content://, and the URI needs to set an authority. anyways, I have a local playback WIP on my app, might finish it this weekend.

the content URI format is actually this: content://com.android.externalstorage.documents/tree/primary%3APodcasts/document/primary%3APodcasts/{some number} so the way we guessed about the content URI (adding filename to it) is actually wrong.

specifically for PV @ReactMethod fun listMediaDir is the most important one; this will return an array of audio items that is in the given subpath name (and only created by PV, if no permissions are granted), where item.realPath is the file:/// URI that can be used universally. in addition item.URI, the content:// URI can also be passed to RNTP, but not to ffmpeg-kit. PV probably would like to do the name filter natively. I will write it out later in my repo as an example.

lovegaoshi commented 9 months ago

ok! i think i have a functional local playback feature specifically for PV there are several points:

  1. PV's isPodcastDownloaded fails immediately on external download management and always resort to network;
  2. even if force setting 1 to true, RNTP does not take PV's parsed content:// URI.

to resolve 1: isPodcastDownloaded needs to have an android specific logic that detects file presence via MediaStore. here's my native module for this: https://github.com/lovegaoshi/azusa-player-mobile/pull/309/files#diff-e4aa2e1ec6e5262a3f2f3fc9fb457bd8407f6e697407dd16b2f2dcde15db10aeR68 just pass the derived podcast id/filename should be fine. the return (an array)'s element has realPath and URI that both work with RNTP, but if say ffmpeg is involved realPath is the one to use.

  1. just use 1's realPath.

PV should not need any permission requirements as MediaStore by default allows to see any files created by the app itself.

mitchdowney commented 7 months ago

Fix for this should be in v4.16.0