mirror of https://github.com/oxen-io/session-ios
Refactored SessionShareExtension code to Swift
parent
7a22c9d329
commit
4f3faa28bc
@ -1,18 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <SignalUtilitiesKit/OWSViewController.h>
|
|
||||||
#import <SignalUtilitiesKit/ScreenLockViewController.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@protocol ShareViewDelegate;
|
|
||||||
|
|
||||||
@interface SAEScreenLockViewController : ScreenLockViewController
|
|
||||||
|
|
||||||
- (instancetype)initWithShareViewDelegate:(id<ShareViewDelegate>)shareViewDelegate;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -1,206 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "SAEScreenLockViewController.h"
|
|
||||||
#import "UIColor+OWS.h"
|
|
||||||
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
|
|
||||||
#import <SessionUtilitiesKit/AppContext.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface SAEScreenLockViewController () <ScreenLockViewDelegate>
|
|
||||||
|
|
||||||
@property (nonatomic, readonly, weak) id<ShareViewDelegate> shareViewDelegate;
|
|
||||||
|
|
||||||
@property (nonatomic) BOOL hasShownAuthUIOnce;
|
|
||||||
|
|
||||||
@property (nonatomic) BOOL isShowingAuthUI;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
@implementation SAEScreenLockViewController
|
|
||||||
|
|
||||||
- (instancetype)initWithShareViewDelegate:(id<ShareViewDelegate>)shareViewDelegate
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
if (!self) {
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
_shareViewDelegate = shareViewDelegate;
|
|
||||||
|
|
||||||
self.delegate = self;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)loadView
|
|
||||||
{
|
|
||||||
[super loadView];
|
|
||||||
|
|
||||||
UIView.appearance.tintColor = LKColors.text;
|
|
||||||
|
|
||||||
// Gradient background
|
|
||||||
self.view.backgroundColor = UIColor.clearColor;
|
|
||||||
CAGradientLayer *layer = [CAGradientLayer new];
|
|
||||||
layer.frame = UIScreen.mainScreen.bounds;
|
|
||||||
UIColor *gradientStartColor = LKAppModeUtilities.isLightMode ? [UIColor colorWithRGBHex:0xFCFCFC] : [UIColor colorWithRGBHex:0x171717];
|
|
||||||
UIColor *gradientEndColor = LKAppModeUtilities.isLightMode ? [UIColor colorWithRGBHex:0xFFFFFF] : [UIColor colorWithRGBHex:0x121212];
|
|
||||||
layer.colors = @[ (id)gradientStartColor.CGColor, (id)gradientEndColor.CGColor ];
|
|
||||||
[self.view.layer insertSublayer:layer atIndex:0];
|
|
||||||
|
|
||||||
// Navigation bar background color
|
|
||||||
UINavigationBar *navigationBar = self.navigationController.navigationBar;
|
|
||||||
[navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
|
|
||||||
navigationBar.shadowImage = [UIImage new];
|
|
||||||
[navigationBar setTranslucent:NO];
|
|
||||||
navigationBar.barTintColor = LKColors.navigationBarBackground;
|
|
||||||
|
|
||||||
// Title
|
|
||||||
UILabel *titleLabel = [UILabel new];
|
|
||||||
titleLabel.text = NSLocalizedString(@"vc_share_title", @"");
|
|
||||||
titleLabel.textColor = LKColors.text;
|
|
||||||
titleLabel.font = [UIFont boldSystemFontOfSize:LKValues.veryLargeFontSize];
|
|
||||||
self.navigationItem.titleView = titleLabel;
|
|
||||||
|
|
||||||
// Close button
|
|
||||||
UIBarButtonItem *closeButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"X"] style:UIBarButtonItemStylePlain target:self action:@selector(dismissPressed:)];
|
|
||||||
closeButton.tintColor = LKColors.text;
|
|
||||||
self.navigationItem.leftBarButtonItem = closeButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewWillAppear:(BOOL)animated
|
|
||||||
{
|
|
||||||
[super viewWillAppear:animated];
|
|
||||||
|
|
||||||
[self ensureUI];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidAppear:(BOOL)animated
|
|
||||||
{
|
|
||||||
[super viewDidAppear:animated];
|
|
||||||
|
|
||||||
[self ensureUI];
|
|
||||||
|
|
||||||
// Auto-show the auth UI f
|
|
||||||
if (!self.hasShownAuthUIOnce) {
|
|
||||||
self.hasShownAuthUIOnce = YES;
|
|
||||||
|
|
||||||
[self tryToPresentAuthUIToUnlockScreenLock];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
// Surface memory leaks by logging the deallocation of view controllers.
|
|
||||||
OWSLogVerbose(@"Dealloc: %@", self.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tryToPresentAuthUIToUnlockScreenLock
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
if (self.isShowingAuthUI) {
|
|
||||||
// We're already showing the auth UI; abort.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OWSLogInfo(@"try to unlock screen lock");
|
|
||||||
|
|
||||||
self.isShowingAuthUI = YES;
|
|
||||||
|
|
||||||
[OWSScreenLock.sharedManager tryToUnlockScreenLockWithSuccess:^{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
OWSLogInfo(@"unlock screen lock succeeded.");
|
|
||||||
|
|
||||||
self.isShowingAuthUI = NO;
|
|
||||||
|
|
||||||
[self.shareViewDelegate shareViewWasUnlocked];
|
|
||||||
}
|
|
||||||
failure:^(NSError *error) {
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
OWSLogInfo(@"unlock screen lock failed.");
|
|
||||||
|
|
||||||
self.isShowingAuthUI = NO;
|
|
||||||
|
|
||||||
[self ensureUI];
|
|
||||||
|
|
||||||
[self showScreenLockFailureAlertWithMessage:error.localizedDescription];
|
|
||||||
}
|
|
||||||
unexpectedFailure:^(NSError *error) {
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
OWSLogInfo(@"unlock screen lock unexpectedly failed.");
|
|
||||||
|
|
||||||
self.isShowingAuthUI = NO;
|
|
||||||
|
|
||||||
// Local Authentication isn't working properly.
|
|
||||||
// This isn't covered by the docs or the forums but in practice
|
|
||||||
// it appears to be effective to retry again after waiting a bit.
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[self ensureUI];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cancel:^{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
OWSLogInfo(@"unlock screen lock cancelled.");
|
|
||||||
|
|
||||||
self.isShowingAuthUI = NO;
|
|
||||||
|
|
||||||
[self ensureUI];
|
|
||||||
}];
|
|
||||||
|
|
||||||
[self ensureUI];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)ensureUI
|
|
||||||
{
|
|
||||||
[self updateUIWithState:ScreenLockUIStateScreenLock isLogoAtTop:NO animated:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)showScreenLockFailureAlertWithMessage:(NSString *)message
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
[OWSAlerts showAlertWithTitle:NSLocalizedString(@"SCREEN_LOCK_UNLOCK_FAILED",
|
|
||||||
@"Title for alert indicating that screen lock could not be unlocked.")
|
|
||||||
message:message
|
|
||||||
buttonTitle:nil
|
|
||||||
buttonAction:^(UIAlertAction *action) {
|
|
||||||
// After the alert, update the UI.
|
|
||||||
[self ensureUI];
|
|
||||||
}
|
|
||||||
fromViewController:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dismissPressed:(id)sender
|
|
||||||
{
|
|
||||||
OWSLogDebug(@"tapped dismiss share button");
|
|
||||||
|
|
||||||
[self cancelShareExperience];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)cancelShareExperience
|
|
||||||
{
|
|
||||||
[self.shareViewDelegate shareViewWasCancelled];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - ScreenLockViewDelegate
|
|
||||||
|
|
||||||
- (void)unlockButtonWasTapped
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
OWSLogInfo(@"unlockButtonWasTapped");
|
|
||||||
|
|
||||||
[self tryToPresentAuthUIToUnlockScreenLock];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,206 @@
|
|||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import PromiseKit
|
||||||
|
import SignalCoreKit
|
||||||
|
import SignalUtilitiesKit
|
||||||
|
import SessionUIKit
|
||||||
|
import SessionUtilitiesKit
|
||||||
|
|
||||||
|
final class SAEScreenLockViewController: ScreenLockViewController, ScreenLockViewDelegate {
|
||||||
|
private var hasShownAuthUIOnce: Bool = false
|
||||||
|
private var isShowingAuthUI: Bool = false
|
||||||
|
|
||||||
|
private weak var shareViewDelegate: ShareViewDelegate?
|
||||||
|
|
||||||
|
// MARK: - Initialization
|
||||||
|
|
||||||
|
init(shareViewDelegate: ShareViewDelegate) {
|
||||||
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
|
||||||
|
self.shareViewDelegate = shareViewDelegate
|
||||||
|
self.delegate = self
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
OWSLogger.verbose("Dealloc: \(type(of: self))")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - UI
|
||||||
|
|
||||||
|
private lazy var gradientBackground: CAGradientLayer = {
|
||||||
|
let layer: CAGradientLayer = CAGradientLayer()
|
||||||
|
|
||||||
|
let gradientStartColor: UIColor = (LKAppModeUtilities.isLightMode ?
|
||||||
|
UIColor(rgbHex: 0xFCFCFC) :
|
||||||
|
UIColor(rgbHex: 0x171717)
|
||||||
|
)
|
||||||
|
let gradientEndColor: UIColor = (LKAppModeUtilities.isLightMode ?
|
||||||
|
UIColor(rgbHex: 0xFFFFFF) :
|
||||||
|
UIColor(rgbHex: 0x121212)
|
||||||
|
)
|
||||||
|
layer.colors = [gradientStartColor.cgColor, gradientEndColor.cgColor]
|
||||||
|
|
||||||
|
return layer
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let titleLabel: UILabel = UILabel()
|
||||||
|
titleLabel.font = UIFont.boldSystemFont(ofSize: Values.veryLargeFontSize)
|
||||||
|
titleLabel.text = "vc_share_title".localized()
|
||||||
|
titleLabel.textColor = Colors.text
|
||||||
|
|
||||||
|
return titleLabel
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var closeButton: UIBarButtonItem = {
|
||||||
|
let closeButton: UIBarButtonItem = UIBarButtonItem(image: UIImage(named: "X"), style: .plain, target: self, action: #selector(dismissPressed))
|
||||||
|
closeButton.tintColor = Colors.text
|
||||||
|
|
||||||
|
return closeButton
|
||||||
|
}()
|
||||||
|
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
|
override func loadView() {
|
||||||
|
super.loadView()
|
||||||
|
|
||||||
|
UIView.appearance().tintColor = Colors.text
|
||||||
|
|
||||||
|
self.view.backgroundColor = UIColor.clear
|
||||||
|
self.view.layer.insertSublayer(gradientBackground, at: 0)
|
||||||
|
|
||||||
|
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
|
||||||
|
self.navigationController?.navigationBar.shadowImage = UIImage()
|
||||||
|
self.navigationController?.navigationBar.isTranslucent = false
|
||||||
|
self.navigationController?.navigationBar.tintColor = Colors.navigationBarBackground
|
||||||
|
|
||||||
|
self.navigationItem.titleView = titleLabel
|
||||||
|
self.navigationItem.leftBarButtonItem = closeButton
|
||||||
|
|
||||||
|
setupLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
self.ensureUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
|
self.ensureUI()
|
||||||
|
|
||||||
|
// Auto-show the auth UI f
|
||||||
|
if !hasShownAuthUIOnce {
|
||||||
|
hasShownAuthUIOnce = true
|
||||||
|
|
||||||
|
self.tryToPresentAuthUIToUnlockScreenLock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Layout
|
||||||
|
|
||||||
|
private func setupLayout() {
|
||||||
|
gradientBackground.frame = UIScreen.main.bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Functions
|
||||||
|
|
||||||
|
private func tryToPresentAuthUIToUnlockScreenLock() {
|
||||||
|
AssertIsOnMainThread()
|
||||||
|
|
||||||
|
// If we're already showing the auth UI; abort.
|
||||||
|
if self.isShowingAuthUI { return }
|
||||||
|
|
||||||
|
OWSLogger.info("try to unlock screen lock")
|
||||||
|
|
||||||
|
isShowingAuthUI = true
|
||||||
|
|
||||||
|
OWSScreenLock.shared.tryToUnlockScreenLock(
|
||||||
|
success: { [weak self] in
|
||||||
|
AssertIsOnMainThread()
|
||||||
|
OWSLogger.info("unlock screen lock succeeded.")
|
||||||
|
|
||||||
|
self?.isShowingAuthUI = false
|
||||||
|
self?.shareViewDelegate?.shareViewWasUnlocked()
|
||||||
|
},
|
||||||
|
failure: { [weak self] error in
|
||||||
|
AssertIsOnMainThread()
|
||||||
|
OWSLogger.info("unlock screen lock failed.")
|
||||||
|
|
||||||
|
self?.isShowingAuthUI = false
|
||||||
|
self?.ensureUI()
|
||||||
|
self?.showScreenLockFailureAlert(message: error.localizedDescription)
|
||||||
|
},
|
||||||
|
unexpectedFailure: { [weak self] error in
|
||||||
|
AssertIsOnMainThread()
|
||||||
|
OWSLogger.info("unlock screen lock unexpectedly failed.")
|
||||||
|
|
||||||
|
self?.isShowingAuthUI = false
|
||||||
|
|
||||||
|
// Local Authentication isn't working properly.
|
||||||
|
// This isn't covered by the docs or the forums but in practice
|
||||||
|
// it appears to be effective to retry again after waiting a bit.
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self?.ensureUI()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancel: { [weak self] in
|
||||||
|
AssertIsOnMainThread()
|
||||||
|
OWSLogger.info("unlock screen lock cancelled.")
|
||||||
|
|
||||||
|
self?.isShowingAuthUI = false
|
||||||
|
self?.ensureUI()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.ensureUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func ensureUI() {
|
||||||
|
self.updateUI(with: .screenLock, isLogoAtTop: false, animated: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showScreenLockFailureAlert(message: String) {
|
||||||
|
AssertIsOnMainThread()
|
||||||
|
|
||||||
|
OWSAlerts.showAlert(
|
||||||
|
// Title for alert indicating that screen lock could not be unlocked.
|
||||||
|
title: "SCREEN_LOCK_UNLOCK_FAILED".localized(),
|
||||||
|
message: message,
|
||||||
|
buttonTitle: nil,
|
||||||
|
buttonAction: { [weak self] action in
|
||||||
|
// After the alert, update the UI
|
||||||
|
self?.ensureUI()
|
||||||
|
},
|
||||||
|
fromViewController: self
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Transitions
|
||||||
|
|
||||||
|
@objc private func dismissPressed() {
|
||||||
|
OWSLogger.debug("unlock screen lock cancelled.")
|
||||||
|
|
||||||
|
self.cancelShareExperience()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func cancelShareExperience() {
|
||||||
|
self.shareViewDelegate?.shareViewWasCancelled()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - ScreenLockViewDelegate
|
||||||
|
|
||||||
|
func unlockButtonWasTapped() {
|
||||||
|
AssertIsOnMainThread()
|
||||||
|
OWSLogger.info("unlockButtonWasTapped")
|
||||||
|
|
||||||
|
self.tryToPresentAuthUIToUnlockScreenLock()
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <SessionUtilitiesKit/AppContext.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
// This is _NOT_ a singleton and will be instantiated each time that the SAE is used.
|
|
||||||
@interface ShareAppExtensionContext : NSObject <AppContext>
|
|
||||||
|
|
||||||
- (instancetype)init NS_UNAVAILABLE;
|
|
||||||
|
|
||||||
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -1,240 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "ShareAppExtensionContext.h"
|
|
||||||
#import <SignalUtilitiesKit/UIViewController+OWS.h>
|
|
||||||
#import <SessionMessagingKit/OWSStorage.h>
|
|
||||||
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
|
|
||||||
#import <SignalUtilitiesKit/TSConstants.h>
|
|
||||||
#import <SessionUtilitiesKit/SessionUtilitiesKit-Swift.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface ShareAppExtensionContext ()
|
|
||||||
|
|
||||||
@property (nonatomic) UIViewController *rootViewController;
|
|
||||||
|
|
||||||
@property (atomic) UIApplicationState reportedApplicationState;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
@implementation ShareAppExtensionContext
|
|
||||||
|
|
||||||
@synthesize mainWindow = _mainWindow;
|
|
||||||
@synthesize appLaunchTime = _appLaunchTime;
|
|
||||||
|
|
||||||
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
if (!self) {
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
OWSAssertDebug(rootViewController);
|
|
||||||
|
|
||||||
_rootViewController = rootViewController;
|
|
||||||
|
|
||||||
self.reportedApplicationState = UIApplicationStateActive;
|
|
||||||
|
|
||||||
_appLaunchTime = [NSDate new];
|
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
||||||
selector:@selector(extensionHostDidBecomeActive:)
|
|
||||||
name:NSExtensionHostDidBecomeActiveNotification
|
|
||||||
object:nil];
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
||||||
selector:@selector(extensionHostWillResignActive:)
|
|
||||||
name:NSExtensionHostWillResignActiveNotification
|
|
||||||
object:nil];
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
||||||
selector:@selector(extensionHostDidEnterBackground:)
|
|
||||||
name:NSExtensionHostDidEnterBackgroundNotification
|
|
||||||
object:nil];
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
||||||
selector:@selector(extensionHostWillEnterForeground:)
|
|
||||||
name:NSExtensionHostWillEnterForegroundNotification
|
|
||||||
object:nil];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Notifications
|
|
||||||
|
|
||||||
- (void)extensionHostDidBecomeActive:(NSNotification *)notification
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
OWSLogInfo(@"");
|
|
||||||
|
|
||||||
self.reportedApplicationState = UIApplicationStateActive;
|
|
||||||
|
|
||||||
[NSNotificationCenter.defaultCenter postNotificationName:OWSApplicationDidBecomeActiveNotification object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)extensionHostWillResignActive:(NSNotification *)notification
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
self.reportedApplicationState = UIApplicationStateInactive;
|
|
||||||
|
|
||||||
OWSLogInfo(@"");
|
|
||||||
[DDLog flushLog];
|
|
||||||
|
|
||||||
[NSNotificationCenter.defaultCenter postNotificationName:OWSApplicationWillResignActiveNotification object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)extensionHostDidEnterBackground:(NSNotification *)notification
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
OWSLogInfo(@"");
|
|
||||||
[DDLog flushLog];
|
|
||||||
|
|
||||||
self.reportedApplicationState = UIApplicationStateBackground;
|
|
||||||
|
|
||||||
[NSNotificationCenter.defaultCenter postNotificationName:OWSApplicationDidEnterBackgroundNotification object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)extensionHostWillEnterForeground:(NSNotification *)notification
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
OWSLogInfo(@"");
|
|
||||||
|
|
||||||
self.reportedApplicationState = UIApplicationStateInactive;
|
|
||||||
|
|
||||||
[NSNotificationCenter.defaultCenter postNotificationName:OWSApplicationWillEnterForegroundNotification object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
- (BOOL)isMainApp
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isMainAppAndActive
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isRTL
|
|
||||||
{
|
|
||||||
static BOOL isRTL = NO;
|
|
||||||
static dispatch_once_t onceToken;
|
|
||||||
dispatch_once(&onceToken, ^{
|
|
||||||
// Borrowed from PureLayout's AppExtension compatible RTL support.
|
|
||||||
// App Extensions may not access -[UIApplication sharedApplication]; fall back to checking the bundle's
|
|
||||||
// preferred localization character direction
|
|
||||||
isRTL = [NSLocale characterDirectionForLanguage:[[NSBundle mainBundle] preferredLocalizations][0]]
|
|
||||||
== NSLocaleLanguageDirectionRightToLeft;
|
|
||||||
});
|
|
||||||
return isRTL;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setStatusBarHidden:(BOOL)isHidden animated:(BOOL)isAnimated
|
|
||||||
{
|
|
||||||
OWSLogInfo(@"Ignoring request to show/hide status bar since we're in an app extension");
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)statusBarHeight
|
|
||||||
{
|
|
||||||
return 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isInBackground
|
|
||||||
{
|
|
||||||
return self.reportedApplicationState == UIApplicationStateBackground;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isAppForegroundAndActive
|
|
||||||
{
|
|
||||||
return self.reportedApplicationState == UIApplicationStateActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:
|
|
||||||
(BackgroundTaskExpirationHandler)expirationHandler
|
|
||||||
{
|
|
||||||
return UIBackgroundTaskInvalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)endBackgroundTask:(UIBackgroundTaskIdentifier)backgroundTaskIdentifier
|
|
||||||
{
|
|
||||||
OWSAssertDebug(backgroundTaskIdentifier == UIBackgroundTaskInvalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)ensureSleepBlocking:(BOOL)shouldBeBlocking blockingObjects:(NSArray<id> *)blockingObjects
|
|
||||||
{
|
|
||||||
OWSLogDebug(@"Ignoring request to block sleep.");
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMainAppBadgeNumber:(NSInteger)value
|
|
||||||
{
|
|
||||||
OWSFailDebug(@"");
|
|
||||||
}
|
|
||||||
|
|
||||||
- (nullable UIViewController *)frontmostViewController
|
|
||||||
{
|
|
||||||
OWSAssertDebug(self.rootViewController);
|
|
||||||
|
|
||||||
return [self.rootViewController findFrontmostViewController:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (nullable UIAlertAction *)openSystemSettingsAction
|
|
||||||
{
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isRunningTests
|
|
||||||
{
|
|
||||||
// We don't need to distinguish this in the SAE.
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setNetworkActivityIndicatorVisible:(BOOL)value
|
|
||||||
{
|
|
||||||
OWSFailDebug(@"");
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)runNowOrWhenMainAppIsActive:(AppActiveBlock)block
|
|
||||||
{
|
|
||||||
OWSFailDebug(@"cannot run main app active blocks in share extension.");
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id<SSKKeychainStorage>)keychainStorage
|
|
||||||
{
|
|
||||||
return [SSKDefaultKeychainStorage shared];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)appDocumentDirectoryPath
|
|
||||||
{
|
|
||||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
||||||
NSURL *documentDirectoryURL =
|
|
||||||
[[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
|
|
||||||
return [documentDirectoryURL path];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)appSharedDataDirectoryPath
|
|
||||||
{
|
|
||||||
NSURL *groupContainerDirectoryURL =
|
|
||||||
[[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:SignalApplicationGroup];
|
|
||||||
return [groupContainerDirectoryURL path];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSUserDefaults *)appUserDefaults
|
|
||||||
{
|
|
||||||
return [[NSUserDefaults alloc] initWithSuiteName:SignalApplicationGroup];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,204 @@
|
|||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SignalUtilitiesKit
|
||||||
|
import SessionUtilitiesKit
|
||||||
|
import SessionMessagingKit
|
||||||
|
|
||||||
|
/// This is _NOT_ a singleton and will be instantiated each time that the SAE is used.
|
||||||
|
final class ShareAppExtensionContext: NSObject, AppContext {
|
||||||
|
var rootViewController: UIViewController
|
||||||
|
var reportedApplicationState: UIApplication.State
|
||||||
|
|
||||||
|
let appLaunchTime = Date()
|
||||||
|
let isMainApp = false
|
||||||
|
let isMainAppAndActive = false
|
||||||
|
|
||||||
|
var mainWindow: UIWindow?
|
||||||
|
var wasWokenUpByPushNotification: Bool = false
|
||||||
|
|
||||||
|
private static var _isRTL: Bool = {
|
||||||
|
// Borrowed from PureLayout's AppExtension compatible RTL support.
|
||||||
|
// App Extensions may not access -[UIApplication sharedApplication]; fall back
|
||||||
|
// to checking the bundle's preferred localization character direction
|
||||||
|
return (
|
||||||
|
Locale.characterDirection(
|
||||||
|
forLanguage: (Bundle.main.preferredLocalizations.first ?? "")
|
||||||
|
) == Locale.LanguageDirection.rightToLeft
|
||||||
|
)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var isRTL: Bool { return ShareAppExtensionContext._isRTL }
|
||||||
|
var isRunningTests: Bool { return false } // We don't need to distinguish this in the SAE
|
||||||
|
|
||||||
|
var statusBarHeight: CGFloat { return 20 }
|
||||||
|
var openSystemSettingsAction: UIAlertAction?
|
||||||
|
|
||||||
|
// MARK: - Initialization
|
||||||
|
|
||||||
|
init(rootViewController: UIViewController) {
|
||||||
|
self.rootViewController = rootViewController
|
||||||
|
self.reportedApplicationState = .active
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(extensionHostDidBecomeActive(notification:)),
|
||||||
|
name: .NSExtensionHostDidBecomeActive,
|
||||||
|
object: nil
|
||||||
|
)
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(extensionHostWillResignActive(notification:)),
|
||||||
|
name: .NSExtensionHostWillResignActive,
|
||||||
|
object: nil
|
||||||
|
)
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(extensionHostDidEnterBackground(notification:)),
|
||||||
|
name: .NSExtensionHostDidEnterBackground,
|
||||||
|
object: nil
|
||||||
|
)
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(extensionHostWillEnterForeground(notification:)),
|
||||||
|
name: .NSExtensionHostWillEnterForeground,
|
||||||
|
object: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
NotificationCenter.default.removeObserver(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Notifications
|
||||||
|
|
||||||
|
@objc private func extensionHostDidBecomeActive(notification: NSNotification) {
|
||||||
|
AssertIsOnMainThread()
|
||||||
|
OWSLogger.info("")
|
||||||
|
|
||||||
|
self.reportedApplicationState = .active
|
||||||
|
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: .OWSApplicationDidBecomeActive,
|
||||||
|
object: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func extensionHostWillResignActive(notification: NSNotification) {
|
||||||
|
AssertIsOnMainThread()
|
||||||
|
|
||||||
|
self.reportedApplicationState = .inactive
|
||||||
|
|
||||||
|
OWSLogger.info("")
|
||||||
|
DDLog.flushLog()
|
||||||
|
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: .OWSApplicationWillResignActive,
|
||||||
|
object: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func extensionHostDidEnterBackground(notification: NSNotification) {
|
||||||
|
AssertIsOnMainThread()
|
||||||
|
|
||||||
|
OWSLogger.info("")
|
||||||
|
DDLog.flushLog()
|
||||||
|
|
||||||
|
self.reportedApplicationState = .background
|
||||||
|
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: .OWSApplicationDidEnterBackground,
|
||||||
|
object: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func extensionHostWillEnterForeground(notification: NSNotification) {
|
||||||
|
AssertIsOnMainThread()
|
||||||
|
|
||||||
|
OWSLogger.info("")
|
||||||
|
|
||||||
|
self.reportedApplicationState = .inactive
|
||||||
|
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: .OWSApplicationWillEnterForeground,
|
||||||
|
object: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - AppContext Functions
|
||||||
|
|
||||||
|
func isAppForegroundAndActive() -> Bool {
|
||||||
|
return (reportedApplicationState == .active)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isInBackground() -> Bool {
|
||||||
|
return (reportedApplicationState == .background)
|
||||||
|
}
|
||||||
|
|
||||||
|
func frontmostViewController() -> UIViewController? {
|
||||||
|
return rootViewController.findFrontmostViewController(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func keychainStorage() -> SSKKeychainStorage {
|
||||||
|
return SSKDefaultKeychainStorage.shared
|
||||||
|
}
|
||||||
|
|
||||||
|
func appDocumentDirectoryPath() -> String {
|
||||||
|
let targetPath: String? = FileManager.default
|
||||||
|
.urls(
|
||||||
|
for: .documentDirectory,
|
||||||
|
in: .userDomainMask
|
||||||
|
)
|
||||||
|
.last?
|
||||||
|
.path
|
||||||
|
owsAssertDebug(targetPath != nil)
|
||||||
|
|
||||||
|
return (targetPath ?? "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func appSharedDataDirectoryPath() -> String {
|
||||||
|
let targetPath: String? = FileManager.default
|
||||||
|
.containerURL(forSecurityApplicationGroupIdentifier: SignalApplicationGroup)?
|
||||||
|
.path
|
||||||
|
owsAssertDebug(targetPath != nil)
|
||||||
|
|
||||||
|
return (targetPath ?? "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func appUserDefaults() -> UserDefaults {
|
||||||
|
let targetUserDefaults: UserDefaults? = UserDefaults(suiteName: SignalApplicationGroup)
|
||||||
|
owsAssertDebug(targetUserDefaults != nil)
|
||||||
|
|
||||||
|
return (targetUserDefaults ?? UserDefaults.standard)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setStatusBarHidden(_ isHidden: Bool, animated isAnimated: Bool) {
|
||||||
|
OWSLogger.info("Ignoring request to show/hide status bar since we're in an app extension")
|
||||||
|
}
|
||||||
|
|
||||||
|
func beginBackgroundTask(expirationHandler: @escaping BackgroundTaskExpirationHandler) -> UIBackgroundTaskIdentifier {
|
||||||
|
return .invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func endBackgroundTask(_ backgroundTaskIdentifier: UIBackgroundTaskIdentifier) {
|
||||||
|
owsAssertDebug(backgroundTaskIdentifier == .invalid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureSleepBlocking(_ shouldBeBlocking: Bool, blockingObjects: [Any]) {
|
||||||
|
OWSLogger.debug("Ignoring request to block sleep.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func setMainAppBadgeNumber(_ value: Int) {
|
||||||
|
owsFailDebug("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func setNetworkActivityIndicatorVisible(_ value: Bool) {
|
||||||
|
owsFailDebug("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func runNowOr(whenMainAppIsActive block: @escaping AppActiveBlock) {
|
||||||
|
owsFailDebug("cannot run main app active blocks in share extension.")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
public protocol ReusableView: AnyObject {
|
||||||
|
static var defaultReuseIdentifier: String { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension ReusableView where Self: UIView {
|
||||||
|
static var defaultReuseIdentifier: String {
|
||||||
|
return String(describing: self.self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UITableViewCell: ReusableView {}
|
||||||
|
extension UITableViewHeaderFooterView: ReusableView {}
|
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import SignalCoreKit
|
||||||
|
|
||||||
|
public extension String {
|
||||||
|
func localized() -> String {
|
||||||
|
// If the localized string matches the key provided then the localisation failed
|
||||||
|
let localizedString = NSLocalizedString(self, comment: "")
|
||||||
|
owsAssertDebug(localizedString != self, "Key \"\(self)\" is not set in Localizable.strings")
|
||||||
|
|
||||||
|
return localizedString
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
public extension UITableView {
|
||||||
|
func register<View>(view: View.Type) where View: UITableViewCell {
|
||||||
|
register(view.self, forCellReuseIdentifier: view.defaultReuseIdentifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerHeaderFooterView<View>(view: View.Type) where View: UITableViewHeaderFooterView {
|
||||||
|
register(view.self, forHeaderFooterViewReuseIdentifier: view.defaultReuseIdentifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dequeue<T>(type: T.Type, for indexPath: IndexPath) -> T where T: UITableViewCell {
|
||||||
|
let reuseIdentifier = T.defaultReuseIdentifier
|
||||||
|
return dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! T
|
||||||
|
}
|
||||||
|
|
||||||
|
func dequeueHeaderFooterView<T>(type: T.Type) -> T where T: UITableViewHeaderFooterView {
|
||||||
|
let reuseIdentifier = T.defaultReuseIdentifier
|
||||||
|
return dequeueReusableHeaderFooterView(withIdentifier: reuseIdentifier) as! T
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue