<commit_before>//
//  ViewController.swift
//  Flycut-iOS
//
//  Created by <NAME> on 7/12/17.
//
//

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, FlycutStoreDelegate, FlycutOperatorDelegate {

	let flycut:FlycutOperator = FlycutOperator()
	var activeUpdates:Int = 0
	var tableView:UITableView!
	var currentAnimation = UITableViewRowAnimation.none
	var pbCount:Int = -1
	var rememberedSyncSettings:Bool = false
	var rememberedSyncClippings:Bool = false

	let pasteboardInteractionQueue = DispatchQueue(label: "com.Flycut.pasteboardInteractionQueue")
	let alertHandlingSemaphore = DispatchSemaphore(value: 0)
	let defaultsChangeHandlingQueue = DispatchQueue(label: "com.Flycut.defaultsChangeHandlingQueue")

	let isURLDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)

	// Some buttons we will reuse.
	var deleteButton:MGSwipeButton? = nil
	var openURLButton:MGSwipeButton? = nil

	override func viewDidLoad() {
		super.viewDidLoad()
		// Do any additional setup after loading the view, typically from a nib.

		tableView = self.view.subviews.first as! UITableView
		tableView.delegate = self
		tableView.dataSource = self

		tableView.register(MGSwipeTableCell.self, forCellReuseIdentifier: "FlycutCell")

		deleteButton = MGSwipeButton(title: "Delete", backgroundColor: .red, callback: { (cell) -> Bool in
			let indexPath = self.tableView.indexPath(for: cell)
			if ( nil != indexPath ) {
				let previousAnimation = self.currentAnimation
				self.currentAnimation = UITableViewRowAnimation.left // Use .left to look better with swiping left to delete.
				self.flycut.setStackPositionTo( Int32((indexPath?.row)! ))
				self.flycut.clearItemAtStackPosition()
				self.currentAnimation = previousAnimation
			}

			return true;
		})

		openURLButton = MGSwipeButton(title: "Open", backgroundColor: .blue, callback: { (cell) -> Bool in
			let indexPath = self.tableView.indexPath(for: cell)
			if ( nil != indexPath ) {
				let url = URL(string: self.flycut.clippingString(withCount: Int32((indexPath?.row)!) )! )
				if #available(iOS 10.0, *) {
					UIApplication.shared.open(url!, options: [:], completionHandler: nil)
				} else {
					// Fallback on earlier versions
					UIApplication.shared.openURL(url!)
				}
				self.tableView.reloadRows(at: [indexPath!], with: UITableViewRowAnimation.none)
			}

			return true;
		})

		// Force sync disable for test if needed.
		//UserDefaults.standard.set(NSNumber(value: false), forKey: "syncSettingsViaICloud")
		//UserDefaults.standard.set(NSNumber(value: false), forKey: "syncClippingsViaICloud")
		// Force to ask to enable sync for test if needed.
		//UserDefaults.standard.set(false, forKey: "alreadyAskedToEnableSync")

		// Ensure these are false since there isn't a way to access the saved clippings on iOS as this point.
		UserDefaults.standard.set(NSNumber(value: false), forKey: "saveForgottenClippings")
		UserDefaults.standard.set(NSNumber(value: false), forKey: "saveForgottenFavorites")

		flycut.setClippingsStoreDelegate(self)
		flycut.delegate = self

		flycut.awake(fromNibDisplaying: 10, withDisplayLength: 140, withSave: #selector(savePreferences(toDict:)), forTarget: self) // The 10 isn't used in iOS right now and 140 characters seems to be enough to cover the width of the largest screen.

		NotificationCenter.default.addObserver(self, selector: #selector(self.checkForClippingAddedToClipboard), name: .UIPasteboardChanged, object: nil)

		NotificationCenter.default.addObserver(self, selector: #selector(self.applicationWillTerminate), name: .UIApplicationWillTerminate, object: nil)

		// Check for clipping whenever we become active.
		NotificationCenter.default.addObserver(self, selector: #selector(self.checkForClippingAddedToClipboard), name: .UIApplicationDidBecomeActive, object: nil)
		checkForClippingAddedToClipboard() // Since the first-launch notification will occur before we add observer.

		// Register for notifications for the scenarios in which we should save the engine.
		[ Notification.Name.UIApplicationWillResignActive,
		  Notification.Name.UIApplicationDidEnterBackground,
		  Notification.Name.UIApplicationWillTerminate ]
			.forEach { (notification) in
			NotificationCenter.default.addObserver(self,
			                                       selector: #selector(self.saveEngine),
			                                       name: notification,
			                                       object: nil)
		}

		NotificationCenter.default.addObserver(self, selector: #selector(self.defaultsChanged), name: UserDefaults.didChangeNotification, object: nil)
	}

	func defaultsChanged() {
		// This seems to be the only way to respond to Settings changes, though it doesn't inform us what changed so we will have to check each to see if they were the one(s).

		// Don't use DispatchQueue.main.async since that will still end up blocking the UI draw until the user responds to what hasn't been drawn yet.
		// Use async on a sequential queue to avoid concurrent response to the same change.  This allows enqueuing of defaultsChanged calls in reponse to changes made within the handling, but using sync causes EXC_BAD_ACCESS in this case.
		defaultsChangeHandlingQueue.async {
			let newRememberNum = Int32(UserDefaults.standard.integer(forKey: "rememberNum"))
			if ( UserDefaults.standard.value(forKey: "rememberNum") is String )<commit_msg>Add demo content for App Store screenshot use.<commit_after>//
//  ViewController.swift
//  Flycut-iOS
//
//  Created by <NAME> on 7/12/17.
//
//

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, FlycutStoreDelegate, FlycutOperatorDelegate {

	let flycut:FlycutOperator = FlycutOperator()
	var activeUpdates:Int = 0
	var tableView:UITableView!
	var currentAnimation = UITableViewRowAnimation.none
	var pbCount:Int = -1
	var rememberedSyncSettings:Bool = false
	var rememberedSyncClippings:Bool = false

	let pasteboardInteractionQueue = DispatchQueue(label: "com.Flycut.pasteboardInteractionQueue")
	let alertHandlingSemaphore = DispatchSemaphore(value: 0)
	let defaultsChangeHandlingQueue = DispatchQueue(label: "com.Flycut.defaultsChangeHandlingQueue")

	let isURLDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)

	// Some buttons we will reuse.
	var deleteButton:MGSwipeButton? = nil
	var openURLButton:MGSwipeButton? = nil

	override func viewDidLoad() {
		super.viewDidLoad()
		// Do any additional setup after loading the view, typically from a nib.

		// Uncomment the following line to load the demo state for screenshots.
		//UserDefaults.standard.set(NSNumber(value: true), forKey: "demoForAppStoreScreenshots")
		// Use this command to get screenshots:
		// while true; do xcrun simctl io booted screenshot;sleep 1;done

		if ( UserDefaults.standard.bool(forKey: "demoForAppStoreScreenshots") )
		{
			// Ensure we will not load or save clippings in demo mode.
			let savePref = UserDefaults.standard.integer(forKey: "savePreference")
			if ( 0 < savePref )
			{
				UserDefaults.standard.set(0, forKey: "savePreference")
			}
		}

		tableView = self.view.subviews.first as! UITableView
		tableView.delegate = self
		tableView.dataSource = self

		tableView.register(MGSwipeTableCell.self, forCellReuseIdentifier: "FlycutCell")

		deleteButton = MGSwipeButton(title: "Delete", backgroundColor: .red, callback: { (cell) -> Bool in
			let indexPath = self.tableView.indexPath(for: cell)
			if ( nil != indexPath ) {
				let previousAnimation = self.currentAnimation
				self.currentAnimation = UITableViewRowAnimation.left // Use .left to look better with swiping left to delete.
				self.flycut.setStackPositionTo( Int32((indexPath?.row)! ))
				self.flycut.clearItemAtStackPosition()
				self.currentAnimation = previousAnimation
			}

			return true;
		})

		openURLButton = MGSwipeButton(title: "Open", backgroundColor: .blue, callback: { (cell) -> Bool in
			let indexPath = self.tableView.indexPath(for: cell)
			if ( nil != indexPath ) {
				let url = URL(string: self.flycut.clippingString(withCount: Int32((indexPath?.row)!) )! )
				if #available(iOS 10.0, *) {
					UIApplication.shared.open(url!, options: [:], completionHandler: nil)
				} else {
					// Fallback on earlier versions
					UIApplication.shared.openURL(url!)
				}
				self.tableView.reloadRows(at: [indexPath!], with: UITableViewRowAnimation.none)
			}

			return true;
		})

		// Force sync disable for test if needed.
		//UserDefaults.standard.set(NSNumber(value: false), forKey: "syncSettingsViaICloud")
		//UserDefaults.standard.set(NSNumber(value: false), forKey: "syncClippingsViaICloud")
		// Force to ask to enable sync for test if needed.
		//UserDefaults.standard.set(false, forKey: "alreadyAskedToEnableSync")

		// Ensure these are false since there isn't a way to access the saved clippings on iOS as this point.
		UserDefaults.standard.set(NSNumber(value: false), forKey: "saveForgottenClippings")
		UserDefaults.standard.set(NSNumber(value: false), forKey: "saveForgottenFavorites")

		flycut.setClippingsStoreDelegate(self)
		flycut.delegate = self

		flycut.awake(fromNibDisplaying: 10, withDisplayLength: 140, withSave: #selector(savePreferences(toDict:)), forTarget: self) // The 10 isn't used in iOS right now and 140 characters seems to be enough to cover the width of the largest screen.

		NotificationCenter.default.addObserver(self, selector: #selector(self.checkForClippingAddedToClipboard), name: .UIPasteboardChanged, object: nil)

		NotificationCenter.default.addObserver(self, selector: #selector(self.applicationWillTerminate), name: .UIApplicationWillTerminate, object: nil)

		// Check for clipping whenever we become active.
		NotificationCenter.default.addObserver(self, selector: #selector(self.checkForClippingAddedToClipboard), name: .UIApplicationDidBecomeActive, object: nil)
		checkForClippingAddedToClipboard() // Since the first-launch notification will occur before we add observer.

		// Register for notifications for the scenarios in which we should save the engine.
		[ Notification.Name.UIApplicationWillResignActive,
		  Notification.Name.UIApplicationDidEnterBackground,
		  Notification.Name.UIApplicationWillTerminate ]
			.forEach { (notification) in
			NotificationCenter.default.addObserver(self,
			                                       selector: #selector(self.saveEngine),
			                                       name: notification,
			                                       object: nil)
		}

		NotificationCenter.default.addObserver(self, selector: #selector(self.defaultsChanged), name: UserDefaults.didChangeNotification, object: nil)

		if ( UserDefaults.standard.bool(forKey: "demoForAppStoreScreenshots") )
		{
			// Make sure we won't send these change to iCloud.
			UserDefaults.standard.set(NSNumber(value: false), forKey: "syncSettingsViaICloud")
			UserDefaults.standard.set(NSNumber(value: false), forKey: "syncClippingsViaICloud")
			self.flycut.registerOrDeregisterICloudSync()
			NotificationCenter.default.removeObserver(self)

			// Load sample content, reverse order.
			self.flycut.addClipping("https://www.apple.<EMAIL>", ofType: "public.text", fromApp: "iOS", withAppBundleURL: "iOS", target: nil, clippingAddedSelector: nil)
			self.flycut.addClipping("App Store is a digital distribution platform, developed and maintained by Apple Inc., for mobile apps on its iOS operating system.", ofType: "public.text", fromApp: "iOS", withAppBundleURL: "iOS", target: nil, clippingAddedSelector: nil)
			self.flycut.addClipping("https://itunesconnect.apple.com/", ofType: "public.text", fromApp: "iOS", withAppBundleURL: "iOS", target: nil, clippingAddedSelector: nil)
			self.flycut.addClipping("The party is at 123 Main St. 6 PM. Please bring some chips to share.", ofType: "public.text", fromApp: "iOS", withAppBundleURL: "iOS", target: nil, clippingAddedSelector: nil)
			self.flycut.addClipping("You are going to love this new design I found. It takes half the effort and resonates with today's hottest trends. With our throughput up we can now keep up with demand.", ofType: "public.text", fromApp: "iOS", withAppBundleURL: "iOS", target: nil, clippingAddedSelector: nil)
			self.flycut.addClipping("http://www.makeuseof.com/tag/5-best-mac-clipboard-manager-apps-improve-workflow/", ofType: "public.text", fromApp: "iOS", withAppBundleURL: "iOS", target: nil, clippingAddedSelector: nil)
			self.flycut.addClipping("Swipe left to delete", ofType: "public.text", fromApp: "iOS", withAppBundleURL: "iOS", target: nil, clippingAddedSelector: nil)
			self.flycut.addClipping("Swipe right to open web links", ofType: "public.text", fromApp: "iOS", withAppBundleURL: "iOS", target: nil, clippingAddedSelector: nil)
			self.flycut.addClipping("Tap to copy", ofType: "public.text", fromApp: "iOS", withAppBundleURL: "iOS", target: nil, clippingAddedSelector: nil)
			self.flycut.addClipping("Manage your clippings in iOS", ofType: "public.text", fromApp: "iOS", withAppBundleURL: "iOS", target: nil, clippingAddedSelector: nil)
			self.flycut.addClipping("Flycut has made the leap from macOS to iOS", ofType: "public.text", fromApp: "iOS", withAppBundleURL: "iOS", target: nil, clippingAddedSelector: nil)
			self.flycut.addClipping("Flycut has made the leap from OS X to iOS", ofType: "public.text", fromApp: "iOS", withAppBundleURL: "iOS", target: nil, clippingAddedSelector: nil)

			// Unset the demo setting.
			UserDefaults.standard.set(NSNumber(value: false), forKey: "demoForAppStoreScreenshots")
		}
	}

	func defaultsChanged() {
		// This seems to be the only way to respond to Settings changes, though it doesn't inform us what changed so we will have to check each to see if they were the one(s).

		// Don't use DispatchQueue.main.async since that will still end up blocking the UI draw until the user responds to what hasn't been drawn yet.
		// Use async on a sequential queue to avoid concurrent response to the same change.  This allows enqueuing of defaultsChanged calls in reponse to changes made within the handling, but using sync causes EXC_BAD_ACCESS in this case.
		defaultsChangeHandlingQueue.async {
			let newRememberNum = Int32(UserDefaults.standard.integer(forKey: "rememberNum"))
			if ( UserDefaults.standard.value(forKey: "rememberNum") is String )
{'fraction_non_alphanumeric': 0.09906646231015745, 'fraction_numerical': 0.004319353490316288, 'mean_word_length': 4.553191489361702, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 10, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5977884', 'n_tokens_mistral': 4633, 'n_tokens_neox': 4075, 'n_words': 1194}