PageRenderTime 47ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/AddressBook/ABAddressBook.cs

https://github.com/kjpou1/maccore
C# | 515 lines | 398 code | 72 blank | 45 comment | 49 complexity | eceafe2372d5702dda4225943e171571 MD5 | raw file
Possible License(s): Apache-2.0
  1. //
  2. // ABAddressBook.cs: Implements the managed ABAddressBook
  3. //
  4. // Authors: Mono Team
  5. //
  6. // Copyright (C) 2009 Novell, Inc
  7. // Copyright 2011-2013 Xamarin Inc.
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. //
  29. using System;
  30. using System.Collections;
  31. using System.Collections.Generic;
  32. using System.Runtime.InteropServices;
  33. using MonoMac;
  34. using MonoMac.Foundation;
  35. using MonoMac.CoreFoundation;
  36. using MonoMac.ObjCRuntime;
  37. namespace MonoMac.AddressBook {
  38. public enum ABAddressBookError {
  39. OperationNotPermittedByStore = 0,
  40. OperationNotPermittedByUserError
  41. }
  42. public enum ABAuthorizationStatus {
  43. NotDetermined = 0,
  44. Restricted,
  45. Denied,
  46. Authorized
  47. }
  48. public class ExternalChangeEventArgs : EventArgs {
  49. public ExternalChangeEventArgs (ABAddressBook addressBook, NSDictionary info)
  50. {
  51. AddressBook = addressBook;
  52. Info = info;
  53. }
  54. public ABAddressBook AddressBook {get; private set;}
  55. public NSDictionary Info {get; private set;}
  56. }
  57. // Quoth the docs:
  58. // http://developer.apple.com/iphone/library/documentation/AddressBook/Reference/ABPersonRef_iPhoneOS/Reference/reference.html#//apple_ref/c/func/ABPersonGetSortOrdering
  59. //
  60. // "The value of these constants is undefined until one of the following has
  61. // been called: ABAddressBookCreate, ABPersonCreate, ABGroupCreate."
  62. //
  63. // Meaning we can't rely on static constructors, as they could be invoked
  64. // before those functions have been invoked. :-/
  65. //
  66. // Note that the above comment was removed from iOS 6.0+ documentation (and were not part of OSX docs AFAIK).
  67. // It make sense since it's not possible to call those functions, from 6.0+ they will return NULL on devices,
  68. // unless the application has been authorized to access the address book.
  69. class InitConstants {
  70. public static void Init () {}
  71. static InitConstants ()
  72. {
  73. // ensure we can init. This is needed before iOS6 (as per doc).
  74. IntPtr p = ABAddressBook.ABAddressBookCreate ();
  75. ABGroupProperty.Init ();
  76. ABLabel.Init ();
  77. ABPersonAddressKey.Init ();
  78. ABPersonDateLabel.Init ();
  79. ABPersonInstantMessageKey.Init ();
  80. ABPersonInstantMessageService.Init ();
  81. ABPersonKindId.Init ();
  82. ABPersonPhoneLabel.Init ();
  83. ABPersonPropertyId.Init ();
  84. ABPersonRelatedNamesLabel.Init ();
  85. ABPersonUrlLabel.Init ();
  86. ABSourcePropertyId.Init ();
  87. // From iOS 6.0+ this might return NULL, e.g. if the application is not authorized to access the
  88. // address book, and we would crash if we tried to release a null pointer
  89. if (p != IntPtr.Zero)
  90. CFObject.CFRelease (p);
  91. }
  92. }
  93. public class ABAddressBook : INativeObject, IDisposable, IEnumerable<ABRecord> {
  94. public static readonly NSString ErrorDomain;
  95. IntPtr handle;
  96. GCHandle sender;
  97. [Obsolete ("Deprecated in iOS 6.0")]
  98. [DllImport (Constants.AddressBookLibrary)]
  99. internal extern static IntPtr ABAddressBookCreate ();
  100. [Obsolete ("Deprecated in iOS 6.0. Use static Create method instead")]
  101. public ABAddressBook ()
  102. {
  103. this.handle = ABAddressBookCreate ();
  104. InitConstants.Init ();
  105. }
  106. [DllImport (Constants.AddressBookLibrary)]
  107. internal extern static IntPtr ABAddressBookCreateWithOptions (IntPtr dictionary, out IntPtr cfError);
  108. [Since (6,0)]
  109. public static ABAddressBook Create (out NSError error)
  110. {
  111. IntPtr e;
  112. var handle = ABAddressBookCreateWithOptions (IntPtr.Zero, out e);
  113. if (handle == IntPtr.Zero){
  114. error = new NSError (e);
  115. return null;
  116. }
  117. error = null;
  118. return new ABAddressBook (handle, true);
  119. }
  120. internal ABAddressBook (IntPtr handle, bool owns)
  121. {
  122. InitConstants.Init ();
  123. if (!owns)
  124. CFObject.CFRetain (handle);
  125. this.handle = handle;
  126. }
  127. internal ABAddressBook (IntPtr handle)
  128. {
  129. InitConstants.Init ();
  130. this.handle = handle;
  131. }
  132. static ABAddressBook ()
  133. {
  134. var handle = Dlfcn.dlopen (Constants.AddressBookLibrary, 0);
  135. if (handle == IntPtr.Zero)
  136. return;
  137. try {
  138. ErrorDomain = Dlfcn.GetStringConstant (handle, "ABAddressBookErrorDomain");
  139. }
  140. finally {
  141. Dlfcn.dlclose (handle);
  142. }
  143. }
  144. ~ABAddressBook ()
  145. {
  146. Dispose (false);
  147. }
  148. public void Dispose ()
  149. {
  150. Dispose (true);
  151. GC.SuppressFinalize (this);
  152. }
  153. protected virtual void Dispose (bool disposing)
  154. {
  155. if (handle != IntPtr.Zero)
  156. CFObject.CFRelease (handle);
  157. if (sender.IsAllocated)
  158. sender.Free ();
  159. handle = IntPtr.Zero;
  160. }
  161. void AssertValid ()
  162. {
  163. if (handle == IntPtr.Zero)
  164. throw new ObjectDisposedException ("");
  165. }
  166. public IntPtr Handle {
  167. get {
  168. AssertValid ();
  169. return handle;
  170. }
  171. }
  172. [DllImport (Constants.AddressBookLibrary)]
  173. extern static ABAuthorizationStatus ABAddressBookGetAuthorizationStatus ();
  174. [Since (6,0)]
  175. public static ABAuthorizationStatus GetAuthorizationStatus ()
  176. {
  177. return ABAddressBookGetAuthorizationStatus ();
  178. }
  179. [DllImport (Constants.AddressBookLibrary)]
  180. extern unsafe static void ABAddressBookRequestAccessWithCompletion (IntPtr addressbook, void * completion);
  181. [Since (6,0)]
  182. public void RequestAccess (Action<bool,NSError> onCompleted)
  183. {
  184. if (onCompleted == null)
  185. throw new ArgumentNullException ("onCompleted");
  186. unsafe {
  187. BlockLiteral *block_ptr_handler;
  188. BlockLiteral block_handler;
  189. block_handler = new BlockLiteral ();
  190. block_ptr_handler = &block_handler;
  191. block_handler.SetupBlock (static_completionHandler, onCompleted);
  192. ABAddressBookRequestAccessWithCompletion (Handle, (void*) block_ptr_handler);
  193. block_ptr_handler->CleanupBlock ();
  194. }
  195. }
  196. internal delegate void InnerCompleted (IntPtr block, bool success, IntPtr error);
  197. static readonly InnerCompleted static_completionHandler = TrampolineCompletionHandler;
  198. [MonoPInvokeCallback (typeof (InnerCompleted))]
  199. static unsafe void TrampolineCompletionHandler (IntPtr block, bool success, IntPtr error)
  200. {
  201. var descriptor = (BlockLiteral *) block;
  202. var del = (Action<bool,NSError>) (descriptor->global_handle != IntPtr.Zero ? GCHandle.FromIntPtr (descriptor->global_handle).Target : GCHandle.FromIntPtr (descriptor->local_handle).Target);
  203. if (del != null)
  204. del (success, error == IntPtr.Zero ? null : (MonoMac.Foundation.NSError) Runtime.GetNSObject (error));
  205. }
  206. [DllImport (Constants.AddressBookLibrary)]
  207. extern static bool ABAddressBookHasUnsavedChanges (IntPtr addressBook);
  208. public bool HasUnsavedChanges {
  209. get {
  210. AssertValid ();
  211. return ABAddressBookHasUnsavedChanges (Handle);
  212. }
  213. }
  214. [DllImport (Constants.AddressBookLibrary)]
  215. extern static bool ABAddressBookSave (IntPtr addressBook, out IntPtr error);
  216. public void Save ()
  217. {
  218. AssertValid ();
  219. IntPtr error;
  220. if (!ABAddressBookSave (Handle, out error))
  221. throw CFException.FromCFError (error);
  222. }
  223. [DllImport (Constants.AddressBookLibrary)]
  224. extern static void ABAddressBookRevert (IntPtr addressBook);
  225. public void Revert ()
  226. {
  227. AssertValid ();
  228. ABAddressBookRevert (Handle);
  229. }
  230. [DllImport (Constants.AddressBookLibrary)]
  231. extern static bool ABAddressBookAddRecord (IntPtr addressBook, IntPtr record, out IntPtr error);
  232. public void Add (ABRecord record)
  233. {
  234. if (record == null)
  235. throw new ArgumentNullException ("record");
  236. AssertValid ();
  237. IntPtr error;
  238. if (!ABAddressBookAddRecord (Handle, record.Handle, out error))
  239. throw CFException.FromCFError (error);
  240. record.AddressBook = this;
  241. }
  242. [DllImport (Constants.AddressBookLibrary)]
  243. extern static bool ABAddressBookRemoveRecord (IntPtr addressBook, IntPtr record, out IntPtr error);
  244. public void Remove (ABRecord record)
  245. {
  246. if (record == null)
  247. throw new ArgumentNullException ("record");
  248. AssertValid ();
  249. IntPtr error;
  250. if (!ABAddressBookRemoveRecord (Handle, record.Handle, out error))
  251. throw CFException.FromCFError (error);
  252. record.AddressBook = null;
  253. }
  254. [DllImport (Constants.AddressBookLibrary)]
  255. extern static int ABAddressBookGetPersonCount (IntPtr addressBook);
  256. public int PeopleCount {
  257. get {
  258. AssertValid ();
  259. return ABAddressBookGetPersonCount (Handle);
  260. }
  261. }
  262. [DllImport (Constants.AddressBookLibrary)]
  263. extern static IntPtr ABAddressBookCopyArrayOfAllPeople (IntPtr addressBook);
  264. public ABPerson [] GetPeople ()
  265. {
  266. AssertValid ();
  267. IntPtr cfArrayRef = ABAddressBookCopyArrayOfAllPeople (Handle);
  268. return NSArray.ArrayFromHandle (cfArrayRef, h => new ABPerson (h, this));
  269. }
  270. [DllImport (Constants.AddressBookLibrary)]
  271. extern static IntPtr ABAddressBookCopyArrayOfAllPeopleInSource (IntPtr addressBook, IntPtr source);
  272. [Since (4,0)]
  273. public ABPerson [] GetPeople (ABRecord source)
  274. {
  275. if (source == null)
  276. throw new ArgumentNullException ("source");
  277. AssertValid ();
  278. IntPtr cfArrayRef = ABAddressBookCopyArrayOfAllPeopleInSource (Handle, source.Handle);
  279. return NSArray.ArrayFromHandle (cfArrayRef, l => new ABPerson (l, this));
  280. }
  281. [DllImport (Constants.AddressBookLibrary)]
  282. extern static IntPtr ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering (IntPtr addressBook, IntPtr source, ABPersonSortBy sortOrdering);
  283. [Since (4,0)]
  284. public ABPerson [] GetPeople (ABRecord source, ABPersonSortBy sortOrdering)
  285. {
  286. if (source == null)
  287. throw new ArgumentNullException ("source");
  288. AssertValid ();
  289. IntPtr cfArrayRef = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering (Handle, source.Handle, sortOrdering);
  290. return NSArray.ArrayFromHandle (cfArrayRef, l => new ABPerson (l, this));
  291. }
  292. [DllImport (Constants.AddressBookLibrary)]
  293. extern static int ABAddressBookGetGroupCount (IntPtr addressBook);
  294. public int GroupCount {
  295. get {
  296. AssertValid ();
  297. return ABAddressBookGetGroupCount (Handle);
  298. }
  299. }
  300. [DllImport (Constants.AddressBookLibrary)]
  301. extern static IntPtr ABAddressBookCopyArrayOfAllGroups (IntPtr addressBook);
  302. public ABGroup [] GetGroups ()
  303. {
  304. AssertValid ();
  305. IntPtr cfArrayRef = ABAddressBookCopyArrayOfAllGroups (Handle);
  306. return NSArray.ArrayFromHandle (cfArrayRef, h => new ABGroup (h, this));
  307. }
  308. [DllImport (Constants.AddressBookLibrary)]
  309. extern static IntPtr ABAddressBookCopyArrayOfAllGroupsInSource (IntPtr addressBook, IntPtr source);
  310. [Since (4,0)]
  311. public ABGroup[] GetGroups (ABRecord source)
  312. {
  313. if (source == null)
  314. throw new ArgumentNullException ("source");
  315. AssertValid ();
  316. IntPtr cfArrayRef = ABAddressBookCopyArrayOfAllGroupsInSource (Handle, source.Handle);
  317. return NSArray.ArrayFromHandle (cfArrayRef, l => new ABGroup (l, this));
  318. }
  319. [DllImport (Constants.AddressBookLibrary)]
  320. extern static IntPtr ABAddressBookCopyLocalizedLabel (IntPtr label);
  321. public static string LocalizedLabel (NSString label)
  322. {
  323. if (label == null)
  324. throw new ArgumentNullException ("label");
  325. using (var s = new NSString (ABAddressBookCopyLocalizedLabel (label.Handle)))
  326. return s.ToString ();
  327. }
  328. [DllImport (Constants.AddressBookLibrary)]
  329. extern static void ABAddressBookRegisterExternalChangeCallback (IntPtr addressBook, ABExternalChangeCallback callback, IntPtr context);
  330. [DllImport (Constants.AddressBookLibrary)]
  331. extern static void ABAddressBookUnregisterExternalChangeCallback (IntPtr addressBook, ABExternalChangeCallback callback, IntPtr context);
  332. delegate void ABExternalChangeCallback (IntPtr addressBook, IntPtr info, IntPtr context);
  333. [MonoPInvokeCallback (typeof (ABExternalChangeCallback))]
  334. static void ExternalChangeCallback (IntPtr addressBook, IntPtr info, IntPtr context)
  335. {
  336. GCHandle s = GCHandle.FromIntPtr (context);
  337. var self = s.Target as ABAddressBook;
  338. if (self == null)
  339. return;
  340. self.OnExternalChange (new ExternalChangeEventArgs (
  341. new ABAddressBook (addressBook, false),
  342. (NSDictionary) Runtime.GetNSObject (info)));
  343. }
  344. object eventLock = new object ();
  345. EventHandler<ExternalChangeEventArgs> externalChange;
  346. protected virtual void OnExternalChange (ExternalChangeEventArgs e)
  347. {
  348. AssertValid ();
  349. EventHandler<ExternalChangeEventArgs> h = externalChange;
  350. if (h != null)
  351. h (this, e);
  352. }
  353. public event EventHandler<ExternalChangeEventArgs> ExternalChange {
  354. add {
  355. lock (eventLock) {
  356. if (externalChange == null) {
  357. sender = GCHandle.Alloc (this);
  358. ABAddressBookRegisterExternalChangeCallback (Handle, ExternalChangeCallback, GCHandle.ToIntPtr (sender));
  359. }
  360. externalChange += value;
  361. }
  362. }
  363. remove {
  364. lock (eventLock) {
  365. externalChange -= value;
  366. if (externalChange == null) {
  367. ABAddressBookUnregisterExternalChangeCallback (Handle, ExternalChangeCallback, GCHandle.ToIntPtr (sender));
  368. sender.Free ();
  369. }
  370. }
  371. }
  372. }
  373. IEnumerator IEnumerable.GetEnumerator ()
  374. {
  375. return GetEnumerator ();
  376. }
  377. public IEnumerator<ABRecord> GetEnumerator ()
  378. {
  379. AssertValid ();
  380. foreach (var p in GetPeople ())
  381. yield return p;
  382. foreach (var g in GetGroups ())
  383. yield return g;
  384. }
  385. [DllImport (Constants.AddressBookLibrary)]
  386. extern static IntPtr ABAddressBookGetGroupWithRecordID (IntPtr addressBook, int recordId);
  387. public ABGroup GetGroup (int recordId)
  388. {
  389. var h = ABAddressBookGetGroupWithRecordID (Handle, recordId);
  390. if (h == IntPtr.Zero)
  391. return null;
  392. return new ABGroup (h, this);
  393. }
  394. [DllImport (Constants.AddressBookLibrary)]
  395. extern static IntPtr ABAddressBookGetPersonWithRecordID (IntPtr addressBook, int recordId);
  396. public ABPerson GetPerson (int recordId)
  397. {
  398. var h = ABAddressBookGetPersonWithRecordID (Handle, recordId);
  399. if (h == IntPtr.Zero)
  400. return null;
  401. return new ABPerson (h, this);
  402. }
  403. [DllImport (Constants.AddressBookLibrary)]
  404. extern static IntPtr ABAddressBookCopyPeopleWithName (IntPtr addressBook, IntPtr name);
  405. public ABPerson [] GetPeopleWithName (string name)
  406. {
  407. IntPtr cfArrayRef;
  408. using (var s = new NSString (name))
  409. cfArrayRef = ABAddressBookCopyPeopleWithName (Handle, s.Handle);
  410. return NSArray.ArrayFromHandle (cfArrayRef, h => new ABPerson (h, this));
  411. }
  412. // ABSource
  413. // http://developer.apple.com/library/IOs/#documentation/AddressBook/Reference/ABSourceRef_iPhoneOS/Reference/reference.html
  414. [DllImport (Constants.AddressBookLibrary)]
  415. extern static IntPtr /* CFArrayRef */ ABAddressBookCopyArrayOfAllSources (IntPtr /* ABAddressBookRef */ addressBook);
  416. public ABSource [] GetAllSources ()
  417. {
  418. AssertValid ();
  419. IntPtr cfArrayRef = ABAddressBookCopyArrayOfAllSources (Handle);
  420. return NSArray.ArrayFromHandle (cfArrayRef, h => new ABSource (h, this));
  421. }
  422. [DllImport (Constants.AddressBookLibrary)]
  423. extern static IntPtr /* ABRecordRef */ ABAddressBookCopyDefaultSource (IntPtr /* ABAddressBookRef */ addressBook);
  424. public ABSource GetDefaultSource ()
  425. {
  426. AssertValid ();
  427. IntPtr h = ABAddressBookCopyDefaultSource (Handle);
  428. if (h == IntPtr.Zero)
  429. return null;
  430. return new ABSource (h, this);
  431. }
  432. [DllImport (Constants.AddressBookLibrary)]
  433. extern static IntPtr /* ABRecordRef */ ABAddressBookGetSourceWithRecordID (IntPtr /* ABAddressBookRef */ addressBook, int /* ABRecordID */ sourceID);
  434. public ABSource GetSource (int sourceID)
  435. {
  436. AssertValid ();
  437. var h = ABAddressBookGetSourceWithRecordID (Handle, sourceID);
  438. if (h == IntPtr.Zero)
  439. return null;
  440. return new ABSource (h, this);
  441. }
  442. }
  443. }