-
Notifications
You must be signed in to change notification settings - Fork 65
Remember the pages you were on in a PDF even if the app is closed. #133
base: dev
Are you sure you want to change the base?
Conversation
Using the |
Thanks @derekelkins, interesting work! What about using a separate SharedPreferences file for saved locations? It would also have the advantage that the concurrency would be managed internally by SharedPreferences instead of manually by us. |
I agree with you, currently this feature wouldn't work if the user opens the same document via different apps. As you said, the only meaningful unique identifier I can think of is a hash of the document. |
I considered just using the preferences system, but this seems like an abuse of it and usage patterns (e.g. relatively heavy writes) seem like exactly the thing the Android docs warn against. That said, it may still work fine in practice, possibly with the addition of a (fairly generous, say 1,000 entry) limit on the number of saved locations and/or throttling of the writes. For MD5 hashing, I agree that computing the hash is probably fast enough to not be noticeable for most files. Unfortunately, I have several PDFs which are over 100MB, and, of course, larger PDFs (page count-wise at least) are the ones where this feature most comes in handy. If the hash is computed in the background, this is much less of an issue, though it might still be a bit problematic for PDFs from the internet. A mapping from Uri to hash could be maintained so we don't need to recalculate the hash every time we open a file. The current mapping would then be from hash to saved location rather than from Uri to saved location. (It may makes sense to store a date last viewed as well and to pop up a toast if when opening a Uri that hashes to a PDF that hasn't been viewed in, say, more than a week. It might be surprising to open a seemingly new file and get scrolled halfway through it because you've forgotten that you've read it before. I fairly often open papers that I last looked at well over a year ago.) I may play around with doing the hashing. |
I thought more about this and probably is not a very good idea UX-wise when you have very big files. If the hash computation took for example 5 secs, the user would start reading/scrolling through the document and then suddenly the viewer would jump to the previously saved location. |
This seems like a good idea! In those cases you could prompt the user to go to the previously saved location using a snackbar (I don't know if that's what you meant with "popup a toast"). Also, to prevent storing useless entries in the database you could avoid saving the location (and deleting the previously saved location if present) when the current page is 1. |
I updated the pull request to identify the PDF based on a hash, however, it computes the hash synchronously so it can find the saved page before showing the PDF. To avoid this causing a large delay (which was on the order of 7 seconds when I opened a ~200MB file on a real phone), the code only hashes the first megabyte. This should be unique in practice. The delay for this is unnoticeable on my device, but the size could easily be reduced if it's too slow on other devices. |
Good idea @derekelkins! I have some more lower-level suggestions on your PR:
|
|
||
@PrimaryKey | ||
@NonNull | ||
public byte[] hash; |
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.
How does Room store byte arrays in the DB? If it serializes them as strings then it's fine, but if it stored them as blobs it would be very sub-optimal.
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.
I don't know, but I suspect so. Frankly, I'm a bit surprised that it worked without issue at all. But I've now changed it to explicitly be a String
.
The
I don't think that this triggers less often than the
I don't think the (admittedly fairly small) code complexity is worth this fairly small savings in disk space. As mentioned before, we're looking at something like 1MB of disk space per 10,000 distinct PDFs opened.
I made this change, though I personally see the saved locations as more valuable than any of the other stuff you'd lose by clearing data (versus cache). I'm also not sure there will be much of a reason to blow away just the saved locations. But it doesn't really impact me one way or another. I don't know if a typical user would be surprised that clearing cache blew away the saved locations, but I don't think a typical user would do that. |
This is a mindful consideration, and I'm fine with that 👍
This is really a one-line if condition 🤣. It's just the fact of saving unnecessary (even if small) data that bothers me (but maybe it's just me)
Didn't notice you were using a single thread executor, so OK for the race conditions.
This is a fair point, mine was just a mere suggestion because I'm not too sure on how much this data could be valuable from a user's standpoint. I personally didn't like much the idea of not being able to clear saved location data without clearing also all app data (particularly settings). Maybe we could just keep it as it is and change that if someone raises a complaint. Btw, thanks @derekelkins for keeping up! :) |
@@ -447,6 +504,13 @@ void navToSettings() { | |||
} | |||
|
|||
private void setCurrentPage(int page, int pageCount) { | |||
if (fileContentHash != null) { | |||
String hash = fileContentHash; // Don't want fileContentHash to change out from under us |
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.
If you really want to avoid this value to change out, you need to assign it to the local variable before the if (the value could change after the condition gets evaluated).
However, I don't see a reason why fileContentHash
could change in the middle of this method. setCurrentPage
won't be called if the document isn't loaded yet.
This change persists the page in a PDF to a database and then restores it when reopening a PDF. In other words, even if you completely close the app/restart your phone/etc. it will still remember where you were in a PDF. It remembers forever. It would probably take on the order of opening 10,000 distinct PDFs to have the database grow to 1 megabyte.
Some implementation details:
I didn't want to pull in something like RxJava and RxAndroid just for this, so I do the asynchronous handling using
java.util.concurrent
directly as the deprecation message forAsyncTask
suggests. It sounds like you are going to rewrite to Kotlin, so then using Kotlin's coroutines would be the recommended pattern.I didn't do much testing on older versions. I ran it in an emulator using API Level 27 and my phone is API Level 29 or 30. I think Android Room will work a decent ways back.
Writing the page to the DB every time it changes seems fine, performance-wise, in practice for me.