Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using Android.App;
- using Android.Content;
- using Android.Content.Res;
- using Android.Database;
- using Android.OS;
- using Android.Provider;
- using EasyPhone.AndroidHelpers;
- using Microsoft.Xna.Framework;
- using Microsoft.Xna.Framework.Content;
- using Microsoft.Xna.Framework.Graphics;
- using SeniorLauncher.Helpers;
- using Uri = Android.Net.Uri;
- using Phone = Android.Provider.ContactsContract.CommonDataKinds.Phone;
- using StructuredName = Android.Provider.ContactsContract.CommonDataKinds.StructuredName;
- namespace EasyPhone.DataLayer
- {
- /// <summary>
- /// Třída pro získání kontaktů ze zařízení (jméno, Id, telefonní číslo...)
- /// - jednotlivé metody jsou interně locked, využívají lokální cachování na vazvu Id -> číslo
- /// - cachování: viz parciální třída ContactsAdapter_StorageCache
- /// </summary>
- public partial class ContactsAdapter
- {
- public List<Contact> ContactList = null;
- public bool NumbersFilled = false;
- Activity activity;
- object lockContactNames = new object();
- object lockFillNumbers = new object();
- object lockGetNumberByID = new object();
- object lockLoadContactName = new object();
- object lockGetFullPhoto = new object();
- public ContactsAdapter(Activity activity)
- {
- this.activity = activity;
- }
- /// <summary>
- /// Naplní pole jmen kontaktů z telefonu
- /// - pouze jména, ne čísla, viz FillNumbers
- /// </summary>
- public void FillContactsFromPhone()
- {
- lock (lockContactNames)
- {
- FillContacts_Locked();
- }
- }
- private void FillContacts_Locked()
- {
- // Upraveno podle: http://stackoverflow.com/questions/7803633/how-to-get-android-contact-list-data-on-my-seperate-listview-in-android-2-1
- // - pouze lokální kontakty: http://stackoverflow.com/questions/12932111/how-to-prevent-gmail-contacts-in-contentresolver-query-in-android
- var uri = ContactsContract.Contacts.ContentUri;
- string[] projection = {
- ContactsContract.Contacts.InterfaceConsts.Id,
- ContactsContract.Contacts.InterfaceConsts.DisplayName,
- ContactsContract.Contacts.InterfaceConsts.PhotoId,
- ContactsContract.Contacts.InterfaceConsts.HasPhoneNumber
- };
- string selection = ContactsContract.Contacts.InterfaceConsts.InVisibleGroup + "=1";
- selection += " AND " + ContactsContract.Contacts.InterfaceConsts.HasPhoneNumber + "=1";
- ICursor cursor = null;
- try
- {
- cursor = Game.Activity.ContentResolver.Query(uri, projection, selection, null, null);
- if (cursor == null)
- return;
- ContactList = new List<Contact>();
- if (cursor.MoveToFirst())
- {
- do
- {
- // Načítá pouze viditelné kontakty s telefonním číslem
- ContactList.Add(new Contact
- {
- Id = cursor.GetLong(cursor.GetColumnIndex(projection[0])),
- DisplayName = cursor.GetString(cursor.GetColumnIndex(projection[1])),
- PhotoId = cursor.GetString(cursor.GetColumnIndex(projection[2]))
- });
- } while (cursor.MoveToNext());
- }
- }
- catch (Exception e)
- {
- ExceptionLogger.LogException(e, false, false);
- }
- finally
- {
- // Na konci uzavře cursor
- if (cursor != null)
- cursor.Close();
- }
- // Setřídí seznam kontaktů podle jména...
- ContactList.Sort();
- NumbersFilled = false;
- }
- /// <summary>
- /// Ke každému kontaktu v ContactList načte i jeho číslo (podle ID)
- /// - hodí se nám pro MessagesAdapter
- /// - při načítání jenom seznamu kontaktů je to zbytečné volat! (pomalé?)
- /// - cachuje si hodnoty, aby to příště bylo rychlejší :)
- /// </summary>
- public void FillNumbers()
- {
- lock (lockFillNumbers)
- {
- try
- {
- FillNumbers_Locked();
- }
- catch (Exception e)
- {
- ExceptionLogger.LogException(e, false, false);
- }
- }
- }
- private void FillNumbers_Locked()
- {
- // Je potřeba aktualizovat tuhle cache, když přidáváme nějaké nové číslo / měníme ho!
- bool save = false;
- if (cachedNumbers.Count == 0)
- {
- // Načte hodnoty z Isolated Storage (je to rychlejší, než přes Cursory z Android API)
- // - načtené hodnoty rovnou uloží (cache se dále aktualizuje jen přes ActualizeCacheValue, AddCache, RemoveCache...)
- LoadNumbersFromStorage();
- save = true;
- }
- for (int i = 0; i < ContactList.Count; i++)
- {
- var c = ContactList[i];
- if (cachedNumbers.ContainsKey(c.Id))
- c.Number = cachedNumbers[c.Id];
- else
- {
- try
- {
- // Mohlo zde opět padat, že CursorWindows nebylo vytvořeno
- // - stává se, když se 2x znovu dotazuji na stejné číslo kontaktu?
- // tohle cachování by to mělo snad řešit... Kdyžtak opravit!
- // - v API Androidu 2.3 se musí načítat každé číslo kontaktu zvlášť, nejde to už v FillNumbers...
- // - tohle je ale navržené OK, pokud to náhodou načetlo "", příště už to načte správně
- c.Number = GetNumberByID(c.Id);
- } catch { }
- if (c.Number == null)
- c.Number = "";
- if (c.Number != "")
- cachedNumbers.Add(c.Id, c.Number);
- }
- }
- if (save)
- SaveNumbersToStorage();
- NumbersFilled = true;
- }
- /// <summary>
- /// Vrátí telefonní číslo kontaktu podle jeho ID (nastavuje jako parametr kontaktu v ContactsList)
- /// - voláno z FillNumbers
- /// </summary>
- public string GetNumberByID(long Id)
- {
- lock (lockGetNumberByID)
- {
- return GetNumberByID_Locked(Id);
- }
- }
- private string GetNumberByID_Locked(long Id)
- {
- // Načte od daného kontaktu 1 číslo (otevře a zavře si vlastní kurzor)
- var uri = Phone.ContentUri;
- String where = Phone.InterfaceConsts.ContactId + " = ?";
- String[] selectionArgs = { Id.ToString(CultureInfo.InvariantCulture) };
- ICursor cursor = null;
- string loadedPhone = "";
- try
- {
- cursor = Game.Activity.ContentResolver.Query(uri, null, where, selectionArgs, null);
- if (cursor == null)
- return cachedNumbers.ContainsKey(Id) ? cachedNumbers[Id] : "";
- if (cursor.MoveToFirst())
- {
- // OK, kurzor je fajn, získáme z něj to číslo (a zaktualizujeme případně cache)
- String phone = cursor.GetString(cursor.GetColumnIndex(Phone.Number));
- loadedPhone = phone ?? (cachedNumbers.ContainsKey(Id) ? cachedNumbers[Id] : "");
- }
- }
- catch (Exception e)
- {
- ExceptionLogger.LogException(e, false, false);
- }
- finally
- {
- // Na konci uzavře cursor
- if (cursor != null)
- cursor.Close();
- }
- // Kdybychom chtěli všechna čísla daného kontaktu (viz 1. odpověď): http://stackoverflow.com/questions/9644704/how-to-get-specific-contact-number-by-using-contact-id
- return loadedPhone;
- }
- /// <summary>
- /// Podle ID vrátí číslo kontaktu ze seznamu
- /// - pokud seznam zatím není načtený, načte ho
- /// </summary>
- public Contact ReturnContactByID(long contactID)
- {
- // Obecně FillContacts a FillNumbers není voláno pokaždé!
- // - např. u výpisu hovorů by se volalo na každý kontakt...
- // - ale můžeme si to vždy zavolat sami (např. při otevření obrazovky výpisu hovorů)
- if (ContactList == null)
- FillContactsFromPhone();
- if (!NumbersFilled)
- FillNumbers();
- lock (lockContactNames)
- {
- // Vrátí objekt kontaktu podle daného ID (ten stav null nastane jen tehdy, když tam neexistuje)
- if (ContactList == null)
- return new Contact() { Id = contactID, InContactList = false };
- for (int i = 0; i < ContactList.Count; i++)
- if (ContactList[i].Id == contactID)
- return ContactList[i];
- }
- return new Contact() { Id = contactID, InContactList = false };
- }
- /// <summary>
- /// Vrátí ID kontaktu a "display name" pro dané telefonní číslo
- /// - číslo může být "podobné" (tj. zde se otestuje na mezery, chybějící předvolbu apod.)
- /// - využití: SMS zprávy, nebo nepřijaté hovory - načte k danému číslu jméno
- /// - voláno z MessagesAdapter a CallListAdapter.FillList - asynchronně!
- /// - ale asynchronnost je pořešena o úroveň výš (aby si nemohl zapisovat do jednoho pole z víc míst)
- /// - přeci jen to tu znovu lockujeme
- /// </summary>
- public void LoadContactNameBySimilarNumber(string contactNumber, out string displayName, out long contactID)
- {
- if (ContactList == null)
- FillContactsFromPhone();
- if (!NumbersFilled)
- FillNumbers();
- lock (lockLoadContactName)
- {
- try
- {
- LoadContactNameBySimilarNumber_Locked(contactNumber, out displayName, out contactID);
- }
- catch (Exception e)
- {
- ExceptionLogger.LogException(e, false, false);
- displayName = "";
- contactID = 0;
- }
- }
- }
- private void LoadContactNameBySimilarNumber_Locked(string contactNumber, out string displayName, out long contactID)
- {
- // Vyhledá Id kontaktu + jeho jméno, podle "podobného čísla" (pro matchování v SMS)
- displayName = "";
- contactID = -1;
- if (ContactList == null)
- return;
- for (int i = 0; i < ContactList.Count; i++)
- {
- var c = ContactList[i];
- if (Operations.IsSimilarNumber(c.Number, contactNumber))
- {
- displayName = c.DisplayName;
- contactID = c.Id;
- break;
- }
- }
- if (displayName == "")
- {
- displayName = contactNumber; // Pro ty, co nejsou v telefonním seznamu
- contactID = -1;
- }
- }
- /// <summary>
- /// Vytáhne si fotku kontaktu z contact URI (pokud existuje)
- /// - předává se rovnou odkaz na texturu, na ni se případně udělá dispose, pokud už tam něco je!
- /// - vrací null, pokud fotka neexistuje
- /// - neděje se zde cachování textury, je nutné tuhle metodu zavolat vždy z
- /// LoadContent a RefreshScreenGraphics (z dané obrazovky, kde je takový obrázek)
- /// </summary>
- public void GetFullPhotoOfContact(Contact contact, GraphicsDevice device, ContentManager content, ref Texture2D texture)
- {
- lock (lockGetFullPhoto)
- {
- GetFullPhotoOfContact_Locked(contact, device, content, ref texture);
- }
- }
- private void GetFullPhotoOfContact_Locked(Contact contact, GraphicsDevice device, ContentManager content, ref Texture2D texture)
- {
- if (device == null || content == null)
- {
- texture = null;
- return;
- }
- if (string.IsNullOrEmpty(contact.PhotoId))
- {
- texture = null;
- return;
- }
- // Načte velkou fotku kontaktu
- if (((int) Build.VERSION.SdkInt) >= 14)
- {
- // Na Androidu 14+ použije tu verzi z PDA projektu
- try
- {
- Uri cUri = Uri.WithAppendedPath(ContactsContract.Contacts.ContentUri, Convert.ToString(contact.Id));
- using (var inStream = ContactsContract.Contacts.OpenContactPhotoInputStream(
- Game.Activity.ContentResolver, cUri, true))
- {
- Texture2D mgTexture = Texture2D.FromStream(device, inStream);
- texture = mgTexture;
- return;
- }
- }
- catch (Exception e)
- {
- // Zaloguje soft exception...
- ExceptionLogger.LogException(e, false, true);
- }
- texture = null;
- }
- else
- {
- // Starší Androidy:
- // - musíme trochu přes hack (Xamarin nemá potřebnou konstantu, API je už deprecated)
- // http://developer.android.com/reference/android/provider/ContactsContract.Contacts.Photo.html
- var contactUri = ContentUris.WithAppendedId(ContactsContract.Contacts.ContentUri, contact.Id);
- var contactPhotoUri = Uri.WithAppendedPath(contactUri, "display_photo");
- AssetFileDescriptor fd = null;
- try
- {
- fd = Game.Activity.ContentResolver.OpenAssetFileDescriptor(contactPhotoUri, "r");
- using (var inStream = fd.CreateInputStream())
- {
- // Načte texturu ze streamu z daného Content URI, vytvoří MonoGame texturu...
- // - obrázek je velký 480x480 px, super :)
- // - není zde cachování textury, metoda se musí volat vždy z LoadContent a RefreshScreenGraphics
- Texture2D mgTexture = Texture2D.FromStream(device, inStream);
- texture = mgTexture;
- return;
- }
- }
- catch (Exception e)
- {
- // Zaloguje soft exception...
- ExceptionLogger.LogException(e, false, true);
- }
- finally
- {
- if (fd != null)
- fd.Close();
- }
- texture = null;
- }
- }
- /// <summary>
- /// Přidá nový kontakt do telefonu, vrací přímo jako objekt kontaktu (aby se mohla zobrazit správná obrazovka)
- /// - automaticky zaktualizuje pole ContactsList, i cache Id/číslo
- /// </summary>
- public Contact AddNewContact(string number, string displayName)
- {
- try
- {
- // Via http://wptrafficanalyzer.in/blog/adding-contacts-programatically-using-contacts-provider-in-android-example/
- var ops = new List<ContentProviderOperation>();
- int rawContactID = ops.Count;
- // Adding insert operation to operations list
- // to insert a new raw contact in the table ContactsContract.RawContacts
- ops.Add(ContentProviderOperation.NewInsert(ContactsContract.RawContacts.ContentUri)
- .WithValue(ContactsContract.RawContacts.InterfaceConsts.AccountType, null)
- .WithValue(ContactsContract.RawContacts.InterfaceConsts.AccountName, null)
- .Build());
- // Adding insert operation to operations list
- // to insert display name in the table ContactsContract.Data
- ops.Add(ContentProviderOperation.NewInsert(ContactsContract.Data.ContentUri)
- .WithValueBackReference(ContactsContract.Data.InterfaceConsts.RawContactId, rawContactID)
- .WithValue(ContactsContract.Data.InterfaceConsts.Mimetype, StructuredName.ContentItemType)
- .WithValue(StructuredName.DisplayName, displayName)
- .Build());
- // Adding insert operation to operations list
- // to insert Mobile Number in the table ContactsContract.Data
- ops.Add(ContentProviderOperation.NewInsert(ContactsContract.Data.ContentUri)
- .WithValueBackReference(ContactsContract.Data.InterfaceConsts.RawContactId, rawContactID)
- .WithValue(ContactsContract.Data.InterfaceConsts.Mimetype, Phone.ContentItemType)
- .WithValue(Phone.Number, number)
- .WithValue(Phone.InterfaceConsts.Type, 2) //2 = CommonDataKinds.Phone.TYPE_MOBILE
- .Build());
- // Executing all the insert operations as a single database transaction
- var contentResolver = Game.Activity.ContentResolver;
- contentResolver.ApplyBatch(ContactsContract.Authority, ops);
- Operations.ShowToast(Engine.Lang.Get("new_contact_added") + ": " + displayName);
- }
- catch (Exception e)
- {
- Operations.ShowToast(Engine.Lang.Get("cannot_add_contact"));
- ExceptionLogger.LogException(e, false, false);
- }
- // Kontakt už je uložený v seznamu
- // - podle jména a čísla načteme aktuální ID
- FillContactsFromPhone();
- FillNumbers();
- //...vymazání bufferů obrazovek + přesměrování dál se děje v té metodě, co tohle volá (PressedGreenOKButton)
- Contact c = GetContactByNameAndNumber(displayName, number);
- if (c != null)
- AddCacheValue(c.Id, number);
- return c;
- }
- private Contact GetContactByNameAndNumber(string displayName, string number)
- {
- // Projde si načtený seznam kontaktů, vrátí objekt kontaktu podle jména / čísla
- lock (lockContactNames)
- {
- for (int i = 0; i < ContactList.Count; i++)
- {
- if (ContactList[i].DisplayName == displayName &&
- ContactList[i].Number == number)
- {
- return ContactList[i];
- }
- }
- }
- return null;
- }
- /// <summary>
- /// Nastaví danému kontaktu nové jméno a číslo, zaktualizuje ho v telefonu
- /// - obnoví i potřebné buffery (cache)
- /// </summary>
- public Contact UpdateContact(Contact contact, string number, string displayName)
- {
- // Zaktualizuje reálně kontakt v telefonu:
- // http://stackoverflow.com/questions/8490123/how-to-update-existing-contact?rq=1
- try
- {
- var ops = new List<ContentProviderOperation>();
- int rawContactID = GetRawContactID(contact);
- if (rawContactID == -1)
- {
- Operations.ShowToast(Engine.Lang.Get("cannot_update_contact"));
- return contact;
- }
- // Jméno a číslo...
- var contentResolver = Game.Activity.ContentResolver;
- String where = ContactsContract.Data.InterfaceConsts.ContactId + " = ? AND "
- + ContactsContract.Data.InterfaceConsts.Mimetype + " = ?";
- String[] nameParams = { Convert.ToString(contact.Id), StructuredName.ContentItemType };
- String[] numberParams = { Convert.ToString(contact.Id), Phone.ContentItemType };
- ops.Add(ContentProviderOperation.NewUpdate(ContactsContract.Data.ContentUri)
- .WithSelection(where, nameParams)
- .WithValue(StructuredName.DisplayName, displayName)
- .Build());
- ops.Add(ContentProviderOperation.NewUpdate(ContactsContract.Data.ContentUri)
- .WithSelection(where,numberParams)
- .WithValue(Phone.Number, number)
- .Build());
- // Executing all the insert operations as a single database transaction
- contentResolver.ApplyBatch(ContactsContract.Authority, ops);
- Operations.ShowToast(Engine.Lang.Get("new_contact_edited") + ": " + displayName);
- }
- catch (Exception e)
- {
- Operations.ShowToast(Engine.Lang.Get("cannot_update_contact"));
- ExceptionLogger.LogException(e, false, false);
- }
- // Zaktualizuje cache
- ActualizeCacheValue(contact.Id, number);
- contact.Number = number;
- contact.DisplayName = displayName;
- // A znovu načte aktuální data...
- FillContactsFromPhone();
- return contact;
- }
- private int GetRawContactID(Contact contact)
- {
- // Načte "raw" ID kontaktu (to, podle kterého se přidávalo do seznamu)
- // - potřebujeme ho pro editaci (v UpdateContact)
- int outValue = -1;
- ICursor cursor = null;
- try
- {
- cursor = Game.Activity.ContentResolver.Query(
- ContactsContract.RawContacts.ContentUri,
- new[] { ContactsContract.RawContacts.InterfaceConsts.Id },
- ContactsContract.RawContacts.InterfaceConsts.ContactId + "=?",
- new[] { Convert.ToString(contact.Id)}, null);
- if (cursor != null)
- if (cursor.MoveToFirst())
- outValue = Convert.ToInt32(cursor.GetLong(0));
- }
- catch (Exception e)
- {
- ExceptionLogger.LogException(e, false, false);
- }
- finally
- {
- if (cursor != null)
- cursor.Close();
- }
- return outValue;
- }
- /// <summary>
- /// Smaže daný kontakt... (3. metoda z ContactDetailScreen)
- /// </summary>
- public void RemoveContact(Contact contact)
- {
- // Najde kontakt podle ID, smaže ho, uzavře zpátky cursor
- ICursor cursor = null;
- try
- {
- var cr = Game.Activity.ContentResolver;
- cursor = cr.Query(ContactsContract.Contacts.ContentUri,
- null, ContactsContract.Contacts.InterfaceConsts.Id + "=" + contact.Id, null, null);
- if (cursor == null)
- {
- Operations.ShowToast(Engine.Lang.Get("cannot_delete_contact"));
- return;
- }
- // Smaže pouze první, i kdyby jich tam bylo víc...
- if (cursor.MoveToNext())
- {
- String lookupKey = cursor.GetString(cursor.GetColumnIndex(ContactsContract.Contacts.InterfaceConsts.LookupKey));
- Uri uri = Uri.WithAppendedPath(
- ContactsContract.Contacts.ContentLookupUri, lookupKey);
- cr.Delete(uri, ContactsContract.Contacts.InterfaceConsts.Id + "=" + contact.Id, null);
- Operations.ShowToast(Engine.Lang.Get("contact_deleted") + ": " + contact.DisplayName);
- }
- }
- catch (Exception e)
- {
- Operations.ShowToast(Engine.Lang.Get("cannot_delete_contact"));
- ExceptionLogger.LogException(e, false, false);
- }
- finally
- {
- if (cursor != null)
- cursor.Close();
- }
- // A znovu načte aktuální data...
- RemoveCacheValue(contact.Id);
- FillContactsFromPhone();
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment