Один из способов сделать партнерские отношения нестабильными, потерять время и деньги — это не составить партнерское соглашение или по-простому договор между партнерами-основателями.
В теории все всё знают, но когда дело доходит до практики, оказывается, что важные вопросы, которые в будущем могут стоить жизнедеятельности компании, остаются в стороне. Мы создали чек-лист основных вопросов, которые должны обговорить партнеры, чтобы избежать закрытия компании через год. Обсуждать правила совместного бизнеса нужно «на берегу», но если вы этого еще не сделали — самое время оградить себя от будущих проблем. И чтобы не получилось как в том анекдоте…
… мы написали ТОП 6 юридических советов, которые нужно учитывать перед составлением партнерского соглашения.
Рубрика юридические советы
Законодательство не требует обязательного заключения партнерского соглашения. Но в отличие от устава и учредительного договора, партнерское соглашение — это конфиденциальный договор между партнерами. В нем вы можете урегулировать любые положения, которые касаются деятельности вашего бизнеса с партнером и не раскрывать их посторонним лицам. Процесс изменения договоренностей по партнерскому соглашению не требует регистрации и внесение изменений в устав.
В течение одного года до Верховного Суда Украины дошло 496 дел, возникающих из корпоративных правоотношений, поэтому важно договориться «на берегу» перед началом ведения бизнеса.
Часто вместо того, чтобы заключить договор и урегулировать отношения, стороны говорят «мы доверяем друг другу». Эти партнерские отношения очень похожи на отношения в браке, когда супруги сначала доверяют друг другу, а потом делят имущество в суде. Когда возникает спор между основателями (например после смерти одного основателя наследники желают продать бизнес, а второй основатель хочет его и дальше развивать), на суды уходят годы, а деятельность бизнеса фактически заблокирована.
Не стоит делать бизнес 50/50, поскольку в случае конфликта или возникновения спорных вопросов вы окажетесь в тупиковой ситуации и деятельность вашего бизнеса опять же будет заблокирована.
Предусмотрите в партнерском соглашении разрешение «тупиковых» (deadlock) ситуаций (ситуация, когда ни у одного из акционеров не хватает голосов для принятия решения по ключевому вопросу деятельности компании).
Если вам неудобно друг другу задавать вопросы и обсуждать условия партнерского соглашения, наймите юриста, который сделает это за вас и поможет правильно оформить договор.
Чек-лист вопросов для партнеров
Миссии и цели стартапа
Зачем вам партнерское соглашение?
Что было до того, как вы решили его составить?
Что может измениться после его составления?
Как часто мы будем пересматривать партнерское соглашение?
Что является нашим бизнесом?
Какую основную ценность мы несем?
На чем мы фокусируемся?
Чего мы хотим добиться?
Для чего это каждому из нас?
Какие проблемы нам необходимо решить?
Что является критерием достижения цели?
Будем ли мы покупать другие бизнесы?
Готовы ли мы присоединиться к более крупному бизнесу?
Капитал компании и взносы
Кто вносит первоначальный капитал, предполагаются ли дополнительные взносы?
Что именно считается вкладом (денежные средства, имущество, объекты интеллектуальной собственности, оказанные услуги и пр.)?
Что будет если партнер не справляется со своими индивидуальными обязанностями по вкладу (не все деньги/не передал интеллектуальную собственность)?
Управление
Кто управляет операционной деятельностью компании, кто будет директором?
Какие полномочия у директора? Есть ли ограничения полномочий директора?
Какой порядок назначения / отстранения директора, или формирования / роспуска совета директоров? Какой порядок смены директора?
Какие ограничения по сумме сделок с третьими или аффилированными лицами, которые может заключать директор без согласия других основателей?
Какие коллегиальные полномочия у основателей, и какие полномочия предоставляются директору единолично? Кто будет нести ответственность за принятие решений директором?
Какая ответственность за превышения полномочий директором? Кто будет нести ответственность за принятия незаконного решения?
Распределение прибыли
В каких долях основатели будут владеть компанией?
Как будет происходить распределение прибыли компании между основателями?
Как принимается решение о распределении прибыли?
Будет ли действовать мораторий на распределение прибыли? Как долго?
Какая часть прибыли направляется на дальнейшее развитие бизнеса?
Собрания партнеров-основателей
Какая регулярность проведения собраний и кто может инициировать собрания?
Какой кворум для общих собраний?
Как решаются вопросы, которые отнесены к компетенции собрания?
Работа партнеров в компании
Распределение сфер ответственности, кто за какое направление отвечает?
Допускается работа еще где-то на стороне или фриланс?
Будут ли KPI для партнеров работающих на компанию?
Что если партнер не выполняет/выполняет ненадлежащим образом принятые на себя обязательства в своей сфере ответственности?
Конфиденциальность и конкуренция
Какая информация строго конфиденциальна, а какая может быть разглашена?
Как решаются вопросы непереманивании и неконкуренции?
Привлечение инвестиций в будущем
Какой порядок финансирования проекта компании?
Какой порядок прекращения финансирования проекта?
Акции компании
Разрешен ли выпуск новых акций? Если да, то для каких целей и в каком порядке?
Какие есть классы акций? Чем классы различаются? Для каких случаев предназначен тот или иной класс?
Какие ограничения вы хотите наложить на передачу акций?
Будет ли действовать мораторий на передачу акций? Может ли акционер продавать акции третьим лицам?
Как защищены права миноритариев?
Разрешение споров
Будут ли использоваться какие-либо механизмы для разрешения “тупиковых” ситуаций (deadlock), например: Russian roulette, Texas shoot-out, Mexican shoot-out (Dutch auction)?
Компания, которая 99% работы проводит в офисе. Для коммуникации используют живое общение и электронную почту. С учетом последних событий и тенденций планируют перевести своих сотрудников на удаленку. Что делать, о чем подумать и как не проиграть?
Важно сразу понимать: любой интеллектуальный труд можно перевести на удаленку без потери эффективности (а можно даже и выиграть).
Итак, 4 блока перевода из офиса на дом:
1. IT
Установить на компьютеры сотрудников софт для удаленного подключения
Что рекомендую использовать: Teamviewer.
Зачем? Для того, чтобы в любое время можно было подсоединиться к компьютеру сотрудника и помочь ему с установкой софта или с другими вопросами.
Установите VPN или аналог. (VPN)
Зачем? Для защиты от утечки персональных данных, слежки, DPI, перехвата трафика и других киберугроз.
Оборудование
Купите/предоставьте/компенсируйте сотрудникам качественные и недорогие гарнитуры (наушники+микрофон) и проверьте их работоспособность.
Что рекомендую использовать: Plantronics BlackWire C3220-C
Зачем? У сотрудника должен быть качественный звук на всех встречах.
Купите/предоставьте/компенсируйте веб-камеру (если нет в ноутбуке).
Что рекомендую использовать: Любая недорогая камера
Зачем? У сотрудника должно быть видео на всех встречах и это обязательно.
Интернет
Выпустите приказ о необходимости выходить в интернет со стационарной домашней линии (в том числе через домашний Wi-Fi) и контролируйте это.
Что рекомендую использовать: Подключать интернет через кабель
Зачем? Мобильная связь или интернет на даче или в кафе не позволят проводить встречи качественно. Ничто так не раздражает, как обрыв связи во время встречи.
Чат для всей компании
Используйте корпоративные мессенджеры. Регламентируйте общение, включая скорость ответа.
Что рекомендую использовать: Slack, Microsoft Teams, для небольших команд подходит Discord
Зачем? Обычные мессенджеры (например, Telegram) не предназначены для рабочих целей и плохо подходят для средних и больших команд. Во-первых это небезопасно: права на созданную группу принадлежат отдельному сотруднику, который может покинуть организацию. Во-вторых, идет смешение рабочих чатов с личными. В корпоративных чатах даже есть возможность интеграции с Active Directory.
Видеозвонки
Предоставьте сотрудникам минимум 2–3 варианта софта для видеозвонков. Можно использовать бесплатные версии. Что рекомендую использовать: Zoom, Microsoft Teams,
Важно: Во время звонков видео обязательно.
Учите сотрудников демонстрировать экран.
Договоренности о звонках делают через чат и заносят в календарь.
Важные звонки ставьте на запись.
Зачем? Любой софт может работать со сбоями, а клиенты используют разные инструменты для видеозвонков. Не ставьте сотрудников в позицию заложника одного сервиса.
Календарь
Приучайте сотрудников пользоваться календарем и создавайте регламент. Все встречи следует высылать заранее для подтверждения всеми участникам. Если встречи нет в календаре — значит встречи не существует.
Что рекомендую использовать: Google Suite, Office 365
Зачем? Если нет встречи в календаре, то шанс ее пропустить крайне велик. На удаленке не получится подойти к сотруднику и напомнить про встречу.
Облако с документами
Приучайте сотрудников пользоваться облаком. Никаких папок на личных устройствах!
Что рекомендую использовать: Google Диск, Яндекс Диск, Dropbox
Зачем? Для порядка в документах и быстрого доступа к ним у всей команды.
Таск-трекер и сервисы совместной работы
Что рекомендую использовать: Битрикс24, Asana, Trello
Зачем? Все сотрудники знают о своих задачах и дедлайнах, а вы можете ими управлять
Кадровый ЭДО
Подумайте сразу, как подписывать с сотрудниками кадровые документы и получать от них заявления. Курьеров отправлять или копить кипу бумаг — неудобно!
Что рекомендую использовать: HR-Link
Зачем? С каждым сотрудником в год подписывают около 30 кадровых документов. Делать это по старинке: вызывать в отдел кадров или отдавать руководителю документ — не вариант!
2. Организационные моменты
Составьте список тех, кого хотите перевести на удаленку.
Зачем? Это основа, вы должны понимать кого отправляете, а кого нет. .
Разбейте на группы и составьте график перевода.
Зачем? Делать все сразу рискованно, так как можно запутаться и упустить важные моменты.
Проведите серию вебинаров для сотрудников (а в случае крупных компаний для ключевых сотрудников) Расскажите максимально подробно “правила игры”: что им нужно сделать, какие документы подписать, какие регламенты работы, как взаимодействовать между собой и важно, что начните с плюсов такой работы для самих сотрудников.
Зачем? Если сотрудник не знает, что нужно делать, то он не будет выполнять свою работу. Убедитесь, что все ознакомились со своей должностной инструкцией и обязанностями. Все директивы сотрудники воспринимают “в штыки” Делайте акценты на плюсах для них. Обучите сотрудников работе с софтом на удаленке.
Научите сотрудников использовать необходимые программы и электронную подпись для кадровых документов
Зачем? Сотрудникам не придется самостоятельно разбираться в новых технологиях, что сэкономит время и повысит эффективность.
3. Юридические особенности
Подготовьте или внесите изменения и ознакомьте всех сотрудников с ЛНА, который:
Регулирует порядок дистанционной (удаленной) работы;
Позволяет переводить работников по инициативе работодателя на дистанционную работу;
Легализует и регламентирует обмен электронными документами с использованием электронной подписи.
Определяет режим работы дистанционщика;
Регламентирует режим рабочего времени;
Упорядочивает продолжительность и периодичность дистанционки, если она временная;
Регламентирует Порядок предоставления отпусков;
Уточняют порядок вызова в офис для временных дистанционщиков;
Определяет порядок выхода в офис по своему желанию;
Уточняют порядок и размер возмещения расходов
Зачем? Во-первых, так требует закон. Во-вторых, вы усиливаете контроль за деятельностью работников и исключаете риск манипуляций с их стороны. Если вы, к примеру, забыли прописать порядок вызова сотрудника в офис, то заставить его приехать туда на совещание, будет затруднительно.
А если не зафиксировать время работы сотрудника, то может возникнуть следующая ситуация: руководитель звонит работнику в 15:00, а тот не берет трубку и потом говорит, что работал до 14:59. В этом случае ваши претензии к нему будут безосновательны.
Включите в ПВТР основные положения, регулирующие дистанционную (удаленную) работу.
Зачем? Наличие основных положений в ПВТР позволит не допустить рисков манипуляций и избежать снижения качества труда.
Разработайте и заключите с работниками дополнительные соглашения к трудовым договорам, которые будут содержать корректные условия о дистанционной работе.
Зачем? Это позволит снизить риск при проверке ГИТ и при обращениях работников в суд.
Заключите с сотрудниками соглашения об ЭДО.
Зачем? Бумажный документооборот на дистанционке крайне сложный
Проверьте наличие у сотрудников подписанных соглашений на обработку персональных данных, позволяющих поручать обработку третьей стороне.
Зачем? Для использования системы кадрового электронного документооборота
ВАЖНО:
Документы компании — это взаимосвязанная система, так что нужно проверить, как минимум, следующие ЛНА на актуальность и противоречия:
Правила внутреннего трудового распорядка — виды дистанционки, правила приема/увольнения и режим работы
Положение об оплате труда работников, Положение о премировании и материальном стимулировании работников — размер и сроки компенсации за использование своего компьютера или другого оборудования для работы
Положение об аттестации — как проводить аттестацию и оценку дистанционщиков
Положение об обработке персональных данных работников — как работники будут передавать документы с персональными данными и где кадровый сотрудник будет их хранить
Регламенты работы отделов — как дистанционные работники будут взаимодействовать, выполнять задания и отчитываться
4. Психологические факторы
Для правильного выполнения функций планирования, организации, мотивации, контроля и координации принимайте и дополняйте следующие принципы:
Установите часы присутствия на работе (онлайн).
Они должны быть одинаковыми для всех сотрудников компании. В рабочие часы сотрудники должны быть доступны. Удаленка ≠ каждый работает, как и когда хочет.
Не контролируйте время онлайн, а отслеживайте выполнение задач. Если вы будете контролировать присутствие, единственное, что сотрудники будут делать, так это быть в сети и создавать видимость работы.
Установите регламент ответа в чатах и не требуйте мгновенного ответа (это главный враг продуктивности!)
Решайте в чате только несложные вопросы. Основные же решайте на видеозвонках с последующими обязательными “минутками” встречи в чате.
Попросите сотрудников использовать в чате только реальные имена и фото (как бы это ни было банально). Чат может и должен стать заменой публичного справочника сотрудников.
Используйте календарь для назначения встреч (всем сотрудникам должны быть доступны календари остальных).
Проводите регулярные общие видеозвонки с командой и встречи 1-1 с ключевыми сотрудниками. Не реже раза в неделю!
Введите «тихие» часы. Для максимальной продуктивности у сотрудников должен быть баланс между работой и личной жизнью.
Подумайте над программой wellbeing. Постоянное сидение на одном месте может вызвать проблемы со здоровьем.
Создайте курс «Как организовать удаленную работу дома», где подробно расскажете о тайм-менеджменте и прочих правилах и принципах. Дома рамки между отдыхом и рабочим временем размываются. В результате, накапливается усталость, которая может привести к психологическому выгоранию.
Принципы организации рабочего места сотрудника:
Отдельная комната с хорошим интернетом.
Десктоп, ноутбук, гарнитура, камера.
ВАЖНО!
Никаких личных дел во время работы.
Подумайте как помочь сотруднику “продать” семье почему его нельзя беспокоить в рабочии часы
Учтите, что на первых порах у сотрудников будет много вопросов, поэтому заведите канал помощи в чате.
Если вы будете следовать предложенным правилам и принципам, то сложностей с переводом сотрудников на удаленку не возникнет.
А если у вас появились вопросы, возражения или пожелания, то буду рад вашим обращениям:
Если у вас нет времени читать или информация известна, окончательный код получения полноразмерного изображения из камеры Android-смартфона расположен в конце статьи.
Описание проблемы
Если вы пишете кросс-платформенное приложение, то для получения изображения из камеры для ПК можно воспользоваться классом QCamera, пример для работы с которым описан в документации Qt.
В соответствии с указанным примером мы добавляем в .pro файл
QT += multimedia multimediawidgets
Далее создаём виджет в своей программе, отображающий изображение из веб-камеры и сохраняющий его в QPixmap или QImage для дальнейшего использования.
Когда возникает задача сделать то-же самое на Android, то выясняется, что multimediawidgets не поддерживаются данной ОС и камера снимать и сохранять снимки будет, но что она отображает в текущий момент будет загадкой, т. к. QCameraViewfinder использует multimediawidgets и на Android не отображает ничего. Дальнейший поиск решения проблемы приводит к двум вариантам решения:
использовать QML и написать свой Qt Quick-элемент, выполняющий эту функцию, затем состыковать его остальной частью приложения на Qt Widgets, С++;
использовать приложение по-умолчанию Android-смартфона для получения фотографии, затем обработать её в своём приложении.
Рассмотрим первый вариант
Если вы С++ программист Qt Widgets, то очередное эпизодическое углубление в QML займёт у вас время, добавим к этому время на написание Qt Quick-элемента, стыковки этого элемента с С++ кодом, отладки написанного кода. Если вы не профессионал в QML получается долго и сложно.
Рассмотрим второй вариант
В Android-смартфоне уже есть приложение по-умолчанию, прекрасно выполняющее нужную функцию, нужно им просто воспользоваться, применив Java-вызовы (JNI — Java Native Interface) из С++ кода при помощи QtAndroid. Выглядит проще. Полностью работающего кода в интернете я не нашёл, и, изучив опыт других, опираясь на документацию разработчка на Android написал собственный.
Как это сделать
Если у вас нет времени читать или информация известна, окончательный код получения полноразмерного изображения из камеры Android-смартфона расположен в конце статьи.
Прочитав статью Получить фотографии на Android я сделал вывод, что применив данный метод можно получить или миниатюру изображения в виде массива пикселей или сохранить изображение в файл. Поискав готовые решения, я нашёл на GitHub подходящий код, который должен был выполнить нужную мне задачу. При его проверке, оказалось, что он устарел и теперь приводит к FileUriExposedException исключению, причины возникновения которого описаны в вышеуказанной ссылке на статью.
Чтобы разобраться самостоятельно, как оно работает, начнём с простой задачи, не требующей обращения к файлам и множества Java-вызовов — получению миниатюры.
Получение миниатюры
Начнём с .pro файла.
Он должен содержать следующие строки для поддержки Android.
android { QT +=androidextras }
Для получения результата нам понадобится класс, унаследованный от QAndroidActivityResultReceiver. Если требуется, чтобы объект нашего класса высылал изображение при помощи сигнала, то он также должен быть унаследован от любого класса Qt, имеющего базовый класс QObject.
Заголовочный файл (.h) класса имеет вид:
#ifndef CAMSHOT_H #define CAMSHOT_H #include <QObject> #include <QString> #include <cstring> #include <QImage> #include <QDebug> #include <QtAndroid> #include <QAndroidActivityResultReceiver> #include <QAndroidParcel> class CamShot : public QObject, public QAndroidActivityResultReceiver { Q_OBJECT public: CamShot(QObject *parent = nullptr):QObject(parent),QAndroidActivityResultReceiver(){} static const int RESULT_OK = -1; static const int REQUEST_IMAGE_CAPTURE = 1; static const int REQUEST_TAKE_PHOTO = REQUEST_IMAGE_CAPTURE; void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) override; static QImage camThumbnailToQImage(const QAndroidJniObject &data); public slots: void aMakeShot(); signals: void createNew(const QImage &img); }; #endif // CAMSHOT_H
Заголовочный файл (.cpp) класса имеет вид:
QImage CamShot::camThumbnailToQImage(const QAndroidJniObject &data){ QAndroidJniObject bundle = data.callObjectMethod("getExtras","()Landroid/os/Bundle;"); qDebug()<<"bundle.isValid() "<<bundle.isValid()<<bundle.toString(); QAndroidJniObject bundleKey = QAndroidJniObject::fromString("data"); const QAndroidJniObject aBitmap (data.callObjectMethod("getParcelableExtra", "(Ljava/lang/String;)Landroid/os/Parcelable;", bundleKey.object<jstring>())); qDebug()<<"aBitmap.isValid() "<<aBitmap.isValid()<<aBitmap.toString(); jint aBitmapWidth = aBitmap.callMethod<jint>("getWidth"); jint aBitmapHeight = aBitmap.callMethod<jint>("getHeight"); QAndroidJniEnvironment env; const int32_t aBitmapPixelsCount = aBitmapWidth * aBitmapHeight; jintArray pixels = env->NewIntArray(aBitmapPixelsCount); jint aBitmapOffset = 0; jint aBitmapStride = aBitmapWidth; jint aBitmapX = 0; jint aBitmapY = 0; aBitmap.callMethod<void>("getPixels","([IIIIIII)V", pixels, aBitmapOffset, aBitmapStride, aBitmapX, aBitmapY, aBitmapWidth, aBitmapHeight); jint *pPixels = env->GetIntArrayElements(pixels, nullptr); QImage img(aBitmapWidth, aBitmapHeight, QImage::Format_ARGB32); int lineSzB = aBitmapWidth * sizeof(jint); for (int i = 0; i < aBitmapHeight; ++i){ uchar *pDst = img.scanLine(i); const uchar *pSrc = reinterpret_cast<const uchar*>(pPixels + aBitmapWidth * i + aBitmapWidth); memcpy(pDst, pSrc, lineSzB); } env->DeleteLocalRef(pixels); //env->ReleaseIntArrayElements(pixels, pPixels, 0); отвязывает указатель на данные массива от массива, а надо удалить сам массив, поэтому DeleteLocalRef. return img; } void CamShot::aMakeShot() { QAndroidJniObject action = QandroidJniObject::fromString("android.media.action.IMAGE_CAPTURE"); //Если необходимо указать Java-класс (не аргумент функции), то указывается полное имя класса (точки-разделители заменяются на "/"), например "android/content/Intent", "java/lang/String". //Если аргумент функции Java-объект, то писать имя класса начиная с "L" и ";" в конце, например "Landroid/content/Intent ;", "Ljava/lang/String;". //Если примитивный тип или массив, то указываются соответствующие символы без разделителей, например "V" (void) или "[IIIIIII" (массив jint, и 6 jint за ним) //Символы, соответствия примитивны типам: QAndroidJniObject intent=QAndroidJniObject("android/content/Intent","(Ljava/lang/String;)V", action.object<jstring>()); QtAndroid::startActivity(intent, REQUEST_IMAGE_CAPTURE, this); } void CamShot::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data){ if ( receiverRequestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK ) { const QImage thumbnail (camThumbnailToQImage(data)); if (!thumbnail.isNull()) emit createNew(thumbnail); } }
Разберём приведённый код
Краткие правила указания аргументов в JNI-вызовах
если необходимо указать имя Java-класса (не в качестве аргумента Java-функции), то указывается полное имя класса (точки-разделители заменяются на «/«), например «android/content/Intent«, «java/lang/String«;
если аргумент функции Java-объект, то писать имя его класса начиная с «L» и «;» в конце, например «Landroid/content/Intent;«, «Ljava/lang/String;«;
если примитивный тип или массив, то указываются соответствующие сигнатуры (символы без разделителей), например «V» (void), «I» (jint) или «[IIIIIII» (массив jint, и 6 jint за ним);
сигнатуры примитивных типов:
C/C++
JNI
Java
Signature
uint8_t/unsigned char
jboolean
bool
Z
int8_t/char/signed char
jbyte
byte
B
uint16_t/unsigned short
jchar
char
C
int16_t/short
jshort
short
S
int32_t/int/(long)
jint
int
I
int64_t/(long)/long long
jlong
long
J
float
jfloat
float
F
double
jdouble
double
D
void
void
V
сигнатуры массивов:
JNI
Java
Signature
jbooleanArray
bool[]
[Z
jbyteArray
byte[]
[B
jcharArray
char[]
[C
jshortArray
short[]
[S
jintArray
int[]
[I
jlongArray
long[]
[L
jfloatArray
float[]
[F
jdoubleArray
double[]
[D
jarray
type[]
[Lfully/qualified/type/name;
jarray
String[]
[Ljava/lang/String;
Чтобы получить доступ к элементам массива, необходимо использовать JNI-методы объекта класса QAndroidJniEnvironment, например такие как: NewIntArray, GetIntArrayElements, DeleteLocalRef GetArrayLength,GetObjectArrayElement, SetObjectArrayElement, и т.д.
переопределение абстрактного метода void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) override; в который будет передаваться Java-объект класса Intent с миниатюрой изображения;
статический метод static QImage camThumbnailToQImage(const QAndroidJniObject &data); извлекающий из Java-объекта класса Intent Java-объект класса Bitmap, копирующий пиксели в массив пкселей (32-битных значений) и построчно копирующий эти пиксели в QImage;
общедоступный слот void aMakeShot(); вызывающий операцию по фотографированию изображения и получению его миниатюры;
В методе void aMakeShot() создаётся Java-объект Intent в который передаётся строка со значением, указывающим, что необходимо сделать — произвести захват изображения. После этого сформированное действие (Intent) отправляется на исполнение (Activity).
В процессе выполнения действия будет запущено приложение по-умолчанию для фотографирования. Как только фотография будет сделана и подтверждена пользователем, будет произведён вызов виртуального метода handleActivityResult, в котором осуществляется проверка: является ли выполненное действие запрошенным и успешно выполненным. Если да, то вызовем статический метод camThumbnailToQImage получения изображения QImage из Java-объекта класса Bitmap и при успешном результате отправим полученное изображение потребителю сигналом Qt.
Рассмотрим статический метод static QImage camThumbnailToQImage(const QAndroidJniObject &data) override;
Интересующее нас изображение передаётся в блоке дополнительных данных Java-объекта класса Intent и является Java-объектом класса Bundle, чтобы его получить нужно воспользоваться методом объекта Intent: Bundle getExtras()
В Bundle хранятся ассоциативные пары <ключ-строка>:<значение>. В статье получить фотографии на Android указан ключ, по которому располагается миниатюра. Это строка «data».
Cобрать из BitmapQImage сразу не получится, т. к. у Bitmap нет указателя на данные изображения вместе с заголовком его формата. Поэтому получим его размер (ширину и высоту) в пискселях и создадим QImage аналогичного размера для копирования значений пикселей в него.
Для этого понадобится создать линейный массив значений пикселей jintArray pixels = env->NewIntArray(aBitmapPixelsCount); После того, как пиксели будут скопированы, получим указатель на начало массива, который можно использовать в C++ коде: jint *pPixels = env->GetIntArrayElements(pixels, nullptr); Затем в цикле построчно скопируем значения пикселей из массива в изображение Qimage. По завершению копирования освобождаем память, выделенную под массив значений пикселей env->DeleteLocalRef(pixels); и возвращаем результат в виде QImage.
Отлично. Миниатюра изображения получена.
Получение полноразмерного изображения
Для получения полноразмерного изображения необходимо воспользоваться классом FileProvider, чтобы получить разделяемый Uri для файла фотоснимка. Обращаю ваше внимание, что у Android, по крайней мере, их два:
androidx.core.content.FileProvider;
android.support.v4.content.FileProvider.
Первый — самый современный, не поддерживается Qt, а для использования второго необходимо настроить среду QtCerator:
Установить дополнительные репозитории
Главное меню (сверху)→ «Инструменты» → «Параметры» → «Устройства»→ вкладка «Android»→ вкладка «SDK Manager»→Развернуть элемент списка «Инструменты» в список→ «Extras»→ «Android Support Repository» — поставить флажок установить и нажить на кнопку «Применить» справа.
Заменить автогенерируемые файлы настройки сборки для Android собственными
Перейти на боковой панели QtCreator на вкладку «Проекты». В левой области окна «Сборка и запуск»→ «Сборка». Тогда в правой области окна «Build Android APK» → «Create Templates». В появившемся диалоговом окне установить флажок «Копировать файлы Gradle в каталог Android», нажать на кнопку «Завершить»:
Добавить каталог со своими настройками сборки в проект
В каталоге с исходными кодами вашего приложения появится каталог «android», который необходимо добавить в проект.
Настроить в файле проекта отключаемую возможность поддержки Android
Если приложение кросс-платформенное и предполагается компиляция не только на Android, то в .pro файле необходимо добавить директиву android: перед каждым добавленным файлом:
Создать файл с указанием каталога совместного использования с другими приложениями
Это нужно для того, чтобы приложение фотографирования по-умолчанию могло передать нашему приложению файл.
В каталоге сборки, там где находится автогенерируемый файл «AndroidManifest.xml» внутри каталога «res» рядом с каталогом «values», создать каталог «xml», а в нём файл «file_paths.xml» (… /abin/AndroidManifest.xml) (… /abin/res/xml/file_paths.xml). В созданный файл поместить следующие строки:
void Aux::resizeCenteredImg(QImage *image, const QSize &newSize, const QColor bgColor){ if (image->size() == newSize) return; const QSize szDiff = newSize - image->size(); QImage newImage(newSize, QImage::Format_ARGB32); newImage.fill(bgColor); QPainter painter(&newImage); painter.drawImage(QPoint(szDiff.width()/2, szDiff.height()/2), *image); *image = newImage; } void Aux::rotateImg(QImage &img, qreal degrees){ QPoint center = img.rect().center(); QMatrix matrix; matrix.translate(center.x(), center.y()); matrix.rotate(degrees); img = img.transformed(matrix, Qt::SmoothTransformation); } void Aux::rotateImgCW90(QImage &img){ const int w = img.width(); const int h = img.height(); const int maxDim = std::max(w, h); resizeCenteredImg(&img, QSize(maxDim, maxDim), Qt::white); rotateImg(img, 90); resizeCenteredImg(&img, QSize(h, w), Qt::white); } void Aux::rotateImgCW180(QImage &img){ rotateImg(img, 180); } void Aux::rotateImgCW270(QImage &img){ const int w = img.width(); const int h = img.height(); const int maxDim = std::max(w, h); resizeCenteredImg(&img, QSize(maxDim, maxDim), Qt::white); rotateImg(img, 270); resizeCenteredImg(&img, QSize(h, w), Qt::white); }
Указанный метод позволяет получить за один раз или миниатюру или полноразмерное изображение.
Результат зависит от переданного логического параметра «thumbnailNotFullScale». Если он равен логической единице, то будет получена миниатюра, если логическому нулю, то полноразмерное изображение. Попытка получить миниатюру при запросе сохранения полноразмерного изображения в файл приведёт к исключению в JNI-вызовах.
Если миниатюра всегда ориентирована правильно, то полноразмерное изображение направлено в одну строну и его необходимо поворачивать. Информацию о необходимых преобразованиях можно получить из exif-свойств изображения при помощи ExifInterface. В обнаруженных в интернете Java-примерах преобразование к нормальной ориентации производится в Java-коде, в случае с Qt нет смысла мучить себя трудно отлаживаемыми, громоздкими JNI-вызовами и проще выполнить все необходимые преобразования в Qt.
21 марта 2021 года (воскресенье), с 12:00 и до 19:00, состоится Бесплатная онлайн-Конференция: Нарратив в играх.
Мероприятие посвящено всем, кто по-настоящему любит игры, хороший сюжет, и хочет продолжить свой рост в игровой индустрии или только начать свой путь в геймдев.
Кроме интересных лекций вас ждет конкурс на лучший сценарий игры (и не только) с крутыми призами.
На конкурс принимаются видео-игры и настолки, выпущенные в 2020-2021 году (даже в «Раннем доступе» или в виде демо/beta) на русском языке.
Для участия в конкурсе необходимо подать заявкудо 05.03.2021
Участие в мероприятии абсолютно бесплатно. Организатор конференции: Центр развития компетенций в бизнес-информатике Высшей школы бизнеса НИУ ВШЭ, при поддержке WN Conference, Talents in Games и Союза Литераторов РФ. Регистрация на конференцию здесь>>>
Оказалось, что PowerBI не имеет встроенной возможности настроить доступ к БД, защищённой SSH-туннелем. Приходится выкручиваться. Мне очень помогла эта статья — спасибо тебе добрый и компетентный в написании инструкций человек, без тебя я бы впала в отчаяние.
И тем не менее, в ней раскрыты не все нюансы. В своём материале я добавлю следующее:
Два уникальных совета, как сделать так, чтобы установленный туннель не падал после авторизации
Дополнительная инструкция для подключения к SSH при помощи приватного ключа, а не логина и пароля
Скрины из самого PowerBI с настройкой БД и советы о том, как работает выборка из подключенной БД и как обновлять данные, полученные по SQL-запросам.
Плюс я ориентирую свой материал на продуктовых аналитиков и аналитиков данных, то есть на тех, у кого нет доступа на редактирование БД и кто может не знать, что такое проброс портов и SSH-tunnel в принципе.
пароль для доступа или связка приватного и публичного ключа*
IP-адрес самой БД (обычно 127.0.0.1);
порт самой БД;
название БД;
логин доступа к БД (не то же самое, что username для доступа на SSH-сервер);
пароль для доступа к БД.
*Выбирать пароль или ключ не вам, а Хранителю сервера, пароль проще, ключ — безопаснее. В статье будут инструкции на оба случая. Если у вас кейс с ключами, то на этапе подготовки вам надо сгенерить оба ключа, разместить их в нужную папку на вашем компьютере и отдать внешний ключ Хранителю сервера, чтобы он его туда добавил. На эту тему есть много инструкций в интернете, я не буду повторяться.
Поднимаем SSH-туннель
Открываем Putty
В Category/Session вводим IP-адрес SSH-сервера, порт SSH-сервера и выставляем радио-баттон Close window on exit на позицию Never
Переходим в Category/Connection/SSH и ставим галочку на Don’t start a shell or command at all
Переходим в Category/Connection/SSH/Tunnels, в поле Source port вбиваем порт самой БД, в поле Destination IP-адрес самой БД:порт самой БД. Жмём Add.
*пункт для тех, кто подключается с приватным ключом, если у вас случай с логином и паролем, то переходите сразу к 6 пункту инструкции
Запустите PuttyGen (установился на ваш компьютер вместе с Putty)
Выберите в верхнем меню Conversions/Import Key
В открывшемся окне Проводника откройте папку, куда вы сохранили файлы приватного и публичного ключа (пункт 2d списка “Вам понадобится”) и выберите файл приватного ключа. Иногда Windows делает этот файл скрытым. Возможно, вам надо будет включить отображение скрытых файлов в Проводнике, нажав на Вид и поставив галочку напротив “Скрытые элементы”
Нажимаем Save private key. Даём ключу любое имя на латинице и сохраняем в папку с ключами.
Возвращаемся в Putty. Переходим в Category/Connection/SSH/Auth и нажимаем Browse рядом с Private key file for authentication
В открывшемся окне Проводника выбираем сохранённый в пункте 5d файл приватного ключа.
Переходим в Category/Session, в поле Saved Session вводим имя нашего туннеля (любое), жмём Save. Это позволит нам не вводить все настройки каждый раз заново. После чего жмём Open
В открывшемся окне Терминала рядом с Login as вводим username для доступа на SSH-сервер и жмём Enter
*пункт для тех, у кого авторизация по паролю, если вы авторизовались по связке ключей, то пропускайте этот пункт и переходите сразу к 9.
Вводим пароль для доступа на SSH-сервер и жмём Enter
Настройка PowerBI
SSH-туннель настроен, не закрывайте окно терминала Putty. Теперь переходим в PowerBI. Жмём Получить данные и выбираем “База данных MySQL” или “База данных PostgreSQL” в зависимости от того, что у вас. Интерфейс будет одинаковым, а вот вероятность успеха — разной, потому что MySQL И PostgreSQL используют разные драйвера. Убедитесь, что выбрали свою БД правильно.
В поле Сервер вводим IP-адрес самой БД:порт самой БД
В поле База данных вводим название БД
Жмём “Расширенные настройки” и в поле Инструкция SQL вставляем запрос, по которому нужно импортировать данные. Если вы его не напишете, PowerBI приконнектится ко всей БД, но не позволит вам вытаскивать из неё данные запросами и не позволит построить модели (или я не нашла как, если у вас есть успешный опыт, с удовольствием прочитаю его в комментах)
Жмём ok
Вводим логин доступа к БД и пароль для доступа к БД, жмём Подключение
Возможна вот такая ошибка, это ok
Как обновить данные из БД в PowerBI
Поднимаем SSH-туннель в Putty
Переходим в PowerBI и жмём Обновить. Все созданные запросы ещё раз отправятся на сервер и выгрузят свежую информацию