/ContactsApp

ContactsApp апликација за менаџирање на контакти и праќање на бесплатни SMS пораки. Проектна задача по предметот Визуелно Програмирање @ FINKI - 2018

Primary LanguageC#

Лого на апликацијата/Application logo

ContactsApp

Модерна апликација за менаџирање на контакти и испраќање на бесплатни1 SMS пораки

Modern application for managing contacts and sending free1 SMS



Содржина


Карактеристики

  • Модерен интерфејс и темна тема за полесно користење на апликацијата
  • Лесен и едноставен начин на менаџирање и пребарување на вашите контакти
  • Преглед на детали за контактот
  • Импортирање на конктати од vCard (.vcf) датотека
  • Експортирање на контактите во vCard (.vcf) датотека
  • Бесплатно испраќање на SMS пораки
  • Преглед на испратените пораки до секој контакт

Краток опис на апликацијата

Оваа именик-апликација е намената за менаџирање на контакти. За полесна употреба во неа е имплементиран лесен и јасен интерфејс за користење. Темната тема на интерфејсот е соодветна и при дневна и при ноќна светлина. Апликацијата исто така нуди можност за испраќање на бесплатни SMS пораки до контактите кои што ги имате зачувано во именикот. Испраќањето на SMS пораките се одвива преку веб сервисот TextLocal. Важно е да се напомене дека успешното праќање на бесплатната SMS порака повлекува имање на интернет конекција и ненадминување на лимитот дефиниран од веб сервисот. Апликацијата нуди можност за прегледување и измена на деталите за контактот, како и негово отстранување од именикот, и преглед на историјата на пратени пораки до контактот. Во апликацијата е имплементирано импортирање и експортирање на контактите во стандарден, vCard (.vcf), формат. Исто така, за обезбедување на подобро корисничко искуство со апликацијата, е имплементирана бинарна серијализација на контактите во скриена (hidden) датотека во директориумот на апликацијата (C:\Users\*username*\Documents\ContactsApp).


Упатство за користење

1. Опис на формите во апликацијата

При уклучувањето на апликацијата, во средината на екранот, се појавува главниот прозорец на апликацијата кој е прикажан на Слика 1.

Главен прозорец
Слика 1: Главен прозорец на апликацијата

Овде на корисникот му се прикажуваат сите контакти кои што ги има, доколку нема внесено се прикажува соодветната лабела, како и поле за пребарување и копче за додавање на нов контакт. Полето за пребарување е оневозможено доколку нема додадено ниеден контакт во листата. Во рамките на главниот прозорец, на почетниот дел е прикажано мени со 3 подкатегории (File, Edit, Help) чии функционалности се прикажани во продолжение.

File мени Edit мени Help мени
Слика 2, 3, 4: Функционалности на менито

Исто така експортирањето и бришењето на сите контакти се оневозможени доколку нема контакти во листата.

Со клик на копчето Add Contact се отвара нова форма (Слика 5) во која во означените полиња се внесуваат податоците за контактот и опционално се внесува E-mail адреса и се избира слика.

Форма за нов контакт
Слика 5: Форма за додавање на нов контакт

На секое поле е додадена валидација со помош на ErrorProvider контрола во случај ако задолжителни податоци недостасуваат или не се внесени во точниот формат.

При клик на контакт (Слика 6) се отвара нова форма во која се прикажани деталите за корисникот и 3 копчиња.

Избор на контакт Детали за избраниот контакт Промена на деталите за контактот
Слика 6: Избор на контакт и отварање на формата со деталите за контактот и нивна промена

Прикажаните копчиња овозможуваат бришење на контактот, преглед и праќање на SMS пораки. Со двоен лев клик на некое од полињата се влегува во режим на промена и сите други полиња и копчиња се оневозможени се додека не се кликне прикажаното копче Save за зачувување на промената. Исто така и за овие полиња е додадена валидација со помош на ErrorProvider контролата.

Со клик на копчето Send a message, на корисникот му се прикажува форма (Слика 7) во која го внесува испраќачот во првото поле, а додека во второто поле ја внесува пораката.

Форма за праќање на SMS порака
Слика 7: Форма за праќање на SMS порака

Пораката се испраќа со клик на копчето Send и се добива соодветен одговор дали праќањето е успешно или не.

Испратените пораки, на корисникот, му се прикажуваат со клик на копчето Sent messages каде што во нова форма (Слика 8) се листаат сите пораки кои што се пратени до тој контакт. Исто така при избор на некоја порака, во нов поглед, се прикажуваат информациите за неа (Слика 8).

Приказ на лабела кога нема пратено пораки до контактот Листање на пратените пораки Детали за избраната порака
Слика 8: Форма за листање на пратените пораки и прикажување на детали за порака

Дополнително, на корисникот му е дозволено да ја избрише пораката од листата на пратени пораки.

2. Опис на имплементацијата

За чување на контактите, е употребена мапа SortedDictionary<char, HashSet<ContactEntry>>() во која клучевите се карактери (букви од азбуката) а вредностите се множества HashSet од тип ContactEntry. Уникатноста во рамките на едно множество е обезбедена со IEqualityComparer<ContactEntry> TelephoneComparer кој што обезбедува уникатност по телефонскиот број на контактите. Исто така е обезбедена и уникатност на контактите во целата мапа со помош на методот IsDuplicate() чија имплементација е следната:

private bool IsDuplicate(string number, bool importing = false)
{
    foreach (var entry in Contacts)
    {
        foreach (var usr in entry.Value)
        {
            if (usr.TelephoneNumber.Equals(number))
            {
                if (!importing)
                    MessageBox.Show(
                        "You have this number saved with different name.\n" +
                        $"Here are the informations: {usr} {usr.TelephoneNumber}",
                        "Found duplicate",
                        MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return true;
            }
        }
    }
    
    return false;
}

Импортирањето и експортирањето на контактите е направено со помош на библиотеката MixERP.Net.VCards. Импортирањето најпрво започнува со избор на датотеката која што ќе се импортира со помош на OpenFileDialog:

var dialog = new OpenFileDialog();
dialog.Filter = "VCard files (*.vcf) | *.vcf";
dialog.Title = "Choose a VCard file to import";

if (dialog.ShowDialog() == DialogResult.OK)
{
    vcardPath = dialog.FileName;
    Console.WriteLine($"[{vcardPath}] was picked.");
}
else
{
    Console.WriteLine("File choosing was canceled!");
    return;
}

Потоа, избраната датотека се парсира со помош на библиотеката чиј метод, како резултат, враќа IEnumerable<VCard>. Од вратениот резултат, со изминување на колекцијата, се креираат контактите

ContactEntry contact = null;
.
.
.
contact = new ContactEntry()
{
    FirstName = DecodeQuotedPrintable(vcard.FirstName.Trim()),
    LastName = DecodeQuotedPrintable(vcard.LastName.Trim()),
    TelephoneNumber = number.Trim(),
    Email = email.EmailAddress.Trim(),
    ImageBase64 = photoString
};

а потоа и се додаваат во мапата од контакти

.
.
.
if (Contacts.ContainsKey(key))
{
    Contacts[key].Add(contact);
}
else
{
    Contacts[key] = new HashSet<ContactEntry>(ContactEntry.TelephoneComparer);
    Contacts[key].Add(contact);
}

Помошниот методот DecodeQuotedPrintable() со имплементација

public string DecodeQuotedPrintable(string encoded)
{
    if (!Regex.IsMatch(encoded, @"^(=[0-9a-f]{2}){1,}", RegexOptions.IgnoreCase))
        return encoded;

    var output = new List<byte>();

    for (int i = 0; i < encoded.Length; i++)
    {
        if (encoded[i] == '=')
        {
            var sHex = encoded.Substring(i + 1, 2);
            var hex = Convert.ToInt32(sHex, 16);
            var b = Convert.ToByte(hex);
            output.Add(b);
            i += 2;
        }
    }

    return Encoding.UTF8.GetString(output.ToArray());
}

врши декодирање на специјалните знаци (мак азбука и други non-ASCII карактери) кои што енкодирани во QuotedPrintable формат.

При експортирање, најпрво се врши проверка (и креирање) доколку работниот фолдер на апликацијата не постои. Потоа, се изминуват сите контакти од кои се креира објект од класата VCard кој подоцна ќе биде искористен од библиотеката за да се запише контактот во датотеката.

var vcard = new VCard()
{
    Version = VCardVersion.V2_1,
    FirstName = contact.FirstName,
    LastName = contact.LastName,
    Telephones = new List<Telephone>()
    {
        new Telephone()
        {
            Number = contact.TelephoneNumber,
            Type = TelephoneType.Cell
        }
    },
    Emails = new List<Email>()
    {
        new Email()
        {
            EmailAddress = contact.Email,
            Type = EmailType.Smtp
        }
    },
    Photo = new Photo(true, "JPEG", contact.ImageBase64)
};

Испраќањето на SMS пораката се прави преку HTTP/S протоколот и се комуницира со веб сервисот TextLocal. Најпрво се крера URL адресата

var number = $"00389 {SelectedContact.TelephoneNumber.Substring(1)}";
Message = txtMessage.Text;
Sender = txtBoxSender.Text.Trim();

var url =
    "https://api.txtlocal.com/send/?" +
    "apikey=" + ApiKey +
    "&numbers=" + number +
    "&message=" + Message +
    "&sender=" + Sender;

со соодветната порака, испраќач и број на која се испраќа HTTP PUSH барање до веб сервисот.

.
.
.
objRequest.Method = "POST";
objRequest.ContentLength = Encoding.UTF8.GetByteCount(url);
objRequest.ContentType = "application/x-www-form-urlencoded";

try
{
    using (var myWriter = new StreamWriter(objRequest.GetRequestStream()))
    {
        myWriter.Write(url);
    }
    Date = DateTime.Now;
}
.
.
.

Добиениот HTTP одговор се запишува во променливата string result

var objResponse = objRequest.GetResponse() as HttpWebResponse;
if (objResponse == null)
    return string.Empty;

using (var sr = new StreamReader(objResponse.GetResponseStream()))
{
    result = sr.ReadToEnd();
}

Користени библиотеки/сервиси

Во рамките на нашиот проект беа искористени една надворешна библиотека (MixERP.Net.VCards како NuGet пакет) и еден веб сервис (TextLocal).

  1. MixERP.Net.VCards - за импортирање/експортирање на контактите
  2. TextLocal - за испраќање на SMS пораки

Изработено од:

  1. Костадин Крстев 161 169
  2. Јован Наков 161 195

1Бесплатните SMS пораки се лимитирани на приближно 20 од страна на веб сервисот за праќање на истите



Table of Contents


Features

  • Modern interface and dark theme for better usability
  • Simple and easy way for managing and searching your contacts
  • Contact details overview
  • Import contacts from vCard (.vcf) file
  • Export contacts to vCard (.vcf) file
  • Send Free SMS
  • Sent messages overview for every contact

Brief description of the application

This contacts-application is intended for managing contacts. For easier use, an easy and clean interface is implemented. The dark theme of the interface is appropriate for both daylight and night light. The application also offers the option of sending free SMS to the contacts that you have stored in the contact list. Sending SMS takes place via the TextLocal web service. It is important to note that the successful sending of a free SMS entails the existence of an Internet connection and not exceeding the limit defined by the web service. The application provides the ability to view and edit contact details, as well as remove it from the contact list, and view the history of sent messages to the contact. In the application, importing and exporting contacts in standard, vCard (.vcf) format, is implemented. Also, in order to provide a better user experience with the application, binary serialization of the contacts is implemented in a hidden file in the application directory (C:\Users\*username*\Documents\ContactsApp).

User manual

1. Description of the forms in the application

When the application is started, in the middle of the screen, the main application window appears, which is shown in Figure 1.

Main window
Figure 1: Main application window

Here the user is shown all the contacts that he/she has, if no contacts are added a corresponding label is displayed, a search box and a button to add a new contact. The search field is disabled if no contact has been added to the list. Within the main window, the home screen shows a menu of 3 subcategories (File, Edit, Help) whose functions are shown below.

File menu Edit menu Help menu
Figure 2, 3, 4: Menu functions

Also, exporting and deleting all contacts is disabled if there are no contacts in the list.

By clicking on the Add Contact button, a new form is opened (Figure 5) in which the contact information is entered in the marked fields and, optionally, an e-mail address is entered or an image is selected.

New contact form
Figure 5: New contact form

Validation is added to each field using ErrorProvider control, in case the mandatory data is missing or not entered in the correct format.

When clicking on a contact (Figure 6), a new form is displayed showing user details and 3 buttons.

Choosing contact Contact details form Editing contact
Figure 6: Choosing a contact and opening the form with contact details and editing them

The displayed buttons allow to delete the contact, view and send SMS messages. Double-click on one of the fields enters editing mode and all other fields and buttons are disabled until the displayed Save button is clicked to save the changes. Also, validation is added to these fields by using the ErrorProvider control.

By clicking the Send a message button, the user is presented with a form (Figure 7) in which the sender is entered in the first field, while in the second field the message is entered.

Send SMS form
Figure 7: Send a SMS Form

The message is sent by clicking the Send button and getting the appropriate answer whether the sending is successful or not.

Sent messages, to the user are displayed, by clicking the Sent messages button where in the new form (Figure 8) are listed all messages that are sent to that contact. Also, when selecting a message, the information about it is displayed in a new view (Figure 8).

Label showing when no messages are sent Listing sent messages Message details
Figure 8: A form for listing sent messages and displaying message details

Additionally, the user is allowed to delete the message from the list of sent messages.

2. Description of the implementation

For contacts storing, a SortedDictionary<char, HashSet <ContactEntry>>() map is used, in which keys are characters (alphabet letters) and values are HashSet sets of type ContactEntry. The uniqueness within a set is provided by IEqualityComparer<ContactEntry> TelephoneComparer, which ensures the uniqueness of the phone number of the contacts. It is also ensured the uniqueness of contacts in the entire map using the IsDuplicate() method, whose implementation is given below:

private bool IsDuplicate(string number, bool importing = false)
{
    foreach (var entry in Contacts)
    {
        foreach (var usr in entry.Value)
        {
            if (usr.TelephoneNumber.Equals(number))
            {
                if (!importing)
                    MessageBox.Show(
                        "You have this number saved with different name.\n" +
                        $"Here are the informations: {usr} {usr.TelephoneNumber}",
                        "Found duplicate",
                        MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return true;
            }
        }
    }
    
    return false;
}

Importing and exporting contacts is done with the help of the library MixERP.Net.VCards. The importing starts with the selection of the file that will be imported using OpenFileDialog:

var dialog = new OpenFileDialog();
dialog.Filter = "VCard files (*.vcf) | *.vcf";
dialog.Title = "Choose a VCard file to import";

if (dialog.ShowDialog() == DialogResult.OK)
{
    vcardPath = dialog.FileName;
    Console.WriteLine($"[{vcardPath}] was picked.");
}
else
{
    Console.WriteLine("File choosing was canceled!");
    return;
}

Then, the selected file is parsed using a library whose method returns IEnumerable<VCard> as a result. From the returned result, with iteration, contacts are created

ContactEntry contact = null;
.
.
.
contact = new ContactEntry()
{
    FirstName = DecodeQuotedPrintable(vcard.FirstName.Trim()),
    LastName = DecodeQuotedPrintable(vcard.LastName.Trim()),
    TelephoneNumber = number.Trim(),
    Email = email.EmailAddress.Trim(),
    ImageBase64 = photoString
};

and then are added to the map.

.
.
.
if (Contacts.ContainsKey(key))
{
    Contacts[key].Add(contact);
}
else
{
    Contacts[key] = new HashSet<ContactEntry>(ContactEntry.TelephoneComparer);
    Contacts[key].Add(contact);
}

Auxiliary method DecodeQuotedPrintable() with implementation:

public string DecodeQuotedPrintable(string encoded)
{
    if (!Regex.IsMatch(encoded, @"^(=[0-9a-f]{2}){1,}", RegexOptions.IgnoreCase))
        return encoded;

    var output = new List<byte>();

    for (int i = 0; i < encoded.Length; i++)
    {
        if (encoded[i] == '=')
        {
            var sHex = encoded.Substring(i + 1, 2);
            var hex = Convert.ToInt32(sHex, 16);
            var b = Convert.ToByte(hex);
            output.Add(b);
            i += 2;
        }
    }

    return Encoding.UTF8.GetString(output.ToArray());
}

decodes special characters (Macedonian alphabet and other non-ASCII characters) encoded in QuotedPrintable format.

When exporting, first a check (and creation) is done for the application's working directory. Then, the contacts in the map are iterated from which a VCard object is created, which will later be used by the library to write the contact in the file.

var vcard = new VCard()
{
    Version = VCardVersion.V2_1,
    FirstName = contact.FirstName,
    LastName = contact.LastName,
    Telephones = new List<Telephone>()
    {
        new Telephone()
        {
            Number = contact.TelephoneNumber,
            Type = TelephoneType.Cell
        }
    },
    Emails = new List<Email>()
    {
        new Email()
        {
            EmailAddress = contact.Email,
            Type = EmailType.Smtp
        }
    },
    Photo = new Photo(true, "JPEG", contact.ImageBase64)
};

Sending the SMS is done through the HTTP/S protocol for communication with the web service TextLocal. First, the URL is created

var number = $"00389 {SelectedContact.TelephoneNumber.Substring(1)}";
Message = txtMessage.Text;
Sender = txtBoxSender.Text.Trim();

var url =
    "https://api.txtlocal.com/send/?" +
    "apikey=" + ApiKey +
    "&numbers=" + number +
    "&message=" + Message +
    "&sender=" + Sender;

with the corresponding message, the sender and the number to which the HTTP PUSH Request is sent to the web service.

.
.
.
objRequest.Method = "POST";
objRequest.ContentLength = Encoding.UTF8.GetByteCount(url);
objRequest.ContentType = "application/x-www-form-urlencoded";

try
{
    using (var myWriter = new StreamWriter(objRequest.GetRequestStream()))
    {
        myWriter.Write(url);
    }
    Date = DateTime.Now;
}
.
.
.

The received HTTP Response is written to the string result variable

var objResponse = objRequest.GetResponse() as HttpWebResponse;
if (objResponse == null)
    return string.Empty;

using (var sr = new StreamReader(objResponse.GetResponseStream()))
{
    result = sr.ReadToEnd();
}

Third-party libraries/services

Within our project, we used one third-party library (MixERP.Net.VCards as a NuGet package) and one web service (TextLocal).

  1. MixERP.Net.VCards - import/export for the contacts
  2. TextLocal - for SMS sending

Made by:

  1. Kostadin Krstev 161 169
  2. Jovan Nakov 161 195

1Free SMS is limited to approximately 20 by the web service used for sending them