Файл: Диплом-DESKTOP-T5SUMLS.docx

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 26.11.2019

Просмотров: 932

Скачиваний: 2

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

РОЗДІЛ 3. ОПИС ПРОГРАМНОГО ПРОДУКТУ ТА АЛГОРИТМ ЙОГО СТВОРЕННЯ


Перш ніж приступити безпосередньо до розробки додатку потрібно було вивчити спосіб обміну інформації з онлайн-сервісом за допомогою його API «див. розділ 1» «з електронного ресурсу [11]», визначити необхідні для роботи додатку дані та спосіб їх збереження на клієнтському пристрої.

Приклад JSON об’єкту отриманого з сервера, що містить в собі один запис:

{

"direction":"ltr",

"id":"feed/5594de7cfea0e74d87000305",

"title":"Хабрахабр / Все публикации",

"description":"",

"self":

{"href":"https://theoldreader.com/reader/api/0/stream/contents?output=json&xt=user/-/state/com.google/read&n=1&s=user/-/state/com.google/reading-list"},

"alternate":

{"href":"http://habrahabr.ru/","type":"text/html"},

"updated":1443100001,

"items":[

{

"crawlTimeMsec":"1443100000931",

"timestampUsec":"1443099553000000",

"id":"tag:google.com,2005:reader/item/5603f5615f45b7c15c003bed",

"categories":

[

"user/-/state/com.google/reading-list","user/-/state/com.google/fresh",

"user/-/label/Разное"

],

"title":"[Перевод] Еще одно встроенное шпионское приложение обнаружено на компьютерах Lenovo",

"published":1443099553,

"updated":1443099553,

"canonical":[{"href":"http://habrahabr.ru/post/267645/"}],

"alternate":[{"href":"http://habrahabr.ru/post/267645/","type":"text/html"}],

"summary":

{

"direction":"ltr",

"content":"<br><img src=\"https://habrastorage.org/files/0c0/a69/3d9/0c0a693d92c34237b803604f5545baf1.jpg\"><br>\r\n<br>\r\nУ восстановленного с завода Thinkpad с предустановленной Windows 7 в планировщике есть приложение, которое включается раз в день и собирает данные о том, как вы используете компьютер. После этого все сведения отправляются компании-аналитику. Информация о сборе данных присутствует в пользовательском соглашении, но зарыта очень глубоко.<br>\r\n <a href=\"http://habrahabr.ru/post/267645/#habracut\">Читать дальше →</a>"

},

"author":"VahMaster",

"annotations":[],

"likingUsers":[],

"likingUsersCount":0,

"comments":[],

"origin":

{

"streamId":"feed/5594de7cfea0e74d87000305",

"title":"Хабрахабр / Все публикации",

"htmlUrl":"http://habrahabr.ru/"

}

}

],

"continuation":"1443099999"

}

Клас який описує структуру цього об’єкта вигляде так:

public class Self

{

public string href { get; set; }

}


public class Canonical

{

public string href { get; set; }

}


public class Alternate

{

public string href { get; set; }

public string type { get; set; }

}


public class Summary

{

public string direction { get; set; }

public string content { get; set; }

}


public class Origin

{

public string streamId { get; set; }

public string title { get; set; }

public string htmlUrl { get; set; }

}


public class RawItem

{

public string crawlTimeMsec { get; set; }

public string timestampUsec { get; set; }

public string id { get; set; }

public List<string> categories { get; set; }

public string title { get; set; }

public int published { get; set; }

public int updated { get; set; }

public List<Canonical> canonical { get; set; }

public List<Alternate> alternate { get; set; }

public Summary summary { get; set; }

public string author { get; set; }

public List<object> annotations { get; set; }

public List<object> likingUsers { get; set; }

public int likingUsersCount { get; set; }

public List<object> comments { get; set; }

public Origin origin { get; set; }

}


public class RawFeedItem

{

public string direction { get; set; }

public string id { get; set; }

public string title { get; set; }

public string description { get; set; }

public Self self { get; set; }

public int updated { get; set; }

public List<RawItem> items { get; set; }

}


    1. Модель БД для збереження даних

Структура даних, що описана вище, підходить для того щоб успішно десеріалізувати об’єкти отримані з сервера, але вона не підходить для збереження даних в локальній БД через складну структуру. Річ у тому, що SQLite лише імітує ORM «що описано на офіційному сайті додатку [16]», але не являється такою, тому вона може оперувати тільки простими типами даних. В нашому випадку всю структуру можна звести до однієї таблиці, яка виглядає наступним чином:


[Table("FeedItem")]

class FeedItem : INotifyPropertyChanged

{

[PrimaryKey]

public string id { get; set; }

public string title { get; set; }


public string timestampUsec { get; set; }

public string dateTime

{

get { return DataController.UnixTimeStampToDateTime(timestampUsec).ToString("HH:mm:ss dd.MM.yyyy"); }

}


public string href { get; set; }

public string content { get; set; }

public string author { get; set; }

public string tagid { get; set; }

public string tagName { get; set; }

public string streamId { get; set; }

public string streamName { get; set; }


private bool _isReaded;

public bool isReaded

{

get { return _isReaded; }

set { _isReaded = value; NotifyPropertyChanged(); }

}

public Visibility VisibilityIsReaded

{

get { return isReaded ? Visibility.Collapsed : Visibility.Visible; }

}

public Brush ReadedForeground

{

get { return isReaded ? new SolidColorBrush(Colors.Black) : new SolidColorBrush(Colors.Blue); }

}


private bool _isStarred;

public bool isStarred

{

get { return _isStarred; }

set { _isStarred = value; NotifyPropertyChanged(); }

}

public Symbol SymbolIsStarred

{

get { return isStarred ? Symbol.SolidStar : Symbol.OutlineStar; }

}


private bool _isLiked;

public bool isLiked

{

get { return _isLiked; }

set { _isLiked = value; NotifyPropertyChanged(); }

}

public string SymbolIsLikedStr

{

get { return isLiked ? "\U0000E00B" : "\U0000E006"; }

}

}

Тобто, модель «сирих» даних «рис. 3.1» в процесі синхронізації перетворюється в одну таблицю яку можна використовувати з додатком SQLite «рис. 3.2», що дозволить використовувати прив’язку даних до елементів управління та перекласти більшість CRUD (Create, Read, Update, Delete) операцій з БД на додаток SQLite, замість того щоб описувати їх самостійно «що описано в пряцях [16, 24, 26, 27]».


Рис. 3.1. Схема моделі «сирих» даних (що приходять з сервера)

Рис. 3.2. Схема моделі локальної бази даних

    1. Процес оновлення та збереження даних

Враховуючи описане вище, оновлення та збереження даних проходить у 3 етапи:

  • Отримання списку тегів (папок)

  • Отримання підписок для кожного тега

  • Отримання записів для кожної підписки

Справ в тому, що самі записи не містять у собі інформації до якого тегу вони належать, також там не має інформації про відмітки Starred, Liked, що також треба враховувати. Тому при отриманні записів з підписок ми рекурсивно записуємо в БД записи та присвоюємо їм ідентифікатор тега та підписки, для того щоб в подальшому користувач мав змогу фільтрації даних за цими параметрами.

Після цього з’являєтеся вся необхідна інформація для збереження даних в локальну БД та відображення їх користувачу.


    1. Інструкція для користувача

Перед використанням додатку необхідно зареєструватися на сайті http://www.theoldreader.com. При першому запуску додатку буде запропоновано ввести ваш логін та пароль, як показано на рис. 1.

Рис. 3.3. Вікно вводу логіна та пароля

Після авторизації додаток оновить усі, існуючі в профілі, користувача підписки, та надалі буде автоматично слідкувати за їх оновленням, а у разі появи нових записів повідомить про це користувача за допомогою центру повідомлень Windows 10 та позначкою на плитці додатку.

Рис. 3.4. Робоче вікно програми

Рис. 3.5. Робоче вікно програми на мобільному пристрої


Також у користувача є можливість відмітити всі нові записи прочитаними, та оновити всі підписки вручну як видно на рис. 2.

Панель зліва дозволяє перемикатися між теками користувача (всі записи, обрані, улюблені). Натиснувши на кнопку з трьома горизонтальними лініями зверху, панель розшириться та покаже підписи до всіх кнопок що на ній знаходяться, див. рис. 3.

Рис. 3.6. Робоче вікно програми з розгорнутою панеллю

Рис. 3.7. Робоче вікно програми з розгорнутою панеллю на мобільному пристрої

Натиснувши на новину вона відкриється в головному вікні з повним її змістом і автоматично буде помічена як прочитана, також з’являться кнопки для додавання цієї новини в улюблені або в обрані записи за допомогою кнопок на верхній панелі, як показано на рис. 4.

Рис. 3.8. Робоче вікно з контентом новини

Рис. 3.9. Робоче вікно з контентом новини на мобільному пристрої


    1. Вихідний код програмного продукту

Клас для роботи з API сервера:

using System;

using System.Collections.Generic;

using System.IO;

using System.Linq;

using System.Net;

using System.Net.Http;

using System.Net.Http.Headers;

using System.Text;

using System.Threading.Tasks;

using Windows.UI.Popups;


namespace TheOldReader_WinX

{

class TheOldReader

{

private const String APIEndPoint = "https://theoldreader.com/reader/api/0/";


private string _token;

public string token

{

get { return _token; }

set { _token = "GoogleLogin auth=" + value; }

}


public async Task<bool> LogIn(string user, string pwd)

{

var output = await Post("https://theoldreader.com/reader/api/0/accounts/ClientLogin",

String.Format("client=TheOldReaderWX&accountType=HOSTED_OR_GOOGLE&service=reader&Email={0}&Passwd={1}", user, pwd));

try

{

token = output.Substring(output.IndexOf("Auth=")).Replace("Auth=", "");

return true;

}

catch

{

return false;

}

}


private async Task<string> Post(String URI, String _PostData)

{

var client = new HttpClient();

client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization: ", token);

HttpResponseMessage wcfResponse = await client.PostAsync(new Uri(URI), new StringContent(_PostData, Encoding.UTF8, "application/x-www-form-urlencoded"));

return await wcfResponse.Content.ReadAsStringAsync();

}


private async Task<string> Get(String URI)

{

var client = new HttpClient();

client.BaseAddress = new Uri(URI);

client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization: ", token);

return await client.GetStringAsync(URI);

}


#region Params

public async Task<string> getTagList()

{

String API = "tag/list?output=json";

String URI = APIEndPoint + API;

return await Get(URI);

}


public async Task<string> getSubscriptionList()

{

String API = "subscription/list?output=json";

String URI = APIEndPoint + API;

return await Get(URI);

}


public async Task<string> getUnreadCount()

{

String API = "unread-count?output=json";

String URI = APIEndPoint + API;

return await Get(URI);

}


public async Task<string> getUserInfo()

{

String API = "user-info?output=json";

String URI = APIEndPoint + API;

return await Get(URI);

}


public async Task<string> getMoreItemsForSubscription(String SubName, int ItemCount, String Continuation)

{

String URI = String.Format("{0}stream/contents?output=json&n={2}&c={3}&s={1}", APIEndPoint, SubName, ItemCount, Continuation);

return await Get(URI);

}

public async Task<string> getItemsForSubscription(String SubName, int ItemCount)

{

String URI = String.Format("{0}stream/contents?output=json&n={2}&s={1}", APIEndPoint, SubName, ItemCount);

return await Get(URI);

}


public async Task<string> getUnreadItemsForSubscription(String SubName, int ItemCount)


{

String URI = String.Format("{0}stream/contents?output=json&xt=user/-/state/com.google/read&n={2}&s={1}", APIEndPoint, SubName, ItemCount);

return await Get(URI);

}


//Получить все непрочитанные записи

public async Task<string> getAllUnreadItems(int ItemCount)

{

String URI = String.Format("{0}stream/contents?output=json&xt=user/-/state/com.google/read&n={2}&s={1}", APIEndPoint, "user/-/state/com.google/reading-list", ItemCount);

return await Get(URI);

}


//Получить все избранные записи

public async Task<string> getStarredItems()

{

String URI = String.Format("{0}stream/contents?output=json&xt=user/-/state/com.google/read&n={2}&s={1}", APIEndPoint, "user/-/state/com.google/starred", 1000);

return await Get(URI);

}


//Получить все понравившиеся записи

public async Task<string> getLikedItems()

{

String URI = String.Format("{0}stream/contents?output=json&xt=user/-/state/com.google/read&n={2}&s={1}", APIEndPoint, "user/-/state/com.google/like", 1000);

return await Get(URI);

}


//Отметить запись прочитанной

public async Task<string> markFeedItemRead(string ItemId, bool bRead)

{

String PostData = String.Format("{0}=user/-/state/com.google/read&i={1}", bRead ? "a" : "r", ItemId);

return await Post(APIEndPoint + "edit-tag", PostData);

}


//Отметить все прочитанными

public async Task<string> markAllItemsAsRead(string FeedId)

{

String PostData = String.Format("s={0}", FeedId);

return await Post(APIEndPoint + "mark-all-as-read", PostData);

}


//Отметить несколько записей прочитанными

public async Task<string> markFeedItemsRead(List<string> ItemIds, bool Read)

{

return await changeTagOfItems("user/-/state/com.google/read", ItemIds, Read);

}


//Отметить звездочкой

public async Task<string> starItem(string FeedId, bool Starred)

{

String szPostData = String.Format("{0}={2}&i={1}", Starred ? "a" : "r", FeedId, "user/-/state/com.google/starred");

return await Post(APIEndPoint + "edit-tag", szPostData);

}


//Отметить звездочкой несколько записей

public async Task<string> starItems(List<string> FeedIds, bool Starred)

{

return await changeTagOfItems("user/-/state/com.google/starred", FeedIds, Starred);

}


//Изменить тег нескольких записей

private async Task<string> changeTagOfItems(string TagName, List<string> FeedIds, bool Add)

{

if (FeedIds != null && FeedIds.Count > 0)

{

StringBuilder sb = new StringBuilder();

sb.AppendFormat("{0}={1}", Add ? "a" : "r", TagName);


foreach (String curItemId in FeedIds)

sb.AppendFormat("&i={0}", curItemId);


String PostData = sb.ToString();

return await Post(APIEndPoint + "edit-tag", PostData);

}

return "";

}


//Переметить подписку в папку

public async Task<string> moveSubscriptionToFolder(string feedId, string folderId)

{

String PostData = "";

if (!String.IsNullOrEmpty(folderId) && folderId != "user/-/state/com.google/reading-list")

PostData = String.Format("ac=edit&s={0}&a={1}", feedId, folderId);

else

PostData = String.Format("ac=edit&s={0}&r={1}", feedId, folderId);

return await Post(APIEndPoint + "subscription/edit", PostData);

}


//Подписаться

public async Task<string> addSubscription(string FeedUrl)

{

String PostData = "";

return await Post(APIEndPoint + "subscription/quickadd?quickadd=" + FeedUrl, PostData);

}


//Отписаться

public async Task<string> unsubscribe(string feedId)

{

String PostData = String.Format("ac=unsubscribe&s={0}", feedId);

return await Post(APIEndPoint + "subscription/edit", PostData);

}

#endregion

}

}


    1. Висновки

Отже даний додаток використовує найновіші функції UWP, його можна завантажити з магазина на ПК, телефон чи телевізор з Windows 10 і він буде виглядати та працювати на цих пристроях саме так як очікує того користувач.


ВИСНОВКИ


У дипломній роботі вирішено актуальну задачу, пов’язану з розробкою клієнтського додатку для онлайн-агрегатора новин, на базі універсальної програмної платформи Microsoft Windows (UWP).

На початку написання дипломної роботи було переглянуто історію створення подібних додатків та існуючі сервіси для агрегації новин. Отримано основну інформацію що до їх функціонування та обрано сервіс для якого буде розроблятися додаток.

В процесі проектування програмного забезпечення була обрана мова програмування С# на базі UWP, створена структура моделі та покрокові етапи розробки програми. UWP дозволяє розробляти один дизайн, один код для будь яких пристроїв що працюють під управлінням сімейства операційних систем Windows 10. На мою думку у даної платформи величезний потенціал, який розкривається перед розробниками програмного забезпечення.

З розробленим додатком, користувачам сервісу TheOldReader, стане простіше та зручніше слідкувати за оновленням улюблених блогів, сайтів новин чи просто розважальних ресурсів, з будь якого пристрою, з будь якого місця.