diff --git a/Assets/Editor/Tests/TestDownloadingCache.cs b/Assets/Editor/Tests/TestDownloadingCache.cs
new file mode 100644
index 0000000000..196ccf4f91
--- /dev/null
+++ b/Assets/Editor/Tests/TestDownloadingCache.cs
@@ -0,0 +1,76 @@
+using System.IO;
+using System.Linq;
+using Assets.Oculus.VR.Editor;
+using NUnit.Framework;
+namespace TiltBrush
+{
+ [TestFixture]
+ public class TestDownloadingCache
+ {
+ private DownloadingCache m_dlCache;
+ private FileCache m_Cache;
+ private string m_Path;
+
+ private const string kLocalFile = "file://TestData/main_1.png";
+ private const string kRemoteFile = "http://openbrush.app/assets/icon.png";
+
+ [SetUp]
+ public void Setup()
+ {
+ m_Path = Path.Combine(Path.GetTempPath(), "FileCacheTest");
+ if (File.Exists(m_Path))
+ {
+ File.Delete(m_Path);
+ }
+ if (Directory.Exists(m_Path))
+ {
+ Directory.Delete(m_Path, recursive: true);
+ }
+ m_Cache = new FileCache(m_Path, 1);
+ m_dlCache = new DownloadingCache(m_Cache);
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ if (Directory.Exists(m_Path))
+ {
+ Directory.Delete(m_Path, recursive: true);
+ }
+ }
+
+ [Test]
+ public async void LocalFileLoads()
+ {
+ var bytes = await m_dlCache.Read("test", "logo1", kLocalFile);
+ Assert.That(bytes != null);
+ Assert.That(bytes.Length == 32983);
+ }
+
+ [Test]
+ public async void RemoteFileLoads()
+ {
+ var bytes = await m_dlCache.Read("test", "logo1", kRemoteFile);
+ Assert.That(bytes != null);
+ Assert.That(bytes.Length == 32983);
+ }
+
+ [Test]
+ public async void RemoteFileIsStoredInCache()
+ {
+ var bytes = await m_dlCache.Read("test", "logo1", kRemoteFile);
+ Assert.That(m_Cache.CacheSize == 32983);
+ }
+
+ [Test]
+ public async void RemoteFileCanBeLoadedFromCache()
+ {
+ var bytes = await m_dlCache.Read("test", "logo1", kRemoteFile);
+ var bytes2 = m_Cache.Read("test", "logo1");
+ Assert.That(Enumerable.SequenceEqual(bytes, bytes2));
+ var bytes3 = await m_dlCache.Read("test", "logo1", kRemoteFile);
+ Assert.That(Enumerable.SequenceEqual(bytes, bytes3));
+ }
+
+ }
+}
diff --git a/Assets/Editor/Tests/TestDownloadingCache.cs.meta b/Assets/Editor/Tests/TestDownloadingCache.cs.meta
new file mode 100644
index 0000000000..63b74e0f24
--- /dev/null
+++ b/Assets/Editor/Tests/TestDownloadingCache.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: a804d95b31bd499e9e08354bc616fc98
+timeCreated: 1675906363
\ No newline at end of file
diff --git a/Assets/Scripts/App.cs b/Assets/Scripts/App.cs
index b7eab2a4cf..f2cfe7ba64 100644
--- a/Assets/Scripts/App.cs
+++ b/Assets/Scripts/App.cs
@@ -18,6 +18,7 @@
using System.IO;
using System.Linq;
using System.Net;
+using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using UnityEngine;
@@ -75,6 +76,8 @@ public partial class App : MonoBehaviour
"All your " + kAppDisplayName + " files have been moved to\n" +
"/sdcard/" + kAppFolderName + ".\n";
+ public static HttpClient HttpClient = new HttpClient();
+
public enum AppState
{
Error,
diff --git a/Assets/Scripts/Sharing/DownloadingCache.cs b/Assets/Scripts/Sharing/DownloadingCache.cs
new file mode 100644
index 0000000000..be00365a41
--- /dev/null
+++ b/Assets/Scripts/Sharing/DownloadingCache.cs
@@ -0,0 +1,56 @@
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace TiltBrush
+{
+ ///
+ /// Cache where remote or local files can be requested, and remote files will be cached.
+ /// Local files will just be read directly.
+ ///
+ public class DownloadingCache
+ {
+ ///
+ /// Constructor
+ ///
+ /// File cache
+ public DownloadingCache(FileCache cache)
+ {
+ m_Cache = cache;
+ }
+
+ // TODO: I think this needs to store metadata about where the file came from.
+ // and do something about files that have changed etc
+
+ ///
+ /// Read a file from a location. If the file does not exist, it will be cached
+ /// at the given fileset and filename.
+ ///
+ /// Fileset to store/retrieve
+ /// Filename
+ /// Location to load - should be http(s):// or file://
+ /// Task that returns the bytes for a file.
+ public async Task Read(string fileset, string filename, string url)
+ {
+ const string fileStart = "file://";
+ const string httpStart = "http";
+ if (m_Cache.FileExists(fileset, filename))
+ {
+ return m_Cache.Read(fileset, filename);
+ }
+ if (url.StartsWith(fileStart))
+ {
+ return File.ReadAllBytes(url.Skip(fileStart.Length).ToString());
+ }
+ else if (url.StartsWith(httpStart))
+ {
+ byte[] bytes = await App.HttpClient.GetByteArrayAsync(url);
+ m_Cache.Write(fileset, filename, bytes);
+ return bytes;
+ }
+ return null;
+ }
+
+ private FileCache m_Cache;
+ }
+}
diff --git a/Assets/Scripts/Sharing/DownloadingCache.cs.meta b/Assets/Scripts/Sharing/DownloadingCache.cs.meta
new file mode 100644
index 0000000000..65d7ea987e
--- /dev/null
+++ b/Assets/Scripts/Sharing/DownloadingCache.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: e57a79bd52ee446cbfa9d68ed33124d1
+timeCreated: 1675749698
\ No newline at end of file