Files
RothenburgAR/Assets/RothenburgAR/Scripts/Updater/UpdaterBehaviour.cs

281 lines
10 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using System;
using Newtonsoft.Json;
using RothenburgAR.Common;
using System.IO;
namespace RothenburgAR.Updater
{
public class UpdaterBehaviour : MonoBehaviour
{
public UnityEngine.UI.Slider ProgressBar;
public VersioncheckAnswer VersionAnswer { get; set; }
private List<HttpHandler> httpHandlers = new List<HttpHandler>();
void Start()
{
/* TODO: allg. fragen
punkt zum eintragen der credentials? user will ja nur seine/nur bestimmte exhibitions haben schätz ich
oder beim update auswählen lassen, welche kommen sollen
*/
FindObjectsOfType<Camera>().First().enabled = true;
if (Application.internetReachability == NetworkReachability.NotReachable)
{
// just continue to app
LoadMainScene();
return;
}
CheckForUpdates();
}
private void CheckForUpdates()
{
// check for updates and ask for permission to download if there are any
string versionMap = GenerateVersionMap();
HttpHandler http = new HttpRequest(ApiInfo.VersionCheckEndpoint, HttpVerb.POST, versionMap).send();
httpHandlers.Add(http);
http.operation.completed += ar =>
{
if (CheckNetworkErrors(http))
{
return;
}
Debug.Log(http.download.text);
//VersionAnswer = JsonConvert.DeserializeObject<VersioncheckAnswer>(http.download.text);
VersionAnswer = JsonConvert.DeserializeObject<VersioncheckAnswer>(@"{""languages"":[""de"",""en""],""data"":[{""id"":""006e164c-5e31-4ddf-adf5-df7016c8b3a8"",""meta"":{""status"":""ok"",""updateUrl"":""https://lambdalike.pa.kaim.network/meta/006e164c-5e31-4ddf-adf5-df7016c8b3a7/{lang}""},""tracker"":{""status"":""ok"",""updateUrl"":""https://lambdalike.pa.kaim.network/meta/006e164c-5e31-4ddf-adf5-df7016c8b3a7/{lang}""}},{""id"":""006e164c-5e31-4ddf-adf5-df7016c8b3a7"",""meta"":{""status"":""updated"",""updateUrl"":""https://lambdalike.pa.kaim.network/meta/006e164c-5e31-4ddf-adf5-df7016c8b3a7/{lang}""},""tracker"":{""status"":""ok"",""updateUrl"":""https://lambdalike.pa.kaim.network/meta/006e164c-5e31-4ddf-adf5-df7016c8b3a7/{lang}""}}]}");
if (VersionAnswer.data.TrueForAll(d => d.Meta.Status == VersionStatus.ok && d.Tracker.Status == VersionStatus.ok))
{
// no updates required, continue to app
LoadMainScene();
return;
}
if (Application.internetReachability == NetworkReachability.ReachableViaLocalAreaNetwork)
{
TriggerUpdate();
}
else
{
// TODO ask for user permission to perform update
}
};
}
private string GenerateVersionMap()
{
string result = "{";
var rootDir = new DirectoryInfo(PathHelper.ExhibitionPath);
var exhibitionDirs = rootDir.GetDirectories().ToList();
exhibitionDirs.ForEach(dir =>
{
var subdir = dir.GetDirectories().ToList().First();
if (subdir == null) return;
var metaFilePath = Path.Combine(subdir.FullName, "meta.json");
if (!File.Exists(metaFilePath)) return;
var exhibits = JsonConvert.DeserializeObject<List<Exhibit>>(File.ReadAllText(metaFilePath));
foreach (var exhibit in exhibits)
{
result += @"""{id}"":""{version}"",".Replace("{id}", exhibit.Id).Replace("{version}", exhibit.UpdatedTime.ToString());
}
});
result = result.Substring(0, result.Length - 1);
result += "}";
return result;
}
private void TriggerUpdate()
{
// TODO create centralized network error feedback hub thing
/*
* dir structure:
* data
* exhibitions
* id
* tracker.dat
* tracker.xml
* de
* meta.json
* en
* meta.json
* id
* ...
* poi
* ...
* media
* ...
*/
//TODO write languages to file the app can read
var updatedMeta = VersionAnswer.data.Where(d => d.Meta.Status == VersionStatus.updated).ToList();
var updatedTracker = VersionAnswer.data.Where(d => d.Tracker.Status == VersionStatus.updated).ToList();
updatedMeta.Union(updatedTracker).ToList().ForEach(updatedExhibition =>
{
// create exhibition directories
var path = Path.Combine(PathHelper.ExhibitionPath, updatedExhibition.Id);
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
});
updatedMeta.ForEach(d => UpdateMeta(d));
updatedTracker.ForEach(d => UpdateTracker(d));
var deletedMeta = VersionAnswer.data.Where(d => d.Meta.Status == VersionStatus.deleted).ToList();
var deletedTracker = VersionAnswer.data.Where(d => d.Tracker.Status == VersionStatus.deleted).ToList();
//TODO figure out if you can delete a tracker but not the meta (or vice versa)
}
private void UpdateMeta(ExhibitionVersion exhibition)
{
foreach (var lang in VersionAnswer.languages)
{
var path = PathHelper.CombinePaths(PathHelper.ExhibitionPath, exhibition.Id, lang);
var url = exhibition.Meta.UpdateUrl.Replace("{lang}", lang);
var http = new HttpRequest(url, HttpVerb.GET).send();
httpHandlers.Add(http);
http.operation.completed += ar =>
{
if (CheckNetworkErrors(http))
{
return;
}
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
File.WriteAllText(Path.Combine(path, "meta.json"), http.download.text, System.Text.Encoding.UTF8);
var exhibits = JsonConvert.DeserializeObject<List<Exhibit>>(http.download.text);
foreach (var exhibit in exhibits)
{
UpdateMedia(exhibit);
}
};
}
}
private void UpdateMedia(Exhibit exhibit)
{
//TODO implement feedback: which id is the right one?
// ImageId is apparently just the ExhibitId
// Exhibit doesn't have media, POI does. so the Exhibit.MediaId doesn't make sense
//exhibit.MediaId;
//exhibit.Pois[0].ImageId;
//TODO check existing file for creation/alteration date to see if download is necessary
var mediaIDs = new List<string> { exhibit.MediaId };
//var mediaIDs = exhibit.Pois.Select(p => p.ImageId).ToList();
foreach (var mediaId in mediaIDs)
{
var path = PathHelper.CombinePaths(PathHelper.MediaPath, mediaId);
var url = ApiInfo.FileEndpoint.Replace("{id}", mediaId);
var http = new HttpRequest(url, HttpVerb.GET).send();
httpHandlers.Add(http);
http.operation.completed += ar =>
{
if (CheckNetworkErrors(http))
{
return;
}
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
//TODO find out media type
var type = http.request.GetResponseHeader("Content-Type");
File.WriteAllBytes(Path.Combine(path, "media.dat"), http.download.data);
};
}
}
private bool CheckNetworkErrors(HttpHandler http)
{
if (http.request.isNetworkError || http.request.isHttpError)
{
//TODO notify user about error (decide on level of detail)
Debug.LogError(String.Format("Error while downloading\nurl: {0}\nNetwork Error: {1}\nHttp Error: {2}\nHttp Response Code: {3}",
http.request.url,
http.request.isNetworkError,
http.request.isHttpError,
http.request.responseCode));
return true;
}
return false;
}
private void UpdateTracker(ExhibitionVersion exhibition)
{
throw new NotImplementedException();
}
private static void LoadMainScene()
{
UnityEngine.SceneManagement.SceneManager.LoadScene("mainScene");
}
private DateTime downloadEndedTime = DateTime.Now.AddSeconds(1);
private float afterDownloadWaitTime = 3f;
void Update()
{
if (httpHandlers.Count > 0)
{
float downloadProgress = 0f;
httpHandlers.ForEach(h =>
{
downloadProgress += h.operation.progress;
});
downloadProgress /= httpHandlers.Count;
ProgressBar.value = downloadProgress;
}
// Continue to Main Scene after all downloads are done and 3 seconds have passed without any new downloads triggering.
if (httpHandlers.All(h => h.IsDone))
{
if (downloadEndedTime > DateTime.Now)
{
downloadEndedTime = DateTime.Now;
}
if (downloadEndedTime.AddSeconds(afterDownloadWaitTime) < DateTime.Now)
{
Debug.Log("Done Updating");
LoadMainScene();
}
}
else
{
downloadEndedTime = DateTime.Now.AddSeconds(1);
}
}
}
}