Skip to content

Commit

Permalink
Updated to v1.0.3
Browse files Browse the repository at this point in the history
Added separate directory for downloadable sounds
Added grouping for the sound list controller in Tranquil's settings for better organization and readability
Improved RTL language support in Tranquil's preferences
Minor refactoring for readability
Known Issue, pre iOS 13 does not allow opening the preference app directly to a control center module, tranquil will instead open to the Customize Controls section requiring the user to navigate to Tranquil settings from there
  • Loading branch information
CreatureSurvive committed Apr 1, 2022
1 parent e569dae commit 0814310
Show file tree
Hide file tree
Showing 14 changed files with 137 additions and 65 deletions.
76 changes: 32 additions & 44 deletions Classes/Prefix.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,23 @@
// return negative value if condition is true
#define NEGATE_IF(x, c) (c == true ? -x : x)

#define TranquilBundleIdentifier @"com.creaturecoding.tranquil"
#define TranquilPreferencesChanged "com.creaturecoding.tranquil/preferences-changed"
#define TranquilPreferencesChangedExternal "com.creaturecoding.tranquil/preferences-changed-externally"

#define TranquilBundlePath @"/Library/ControlCenter/Bundles/Tranquil.bundle"
#define TranquilSupportPath @"/var/mobile/Library/Application Support/Tranquil/"
#define TranquilBundledAudioPath @"/Library/ControlCenter/Bundles/Tranquil.bundle/Audio"
#define TranquilImportedAudioPath @"/var/mobile/Library/Application Support/Tranquil/Audio/"
#define TranquilDownloadableAudioPath @"/var/mobile/Library/Application Support/Tranquil/Downloadable/"

NS_INLINE __unused NSBundle *ModuleBundle(BOOL loadIfNeeded)
{
static NSBundle *moduleBundle;

if (!moduleBundle) {

moduleBundle = [NSBundle bundleWithPath:@"/Library/ControlCenter/Bundles/Tranquil.bundle"];
moduleBundle = [NSBundle bundleWithPath:TranquilBundlePath];
}

if (loadIfNeeded && ![moduleBundle isLoaded]) {
Expand All @@ -54,7 +64,7 @@ NS_INLINE __unused NSDictionary *Defaults(void)
@"kPlaybackVolume" : @0.6,
@"kPlaybackVolumeWithMedia" : @0.2,
@"kUseWhenMediaIsPlaying" : @YES,
@"kActiveSound" : [ModuleBundle(NO).bundlePath stringByAppendingPathComponent:@"Audio/BROWN_NOISE.m4a"]
@"kActiveSound" : [TranquilBundledAudioPath stringByAppendingPathComponent:@"BROWN_NOISE.m4a"]
};
}

Expand Down Expand Up @@ -151,28 +161,16 @@ NS_INLINE __unused NSArray<NSDictionary *> *DownloadableAudioMetadata(void)

if (!downloadableAudioMetadata) {

// TODO migrate downloadable content to separate directory for improved organization
downloadableAudioMetadata = @[
@{
@"name" : @"INFRA_NOISE",
@"path" : @"/var/mobile/Library/Application Support/Tranquil/Audio/INFRA_NOISE.m4a"
}, @{
@"name" : @"ULTRA_NOISE",
@"path" : @"/var/mobile/Library/Application Support/Tranquil/Audio/ULTRA_NOISE.m4a"
}, @{
@"name" : @"FLOWING_STREAM",
@"path" : @"/var/mobile/Library/Application Support/Tranquil/Audio/FLOWING_STREAM.m4a"
}, @{
@"name" : @"LIGHT_RAIN",
@"path" : @"/var/mobile/Library/Application Support/Tranquil/Audio/LIGHT_RAIN.m4a"
}, @{
@"name" : @"OCEAN_WAVES",
@"path" : @"/var/mobile/Library/Application Support/Tranquil/Audio/OCEAN_WAVES.m4a"
}, @{
@"name" : @"THUNDER_STORM",
@"path" : @"/var/mobile/Library/Application Support/Tranquil/Audio/THUNDER_STORM.m4a"
}
];
NSMutableArray *metadata = [NSMutableArray new];
for (NSString *fileName in DownloadableAudioFileNames())
{
[metadata addObject:@{
@"name" : [fileName stringByDeletingPathExtension],
@"path" : [TranquilDownloadableAudioPath stringByAppendingPathComponent:fileName]
}];
}

downloadableAudioMetadata = metadata.copy;
}

return downloadableAudioMetadata;
Expand All @@ -193,11 +191,9 @@ NS_INLINE __unused BOOL DownloadableContentAvailable(void)

NS_INLINE __unused NSArray<NSDictionary *> *AudioMetadataIncludingDLC(BOOL includeDownloadable)
{
NSString *bundledAudioPath = [ModuleBundle(NO).bundlePath stringByAppendingPathComponent:@"Audio"];
NSArray *bundledAudioFiles = [NSFileManager.defaultManager contentsOfDirectoryAtPath:bundledAudioPath error:nil];

NSString *userProvidedAudioPath = @"/var/mobile/Library/Application Support/Tranquil/Audio";
NSArray *userProvidedAudioFiles = [NSFileManager.defaultManager contentsOfDirectoryAtPath:userProvidedAudioPath error:nil];
NSArray *bundledAudioFiles = [NSFileManager.defaultManager contentsOfDirectoryAtPath:TranquilBundledAudioPath error:nil];
NSArray *importedAudioFiles = [NSFileManager.defaultManager contentsOfDirectoryAtPath:TranquilImportedAudioPath error:nil];
NSArray *downloadedAudioFiles = [NSFileManager.defaultManager contentsOfDirectoryAtPath:TranquilDownloadableAudioPath error:nil];

__block NSMutableSet *uniquePaths = [NSMutableSet new];
__block NSMutableArray *combinedMetadata = [NSMutableArray new];
Expand All @@ -217,26 +213,18 @@ NS_INLINE __unused NSArray<NSDictionary *> *AudioMetadataIncludingDLC(BOOL inclu
}
};

generateMetadata(bundledAudioFiles, bundledAudioPath);
generateMetadata(userProvidedAudioFiles, userProvidedAudioPath);
generateMetadata(bundledAudioFiles, TranquilBundledAudioPath);
generateMetadata(importedAudioFiles, TranquilImportedAudioPath);
generateMetadata(downloadedAudioFiles, TranquilDownloadableAudioPath);

if (includeDownloadable) {

generateMetadata(DownloadableAudioFileNames(), userProvidedAudioPath);
generateMetadata(DownloadableAudioFileNames(), TranquilDownloadableAudioPath);
}

NSArray *downloadableNames = DownloadableAudioFileNames();
// sort metadata alphabetically, then by asset type (1:bundled 2:imported 3:downloadable)
NSSortDescriptor *name = [NSSortDescriptor sortDescriptorWithKey:@"path" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];
NSSortDescriptor *path = [NSSortDescriptor sortDescriptorWithKey:@"path" ascending:YES comparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
BOOL isDownloadable1 = [downloadableNames containsObject:obj1.lastPathComponent];
BOOL isDownloadable2 = [downloadableNames containsObject:obj2.lastPathComponent];
return isDownloadable2 && !isDownloadable1 ? NSOrderedAscending :
isDownloadable1 && !isDownloadable2 ? NSOrderedDescending :
NSOrderedSame;
}];

[combinedMetadata sortUsingDescriptors:@[path, name]];
[combinedMetadata sortUsingDescriptors:@[
[NSSortDescriptor sortDescriptorWithKey:@"path" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]
]];

return combinedMetadata;
}
Expand Down
53 changes: 48 additions & 5 deletions Classes/TranquilListItemsController.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@

@implementation TranquilListItemsController


- (NSMutableArray *)itemsFromParent
{
return [self _groupedItemsForSpecifiers:[super itemsFromParent]];
}

- (NSMutableArray *)itemsFromDataSource
{
return [self _groupedItemsForSpecifiers:[super itemsFromDataSource]];
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
PSTableCell *cell = (PSTableCell *)[super tableView:tableView cellForRowAtIndexPath:indexPath];
Expand All @@ -33,7 +45,7 @@ - (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwip
NSString *path = specifier.values.firstObject;
NSMutableArray<UIContextualAction *> *actions = [NSMutableArray new];

if ([path hasPrefix:@"/var/mobile/Library/Application Support/Tranquil/Audio"]) {
if ([path hasPrefix:TranquilSupportPath]) {

if ([NSFileManager.defaultManager fileExistsAtPath:path]) {

Expand All @@ -44,7 +56,7 @@ - (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwip
}]];
}

if (![DownloadableAudioFileNames() containsObject:path.lastPathComponent]) {
if (![path hasPrefix:TranquilDownloadableAudioPath]) {

[actions addObject:[UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal title:Localize(@"SWIPE_ACTION_RENAME_TITLE") handler:^(UIContextualAction *action, __kindof UIView *sourceView, void (^completionHandler)(BOOL)) {

Expand Down Expand Up @@ -84,7 +96,7 @@ - (void)downloadAudioFileForSpecifierAtIndexPath:(NSIndexPath *)indexPath

if ([DownloadableAudioFileNames() containsObject:identifier]) {

NSString *destinationPath = [@"/var/mobile/Library/Application Support/Tranquil/Audio" stringByAppendingPathComponent:identifier];
NSString *destinationPath = [TranquilDownloadableAudioPath stringByAppendingPathComponent:identifier];

if ([NSFileManager.defaultManager fileExistsAtPath:destinationPath]) {

Expand Down Expand Up @@ -188,7 +200,7 @@ - (void)deleteFileForIndexPath:(NSIndexPath *)indexPath
[self _selectDefaultValue];
}

[(PSListController *) self.parentController reloadSpecifier:self.specifier];
[(PSListController *) self.parentController reloadSpecifiers];

if ([self audioFileNeedsDownload:indexPath]) {

Expand Down Expand Up @@ -284,8 +296,39 @@ - (void)_setPlaybackPausedForDownload:(BOOL)pause withPlaybackValue:(NSString *)

if (notify) {

CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.creaturecoding.tranquil/preferences-changed"), NULL, NULL, TRUE);
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR(TranquilPreferencesChanged), NULL, NULL, TRUE);
}
}

- (NSMutableArray *)_groupedItemsForSpecifiers:(NSMutableArray *)specifiers
{
NSMutableArray *items = [specifiers mutableCopy];

__block NSUInteger importedIndex = 0;
__block NSUInteger downloadableIndex = 0;
[specifiers enumerateObjectsUsingBlock:^(PSSpecifier *obj, NSUInteger idx, BOOL *stop) {
if ([obj.values.firstObject hasPrefix:TranquilImportedAudioPath] && importedIndex == 0) {

importedIndex = idx;
}
else if ([obj.values.firstObject hasPrefix:TranquilDownloadableAudioPath] && downloadableIndex == 0) {

downloadableIndex = idx;
*stop = YES;
}
}];

PSSpecifier *bundledGroup = [PSSpecifier groupSpecifierWithName:Localize(@"BUNDLED_GROUP_TITLE")];
PSSpecifier *importedGroup = [PSSpecifier groupSpecifierWithName:Localize(@"IMPORTED_GROUP_TITLE")];
PSSpecifier *downloadedGroup = [PSSpecifier groupSpecifierWithName:Localize(@"DOWNLOADABLE_GROUP_TITLE")];

[downloadedGroup setProperty:LocalizeWithTable(@"ACTIVE_SOUND_LIST_FOOTER_MESSAGE", @"Preferences") forKey:PSFooterTextGroupKey];

[items insertObject:downloadedGroup atIndex:downloadableIndex];
[items insertObject:importedGroup atIndex:(importedIndex != 0 ? importedIndex : downloadableIndex)];
items[0] = bundledGroup;

return items;
}

@end
18 changes: 14 additions & 4 deletions Classes/TranquilModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ - (instancetype)init
_backgroundViewController = [TranquilModuleBackgroundViewController new];
_backgroundViewController.module = self;

_preferences = [[NSUserDefaults alloc] initWithSuiteName:@"com.creaturecoding.tranquil"];
_preferences = [[NSUserDefaults alloc] initWithSuiteName:TranquilBundleIdentifier];

// disable playback after respring / reload
[_preferences setBool:NO forKey:@"kBackgroundSoundsActive"];
Expand Down Expand Up @@ -131,7 +131,7 @@ - (void)updatePreferences

- (void)updatePreferencesExternally
{
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.creaturecoding.tranquil/preferences-changed-externally"), NULL, NULL, TRUE);
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR(TranquilPreferencesChangedExternal), NULL, NULL, TRUE);
}

- (void)updateDefaults
Expand Down Expand Up @@ -237,12 +237,22 @@ - (void)dealloc

void preferencesChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
CCUIModuleInstance* moduleInstance = [[NSClassFromString(@"CCUIModuleInstanceManager") sharedInstance] instanceForModuleIdentifier:@"com.creaturecoding.tranquil"];
CCUIModuleInstance* moduleInstance = [[NSClassFromString(@"CCUIModuleInstanceManager") sharedInstance] instanceForModuleIdentifier:TranquilBundleIdentifier];
[(TranquilModule*)moduleInstance.module updatePreferences];
}

__attribute__((constructor))
static void init(void)
{
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, preferencesChanged, CFSTR("com.creaturecoding.tranquil/preferences-changed"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:TranquilBundleIdentifier];
NSString *activeSound = [defaults stringForKey:@"kActiveSound"];

// migrate active sound preference from Tranquil/Audio to Tranquil/Downloadable if needed
if (activeSound && [activeSound hasPrefix:TranquilImportedAudioPath] && [DownloadableAudioFileNames() containsObject:activeSound.lastPathComponent]) {

activeSound = [TranquilDownloadableAudioPath stringByAppendingPathComponent:activeSound.lastPathComponent];
[defaults setObject:activeSound forKey:@"kActiveSound"];
}

CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, preferencesChanged, CFSTR(TranquilPreferencesChanged), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
}
14 changes: 12 additions & 2 deletions Classes/TranquilModuleContentViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,12 @@ - (void)willTransitionToExpandedContentMode:(BOOL)expand
if (DownloadableContentAvailable()) {

[self addActionWithTitle:Localize(@"DOWNLOADS_AVAILABLE_TITLE") glyph:[UIImage tranquil_moduleImageNamed:@"Download"] handler:^{
NSString *urlString = [NSString stringWithFormat:@"prefs:root=ControlCenter&path=Tranquil/activeSoundSpecifier"];
NSString *urlString;
if (@available(iOS 13, *)) {
urlString = [NSString stringWithFormat:@"prefs:root=ControlCenter&path=Tranquil/activeSoundSpecifier"];
} else {
urlString = [NSString stringWithFormat:@"prefs:root=ControlCenter&path=CUSTOMIZE_CONTROLS/Tranquil/activeSoundSpecifier"];
}
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]]];

OpenApplicationUrl(url);
Expand All @@ -134,7 +139,12 @@ - (void)willTransitionToExpandedContentMode:(BOOL)expand
}

[self setFooterButtonTitle:Localize(@"PROJECT_SETTINGS_TITLE") handler:^{
NSString *urlString = [NSString stringWithFormat:@"prefs:root=ControlCenter&path=Tranquil"];
NSString *urlString;
if (@available(iOS 13, *)) {
urlString = [NSString stringWithFormat:@"prefs:root=ControlCenter&path=Tranquil"];
} else {
urlString = [NSString stringWithFormat:@"prefs:root=ControlCenter&path=CUSTOMIZE_CONTROLS/Tranquil"];
}
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]]];

OpenApplicationUrl(url);
Expand Down
11 changes: 5 additions & 6 deletions Classes/TranquilPreferencesController.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ - (void)viewDidLoad
[super viewDidLoad];

loadedController = self;
_preferences = [[NSUserDefaults alloc] initWithSuiteName:@"com.creaturecoding.tranquil"];
_preferences = [[NSUserDefaults alloc] initWithSuiteName:TranquilBundleIdentifier];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Expand Down Expand Up @@ -95,7 +95,7 @@ - (NSUserDefaults *)preferences

- (NSURL *)userImportedSoundsDirectoryURL
{
return [NSURL fileURLWithPath:@"/var/mobile/Library/Application Support/Tranquil/Audio"];
return [NSURL fileURLWithPath:TranquilImportedAudioPath];
}

- (NSArray *)audioMetadata
Expand Down Expand Up @@ -167,7 +167,7 @@ - (void)refreshVolumeWithMediaDisplay
- (void)playSampleWithMedia
{
[_preferences setBool:YES forKey:@"kPauseForSample"];
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.creaturecoding.tranquil/preferences-changed"), NULL, NULL, TRUE);
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR(TranquilPreferencesChanged), NULL, NULL, TRUE);

PSSpecifier *stopSampleSpecifier = [PSSpecifier preferenceSpecifierNamed:Localize(@"STOP_SAMPLE_BUTTON_LABEL") target:self set:NULL get:NULL detail:Nil cell:PSButtonCell edit:Nil];
stopSampleSpecifier->action = @selector(stopSampleWithMedia);
Expand Down Expand Up @@ -212,7 +212,7 @@ - (void)stopSampleWithMedia
[[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];

[_preferences setBool:NO forKey:@"kPauseForSample"];
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.creaturecoding.tranquil/preferences-changed"), NULL, NULL, TRUE);
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR(TranquilPreferencesChanged), NULL, NULL, TRUE);
}

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
Expand Down Expand Up @@ -296,7 +296,6 @@ - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocum

NSError *error;
NSURL *destination = [NSURL fileURLWithPath:newFileName relativeToURL:[self userImportedSoundsDirectoryURL]];
// [NSFileManager.defaultManager copyItemAtURL:url toURL:destination error:&error];
[NSFileManager.defaultManager moveItemAtURL:url toURL:destination error:&error];

// error copying file
Expand Down Expand Up @@ -333,5 +332,5 @@ void preferencesChangedExternally(CFNotificationCenterRef center, void *observer
__attribute__((constructor))
static void init(void)
{
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, preferencesChangedExternally, CFSTR("com.creaturecoding.tranquil/preferences-changed-externally"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, preferencesChangedExternally, CFSTR(TranquilPreferencesChangedExternal), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
}
Binary file not shown.
Binary file modified Resources/Assets.car
Binary file not shown.
6 changes: 5 additions & 1 deletion Resources/Preferences.plist
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@
<string>com.creaturecoding.tranquil</string>
<key>default</key>
<real>0.6</real>
<key>iconImageShouldFlipForRightToLeft</key>
<true/>
</dict>

<dict>
Expand Down Expand Up @@ -155,7 +157,9 @@
<key>defaults</key>
<string>com.creaturecoding.tranquil</string>
<key>default</key>
<real>0.6</real>
<real>0.2</real>
<key>iconImageShouldFlipForRightToLeft</key>
<true/>
</dict>

<dict>
Expand Down
5 changes: 5 additions & 0 deletions Resources/base.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
"STOP_SAMPLE_BUTTON_LABEL" = "Stop Sample";
"PLAY_SAMPLE_BUTTON_LABEL" = "Play Sample";

/* preferences sound list group titles */
"BUNDLED_GROUP_TITLE" = "BUNDLED";
"IMPORTED_GROUP_TITLE" = "IMPORTED";
"DOWNLOADABLE_GROUP_TITLE" = "DOWNLOADABLE";

/* sound import rename alert */
"RENAME_FILE_TITLE" = "Rename File";
"RENAME_FILE_MESSAGE" = "You can change the name to your liking, or leave it as is. The name you choose here will be the display name of the sound.";
Expand Down
2 changes: 1 addition & 1 deletion Resources/base.lproj/Preferences.strings
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"BACKGROUND_SOUNDS_SWITCH_TITLE" = "Background Sounds";

"ACTIVE_SOUND_LIST_TITLE" = "Sound";
"ACTIVE_SOUND_LIST_FOOTER_MESSAGE" = "\nSwipe left on downloaded, or imported sounds for more options.\n\nDefault bundled or downloaded sounds cannot be renamed, as they use localized names. User imported sounds can be renamed or deleted.";
"ACTIVE_SOUND_LIST_FOOTER_MESSAGE" = "\nSwipe left on downloaded, or imported sounds for more options.\n\nBundled or downloaded sounds cannot be renamed, as they use localized names. User imported sounds can be renamed or deleted.";

"VOLUME_SLIDER_TITLE" = "Volume";

Expand Down
5 changes: 5 additions & 0 deletions Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
"STOP_SAMPLE_BUTTON_LABEL" = "Stop Sample";
"PLAY_SAMPLE_BUTTON_LABEL" = "Play Sample";

/* preferences sound list group titles */
"BUNDLED_GROUP_TITLE" = "BUNDLED";
"IMPORTED_GROUP_TITLE" = "IMPORTED";
"DOWNLOADABLE_GROUP_TITLE" = "DOWNLOADABLE";

/* sound import rename alert */
"RENAME_FILE_TITLE" = "Rename File";
"RENAME_FILE_MESSAGE" = "You can change the name to your liking, or leave it as is. The name you choose here will be the display name of the sound.";
Expand Down
Loading

0 comments on commit 0814310

Please sign in to comment.