Rageshake Service (#70)

This commit is contained in:
ismailgulek
2022-06-06 12:38:07 +03:00
committed by GitHub
parent 1e7243fcf9
commit fa0721b160
57 changed files with 3010 additions and 32 deletions
@@ -0,0 +1,111 @@
//
// UIImage+.swift
// ElementX
//
// Created by Ismail on 20.05.2022.
// Copyright © 2022 element.io. All rights reserved.
//
import Foundation
import Vision
import UIKit
enum ImageAnonymizerError: Error {
case noCgImageBased
}
struct ImageAnonymizer {
private static var allowedTextItems: [String] = [
"#",
"@",
"%",
"&",
"+",
"-",
"_",
"\"",
"?",
"*"
]
static func anonymizedImage(from image: UIImage,
confidenceLevel: Float = 0.5,
fillColor: UIColor = .red) async throws -> UIImage {
guard let cgImage = image.cgImage else {
throw ImageAnonymizerError.noCgImageBased
}
// create a handler with cgImage
let handler = VNImageRequestHandler(cgImage: cgImage, options: [:])
var observations: [VNDetectedObjectObservation] = []
// create a text request
let textRequest = VNRecognizeTextRequest { request, error in
guard let results = request.results as? [VNRecognizedTextObservation],
error == nil else {
return
}
observations.append(contentsOf: results)
}
textRequest.recognitionLevel = .accurate
textRequest.revision = VNRecognizeTextRequestRevision2
// create a face request
let faceRequest = VNDetectFaceRectanglesRequest { request, error in
guard let results = request.results as? [VNFaceObservation],
error == nil else {
return
}
observations.append(contentsOf: results)
}
// revision3 doesn't work!
faceRequest.revision = VNDetectFaceRectanglesRequestRevision2
// perform requests
try handler.perform([
textRequest,
faceRequest
])
return render(image: image,
confidenceLevel: confidenceLevel,
fillColor: fillColor,
observations: observations)
}
private static func render(image: UIImage,
confidenceLevel: Float,
fillColor: UIColor,
observations: [VNDetectedObjectObservation]) -> UIImage {
let size = image.size
let result = UIGraphicsImageRenderer(size: size).image { rendererContext in
// first draw self
image.draw(in: CGRect(origin: .zero, size: size))
// set fill color
fillColor.setFill()
for observation in observations {
guard observation.confidence >= confidenceLevel else {
// ensure observation's confidence level
continue
}
if let textObservation = observation as? VNRecognizedTextObservation,
let text = textObservation.topCandidates(1).first?.string {
if Double(text) != nil || Self.allowedTextItems.contains(text) {
continue
}
}
let box = observation.boundingBox
// boc is normalized (and in starts from the lower left corner)
// convert it to a rect in the image
let rect = CGRect(x: box.minX * size.width,
y: size.height - box.maxY * size.height,
width: box.width * size.width,
height: box.height * size.height)
rendererContext.fill(rect)
}
}
return result
}
}
@@ -136,14 +136,14 @@ private var logger: SwiftyBeaver.Type = {
// MARK: - Private
fileprivate static func configureLogger(_ logger: SwiftyBeaver.Type, withConfiguration configuration: MXLogConfiguration) {
// if let subLogName = configuration.subLogName {
// MXLogger.setSubLogName(subLogName)
// }
//
// MXLogger.redirectNSLog(toFiles: configuration.redirectLogsToFiles,
// numberOfFiles: configuration.maxLogFilesCount,
// sizeLimit: configuration.logFilesSizeLimit)
//
if let subLogName = configuration.subLogName {
MXLogger.setSubLogName(subLogName)
}
MXLogger.redirectNSLog(toFiles: configuration.redirectLogsToFiles,
numberOfFiles: configuration.maxLogFilesCount,
sizeLimit: configuration.logFilesSizeLimit)
guard configuration.logLevel != .none else {
return
}
+114
View File
@@ -0,0 +1,114 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2020 The Matrix.org Foundation C.I.C
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <Foundation/Foundation.h>
/**
The `MXLogger` tool redirects NSLog output into a fixed pool of files.
Another log file is used every time [MXLogger redirectNSLogToFiles:YES]
is called. The pool contains 3 files.
`MXLogger` can track and log uncatched exceptions or crashes.
*/
@interface MXLogger : NSObject
/**
Redirect NSLog output to MXLogger files.
It is advised to condition this redirection in '#if (!isatty(STDERR_FILENO))' block to enable
it only when the device is not attached to the debugger.
@param redirectNSLogToFiles YES to enable the redirection.
*/
+ (void)redirectNSLogToFiles:(BOOL)redirectNSLogToFiles;
/**
Redirect NSLog output to MXLogger files.
It is advised to condition this redirection in '#if (!isatty(STDERR_FILENO))' block to enable
it only when the device is not attached to the debugger.
@param redirectNSLogToFiles YES to enable the redirection.
@param numberOfFiles number of files to keep (default is 10).
*/
+ (void)redirectNSLogToFiles:(BOOL)redirectNSLogToFiles numberOfFiles:(NSUInteger)numberOfFiles;
/**
Redirect NSLog output to MXLogger files.
@param redirectNSLogToFiles YES to enable the redirection.
@param numberOfFiles number of files to keep (default is 10).
@param sizeLimit size limit of log files in bytes. 0 means no limitation, the default value for other methods
*/
+ (void)redirectNSLogToFiles:(BOOL)redirectNSLogToFiles numberOfFiles:(NSUInteger)numberOfFiles sizeLimit:(NSUInteger)sizeLimit;
/**
Delete all log files.
*/
+ (void)deleteLogFiles;
/**
Get the list of all log files.
@return files of
*/
+ (NSArray<NSString*>*)logFiles;
/**
Make `MXLogger` catch and log unmanaged exceptions or application crashes.
When such error happens, `MXLogger` stores the application stack trace into a file
just before the application leaves. The path of this file is provided by [MXLogger crashLog].
@param logCrashes YES to enable the catch.
*/
+ (void)logCrashes:(BOOL)logCrashes;
/**
Set the app build version.
It will be reported in crash report.
*/
+ (void)setBuildVersion:(NSString*)buildVersion;
/**
Set a sub name for namespacing log files.
A sub name must be set when running from an app extension because extensions can
run in parallel to the app.
It must be called before `redirectNSLogToFiles`.
@param subLogName the subname for log files. Files will be named as 'console-[subLogName].log'
Default is nil.
*/
+ (void)setSubLogName:(NSString*)subLogName;
/**
If any, get the file containing the last application crash log.
Only one crash log is stored at a time. The best moment for the app to handle it is the
at its next startup.
@return the crash log file. nil if there is none.
*/
+ (NSString*)crashLog;
/**
Delete the crash log file.
*/
+ (void)deleteCrashLog;
@end
+392
View File
@@ -0,0 +1,392 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2020 The Matrix.org Foundation C.I.C
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "MXLogger.h"
#import <UIKit/UIKit.h>
// stderr so it can be restored
int stderrSave = 0;
static NSString *buildVersion;
static NSString *subLogName;
#define MXLOGGER_CRASH_LOG @"crash.log"
@implementation MXLogger
#pragma mark - NSLog redirection
+ (void)redirectNSLogToFiles:(BOOL)redirectNSLogToFiles
{
[self redirectNSLogToFiles:redirectNSLogToFiles numberOfFiles:10];
}
+ (void)redirectNSLogToFiles:(BOOL)redirectNSLogToFiles numberOfFiles:(NSUInteger)numberOfFiles
{
[self redirectNSLogToFiles:redirectNSLogToFiles numberOfFiles:numberOfFiles sizeLimit:0];
}
+ (void)redirectNSLogToFiles:(BOOL)redirectNSLogToFiles numberOfFiles:(NSUInteger)numberOfFiles sizeLimit:(NSUInteger)sizeLimit
{
if (redirectNSLogToFiles)
{
NSMutableString *log = [NSMutableString string];
// Default subname
if (!subLogName)
{
subLogName = @"";
}
// Set log location
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *logsFolderPath = [MXLogger logsFolderPath];
// Do a circular buffer based on X files
for (NSInteger index = numberOfFiles - 2; index >= 0; index--)
{
NSString *nsLogPathOlder;
NSString *nsLogPathCurrent;
if (index == 0)
{
nsLogPathOlder = [NSString stringWithFormat:@"console%@.1.log", subLogName];
nsLogPathCurrent = [NSString stringWithFormat:@"console%@.log", subLogName];
}
else
{
nsLogPathOlder = [NSString stringWithFormat:@"console%@.%tu.log", subLogName, index + 1];
nsLogPathCurrent = [NSString stringWithFormat:@"console%@.%tu.log", subLogName, index];
}
nsLogPathOlder = [logsFolderPath stringByAppendingPathComponent:nsLogPathOlder];
nsLogPathCurrent = [logsFolderPath stringByAppendingPathComponent:nsLogPathCurrent];
if ([fileManager fileExistsAtPath:nsLogPathCurrent])
{
if ([fileManager fileExistsAtPath:nsLogPathOlder])
{
// Temp log
[log appendFormat:@"[MXLogger] redirectNSLogToFiles: removeItemAtPath: %@\n", nsLogPathOlder];
NSError *error;
[fileManager removeItemAtPath:nsLogPathOlder error:&error];
if (error)
{
[log appendFormat:@"[MXLogger] ERROR: removeItemAtPath: %@. Error: %@\n", nsLogPathOlder, error];
}
}
// Temp log
[log appendFormat:@"[MXLogger] redirectNSLogToFiles: moveItemAtPath: %@ toPath: %@\n", nsLogPathCurrent, nsLogPathOlder];
NSError *error;
[fileManager moveItemAtPath:nsLogPathCurrent toPath:nsLogPathOlder error:&error];
if (error)
{
[log appendFormat:@"[MXLogger] ERROR: moveItemAtPath: %@ toPath: %@. Error: %@\n", nsLogPathCurrent, nsLogPathOlder, error];
}
}
}
// Save stderr so it can be restored.
stderrSave = dup(STDERR_FILENO);
NSString *nsLogPath = [logsFolderPath stringByAppendingPathComponent:[NSString stringWithFormat:@"console%@.log", subLogName]];
freopen([nsLogPath fileSystemRepresentation], "w+", stderr);
// MXLogDebug(@"[MXLogger] redirectNSLogToFiles: YES");
if (log.length)
{
// We can now log into files
// MXLogDebug(@"%@", log);
}
[self removeExtraFilesFromCount:numberOfFiles];
if (sizeLimit > 0)
{
[self removeFilesAfterSizeLimit:sizeLimit];
}
}
else if (stderrSave)
{
// Flush before restoring stderr
fflush(stderr);
// Now restore stderr, so new output goes to console.
dup2(stderrSave, STDERR_FILENO);
close(stderrSave);
}
}
+ (void)deleteLogFiles
{
NSFileManager *fileManager = [NSFileManager defaultManager];
for (NSString *logFile in [self logFiles])
{
[fileManager removeItemAtPath:logFile error:nil];
}
}
+ (NSArray<NSString*>*)logFiles
{
NSMutableArray *logFiles = [NSMutableArray array];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *logsFolderPath = [MXLogger logsFolderPath];
NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:logsFolderPath];
// Find all *.log files
NSString *file = nil;
while ((file = [dirEnum nextObject]))
{
if ([[file lastPathComponent] hasPrefix:@"console"])
{
NSString *logPath = [logsFolderPath stringByAppendingPathComponent:file];
[logFiles addObject:logPath];
}
}
// MXLogDebug(@"[MXLogger] logFiles: %@", logFiles);
return logFiles;
}
#pragma mark - Exceptions and crashes
// Exceptions uncaught by try catch block are handled here
static void handleUncaughtException(NSException *exception)
{
[MXLogger logCrashes:NO];
// Extract running app information
NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary];
NSString* appVersion;
NSString* app, *appId;
app = infoDict[@"CFBundleExecutable"];
appId = infoDict[@"CFBundleIdentifier"];
if ([infoDict objectForKey:@"CFBundleVersion"])
{
appVersion = [NSString stringWithFormat:@"%@ (r%@)", [infoDict objectForKey:@"CFBundleShortVersionString"], [infoDict objectForKey:@"CFBundleVersion"]];
}
else
{
appVersion = [infoDict objectForKey:@"CFBundleShortVersionString"];
}
// Build the crash log
#if TARGET_OS_IPHONE
NSString *model = [[UIDevice currentDevice] model];
NSString *version = [[UIDevice currentDevice] systemVersion];
#elif TARGET_OS_OSX
NSString *model = @"Mac";
NSString *version = [[NSProcessInfo processInfo] operatingSystemVersionString];
#endif
NSArray *backtrace = [exception callStackSymbols];
NSString *description = [NSString stringWithFormat:@"%.0f - %@\n%@\nApplication: %@ (%@)\nApplication version: %@\nMatrix SDK version: %@\nBuild: %@\n%@ %@\n\nMain thread: %@\n%@\n",
[[NSDate date] timeIntervalSince1970],
[NSDate date],
exception.description,
app, appId,
appVersion,
@"",
buildVersion,
model, version,
[NSThread isMainThread] ? @"YES" : @"NO",
backtrace];
// Write to the crash log file
[MXLogger deleteCrashLog];
NSString *crashLog = crashLogPath();
[description writeToFile:crashLog
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:nil];
NSLog(@"[MXLogger] handleUncaughtException:\n%@", description);
// MXLogError(@"[MXLogger] handleUncaughtException:\n%@", description);
}
// Signals emitted by the app are handled here
static void handleSignal(int signalValue)
{
// Throw a custom Objective-C exception
// The Objective-C runtime will then be able to build a readable call stack in handleUncaughtException
[NSException raise:@"Signal detected" format:@"Signal detected: %d", signalValue];
}
+ (void)logCrashes:(BOOL)logCrashes
{
if (logCrashes)
{
// Handle not managed exceptions by ourselves
NSSetUncaughtExceptionHandler(&handleUncaughtException);
// Register signal event (seg fault & cie)
signal(SIGABRT, handleSignal);
signal(SIGILL, handleSignal);
signal(SIGSEGV, handleSignal);
signal(SIGFPE, handleSignal);
signal(SIGBUS, handleSignal);
}
else
{
// Disable crash handling
NSSetUncaughtExceptionHandler(NULL);
signal(SIGABRT, SIG_DFL);
signal(SIGILL, SIG_DFL);
signal(SIGSEGV, SIG_DFL);
signal(SIGFPE, SIG_DFL);
signal(SIGBUS, SIG_DFL);
}
}
+ (void)setBuildVersion:(NSString *)theBuildVersion
{
buildVersion = theBuildVersion;
}
+ (void)setSubLogName:(NSString *)theSubLogName
{
subLogName = [NSString stringWithFormat:@"-%@", theSubLogName];
}
// Return the path of the crash log file
static NSString* crashLogPath(void)
{
return [[MXLogger logsFolderPath] stringByAppendingPathComponent:MXLOGGER_CRASH_LOG];
}
+ (NSString*)crashLog
{
NSString *exceptionLog;
NSString *crashLog = crashLogPath();
NSFileManager *fileManager = [NSFileManager defaultManager];
if([fileManager fileExistsAtPath:crashLog])
{
exceptionLog = crashLog;
}
return exceptionLog;
}
+ (void)deleteCrashLog
{
NSString *crashLog = crashLogPath();
NSFileManager *fileManager = [NSFileManager defaultManager];
if([fileManager fileExistsAtPath:crashLog])
{
[fileManager removeItemAtPath:crashLog error:nil];
}
}
// The folder where logs are stored
+ (NSString*)logsFolderPath
{
NSString *logsFolderPath = nil;
// NSURL *sharedContainerURL = [[NSFileManager defaultManager] applicationGroupContainerURL];
// if (sharedContainerURL)
// {
// logsFolderPath = [sharedContainerURL path];
// }
// else
// {
NSArray<NSURL *> *paths = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
logsFolderPath = paths[0].path;
// }
return logsFolderPath;
}
// If [self redirectNSLogToFiles: numberOfFiles:] is called with a lower numberOfFiles we need to do some cleanup
+ (void)removeExtraFilesFromCount:(NSUInteger)count
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *logsFolderPath = [MXLogger logsFolderPath];
NSUInteger index = count;
do
{
NSString *fileName = [NSString stringWithFormat:@"console%@.%tu.log", subLogName, index];
NSString *logFile = [logsFolderPath stringByAppendingPathComponent:fileName];
if ([fileManager fileExistsAtPath:logFile])
{
[fileManager removeItemAtPath:logFile error:nil];
// MXLogDebug(@"[MXLogger] removeExtraFilesFromCount: %@. removeItemAtPath: %@\n", @(count), logFile);
}
else
{
break;
}
}
while (index++);
}
+ (void)removeFilesAfterSizeLimit:(NSUInteger)sizeLimit
{
NSUInteger logSize = 0;
BOOL removeFiles = NO;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *logsFolderPath = [MXLogger logsFolderPath];
// Start from console.1.log. Do not consider console.log. It should be almost empty
NSUInteger index = 0;
while (++index)
{
NSString *fileName = [NSString stringWithFormat:@"console%@.%tu.log", subLogName, index];
NSString *logFile = [logsFolderPath stringByAppendingPathComponent:fileName];
if ([fileManager fileExistsAtPath:logFile])
{
logSize += [fileManager attributesOfItemAtPath:logFile error:nil].fileSize;
if (logSize >= sizeLimit)
{
removeFiles = YES;
break;
}
}
else
{
break;
}
}
if (removeFiles)
{
// MXLogDebug(@"[MXLogger] removeFilesAfterSizeLimit: Remove files from index %@ because logs are too large (%@ for a limit of %@)\n",
// @(index),
// [NSByteCountFormatter stringFromByteCount:logSize countStyle:NSByteCountFormatterCountStyleBinary],
// [NSByteCountFormatter stringFromByteCount:sizeLimit countStyle:NSByteCountFormatterCountStyleBinary]);
[self removeExtraFilesFromCount:index];
}
else
{
// MXLogDebug(@"[MXLogger] removeFilesAfterSizeLimit: No need: %@ for a limit of %@\n",
// [NSByteCountFormatter stringFromByteCount:logSize countStyle:NSByteCountFormatterCountStyleBinary],
// [NSByteCountFormatter stringFromByteCount:sizeLimit countStyle:NSByteCountFormatterCountStyleBinary]);
}
}
@end
@@ -0,0 +1,23 @@
//
// ElementToggleStyle.swift
// ElementX
//
// Created by Ismail on 2.06.2022.
// Copyright © 2022 Element. All rights reserved.
//
import SwiftUI
/// A toggle style that uses a button, with a checked/unchecked image like a checkbox.
struct ElementToggleStyle: ToggleStyle {
func makeBody(configuration: Configuration) -> some View {
Button { configuration.isOn.toggle() } label: {
Image(systemName: configuration.isOn ? "checkmark.square.fill" : "square")
.font(.title3.weight(.regular))
.imageScale(.large)
.foregroundColor(Color(uiColor: Asset.Colors.elementGreen.color))
}
.buttonStyle(.plain)
}
}
@@ -103,6 +103,9 @@ class RoundedToastView: UIView {
case .success:
imageView.image = UIImage(systemName: "checkmark.circle")
return imageView
case .error:
imageView.image = UIImage(systemName: "x.circle")
return imageView
}
}
}
@@ -20,6 +20,7 @@ struct ToastViewState {
enum Style {
case loading
case success
case error
}
let style: Style
@@ -20,6 +20,7 @@ import UIKit
enum UserIndicatorType {
case loading(label: String, isInteractionBlocking: Bool)
case success(label: String)
case error(label: String)
}
/// A presenter which can handle `UserIndicatorType` by creating the underlying `UserIndicator`
@@ -72,6 +73,8 @@ class UserIndicatorTypePresenter: UserIndicatorTypePresenterProtocol {
}
case .success(let label):
return successRequest(label: label)
case .error(let label):
return errorRequest(label: label)
}
}
@@ -113,4 +116,18 @@ class UserIndicatorTypePresenter: UserIndicatorTypePresenterProtocol {
dismissal: .timeout(1.5)
)
}
private func errorRequest(label: String) -> UserIndicatorRequest {
let presenter = ToastViewPresenter(
viewState: .init(
style: .error,
label: label
),
presentationContext: presentationContext
)
return UserIndicatorRequest(
presenter: presenter,
dismissal: .timeout(1.5)
)
}
}