Supports removing inactive apps from My Apps

This commit is contained in:
Riley Testut
2020-05-16 15:34:10 -07:00
parent d8f1dcb032
commit 19bf19350e
3 changed files with 102 additions and 35 deletions

View File

@@ -22,6 +22,8 @@ extension UserDefaults
@NSManaged var legacySideloadedApps: [String]?
@NSManaged var isLegacyDeactivationSupported: Bool
var activeAppsLimit: Int? {
get {
return self._activeAppsLimit?.intValue
@@ -41,6 +43,9 @@ extension UserDefaults
func registerDefaults()
{
self.register(defaults: [#keyPath(UserDefaults.isBackgroundRefreshEnabled): true])
self.register(defaults: [
#keyPath(UserDefaults.isBackgroundRefreshEnabled): true,
#keyPath(UserDefaults.isLegacyDeactivationSupported): false
])
}
}

View File

@@ -344,6 +344,48 @@ extension AppManager
self.run([deactivateAppOperation], context: context, requiresSerialQueue: true)
}
func remove(_ installedApp: InstalledApp, completionHandler: @escaping (Result<Void, Error>) -> Void)
{
let authenticationContext = AuthenticatedOperationContext()
let appContext = InstallAppOperationContext(bundleIdentifier: installedApp.bundleIdentifier, authenticatedContext: authenticationContext)
appContext.installedApp = installedApp
let removeAppOperation = RSTAsyncBlockOperation { (operation) in
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
let installedApp = context.object(with: installedApp.objectID) as! InstalledApp
context.delete(installedApp)
do { try context.save() }
catch { appContext.error = error }
operation.finish()
}
}
let removeAppBackupOperation = RemoveAppBackupOperation(context: appContext)
removeAppBackupOperation.resultHandler = { (result) in
switch result
{
case .success: break
case .failure(let error): print("Failed to remove app backup.", error)
}
// Throw the error from removeAppOperation,
// since that's the error we really care about.
if let error = appContext.error
{
completionHandler(.failure(error))
}
else
{
completionHandler(.success(()))
}
}
removeAppBackupOperation.addDependency(removeAppOperation)
self.run([removeAppOperation, removeAppBackupOperation], context: authenticationContext)
}
func installationProgress(for app: AppProtocol) -> Progress?
{
let progress = self.installationProgress[app.bundleIdentifier]

View File

@@ -960,15 +960,31 @@ private extension MyAppsViewController
func remove(_ installedApp: InstalledApp)
{
let alertController = UIAlertController(title: nil, message: NSLocalizedString("Removing a sideloaded app only removes it from AltStore. You must also delete it from the home screen to fully uninstall the app.", comment: ""), preferredStyle: .actionSheet)
let title = String(format: NSLocalizedString("Remove “%@” from AltStore?", comment: ""), installedApp.name)
let message: String
if UserDefaults.standard.isLegacyDeactivationSupported
{
message = NSLocalizedString("You must also delete it from the home screen to fully uninstall the app.", comment: "")
}
else
{
message = NSLocalizedString("This will also erase all backup data for this app.", comment: "")
}
let alertController = UIAlertController(title: title, message: message, preferredStyle: .actionSheet)
alertController.addAction(.cancel)
alertController.addAction(UIAlertAction(title: NSLocalizedString("Remove", comment: ""), style: .destructive, handler: { (action) in
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
let installedApp = context.object(with: installedApp.objectID) as! InstalledApp
context.delete(installedApp)
do { try context.save() }
catch { print("Failed to remove sideloaded app.", error) }
AppManager.shared.remove(installedApp) { (result) in
switch result
{
case .success: break
case .failure(let error):
DispatchQueue.main.async {
let toastView = ToastView(error: error)
toastView.show(in: self)
}
}
}
}))
@@ -1161,39 +1177,43 @@ extension MyAppsViewController
self.remove(installedApp)
}
if installedApp.bundleIdentifier == StoreApp.altstoreAppID
guard installedApp.bundleIdentifier != StoreApp.altstoreAppID else {
return [refreshAction]
}
if installedApp.isActive
{
actions = [refreshAction]
actions.append(refreshAction)
actions.append(deactivateAction)
}
else
{
if installedApp.isActive
{
if UserDefaults.standard.activeAppsLimit != nil
{
actions = [refreshAction, deactivateAction]
}
else
{
actions = [refreshAction]
}
}
else
{
actions.append(activateAction)
}
#if DEBUG
actions.append(removeAction)
#else
if (UserDefaults.standard.legacySideloadedApps ?? []).contains(installedApp.bundleIdentifier)
{
// Only display option for legacy sideloaded apps.
actions.append(removeAction)
}
#endif
actions.append(activateAction)
}
#if DEBUG
if installedApp.bundleIdentifier != StoreApp.altstoreAppID
{
actions.append(removeAction)
}
#else
if (UserDefaults.standard.legacySideloadedApps ?? []).contains(installedApp.bundleIdentifier)
{
// Legacy sideloaded app, so can't detect if it's deleted.
actions.append(removeAction)
}
else if !UserDefaults.standard.isLegacyDeactivationSupported && !installedApp.isActive
{
// Inactive apps are actually deleted, so we need another way
// for user to remove them from AltStore.
actions.append(removeAction)
}
#endif
return actions
}