1 // Copyright 2015-present 650 Industries. All rights reserved.
2 
3 import Foundation
4 import EXManifests
5 
6 let RECENTLY_OPENED_APPS_REGISTRY_KEY = "expo.devlauncher.recentlyopenedapps"
7 
8 let TIME_TO_REMOVE = 1_000 * 60 * 60 * 24 * 3 // 3 days
9 
10 @objc(EXDevLauncherRecentlyOpenedAppsRegistry)
11 public class EXDevLauncherRecentlyOpenedAppsRegistry: NSObject {
12   private var appRegistry: [String: Any] {
13     get {
14       return UserDefaults.standard.dictionary(forKey: RECENTLY_OPENED_APPS_REGISTRY_KEY) ?? [String: Any]()
15     }
16     set (newAppRegistry) {
17       UserDefaults.standard.set(newAppRegistry, forKey: RECENTLY_OPENED_APPS_REGISTRY_KEY)
18     }
19   }
20 
21   @objc
appWasOpenednull22   public func appWasOpened(_ url: String, queryParams: [String: String], manifest: Manifest?) {
23     var appEntry: [String: Any] = [:]
24 
25     // reloading the same url - update the old entry w/ any new fields instead of creating a new one
26     if let previousMatchingEntry = appRegistry[url] {
27       appEntry = previousMatchingEntry as! [String : Any]
28     }
29 
30     let timestamp = getCurrentTimestamp()
31 
32     var isEASUpdate = false
33 
34     if let host = URL(string:url)?.host {
35       isEASUpdate = host == "u.expo.dev" || host == "staging-u.expo.dev"
36     }
37 
38     appEntry["isEASUpdate"] = isEASUpdate
39 
40     if isEASUpdate {
41       if let updateMessage = queryParams["updateMessage"] {
42         appEntry["updateMessage"] = updateMessage
43       }
44     }
45 
46     if let manifest = manifest {
47       appEntry["name"] = manifest.name()
48 
49       // TODO - expose metadata object in expo-manifests
50       let json = manifest.rawManifestJSON()
51 
52       if (isEASUpdate) {
53         if let metadata: [String: Any] = json["metadata"] as? [String : Any], let branchName = metadata["branchName"] {
54           appEntry["branchName"] = branchName;
55         }
56       }
57     }
58 
59     appEntry["timestamp"] = timestamp
60     appEntry["url"] = url
61 
62     var registry = appRegistry
63     registry[url] = appEntry
64     appRegistry = registry
65   }
66 
67   @objc
recentlyOpenedAppsnull68   public func recentlyOpenedApps() -> [[String: Any]] {
69     guard let registry = appRegistry as? [String: [String: Any]] else {
70       return []
71     }
72 
73     var apps: [[String: Any]] = []
74 
75     appRegistry = registry.filter { (url: String, appEntry: [String: Any]) in
76       if getCurrentTimestamp() - (appEntry["timestamp"] as! Int64) > TIME_TO_REMOVE {
77         return false
78       }
79 
80       apps.append(appEntry)
81       return true
82     }
83 
84     return apps
85   }
86 
87   @objc
mostRecentAppnull88   public func mostRecentApp() -> [String: Any]? {
89     let recentlyOpenedApps = self.recentlyOpenedApps()
90 
91     if let mostRecentApp = recentlyOpenedApps.max(by: { ($0["timestamp"] as? Int64) ?? 0 < ($1["timestamp"] as? Int64) ?? 0 }) {
92       return mostRecentApp
93     }
94 
95     return nil
96   }
97 
98   @objc
clearRegistrynull99   public func clearRegistry() {
100     resetStorage()
101   }
102 
getCurrentTimestampnull103   func getCurrentTimestamp() -> Int64 {
104     return Int64(Date().timeIntervalSince1970 * 1_000)
105   }
106 
resetStoragenull107   func resetStorage() {
108     UserDefaults.standard.removeObject(forKey: RECENTLY_OPENED_APPS_REGISTRY_KEY)
109   }
110 }
111