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

Allow quick access to recently viewed files #3

Closed
marcus-crane opened this issue May 24, 2020 · 11 comments
Closed

Allow quick access to recently viewed files #3

marcus-crane opened this issue May 24, 2020 · 11 comments
Labels
enhancement New feature or request

Comments

@marcus-crane
Copy link

I think one thing that would improve Orgro is a list of recently opened files, say the last 5 or 10?

Perhaps some weight based system so it's the most commonly visited files plus the most recent file?

As an an example of my use case, I have a cookbook.org file that I store recipes in, and I tend to open it commonly on my phone. Ditto for my monthly journal as well.

I suppose the possible trickiness may come in making sure that references are updated for moved files, and deleted files are able to be removed from the recents list!

Unfortunately I don't know Dart at all, so I'm not able to even attempt to try and contribute this myself but I've opted to become a Github sponsor since I'd like to see Orgro able to grow :)

@amake
Copy link
Owner

amake commented May 24, 2020

First, thanks very much for the sponsorship, and your words of support elsewhere as well. I really appreciate it.

I have thought about a feature like this, and would like to do something along these lines. However unfortunately it's only straightforward in the trivial case, which would be much less useful for your use case.

The bad news

The core problem is that due to security restrictions, I currently can't access files "in-place". As you probably know, iOS and Android apps are sandboxed and don't have easy raw access to the filesystem. Almost every way you get files into the app is really copying behind the scenes (and then I delete after loading the content).

So it's easy to have a list of recently opened files, but those files will be frozen at the point of time that you opened them. Since your use case (and mine) is to continue to edit the files over time on another machine, this option is not that appealing.

Good news 1

On iOS I did implement support for reading a file in-place with NSFileCoordinator. This lets the iOS share sheet say "Open in Orgro" instead of "Copy to Orgro", and is generally more useful for apps that can also edit files. I just discovered that it seems you can bookmark these URLs for later access, so it could be possible to implement your request based on this, for files opened via the iOS share sheet.

For Android it seems there is an equivalent capability with the ACTION_OPEN_DOCUMENT intent and ContentResolver#takePersistableUriPermission.

For the Open File button I am using the Flutter file_picker plugin, and this can only copy files into the app. They are working on support for persistent in-place access, but it seems to be a long way away. In the meantime someone else has made another plugin, file_picker_writable, which seems to offer the required data.

Implementing this is probably a several-week project based on my impression of the complexity and the amount of time I have to work on it (Orgro is my fun project I work on in the evenings when my kids are finally in bed).

Good news 2

You can already sort of do what you want with a few caveats:

  1. The file picker that opens with the Open File button remembers your last visited folder, so if your most-opened files are in the same folder you should have relatively easy access to them with just one extra tap (the Open File button) and maybe a little scrolling
  2. If you're mostly opening the same files, they should all fit in the Recents tab even if they live in different folders. That's at most another extra tap away.

Questions

So I have a better idea of your situation, can you tell me:

  1. What kind of iOS device do you use?
  2. What iOS version are you running?
  3. How do you access your org files on your iOS device? (for instance I use the Google Drive app)

@marcus-crane
Copy link
Author

Oh! Thanks for that really in-depth response!

I'm using an iPhone SE 2020 (MX9U2X/A) currently running iOS 13.5, although I do have a beta profile installed.

I'm currently a Dropbox user, which is where I store all of my org files. I have considered using Github in some capacity, and I think Working Copy (iOS Git client) has support to be a provider for the Files app? I've never tried it though

I tried out the Recents tab trick that you mentioned. If I navigate to an org file and open it, it does appear in my Recents, however as soon as I navigate away, it disappears from my Recents tab. It seems that only items listed as "On My iPhone" or "iCloud Drive" actually remain in the Recents history?

I tried disabling every provider except Dropbox which prompted Orgro to ask me for a new location. I picked "Save to my iPhone" and now the Browse tab persists in showing me my notes folder (including across app restarts). Before then, it would always open to my (unused) iCloud Drive.

While the Recents tab still doesn't persist anything from Dropbox, having the Browse button persist my chosen directory (Dropbox/org/notes/) is basically the next best thing!

On a side note, I don't have a great understanding of mobile APIs so I'd be curious to know a bit more. You mentioned it's not possible to access a file in place (I'm guessing within the Dropbox provider in this case), but is it possible to copy files from a provider without user input?

In this case, it's easy to imagine a Recents list would just trigger the same action (copy file to device, delete after) and retains a link to whereever the file lives but it sounds like the iOS filesystem APIs don't actually let you iteract with eg; Dropbox without having to prompt the user with the file picker?

Maybe I should do some little mobile side projects to better understand how my devices actually handle this stuff!

Thanks again for the response and for going as in depth as you did 🙂

@marcus-crane
Copy link
Author

I'll close this issue anyway given that I figured out how to get my device to retain the same Dropbox location

@amake
Copy link
Owner

amake commented May 24, 2020

I tried out the Recents tab trick that you mentioned. If I navigate to an org file and open it, it does appear in my Recents, however as soon as I navigate away, it disappears from my Recents tab. It seems that only items listed as "On My iPhone" or "iCloud Drive" actually remain in the Recents history?

That's really strange. I hadn't tried Dropbox, but Google Drive items definitely persist in Recents. I just tried it quick with Dropbox and the file didn't appear in Recents at all. I wonder if there's some API they haven't implemented?

You mentioned it's not possible to access a file in place (I'm guessing within the Dropbox provider in this case), but is it possible to copy files from a provider without user input?

I forgot to mention this possibility. The files "in" Dropbox may not actually exist on the filesystem, and even if they did, apps can't inspect the storage areas of other apps. So the way to make this happen is to give Orgro permission to access your Dropbox via their API, perhaps by embedding their SDK.

This is the road I'm least excited about:

  • I'd have to explicitly add support for any services I want to use, rather than allowing the user to use anything that supports the OS files API
    • In the worst case that means adding SDKs for every provider; those SDKs don't have Dart wrappers, so I'd have to make that and implement the iOS/Android side for each
    • I see a simple_auth package that wraps support for some providers but I'm not sure how much work that does for me (maybe it eases API access but I still have to implement my own picker UI?)
  • There are distribution implications: I know some FOSS apps don't distribute e.g. Dropbox-compatible versions on F-Droid because the Dropbox SDK is not FOSS. (Orgro isn't on F-Droid yet, but it's in the works.)

I think this is a totally legit feature request and I'm interested in the direction I mentioned in "Good news 1", so I'll reopen this.

@amake amake reopened this May 24, 2020
@amake amake added the enhancement New feature or request label May 24, 2020
@amake amake changed the title (feat.) Recently viewed files Allow quick access to recently viewed files May 24, 2020
@marcus-crane
Copy link
Author

I don't actually think I would want to encourage having to incorporate the Dropbox SDK either

Mobileorg I believe uses it, but only scopes to the Apps folder. Of course, that's the properly secure option (as it can't access any of my other files) but I really disliked having to store my core org folders in Dropbox/Apps/Mobileorg just to satisfy one single mobile client, that I may not even use months or years down the track.

You can of course scope it to your entire Dropbox, which I would find ideal, but I imagine some users may question that choice as it requires permission to potentially access more things than you need.

For now, I'm perfectly happy with the above solution to feel that I don't need a quick access list anymore but I'll still accept one of course. Working off of the Files provider thing seems like a really beneficial way for Orgro to operate as mentioned

I'll give Working Copy a try and let you know if it can act as a Files provider and/or if it retains files in the Recents section. If it does, even better then! I'll likely switch from Dropbox to Git in that case.

@marcus-crane
Copy link
Author

Just confirming that Working Copy does indeed have a Files provider and that Orgro can access it. It also retains files in the Recent tab so I'll likely just switch to using version control with Git :)

@amake
Copy link
Owner

amake commented May 31, 2020

I have added a new Recent Files screen in the master branch. I will also have a TestFlight build ready soon. Let me know if you're interested in testing it.

@marcus-crane
Copy link
Author

Oooh, I could probably get all of the Flutter stuff set up and give it a try. If your TestFlight build isn't too far off, I'll probably just use that though. I'd be more than happy to look for any bugs and give feedback

@amake
Copy link
Owner

amake commented Jun 2, 2020

TestFlight is ready! I didn’t realize until just now but you can let people join with a link:

https://testflight.apple.com/join/8YTxcuZC

@marcus-crane
Copy link
Author

D5D85AEC-1247-47E2-A872-5F64FB2E6619

Seems to fit exactly what I was hoping for! Really nice job

@amake amake closed this as completed Jun 7, 2020
@amake
Copy link
Owner

amake commented Jun 7, 2020

I just released version 1.2.1 with this feature included.

amake added a commit that referenced this issue Oct 28, 2022
```
══╡ EXCEPTION CAUGHT BY SCHEDULER LIBRARY ╞═════════════════════════════════════════════════════════
The following assertion was thrown during a scheduler callback:
Layer OffsetEngineLayer was previously used as oldLayer.
Once a layer is used as oldLayer, it may not be used again. Instead, after calling one of the
SceneBuilder.push* methods and passing an oldLayer to it, use the layer returned by the method as
oldLayer in subsequent frames.
'dart:ui/compositing.dart':
Failed assertion: line 110 pos 9: '<optimized out>'

Either the assertion indicates an error in the framework itself, or we should provide substantially
more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=2_bug.md

When the exception was thrown, this was the stack:
#2      _EngineLayerWrapper._debugCheckNotUsedAsOldLayer (dart:ui/compositing.dart:110:9)
#3      SceneBuilder.addRetained.<anonymous closure>.recursivelyCheckChildrenUsedOnce (dart:ui/compositing.dart:695:21)
#4      List.forEach (dart:core-patch/growable_array.dart:416:8)
#5      SceneBuilder.addRetained.<anonymous closure>.recursivelyCheckChildrenUsedOnce (dart:ui/compositing.dart:701:18)
#6      SceneBuilder.addRetained.<anonymous closure> (dart:ui/compositing.dart:704:7)
#7      SceneBuilder.addRetained (dart:ui/compositing.dart:707:6)
#8      Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:671:15)
#9      ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1284:13)
#10     OffsetLayer.addToScene (package:flutter/src/rendering/layer.dart:1421:5)
#11     Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:674:5)
#12     ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1284:13)
#13     ClipRectLayer.addToScene (package:flutter/src/rendering/layer.dart:1590:5)
#14     Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:674:5)
#15     ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1284:13)
#16     OffsetLayer.addToScene (package:flutter/src/rendering/layer.dart:1421:5)
#17     Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:674:5)
#18     ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1284:13)
#19     OffsetLayer.addToScene (package:flutter/src/rendering/layer.dart:1421:5)
#20     Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:674:5)
#21     ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1284:13)
#22     OffsetLayer.addToScene (package:flutter/src/rendering/layer.dart:1421:5)
#23     Layer._addToSceneWithRetainedRendering (package:flutter/src/rendering/layer.dart:674:5)
#24     ContainerLayer.addChildrenToScene (package:flutter/src/rendering/layer.dart:1284:13)
#25     TransformLayer.addToScene (package:flutter/src/rendering/layer.dart:1914:5)
#26     ContainerLayer.buildScene (package:flutter/src/rendering/layer.dart:1097:5)
#27     RenderView.compositeFrame (package:flutter/src/rendering/view.dart:231:37)
#28     RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:514:18)
#29     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:869:13)
#30     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:375:5)
#31     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1271:15)
#32     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1200:9)
#33     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1058:5)
#34     _invoke (dart:ui/hooks.dart:145:13)
#35     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:338:5)
#36     _drawFrame (dart:ui/hooks.dart:112:31)
(elided 2 frames from class _AssertionError)
════════════════════════════════════════════════════════════════════════════════════════════════════
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants