В прошлой статье мы начали изучать мобильную «десятку», обсудив ряд важных вопросов, в том числе способы модернизации унаследованных приложений для платформы UWP, а сегодня я предлагаю несколько рецептов для более глубокого погружения в программирование мобильной винды номер 10.
Содержимое хранилища
С помощью UWP можно легко просмотреть содержимое хранилища — HDD, SDD, Flash-памяти или другого носителя с любой поддерживаемой файловой системой. Поэтому рассматриваемое ниже приложение с одинаковым успехом будет работать на всех аппаратных платформах с Windows 10. Но для нас первостепенный интерес представляют смартфоны.
Создай в VS новый UWP-проект. Чтобы иметь возможность в одном приложении реализовать несколько экспериментов, каждый на отдельной странице, помести на макет компонент Pivot (многообещающее название. — Прим. ред.). Измени у первой страницы, PivotItem, значение свойства Header, например на «Поиск».
Так как на нее нам надо поставить несколько компонентов, а PivotItem этого не позволяет, поскольку внутри него объекты не должны более одного раза включать свойство Content, то нужна подложка. Пусть ее роль сыграет StackPanel. Помести последний на PivotItem. Внутри StackPanel компоненты располагаются друг над другом — стопкой, занимая всю ширину. Затем помести в стек компонент TextBox для ввода имени для поиска, назови его inputText. Ниже положи кнопку — Button — с именем Search для запуска поиска. Результат поиска будем выводить в TextBlock. Свойству TextWrapping последнего присвой значение Wrap.
Создай обработчик события нажатия кнопки. Прежде чем помещать в него код, добавим необходимые пространства имен. Во-первых, для работы с хранилищем понадобится Windows.Storage. Во-вторых, для формирования поисковых запросов в содержимом хранилища нужно будет пространство Windows.Storage.Search
. Оно предоставляет классы для поиска и перечисления файлов с использованием расширенного синтаксиса запросов AQS. Наконец, чтобы работать с текстом и строками, нужно пространство имен System.Text
.
Вернемся в созданный обработчик события нажатия кнопки. В Windows есть широко известный набор предопределенных папок: Music, Documents, Pictures и прочие. У меня на смартфоне куча музыки, поэтому я расположил папку для хранения музыки на SD-карте, но она по-прежнему остается папкой Music. Можно легко получить указатель на известную папку, сохранив его в объекте класса StorageFolder:
StorageFolder musicFolder = await KnownFolders.GetFolderForUserAsync(null, KnownFolderId.MusicLibrary);
Для этого асинхронно вызывается статический метод GetFolderForUserAsync
класса KnownFolders. Последний предоставляет доступ к локальным библиотекам юзера. В качестве параметров метод получает: имя пользователя, библиотеку которого надо вернуть (если подразумевается текущий юзер, передается null
), вторым параметром указывается член перечисления KnownFolderId, куда входят стандартные папки. В нашем случае это MusicLibrary
.
Далее нам надо создать список строк, который будет предоставлять фильтр по типам файлов для выбора:
List<string> fileTypeFilter = new List<string>();
fileTypeFilter.Add(".mp3");
Следующим действием создадим объект класса QueryOptions, он позволяет задать параметры для поиска в содержимом папок. Конструктор получает два параметра: член перечисления CommonFileQuery и список расширений файлов, созданный на предыдущем шаге. Перечисление CommonFileQuery содержит члены, определяющие ход поиска и результирующий список: глубокое, неглубокое представление файлов, сортировка по определенному свойству — дате, заголовку и так далее.
QueryOptions queryOptions = new QueryOptions(CommonFileQuery.OrderBySearchRank, fileTypeFilter);
Тем самым мы определили параметры расширенного синтаксиса запросов — AQS для выборки файлов по ключевым словам. Теперь надо задать ключевое слово для поиска из строки ввода:
queryOptions.UserSearchFilter = inputText.Text;
Следующим действием создадим объект класса StorageFileQueryResult, хранящий результат запроса поиска файлов. Этот объект образуется на выходе метода CreateFileQueryWithOptions
объекта StorageFolder, метод получает один параметр для поиска — объект класса QueryOptions.
StorageFileQueryResult queryResult = musicFolder.CreateFileQueryWithOptions(queryOptions);
Далее надо создать объект, в который поместить текстовые данные, — StringBuilder. После этого асинхронно заполнить коллекцию элементов — найденных файлов через метод GetFileAsync
объекта класса StorageFileQueryResult, созданного на прошлом шаге.
IReadOnlyList<StorageFile> files = await queryResult.GetFilesAsync();
Когда коллекция будет заполнена, можно вывести количество найденных файлов и их список.
outputText.Append(files.Count + " files found:nn");
Чтобы вывести список, достаточно обработать каждый элемент коллекции:
foreach (StorageFile file in files)
outputText.Append(file.Name + "n");
Последним действием метода заполненный объект StringBuilder выводим на экран, присвоив его свойству Text визуального компонента TextBlock.
Напоследок, прежде чем запускать компиляцию, сделай обработчик события асинхронным, добавив ключевое слово async перед void, иначе компиляция не пройдет, ибо внутри метода имеются асинхронные вызовы.
Я искал по слову Nirvana, в результате у меня отобразились все файлы, находящиеся в папке Nirvana, то есть имеющие в своем пути это слово.
Что с папками?
Для нового эксперимента в компоненте Pivot активизируем вторую страницу. На нее помести StackPanel, а в него кнопку и TextBlock, как на первой странице.
Поскольку папки не имеют расширения, то ситуация заметно упрощается. После получения объекта StorageFolder определенной папки собираем все подпапки в коллекцию:
IReadOnlyList<StorageFolder> folderList = await picturesFolder.GetFoldersAsync();
После чего можно перебрать коллекцию и выполнить над ее элементами любые действия (см. страницу «Папки» примера FileSearch).
СМС
Для реализации кейса отправки СМС подготовим третью страницу в нашем приложении. Выбери Pivot, открой коллекцию Items. Откроется редактор коллекции. В списке слева уже находятся два элемента. Для создания еще одной страницы из ниспадающего списка выбери пункт «Другой тип». Откроется обширный список объектов, которые можно создать. Нам нужен PivotItem.
После выбора нужного типа в редакторе коллекции можно настроить цвет фона страницы, фоновый рисунок и прочее.
Помести на страницу StackPanel, а в него стопкой — TextBlock c надписью «Номер телефона», ниже поле редактирования — TextBox, еще ниже — TextBlock, содержащий строку «Сообщение», поле ввода сообщения — компонент TextBox и замыкающий компонент — кнопку с надписью «Отправить». Если ты изменил цвет фона страницы на черный, тогда по умолчанию кнопка не будет видна. В таком случае надо изменить значения свойств в свитке «Кисть» (Brush, если у тебя английская версия студии). Ниже кнопки можно добавить TextBlock для вывода диагностических сообщений.
Создаем у кнопки обработчик события клика и ныряем в код. Так как мы собираемся работать с СМС, понадобится подключить пространство имен Windows.Devices.Sms
.
Вдобавок разрешение на использование сотовых сообщений надо прописать в манифесте приложения — в файле Package.appxmanifest
, открой его с помощью XML-редактора. Вверху после открывающего тега <Package>
вставь строчку подключения пространства имен:
xmlns:r="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
Ниже список IgnorableNamespaces дополни ссылкой:
r: IgnorableNamespaces="uap mp r">
Внизу файла перед закрывающим тегом </Package>
и между тегами <Capabilities>
добавь возможность использования сотовых сообщений:
<r:Capability Name="cellularMessaging" />
Возвращаемся к коду C# в обработчик нажатия кнопки. Не забудь пометить его ключевым словом async. В начале метода проверяем, чтобы поля ввода номера телефона и сообщения содержали данные, иначе отправка СМС не имеет смысла. Если поля заполнены, первым делом создаем СМС-устройство класса SmsDevice2, он содержит всю необходимую для отправки данных функциональность:
SmsDevice2 device = SmsDevice2.GetDefault();
Затем надо создать объект сообщения. Он представлен классом SmsTextMessage2. У объекта-сообщения надо заполнить два поля: кому и само тело сообщения, берем их из полей ввода:
SmsTextMessage2 msg = new SmsTextMessage2();
msg.To = telNumText.Text;
msg.Body = messageText.Text;
Дальше очищаем диагностическое поле и выводим туда текст: «Отправка сообщения...». Затем асинхронно выполняем метод SendMessageAndGetResultAsync
объекта класса SmsDevice2. Этот метод получает объект-сообщение — SmsTextMessage2, а возвращает объект класса SmsSendMessageResult, содержащий результат выполнения. Если выполнить не удалось, в этот объект передаются причины неудачи: код ошибки модема, код ошибки транспорта сети, код ошибки сети и так далее.
SmsSendMessageResult result = await device.SendMessageAndGetResultAsync(msg);
if (result.IsSuccessful) {
text.Append("Сообщение успешно отправлено");
} else {
text.Append("Ошибка отправки сообщения");
}
Наш код только диагностирует успех или неудачу отправки и выводит соответствующее сообщение. При желании можно расширить вывод.
Еще один интересный таск, связанный с СМС, — это создание фильтра приходящих сообщений и регистрация его как фоновой задачи. В результате он будет автоматически проверять все приходящие сообщения и, заметив определенную последовательность символов (например, служебное сообщение от оператора сотовой связи), выполнять заданное действие.
Контакты
Можно, особо не напрягаясь, извлечь из телефонного справочника любые сведения о любом контакте. Вот только выборочный и далеко не полный список: имя, фамилия, отчество, дата рождения, ник, номер телефона, мыло, фотография — любые введенные данные. Напишем приложение, которое будет выводить в список ID контакта и имя. Его можно будет легко расширить для вывода других сведений. Мне надоело добавлять страницы, поэтому я организовал новый проект UWP-приложения. На макет положим кнопку и по традиции текстовое поле — TextBlock.
Чтобы иметь возможность работать с телефонными контактами, в раздел Capabilities манифеста надо добавить возможность:
<uap:Capability Name="contacts" />
Далее создаем обработчик нажатия кнопки и пишем такой код. Сначала подключаем пространства имен: Windows.ApplicationModel.Contacts
— для работы с контактами, System.Text
— для работы с текстом, а именно с типом данных StringBuilder. Пометим обработчик словом async. Внутри него первым делом создадим объект ContactStore:
ContactStore store = await ContactManager.RequestStoreAsync(ContactStoreAccessType.AllContactsReadOnly);
Этот объект предоставляет базу данных контактов. Чтобы его получить, вызывается статический метод RequestStoreAsync
класса ContactManager, метод получает член перечисления ContactStoreAccessType, который обозначает способ доступа к данным, в данном случае все контакты только для чтения. После этого ContactStore преобразуется к ContactReader с помощью метода GetContactReader
первого:
ContactReader reader = store.GetContactReader();
В этом виде записи из БД контактов сгруппированы пачками. Далее с помощью метода ReadBatchAsync
мы извлекаем одну пачку (примерно 20 записей) и помещаем в объект ContactBatch:
ContactBatch contactBatch = await reader.ReadBatchAsync();
Под конец запускаем цикл с предусловием, где проверяем, чтобы contactBatch содержала хотя бы один контакт, в таком случае запускаем внутренний цикл, задача которого — пройтись по контактам, находящимся в contactBatch, а уже из них берем идентификатор, отображаемое имя, формируем из них строку и помещаем в объект класса StringBuilder. После завершения внутреннего цикла берем новую порцию записей. И так по кругу, пока не закончатся контакты и нечего будет выводить. В конце, когда будет завершен внешний цикл, содержимое объекта класса StringBuilder присваиваем объекту — текстовой надписи TextBlock.
Wi-Fi
Перейдем к работе с сетями, а именно с Wi-Fi. Воспользуемся средствами Windows 10 Mobile, чтобы найти все Wi-Fi-сети в округе. Можешь создать новую страницу или приложения. Я пошел по второму пути. На страницу помести кнопку и текстовое поле TextBlock для вывода информации — сведений о найденных сетях. Создай обработчик нажатия кнопки и переходи в редактор кода.
В манифесте объяви возможность взаимодействия с Wi-Fi:
<DeviceCapability Name="wiFiControl" />
Событие должно быть асинхронным. Первое, что надо сделать, — запросить разрешение на взаимодействие с Wi-Fi-устройством, чтобы проверить его наличие и доступность. Данное действие выполняется с помощью статического метода RequestAccessAsync
класса WiFiAdapter. Он ничего не получает, а возвращает член перечисления WiFiAccessStatus, означающий доступность или отсутствие устройства Wi-Fi:
var access = await WiFiAdapter.RequestAccessAsync();
Если результат успешен и устройство доступно, пытаемся найти все Wi-Fi-адаптеры:
var result = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
Если найдено несколько адаптеров, получаем первый:
WiFiAdapter firstAdapter = await WiFiAdapter.FromIdAsync(result[0].Id);
Класс WiFiAdapter служит для перечисления, работы с Wi-Fi-адаптерами, а также для сканирования диапазона. Для выполнения последнего вызывается асинхронный метод ScanAsync
адаптера:
await firstAdapter.ScanAsync();
Результат — найденные сети помещаются во внутренний объект NetworkReport. Чтобы получить сведения о найденных сетях, надо обработать каждый объект WiFiAvailableNetwork, содержащийся в доступной только для чтения коллекции AvailableNetworks объекта NetworkReport:
foreach (var network in firstAdapter.NetworkReport.AvailableNetworks) {
build.AppendLine(network.Ssid);
build.AppendLine(network.Bssid);
build.AppendLine(network.NetworkKind.ToString());
build.AppendLine(network.SecuritySettings.NetworkAuthenticationType.ToString());
build.AppendLine("");
}
Таким образом, после нажатия кнопки Scan на экране в список будут выведены сведения обо всех найденных сетях: имя сети, MAC-адрес, тип сети и тип аутентификации. Объект класса WiFiAvailableNetwork содержит более широкий список параметров Wi-Fi-сети, чем указано в примере.
Итоги
В статье мы рассмотрели четыре рецепта программирования Windows 10 Mobile:
- Определение содержимого каталогов.
- Отправка СМС.
- Анализ контактов из телефонной книги.
- Сканирование и обнаружение Wi-Fi-сетей.
Вместе с этим мы разработали три весьма простых приложения, которые на практике реализуют теоретический материал. Они небольшие настолько, чтобы их описание уместилось в рамки статьи, но при этом они выполняют поставленную перед ними демонстрационную задачу.
Узнать содержимое каталогов — это общая задача для всех аппаратных платформ Windows 10; с другой стороны, отправка СМС и анализ контактов возможны только на смартфоне; сканирование Wi-Fi-сетей может быть произведено или на смартфоне, или на ноутбуке, оснащенном Wi-Fi-адаптером. Есть множество других задач, запросто решаемых с помощью UWP, например: печать, сканирование, широкий набор элементов для построения пользовательского интерфейса, календари, графика, игры, голосовое управление с помощью Cortana.
Немного актуальной истории
Год назад я написал для ][ статью. В ней рассматриваются вопросы использования камеры, карт и геолокации на UWP и Windows 10. Все сведения актуальны и по сей день, поскольку Windows 10 с нами надолго.