Проверяет, была ли включена запрошенная функциональность в мобильном приложении или мобильном клиенте во время сборки. В мобильном клиенте также сопоставляет с информацией на сервере. Если на сервере данная функциональность выключена, то и на клиенте метод вернет значение Ложь.
Синтаксис
ПоддерживаетсяФункциональностьМобильногоПриложения(<ЗапрашиваемаяФункциональностьМобильногоПриложения>)Полное описание, параметры и ограничения
Глобальный контекст.ПоддерживаетсяФункциональностьМобильногоПриложения (Global context.MobileApplicationFunctionalitySupported)
Глобальный контекст (Global context)
ПоддерживаетсяФункциональностьМобильногоПриложения (MobileApplicationFunctionalitySupported)
Доступен, начиная с версии 8.3.18.
Синтаксис:
ПоддерживаетсяФункциональностьМобильногоПриложения(<ЗапрашиваемаяФункциональностьМобильногоПриложения>)
Параметры:
<ЗапрашиваемаяФункциональностьМобильногоПриложения> (обязательный)
Тип: ФункциональностьМобильногоПриложения.
Функциональность мобильного приложения, поддержка которой проверяется.
Возвращаемое значение:
Тип: Булево.
И на клиенте, и на сервере возвращает одно и то же (и при разных конфигурациях) значение, объединяя установки на клиенте и сервере "по И".
Для толстого, тонкого и веб-клиентов всегда возвращает Ложь.
Описание:
Проверяет, была ли включена запрошенная функциональность в мобильном приложении или мобильном клиенте во время сборки.
В мобильном клиенте также сопоставляет с информацией на сервере. Если на сервере данная функциональность выключена, то и на клиенте метод вернет значение Ложь.
Доступность:
Тонкий клиент, веб-клиент, мобильный клиент, сервер, толстый клиент, мобильное приложение (клиент), мобильное приложение (сервер), мобильный автономный сервер.
Использование в версии:
Доступен, начиная с версии 8.3.18.
Методическая информация
Практические примеры
Свой локальный Android пакет750: настоящий callback формы и осознанная общая отмена
Передайте две собственные строки через native локальное уведомление, проверьте реальный четырехпараметрический callback своей формы и отдельно изучите широкую отмену отложенного опыта.
Где и как запускать
Своя новая Android ARM64 developer лаборатория/CF. MAIN клиентские процедуры: только МобильноеПриложениеКлиент>=8.3.18; own scalar identity/UTC helpers: только МобильноеПриложениеСервер. Настоящие form command и Description callbacks, без push/SDK/личных данных/реальных операций сейчас.
Подготовка
Это будущий ручной опыт только в новой полностью собственной Android ARM64 developer лаборатории/эмуляторе, с одной новой CF. Не личный телефон, рабочая ИБ, старое приложение или стенд с чужими уведомлениями. Лаборатория должна целиком принадлежать вам: ниже есть общая отмена, не фильтр одного nonce. Техническая приватность/изоляция здесь не обещана; сейчас устройство/1С/ОС не запускались.
Создайте пустую CF УчебныеУведомления85, версия = 3b362d96-3be0-49b5-84b8-9f69770d28fb, совместимость Не использовать. Общие → Общие формы: ровно одна управляемая Уведомления85 в рабочей области начальной страницы. Весь MAIN вставьте в модуль этой формы; событие ПриОткрытии свяжите с реальным ПриОткрытии(Отказ), не вызывайте вручную. Общий модуль/APP строковый handler не нужны.
Реквизиты формы: СогласенНаУведомление85 и СогласенНаОбщуюОтмену85 (Булево, исходно Ложь, два отдельных ручных флажка); ВерсияRuntime85/Состояние85 (Строка); ОтветПолучен85/ПоказаноОС85 (Булево); Сумма85 (Число15,2). Поля наблюдения ТолькоПросмотр=Да. Начальные false/0 не являются результатом callback — отдельно смотрите ОтветПолучен85.
Создайте четыре команды формы: ОтправитьСвойПакет85, ОтключитьСвойОбработчик85, ПодготовитьОбщуюОтмену85, ОтменитьОтложенные85. Для каждой назначьте одноименный настоящий обработчик с параметром Команда; добавьте собственные кнопки с соответствующим ИмяКоманды. ПолученСвойПакет85 — отдельная экспортная процедура с четырьмя параметрами, не действие кнопки и не вручную вызываемый пример.
У своей CF установите назначение Приложение для мобильной платформы. Проверьте конфигурацию для мобильного приложения клиент/сервер, целостность и неподдерживаемые функции: при ошибке STOP. Конфигурация → Мобильное приложение → Публиковать…: только свой новый виртуальный каталог OwnLocalNotification85_<ownGUID> и собственная новая папка своей Windows-VM/IIS. Это публикация конфигурации, не сборка APK; рабочую ИБ не подключайте.
Вручную подготовьте на собственном отдельном Android ARM64 стенде официальную developer mobile platform1cem-arm64.apk из своего фактически полученного дистрибутива, не client/standalone. В пустом списке developer приложений: + → Address = реальный URL своей private VM/IIS с точным именем публикации → Load → свое имя УчебныеУведомления85 → OK/Done → открыть новое приложение. Не localhost телефона/придуманный URL/публичный или рабочий сервер. Только собственная лабораторная загрузка, никаких push-сервисов/FCM/APNs/магазина/внешнего SDK. Здесь ничего не скачивается/устанавливается/публикуется.
Официальные инструкции будущей ручной подготовки: https://1c-dn.com/library/publishing_of_1c_mobile_application/ ; https://1c-dn.com/library/tutorials/1c_enterprise_development_tools_user_guide/ ; https://1c-dn.com/library/tutorials/first_application_mobile_platform/ . Их наличие не подтверждает вашу установку/runtime. Windows8.5.1.1343 не доказывает версию APK: реальная ВерсияRuntime85 должна быть не ниже8.3.18.
ПоддерживаетсяФункциональностьМобильногоПриложения проверяет включенную при сборке функциональность ЛокальныеУведомления, а не OS grant или гарантированную доставку. Ложь/неизвестный тип → STOP до подключения/Add; desktop/mobile-client fallback не предусмотрен. Реальные настройки/разрешения ОС на своем стенде проверяются вами вручную, без обхода ограничений.
Фаза A: прочитайте опыт, отдельно установите СогласенНаУведомление85 и нажмите ОтправитьСвойПакет85. Этот экземпляр потребляет согласие один раз. Создаются nonce и две собственные строки A:2×250=500, B:1×250=250. В JSON сохраняются полная версия, nonce, фаза, строки и итог750. Никакие существующие сообщения/получатели/идентификаторы подписчика не читаются.
ОписаниеОповещения содержит имя экспортной процедуры именно этой формы, native ЭтотОбъект формы и непустой собственный state. Родной ctor и readonly ДополнительныеПараметры явно добавляют state последним аргументом: три native поля уведомления плюс четвертый ownstate. Это не String→APP resolver и не CommonModule.ЭтотОбъект. Состояние ожидания установлено ДО Attach/Add; код после Add не перезаписывает пришедший немедленно callback.
Дождитесь только настоящего callback. Он сначала требует Локальное=Истина и фактический Bool Показано, затем проверяет все поля четвертого параметра/consumed consent, только native Текст/Данные, целый JSON и повторный расчет750. Точный класс первого native объекта не объявлен: нет fake cast; его Заголовок/Дата/Получатели/Наклейка после доставки не читаются, поскольку native обещает заполненными лишь Текст/Данные. ПоказаноОС85 — фактический флаг callback, не чтение текущего центра ОС.
После ОтветПолучен85=Истина и Сумма85=750 нажмите ОтключитьСвойОбработчик85. Native Detach получает сохраненный ТОТ ЖЕ Description явным аргументом. Параметр optional в native, но здесь не опускается: не отключаем неизвестные handlers. Никакая автоматическая отмена/уборка не выполняется.
Фаза B необязательна для ручного запуска: только на полностью собственной НОВОЙ лаборатории без чужих pending уведомлений. Прочитайте широкую область действия и отдельно установите СогласенНаОбщуюОтмену85. ПодготовитьОбщуюОтмену85 потребляет второе согласие и создает новый nonce/отложенный опыт. Настоящий сервер собственной CF возвращает scalar UTC дату, на клиенте к ней прибавляются180 секунд; ИнтервалПовтора=0. Это wall-clock, не точный таймер и не monotonic clock.
Нажмите отдельную ОтменитьОтложенные85 до прихода delayed callback. Native cancel0 отменяет все отложенные локальные уведомления, которые еще не выведены: нет nonce/ID/фильтра. У процедуры нет результата Bool/ID или queue-readback. Наблюдение лишь «запрос общей отмены без синхронного исключения», не «очередь пуста»/«данное уведомление точно отменено». Отсутствие callback не доказательство. В конце отдельной кнопкой отключите свой Description; это также не проверяет очередь.
Если delayed callback пришел раньше общей отмены, его реальные данные/четвертый state проверяются, затем опыт STOP: не переименовывайте уже доставленное уведомление в успешно отмененное. Любая ошибка/unknown/повтор/чужой nonce/changed version дает sticky STOP этого экземпляра без retry, rollback, cancel-cleanup или восстановления prior state. Повтор возможен лишь новой явной ручной подготовкой собственной лаборатории/формы; блокировка не обещана сохраняющейся между экземплярами. Здесь нет доказанного Android GUI/runtime успеха.
&НаКлиенте
Перем Остановка85, Этап85, ВерсияЗапуска85, НомерОпыта85, ВидОпыта85;
&НаКлиенте
Перем СогласиеИспользовано85, СогласиеОтменыИспользовано85, Пакет85, Описание85;
&НаКлиенте
Процедура ПриОткрытии(Отказ)
Если СогласиеИспользовано85 <> Неопределено Тогда
Отказ = Истина;
Возврат;
КонецЕсли;
Остановка85 = Истина;
СогласиеИспользовано85 = Ложь;
СогласиеОтменыИспользовано85 = Ложь;
СогласенНаУведомление85 = Ложь;
СогласенНаОбщуюОтмену85 = Ложь;
ОтветПолучен85 = Ложь;
ПоказаноОС85 = Ложь;
Сумма85 = 0;
Попытка
ВерсияЗапуска85 = ПроверитьСреду85();
ВерсияRuntime85 = ВерсияЗапуска85;
Этап85 = "Готов";
Остановка85 = Ложь;
Состояние85 = "Прочитайте опыт. Согласие на одно свое уведомление пока не дано";
Исключение
Остановить85(ОписаниеОшибки());
Отказ = Истина;
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Процедура Остановить85(Причина)
Остановка85 = Истина;
Этап85 = "STOP";
СогласенНаУведомление85 = Ложь;
СогласенНаОбщуюОтмену85 = Ложь;
Состояние85 = "STOP этого экземпляра. Без повторов и автоматической уборки. " + Причина;
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьКоманду85(Команда, Имя, НужныйЭтап)
Если Остановка85 <> Ложь Или Этап85 <> НужныйЭтап
Или ТипЗнч(Команда) <> Тип("КомандаФормы") Или Команда <> Команды.Найти(Имя)
Или ВерсияЗапуска85 <> ПроверитьСреду85() Тогда
ВызватьИсключение "Не совпала своя команда, фаза или мобильная среда";
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ОтправитьСвойПакет85(Команда)
Попытка
ПроверитьКоманду85(Команда, "ОтправитьСвойПакет85", "Готов");
Если СогласиеИспользовано85 <> Ложь Или СогласенНаУведомление85 <> Истина Тогда
ВызватьИсключение "Нужно отдельное согласие на собственный учебный пакет";
КонецЕсли;
СогласиеИспользовано85 = Истина;
СогласенНаУведомление85 = Ложь;
НачатьСвойОпыт85("СЕЙЧАС", Неопределено);
Исключение
Остановить85(ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Процедура ПодготовитьОбщуюОтмену85(Команда)
Попытка
ПроверитьКоманду85(Команда, "ПодготовитьОбщуюОтмену85", "ГотовКОтмене");
Если СогласиеОтменыИспользовано85 <> Ложь Или СогласенНаОбщуюОтмену85 <> Истина Тогда
ВызватьИсключение "Нужно новое отдельное согласие: отмена всех отложенных уведомлений своей новой лаборатории";
КонецЕсли;
СогласиеОтменыИспользовано85 = Истина;
СогласенНаОбщуюОтмену85 = Ложь;
UTC = ПолучитьСвоюUTC85();
Если ТипЗнч(UTC) <> Тип("Дата") Тогда
ВызватьИсключение "Свой сервер не вернул фактическую UTC дату";
КонецЕсли;
НачатьСвойОпыт85("ОТЛОЖЕННО", UTC + 180);
Исключение
Остановить85(ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Процедура НачатьСвойОпыт85(Вид, UTCСрабатывания)
Если Остановка85 <> Ложь Тогда
ВызватьИсключение "Остановленный экземпляр не начинает новый опыт";
КонецЕсли;
ВидОпыта85 = Вид;
НомерОпыта85 = Строка(Новый УникальныйИдентификатор);
Если СтрДлина(НомерОпыта85) <> 36 Или НомерОпыта85 = "00000000-0000-0000-0000-000000000000" Тогда
ВызватьИсключение "Нет собственного ненулевого номера опыта";
КонецЕсли;
Строки = Новый Массив;
Строки.Добавить(Новый Структура("Item,Qty,Price", "A", 2, 250));
Строки.Добавить(Новый Структура("Item,Qty,Price", "B", 1, 250));
Источник = Новый Структура("Version,Nonce,Phase,Rows,Total",
ВерсияЗапуска85, НомерОпыта85, ВидОпыта85, Строки, 750);
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, Источник);
Пакет85 = Запись.Закрыть();
Если ПроверитьПакет85(Пакет85) <> 750 Тогда
ВызватьИсключение "Свой исходный пакет не дал 750";
КонецЕсли;
СвоеСостояние = Новый Структура("Nonce,Phase,Version,SourcePacket,Consent",
НомерОпыта85, ВидОпыта85, ВерсияЗапуска85, Пакет85, Истина);
Описание85 = Новый ОписаниеОповещения("ПолученСвойПакет85", ЭтотОбъект, СвоеСостояние);
ПроверитьОписание85();
Уведомление = Новый ДоставляемоеУведомление;
Уведомление.Заголовок = "Учебный пакет 750";
Уведомление.Текст = "Свой расчет 750. Опыт " + НомерОпыта85;
Уведомление.Данные = Пакет85;
Уведомление.ИнтервалПовтора = 0;
Если ВидОпыта85 = "ОТЛОЖЕННО" Тогда
Если ТипЗнч(UTCСрабатывания) <> Тип("Дата") Или UTCСрабатывания <= ПолучитьСвоюUTC85() Тогда
ВызватьИсключение "Нет собственной будущей UTC даты";
КонецЕсли;
Если Остановка85 <> Ложь Тогда
ВызватьИсключение "Не продолжаем после неизвестного исхода во время проверки UTC";
КонецЕсли;
Уведомление.ДатаПоявленияУниверсальноеВремя = UTCСрабатывания;
Этап85 = "ЖдемОтмены";
Состояние85 = "Создается свой отложенный опыт. Затем нажмите отдельную общую отмену";
ИначеЕсли ВидОпыта85 = "СЕЙЧАС" И UTCСрабатывания = Неопределено Тогда
Этап85 = "Ждем";
Состояние85 = "Ожидается настоящий callback; отсутствие ответа не означает успех";
Иначе
ВызватьИсключение "Неизвестный собственный вид опыта";
КонецЕсли;
ОтветПолучен85 = Ложь;
Менеджер = ПолучитьМенеджер85();
Менеджер.ПодключитьОбработчикУведомлений(Описание85);
Если Остановка85 <> Ложь Или (Этап85 <> "Ждем" И Этап85 <> "ЖдемОтмены") Тогда
ВызватьИсключение "Во время подключения возник неизвестный исход";
КонецЕсли;
Менеджер.ДобавитьЛокальноеУведомление(Уведомление);
КонецПроцедуры
&НаКлиенте
Процедура ПолученСвойПакет85(Уведомление, Локальное, Показано, СостояниеОпыта85) Экспорт
Попытка
Если Остановка85 <> Ложь Или (Этап85 <> "Ждем" И Этап85 <> "ЖдемОтмены")
Или ВерсияЗапуска85 <> ПроверитьСреду85()
Или ТипЗнч(Локальное) <> Тип("Булево") Или Локальное <> Истина
Или ТипЗнч(Показано) <> Тип("Булево") Тогда
ВызватьИсключение "Не свой локальный callback, фаза или тип native флага; чужие данные не читаются";
КонецЕсли;
ПроверитьСостояние85(СостояниеОпыта85);
Текст = Уведомление.Текст;
Данные = Уведомление.Данные;
Если ТипЗнч(Текст) <> Тип("Строка") Или Текст <> "Свой расчет 750. Опыт " + НомерОпыта85
Или ТипЗнч(Данные) <> Тип("Строка") Или Данные <> СостояниеОпыта85.SourcePacket Тогда
ВызватьИсключение "Native Text/Data не совпали с целым своим пакетом";
КонецЕсли;
Сумма85 = ПроверитьПакет85(Данные);
ОтветПолучен85 = Истина;
ПоказаноОС85 = Показано;
Если ВидОпыта85 <> "СЕЙЧАС" Тогда
ВызватьИсключение "Отложенный callback уже пришел. Не объявляйте последующую отмену успешной";
КонецЕсли;
Этап85 = "Доставлено";
Состояние85 = "Настоящий свой callback: сумма 750. Флаг ОС показан отдельно. Отключите свой handler отдельной кнопкой";
Исключение
Остановить85(ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Процедура ОтменитьОтложенные85(Команда)
Попытка
ПроверитьКоманду85(Команда, "ОтменитьОтложенные85", "ЖдемОтмены");
ПроверитьОписание85();
Если ВидОпыта85 <> "ОТЛОЖЕННО" Или СогласиеОтменыИспользовано85 <> Истина Тогда
ВызватьИсключение "Нет своего отложенного опыта с использованным отдельным согласием";
КонецЕсли;
Менеджер = ПолучитьМенеджер85();
Менеджер.ОтменитьЛокальныеУведомления();
Если Остановка85 <> Ложь Или Этап85 <> "ЖдемОтмены" Тогда
ВызватьИсключение "Не перезаписываем исход callback во время запроса отмены";
КонецЕсли;
Этап85 = "ОтменаЗапрошена";
Состояние85 = "Запрос ОБЩЕЙ отмены выполнен без синхронного исключения. Пустая очередь и точная отмена не доказаны";
Исключение
Остановить85(ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Процедура ОтключитьСвойОбработчик85(Команда)
Попытка
Если Этап85 <> "Доставлено" И Этап85 <> "ОтменаЗапрошена" Тогда
ВызватьИсключение "Нет законченной ручной фазы для отключения своего handler";
КонецЕсли;
ПроверитьКоманду85(Команда, "ОтключитьСвойОбработчик85", Этап85);
ПроверитьОписание85();
Менеджер = ПолучитьМенеджер85();
Менеджер.ОтключитьОбработчикУведомлений(Описание85);
Если Остановка85 <> Ложь Или (Этап85 <> "Доставлено" И Этап85 <> "ОтменаЗапрошена") Тогда
ВызватьИсключение "Не перезаписываем неизвестный исход во время отключения";
КонецЕсли;
Если ВидОпыта85 = "СЕЙЧАС" Тогда
Этап85 = "ГотовКОтмене";
Иначе
Этап85 = "Завершено";
КонецЕсли;
Состояние85 = "Явно отключен свой сохраненный Description. Это не очистка уведомлений и не подтверждение очереди";
Исключение
Остановить85(ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьОписание85()
Если ТипЗнч(Описание85) <> Тип("ОписаниеОповещения")
Или Описание85.ИмяПроцедуры <> "ПолученСвойПакет85" Или Описание85.Модуль <> ЭтотОбъект Тогда
ВызватьИсключение "Подменен native Description или контекст собственной формы";
КонецЕсли;
ПроверитьСостояние85(Описание85.ДополнительныеПараметры);
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьСостояние85(Состояние)
Если ТипЗнч(Состояние) <> Тип("Структура") Или Состояние.Количество() <> 5
Или Не Состояние.Свойство("Nonce") Или Не Состояние.Свойство("Phase")
Или Не Состояние.Свойство("Version") Или Не Состояние.Свойство("SourcePacket")
Или Не Состояние.Свойство("Consent") Тогда
ВызватьИсключение "Не совпали поля собственного четвертого параметра";
КонецЕсли;
Если ТипЗнч(Состояние.Nonce) <> Тип("Строка") Или Состояние.Nonce <> НомерОпыта85
Или ТипЗнч(Состояние.Phase) <> Тип("Строка") Или Состояние.Phase <> ВидОпыта85
Или ТипЗнч(Состояние.Version) <> Тип("Строка") Или Состояние.Version <> ВерсияЗапуска85
Или ТипЗнч(Состояние.SourcePacket) <> Тип("Строка") Или Состояние.SourcePacket <> Пакет85
Или ТипЗнч(Состояние.Consent) <> Тип("Булево") Или Состояние.Consent <> Истина
Или (ВидОпыта85 = "СЕЙЧАС" И СогласиеИспользовано85 <> Истина)
Или (ВидОпыта85 = "ОТЛОЖЕННО" И СогласиеОтменыИспользовано85 <> Истина)
Или ПроверитьПакет85(Состояние.SourcePacket) <> 750 Тогда
ВызватьИсключение "Не совпал own nonce, фаза, версия, полный пакет или использованное согласие";
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Функция ПроверитьПакет85(JSON)
Если ТипЗнч(JSON) <> Тип("Строка") Или JSON = "" Тогда
ВызватьИсключение "Нет целого JSON пакета";
КонецЕсли;
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(JSON);
Попытка
Пакет = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
Исключение
Чтение.Закрыть();
ВызватьИсключение;
КонецПопытки;
Если ТипЗнч(Пакет) <> Тип("Структура") Или Пакет.Количество() <> 5
Или Не Пакет.Свойство("Version") Или Не Пакет.Свойство("Nonce")
Или Не Пакет.Свойство("Phase") Или Не Пакет.Свойство("Rows") Или Не Пакет.Свойство("Total") Тогда
ВызватьИсключение "Не совпали все поля пакета";
КонецЕсли;
Если ТипЗнч(Пакет.Version) <> Тип("Строка") Или Пакет.Version <> ВерсияЗапуска85
Или ТипЗнч(Пакет.Nonce) <> Тип("Строка") Или Пакет.Nonce <> НомерОпыта85
Или ТипЗнч(Пакет.Phase) <> Тип("Строка") Или Пакет.Phase <> ВидОпыта85
Или ТипЗнч(Пакет.Rows) <> Тип("Массив") Или Пакет.Rows.Количество() <> 2
Или ТипЗнч(Пакет.Total) <> Тип("Число") Или Пакет.Total <> 750 Тогда
ВызватьИсключение "Не свой пакет, типы или количество строк";
КонецЕсли;
Итог = 0;
Для Индекс = 0 По 1 Цикл
СтрокаПакета = Пакет.Rows[Индекс];
Если ТипЗнч(СтрокаПакета) <> Тип("Структура") Или СтрокаПакета.Количество() <> 3
Или Не СтрокаПакета.Свойство("Item") Или Не СтрокаПакета.Свойство("Qty")
Или Не СтрокаПакета.Свойство("Price") Тогда
ВызватьИсключение "Не совпали все поля строки";
КонецЕсли;
Если ТипЗнч(СтрокаПакета.Item) <> Тип("Строка") Или ТипЗнч(СтрокаПакета.Qty) <> Тип("Число")
Или ТипЗнч(СтрокаПакета.Price) <> Тип("Число") Или СтрокаПакета.Price <> 250
Или (Индекс = 0 И (СтрокаПакета.Item <> "A" Или СтрокаПакета.Qty <> 2))
Или (Индекс = 1 И (СтрокаПакета.Item <> "B" Или СтрокаПакета.Qty <> 1)) Тогда
ВызватьИсключение "Подменены собственные две строки, числовые типы, порядок или цены";
КонецЕсли;
Итог = Итог + СтрокаПакета.Qty * СтрокаПакета.Price;
КонецЦикла;
Если Итог <> Пакет.Total Тогда
ВызватьИсключение "500 + 250 не совпало с заявленным итогом";
КонецЕсли;
Возврат Итог;
КонецФункции
&НаКлиенте
Функция ПолучитьМенеджер85()
Менеджер = ДоставляемыеУведомления;
Если ТипЗнч(Менеджер) <> Тип("МенеджерДоставляемыхУведомлений") Тогда
ВызватьИсключение "Не получен настоящий global менеджер";
КонецЕсли;
Возврат Менеджер;
КонецФункции
&НаКлиенте
Функция ПроверитьСреду85()
#Если МобильноеПриложениеКлиент Тогда
Информация = Новый СистемнаяИнформация;
Версия = Информация.ВерсияПриложения;
Если ПодтвердитьСвойПакет85() <> Истина Тогда
ВызватьИсключение "Нужно собственное мобильное приложение не ниже 8.3.18";
КонецЕсли;
Поддержка = ПоддерживаетсяФункциональностьМобильногоПриложения(ФункциональностьМобильногоПриложения.ЛокальныеУведомления);
Если ТипЗнч(Поддержка) <> Тип("Булево") Или Поддержка <> Истина Тогда
ВызватьИсключение "Функциональность не включена. Это проверка сборки, не разрешение ОС";
КонецЕсли;
Возврат Версия;
#Иначе
ВызватьИсключение "Только свое мобильное приложение; desktop/mobile-client fallback запрещен";
#КонецЕсли
КонецФункции
&НаСервереБезКонтекста
Функция ПодтвердитьСвойПакет85()
#Если МобильноеПриложениеСервер Тогда
Если Метаданные.ОбщиеФормы.Количество() <> 1 Или Метаданные.ОбщиеФормы.Найти("Уведомления85") = Неопределено Тогда
ВызватьИсключение "Не совпала своя новая мобильная CF и единственная форма";
КонецЕсли;
Возврат Истина;
#Иначе
ВызватьИсключение "Нужен сервер своего мобильного приложения";
#КонецЕсли
КонецФункции
&НаСервереБезКонтекста
Функция ПолучитьСвоюUTC85()
#Если МобильноеПриложениеСервер Тогда
Если ПодтвердитьСвойПакет85() <> Истина Тогда
ВызватьИсключение "Нет своего сервера";
КонецЕсли;
UTC = ТекущаяУниверсальнаяДата();
Если ТипЗнч(UTC) <> Тип("Дата") Тогда
ВызватьИсключение "Нет native UTC даты";
КонецЕсли;
Возврат UTC;
#Иначе
ВызватьИсключение "UTC берется на сервере, не прямым mobile-client вызовом";
#КонецЕсли
КонецФункции
Разбор по шагам
Подготовьте свою новую CF/единственную форму/четыре реальные команды и actual Android developer runtime. Открытие только проверяет свою среду.
Дайте первое отдельное согласие и запустите фазу A. Native global manager получает настоящий DTO, собственный Description и пакет A500+B250=750.
Настоящий callback проверяет свои четвертые параметры, local/shown Bool и целый native Text/Data; после повторного расчета наблюдайте ОтветПолучен85,750 и actual ПоказаноОС85.
Реальной кнопкой явно отключите тот же свой Description, без optional omission/массового отключения.
Только в новой полностью своей лаборатории дайте второе отдельное согласие. Native серверная UTC дата+180 готовит delayed опыт с новым nonce и interval0.
Отдельная ручная cancel0 запрашивает широкую отмену без success/ID/empty-queue обещания. После этого отдельной кнопкой отключите свой handler; early delayed callback дает STOP.
Как проверить результат
Все6 PRIMARY входят в реальный единый producer→consumer: readonly global manager, native DTO/Add, реальный formDescription callback4, явный savedDescription Detach и отдельный delayed cancel0.
Четвертый callback formal документирован constructor/AdditionalParameters и целиком потребляется по полям nonce/phase/version/sourcepayload/consent; первые3 native аргумента также потребляются.
Source пакет и принятый packet проверяются полностью: exact fields/types/2rows/order/A2×250/B1×250/total750. Unknown/foreign данные не становятся успешным результатом.
Оба согласия отдельные, исходноFalse и потребляются только настоящими командами; waiting установлен до Attach/Add, немедленный callback не затирается.
UTC native call находится только в actual mobile-server helper собственной формы, возвращает safe scalar Дата; native void procedures не присваиваются выдуманному результату.
Нет автоматических повторов/cleanup/ручных callbacks, caller-owned ИсходныйОбъект, push-ID или dummy coverage вызовов; runtimeVerified остаетсяFalse.
Ожидаемый результат
Будущая ручная проверка: actual callback дает750 и собственный факт BoolПоказано. Вторая фаза показывает только запрос общей отмены без синхронного исключения. Настоящие Android OS/UI/доставка здесь не проверены.
На что обратить внимание
Attach rawHTML ошибочно помещает ОписаниеОповещения/Строка под «Значение по умолчанию». Description route подтвержден отдельным native ctor/AdditionalParameters/Module и реальным конфигурационным caller; source ошибку не переписываем.
CommonModule.ЭтотОбъект имеет иной native список доступности; не переносим его на mobile APPclient. Здесь используется только прямо документированный native ЭтотОбъект собственной managedформы.
Точный класс callback Уведомление native не указан; onlyText/Data не означает сохранение остальных полей DTO. Actual LocalFalse не читается как свой payload.
Cancel0 широкая, не адресная и не гарантированный rollback. Возвращаемого Bool/ID, чтения очереди и доказательства по отсутствию callback нет.
Мин8.3.18 относится capability guard; фактический APK/runtime отдельно от установленной Windows платформы. OS grant/точный alarm/изоляция/атомарность не обещаны.
STOP относится текущему экземпляру формы. Исключение после Attach/Add/Cancel оставляет неизвестный исход без автоматической уборки; реальный новый опыт требует новой явной ручной подготовки.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.
Каталог мобильной библиотеки через Ждать: свой PNG без подмены обещания
Получите путь из настоящего обещания и продолжите свой однократный файловый опыт только после фактического результата и повторной проверки состояния.
Где и как запускать
Отдельное собственное новое Android ARM64 developer mobile-platform приложение. Только МобильноеПриложениеКлиент; свой локальный МобильноеПриложениеСервер используется для scalar guard. Минимум8.3.18, фактическая версия отдельно.
Подготовка
Урок описывает будущий ручной опыт только на своем отдельном новом Android ARM64 стенде, не личном устройстве и не рабочем приложении. Сейчас 1С, устройство, сеть и файлы не запускались. Просмотр карточки ничего не создает.
Создайте свою новую пустую конфигурацию УчебнаяМедиатека85: Версия = d25140f9-aa1c-4422-a508-c4ad87c10457, режим совместимости Не использовать, назначение использования Приложение для мобильной платформы. Это учебная метка пакета, не криптографическое доказательство и не идентификатор ИБ/устройства.
Общие → Общие формы: создайте ровно четыре управляемые формы ГлавнаяМедиатеки85, ПультМедиатекиОбычный85, ПультМедиатекиАсинх85, ПультМедиатекиОповещения85. В главной форме создайте команды ОткрытьОбычный85, ОткрытьАсинх85, ОткрытьОповещения85 с одноименными обработчиками и кнопками. Вставьте поддерживающий полный модуль главной формы и добавьте эту форму в рабочую область начальной страницы. Она открывает пульты, но не начинает запись.
В каждом пульте создайте реквизиты СогласенНаОпыт85 (Булево, исходно Ложь), ВерсияRuntime85/Состояние85/СвойКаталог85/СвойФайл85 (Строка неограниченной длины), Размер85/Площадь85 (Число15,0). Выведите флажок и поля наблюдения; поля наблюдения ТолькоПросмотр=Да. Создайте команду ПройтиОпыт85, обработчик ПройтиОпыт85(Команда) и кнопку. Событие формы ПриОткрытии свяжите с ПриОткрытии(Отказ), не вызывайте события вручную.
В соответствующий пульт вставьте именно его полный MAIN модуль. У трех пультов отдельные экземпляры внутреннего состояния, nonce и согласия. Серверный helper внутри формы имеет &НаСервереБезКонтекста, выполняется только как МобильноеПриложениеСервер и возвращает Булево; картинки, файлы, обещания и callback через RPC не передаются.
В свойствах новой конфигурации Используемая функциональность мобильного приложения укажите нужные библиотеки картинок и видео. Это заявленная потребность, а не выдача разрешения ОС. Фактическая функциональность/разрешения зависят от полученного мобильного бинарного пакета и ОС. Getter metadata этого свойства всегда Неопределено, его не используем для проверки или включения.
Вручную проверьте свою конфигурацию для Мобильное приложение — клиент и Мобильное приложение — сервер, целостность и неподдерживаемые функции. Затем Конфигурация → Мобильное приложение → Публиковать… только в собственный новый виртуальный каталог OwnMedia85_<ownGUID> и собственную новую папку своей VM/IIS. Публикация создает мобильную конфигурацию, не APK; автоматическое обновление и чужая/рабочая ИБ не нужны. При любой ошибке остановитесь.
Повторите уже документированный путь developer-платформы: только официальная 1cem-arm64.apk из своего фактически полученного мобильного дистрибутива, не 1cem-client-arm64.apk и не 1cem-standalone-arm64.apk. Здесь нет скачивания, установки, сборки, SDK или настройки разрешений. Windows8.5.1.1343 не доказывает версию мобильного APK. Не обещаем наличие подходящего бинарного пакета; неподходящая фактическая сборка — STOP.
После самостоятельной установки developer-платформы на своем стенде: + → Address = реальный URL своей private VM/IIS с точным именем своей публикации → Load → имя УчебнаяМедиатека85 → OK/Done → открыть свое приложение из списка. Не localhost телефона, не file://, не придуманный URL. Своя локальная лабораторная сеть и новая пустая мобильная ИБ; никаких производственных данных.
Официальный маршрут публикации: https://1c-dn.com/library/publishing_of_1c_mobile_application/ ; developer ARM64 и импорт приложения: https://1c-dn.com/library/tutorials/1c_enterprise_development_tools_user_guide/ ; первая собственная мобильная ИБ: https://1c-dn.com/library/tutorials/first_application_mobile_platform/ . Это подготовка для будущего ручного опыта, а не результат runtime-проверки.
Откройте один пульт: поле ВерсияRuntime85 показывает фактическую мобильную версию. Нужен минимум8.3.18 и действительная функциональность БиблиотекиКартинокИВидео=True. Это только проверка включения в сборку, не probe разрешения ОС. False, ошибка, другой контекст или версия — STOP; без desktop fallback и без AllFilesAccess/пересборки как обхода.
Смысл библиотечного пути — потенциально общая библиотека картинок. Собственный GUID-подкаталог не делает ее приватным sandbox. Урок принимает только фактический обычный абсолютный локальный Android filesystem path с доступным существующим родителем. URI, относительный/корневой/неподдерживаемый путь или отказ ОС — STOP; URI не преобразуется и разрешения не расширяются.
Прочитайте последствия и отдельно поставьте флажок выбранного пульта. Одно нажатие потребляет его согласие: запрос библиотечного каталога может взаимодействовать с ОС; далее максимум один собственный новый GUID-каталог и один собственный новый PNG. Другие пульты требуют отдельного согласия. Нет операций при открытии формы, повторной попытки или автоматического запуска.
Собственный PNG — 1x1 RGB(21,98,232), ровно69 байт, не секрет и не чужая фотография; в карточке дан его фиксированный Base64. Сначала actual Exists=True родителя, затем IsDirectory=True; свой дочерний каталог обязан отсутствовать до CreateDirectory и реально существовать после. Файл обязан отсутствовать непосредственно до Write, поскольку native Write способен перезаписывать.
После записи прочитайте только свой новый файл: actual Exists/IsFile, размер69, все байты через Новый ДвоичныеДанные(имя), полное сравнение двух native Base64Строка и actual Картинка ширина1/высота1. Base64Строка использует MIME строки64 с CR+LF; не сравнивайте reread с исходной неразбитой Base64-литералой.
Свой PNG и подкаталог остаются на стенде для ручного просмотра. Никакого удаления, sweeping библиотеки, retry, rollback или уборки. STOP означает неизвестный/частичный исход, а не отсутствие файла. Состояние одного экземпляра формы не обещано переживающим закрытие/перезапуск. Не закрывайте и не открывайте пульт ради обхода STOP; сначала самостоятельно выясните исход только своего опыта.
&НаКлиенте
Перем СогласиеИспользовано85, Остановка85, Неизвестно85, Этап85, Токен85;
&НаКлиенте
Процедура ПриОткрытии(Отказ)
Если СогласиеИспользовано85 <> Неопределено Или Остановка85 <> Неопределено Тогда
Отказ = Истина;
Возврат;
КонецЕсли;
СогласиеИспользовано85 = Ложь;
Остановка85 = Истина;
Неизвестно85 = Истина;
СогласенНаОпыт85 = Ложь;
СвойКаталог85 = "";
СвойФайл85 = "";
Размер85 = 0;
Площадь85 = 0;
Попытка
ВерсияRuntime85 = ПроверитьМобильнуюВерсию85();
Этап85 = "Готов";
Остановка85 = Ложь;
Неизвестно85 = Ложь;
Состояние85 = "Прочитайте последствия. Отдельно согласитесь на свой каталог и PNG";
Исключение
Остановить85(ОписаниеОшибки());
Отказ = Истина;
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Процедура Остановить85(Причина)
Остановка85 = Истина;
Неизвестно85 = Истина;
СогласенНаОпыт85 = Ложь;
Этап85 = "STOP";
Состояние85 = "STOP. Исход неизвестен; повтор и уборка запрещены. " + Причина;
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьФакт85(Значение, Ожидалось)
Если ТипЗнч(Значение) <> Тип("Булево") Или Значение <> Ожидалось Тогда
ВызватьИсключение "Фактическое состояние файловой системы не соответствует ожиданию";
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьЧисло85(Значение, Ожидалось)
Если ТипЗнч(Значение) <> Тип("Число") Или Значение <> Ожидалось Тогда
ВызватьИсключение "Фактический размер или измерение не соответствует собственному PNG";
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьВозможность85()
Поддерживается = ПоддерживаетсяФункциональностьМобильногоПриложения(
ФункциональностьМобильногоПриложения.БиблиотекиКартинокИВидео);
Если ТипЗнч(Поддерживается) <> Тип("Булево") Или Поддерживается <> Истина Тогда
ВызватьИсключение "В фактическую сборку не включена нужная мобильная функциональность";
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПодготовитьОпыт85(Команда)
#Если МобильноеПриложениеКлиент Тогда
Если ТипЗнч(Команда) <> Тип("КомандаФормы") Или Команда <> Команды.Найти("ПройтиОпыт85")
Или СогласиеИспользовано85 <> Ложь Или Остановка85 <> Ложь Или Неизвестно85 <> Ложь
Или Этап85 <> "Готов" Или СогласенНаОпыт85 <> Истина Тогда
ВызватьИсключение "Нужна своя команда и отдельное неиспользованное согласие; повтор запрещен";
КонецЕсли;
СогласиеИспользовано85 = Истина;
СогласенНаОпыт85 = Ложь;
Неизвестно85 = Истина;
Этап85 = "Получение";
Токен85 = Новый УникальныйИдентификатор;
ПроверитьСостояние85("Получение");
#Иначе
ВызватьИсключение "Этот файловый опыт запрещен в другой среде";
#КонецЕсли
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьСостояние85(ОжидаемыйЭтап)
#Если МобильноеПриложениеКлиент Тогда
Если Остановка85 <> Ложь Или СогласиеИспользовано85 <> Истина Или Неизвестно85 <> Истина
Или Этап85 <> ОжидаемыйЭтап Или ТипЗнч(Токен85) <> Тип("УникальныйИдентификатор")
Или ВерсияRuntime85 <> ПроверитьМобильнуюВерсию85() Тогда
ВызватьИсключение "Не совпадает собственный незавершенный опыт или мобильная версия";
КонецЕсли;
ПроверитьВозможность85();
#Иначе
ВызватьИсключение "Продолжение опыта запрещено в другой среде";
#КонецЕсли
КонецПроцедуры
&НаКлиенте
Функция ПроверитьЛокальныйПуть85(Каталог)
Если ТипЗнч(Каталог) <> Тип("Строка") Или СтрДлина(Каталог) <= 1 Тогда
ВызватьИсключение "Native getter не вернул непустую строку каталога";
КонецЕсли;
Если Лев(Каталог, 1) <> "/" Или СтрНайти(Каталог, ":") > 0
Или СтрНайти(Каталог, "\") > 0 Или СтрНайти(Каталог, "//") > 0
Или СтрНайти(Каталог, Символы.ПС) > 0 Или СтрНайти(Каталог, Символы.ВК) > 0 Тогда
ВызватьИсключение "Нужен обычный абсолютный Android filesystem path; URI не преобразуем";
КонецЕсли;
Части = СтрРазделить(Каталог, "/", Истина);
Для Каждого Часть Из Части Цикл
Если Часть = "." Или Часть = ".." Тогда
ВызватьИсключение "Путь с переходами между каталогами не принимается";
КонецЕсли;
КонецЦикла;
Если Прав(Каталог, 1) = "/" Тогда
Возврат Лев(Каталог, СтрДлина(Каталог) - 1);
КонецЕсли;
Возврат Каталог;
КонецФункции
&НаКлиенте
Процедура ПродолжитьОпыт85(Каталог, ОжидаемыйЭтап)
ПроверитьСостояние85(ОжидаемыйЭтап);
Этап85 = "Файлы";
Родитель = ПроверитьЛокальныйПуть85(Каталог);
Данные = Base64Значение("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGMQTXoBAAHvAWBSkKSJAAAAAElFTkSuQmCC");
Если ТипЗнч(Данные) <> Тип("ДвоичныеДанные") Тогда
ВызватьИсключение "Собственный PNG не декодирован в двоичные данные";
КонецЕсли;
ПроверитьЧисло85(Данные.Размер(), 69);
ОжидаемыеБайты = Base64Строка(Данные);
Если ТипЗнч(ОжидаемыеБайты) <> Тип("Строка") Или ОжидаемыеБайты = "" Тогда
ВызватьИсключение "Не получено полное native Base64 представление своего PNG";
КонецЕсли;
ФайлРодителя = Новый Файл(Родитель);
ПроверитьФакт85(ФайлРодителя.Существует(), Истина);
ПроверитьФакт85(ФайлРодителя.ЭтоКаталог(), Истина);
СвойКаталог = Родитель + "/OwnMedia85_" + Строка(Токен85);
СвойФайл = СвойКаталог + "/pixel_" + Строка(Токен85) + ".png";
ФайлКаталога = Новый Файл(СвойКаталог);
ПроверитьФакт85(ФайлКаталога.Существует(), Ложь);
ПроверитьСостояние85("Файлы");
СоздатьКаталог(СвойКаталог);
ФайлКаталога = Новый Файл(СвойКаталог);
ПроверитьФакт85(ФайлКаталога.Существует(), Истина);
ПроверитьФакт85(ФайлКаталога.ЭтоКаталог(), Истина);
ФайлКартинки = Новый Файл(СвойФайл);
ПроверитьФакт85(ФайлКартинки.Существует(), Ложь);
ПроверитьСостояние85("Файлы");
Данные.Записать(СвойФайл);
ФайлКартинки = Новый Файл(СвойФайл);
ПроверитьФакт85(ФайлКартинки.Существует(), Истина);
ПроверитьФакт85(ФайлКартинки.ЭтоФайл(), Истина);
ФактическийРазмер = ФайлКартинки.Размер();
ПроверитьЧисло85(ФактическийРазмер, 69);
Прочитано = Новый ДвоичныеДанные(СвойФайл);
Если ТипЗнч(Прочитано) <> Тип("ДвоичныеДанные") Тогда
ВызватьИсключение "Не прочитаны реальные двоичные данные собственного файла";
КонецЕсли;
ПроверитьЧисло85(Прочитано.Размер(), 69);
ФактическиеБайты = Base64Строка(Прочитано);
Если ТипЗнч(ФактическиеБайты) <> Тип("Строка") Или ФактическиеБайты <> ОжидаемыеБайты Тогда
ВызватьИсключение "Полное фактическое содержимое своего PNG отличается от producer";
КонецЕсли;
Картинка = Новый Картинка(Прочитано);
Ширина = Картинка.Ширина();
Высота = Картинка.Высота();
ПроверитьЧисло85(Ширина, 1);
ПроверитьЧисло85(Высота, 1);
ПроверитьСостояние85("Файлы");
СвойКаталог85 = СвойКаталог;
СвойФайл85 = СвойФайл;
Размер85 = ФактическийРазмер;
Площадь85 = Ширина * Высота;
Неизвестно85 = Ложь;
Этап85 = "Завершен";
Состояние85 = "Полные байты и картинка 1x1 проверены. Свой PNG оставлен без удаления";
КонецПроцедуры
&НаКлиенте
Функция ПроверитьМобильнуюВерсию85()
#Если МобильноеПриложениеКлиент Тогда
Информация = Новый СистемнаяИнформация;
Версия = Информация.ВерсияПриложения;
Если ПроверитьСвойПакетНаСервере85() <> Истина Тогда
ВызватьИсключение "Своя метка мобильного пакета не подтверждена";
КонецЕсли;
Возврат Версия;
#Иначе
ВызватьИсключение "Только собственное мобильное приложение, без desktop/mobile-client fallback";
#КонецЕсли
КонецФункции
&НаКлиенте
Асинх Процедура ПройтиОпыт85(Команда)
Попытка
ПодготовитьОпыт85(Команда);
ОбещаниеКаталога = КаталогБиблиотекиМобильногоУстройстваАсинх(
ТипКаталогаБиблиотекиМобильногоУстройства.Картинки);
Если ТипЗнч(ОбещаниеКаталога) <> Тип("Обещание") Тогда
ВызватьИсключение "Не получено настоящее native обещание каталога";
КонецЕсли;
Каталог = Ждать ОбещаниеКаталога;
ПродолжитьОпыт85(Каталог, "Получение");
Исключение
Остановить85(ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПроверитьСвойПакетНаСервере85()
#Если МобильноеПриложениеСервер Тогда
Если Метаданные.ОбщиеФормы.Количество() <> 4 Или Метаданные.ОбщиеФормы.Найти("ГлавнаяМедиатеки85") = Неопределено Или Метаданные.ОбщиеФормы.Найти("ПультМедиатекиОбычный85") = Неопределено Или Метаданные.ОбщиеФормы.Найти("ПультМедиатекиАсинх85") = Неопределено Или Метаданные.ОбщиеФормы.Найти("ПультМедиатекиОповещения85") = Неопределено Тогда
ВызватьИсключение "Не совпадает собственный новый учебный мобильный пакет";
КонецЕсли;
Возврат Истина;
#Иначе
ВызватьИсключение "Нужен локальный сервер своего мобильного приложения, не desktop ИБ";
#КонецЕсли
КонецФункции
Главная форма: три настоящие команды, ни одной операции с библиотекой при открытии
Своя CF → Общие → Общие формы → ГлавнаяМедиатеки85 → полный управляемый модуль
МобильноеПриложениеКлиент; серверный helper только МобильноеПриложениеСервер. Проверяется метка собственного пакета и фактическая версия, затем открывается выбранная своя форма. Directory/File/Write не вызываются.
&НаКлиенте
Процедура ОткрытьОбычный85(Команда)
Если ТипЗнч(Команда) <> Тип("КомандаФормы") Или Команда <> Команды.Найти("ОткрытьОбычный85") Тогда
ВызватьИсключение "Нужна собственная команда главной формы";
КонецЕсли;
ПроверитьМобильнуюВерсию85();
ОткрытьФорму("ОбщаяФорма.ПультМедиатекиОбычный85");
КонецПроцедуры
&НаКлиенте
Процедура ОткрытьАсинх85(Команда)
Если ТипЗнч(Команда) <> Тип("КомандаФормы") Или Команда <> Команды.Найти("ОткрытьАсинх85") Тогда
ВызватьИсключение "Нужна собственная команда главной формы";
КонецЕсли;
ПроверитьМобильнуюВерсию85();
ОткрытьФорму("ОбщаяФорма.ПультМедиатекиАсинх85");
КонецПроцедуры
&НаКлиенте
Процедура ОткрытьОповещения85(Команда)
Если ТипЗнч(Команда) <> Тип("КомандаФормы") Или Команда <> Команды.Найти("ОткрытьОповещения85") Тогда
ВызватьИсключение "Нужна собственная команда главной формы";
КонецЕсли;
ПроверитьМобильнуюВерсию85();
ОткрытьФорму("ОбщаяФорма.ПультМедиатекиОповещения85");
КонецПроцедуры
&НаКлиенте
Функция ПроверитьМобильнуюВерсию85()
#Если МобильноеПриложениеКлиент Тогда
Информация = Новый СистемнаяИнформация;
Версия = Информация.ВерсияПриложения;
Если ПроверитьСвойПакетНаСервере85() <> Истина Тогда
ВызватьИсключение "Своя метка мобильного пакета не подтверждена";
КонецЕсли;
Возврат Версия;
#Иначе
ВызватьИсключение "Только собственное мобильное приложение, без desktop/mobile-client fallback";
#КонецЕсли
КонецФункции
&НаСервереБезКонтекста
Функция ПроверитьСвойПакетНаСервере85()
#Если МобильноеПриложениеСервер Тогда
Если Метаданные.ОбщиеФормы.Количество() <> 4 Или Метаданные.ОбщиеФормы.Найти("ГлавнаяМедиатеки85") = Неопределено Или Метаданные.ОбщиеФормы.Найти("ПультМедиатекиОбычный85") = Неопределено Или Метаданные.ОбщиеФормы.Найти("ПультМедиатекиАсинх85") = Неопределено Или Метаданные.ОбщиеФормы.Найти("ПультМедиатекиОповещения85") = Неопределено Тогда
ВызватьИсключение "Не совпадает собственный новый учебный мобильный пакет";
КонецЕсли;
Возврат Истина;
#Иначе
ВызватьИсключение "Нужен локальный сервер своего мобильного приложения, не desktop ИБ";
#КонецЕсли
КонецФункции
Разбор по шагам
В отдельном пульте отдельно согласитесь на собственный каталог/PNG; согласие потребляется до native вызова.
Реальный КаталогБиблиотекиМобильногоУстройстваАсинх(Картинки) обязан вернуть Обещание, не строку и не поддельный объект.
Настоящий Ждать дает фактический каталог; прямое/конечное Неопределено, пустая строка, ошибка или уже STOP запрещают продолжение.
Повторная проверка после Ждать предшествует любому File/CreateDirectory/Write. Только поддерживаемый ordinary local path своего стенда принимается.
Создайте один свежий GUID-child/PNG с collision STOP. После записи прочитайте только свой69-byte файл и проверьте целое содержимое.
Native Картинка из reread должна дать числовые1x1; площадь1 и размер69 наблюдаются на форме. Никакого retry/delete.
Как проверить результат
Getter результата не подменяется Windows путем, пользовательским параметром, fake Promise или ручным callback.
До любого filesystem вызова есть отдельное consumed-согласие, метка своей CF, фактическая версия, native capability и допустимый actual path.
Exists проверяется перед IsDirectory/IsFile. CreateDirectory не считается успешным просто из-за отсутствия исключения.
Write вызывается один раз с одним своим именем после свежего Exists=False; реальный native writer может перезаписывать, поэтому коллизия всегда STOP.
Контрольное чтение получает реальные69 байт, сравнивает полное native Base64 с own producer и потребляет actual Width/Height как площадь1.
Ожидаемый результат
Ожидаемый ручной результат: Размер85=69, Площадь85=1; СвойКаталог85 и СвойФайл85 показывают только свой созданный GUID-child/PNG. Файл оставлен. Здесь runtime и доступ ОС не проверены.
На что обратить внимание
Библиотека потенциально общая, не sandbox. GUID-name не доказывает приватность, отсутствие симлинков/гонки или атомарность записи.
Built-feature=True не выдает разрешения ОС и не гарантирует ordinary writable path. Unsupported representation/URI/отказ ОС — STOP без обходов.
Проверка path — ограничение учебного опыта, не canonical resolver или security boundary. Нет claims приватного parent/canonical containment/race-safe exclusivity.
Неопределенные размеры картинки, пустой каталог и неожиданные типы не превращаем в значения по умолчанию.
Base64Строка native MIME64/CRLF: сравниваются две native строки целиком, не исходная неразбитая литерала.
Нет гарантированного canceled-Bool в данном callback. Неожиданный/пустой результат остановит опыт, но не называем его документированным флагом отмены.
Sticky STOP и consumed-согласие принадлежат текущему экземпляру формы; закрытие/перезапуск не обещаны сохраняющими их.
Файл/child не удаляются даже при ошибке; неизвестный исход не является доказанным отсутствием. Здесь нет actual device/SDK/network/filesystem операций.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.