├── .gitignore ├── ExtensionList.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── ExtensionList ├── ELExtensionTableDataSource.h ├── ELExtensionTableDataSource.m ├── ELValueCell.h ├── ELValueCell.m ├── ExtensionList-Prefix.pch ├── ExtensionList.h ├── ExtensionList.xm ├── Package │ ├── DEBIAN │ │ └── control │ └── Library │ │ └── MobileSubstrate │ │ └── DynamicLibraries │ │ ├── ExtensionList.dylib │ │ └── ExtensionList.plist └── PackageVersion.plist ├── ExtensionListSettings ├── ELExtensionPreferenceViewController.h ├── ELExtensionPreferenceViewController.m ├── ExtensionListSettings-Info.plist ├── ExtensionListSettings-Prefix.pch ├── Package │ ├── DEBIAN │ ├── Library │ └── System │ │ └── Library │ │ └── PreferenceBundles │ │ └── ExtensionListSettings.bundle │ │ ├── ExtensionListSettings │ │ └── Info.plist ├── PackageVersion.plist └── preferenceloader ├── LSApplicationWorkspace.h ├── LSPlugInKitProxy.h ├── Preferences └── Preferences.h └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.mm 2 | xcuserdata 3 | *.xcuserdatad 4 | xcshareddata 5 | LatestBuild 6 | Packages -------------------------------------------------------------------------------- /ExtensionList.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 433453261E769F5B00769232 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 433453251E769F5B00769232 /* Foundation.framework */; }; 11 | 433453311E769F5B00769232 /* ExtensionList.mm in Sources */ = {isa = PBXBuildFile; fileRef = 433453301E769F5B00769232 /* ExtensionList.mm */; }; 12 | 433453411E769F8F00769232 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 433453251E769F5B00769232 /* Foundation.framework */; }; 13 | 433453431E769F8F00769232 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 433453421E769F8F00769232 /* UIKit.framework */; }; 14 | 433453511E769F8F00769232 /* ELExtensionPreferenceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 433453501E769F8F00769232 /* ELExtensionPreferenceViewController.m */; }; 15 | 433453651E76A05800769232 /* Preferences.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 433453641E76A05800769232 /* Preferences.framework */; }; 16 | 4334536A1E76A43700769232 /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 433453681E76A43600769232 /* AdSupport.framework */; }; 17 | 4334536B1E76A43700769232 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 433453691E76A43700769232 /* MobileCoreServices.framework */; }; 18 | 4334536D1E76A44D00769232 /* CydiaSubstrate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4334536C1E76A44D00769232 /* CydiaSubstrate.framework */; }; 19 | 4334536F1E76A45B00769232 /* libMobileGestalt.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4334536E1E76A45B00769232 /* libMobileGestalt.tbd */; }; 20 | 433453711E76A45E00769232 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 433453701E76A45E00769232 /* CoreGraphics.framework */; }; 21 | 433453731E76A46300769232 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 433453721E76A46300769232 /* QuartzCore.framework */; }; 22 | 433453741E76A46700769232 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 433453421E769F8F00769232 /* UIKit.framework */; }; 23 | 433453751E76A48500769232 /* ExtensionList.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 433453221E769F5B00769232 /* ExtensionList.dylib */; }; 24 | 433453761E76A49200769232 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 433453701E76A45E00769232 /* CoreGraphics.framework */; }; 25 | 4334537F1E77C31F00769232 /* ELValueCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 4334537D1E77C31F00769232 /* ELValueCell.h */; }; 26 | 433453801E77C31F00769232 /* ELValueCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4334537E1E77C31F00769232 /* ELValueCell.m */; }; 27 | 433453831E77C34700769232 /* ELExtensionTableDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 433453811E77C34700769232 /* ELExtensionTableDataSource.h */; }; 28 | 433453841E77C34700769232 /* ELExtensionTableDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 433453821E77C34700769232 /* ELExtensionTableDataSource.m */; }; 29 | 435DC2761E791444006875BA /* ELExtensionPreferenceViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4334534E1E769F8F00769232 /* ELExtensionPreferenceViewController.h */; }; 30 | 435DC2771E792041006875BA /* ExtensionList.h in Headers */ = {isa = PBXBuildFile; fileRef = 433453661E76A0E500769232 /* ExtensionList.h */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 433453221E769F5B00769232 /* ExtensionList.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = ExtensionList.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 433453251E769F5B00769232 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 36 | 4334532B1E769F5B00769232 /* control */ = {isa = PBXFileReference; lastKnownFileType = text; name = control; path = Package/DEBIAN/control; sourceTree = ""; }; 37 | 4334532D1E769F5B00769232 /* PackageVersion.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = PackageVersion.plist; sourceTree = ""; }; 38 | 4334532E1E769F5B00769232 /* ExtensionList-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ExtensionList-Prefix.pch"; sourceTree = ""; }; 39 | 4334532F1E769F5B00769232 /* ExtensionList.xm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ExtensionList.xm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 40 | 433453301E769F5B00769232 /* ExtensionList.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ExtensionList.mm; sourceTree = ""; }; 41 | 433453351E769F5B00769232 /* ExtensionList.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = ExtensionList.plist; path = Package/Library/MobileSubstrate/DynamicLibraries/ExtensionList.plist; sourceTree = ""; }; 42 | 433453401E769F8F00769232 /* ExtensionListSettings.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExtensionListSettings.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 433453421E769F8F00769232 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 44 | 4334534C1E769F8F00769232 /* PackageVersion.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = PackageVersion.plist; sourceTree = ""; }; 45 | 4334534D1E769F8F00769232 /* ExtensionListSettings-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ExtensionListSettings-Info.plist"; sourceTree = ""; }; 46 | 4334534E1E769F8F00769232 /* ELExtensionPreferenceViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ELExtensionPreferenceViewController.h; sourceTree = ""; }; 47 | 433453501E769F8F00769232 /* ELExtensionPreferenceViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ELExtensionPreferenceViewController.m; sourceTree = ""; }; 48 | 433453601E769F8F00769232 /* ExtensionListSettings-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ExtensionListSettings-Prefix.pch"; sourceTree = ""; }; 49 | 433453641E76A05800769232 /* Preferences.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Preferences.framework; path = System/Library/PrivateFrameworks/Preferences.framework; sourceTree = SDKROOT; }; 50 | 433453661E76A0E500769232 /* ExtensionList.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExtensionList.h; sourceTree = ""; }; 51 | 433453681E76A43600769232 /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; }; 52 | 433453691E76A43700769232 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; 53 | 4334536C1E76A44D00769232 /* CydiaSubstrate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CydiaSubstrate.framework; path = /Users/duyongchao/Desktop/AppList2/../../../../opt/iOSOpenDev/frameworks/CydiaSubstrate.framework; sourceTree = ""; }; 54 | 4334536E1E76A45B00769232 /* libMobileGestalt.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libMobileGestalt.tbd; path = usr/lib/libMobileGestalt.tbd; sourceTree = SDKROOT; }; 55 | 433453701E76A45E00769232 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 56 | 433453721E76A46300769232 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 57 | 4334537D1E77C31F00769232 /* ELValueCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELValueCell.h; sourceTree = ""; }; 58 | 4334537E1E77C31F00769232 /* ELValueCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELValueCell.m; sourceTree = ""; }; 59 | 433453811E77C34700769232 /* ELExtensionTableDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELExtensionTableDataSource.h; sourceTree = ""; }; 60 | 433453821E77C34700769232 /* ELExtensionTableDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELExtensionTableDataSource.m; sourceTree = ""; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | 4334531E1E769F5B00769232 /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | 4334536A1E76A43700769232 /* AdSupport.framework in Frameworks */, 69 | 4334536F1E76A45B00769232 /* libMobileGestalt.tbd in Frameworks */, 70 | 4334536B1E76A43700769232 /* MobileCoreServices.framework in Frameworks */, 71 | 433453261E769F5B00769232 /* Foundation.framework in Frameworks */, 72 | 433453711E76A45E00769232 /* CoreGraphics.framework in Frameworks */, 73 | 433453731E76A46300769232 /* QuartzCore.framework in Frameworks */, 74 | 433453741E76A46700769232 /* UIKit.framework in Frameworks */, 75 | 4334536D1E76A44D00769232 /* CydiaSubstrate.framework in Frameworks */, 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | 4334533C1E769F8F00769232 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | 433453431E769F8F00769232 /* UIKit.framework in Frameworks */, 84 | 433453411E769F8F00769232 /* Foundation.framework in Frameworks */, 85 | 433453651E76A05800769232 /* Preferences.framework in Frameworks */, 86 | 433453761E76A49200769232 /* CoreGraphics.framework in Frameworks */, 87 | 433453751E76A48500769232 /* ExtensionList.dylib in Frameworks */, 88 | ); 89 | runOnlyForDeploymentPostprocessing = 0; 90 | }; 91 | /* End PBXFrameworksBuildPhase section */ 92 | 93 | /* Begin PBXGroup section */ 94 | 433453171E769F5B00769232 = { 95 | isa = PBXGroup; 96 | children = ( 97 | 433453271E769F5B00769232 /* ExtensionList */, 98 | 433453461E769F8F00769232 /* ExtensionListSettings */, 99 | 433453241E769F5B00769232 /* Frameworks */, 100 | 433453231E769F5B00769232 /* Products */, 101 | ); 102 | sourceTree = ""; 103 | }; 104 | 433453231E769F5B00769232 /* Products */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 433453221E769F5B00769232 /* ExtensionList.dylib */, 108 | 433453401E769F8F00769232 /* ExtensionListSettings.bundle */, 109 | ); 110 | name = Products; 111 | sourceTree = ""; 112 | }; 113 | 433453241E769F5B00769232 /* Frameworks */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 433453721E76A46300769232 /* QuartzCore.framework */, 117 | 433453701E76A45E00769232 /* CoreGraphics.framework */, 118 | 4334536E1E76A45B00769232 /* libMobileGestalt.tbd */, 119 | 4334536C1E76A44D00769232 /* CydiaSubstrate.framework */, 120 | 433453681E76A43600769232 /* AdSupport.framework */, 121 | 433453691E76A43700769232 /* MobileCoreServices.framework */, 122 | 433453641E76A05800769232 /* Preferences.framework */, 123 | 433453251E769F5B00769232 /* Foundation.framework */, 124 | 433453421E769F8F00769232 /* UIKit.framework */, 125 | ); 126 | name = Frameworks; 127 | sourceTree = ""; 128 | }; 129 | 433453271E769F5B00769232 /* ExtensionList */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 433453661E76A0E500769232 /* ExtensionList.h */, 133 | 4334532F1E769F5B00769232 /* ExtensionList.xm */, 134 | 433453301E769F5B00769232 /* ExtensionList.mm */, 135 | 4334537D1E77C31F00769232 /* ELValueCell.h */, 136 | 4334537E1E77C31F00769232 /* ELValueCell.m */, 137 | 433453811E77C34700769232 /* ELExtensionTableDataSource.h */, 138 | 433453821E77C34700769232 /* ELExtensionTableDataSource.m */, 139 | 433453281E769F5B00769232 /* Package */, 140 | 4334532C1E769F5B00769232 /* Supporting Files */, 141 | ); 142 | path = ExtensionList; 143 | sourceTree = ""; 144 | }; 145 | 433453281E769F5B00769232 /* Package */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 433453291E769F5B00769232 /* DEBIAN */, 149 | 433453321E769F5B00769232 /* Library */, 150 | ); 151 | name = Package; 152 | sourceTree = ""; 153 | }; 154 | 433453291E769F5B00769232 /* DEBIAN */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 4334532B1E769F5B00769232 /* control */, 158 | ); 159 | name = DEBIAN; 160 | sourceTree = ""; 161 | }; 162 | 4334532C1E769F5B00769232 /* Supporting Files */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 4334532D1E769F5B00769232 /* PackageVersion.plist */, 166 | 4334532E1E769F5B00769232 /* ExtensionList-Prefix.pch */, 167 | ); 168 | name = "Supporting Files"; 169 | sourceTree = ""; 170 | }; 171 | 433453321E769F5B00769232 /* Library */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | 433453331E769F5B00769232 /* MobileSubstrate */, 175 | ); 176 | name = Library; 177 | sourceTree = ""; 178 | }; 179 | 433453331E769F5B00769232 /* MobileSubstrate */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 433453341E769F5B00769232 /* DynamicLibraries */, 183 | ); 184 | name = MobileSubstrate; 185 | sourceTree = ""; 186 | }; 187 | 433453341E769F5B00769232 /* DynamicLibraries */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | 433453351E769F5B00769232 /* ExtensionList.plist */, 191 | ); 192 | name = DynamicLibraries; 193 | sourceTree = ""; 194 | }; 195 | 433453461E769F8F00769232 /* ExtensionListSettings */ = { 196 | isa = PBXGroup; 197 | children = ( 198 | 4334534E1E769F8F00769232 /* ELExtensionPreferenceViewController.h */, 199 | 433453501E769F8F00769232 /* ELExtensionPreferenceViewController.m */, 200 | 433453471E769F8F00769232 /* Package */, 201 | 4334534B1E769F8F00769232 /* Supporting Files */, 202 | ); 203 | path = ExtensionListSettings; 204 | sourceTree = ""; 205 | }; 206 | 433453471E769F8F00769232 /* Package */ = { 207 | isa = PBXGroup; 208 | children = ( 209 | 4334537A1E77BF9800769232 /* System */, 210 | ); 211 | name = Package; 212 | sourceTree = ""; 213 | }; 214 | 4334534B1E769F8F00769232 /* Supporting Files */ = { 215 | isa = PBXGroup; 216 | children = ( 217 | 4334534C1E769F8F00769232 /* PackageVersion.plist */, 218 | 4334534D1E769F8F00769232 /* ExtensionListSettings-Info.plist */, 219 | 433453601E769F8F00769232 /* ExtensionListSettings-Prefix.pch */, 220 | ); 221 | name = "Supporting Files"; 222 | sourceTree = ""; 223 | }; 224 | 4334537A1E77BF9800769232 /* System */ = { 225 | isa = PBXGroup; 226 | children = ( 227 | 4334537B1E77BF9E00769232 /* Library */, 228 | ); 229 | name = System; 230 | sourceTree = ""; 231 | }; 232 | 4334537B1E77BF9E00769232 /* Library */ = { 233 | isa = PBXGroup; 234 | children = ( 235 | 4334537C1E77BFA500769232 /* PreferenceBundles */, 236 | ); 237 | name = Library; 238 | sourceTree = ""; 239 | }; 240 | 4334537C1E77BFA500769232 /* PreferenceBundles */ = { 241 | isa = PBXGroup; 242 | children = ( 243 | ); 244 | name = PreferenceBundles; 245 | sourceTree = ""; 246 | }; 247 | /* End PBXGroup section */ 248 | 249 | /* Begin PBXHeadersBuildPhase section */ 250 | 4334531F1E769F5B00769232 /* Headers */ = { 251 | isa = PBXHeadersBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | 435DC2771E792041006875BA /* ExtensionList.h in Headers */, 255 | 4334537F1E77C31F00769232 /* ELValueCell.h in Headers */, 256 | 433453831E77C34700769232 /* ELExtensionTableDataSource.h in Headers */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | 4334533D1E769F8F00769232 /* Headers */ = { 261 | isa = PBXHeadersBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | 435DC2761E791444006875BA /* ELExtensionPreferenceViewController.h in Headers */, 265 | ); 266 | runOnlyForDeploymentPostprocessing = 0; 267 | }; 268 | /* End PBXHeadersBuildPhase section */ 269 | 270 | /* Begin PBXNativeTarget section */ 271 | 433453211E769F5B00769232 /* ExtensionList */ = { 272 | isa = PBXNativeTarget; 273 | buildConfigurationList = 433453381E769F5B00769232 /* Build configuration list for PBXNativeTarget "ExtensionList" */; 274 | buildPhases = ( 275 | 4334531C1E769F5B00769232 /* ShellScript */, 276 | 4334531D1E769F5B00769232 /* Sources */, 277 | 4334531E1E769F5B00769232 /* Frameworks */, 278 | 4334531F1E769F5B00769232 /* Headers */, 279 | 433453201E769F5B00769232 /* ShellScript */, 280 | ); 281 | buildRules = ( 282 | ); 283 | dependencies = ( 284 | ); 285 | name = ExtensionList; 286 | productName = ExtensionList; 287 | productReference = 433453221E769F5B00769232 /* ExtensionList.dylib */; 288 | productType = "com.apple.product-type.library.dynamic"; 289 | }; 290 | 4334533F1E769F8F00769232 /* ExtensionListSettings */ = { 291 | isa = PBXNativeTarget; 292 | buildConfigurationList = 433453611E769F8F00769232 /* Build configuration list for PBXNativeTarget "ExtensionListSettings" */; 293 | buildPhases = ( 294 | 4334533B1E769F8F00769232 /* Sources */, 295 | 4334533C1E769F8F00769232 /* Frameworks */, 296 | 4334533D1E769F8F00769232 /* Headers */, 297 | 4334533E1E769F8F00769232 /* ShellScript */, 298 | ); 299 | buildRules = ( 300 | ); 301 | dependencies = ( 302 | ); 303 | name = ExtensionListSettings; 304 | productName = ExtensionListSettings; 305 | productReference = 433453401E769F8F00769232 /* ExtensionListSettings.bundle */; 306 | productType = "com.apple.product-type.bundle"; 307 | }; 308 | /* End PBXNativeTarget section */ 309 | 310 | /* Begin PBXProject section */ 311 | 433453181E769F5B00769232 /* Project object */ = { 312 | isa = PBXProject; 313 | attributes = { 314 | LastUpgradeCheck = 0820; 315 | TargetAttributes = { 316 | 433453211E769F5B00769232 = { 317 | CreatedOnToolsVersion = 8.2.1; 318 | DevelopmentTeam = 49NAE8LY4S; 319 | ProvisioningStyle = Automatic; 320 | }; 321 | 4334533F1E769F8F00769232 = { 322 | CreatedOnToolsVersion = 8.2.1; 323 | DevelopmentTeam = 49NAE8LY4S; 324 | ProvisioningStyle = Automatic; 325 | }; 326 | }; 327 | }; 328 | buildConfigurationList = 4334531B1E769F5B00769232 /* Build configuration list for PBXProject "ExtensionList" */; 329 | compatibilityVersion = "Xcode 3.2"; 330 | developmentRegion = English; 331 | hasScannedForEncodings = 0; 332 | knownRegions = ( 333 | en, 334 | ); 335 | mainGroup = 433453171E769F5B00769232; 336 | productRefGroup = 433453231E769F5B00769232 /* Products */; 337 | projectDirPath = ""; 338 | projectRoot = ""; 339 | targets = ( 340 | 433453211E769F5B00769232 /* ExtensionList */, 341 | 4334533F1E769F8F00769232 /* ExtensionListSettings */, 342 | ); 343 | }; 344 | /* End PBXProject section */ 345 | 346 | /* Begin PBXShellScriptBuildPhase section */ 347 | 4334531C1E769F5B00769232 /* ShellScript */ = { 348 | isa = PBXShellScriptBuildPhase; 349 | buildActionMask = 2147483647; 350 | files = ( 351 | ); 352 | inputPaths = ( 353 | ); 354 | outputPaths = ( 355 | ); 356 | runOnlyForDeploymentPostprocessing = 0; 357 | shellPath = /bin/sh; 358 | shellScript = "/opt/iOSOpenDev/bin/iosod --xcbp-logos"; 359 | }; 360 | 433453201E769F5B00769232 /* ShellScript */ = { 361 | isa = PBXShellScriptBuildPhase; 362 | buildActionMask = 2147483647; 363 | files = ( 364 | ); 365 | inputPaths = ( 366 | ); 367 | outputPaths = ( 368 | ); 369 | runOnlyForDeploymentPostprocessing = 0; 370 | shellPath = /bin/sh; 371 | shellScript = "/opt/iOSOpenDev/bin/iosod --xcbp"; 372 | }; 373 | 4334533E1E769F8F00769232 /* ShellScript */ = { 374 | isa = PBXShellScriptBuildPhase; 375 | buildActionMask = 2147483647; 376 | files = ( 377 | ); 378 | inputPaths = ( 379 | ); 380 | outputPaths = ( 381 | ); 382 | runOnlyForDeploymentPostprocessing = 0; 383 | shellPath = /bin/sh; 384 | shellScript = "/opt/iOSOpenDev/bin/iosod --xcbp"; 385 | }; 386 | /* End PBXShellScriptBuildPhase section */ 387 | 388 | /* Begin PBXSourcesBuildPhase section */ 389 | 4334531D1E769F5B00769232 /* Sources */ = { 390 | isa = PBXSourcesBuildPhase; 391 | buildActionMask = 2147483647; 392 | files = ( 393 | 433453801E77C31F00769232 /* ELValueCell.m in Sources */, 394 | 433453841E77C34700769232 /* ELExtensionTableDataSource.m in Sources */, 395 | 433453311E769F5B00769232 /* ExtensionList.mm in Sources */, 396 | ); 397 | runOnlyForDeploymentPostprocessing = 0; 398 | }; 399 | 4334533B1E769F8F00769232 /* Sources */ = { 400 | isa = PBXSourcesBuildPhase; 401 | buildActionMask = 2147483647; 402 | files = ( 403 | 433453511E769F8F00769232 /* ELExtensionPreferenceViewController.m in Sources */, 404 | ); 405 | runOnlyForDeploymentPostprocessing = 0; 406 | }; 407 | /* End PBXSourcesBuildPhase section */ 408 | 409 | /* Begin XCBuildConfiguration section */ 410 | 433453361E769F5B00769232 /* Debug */ = { 411 | isa = XCBuildConfiguration; 412 | buildSettings = { 413 | COPY_PHASE_STRIP = NO; 414 | EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES = "*.nib *.lproj *.gch (*) .DS_Store CVS .svn .git .hg *.xcodeproj *.xcode *.pbproj *.pbxproj"; 415 | FRAMEWORK_SEARCH_PATHS = ( 416 | "$(iOSOpenDevPath)/frameworks/**", 417 | "$(SDKROOT)/System/Library/PrivateFrameworks", 418 | ); 419 | GCC_C_LANGUAGE_STANDARD = gnu99; 420 | GCC_DYNAMIC_NO_PIC = NO; 421 | GCC_OPTIMIZATION_LEVEL = 0; 422 | GCC_PREPROCESSOR_DEFINITIONS = ( 423 | "DEBUG=1", 424 | "$(inherited)", 425 | ); 426 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 427 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 428 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 429 | GCC_WARN_UNUSED_VARIABLE = YES; 430 | HEADER_SEARCH_PATHS = "$(iOSOpenDevPath)/include/**"; 431 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 432 | LIBRARY_SEARCH_PATHS = "$(iOSOpenDevPath)/lib/**"; 433 | SDKROOT = iphoneos9.2; 434 | TARGETED_DEVICE_FAMILY = "1,2"; 435 | VALIDATE_PRODUCT = NO; 436 | iOSOpenDevPath = /opt/iOSOpenDev; 437 | }; 438 | name = Debug; 439 | }; 440 | 433453371E769F5B00769232 /* Release */ = { 441 | isa = XCBuildConfiguration; 442 | buildSettings = { 443 | COPY_PHASE_STRIP = YES; 444 | EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES = "*.nib *.lproj *.gch (*) .DS_Store CVS .svn .git .hg *.xcodeproj *.xcode *.pbproj *.pbxproj"; 445 | FRAMEWORK_SEARCH_PATHS = ( 446 | "$(iOSOpenDevPath)/frameworks/**", 447 | "$(SDKROOT)/System/Library/PrivateFrameworks", 448 | ); 449 | GCC_C_LANGUAGE_STANDARD = gnu99; 450 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 451 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 452 | GCC_WARN_UNUSED_VARIABLE = YES; 453 | HEADER_SEARCH_PATHS = "$(iOSOpenDevPath)/include/**"; 454 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 455 | LIBRARY_SEARCH_PATHS = "$(iOSOpenDevPath)/lib/**"; 456 | SDKROOT = iphoneos9.2; 457 | TARGETED_DEVICE_FAMILY = "1,2"; 458 | VALIDATE_PRODUCT = YES; 459 | iOSOpenDevPath = /opt/iOSOpenDev; 460 | }; 461 | name = Release; 462 | }; 463 | 433453391E769F5B00769232 /* Debug */ = { 464 | isa = XCBuildConfiguration; 465 | buildSettings = { 466 | CODE_SIGN_IDENTITY = ""; 467 | DEVELOPMENT_TEAM = 49NAE8LY4S; 468 | DYLIB_COMPATIBILITY_VERSION = 1; 469 | DYLIB_CURRENT_VERSION = 1; 470 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 471 | GCC_PREFIX_HEADER = "ExtensionList/ExtensionList-Prefix.pch"; 472 | HEADER_SEARCH_PATHS = ( 473 | "$(iOSOpenDevPath)/include/**", 474 | "\"$(SRCROOT)/\"/**", 475 | ); 476 | INSTALL_PATH = /Library/MobileSubstrate/DynamicLibraries; 477 | PRODUCT_NAME = "$(TARGET_NAME)"; 478 | iOSOpenDevBuildPackageOnAnyBuild = NO; 479 | iOSOpenDevCopyOnBuild = NO; 480 | iOSOpenDevDevice = ""; 481 | iOSOpenDevInstallOnAnyBuild = NO; 482 | iOSOpenDevInstallOnProfiling = NO; 483 | iOSOpenDevRespringOnInstall = YES; 484 | iOSOpenDevUsePackageVersionPList = YES; 485 | }; 486 | name = Debug; 487 | }; 488 | 4334533A1E769F5B00769232 /* Release */ = { 489 | isa = XCBuildConfiguration; 490 | buildSettings = { 491 | CODE_SIGN_IDENTITY = ""; 492 | DEVELOPMENT_TEAM = 49NAE8LY4S; 493 | DYLIB_COMPATIBILITY_VERSION = 1; 494 | DYLIB_CURRENT_VERSION = 1; 495 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 496 | GCC_PREFIX_HEADER = "ExtensionList/ExtensionList-Prefix.pch"; 497 | HEADER_SEARCH_PATHS = ( 498 | "$(iOSOpenDevPath)/include/**", 499 | "\"$(SRCROOT)/\"/**", 500 | ); 501 | INSTALL_PATH = /Library/MobileSubstrate/DynamicLibraries; 502 | PRODUCT_NAME = "$(TARGET_NAME)"; 503 | iOSOpenDevBuildPackageOnAnyBuild = NO; 504 | iOSOpenDevCopyOnBuild = NO; 505 | iOSOpenDevDevice = ""; 506 | iOSOpenDevInstallOnAnyBuild = NO; 507 | iOSOpenDevInstallOnProfiling = NO; 508 | iOSOpenDevRespringOnInstall = YES; 509 | iOSOpenDevUsePackageVersionPList = YES; 510 | }; 511 | name = Release; 512 | }; 513 | 433453621E769F8F00769232 /* Debug */ = { 514 | isa = XCBuildConfiguration; 515 | buildSettings = { 516 | CODE_SIGN_IDENTITY = ""; 517 | DEVELOPMENT_TEAM = 49NAE8LY4S; 518 | ENABLE_BITCODE = NO; 519 | FRAMEWORK_SEARCH_PATHS = ( 520 | "$(inherited)", 521 | "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", 522 | ); 523 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 524 | GCC_PREFIX_HEADER = "ExtensionListSettings/ExtensionListSettings-Prefix.pch"; 525 | HEADER_SEARCH_PATHS = ( 526 | "$(iOSOpenDevPath)/include/**", 527 | "\"$(SRCROOT)/\"", 528 | ); 529 | INFOPLIST_FILE = "ExtensionListSettings/ExtensionListSettings-Info.plist"; 530 | INSTALL_PATH = /System/Library/PreferenceBundles; 531 | PRODUCT_NAME = "$(TARGET_NAME)"; 532 | SDKROOT = iphoneos9.2; 533 | WRAPPER_EXTENSION = bundle; 534 | iOSOpenDevBuildPackageOnAnyBuild = NO; 535 | iOSOpenDevCopyOnBuild = NO; 536 | iOSOpenDevDevice = ""; 537 | iOSOpenDevInstallOnAnyBuild = NO; 538 | iOSOpenDevInstallOnProfiling = NO; 539 | iOSOpenDevRespringOnInstall = YES; 540 | iOSOpenDevUsePackageVersionPList = YES; 541 | }; 542 | name = Debug; 543 | }; 544 | 433453631E769F8F00769232 /* Release */ = { 545 | isa = XCBuildConfiguration; 546 | buildSettings = { 547 | CODE_SIGN_IDENTITY = ""; 548 | DEVELOPMENT_TEAM = 49NAE8LY4S; 549 | ENABLE_BITCODE = NO; 550 | FRAMEWORK_SEARCH_PATHS = ( 551 | "$(inherited)", 552 | "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", 553 | ); 554 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 555 | GCC_PREFIX_HEADER = "ExtensionListSettings/ExtensionListSettings-Prefix.pch"; 556 | HEADER_SEARCH_PATHS = ( 557 | "$(iOSOpenDevPath)/include/**", 558 | "\"$(SRCROOT)/\"", 559 | ); 560 | INFOPLIST_FILE = "ExtensionListSettings/ExtensionListSettings-Info.plist"; 561 | INSTALL_PATH = /System/Library/PreferenceBundles; 562 | PRODUCT_NAME = "$(TARGET_NAME)"; 563 | SDKROOT = iphoneos9.2; 564 | WRAPPER_EXTENSION = bundle; 565 | iOSOpenDevBuildPackageOnAnyBuild = NO; 566 | iOSOpenDevCopyOnBuild = NO; 567 | iOSOpenDevDevice = ""; 568 | iOSOpenDevInstallOnAnyBuild = NO; 569 | iOSOpenDevInstallOnProfiling = NO; 570 | iOSOpenDevRespringOnInstall = YES; 571 | iOSOpenDevUsePackageVersionPList = YES; 572 | }; 573 | name = Release; 574 | }; 575 | /* End XCBuildConfiguration section */ 576 | 577 | /* Begin XCConfigurationList section */ 578 | 4334531B1E769F5B00769232 /* Build configuration list for PBXProject "ExtensionList" */ = { 579 | isa = XCConfigurationList; 580 | buildConfigurations = ( 581 | 433453361E769F5B00769232 /* Debug */, 582 | 433453371E769F5B00769232 /* Release */, 583 | ); 584 | defaultConfigurationIsVisible = 0; 585 | defaultConfigurationName = Release; 586 | }; 587 | 433453381E769F5B00769232 /* Build configuration list for PBXNativeTarget "ExtensionList" */ = { 588 | isa = XCConfigurationList; 589 | buildConfigurations = ( 590 | 433453391E769F5B00769232 /* Debug */, 591 | 4334533A1E769F5B00769232 /* Release */, 592 | ); 593 | defaultConfigurationIsVisible = 0; 594 | defaultConfigurationName = Release; 595 | }; 596 | 433453611E769F8F00769232 /* Build configuration list for PBXNativeTarget "ExtensionListSettings" */ = { 597 | isa = XCConfigurationList; 598 | buildConfigurations = ( 599 | 433453621E769F8F00769232 /* Debug */, 600 | 433453631E769F8F00769232 /* Release */, 601 | ); 602 | defaultConfigurationIsVisible = 0; 603 | defaultConfigurationName = Release; 604 | }; 605 | /* End XCConfigurationList section */ 606 | }; 607 | rootObject = 433453181E769F5B00769232 /* Project object */; 608 | } 609 | -------------------------------------------------------------------------------- /ExtensionList.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ExtensionList/ELExtensionTableDataSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // ELExtensionTableDataSource.h 3 | // ExtensionList 4 | // 5 | // Created by duyongchao on 2017/3/14. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | 14 | @interface ELExtensionTableDataSource : NSObject 15 | { 16 | @private 17 | NSMutableArray *_sectionDescriptors; 18 | UITableView *_tableView; 19 | NSBundle *_localizationBundle; 20 | BOOL _loadsAsynchronously; 21 | } 22 | 23 | + (NSArray *)standardSectionDescriptors; 24 | 25 | + (id)dataSource; 26 | - (id)init; 27 | 28 | @property (nonatomic, copy) NSArray *sectionDescriptors; 29 | @property (nonatomic, retain) UITableView *tableView; 30 | @property (nonatomic, retain) NSBundle *localizationBundle; 31 | @property (nonatomic, assign) BOOL loadsAsynchronously; 32 | 33 | - (id)cellDescriptorForIndexPath:(NSIndexPath *)indexPath; // NSDictionary if custom cell; NSString if app cell; nil if loading 34 | - (NSString *)displayIdentifierForIndexPath:(NSIndexPath *)indexPath; 35 | 36 | - (void)insertSectionDescriptor:(NSDictionary *)sectionDescriptor atIndex:(NSInteger)index; 37 | - (void)removeSectionDescriptorAtIndex:(NSInteger)index; 38 | - (void)removeSectionDescriptorsAtIndexes:(NSIndexSet *)indexSet; 39 | 40 | - (BOOL)waitUntilDate:(NSDate *)date forContentInSectionAtIndex:(NSInteger)sectionIndex; 41 | 42 | @end 43 | 44 | extern const NSString *ALSectionDescriptorTitleKey; 45 | extern const NSString *ALSectionDescriptorFooterTitleKey; 46 | extern const NSString *ALSectionDescriptorPredicateKey; 47 | extern const NSString *ALSectionDescriptorCellClassNameKey; 48 | extern const NSString *ALSectionDescriptorIconSizeKey; 49 | extern const NSString *ALSectionDescriptorSuppressHiddenAppsKey; 50 | extern const NSString *ALSectionDescriptorVisibilityPredicateKey; 51 | 52 | extern const NSString *ALItemDescriptorTextKey; 53 | extern const NSString *ALItemDescriptorDetailTextKey; 54 | extern const NSString *ALItemDescriptorImageKey; 55 | -------------------------------------------------------------------------------- /ExtensionList/ELExtensionTableDataSource.m: -------------------------------------------------------------------------------- 1 | // 2 | // ELExtensionTableDataSource.m 3 | // ExtensionList 4 | // 5 | // Created by duyongchao on 2017/3/14. 6 | // 7 | // 8 | 9 | #import "ELExtensionTableDataSource.h" 10 | #import 11 | #import 12 | #import "ExtensionList.h" 13 | 14 | const NSString *ALSectionDescriptorTitleKey = @"title"; 15 | const NSString *ALSectionDescriptorFooterTitleKey = @"footer-title"; 16 | const NSString *ALSectionDescriptorPredicateKey = @"predicate"; 17 | const NSString *ALSectionDescriptorCellClassNameKey = @"cell-class-name"; 18 | const NSString *ALSectionDescriptorIconSizeKey = @"icon-size"; 19 | const NSString *ALSectionDescriptorItemsKey = @"items"; 20 | const NSString *ALSectionDescriptorSuppressHiddenAppsKey = @"avaliable-extensions"; 21 | const NSString *ALSectionDescriptorVisibilityPredicateKey = @"visibility-predicate"; 22 | 23 | const NSString *ALItemDescriptorTextKey = @"text"; 24 | const NSString *ALItemDescriptorDetailTextKey = @"detail-text"; 25 | const NSString *ALItemDescriptorImageKey = @"image"; 26 | 27 | @interface ELLoadingTableViewCell : UITableViewCell 28 | @end 29 | 30 | @implementation ELLoadingTableViewCell 31 | 32 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 33 | { 34 | if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) { 35 | self.backgroundColor = [UIColor clearColor]; 36 | UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; 37 | CGSize cellSize = self.bounds.size; 38 | CGRect frame = spinner.frame; 39 | frame.origin.x = (cellSize.width - frame.size.width) * 0.5f; 40 | frame.origin.y = (cellSize.height - frame.size.height) * 0.5f; 41 | spinner.frame = frame; 42 | spinner.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; 43 | [spinner startAnimating]; 44 | [self addSubview:spinner]; 45 | [spinner release]; 46 | self.backgroundView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease]; 47 | } 48 | return self; 49 | } 50 | 51 | @end 52 | 53 | __attribute__((visibility("hidden"))) 54 | @interface ELExtensionTableDataSourceSection : NSObject { 55 | @private 56 | ELExtensionTableDataSource *_dataSource; 57 | NSDictionary *_descriptor; 58 | NSArray *_displayNames; 59 | NSArray *_displayIdentifiers; 60 | CGFloat iconSize; 61 | BOOL isStaticSection; 62 | NSInteger loadingState; 63 | CFTimeInterval loadStartTime; 64 | NSCondition *loadCondition; 65 | } 66 | 67 | @property (nonatomic, readonly) NSDictionary *descriptor; 68 | @property (nonatomic, readonly) NSString *title; 69 | @property (nonatomic, readonly) NSString *footerTitle; 70 | 71 | - (void)loadContent; 72 | 73 | @end 74 | 75 | @interface ELExtensionTableDataSource () 76 | - (void)sectionRequestedSectionReload:(ELExtensionTableDataSourceSection *)section animated:(BOOL)animated; 77 | @end 78 | 79 | static NSMutableArray *iconsToLoad; 80 | static OSSpinLock spinLock; 81 | static UIImage *defaultImage; 82 | 83 | @implementation ELExtensionTableDataSourceSection 84 | 85 | + (void)initialize 86 | { 87 | if (self == [ELExtensionTableDataSourceSection class]) { 88 | // defaultImage = [[[ELExtensionList sharedExtensionList] iconOfSize:ALApplicationIconSizeSmall forDisplayIdentifier:@"com.apple.WebSheet"] retain]; 89 | } 90 | } 91 | 92 | + (void)loadIconsFromBackground 93 | { 94 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 95 | OSSpinLockLock(&spinLock); 96 | ELExtensionList *appList = [ELExtensionList sharedExtensionList]; 97 | while ([iconsToLoad count]) { 98 | NSDictionary *userInfo = [[iconsToLoad objectAtIndex:0] retain]; 99 | [iconsToLoad removeObjectAtIndex:0]; 100 | OSSpinLockUnlock(&spinLock); 101 | CGImageRelease([appList copyIconOfSize:[[userInfo objectForKey:ALIconSizeKey] integerValue] forDisplayIdentifier:[userInfo objectForKey:ALDisplayIdentifierKey]]); 102 | [userInfo release]; 103 | [pool drain]; 104 | pool = [[NSAutoreleasePool alloc] init]; 105 | OSSpinLockLock(&spinLock); 106 | } 107 | [iconsToLoad release]; 108 | iconsToLoad = nil; 109 | OSSpinLockUnlock(&spinLock); 110 | [pool drain]; 111 | } 112 | 113 | - (id)initWithDescriptor:(NSDictionary *)descriptor dataSource:(ELExtensionTableDataSource *)dataSource loadsAsynchronously:(BOOL)loadsAsynchronously 114 | { 115 | if ((self = [super init])) { 116 | _dataSource = dataSource; 117 | _descriptor = [descriptor copy]; 118 | NSArray *items = [_descriptor objectForKey:@"items"]; 119 | if ([items isKindOfClass:[NSArray class]]) { 120 | _displayNames = [items copy]; 121 | isStaticSection = YES; 122 | } else { 123 | if (loadsAsynchronously) { 124 | loadingState = 1; 125 | loadStartTime = CACurrentMediaTime(); 126 | [self performSelectorInBackground:@selector(loadContent) withObject:nil]; 127 | loadCondition = [[NSCondition alloc] init]; 128 | } else { 129 | [self loadContent]; 130 | } 131 | } 132 | } 133 | return self; 134 | } 135 | 136 | - (void)dealloc 137 | { 138 | [loadCondition release]; 139 | [_displayIdentifiers release]; 140 | [_displayNames release]; 141 | [_descriptor release]; 142 | [super dealloc]; 143 | } 144 | 145 | - (void)potentialLoadFail 146 | { 147 | if ([ELExtensionList sharedExtensionList].extensionCount == 0) { 148 | static BOOL hasFailedAlready; 149 | if (!hasFailedAlready) { 150 | hasFailedAlready = YES; 151 | UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Unable To Load Extensions" message:@"ExtensionList was unable to load the list of installed plugins.\n\nPotential causes include the device being in safe mode, ExtensionList being disabled or tampered with, RocketBootstrap being disabled or tampered with, and conflicts between packages currently installed on this device." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; 152 | [av show]; 153 | [av release]; 154 | } 155 | } 156 | } 157 | 158 | - (void)loadContent 159 | { 160 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 161 | NSDictionary *descriptor = _descriptor; 162 | NSString *predicateText = [descriptor objectForKey:ALSectionDescriptorPredicateKey]; 163 | NSPredicate *predicate = predicateText ? [NSPredicate predicateWithFormat:predicateText] : nil; 164 | BOOL onlyVisible = [[descriptor objectForKey:ALSectionDescriptorSuppressHiddenAppsKey] boolValue]; 165 | NSArray *displayIdentifiers = nil; 166 | NSDictionary *applications = [[ELExtensionList sharedExtensionList] extensionsFilteredUsingPredicate:predicate sysVerAvaliable:onlyVisible titleSortedIdentifiers:&displayIdentifiers]; 167 | if ([applications count] == 0) { 168 | [self performSelectorOnMainThread:@selector(potentialLoadFail) withObject:nil waitUntilDone:NO]; 169 | } 170 | [displayIdentifiers retain]; 171 | NSMutableArray *displayNames = [[NSMutableArray alloc] init]; 172 | for (NSString *displayId in displayIdentifiers) 173 | [displayNames addObject:[applications objectForKey:displayId]]; 174 | [loadCondition lock]; 175 | _displayIdentifiers = displayIdentifiers; 176 | _displayNames = displayNames; 177 | iconSize = [[descriptor objectForKey:ALSectionDescriptorIconSizeKey] floatValue]; 178 | loadingState = 2; 179 | if (![NSThread isMainThread]) { 180 | [self performSelectorOnMainThread:@selector(completedLoading) withObject:nil waitUntilDone:NO]; 181 | } 182 | [loadCondition signal]; 183 | [loadCondition unlock]; 184 | [pool drain]; 185 | } 186 | 187 | - (void)completedLoading 188 | { 189 | if (loadingState) { 190 | loadingState = 0; 191 | [_dataSource sectionRequestedSectionReload:self animated:CACurrentMediaTime() - loadStartTime > 0.1]; 192 | } 193 | } 194 | 195 | - (BOOL)waitForContentUntilDate:(NSDate *)date 196 | { 197 | if (loadingState) { 198 | [loadCondition lock]; 199 | BOOL result; 200 | if (loadingState == 1) { 201 | if (date) 202 | result = [loadCondition waitUntilDate:date]; 203 | else { 204 | [loadCondition wait]; 205 | result = YES; 206 | } 207 | } else { 208 | result = YES; 209 | } 210 | [loadCondition unlock]; 211 | if (loadingState == 2) { 212 | [self completedLoading]; 213 | } 214 | return result; 215 | } 216 | return YES; 217 | } 218 | 219 | @synthesize descriptor = _descriptor; 220 | 221 | static inline NSString *Localize(NSBundle *bundle, NSString *string) 222 | { 223 | return bundle ? [bundle localizedStringForKey:string value:string table:nil] : string; 224 | } 225 | #define Localize(string) Localize(_dataSource.localizationBundle, string) 226 | 227 | - (NSString *)title 228 | { 229 | return Localize([_descriptor objectForKey:ALSectionDescriptorTitleKey]); 230 | } 231 | 232 | - (NSString *)footerTitle 233 | { 234 | return Localize([_descriptor objectForKey:ALSectionDescriptorFooterTitleKey]); 235 | } 236 | 237 | - (NSString *)displayIdentifierForRow:(NSInteger)row 238 | { 239 | NSArray *array = _displayIdentifiers; 240 | return (row < [array count]) ? [array objectAtIndex:row] : nil; 241 | } 242 | 243 | - (id)cellDescriptorForRow:(NSInteger)row 244 | { 245 | NSArray *array = isStaticSection ? _displayNames : _displayIdentifiers; 246 | return (row < [array count]) ? [array objectAtIndex:row] : nil; 247 | } 248 | 249 | - (NSInteger)rowCount 250 | { 251 | return loadingState ? 1 : [_displayNames count]; 252 | } 253 | 254 | static inline UITableViewCell *CellWithClassName(NSString *className, UITableView *tableView) 255 | { 256 | return [tableView dequeueReusableCellWithIdentifier:className] ?: [[[NSClassFromString(className) alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:className] autorelease]; 257 | } 258 | 259 | #define CellWithClassName(className) \ 260 | CellWithClassName(className, tableView) 261 | 262 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRow:(NSInteger)row 263 | { 264 | if (isStaticSection) { 265 | NSDictionary *itemDescriptor = [_displayNames objectAtIndex:row]; 266 | UITableViewCell *cell = CellWithClassName([itemDescriptor objectForKey:ALSectionDescriptorCellClassNameKey] ?: [_descriptor objectForKey:ALSectionDescriptorCellClassNameKey] ?: @"UITableViewCell"); 267 | cell.textLabel.text = Localize([itemDescriptor objectForKey:ALItemDescriptorTextKey]); 268 | cell.detailTextLabel.text = Localize([itemDescriptor objectForKey:ALItemDescriptorDetailTextKey]); 269 | NSString *imagePath = [itemDescriptor objectForKey:ALItemDescriptorImageKey]; 270 | UIImage *image = nil; 271 | if (imagePath) { 272 | CGFloat scale; 273 | if ([UIScreen instancesRespondToSelector:@selector(scale)] && ((scale = [[UIScreen mainScreen] scale]) != 1.0f)) 274 | image = [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@@%gx.%@", [imagePath stringByDeletingPathExtension], scale, [imagePath pathExtension]]]; 275 | if (!image) 276 | image = [UIImage imageWithContentsOfFile:imagePath]; 277 | } 278 | cell.imageView.image = image; 279 | return cell; 280 | } 281 | if (loadingState) { 282 | return [tableView dequeueReusableCellWithIdentifier:@"ALApplicationLoadingTableViewCell"] ?: [[[ELLoadingTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"ALApplicationLoadingTableViewCell"] autorelease]; 283 | } 284 | UITableViewCell *cell = CellWithClassName([_descriptor objectForKey:ALSectionDescriptorCellClassNameKey] ?: @"UITableViewCell"); 285 | cell.textLabel.text = [_displayNames objectAtIndex:row]; 286 | if (iconSize > 0) { 287 | NSString *displayIdentifier = [_displayIdentifiers objectAtIndex:row]; 288 | ELExtensionList *appList = [ELExtensionList sharedExtensionList]; 289 | if ([appList hasCachedIconOfSize:iconSize forDisplayIdentifier:displayIdentifier]) { 290 | cell.imageView.image = [appList iconOfSize:iconSize forDisplayIdentifier:displayIdentifier]; 291 | cell.indentationWidth = 10.0f; 292 | cell.indentationLevel = 0; 293 | } else { 294 | if (defaultImage.size.width == iconSize) { 295 | cell.imageView.image = defaultImage; 296 | cell.indentationWidth = 10.0f; 297 | cell.indentationLevel = 0; 298 | } else { 299 | cell.indentationWidth = iconSize + 7.0f; 300 | cell.indentationLevel = 1; 301 | cell.imageView.image = nil; 302 | } 303 | cell.imageView.image = defaultImage; 304 | NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys: 305 | [NSNumber numberWithInteger:iconSize], ALIconSizeKey, 306 | displayIdentifier, ALDisplayIdentifierKey, 307 | nil]; 308 | OSSpinLockLock(&spinLock); 309 | if (iconsToLoad) 310 | [iconsToLoad insertObject:userInfo atIndex:0]; 311 | else { 312 | iconsToLoad = [[NSMutableArray alloc] initWithObjects:userInfo, nil]; 313 | [ELExtensionTableDataSourceSection performSelectorInBackground:@selector(loadIconsFromBackground) withObject:nil]; 314 | } 315 | OSSpinLockUnlock(&spinLock); 316 | } 317 | } else { 318 | cell.imageView.image = nil; 319 | } 320 | return cell; 321 | } 322 | 323 | - (void)updateIndexPath:(NSIndexPath *)indexPath ofTableView:(UITableView *)tableView withLoadedIconOfSize:(CGFloat)newIconSize forDisplayIdentifier:(NSString *)displayIdentifier 324 | { 325 | if ((loadingState == 0) && [displayIdentifier isEqual:[_displayIdentifiers objectAtIndex:indexPath.row]] && newIconSize == iconSize) { 326 | UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; 327 | UIImageView *imageView = cell.imageView; 328 | UIImage *image = imageView.image; 329 | if (!image || (image == defaultImage)) { 330 | cell.indentationLevel = 0; 331 | cell.indentationWidth = 10.0f; 332 | imageView.image = [[ELExtensionList sharedExtensionList] iconOfSize:newIconSize forDisplayIdentifier:displayIdentifier]; 333 | [cell setNeedsLayout]; 334 | } 335 | } 336 | } 337 | 338 | - (void)detach 339 | { 340 | _dataSource = nil; 341 | } 342 | 343 | @end 344 | 345 | @implementation ELExtensionTableDataSource 346 | 347 | + (NSArray *)standardSectionDescriptors 348 | { 349 | NSNumber *iconSize = [NSNumber numberWithUnsignedInteger:ALApplicationIconSizeSmall]; 350 | return [NSArray arrayWithObjects: 351 | [NSDictionary dictionaryWithObjectsAndKeys: 352 | @"System Applications", ALSectionDescriptorTitleKey, 353 | @"isSystemApplication = TRUE", ALSectionDescriptorPredicateKey, 354 | @"UITableViewCell", ALSectionDescriptorCellClassNameKey, 355 | iconSize, ALSectionDescriptorIconSizeKey, 356 | (id)kCFBooleanTrue, ALSectionDescriptorSuppressHiddenAppsKey, 357 | nil], 358 | [NSDictionary dictionaryWithObjectsAndKeys: 359 | @"User Applications", ALSectionDescriptorTitleKey, 360 | @"isSystemApplication = FALSE", ALSectionDescriptorPredicateKey, 361 | @"UITableViewCell", ALSectionDescriptorCellClassNameKey, 362 | iconSize, ALSectionDescriptorIconSizeKey, 363 | (id)kCFBooleanTrue, ALSectionDescriptorSuppressHiddenAppsKey, 364 | nil], 365 | nil]; 366 | } 367 | 368 | + (id)dataSource 369 | { 370 | return [[[self alloc] init] autorelease]; 371 | } 372 | 373 | - (id)init 374 | { 375 | if ((self = [super init])) { 376 | _loadsAsynchronously = YES; 377 | _sectionDescriptors = [[NSMutableArray alloc] init]; 378 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(iconLoadedFromNotification:) name:ALIconLoadedNotification object:nil]; 379 | } 380 | return self; 381 | } 382 | 383 | - (void)dealloc 384 | { 385 | for (ELExtensionTableDataSourceSection *section in _sectionDescriptors) { 386 | [section detach]; 387 | } 388 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 389 | [_localizationBundle release]; 390 | [_tableView release]; 391 | [_sectionDescriptors release]; 392 | [super dealloc]; 393 | } 394 | 395 | @synthesize tableView = _tableView; 396 | @synthesize localizationBundle = _localizationBundle; 397 | @synthesize loadsAsynchronously = _loadsAsynchronously; 398 | 399 | - (void)setSectionDescriptors:(NSArray *)sectionDescriptors 400 | { 401 | for (ELExtensionTableDataSourceSection *section in _sectionDescriptors) { 402 | [section detach]; 403 | } 404 | [_sectionDescriptors removeAllObjects]; 405 | for (NSDictionary *descriptor in sectionDescriptors) { 406 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 407 | ELExtensionTableDataSourceSection *section = [[ELExtensionTableDataSourceSection alloc] initWithDescriptor:descriptor dataSource:self loadsAsynchronously:_loadsAsynchronously]; 408 | [_sectionDescriptors addObject:section]; 409 | [section release]; 410 | [pool release]; 411 | } 412 | [_tableView reloadData]; 413 | } 414 | 415 | - (NSArray *)sectionDescriptors 416 | { 417 | // Recreate the array 418 | NSMutableArray *result = [[[NSMutableArray alloc] initWithCapacity:[_sectionDescriptors count]] autorelease]; 419 | for (ELExtensionTableDataSourceSection *section in _sectionDescriptors) { 420 | [result addObject:section.descriptor]; 421 | } 422 | return result; 423 | } 424 | 425 | - (void)removeSectionDescriptorsAtIndexes:(NSIndexSet *)indexSet 426 | { 427 | if (indexSet) { 428 | NSUInteger index = [indexSet firstIndex]; 429 | if (index != NSNotFound) { 430 | NSUInteger lastIndex = [indexSet lastIndex]; 431 | for (;;) { 432 | [[_sectionDescriptors objectAtIndex:index] detach]; 433 | if (index == lastIndex) { 434 | break; 435 | } 436 | index = [indexSet indexGreaterThanIndex:index]; 437 | } 438 | } 439 | } 440 | [_sectionDescriptors removeObjectsAtIndexes:indexSet]; 441 | [_tableView deleteSections:indexSet withRowAnimation:UITableViewRowAnimationFade]; 442 | } 443 | 444 | - (void)removeSectionDescriptorAtIndex:(NSInteger)index 445 | { 446 | [self removeSectionDescriptorsAtIndexes:[NSIndexSet indexSetWithIndex:index]]; 447 | } 448 | 449 | - (void)insertSectionDescriptor:(NSDictionary *)sectionDescriptor atIndex:(NSInteger)index 450 | { 451 | ELExtensionTableDataSourceSection *section = [[ELExtensionTableDataSourceSection alloc] initWithDescriptor:sectionDescriptor dataSource:self loadsAsynchronously:_loadsAsynchronously]; 452 | [_sectionDescriptors insertObject:section atIndex:index]; 453 | [section release]; 454 | [_tableView insertSections:[NSIndexSet indexSetWithIndex:index] withRowAnimation:UITableViewRowAnimationFade]; 455 | } 456 | 457 | - (void)setLocalizationBundle:(NSBundle *)localizationBundle 458 | { 459 | if (_localizationBundle != localizationBundle) { 460 | [_localizationBundle autorelease]; 461 | _localizationBundle = [localizationBundle retain]; 462 | [_tableView reloadData]; 463 | } 464 | } 465 | 466 | - (NSString *)displayIdentifierForIndexPath:(NSIndexPath *)indexPath 467 | { 468 | NSInteger section = indexPath.section; 469 | if ([_sectionDescriptors count] > section) 470 | return [[_sectionDescriptors objectAtIndex:section] displayIdentifierForRow:indexPath.row]; 471 | else 472 | return nil; 473 | } 474 | 475 | - (id)cellDescriptorForIndexPath:(NSIndexPath *)indexPath 476 | { 477 | NSInteger section = indexPath.section; 478 | if ([_sectionDescriptors count] > section) 479 | return [[_sectionDescriptors objectAtIndex:section] cellDescriptorForRow:indexPath.row]; 480 | else 481 | return nil; 482 | } 483 | 484 | - (void)iconLoadedFromNotification:(NSNotification *)notification 485 | { 486 | NSDictionary *userInfo = notification.userInfo; 487 | NSString *displayIdentifier = [userInfo objectForKey:ALDisplayIdentifierKey]; 488 | CGFloat iconSize = [[userInfo objectForKey:ALIconSizeKey] floatValue]; 489 | for (NSIndexPath *indexPath in _tableView.indexPathsForVisibleRows) { 490 | NSInteger section = indexPath.section; 491 | ELExtensionTableDataSourceSection *sectionObject = [_sectionDescriptors objectAtIndex:section]; 492 | [sectionObject updateIndexPath:indexPath ofTableView:_tableView withLoadedIconOfSize:iconSize forDisplayIdentifier:displayIdentifier]; 493 | } 494 | } 495 | 496 | - (void)sectionRequestedSectionReload:(ELExtensionTableDataSourceSection *)section animated:(BOOL)animated 497 | { 498 | if (animated) { 499 | NSInteger index = [_sectionDescriptors indexOfObjectIdenticalTo:section]; 500 | if (index != NSNotFound) { 501 | [_tableView reloadSections:[NSIndexSet indexSetWithIndex:index] withRowAnimation:UITableViewRowAnimationFade]; 502 | } 503 | } else { 504 | [_tableView reloadData]; 505 | } 506 | } 507 | 508 | - (BOOL)waitUntilDate:(NSDate *)date forContentInSectionAtIndex:(NSInteger)sectionIndex 509 | { 510 | ELExtensionTableDataSourceSection *section = [_sectionDescriptors objectAtIndex:sectionIndex]; 511 | return [section waitForContentUntilDate:date]; 512 | } 513 | 514 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 515 | { 516 | if (!_tableView) { 517 | _tableView = [tableView retain]; 518 | NSLog(@"ALApplicationTableDataSource warning: Assumed control over %@", tableView); 519 | } 520 | return [_sectionDescriptors count]; 521 | } 522 | 523 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 524 | { 525 | return [[_sectionDescriptors objectAtIndex:section] title]; 526 | } 527 | 528 | - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section 529 | { 530 | return [[_sectionDescriptors objectAtIndex:section] footerTitle]; 531 | } 532 | 533 | - (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section 534 | { 535 | return [[_sectionDescriptors objectAtIndex:section] rowCount]; 536 | } 537 | 538 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 539 | { 540 | ELExtensionTableDataSourceSection *section = [_sectionDescriptors objectAtIndex:indexPath.section]; 541 | return [section tableView:tableView cellForRow:indexPath.row]; 542 | } 543 | 544 | @end 545 | -------------------------------------------------------------------------------- /ExtensionList/ELValueCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ELValueCell.h 3 | // ExtensionList 4 | // 5 | // Created by duyongchao on 2017/3/14. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @protocol ELValueCellDelegate; 12 | 13 | @interface ELValueCell : UITableViewCell { 14 | @private 15 | id delegate; 16 | } 17 | 18 | @property (nonatomic, assign) id delegate; 19 | 20 | - (void)loadValue:(id)value; // Deprecated 21 | - (void)loadValue:(id)value withTitle:(NSString *)title; 22 | - (void)didSelect; 23 | 24 | @end 25 | 26 | @protocol ELValueCellDelegate 27 | @required 28 | - (void)valueCell:(ELValueCell *)valueCell didChangeToValue:(id)newValue; 29 | @end 30 | 31 | @interface ELSwitchCell : ELValueCell { 32 | @private 33 | UISwitch *switchView; 34 | } 35 | 36 | @property (nonatomic, readonly) UISwitch *switchView; 37 | 38 | @end 39 | 40 | @interface ELCheckCell : ELValueCell 41 | 42 | @end 43 | 44 | @interface ELDisclosureIndicatedCell : ELValueCell 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /ExtensionList/ELValueCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ELValueCell.m 3 | // ExtensionList 4 | // 5 | // Created by duyongchao on 2017/3/14. 6 | // 7 | // 8 | 9 | #import "ELValueCell.h" 10 | 11 | @implementation ELValueCell 12 | 13 | @synthesize delegate; 14 | 15 | - (void)loadValue:(id)value 16 | { 17 | } 18 | 19 | - (void)loadValue:(id)value withTitle:(NSString *)title 20 | { 21 | [self loadValue:value]; 22 | } 23 | 24 | - (void)didSelect 25 | { 26 | } 27 | 28 | @end 29 | 30 | @implementation ELSwitchCell 31 | 32 | @synthesize switchView; 33 | 34 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 35 | { 36 | if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) { 37 | switchView = [[UISwitch alloc] initWithFrame:CGRectZero]; 38 | [switchView addTarget:self action:@selector(valueChanged) forControlEvents:UIControlEventValueChanged]; 39 | [self setAccessoryView:switchView]; 40 | } 41 | return self; 42 | } 43 | 44 | - (void)dealloc 45 | { 46 | [switchView release]; 47 | [super dealloc]; 48 | } 49 | 50 | - (void)loadValue:(id)value 51 | { 52 | switchView.on = [value boolValue]; 53 | } 54 | 55 | - (void)valueChanged 56 | { 57 | id value = [NSNumber numberWithBool:switchView.on]; 58 | [[self delegate] valueCell:self didChangeToValue:value]; 59 | } 60 | 61 | @end 62 | 63 | @implementation ELCheckCell 64 | 65 | - (void)loadValue:(id)value 66 | { 67 | [self setAccessoryType:[value boolValue] ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone]; 68 | } 69 | 70 | - (void)didSelect 71 | { 72 | UITableViewCellAccessoryType type = [self accessoryType]; 73 | [self setAccessoryType:(type == UITableViewCellAccessoryNone) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone]; 74 | id value = [NSNumber numberWithBool:type == UITableViewCellAccessoryNone]; 75 | [[self delegate] valueCell:self didChangeToValue:value]; 76 | } 77 | 78 | @end 79 | 80 | @implementation ELDisclosureIndicatedCell 81 | 82 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 83 | { 84 | if ((self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier])) { 85 | self.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 86 | } 87 | return self; 88 | } 89 | 90 | - (void)loadValue:(id)value withTitle:(NSString *)title 91 | { 92 | self.detailTextLabel.text = title ?: [value description]; 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /ExtensionList/ExtensionList-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'ExtensionList' target in the 'ExtensionList' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /ExtensionList/ExtensionList.h: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionList.h 3 | // ExtensionList 4 | // 5 | // Created by duyongchao on 2017/3/13. 6 | // 7 | // 8 | 9 | #ifndef ExtensionList_h 10 | #define ExtensionList_h 11 | 12 | #import 13 | #import 14 | #import 15 | 16 | enum { 17 | ALApplicationIconSizeSmall = 29, 18 | ALApplicationIconSizeLarge = 59 19 | }; 20 | typedef NSUInteger ALApplicationIconSize; 21 | 22 | @interface ELExtensionList : NSObject { 23 | @private 24 | NSMutableDictionary *cachedIcons; 25 | OSSpinLock spinLock; 26 | } 27 | + (ELExtensionList *)sharedExtensionList; 28 | 29 | @property (nonatomic, readonly) NSDictionary *extensions; 30 | - (NSDictionary *)extensionsFilteredUsingPredicate:(NSPredicate *)predicate; 31 | - (NSDictionary *)extensionsFilteredUsingPredicate:(NSPredicate *)predicate sysVerAvaliable:(BOOL)sysVerAvaliable titleSortedIdentifiers:(NSArray **)outSortedByTitle; 32 | 33 | - (id)valueForKeyPath:(NSString *)keyPath forDisplayIdentifier:(NSString *)displayIdentifier; 34 | - (id)valueForKey:(NSString *)keyPath forDisplayIdentifier:(NSString *)displayIdentifier; 35 | 36 | - (CGImageRef)copyIconOfSize:(ALApplicationIconSize)iconSize forDisplayIdentifier:(NSString *)displayIdentifier; 37 | - (UIImage *)iconOfSize:(ALApplicationIconSize)iconSize forDisplayIdentifier:(NSString *)displayIdentifier; 38 | - (BOOL)hasCachedIconOfSize:(ALApplicationIconSize)iconSize forDisplayIdentifier:(NSString *)displayIdentifier; 39 | 40 | // private 41 | - (NSInteger)extensionCount; 42 | 43 | @end 44 | 45 | extern NSString *const ALIconLoadedNotification; 46 | extern NSString *const ALDisplayIdentifierKey; 47 | extern NSString *const ALIconSizeKey; 48 | 49 | #endif /* ExtensionList_h */ 50 | -------------------------------------------------------------------------------- /ExtensionList/ExtensionList.xm: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | 6 | #define ROCKETBOOTSTRAP_LOAD_DYNAMIC 7 | #import 8 | 9 | #import 10 | #import 11 | #import "ExtensionList.h" 12 | 13 | @interface UIImage (Private) 14 | + (UIImage *)_applicationIconImageForBundleIdentifier:(NSString *)bundleIdentifier format:(int)format scale:(CGFloat)scale; 15 | @end 16 | 17 | 18 | NSString *const ALIconLoadedNotification = @"ELIconLoadedNotification"; 19 | NSString *const ALDisplayIdentifierKey = @"ELDisplayIdentifier"; 20 | NSString *const ALIconSizeKey = @"ELIconSize"; 21 | 22 | enum { 23 | ELMessageIdGetExtensions, 24 | ELMessageIdIconForSize, 25 | ELMessageIdValueForKey, 26 | ELMessageIdValueForKeyPath, 27 | ELMessageIdGetExtensionCount, 28 | ELMessageIdGetAvaliableExtensions 29 | }; 30 | 31 | static LMConnection connection = { 32 | MACH_PORT_NULL, 33 | "extensionlist.datasource" 34 | }; 35 | 36 | @interface ELExtensionListImpl : ELExtensionList 37 | 38 | @end 39 | 40 | static ELExtensionList * sharedExtensionList; 41 | 42 | @implementation ELExtensionList 43 | 44 | +(void)initialize { 45 | if (self == [ELExtensionList class] && !%c(SBIconModel)) { 46 | sharedExtensionList = [[self alloc] init]; 47 | } 48 | } 49 | 50 | +(ELExtensionList *) sharedExtensionList { 51 | return sharedExtensionList; 52 | } 53 | 54 | -(instancetype)init { 55 | if ((self = [super init])) { 56 | if (sharedExtensionList) { 57 | [self release]; 58 | @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Only one instance of ELExtensionList is permitted at a time! Use [ELExtensionList sharedExtensionList] instead." userInfo:nil]; 59 | } 60 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 61 | cachedIcons = [[NSMutableDictionary alloc] init]; 62 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 63 | [pool drain]; 64 | } 65 | return self; 66 | } 67 | 68 | - (void)dealloc 69 | { 70 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 71 | [cachedIcons release]; 72 | [super dealloc]; 73 | } 74 | 75 | -(NSInteger)extensionCount { 76 | LMResponseBuffer buffer; 77 | if (LMConnectionSendTwoWay(&connection, ELMessageIdGetExtensionCount, NULL, 0, &buffer)) 78 | return 0; 79 | return LMResponseConsumeInteger(&buffer); 80 | } 81 | 82 | - (NSString *)description 83 | { 84 | return [NSString stringWithFormat:@"", self, (long)self.extensionCount]; 85 | } 86 | 87 | - (void)didReceiveMemoryWarning 88 | { 89 | OSSpinLockLock(&spinLock); 90 | [cachedIcons removeAllObjects]; 91 | OSSpinLockUnlock(&spinLock); 92 | } 93 | 94 | - (NSDictionary *)extensions 95 | { 96 | return [self extensionsFilteredUsingPredicate:nil]; 97 | } 98 | 99 | - (NSDictionary *)extensionsFilteredUsingPredicate:(NSPredicate *)predicate 100 | { 101 | LMResponseBuffer buffer; 102 | if (LMConnectionSendTwoWayData(&connection, ELMessageIdGetExtensions, (CFDataRef)[NSKeyedArchiver archivedDataWithRootObject:predicate], &buffer)) 103 | return nil; 104 | id result = LMResponseConsumePropertyList(&buffer); 105 | return [result isKindOfClass:[NSDictionary class]] ? result : nil; 106 | } 107 | 108 | 109 | static NSInteger DictionaryTextComparator(id a, id b, void *context) 110 | { 111 | return [[(NSDictionary *)context objectForKey:a] localizedCaseInsensitiveCompare:[(NSDictionary *)context objectForKey:b]]; 112 | } 113 | 114 | - (NSDictionary *)extensionsFilteredUsingPredicate:(NSPredicate *)predicate sysVerAvaliable:(BOOL)sysVerAvaliable titleSortedIdentifiers:(NSArray **)outSortedByTitle 115 | { 116 | LMResponseBuffer buffer; 117 | if (LMConnectionSendTwoWayData(&connection, sysVerAvaliable ? ELMessageIdGetAvaliableExtensions : ELMessageIdGetExtensions, (CFDataRef)[NSKeyedArchiver archivedDataWithRootObject:predicate], &buffer)) 118 | return nil; 119 | NSDictionary *result = LMResponseConsumePropertyList(&buffer); 120 | if (![result isKindOfClass:[NSDictionary class]]) 121 | return nil; 122 | if (outSortedByTitle) { 123 | // Generate a sorted list of apps 124 | *outSortedByTitle = [[result allKeys] sortedArrayUsingFunction:DictionaryTextComparator context:result]; 125 | } 126 | return result; 127 | } 128 | 129 | 130 | - (id)valueForKeyPath:(NSString *)keyPath forDisplayIdentifier:(NSString *)displayIdentifier 131 | { 132 | if (!keyPath || !displayIdentifier) 133 | return nil; 134 | LMResponseBuffer buffer; 135 | if (LMConnectionSendTwoWayPropertyList(&connection, ELMessageIdValueForKeyPath, [NSDictionary dictionaryWithObjectsAndKeys:keyPath, @"key", displayIdentifier, @"displayIdentifier", nil], &buffer)) 136 | return nil; 137 | return LMResponseConsumePropertyList(&buffer); 138 | } 139 | 140 | - (id)valueForKey:(NSString *)key forDisplayIdentifier:(NSString *)displayIdentifier 141 | { 142 | if (!key || !displayIdentifier) 143 | return nil; 144 | LMResponseBuffer buffer; 145 | if (LMConnectionSendTwoWayPropertyList(&connection, ELMessageIdValueForKey, [NSDictionary dictionaryWithObjectsAndKeys:key, @"key", displayIdentifier, @"displayIdentifier", nil], &buffer)) 146 | return nil; 147 | return LMResponseConsumePropertyList(&buffer); 148 | } 149 | 150 | - (void)postNotificationWithUserInfo:(NSDictionary *)userInfo 151 | { 152 | [[NSNotificationCenter defaultCenter] postNotificationName:ALIconLoadedNotification object:self userInfo:userInfo]; 153 | } 154 | 155 | - (CGImageRef)copyIconOfSize:(ALApplicationIconSize)iconSize forDisplayIdentifier:(NSString *)displayIdentifier 156 | { 157 | if (iconSize <= 0) 158 | return NULL; 159 | LSBundleProxy * bundle = (LSBundleProxy *)[[LSPlugInKitProxy pluginKitProxyForIdentifier:displayIdentifier] containingBundle]; 160 | NSString * containingBundleId = [bundle valueForKey:@"bundleIdentifier"]; 161 | if (!containingBundleId || containingBundleId.length == 0) { 162 | return NULL; 163 | } 164 | NSString *key = [displayIdentifier stringByAppendingFormat:@"#%f", (CGFloat)iconSize]; 165 | OSSpinLockLock(&spinLock); 166 | CGImageRef result = (CGImageRef)[cachedIcons objectForKey:key]; 167 | if (result) { 168 | result = CGImageRetain(result); 169 | OSSpinLockUnlock(&spinLock); 170 | return result; 171 | } 172 | OSSpinLockUnlock(&spinLock); 173 | if (iconSize == ALApplicationIconSizeSmall) { 174 | result = [UIImage _applicationIconImageForBundleIdentifier:containingBundleId format:0 scale:[UIScreen mainScreen].scale].CGImage; 175 | if (result) 176 | goto skip; 177 | } 178 | LMResponseBuffer buffer; 179 | if (LMConnectionSendTwoWayPropertyList(&connection, ELMessageIdIconForSize, [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:iconSize], @"iconSize", displayIdentifier, @"displayIdentifier", nil], &buffer)) 180 | return NULL; 181 | result = [LMResponseConsumeImage(&buffer) CGImage]; 182 | if (!result) 183 | return NULL; 184 | skip: 185 | OSSpinLockLock(&spinLock); 186 | [cachedIcons setObject:(id)result forKey:key]; 187 | OSSpinLockUnlock(&spinLock); 188 | NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys: 189 | [NSNumber numberWithInteger:iconSize], ALIconSizeKey, 190 | displayIdentifier, ALDisplayIdentifierKey, 191 | nil]; 192 | if ([NSThread isMainThread]) 193 | [self performSelector:@selector(postNotificationWithUserInfo:) withObject:userInfo afterDelay:0.0]; 194 | else 195 | [self performSelectorOnMainThread:@selector(postNotificationWithUserInfo:) withObject:userInfo waitUntilDone:NO]; 196 | return CGImageRetain(result); 197 | } 198 | 199 | - (UIImage *)iconOfSize:(ALApplicationIconSize)iconSize forDisplayIdentifier:(NSString *)displayIdentifier 200 | { 201 | CGImageRef image = [self copyIconOfSize:iconSize forDisplayIdentifier:displayIdentifier]; 202 | if (!image) 203 | return nil; 204 | UIImage *result; 205 | if ([UIImage respondsToSelector:@selector(imageWithCGImage:scale:orientation:)]) { 206 | CGFloat scale = (CGImageGetWidth(image) + CGImageGetHeight(image)) / (CGFloat)(iconSize + iconSize); 207 | result = [UIImage imageWithCGImage:image scale:scale orientation:0]; 208 | } else { 209 | result = [UIImage imageWithCGImage:image]; 210 | } 211 | CGImageRelease(image); 212 | return result; 213 | } 214 | 215 | - (BOOL)hasCachedIconOfSize:(ALApplicationIconSize)iconSize forDisplayIdentifier:(NSString *)displayIdentifier 216 | { 217 | NSString *key = [displayIdentifier stringByAppendingFormat:@"#%f", (CGFloat)iconSize]; 218 | OSSpinLockLock(&spinLock); 219 | id result = [cachedIcons objectForKey:key]; 220 | OSSpinLockUnlock(&spinLock); 221 | return result != nil; 222 | } 223 | 224 | @end 225 | 226 | @implementation ELExtensionListImpl 227 | 228 | static void processMessage(SInt32 messageId, mach_port_t replyPort, CFDataRef data) 229 | { 230 | switch (messageId) { 231 | case ELMessageIdGetExtensions: { 232 | NSDictionary *result; 233 | if (data && CFDataGetLength(data)) { 234 | NSPredicate *predicate = [NSKeyedUnarchiver unarchiveObjectWithData:(NSData *)data]; 235 | @try { 236 | result = [predicate isKindOfClass:[NSPredicate class]] ? [sharedExtensionList extensionsFilteredUsingPredicate:predicate] : [sharedExtensionList extensions]; 237 | } 238 | @catch (NSException *exception) { 239 | NSLog(@"ExtensionList: In call to extensionsFilteredUsingPredicate:%@ trapped %@", predicate, exception); 240 | break; 241 | } 242 | } else { 243 | result = [sharedExtensionList extensions]; 244 | } 245 | LMSendPropertyListReply(replyPort, result); 246 | return; 247 | } 248 | case ELMessageIdGetAvaliableExtensions: { 249 | NSDictionary *result; 250 | if (data && CFDataGetLength(data)) { 251 | NSPredicate *predicate = [NSKeyedUnarchiver unarchiveObjectWithData:(NSData *)data]; 252 | @try { 253 | result = [predicate isKindOfClass:[NSPredicate class]] ? [sharedExtensionList extensionsFilteredUsingPredicate:predicate sysVerAvaliable:YES titleSortedIdentifiers:NULL] : [sharedExtensionList extensions]; 254 | } 255 | @catch (NSException *exception) { 256 | NSLog(@"AppList: In call to applicationsFilteredUsingPredicate:%@ onlyVisible:YES titleSortedIdentifiers:NULL trapped %@", predicate, exception); 257 | break; 258 | } 259 | } else { 260 | result = [sharedExtensionList extensions]; 261 | } 262 | LMSendPropertyListReply(replyPort, result); 263 | return; 264 | } 265 | case ELMessageIdIconForSize: { 266 | if (!data) 267 | break; 268 | NSDictionary *params = [NSPropertyListSerialization propertyListFromData:(NSData *)data mutabilityOption:0 format:NULL errorDescription:NULL]; 269 | if (![params isKindOfClass:[NSDictionary class]]) 270 | break; 271 | id iconSize = [params objectForKey:@"iconSize"]; 272 | if (![iconSize respondsToSelector:@selector(floatValue)]) 273 | break; 274 | NSString *displayIdentifier = [params objectForKey:@"displayIdentifier"]; 275 | if (![displayIdentifier isKindOfClass:[NSString class]]) 276 | break; 277 | CGImageRef result = [sharedExtensionList copyIconOfSize:[iconSize floatValue] forDisplayIdentifier:displayIdentifier]; 278 | if (result) { 279 | LMSendImageReply(replyPort, [UIImage imageWithCGImage:result]); 280 | CGImageRelease(result); 281 | return; 282 | } 283 | break; 284 | } 285 | case ELMessageIdValueForKeyPath: 286 | case ELMessageIdValueForKey: { 287 | if (!data) 288 | break; 289 | NSDictionary *params = [NSPropertyListSerialization propertyListFromData:(NSData *)data mutabilityOption:0 format:NULL errorDescription:NULL]; 290 | if (![params isKindOfClass:[NSDictionary class]]) 291 | break; 292 | NSString *key = [params objectForKey:@"key"]; 293 | Class stringClass = [NSString class]; 294 | if (![key isKindOfClass:stringClass]) 295 | break; 296 | NSString *displayIdentifier = [params objectForKey:@"displayIdentifier"]; 297 | if (![displayIdentifier isKindOfClass:stringClass]) 298 | break; 299 | id result; 300 | @try { 301 | result = messageId == ELMessageIdValueForKeyPath ? [sharedExtensionList valueForKeyPath:key forDisplayIdentifier:displayIdentifier] : [sharedExtensionList valueForKey:key forDisplayIdentifier:displayIdentifier]; 302 | } 303 | @catch (NSException *exception) { 304 | NSLog(@"AppList: In call to valueForKey%s:%@ forDisplayIdentifier:%@ trapped %@", messageId == ELMessageIdValueForKeyPath ? "Path" : "", key, displayIdentifier, exception); 305 | break; 306 | } 307 | LMSendPropertyListReply(replyPort, result); 308 | return; 309 | } 310 | case ELMessageIdGetExtensionCount: { 311 | LMSendIntegerReply(replyPort, [sharedExtensionList extensionCount]); 312 | return; 313 | } 314 | } 315 | LMSendReply(replyPort, NULL, 0); 316 | } 317 | 318 | static void machPortCallback(CFMachPortRef port, void *bytes, CFIndex size, void *info) 319 | { 320 | LMMessage *request = (LMMessage *)bytes; 321 | if (size < sizeof(LMMessage)) { 322 | LMSendReply(request->head.msgh_remote_port, NULL, 0); 323 | LMResponseBufferFree((LMResponseBuffer *)bytes); 324 | return; 325 | } 326 | // Send Response 327 | const void *data = LMMessageGetData(request); 328 | size_t length = LMMessageGetDataLength(request); 329 | mach_port_t replyPort = request->head.msgh_remote_port; 330 | CFDataRef cfdata = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)(data ?: &data), length, kCFAllocatorNull); 331 | processMessage(request->head.msgh_id, replyPort, cfdata); 332 | if (cfdata) 333 | CFRelease(cfdata); 334 | LMResponseBufferFree((LMResponseBuffer *)bytes); 335 | } 336 | 337 | - (id)init 338 | { 339 | if ((self = [super init])) { 340 | kern_return_t err = LMStartService(connection.serverName, CFRunLoopGetCurrent(), machPortCallback); 341 | if (err) { 342 | NSLog(@"AppList: Unable to register mach server with error %x", err); 343 | } 344 | } 345 | return self; 346 | } 347 | 348 | static LSApplicationWorkspace *appWorkspace(void); 349 | static LSPlugInKitProxy *extensionWithPluginIdentifier(NSString *pluginIdentifier); 350 | 351 | static inline NSMutableDictionary *dictionaryOfExtensionsList(id extensions) 352 | { 353 | NSMutableDictionary *result = [NSMutableDictionary dictionary]; 354 | for (LSPlugInKitProxy *app in extensions) { 355 | NSString *displayName = [[app localizedName] description]; 356 | if (displayName) { 357 | NSString *displayIdentifier = [[app pluginIdentifier] description]; 358 | if (displayIdentifier) { 359 | [result setObject:displayName forKey:displayIdentifier]; 360 | } 361 | } 362 | } 363 | return result; 364 | } 365 | 366 | - (NSDictionary *)extensions { 367 | return dictionaryOfExtensionsList([appWorkspace() installedPlugins]); 368 | } 369 | 370 | -(NSInteger)extensionCount { 371 | return [[appWorkspace() installedPlugins] count]; 372 | } 373 | 374 | -(NSDictionary *)extensionsFilteredUsingPredicate:(NSPredicate *)predicate { 375 | NSArray *plugins = [appWorkspace() installedPlugins]; 376 | if (predicate) 377 | plugins = [plugins filteredArrayUsingPredicate:predicate]; 378 | return dictionaryOfExtensionsList(plugins); 379 | } 380 | 381 | - (NSDictionary *)extensionsFilteredUsingPredicate:(NSPredicate *)predicate sysVerAvaliable:(BOOL)sysVerAvaliable titleSortedIdentifiers:(NSArray **)outSortedByTitle 382 | { 383 | NSArray *apps = [appWorkspace() installedPlugins]; 384 | if (predicate) 385 | apps = [apps filteredArrayUsingPredicate:predicate]; 386 | NSMutableDictionary *result; 387 | if (sysVerAvaliable) { 388 | float sysVer = [UIDevice currentDevice].systemVersion.floatValue; 389 | result = dictionaryOfExtensionsList([apps filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"infoPlist['MinimumOSVersion'].floatValue <= %f",sysVer]]); 390 | } else { 391 | result = dictionaryOfExtensionsList(apps); 392 | } 393 | if (outSortedByTitle) { 394 | // Generate a sorted list of apps 395 | *outSortedByTitle = [[result allKeys] sortedArrayUsingFunction:DictionaryTextComparator context:result]; 396 | } 397 | return result; 398 | } 399 | 400 | - (id)valueForKeyPath:(NSString *)keyPath forDisplayIdentifier:(NSString *)displayIdentifier 401 | { 402 | return [extensionWithPluginIdentifier(displayIdentifier) valueForKeyPath:keyPath]; 403 | } 404 | 405 | - (id)valueForKey:(NSString *)keyPath forDisplayIdentifier:(NSString *)displayIdentifier 406 | { 407 | return [extensionWithPluginIdentifier(displayIdentifier) valueForKey:keyPath]; 408 | } 409 | 410 | - (CGImageRef)copyIconOfSize:(ALApplicationIconSize)iconSize forDisplayIdentifier:(NSString *)displayIdentifier 411 | { 412 | if (![NSThread isMainThread]) { 413 | return [super copyIconOfSize:iconSize forDisplayIdentifier:displayIdentifier]; 414 | } 415 | return NULL; 416 | } 417 | 418 | @end 419 | 420 | static LSApplicationWorkspace *appWorkspace(void) 421 | { 422 | static LSApplicationWorkspace *cached; 423 | LSApplicationWorkspace *result = cached; 424 | if (!result) { 425 | result = cached = (LSApplicationWorkspace *)[%c(LSApplicationWorkspace) defaultWorkspace]; 426 | } 427 | return result; 428 | } 429 | 430 | static LSPlugInKitProxy *extensionWithPluginIdentifier(NSString *displayIdentifier) 431 | { 432 | return [%c(LSPlugInKitProxy) pluginKitProxyForIdentifier:displayIdentifier]; 433 | } 434 | 435 | %ctor 436 | { 437 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 438 | if (%c(SBIconModel)) { 439 | sharedExtensionList = [[ELExtensionListImpl alloc] init]; 440 | } 441 | [pool drain]; 442 | } 443 | -------------------------------------------------------------------------------- /ExtensionList/Package/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: com.zidaneno5.ExtensionList 2 | Name: ExtensionList 3 | Version: 1.0-1 4 | Description: Provide a list of App Extensions. 5 | Section: System 6 | Depends: firmware (>= 8.0), mobilesubstrate, com.rpetrich.rocketbootstrap 7 | Conflicts: com.a3tweaks.polus (<< 2.0.7) 8 | Replaces: 9 | Priority: optional 10 | Architecture: iphoneos-arm 11 | Author: duyongchao 12 | dev: zidaneno5 13 | Homepage: 14 | Depiction: 15 | Maintainer: duyongchao 16 | Icon: 17 | 18 | -------------------------------------------------------------------------------- /ExtensionList/Package/Library/MobileSubstrate/DynamicLibraries/ExtensionList.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zidaneno5/ExtensionList/c71483d35f709886d49eaf8194eee6d3066f7347/ExtensionList/Package/Library/MobileSubstrate/DynamicLibraries/ExtensionList.dylib -------------------------------------------------------------------------------- /ExtensionList/Package/Library/MobileSubstrate/DynamicLibraries/ExtensionList.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Filter 6 | 7 | Bundles 8 | 9 | com.apple.springboard 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /ExtensionList/PackageVersion.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BugFix 6 | 7 | Major 8 | 1 9 | Minor 10 | 0 11 | PackageRevision 12 | 1 13 | Stage 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ExtensionListSettings/ELExtensionPreferenceViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionListSettingsController.h 3 | // ExtensionListSettings 4 | // 5 | // Created by duyongchao on 2017/3/13. 6 | // Copyright (c) 2017年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import "ELExtensionTableDataSource.h" 13 | #import "ELValueCell.h" 14 | 15 | @interface PSSpecifier (iOS5) 16 | @property (retain, nonatomic) NSString *identifier; 17 | @end 18 | 19 | @interface PSListController (iOS4) 20 | - (PSViewController *)controllerForSpecifier:(PSSpecifier *)specifier; 21 | @end 22 | 23 | @class ELPreferencesTableDataSource; 24 | 25 | @interface ELExtensionPreferenceViewController : PSListController { 26 | @private 27 | ELPreferencesTableDataSource *_dataSource; 28 | UITableView *_tableView; 29 | NSString *_navigationTitle; 30 | NSArray *descriptors; 31 | id settingsDefaultValue; 32 | NSString *settingsPath; 33 | NSString *preferencesKey; 34 | NSMutableDictionary *settings; 35 | NSString *settingsKeyPrefix; 36 | NSString *settingsChangeNotification; 37 | BOOL singleEnabledMode; 38 | } 39 | 40 | - (id)initForContentSize:(CGSize)size; 41 | 42 | @property (nonatomic, retain) NSString *navigationTitle; 43 | //@property (nonatomic, readonly) UITableView *tableView; 44 | @property (nonatomic, readonly) ELExtensionTableDataSource *dataSource; 45 | 46 | - (void)cellAtIndexPath:(NSIndexPath *)indexPath didChangeToValue:(id)newValue; 47 | - (id)valueForCellAtIndexPath:(NSIndexPath *)indexPath; 48 | - (id)valueTitleForCellAtIndexPath:(NSIndexPath *)indexPath; 49 | 50 | @end 51 | 52 | @interface ELPreferencesTableDataSource : ELExtensionTableDataSource { 53 | @private 54 | ELExtensionPreferenceViewController *_controller; 55 | } 56 | 57 | - (id)initWithController:(ELExtensionPreferenceViewController *)controller; 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /ExtensionListSettings/ELExtensionPreferenceViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionListSettingsController.m 3 | // ExtensionListSettings 4 | // 5 | // Created by duyongchao on 2017/3/13. 6 | // Copyright (c) 2017年 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "ELExtensionPreferenceViewController.h" 10 | #import "preferenceloader/prefs.h" 11 | #import 12 | #import 13 | #include 14 | #include 15 | 16 | #import "ExtensionList.h" 17 | 18 | 19 | 20 | @implementation ELExtensionPreferenceViewController 21 | 22 | - (id)initForContentSize:(CGSize)size 23 | { 24 | if ([PSViewController instancesRespondToSelector:@selector(initForContentSize:)]) 25 | self = [super initForContentSize:size]; 26 | else 27 | self = [super init]; 28 | if (self) { 29 | CGRect frame; 30 | frame.origin = CGPointZero; 31 | frame.size = size; 32 | _tableView = [[UITableView alloc] initWithFrame:frame style:UITableViewStyleGrouped]; 33 | _dataSource = [[ELPreferencesTableDataSource alloc] initWithController:self]; 34 | [_tableView setDataSource:_dataSource]; 35 | [_tableView setDelegate:_dataSource]; 36 | [_tableView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 37 | _dataSource.tableView = _tableView; 38 | } 39 | return self; 40 | } 41 | 42 | - (void)dealloc 43 | { 44 | if (settingsChangeNotification) { 45 | CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), self, (CFStringRef)settingsChangeNotification, NULL); 46 | } 47 | [_tableView setDelegate:nil]; 48 | [_tableView setDataSource:nil]; 49 | [_tableView release]; 50 | _dataSource.tableView = nil; 51 | [_dataSource release]; 52 | [settingsDefaultValue release]; 53 | [settingsPath release]; 54 | [preferencesKey release]; 55 | [settingsKeyPrefix release]; 56 | [settingsChangeNotification release]; 57 | [_navigationTitle release]; 58 | [super dealloc]; 59 | } 60 | 61 | @synthesize /*tableView = _tableView,*/ dataSource = _dataSource, navigationTitle = _navigationTitle; 62 | 63 | 64 | - (void)setNavigationTitle:(NSString *)navigationTitle 65 | { 66 | [_navigationTitle autorelease]; 67 | _navigationTitle = [navigationTitle retain]; 68 | if ([self respondsToSelector:@selector(navigationItem)]) 69 | [[self navigationItem] setTitle:_navigationTitle]; 70 | } 71 | 72 | - (void)_updateSections 73 | { 74 | NSInteger index = 0; 75 | for (NSDictionary *descriptor in descriptors) { 76 | NSString *predicateFormat = [descriptor objectForKey:ALSectionDescriptorVisibilityPredicateKey]; 77 | if (!predicateFormat) { 78 | index++; 79 | continue; 80 | } 81 | NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateFormat]; 82 | BOOL visible = [predicate evaluateWithObject:settings]; 83 | NSArray *existingDescriptors = [_dataSource sectionDescriptors]; 84 | BOOL already = [existingDescriptors count] > index ? [existingDescriptors objectAtIndex:index] == descriptor : NO; 85 | if (visible) { 86 | if (!already) { 87 | [_dataSource insertSectionDescriptor:descriptor atIndex:index]; 88 | } 89 | index++; 90 | } else { 91 | if (already) { 92 | [_dataSource removeSectionDescriptorAtIndex:index]; 93 | } 94 | } 95 | } 96 | } 97 | 98 | - (void)settingsChanged 99 | { 100 | [settings release]; 101 | BOOL skipOnDiskRead = NO; 102 | if (preferencesKey) { 103 | CFPreferencesAppSynchronize((CFStringRef)preferencesKey); 104 | CFArrayRef keys = CFPreferencesCopyKeyList((CFStringRef)preferencesKey, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 105 | if (keys) { 106 | if (CFArrayGetCount(keys)) { 107 | CFDictionaryRef dict = CFPreferencesCopyMultiple(keys, (CFStringRef)preferencesKey, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 108 | if (dict) { 109 | settings = [(NSDictionary *)dict mutableCopy]; 110 | skipOnDiskRead = YES; 111 | CFRelease(dict); 112 | } 113 | } 114 | CFRelease(keys); 115 | } 116 | } 117 | if (!skipOnDiskRead) { 118 | settings = [[NSMutableDictionary alloc] initWithContentsOfFile:settingsPath] ?: [[NSMutableDictionary alloc] init]; 119 | } 120 | [_tableView reloadData]; 121 | } 122 | 123 | static void SettingsChangedNotificationFired(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) 124 | { 125 | [(ELExtensionPreferenceViewController *)observer settingsChanged]; 126 | } 127 | 128 | - (void)loadFromSpecifier:(PSSpecifier *)specifier 129 | { 130 | [self setNavigationTitle:[specifier propertyForKey:@"ALNavigationTitle"] ?: [specifier name]]; 131 | singleEnabledMode = [[specifier propertyForKey:@"ALSingleEnabledMode"] boolValue]; 132 | 133 | [descriptors release]; 134 | descriptors = [specifier propertyForKey:@"ALSectionDescriptors"]; 135 | if (descriptors == nil) { 136 | NSString *defaultCellClass = singleEnabledMode ? @"ELCheckCell" : @"ELSwitchCell"; 137 | NSNumber *iconSize = [NSNumber numberWithUnsignedInteger:ALApplicationIconSizeSmall]; 138 | descriptors = [NSArray arrayWithObjects: 139 | [NSDictionary dictionaryWithObjectsAndKeys: 140 | @"System Applications", ALSectionDescriptorTitleKey, 141 | @"isSystemApplication = TRUE", ALSectionDescriptorPredicateKey, 142 | defaultCellClass, ALSectionDescriptorCellClassNameKey, 143 | iconSize, ALSectionDescriptorIconSizeKey, 144 | (id)kCFBooleanTrue, ALSectionDescriptorSuppressHiddenAppsKey, 145 | nil], 146 | [NSDictionary dictionaryWithObjectsAndKeys: 147 | @"User Applications", ALSectionDescriptorTitleKey, 148 | @"isSystemApplication = FALSE", ALSectionDescriptorPredicateKey, 149 | defaultCellClass, ALSectionDescriptorCellClassNameKey, 150 | iconSize, ALSectionDescriptorIconSizeKey, 151 | (id)kCFBooleanTrue, ALSectionDescriptorSuppressHiddenAppsKey, 152 | nil], 153 | nil]; 154 | } 155 | [_dataSource setSectionDescriptors:descriptors]; 156 | [descriptors retain]; 157 | 158 | NSString *bundlePath = [specifier propertyForKey:@"ALLocalizationBundle"]; 159 | _dataSource.localizationBundle = bundlePath ? [NSBundle bundleWithPath:bundlePath] : nil; 160 | 161 | [settingsDefaultValue release]; 162 | settingsDefaultValue = [[specifier propertyForKey:@"ALSettingsDefaultValue"] retain]; 163 | 164 | [settingsPath release]; 165 | settingsPath = [[specifier propertyForKey:@"ALSettingsPath"] retain]; 166 | [preferencesKey release]; 167 | if ((kCFCoreFoundationVersionNumber >= 1000) && [settingsPath hasPrefix:@"/var/mobile/Library/Preferences/"] && [settingsPath hasSuffix:@".plist"]) { 168 | preferencesKey = [[[settingsPath lastPathComponent] stringByDeletingPathExtension] retain]; 169 | } else { 170 | preferencesKey = nil; 171 | } 172 | 173 | [settingsKeyPrefix release]; 174 | settingsKeyPrefix = [[specifier propertyForKey:singleEnabledMode ? @"ALSettingsKey" : @"ALSettingsKeyPrefix"] ?: @"ALValue-" retain]; 175 | 176 | settings = [[NSMutableDictionary alloc] initWithContentsOfFile:settingsPath] ?: [[NSMutableDictionary alloc] init]; 177 | 178 | if (settingsChangeNotification) { 179 | CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), self, (CFStringRef)settingsChangeNotification, NULL); 180 | [settingsChangeNotification release]; 181 | } 182 | settingsChangeNotification = [specifier propertyForKey:@"ALChangeNotification"]; 183 | if (settingsChangeNotification) { 184 | [settingsChangeNotification retain]; 185 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), self, SettingsChangedNotificationFired, (CFStringRef)settingsChangeNotification, NULL, CFNotificationSuspensionBehaviorDeliverImmediately); 186 | } 187 | 188 | id temp = [specifier propertyForKey:@"ALAllowsSelection"]; 189 | [_tableView setAllowsSelection:temp ? [temp boolValue] : singleEnabledMode]; 190 | 191 | [self _updateSections]; 192 | [_tableView reloadData]; 193 | } 194 | 195 | - (void)setSpecifier:(PSSpecifier *)specifier 196 | { 197 | [self loadFromSpecifier:specifier]; 198 | [super setSpecifier:specifier]; 199 | } 200 | 201 | - (void)viewWillBecomeVisible:(void *)source 202 | { 203 | if (source) 204 | [self loadFromSpecifier:(PSSpecifier *)source]; 205 | [super viewWillBecomeVisible:source]; 206 | } 207 | 208 | - (void)setTitle:(NSString *)title 209 | { 210 | [super setTitle:[self navigationTitle]]; 211 | } 212 | 213 | - (UIView *)view 214 | { 215 | UIView *result = [super view]; 216 | if (!_tableView.superview) { 217 | _tableView.frame = result.bounds; 218 | [_tableView setScrollsToTop:YES]; 219 | [result addSubview:_tableView]; 220 | if ([result respondsToSelector:@selector(setScrollsToTop:)]) { 221 | [(UIScrollView *)result setScrollsToTop:NO]; 222 | } 223 | if ([result respondsToSelector:@selector(setScrollEnabled:)]) { 224 | [(UIScrollView *)result setScrollEnabled:NO]; 225 | } 226 | } 227 | return result; 228 | } 229 | 230 | 231 | - (CGSize)contentSize 232 | { 233 | return [_tableView frame].size; 234 | } 235 | 236 | - (void)cellAtIndexPath:(NSIndexPath *)indexPath didChangeToValue:(id)newValue 237 | { 238 | id cellDescriptor = [_dataSource cellDescriptorForIndexPath:indexPath]; 239 | if ([cellDescriptor isKindOfClass:[NSDictionary class]]) { 240 | NSString *key = [cellDescriptor objectForKey:@"ALSettingsKey"]; 241 | [settings setObject:newValue forKey:key]; 242 | if (preferencesKey) { 243 | CFPreferencesSetAppValue((CFStringRef)key, newValue, (CFStringRef)preferencesKey); 244 | } 245 | } else if (singleEnabledMode) { 246 | if ([newValue boolValue]) { 247 | [settings setObject:cellDescriptor forKey:settingsKeyPrefix]; 248 | if (preferencesKey) 249 | CFPreferencesSetAppValue((CFStringRef)settingsKeyPrefix, (CFPropertyListRef)cellDescriptor, (CFStringRef)preferencesKey); 250 | for (NSIndexPath *otherIndexPath in [_tableView indexPathsForVisibleRows]) { 251 | if (![otherIndexPath isEqual:indexPath]) { 252 | ELValueCell *otherCell = (ELValueCell *)[_tableView cellForRowAtIndexPath:otherIndexPath]; 253 | if ([otherCell respondsToSelector:@selector(loadValue:withTitle:)]) { 254 | [otherCell loadValue:(id)kCFBooleanFalse withTitle:[self valueTitleForCellAtIndexPath:otherIndexPath]]; 255 | } 256 | } 257 | } 258 | } else if ([[settings objectForKey:settingsKeyPrefix] isEqual:cellDescriptor]) { 259 | [settings removeObjectForKey:settingsKeyPrefix]; 260 | if (preferencesKey) 261 | CFPreferencesSetAppValue((CFStringRef)settingsKeyPrefix, NULL, (CFStringRef)preferencesKey); 262 | } 263 | } else { 264 | NSString *key = [settingsKeyPrefix stringByAppendingString:cellDescriptor ?: @""]; 265 | [settings setObject:newValue forKey:key]; 266 | if (preferencesKey) 267 | CFPreferencesSetAppValue((CFStringRef)key, newValue, (CFStringRef)preferencesKey); 268 | } 269 | if (settingsPath) 270 | [settings writeToFile:settingsPath atomically:YES]; 271 | if (preferencesKey) 272 | CFPreferencesAppSynchronize((CFStringRef)preferencesKey); 273 | if (settingsChangeNotification) { 274 | CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), self, (CFStringRef)settingsChangeNotification, NULL); 275 | notify_post([settingsChangeNotification UTF8String]); 276 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), self, SettingsChangedNotificationFired, (CFStringRef)settingsChangeNotification, NULL, CFNotificationSuspensionBehaviorDeliverImmediately); 277 | } 278 | [self _updateSections]; 279 | } 280 | 281 | - (id)valueForCellAtIndexPath:(NSIndexPath *)indexPath 282 | { 283 | id cellDescriptor = [_dataSource cellDescriptorForIndexPath:indexPath]; 284 | if ([cellDescriptor isKindOfClass:[NSDictionary class]]) { 285 | return [settings objectForKey:[cellDescriptor objectForKey:@"ALSettingsKey"]] ?: [cellDescriptor objectForKey:@"ALSettingsDefaultValue"]; 286 | } 287 | if (singleEnabledMode) { 288 | return [[settings objectForKey:settingsKeyPrefix] isEqualToString:cellDescriptor] ? (id)kCFBooleanTrue : (id)kCFBooleanFalse; 289 | } else { 290 | NSString *key = [settingsKeyPrefix stringByAppendingString:cellDescriptor ?: @""]; 291 | return [settings objectForKey:key] ?: settingsDefaultValue; 292 | } 293 | } 294 | 295 | - (id)valueTitleForCellAtIndexPath:(NSIndexPath *)indexPath 296 | { 297 | id cellDescriptor = [_dataSource cellDescriptorForIndexPath:indexPath]; 298 | if ([cellDescriptor isKindOfClass:[NSDictionary class]]) { 299 | id value = [[settings objectForKey:[cellDescriptor objectForKey:@"ALSettingsKey"]] ?: [cellDescriptor objectForKey:@"ALSettingsDefaultValue"] description]; 300 | NSArray *validValues = [cellDescriptor objectForKey:@"validValues"] ?: [settings objectForKey:@"validValues"]; 301 | NSInteger index = [validValues indexOfObject:value]; 302 | if (index == NSNotFound) 303 | return nil; 304 | NSArray *validTitles = [cellDescriptor objectForKey:@"validTitles"] ?: [settings objectForKey:@"validTitles"]; 305 | if (index >= [validTitles count]) 306 | return nil; 307 | return [validTitles objectAtIndex:index]; 308 | } 309 | id value; 310 | if (singleEnabledMode) { 311 | value = [[settings objectForKey:settingsKeyPrefix] isEqualToString:cellDescriptor] ? @"1" : @"0"; 312 | } else { 313 | NSString *key = [settingsKeyPrefix stringByAppendingString:cellDescriptor ?: @""]; 314 | value = [[settings objectForKey:key] ?: settingsDefaultValue description]; 315 | } 316 | NSDictionary *sectionDescriptor = [descriptors objectAtIndex:indexPath.section]; 317 | NSArray *validValues = [sectionDescriptor objectForKey:@"validValues"] ?: [settings objectForKey:@"validValues"]; 318 | NSInteger index = [validValues indexOfObject:value]; 319 | if (index == NSNotFound) 320 | return nil; 321 | NSArray *validTitles = [sectionDescriptor objectForKey:@"validTitles"] ?: [settings objectForKey:@"validTitles"]; 322 | if (index >= [validTitles count]) 323 | return nil; 324 | return [validTitles objectAtIndex:index]; 325 | } 326 | 327 | - (void)pushController:(id)controller 328 | { 329 | [super pushController:controller]; 330 | [controller setParentController:self]; 331 | } 332 | 333 | static id RecursivelyApplyMacro(id input, NSString *macro, NSString *value); 334 | 335 | static NSDictionary *RecursivelyApplyMacroDictionary(NSDictionary *input, NSString *macro, NSString *value) { 336 | NSMutableDictionary *result = nil; 337 | for (id key in input) { 338 | id object = [input objectForKey:key]; 339 | id newObject = RecursivelyApplyMacro(object, macro, value); 340 | if (object != newObject) { 341 | if (!result) { 342 | result = [[input mutableCopy] autorelease]; 343 | } 344 | [result setObject:newObject forKey:key]; 345 | } 346 | } 347 | return result ?: input; 348 | } 349 | 350 | static NSArray *RecursivelyApplyMacroArray(NSArray *input, NSString *macro, NSString *value) { 351 | NSMutableArray *result = nil; 352 | NSInteger i = 0; 353 | for (id object in input) { 354 | id newObject = RecursivelyApplyMacro(object, macro, value); 355 | if (object != newObject) { 356 | if (!result) { 357 | result = [[input mutableCopy] autorelease]; 358 | } 359 | [result replaceObjectAtIndex:i withObject:newObject]; 360 | } 361 | i++; 362 | } 363 | return result ?: input; 364 | } 365 | 366 | static NSString *RecursivelyApplyMacroString(NSString *input, NSString *macro, NSString *value) { 367 | id result = [input stringByReplacingOccurrencesOfString:macro withString:value]; 368 | return [result isEqualToString:input] ? input : result; 369 | } 370 | 371 | static id RecursivelyApplyMacro(id input, NSString *macro, NSString *value) { 372 | if ([input isKindOfClass:[NSString class]]) 373 | return RecursivelyApplyMacroString(input, macro, value); 374 | if ([input isKindOfClass:[NSDictionary class]]) 375 | return RecursivelyApplyMacroDictionary(input, macro, value); 376 | if ([input isKindOfClass:[NSArray class]]) 377 | return RecursivelyApplyMacroArray(input, macro, value); 378 | return input; 379 | } 380 | 381 | - (id)appliedValueForKey:(NSString *)key inCellDescriptor:(id)cellDescriptor sectionDescriptor:(NSDictionary *)sectionDescriptor 382 | { 383 | if ([cellDescriptor isKindOfClass:[NSDictionary class]]) { 384 | return [cellDescriptor objectForKey:key]; 385 | } 386 | if ([cellDescriptor isKindOfClass:[NSString class]]) { 387 | NSString *macro = [sectionDescriptor objectForKey:@"display-identifier-macro"]; 388 | id result = RecursivelyApplyMacro([sectionDescriptor objectForKey:key], macro, cellDescriptor); 389 | NSLog(@" = %@", result); 390 | return result; 391 | } 392 | return nil; 393 | } 394 | 395 | - (PSSpecifier *)specifierForIndexPath:(NSIndexPath *)indexPath 396 | { 397 | NSInteger section = indexPath.section; 398 | [_dataSource waitUntilDate:nil forContentInSectionAtIndex:section]; 399 | id cellDescriptor = [_dataSource cellDescriptorForIndexPath:indexPath]; 400 | if (!cellDescriptor) { 401 | NSLog(@"AppList: no cell descriptor for cell!"); 402 | return nil; 403 | } 404 | NSDictionary *sectionDescriptor = [_dataSource.sectionDescriptors objectAtIndex:section]; 405 | NSDictionary *entry = [self appliedValueForKey:@"entry" inCellDescriptor:cellDescriptor sectionDescriptor:sectionDescriptor]; 406 | if (!entry) { 407 | NSLog(@"AppList: entry key missing!"); 408 | return nil; 409 | } 410 | NSString *title; 411 | if ([cellDescriptor isKindOfClass:[NSString class]]) { 412 | title = [[ELExtensionList sharedExtensionList] valueForKey:@"displayName" forDisplayIdentifier:cellDescriptor]; 413 | } else { 414 | title = [cellDescriptor objectForKey:@"text"]; 415 | } 416 | NSArray *specifiers = [self specifiersFromEntry:entry sourcePreferenceLoaderBundlePath:self.specifier.preferenceLoaderBundle.bundlePath title:[title length] ? title : @" "]; 417 | if ([specifiers count] == 0) { 418 | NSLog(@"AppList: preferenceloader failed to load specifier!"); 419 | return nil; 420 | } 421 | PSSpecifier *specifier = [specifiers objectAtIndex:0]; 422 | if ([specifier respondsToSelector:@selector(setIdentifier:)]) { 423 | [specifier setIdentifier:[NSString stringWithFormat:@"applist:%ld,%ld", (long)section, (long)indexPath.row]]; 424 | } 425 | return specifier; 426 | } 427 | 428 | - (void)showPreferencesFromCellDescriptor:(id)cellDescriptor sectionDescriptor:(NSDictionary *)sectionDescriptor indexPath:(NSIndexPath *)indexPath 429 | { 430 | PSSpecifier *specifier = [self specifierForIndexPath:indexPath]; 431 | if (specifier) { 432 | [self pushController:[self controllerForSpecifier:specifier]]; 433 | } 434 | } 435 | 436 | - (void)launchURLFromCellDescriptor:(id)cellDescriptor sectionDescriptor:(NSDictionary *)sectionDescriptor 437 | { 438 | NSString *url = [self appliedValueForKey:@"url" inCellDescriptor:cellDescriptor sectionDescriptor:sectionDescriptor]; 439 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]]; 440 | } 441 | 442 | - (PSSpecifier *)specifierForID:(NSString *)identifier 443 | { 444 | if ([identifier hasPrefix:@"applist:"]) { 445 | NSArray *components = [[identifier substringFromIndex:8] componentsSeparatedByString:@","]; 446 | if ([components count] == 2) { 447 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[[components objectAtIndex:1] integerValue] inSection:[[components objectAtIndex:0] integerValue]]; 448 | PSSpecifier *result = [self specifierForIndexPath:indexPath]; 449 | if (result) { 450 | return result; 451 | } 452 | } 453 | } 454 | return [super specifierForID:identifier]; 455 | } 456 | 457 | @end 458 | 459 | 460 | @implementation ELPreferencesTableDataSource 461 | 462 | - (id)initWithController:(ELExtensionPreferenceViewController *)controller 463 | { 464 | if ((self = [super init])) { 465 | _controller = controller; 466 | } 467 | return self; 468 | } 469 | 470 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 471 | { 472 | id cell = [super tableView:tableView cellForRowAtIndexPath:indexPath]; 473 | if ([cell isKindOfClass:[ELValueCell class]]) { 474 | [(ELValueCell *)cell setDelegate:self]; 475 | [(ELValueCell *)cell loadValue:[_controller valueForCellAtIndexPath:indexPath] withTitle:[_controller valueTitleForCellAtIndexPath:indexPath]]; 476 | } 477 | return cell; 478 | } 479 | 480 | - (void)valueCell:(ELValueCell *)valueCell didChangeToValue:(id)newValue 481 | { 482 | [_controller cellAtIndexPath:[self.tableView indexPathForCell:valueCell] didChangeToValue:newValue]; 483 | } 484 | 485 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 486 | { 487 | id cell = [tableView cellForRowAtIndexPath:indexPath]; 488 | if ([cell respondsToSelector:@selector(didSelect)]) 489 | [cell didSelect]; 490 | id cellDescriptor = [self cellDescriptorForIndexPath:indexPath]; 491 | if (cellDescriptor) { 492 | NSDictionary *sectionDescriptor = [self.sectionDescriptors objectAtIndex:indexPath.section]; 493 | NSString *stringAction = [_controller appliedValueForKey:@"action" inCellDescriptor:cellDescriptor sectionDescriptor:sectionDescriptor]; 494 | SEL action = NSSelectorFromString([stringAction stringByAppendingString:@"FromCellDescriptor:sectionDescriptor:indexPath:"]); 495 | if ([_controller respondsToSelector:action]) { 496 | ((void (*)(ELExtensionPreferenceViewController *, SEL, id, NSDictionary *, NSIndexPath *))objc_msgSend)(_controller, action, cellDescriptor, sectionDescriptor, indexPath); 497 | } else { 498 | action = NSSelectorFromString([stringAction stringByAppendingString:@"FromCellDescriptor:sectionDescriptor:"]); 499 | if ([_controller respondsToSelector:action]) { 500 | ((void (*)(ELExtensionPreferenceViewController *, SEL, id, NSDictionary *))objc_msgSend)(_controller, action, cellDescriptor, sectionDescriptor); 501 | } else { 502 | action = NSSelectorFromString([stringAction stringByAppendingString:@"FromCellDescriptor:"]); 503 | if ([_controller respondsToSelector:action]) { 504 | ((void (*)(ELExtensionPreferenceViewController *, SEL, id))objc_msgSend)(_controller, action, cellDescriptor); 505 | } 506 | } 507 | } 508 | } 509 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 510 | } 511 | 512 | @end 513 | -------------------------------------------------------------------------------- /ExtensionListSettings/ExtensionListSettings-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.zidaneno5.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1.0 21 | NSPrincipalClass 22 | ELExtensionPreferenceViewController 23 | 24 | 25 | -------------------------------------------------------------------------------- /ExtensionListSettings/ExtensionListSettings-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'ExtensionListSettings' target in the 'ExtensionListSettings' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /ExtensionListSettings/Package/DEBIAN: -------------------------------------------------------------------------------- 1 | ../../ExtensionList/Package/DEBIAN -------------------------------------------------------------------------------- /ExtensionListSettings/Package/Library: -------------------------------------------------------------------------------- 1 | ../../ExtensionList/Package/Library -------------------------------------------------------------------------------- /ExtensionListSettings/Package/System/Library/PreferenceBundles/ExtensionListSettings.bundle/ExtensionListSettings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zidaneno5/ExtensionList/c71483d35f709886d49eaf8194eee6d3066f7347/ExtensionListSettings/Package/System/Library/PreferenceBundles/ExtensionListSettings.bundle/ExtensionListSettings -------------------------------------------------------------------------------- /ExtensionListSettings/Package/System/Library/PreferenceBundles/ExtensionListSettings.bundle/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zidaneno5/ExtensionList/c71483d35f709886d49eaf8194eee6d3066f7347/ExtensionListSettings/Package/System/Library/PreferenceBundles/ExtensionListSettings.bundle/Info.plist -------------------------------------------------------------------------------- /ExtensionListSettings/PackageVersion.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BugFix 6 | 7 | Major 8 | 1 9 | Minor 10 | 0 11 | PackageRevision 12 | 1 13 | Stage 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ExtensionListSettings/preferenceloader: -------------------------------------------------------------------------------- 1 | /Users/duyongchao/Documents/preferenceloader -------------------------------------------------------------------------------- /LSApplicationWorkspace.h: -------------------------------------------------------------------------------- 1 | /* Generated by RuntimeBrowser 2 | Image: /System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices 3 | */ 4 | 5 | @interface LSApplicationWorkspace : NSObject { 6 | NSXPCConnection * _connection; 7 | NSMutableDictionary * _createdInstallProgresses; 8 | // LSInstallProgressDelegate * _delegateProxy; 9 | BOOL _enhancedAppValidationEnabled; 10 | // LSInstallProgressList * _observedInstallProgresses; 11 | // LSApplicationWorkspaceRemoteObserver * _remoteObserver; 12 | } 13 | 14 | @property (readonly) NSXPCConnection *connection; 15 | @property (readonly) NSMutableDictionary *createdInstallProgresses; 16 | //@property (readonly) LSInstallProgressDelegate *delegateProxy; 17 | //@property (readonly) LSInstallProgressList *observedInstallProgresses; 18 | //@property (readonly) LSApplicationWorkspaceRemoteObserver *remoteObserver; 19 | 20 | // Image: /System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices 21 | 22 | + (id)activeManagedConfigurationRestrictionUUIDs; 23 | + (id)callbackQueue; 24 | + (id)defaultWorkspace; 25 | 26 | - (id)URLOverrideForURL:(id)arg1; 27 | - (id)URLSchemesOfType:(int)arg1; 28 | - (void)_LSClearSchemaCaches; 29 | - (void)_LSFailedToOpenURL:(id)arg1 withBundle:(id)arg2; 30 | - (BOOL)_LSPrivateDatabaseNeedsRebuild; 31 | - (BOOL)_LSPrivateRebuildApplicationDatabasesForSystemApps:(BOOL)arg1 internal:(BOOL)arg2 user:(BOOL)arg3; 32 | - (void)_LSPrivateSyncWithMobileInstallation; 33 | - (void)addObserver:(id)arg1; 34 | - (id)allApplications; 35 | - (id)allInstalledApplications; 36 | - (id)applicationForOpeningResource:(id)arg1; 37 | - (id)applicationForUserActivityDomainName:(id)arg1; 38 | - (id)applicationForUserActivityType:(id)arg1; 39 | - (BOOL)applicationIsInstalled:(id)arg1; 40 | - (id)applicationProxiesWithPlistFlags:(unsigned long)arg1 bundleFlags:(unsigned long long)arg2; 41 | - (id)applicationsAvailableForHandlingURLScheme:(id)arg1; 42 | - (id)applicationsAvailableForOpeningDocument:(id)arg1; 43 | - (id)applicationsAvailableForOpeningURL:(id)arg1; 44 | - (id)applicationsAvailableForOpeningURL:(id)arg1 legacySPI:(BOOL)arg2; 45 | - (id)applicationsForUserActivityType:(id)arg1; 46 | - (id)applicationsForUserActivityType:(id)arg1 limit:(unsigned int)arg2; 47 | - (id)applicationsOfType:(unsigned int)arg1; 48 | - (id)applicationsWithAudioComponents; 49 | - (id)applicationsWithUIBackgroundModes; 50 | - (id)applicationsWithVPNPlugins; 51 | - (id)bundleIdentifiersForMachOUUIDs:(id)arg1 error:(id*)arg2; 52 | - (void)clearAdvertisingIdentifier; 53 | - (void)clearCreatedProgressForBundleID:(id)arg1; 54 | - (id)connection; 55 | - (id)createdInstallProgresses; 56 | - (void)dealloc; 57 | - (id)delegateProxy; 58 | - (id)deviceIdentifierForAdvertising; 59 | - (id)deviceIdentifierForVendor; 60 | - (id)directionsApplications; 61 | - (BOOL)downgradeApplicationToPlaceholder:(id)arg1 withOptions:(id)arg2 error:(id*)arg3; 62 | - (BOOL)enhancedAppLoggingEnabled; 63 | - (void)enumerateApplicationsForSiriWithBlock:(id /* block */)arg1; 64 | - (void)enumerateApplicationsOfType:(unsigned int)arg1 block:(id /* block */)arg2; 65 | - (void)enumerateApplicationsOfType:(unsigned int)arg1 legacySPI:(BOOL)arg2 block:(id /* block */)arg3; 66 | - (void)enumerateBundlesOfType:(unsigned int)arg1 block:(id /* block */)arg2; 67 | - (void)enumerateBundlesOfType:(unsigned int)arg1 legacySPI:(BOOL)arg2 block:(id /* block */)arg3; 68 | - (void)enumerateBundlesOfType:(unsigned int)arg1 usingBlock:(id /* block */)arg2; 69 | - (void)enumeratePluginsMatchingQuery:(id)arg1 withBlock:(id /* block */)arg2; 70 | - (BOOL)establishConnection; 71 | - (BOOL)getClaimedActivityTypes:(id*)arg1 domains:(id*)arg2; 72 | - (unsigned int)getInstallTypeForOptions:(id)arg1 andApp:(id)arg2; 73 | - (void)getKnowledgeUUID:(id*)arg1 andSequenceNumber:(id*)arg2; 74 | - (id)init; 75 | - (BOOL)installApplication:(id)arg1 withOptions:(id)arg2; 76 | - (BOOL)installApplication:(id)arg1 withOptions:(id)arg2 error:(id*)arg3; 77 | - (BOOL)installApplication:(id)arg1 withOptions:(id)arg2 error:(id*)arg3 usingBlock:(id /* block */)arg4; 78 | - (id)installBundle:(id)arg1 withOptions:(id)arg2 usingBlock:(id /* block */)arg3 forApp:(id)arg4 withError:(id*)arg5 outInstallProgress:(id*)arg6; 79 | - (BOOL)installPhaseFinishedForProgress:(id)arg1; 80 | - (id)installProgressForApplication:(id)arg1 withPhase:(unsigned int)arg2; 81 | - (id)installProgressForBundleID:(id)arg1 makeSynchronous:(unsigned char)arg2; 82 | - (id)installedPlugins; 83 | - (BOOL)invalidateIconCache:(id)arg1; 84 | - (BOOL)isApplicationAvailableToOpenURL:(id)arg1 error:(id*)arg2; 85 | - (BOOL)isApplicationAvailableToOpenURL:(id)arg1 includePrivateURLSchemes:(BOOL)arg2 error:(id*)arg3; 86 | - (BOOL)isApplicationAvailableToOpenURLCommon:(id)arg1 includePrivateURLSchemes:(BOOL)arg2 error:(id*)arg3; 87 | - (id)legacyApplicationProxiesListWithType:(unsigned int)arg1; 88 | - (id)machOUUIDsForBundleIdentifiers:(id)arg1 error:(id*)arg2; 89 | - (id)observedInstallProgresses; 90 | - (BOOL)openApplicationWithBundleID:(id)arg1; 91 | - (BOOL)openSensitiveURL:(id)arg1 withOptions:(id)arg2; 92 | - (BOOL)openSensitiveURL:(id)arg1 withOptions:(id)arg2 error:(id*)arg3; 93 | - (BOOL)openURL:(id)arg1; 94 | - (BOOL)openURL:(id)arg1 withOptions:(id)arg2; 95 | - (BOOL)openURL:(id)arg1 withOptions:(id)arg2 error:(id*)arg3; 96 | - (void)openUserActivity:(id)arg1 withApplicationProxy:(id)arg2 completionHandler:(id /* block */)arg3; 97 | - (void)openUserActivity:(id)arg1 withApplicationProxy:(id)arg2 options:(id)arg3 completionHandler:(id /* block */)arg4; 98 | - (id)operationToOpenResource:(id)arg1 usingApplication:(id)arg2 uniqueDocumentIdentifier:(id)arg3 sourceIsManaged:(BOOL)arg4 userInfo:(id)arg5 delegate:(id)arg6; 99 | - (id)operationToOpenResource:(id)arg1 usingApplication:(id)arg2 uniqueDocumentIdentifier:(id)arg3 userInfo:(id)arg4; 100 | - (id)operationToOpenResource:(id)arg1 usingApplication:(id)arg2 uniqueDocumentIdentifier:(id)arg3 userInfo:(id)arg4 delegate:(id)arg5; 101 | - (id)operationToOpenResource:(id)arg1 usingApplication:(id)arg2 userInfo:(id)arg3; 102 | - (id)placeholderApplications; 103 | - (id)pluginsMatchingQuery:(id)arg1 applyFilter:(id /* block */)arg2; 104 | - (id)pluginsWithIdentifiers:(id)arg1 protocols:(id)arg2 version:(id)arg3; 105 | - (id)pluginsWithIdentifiers:(id)arg1 protocols:(id)arg2 version:(id)arg3 applyFilter:(id /* block */)arg4; 106 | - (id)pluginsWithIdentifiers:(id)arg1 protocols:(id)arg2 version:(id)arg3 withFilter:(id /* block */)arg4; 107 | - (id)privateURLSchemes; 108 | - (id)publicURLSchemes; 109 | - (BOOL)registerApplication:(id)arg1; 110 | - (BOOL)registerApplicationDictionary:(id)arg1; 111 | - (BOOL)registerApplicationDictionary:(id)arg1 withObserverNotification:(int)arg2; 112 | - (BOOL)registerBundleWithInfo:(id)arg1 options:(id)arg2 type:(unsigned int)arg3 progress:(id)arg4; 113 | - (BOOL)registerPlugin:(id)arg1; 114 | - (id)remoteObserver; 115 | - (void)removeInstallProgressForBundleID:(id)arg1; 116 | - (void)removeObserver:(id)arg1; 117 | - (id)removedSystemApplications; 118 | - (BOOL)restoreSystemApplication:(id)arg1; 119 | - (void)scanForApplicationStateChangesFromRank:(id)arg1 toRank:(id)arg2; 120 | - (void)scanForApplicationStateChangesFromWhitelist:(id)arg1 to:(id)arg2; 121 | - (void)sendApplicationStateChangedNotificationsFor:(id)arg1; 122 | - (void)sendInstallNotificationForApp:(id)arg1 withPlugins:(id)arg2; 123 | - (void)sendUninstallNotificationForApp:(id)arg1 withPlugins:(id)arg2; 124 | - (BOOL)uninstallApplication:(id)arg1 withOptions:(id)arg2; 125 | - (BOOL)uninstallApplication:(id)arg1 withOptions:(id)arg2 error:(id*)arg3 usingBlock:(id /* block */)arg4; 126 | - (BOOL)uninstallApplication:(id)arg1 withOptions:(id)arg2 usingBlock:(id /* block */)arg3; 127 | - (BOOL)uninstallSystemApplication:(id)arg1 withOptions:(id)arg2 usingBlock:(id /* block */)arg3; 128 | - (BOOL)unregisterApplication:(id)arg1; 129 | - (BOOL)unregisterPlugin:(id)arg1; 130 | - (id)unrestrictedApplications; 131 | - (BOOL)updateRecordForApp:(id)arg1 withSINF:(id)arg2 iTunesMetadata:(id)arg3 error:(id*)arg4; 132 | - (BOOL)updateSINFWithData:(id)arg1 forApplication:(id)arg2 options:(id)arg3 error:(id*)arg4; 133 | - (BOOL)updateiTunesMetadataWithData:(id)arg1 forApplication:(id)arg2 options:(id)arg3 error:(id*)arg4; 134 | 135 | // Image: /System/Library/Frameworks/SafariServices.framework/SafariServices 136 | 137 | - (void)_sf_openURL:(id)arg1 withOptions:(id)arg2 completionHandler:(id /* block */)arg3; 138 | 139 | @end 140 | -------------------------------------------------------------------------------- /LSPlugInKitProxy.h: -------------------------------------------------------------------------------- 1 | /* Generated by RuntimeBrowser 2 | Image: /System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices 3 | */ 4 | @class LSBundleProxy; 5 | 6 | @interface LSPlugInKitProxy : NSObject { 7 | LSBundleProxy * _containingBundle; 8 | BOOL _isOnSystemPartition; 9 | NSString * _originalIdentifier; 10 | NSString * _pluginIdentifier; 11 | NSUUID * _pluginUUID; 12 | NSString * _protocol; 13 | NSDate * _registrationDate; 14 | } 15 | 16 | @property (nonatomic, readonly) LSBundleProxy *containingBundle; 17 | @property (nonatomic, readonly) NSDictionary *infoPlist; 18 | @property (nonatomic, readonly) BOOL isOnSystemPartition; 19 | @property (nonatomic, readonly) NSString *originalIdentifier; 20 | @property (nonatomic, readonly) NSString *pluginIdentifier; 21 | @property (nonatomic, readonly) NSDictionary *pluginKitDictionary; 22 | @property (nonatomic, readonly) NSUUID *pluginUUID; 23 | @property (nonatomic, readonly) NSString *protocol; 24 | @property (nonatomic, readonly) NSDate *registrationDate; 25 | @property (nonatomic, readonly) NSString *teamID; 26 | 27 | // Image: /System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices 28 | 29 | + (id)containingBundleIdentifiersForPlugInBundleIdentifiers:(id)arg1 error:(id*)arg2; 30 | + (id)plugInKitProxyForPlugin:(unsigned int)arg1; 31 | //+ (id)plugInKitProxyForPlugin:(unsigned int)arg1 withContext:(const struct LSContext { struct LSDatabase {} *x1; }*)arg2; 32 | + (id)plugInKitProxyForUUID:(id)arg1 bundleIdentifier:(id)arg2 pluginIdentifier:(id)arg3 effectiveIdentifier:(id)arg4 version:(id)arg5 bundleURL:(id)arg6; 33 | + (id)pluginKitProxyForIdentifier:(id)arg1; 34 | + (id)pluginKitProxyForURL:(id)arg1; 35 | + (id)pluginKitProxyForUUID:(id)arg1; 36 | + (BOOL)supportsSecureCoding; 37 | 38 | - (BOOL)UPPValidated; 39 | - (id)_initWithPlugin:(unsigned int)arg1; 40 | //- (id)_initWithPlugin:(unsigned int)arg1 andContext:(const struct LSContext { struct LSDatabase {} *x1; }*)arg2; 41 | - (id)_initWithUUID:(id)arg1 bundleIdentifier:(id)arg2 pluginIdentifier:(id)arg3 effectiveIdentifier:(id)arg4 version:(id)arg5 bundleURL:(id)arg6; 42 | - (id)_valueForEqualityTesting; 43 | - (id)boundIconsDictionary; 44 | - (id)containingBundle; 45 | - (void)dealloc; 46 | - (id)description; 47 | - (void)encodeWithCoder:(id)arg1; 48 | - (id)iconDataForVariant:(int)arg1; 49 | - (id)infoPlist; 50 | - (id)initWithCoder:(id)arg1; 51 | - (BOOL)isOnSystemPartition; 52 | - (id)localizedName; 53 | - (id)localizedShortName; 54 | - (id)objectForInfoDictionaryKey:(id)arg1 ofClass:(Class)arg2 inScope:(unsigned int)arg3; 55 | - (id)originalIdentifier; 56 | - (BOOL)pluginCanProvideIcon; 57 | - (id)pluginIdentifier; 58 | - (id)pluginKitDictionary; 59 | - (id)pluginUUID; 60 | - (BOOL)profileValidated; 61 | - (id)protocol; 62 | - (id)registrationDate; 63 | - (id)teamID; 64 | 65 | // Image: /System/Library/Frameworks/UserNotifications.framework/UserNotifications 66 | 67 | - (id)_un_applicationBundleURL; 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /Preferences/Preferences.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface PSSpecifier : NSObject 4 | @property (nonatomic, copy) NSString *name; 5 | - (id)propertyForKey:(NSString *)key; 6 | @end 7 | 8 | @protocol PSBaseView 9 | - (void)setParentController:(id)parentController; 10 | @end 11 | 12 | @interface PSViewController : UIViewController 13 | - (instancetype)initForContentSize:(CGSize)contentSize; 14 | @property (nonatomic, copy) PSSpecifier *specifier; 15 | - (void)pushController:(id)controller; 16 | @end 17 | 18 | @interface PSViewController (Legacy) 19 | - (void)viewWillBecomeVisible:(void *)source; 20 | @end 21 | 22 | @interface PSListController : PSViewController 23 | - (PSSpecifier *)specifierForID:(NSString *)identifier; 24 | @end 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > ExtensionList is inspired by [Applist](https://github.com/rpetrich/AppList). So the basic rules to use this library is very similar. 2 | 3 | > You can refer to [Applist manual](http://iphonedevwiki.net/index.php/AppList). 4 | 5 | # Differences in 'ALSectionDescriptors' 6 | ## avaliable-extensions 7 | The difference is that i removed the `suppress-hidden-apps` key for `ALSectionDescriptors`, because it's not appropriate for PlugIns. And I add a `avaliable-extensions` to filter only avaliable plugins that iOS version higher than the required iOS version. The default value for `avaliable-extensions` is `true`. 8 | ## predicate 9 | `ExtensionList` query an array of `LSPlugInKitProxy` instance, so the `predicate` key is apply on the property of `LSPlugInKitProxy` class. There is a property named `protocol` in the said class, which infers to the Apple private service ID. For example, if you want to filter `Today-widget extension`, put `protocol contains 'com.apple.widget-extension'` into `predicate` key. 10 | For further infomation about App Extension and the protocol refered to, you can see the [Apple Guides](https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Action.html#//apple_ref/doc/uid/TP40014214-CH13-SW1) about App Extension. Or you can find it in the `info.plist` of `*.appex`. The value for key `NSExtensionPointIdentifier` is equal to `protocol` of `LSPlugInKitProxy`. 11 | 12 | - A demo of `ALSectionDescriptors` 13 | 14 | ``` 15 | ALSectionDescriptors = ( 16 | { 17 | title = "Custom Keyboard"; 18 | predicate = "protocol contains 'keyboard-service'"; 19 | "icon-size" = 29; 20 | "cell-class-name" = ELSwitchCell; 21 | "avaliable-extensions" = 0; 22 | }, 23 | ) 24 | ``` 25 | --------------------------------------------------------------------------------