NullPointerException beim Senden einer Nachricht in einen leeren Chat (AddSeparators) behoben; die previousMessage war null. Download von Dokumenten und Audio-Dateien geht jetzt neben den Bildern Cache der Profilbilder optimiert Beim Klick auf Senden wird die Warteanimation angezeigt
559 lines
18 KiB
C#
559 lines
18 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text.RegularExpressions;
|
|
using System.Windows;
|
|
using System.Windows.Documents;
|
|
using System.Windows.Interop;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Imaging;
|
|
using ChatController.Core;
|
|
using RestSharp;
|
|
using PixelFormat = System.Drawing.Imaging.PixelFormat;
|
|
|
|
namespace ChatController.Utilities
|
|
{
|
|
public class Utils
|
|
{
|
|
public static string AuthToken { get; set; }
|
|
|
|
public static string Tenant { get; set; }
|
|
|
|
public static DateTime DefaultDate = new DateTime(1, 1, 1);
|
|
|
|
public static void DownloadUserProfilePicturesSync(Dictionary<long, string> userIds2Uris)
|
|
{
|
|
foreach(var userId2Uri in userIds2Uris)
|
|
{
|
|
DownloadImageSync(userId2Uri.Value, userId2Uri.Key, null);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lädt ein Bild aus einer Nachricht herunter.
|
|
/// </summary>
|
|
/// <param name="uri">Uri zum Bild</param>
|
|
/// <param name="callback">Callback-Methode, die nach dem Herunterladen des Bildes aufgerufen wird und das Bild zurückgibt. Fügt das Bild der GUI hinzu.</param>
|
|
private static void DownloadImage(string uri, Action<ImageSource> callback)
|
|
{
|
|
#if DEBUG
|
|
Debug.WriteLine($"####{uri} wird heruntergeladen (Utils.DownloadImage) ...");
|
|
#endif
|
|
|
|
var client = new RestClient(uri);
|
|
var request = new RestRequest
|
|
{
|
|
ResponseWriter = stream =>
|
|
{
|
|
var rotation = Rotation.Rotate0;
|
|
|
|
using(var image = Image.FromStream(stream))
|
|
{
|
|
if(image.PropertyIdList.Contains(0x112))
|
|
{
|
|
rotation = GetRotationFromExifTag(image.GetPropertyItem(0x112).Value[0]);
|
|
}
|
|
|
|
var bitmap = new BitmapImage();
|
|
|
|
var localStream = new MemoryStream();
|
|
|
|
image.Save(localStream, image.RawFormat);
|
|
|
|
localStream.Position = 0;
|
|
|
|
bitmap.BeginInit();
|
|
bitmap.StreamSource = localStream;
|
|
bitmap.Rotation = rotation;
|
|
bitmap.EndInit();
|
|
bitmap.Freeze();
|
|
|
|
callback?.Invoke(bitmap);
|
|
}
|
|
}
|
|
};
|
|
|
|
request.AddHeader(Constants.Token, AuthToken);
|
|
request.AddHeader(Constants.CustomerId, Tenant);
|
|
|
|
client.ExecuteAsync(request, null);
|
|
}
|
|
|
|
private static Rotation GetRotationFromExifTag(int value)
|
|
{
|
|
switch(value)
|
|
{
|
|
case 6:
|
|
return Rotation.Rotate90;
|
|
case 8:
|
|
return Rotation.Rotate270;
|
|
case 3:
|
|
return Rotation.Rotate180;
|
|
default:
|
|
return Rotation.Rotate0;
|
|
}
|
|
}
|
|
|
|
public static void DownloadImageAsync(string uri, string key, CacheCategory cacheCategory, Action<ImageSource> callback)
|
|
{
|
|
if(string.IsNullOrEmpty(uri))
|
|
{
|
|
callback?.Invoke(null);
|
|
return;
|
|
}
|
|
|
|
var cache = OwnChatCache.GetInstance();
|
|
|
|
var cachedImage = cache.GetImageSourceFromCache(key, uri, cacheCategory);
|
|
|
|
if(!(cachedImage is null))
|
|
{
|
|
callback?.Invoke(cachedImage);
|
|
return;
|
|
}
|
|
|
|
DownloadImage(uri, imageSource =>
|
|
{
|
|
cache.AddImageToCache(key, imageSource, cacheCategory, uri);
|
|
callback?.Invoke(imageSource);
|
|
});
|
|
}
|
|
|
|
public static void DownloadDocumentThumbnail(string uri, Action<ImageSource> callback)
|
|
{
|
|
if(string.IsNullOrEmpty(uri))
|
|
{
|
|
callback?.Invoke(null);
|
|
return;
|
|
}
|
|
|
|
var cache = OwnChatCache.GetInstance();
|
|
|
|
if(!(cache.DocumentThumbnail is null))
|
|
{
|
|
callback?.Invoke(cache.DocumentThumbnail);
|
|
return;
|
|
}
|
|
|
|
DownloadImage(uri, imageSource =>
|
|
{
|
|
cache.DocumentThumbnail = imageSource;
|
|
callback?.Invoke(imageSource);
|
|
});
|
|
}
|
|
|
|
public static ImageSource GetPicturePlaceholder()
|
|
{
|
|
var cache = OwnChatCache.GetInstance();
|
|
|
|
if(cache.ImagePlaceholder is null)
|
|
{
|
|
var resultBitmapImage = new BitmapImage();
|
|
|
|
resultBitmapImage.BeginInit();
|
|
resultBitmapImage.UriSource = new Uri(Constants.ImagePlaceholderPath);
|
|
resultBitmapImage.EndInit();
|
|
resultBitmapImage.Freeze();
|
|
|
|
cache.ImagePlaceholder = resultBitmapImage;
|
|
}
|
|
|
|
return cache.ImagePlaceholder;
|
|
}
|
|
|
|
public static string ReadStream(WebResponse response)
|
|
{
|
|
var dataStream = response.GetResponseStream();
|
|
|
|
if(dataStream is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var reader = new StreamReader(dataStream);
|
|
var responseFromServer = reader.ReadToEnd();
|
|
|
|
dataStream.Dispose();
|
|
reader.Dispose();
|
|
|
|
dataStream.Close();
|
|
reader.Close();
|
|
|
|
return responseFromServer;
|
|
}
|
|
|
|
public static string GetAndCreateUserAppDataPath()
|
|
{
|
|
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
|
var companyFilePath = Path.Combine(localAppData, Resource.CompanyName);
|
|
|
|
if(!Directory.Exists(companyFilePath))
|
|
{
|
|
Directory.CreateDirectory(companyFilePath);
|
|
}
|
|
|
|
var bewoFilePath = Path.Combine(companyFilePath, Resource.ApplicationFolderName);
|
|
|
|
if(!Directory.Exists(bewoFilePath))
|
|
{
|
|
Directory.CreateDirectory(bewoFilePath);
|
|
}
|
|
|
|
return bewoFilePath;
|
|
}
|
|
|
|
public static string GenerateTempName()
|
|
{
|
|
const int length = 6;
|
|
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
var random = new Random();
|
|
|
|
var tempName = new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
|
|
|
|
return tempName;
|
|
}
|
|
|
|
public static byte[] ImageToByteArray(Image pImage)
|
|
{
|
|
using(var memoryStream = new MemoryStream())
|
|
{
|
|
pImage.Save(memoryStream, ImageFormat.Bmp);
|
|
return memoryStream.ToArray();
|
|
}
|
|
}
|
|
|
|
public static ImageSource ConvertIconToImageSource(Icon pIcon)
|
|
{
|
|
var bitmap = new Bitmap(pIcon.Width, pIcon.Height);
|
|
|
|
var graphics = Graphics.FromImage(bitmap);
|
|
|
|
graphics.DrawIcon(pIcon, 0, 0);
|
|
|
|
graphics.Dispose();
|
|
|
|
bitmap.Save("icon.ico", ImageFormat.Icon);
|
|
|
|
var imageSource = ImageSourceForBitmap(bitmap);
|
|
|
|
bitmap.Dispose();
|
|
|
|
return imageSource;
|
|
}
|
|
|
|
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
public static extern bool DeleteObject([In] IntPtr hObject);
|
|
|
|
public static ImageSource ImageSourceForBitmap(Bitmap bmp)
|
|
{
|
|
var handle = bmp.GetHbitmap();
|
|
|
|
try
|
|
{
|
|
return Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
|
|
}
|
|
finally
|
|
{
|
|
DeleteObject(handle);
|
|
}
|
|
}
|
|
|
|
public static Icon ImageSourceToIcon(ImageSource pImageSource)
|
|
{
|
|
var bitmapSource = (BitmapSource)pImageSource;
|
|
|
|
var width = bitmapSource.PixelWidth;
|
|
var height = bitmapSource.PixelHeight;
|
|
|
|
var newWidth = width;
|
|
var newHeight = height;
|
|
|
|
var x = 0;
|
|
var y = 0;
|
|
|
|
if(width < height)
|
|
{
|
|
newHeight = width;
|
|
y = (height - width) / 2;
|
|
}
|
|
else
|
|
{
|
|
newWidth = height;
|
|
x = (width - height) / 2;
|
|
}
|
|
|
|
var stride = width * ((bitmapSource.Format.BitsPerPixel + 7) / 8);
|
|
var memoryBlockPointer = Marshal.AllocHGlobal(height * stride);
|
|
|
|
bitmapSource.CopyPixels(new Int32Rect(x, y, newWidth, newHeight), memoryBlockPointer, newHeight * stride, stride);
|
|
|
|
var bitmap = new Bitmap(newWidth, newHeight, stride, PixelFormat.Format32bppPArgb, memoryBlockPointer);
|
|
|
|
return Icon.FromHandle(bitmap.GetHicon());
|
|
}
|
|
|
|
public static byte[] ReadFully(Stream pStream)
|
|
{
|
|
using(var memStream = new MemoryStream())
|
|
{
|
|
pStream.CopyTo(memStream);
|
|
return memStream.ToArray();
|
|
}
|
|
}
|
|
|
|
public static RotateFlipType OrientationToFlipType(string orientation)
|
|
{
|
|
switch(int.Parse(orientation))
|
|
{
|
|
case 1:
|
|
return RotateFlipType.RotateNoneFlipNone;
|
|
case 2:
|
|
return RotateFlipType.RotateNoneFlipX;
|
|
case 3:
|
|
return RotateFlipType.Rotate180FlipNone;
|
|
case 4:
|
|
return RotateFlipType.Rotate180FlipX;
|
|
case 5:
|
|
return RotateFlipType.Rotate90FlipX;
|
|
case 6:
|
|
return RotateFlipType.Rotate90FlipNone;
|
|
case 7:
|
|
return RotateFlipType.Rotate270FlipX;
|
|
case 8:
|
|
return RotateFlipType.Rotate270FlipNone;
|
|
default:
|
|
return RotateFlipType.RotateNoneFlipNone;
|
|
}
|
|
}
|
|
|
|
public static List<T> GetVisualChildCollection<T>(object parent) where T : Visual
|
|
{
|
|
var visualCollection = new List<T>();
|
|
GetVisualChildCollection(parent as DependencyObject, visualCollection);
|
|
|
|
return visualCollection;
|
|
}
|
|
|
|
public static void GetVisualChildCollection<T>(DependencyObject parent, ICollection<T> visualCollection) where T : Visual
|
|
{
|
|
var count = VisualTreeHelper.GetChildrenCount(parent);
|
|
for(var i = 0; i < count; i++)
|
|
{
|
|
var child = VisualTreeHelper.GetChild(parent, i);
|
|
|
|
if(child is T item)
|
|
{
|
|
visualCollection.Add(item);
|
|
}
|
|
else
|
|
{
|
|
GetVisualChildCollection(child, visualCollection);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static int GetHeightFromThumbnailUri(string uri)
|
|
{
|
|
/*
|
|
https://test.ownchat.de/document.png
|
|
https://test.ownchat.de/profile/view-image/{customerid}/{width}/{height}/{filename}
|
|
https://test.ownchat.de/message/view-image/{customerid}/{group}/{height}/{filename}
|
|
*/
|
|
|
|
if(uri?.Contains("/") ?? false)
|
|
{
|
|
var splitUri = uri.Split('/');
|
|
|
|
if(splitUri.Length >= 2)
|
|
{
|
|
var heightStr = splitUri[splitUri.Length - 2];
|
|
|
|
if(int.TryParse(heightStr, out var height))
|
|
{
|
|
return height;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 100;
|
|
}
|
|
|
|
private static readonly Regex _UrlRegex = new Regex(@"(?#Protocol)(http(?:s?)\:(\/\/|\\\\)|(w){3}(2|3)?\.{1})(?#Subdomains)(?:(?:[-\w]+\.)+(?#TopLevel Domains)(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))(?#Port)(?::[\d]{1,5})?(?#Directories)(?:(?:(?:/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|/)+|\?|#)?(?#Query)(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?#Anchor)(?:#(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
public static ObservableCollection<Inline> ConvertLinksToHyperlinks(string messageText)
|
|
{
|
|
var result = new ObservableCollection<Inline>();
|
|
|
|
if(messageText is null)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
var guidString = Guid.NewGuid().ToString();
|
|
var replacementString = Guid.NewGuid().ToString();
|
|
|
|
var matches = _UrlRegex.Matches(messageText);
|
|
|
|
var index2Link = new Dictionary<int, Hyperlink>();
|
|
|
|
for(var i = 0; i < matches.Count; i++)
|
|
{
|
|
var link = matches[i].Value;
|
|
|
|
if(!link.Contains("http"))
|
|
{
|
|
link = $"http://{link}";
|
|
}
|
|
|
|
var hyperlink = new Hyperlink(new Run(link))
|
|
{
|
|
NavigateUri = new Uri(link, UriKind.Absolute)
|
|
};
|
|
|
|
hyperlink.RequestNavigate += (s, e) =>
|
|
{
|
|
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
|
|
e.Handled = true;
|
|
};
|
|
|
|
index2Link[i] = hyperlink;
|
|
}
|
|
|
|
var splitMessage = _UrlRegex.Replace(messageText, replacementString + guidString).Split(new[] { guidString }, StringSplitOptions.None);
|
|
|
|
var linkIndex = 0;
|
|
foreach(var text in splitMessage)
|
|
{
|
|
if(text.Equals(replacementString))
|
|
{
|
|
result.Add(index2Link[linkIndex]);
|
|
linkIndex++;
|
|
}
|
|
else if(text.StartsWith(replacementString) || text.EndsWith(replacementString))
|
|
{
|
|
var splitLine = text.Split(new[] { replacementString }, StringSplitOptions.None);
|
|
|
|
foreach(var partLine in splitLine)
|
|
{
|
|
if(string.Empty.Equals(partLine))
|
|
{
|
|
result.Add(index2Link[linkIndex]);
|
|
linkIndex++;
|
|
}
|
|
else
|
|
{
|
|
result.Add(new Run(partLine));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.Add(new Run(text));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public static string GetUserIdFromProfilePictureUri(string uri)
|
|
{
|
|
#if DEBUG
|
|
if(string.IsNullOrWhiteSpace(uri))
|
|
{
|
|
return uri;
|
|
}
|
|
|
|
string userId = null;
|
|
var splitUri = uri.Split('/');
|
|
if(splitUri.Length <= 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var lastIndex = splitUri.Length - 1;
|
|
var fileName = splitUri[lastIndex];
|
|
var splitFileName = fileName.Split('_');
|
|
|
|
if(splitFileName.Length > 0)
|
|
{
|
|
userId = long.TryParse(splitFileName[0], out var unused) ? splitFileName[0] : fileName;
|
|
}
|
|
|
|
return userId;
|
|
#endif
|
|
}
|
|
|
|
public static ImageSource DownloadImageSync(string uri, long? userId, int? groupId)
|
|
{
|
|
var userOrGroup = "FEHLER: USER-ID UND GROUP-ID SIND NULL!";
|
|
if(groupId.HasValue && userId is null)
|
|
{
|
|
userOrGroup = $"GroupId {groupId}";
|
|
}
|
|
else if(groupId is null && userId.HasValue)
|
|
{
|
|
userOrGroup = $"UserId {userId}";
|
|
}
|
|
|
|
var cache = OwnChatCache.GetInstance();
|
|
|
|
if(cache.IsAvatarInCache(uri, userId, groupId))
|
|
{
|
|
return cache.GetAvatar(uri, userId, groupId);
|
|
}
|
|
|
|
Debug.WriteLine($"####Lade {uri} für {userOrGroup} herunter ...");
|
|
|
|
var request = WebRequest.Create(uri);
|
|
request.Credentials = CredentialCache.DefaultCredentials;
|
|
request.Headers[Constants.Token] = AuthToken;
|
|
request.Headers[Constants.CustomerId] = Tenant;
|
|
|
|
using(var response = request.GetResponse())
|
|
{
|
|
var responseStream = response?.GetResponseStream();
|
|
|
|
if(responseStream is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var rotation = Rotation.Rotate0;
|
|
|
|
using(var image = Image.FromStream(responseStream))
|
|
{
|
|
if(image.PropertyIdList.Contains(0x112))
|
|
{
|
|
rotation = GetRotationFromExifTag(image.GetPropertyItem(0x112).Value[0]);
|
|
}
|
|
|
|
var bitmap = new BitmapImage();
|
|
|
|
var localStream = new MemoryStream();
|
|
|
|
image.Save(localStream, image.RawFormat);
|
|
|
|
localStream.Position = 0;
|
|
|
|
bitmap.BeginInit();
|
|
bitmap.StreamSource = localStream;
|
|
bitmap.Rotation = rotation;
|
|
bitmap.EndInit();
|
|
bitmap.Freeze();
|
|
|
|
cache.AddAvatar(uri, bitmap, userId, groupId);
|
|
return cache.GetAvatar(uri, userId, groupId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|