Интервью с аналитиком — Кирилл Шмидт, Senior Product Analyst в Wrike

В какой области аналитики более востребованы? Где взять идеи для автоматизации процессов? Как можно замотивировать крутого аналитика стать ментором? Сегодня в рубрике #АналитикОтвечает — Кирилл Шмидт, Senior Product Analyst в Wrike

В какой области аналитики более востребованы?

Мне кажется, что сейчас наиболее популярная отрасль — это маркетинговая продуктовая аналитика. И даже может быть, продуктовая аналитика, которая реже встречается в компаниях. Финансовая аналитика очень старая, в ней много людей, которые работают традиционно, можно встретить даже тех, у кого десятки лет опыта. Маркетинг тоже довольно давно развивается, но он младше, чем финансовая аналитика. Продуктовая аналитика становится все более востребована в контексте разработки IT-продуктов. Эта тема также начинает мигрировать и в оффлайн — появляются концепции продуктового менеджмента и в оффлайн продуктах. Именно поэтому мне кажется, что это наиболее горячая область. Хотя, в конечном итоге, в каждой из этих областей вы сможете найти хорошее место с хорошим доходом, если вы профессионал.

Насколько важно аналитику строить свой бренд и, например, писать статьи про свои кейсы на Хабре, VC или где-то ещё?

Здесь нужно определиться с тем, чего вы хотите достичь. Аналитики не слишком общительные люди и существует не очень много сообществ аналитиков данных, но это не мешает им строить карьеру. Если вы хотите чем-то поделиться с миром, тогда, конечно, стоит всем этим заниматься. И когда вы становитесь открытым человеком, строите свой личный бренд, вам становится легче заводить знакомых, вас лучше знают и вам проще находить работу и какие-то возможности. Если есть такая цель, тогда вперед! Но если просто хотите найти работу, то резюме с результатами прошлой работы будет достаточно. Скажем так, это полезно, но не обязательно.

Где взять идеи для автоматизации процессов?

Стоит посмотреть, какие вещи требуют больше всего труда и в каких замешано большее количество людей, чтобы понять, можно ли их заменить на какие-то автоматические вещи, например, через ETL процессы. И затраты на автоматизацию должны быть покрыты за счет уменьшения ручного труда, чтобы эти люди были либо совсем высвобождены, либо занимались бы какими-то другими вещами. Берем процесс, смотрим насколько он трудозатратен, насколько он нервирует людей и сколько ошибок в нем делают, ранжируем по этим критериям все основные процессы, которые происходят в компании. Обычно, когда проделываешь такую работу, становится понятно, что можно сделать и что сделать важнее.

Как понять, ценнее ли остаться в компании, где нужно наводить порядок в процессах (больше свободы) или уходить туда, где они настроены и можно сразу приступать к практике (больше конкретного опыта)?

Здесь вопрос состоит в том, к чему больше тянет. В компаниях, где меньше порядка, человек получает больше менеджерского опыта. Ему необходимо собирать разных инженеров, чтобы понять, что стоит сделать, нужно быстро реагировать и много чего держать в уме. Это прикольный опыт, особенно если вы целитесь в менеджмент и вам интересна широта знаний. Но, в таких компаниях, как правило, не хватает времени на глубину знаний. Если вы хотите делать сложные задачи, применять какие-то интересные методы, то лучше идти в компанию с выстроенными процессами. Поэтому все зависит от того, в каком направлении вы хотите развиваться. И там, и там есть способ построить карьеру и уровень доходов сопоставим.

Как перейти из QA в аналитику?

Не думаю, что есть какие-то особые правила. Если вы не имеете опыта, то в резюме у вас чистый лист. В таком случае, нужно поступать как junior: искать места, где вас приняли бы на работу, когда у вас мало опыта или его совсем нет. При этом, если вы, как QA, разбираетесь в вещах связанными со структурой базы данных, можете писать SQL запросы, то у вас уже есть определенная база при входе в аналитику. Тем, кто вас нанимает, будет сразу понятно на какие задачи вас можно бросить и не требовать от вас аналитических навыков, получая сразу пользу, постепенно добрасывая задачи, где вы бы развивали свои аналитические навыки.

О каких навыках следует упоминать в резюме на аналитика? Что будет плюсом, что оценят?

Первое, что оценят в любом резюме — описание реальных задач, которые вы решали. Не грустную историю, в которой были какие-то непонятные процессы, которые постоянно валились на вас, а вы это дело беспробудно считали, непонятно зачем. Лучше всего упоминать конкретные достижения, придуманные метрики, решенные задачи, когда при помощи аналитики и вашего труда в бизнесе решились проблемы. Это, в первую очередь, говорит о том, что вам можно поручить задачу и вы сможете найти решение. Для работодателя самым важным является понимание того, что от вас будет прок как от аналитика.

Помимо этого, также будет плюсом, если вы укажете навыки, связанные с техническими вещами: SQL, BI-системы, ARL Python (зависит от компании, так как далеко не во всех компаниях это требуется).

Достаточно ли для начала карьеры аналитика знаний SQL и Python, без статистики и матанализа?

Наверное, это хороший набор для начала карьеры. Но, чтобы быть аналитиком, этого явно недостаточно. Вам нужно будет добирать необходимые знания. Зная SQL и Python, вы можете уйти в специализацию data engineering, где вы будете больше заниматься подготовкой трансформации данных. Это тоже очень востребованная профессия, очень важная для аналитиков, так как аналитики не могут работать с неподготовленными данными. Здесь вам статистика и матанализ и не понадобятся. Возможно, что это будет даже более интересная область для вас. Но, если вам все же интересна предметная область по бизнесу, интересно залезть в статистику, тогда эти навыки нужно получать на работе, интересоваться этой темой и понимать, как вы можете применить эти методы в работе.

Часто Junior это человек с опытом до года, а как стартовать если нет опыта? Искать смежные вакансии?

Мой личный опыт такой: меня позвали работать аналитиком, потому что я задавал слишком много вопросов внутри компании. Я работал в компании и донимал отдел аналитики разного рода вопросами по клиентской базе, по поводу того, что с ними происходит и как это работает, и однажды они сделали мне предложение, чтобы я у них поработал. Таким образом я и попал в аналитику. Довольно странно и случайно.

А если бы я целенаправленно искал работу в аналитике сейчас, то я бы поступил путем «в лоб». Нужно написать резюме и искать компании, которые принимают людей с небольшим опыта или стажеров, чтобы получить этот опыт. Есть определенное количество компаний, которые готовы вам чуть поменьше заплатить, зато вы сможете набраться опыта, который сможете использовать в своем дальнейшем резюме. Это основной способ старта. Если вы будете искать смежные вакансии, то это может стать слишком окольным путем. В итоге, вы поработаете в нескольких местах и все равно вам придется объяснять работодателю, каким образом все эти окольные пути имеют отношение к вакансии, на которую вы пытаетесь попасть. Скорее всего, вы не выиграете, потеряв время на смежную работу.

Как можно замотивировать крутого аналитика стать ментором? В чём может быть полезен ментору новичок?

Быть ментором — это свойство характера. Человеку должно нравиться рассказывать что-то, делиться знаниями и в какой-то степени удовлетворять чувство собственного достоинства и собственной важности через обучение других. Это мотив для хорошего ментора, чтобы он был заинтересован в людях, с которыми он работает. Такое менторство дает способы самореализоваться. То, что твои знания живут в других людях, что ты развиваешь других людей, что ты можешь внести какое-то изменение в сообщество аналитиков, тем самым распространяя свои подходы и методы в этом сообществе. А еще, менторство — это один из этапов к менеджменту. Если человек хочет управлять другими людьми, то лидерство во многом состоит в том, чтобы развивать людей, с которыми ты работаешь. Это то, ради чего полезно быть ментором. Если человек хочет просто решать задачи и ему неинтересно учить других людей, его абсолютно не беспокоит, что его коллеги не умеют что-то делать, у него не появляется такого зуда, чтобы их этому научить, он считает, что есть его полянка, на которой ему нужно работать, то лучше использовать этого человека по прямому назначению, чтобы он работал на этой полянке и был эффективен, грузить его более сложными задачами. А в качестве ментора взять кого-то другого.

Как у вас в Wrike построена работа в группы аналитиков?

В Wrike работа построена так, что у нас есть продуктовые команды, в которых есть продуктовый менеджер и, обычно, к нему назначен продуктовый аналитик, который вместе с ним разбирается, что происходит с продуктом, и они вместе пытаются понять, какие нужны метрики, как их применить, как понять наличие успеха. Аналитик выясняет, какие нужны данные и как это все посчитать.

Наш инструментарий — это Tableau, ARL Python и SQL запросы. Мы собираем достаточно много данных о клиентском поведении, агрегируем данные по маркетингу, по финансам, и все это у нас находится в централизованном хранилище данных. Это наш основной инструментарий. Если вспоминать про наведение порядка, то у нас значительная часть базового порядка уже наведена и нам нужно глубже копать в самих продуктах, понимать, что в них происходит.

Data Scientist, Data Analyst, Business Analyst: как вы считаете, если смешать эти специализации в одного аналитика, то какие доли они будут занимать? Учитывая, что не каждая компания может себе позволить этих специалистов в чистом виде, то какой вариант смеси будет самым востребованным на рынке?

Мне кажется, что нельзя смешать эти три направления. Это как взять человека, который и фронтендер, и бэкендер, и знает 20 языков, одновременно пишет все требования и управляет проектами. Ничего хорошего из этого не выйдет, никакой специализации и глубины ни в одном из направлений не будет. Соответственно, лучше не смешивать эти направления.

Дата-аналитики используют методы из бизнес аналитики и дата сайенса, но специализируются немного на других вопросах. Как и дата сайентисты, им нужно понимать определенные методы из бизнес и дата аналитики, чтобы эффективно выполнять свою работу. Если компания не может нанять всех трех специалистов, вопрос такой: «Зачем она хочет нанимать тех или иных специалистов?». Эти специалисты решают определенный круг задач.

Бизнес аналитики формализируют то, что мы хотим сделать, чтобы разработчикам было понятно, что им нужно делать, чтобы требования были в нормальном виде, с которыми согласен бизнес-заказчик и которые понимают разработчики. Если компания специализируется на разработке софта, то им нужен либо отдельный человек на должности бизнес аналитика, либо это будет находиться в компетенции продакт-менеджера. Если компания хочет анализировать данные, которые у нее есть, принимать решения на основании этих данных, тогда им нужны аналитики данных. А если компания хочет заниматься машинным обучением, если у нее есть предметная область для этого, есть какие-то задачи, которые можно решить при помощи ML, тогда ей нужно нанимать дата сайентиста или брать их на аутсорс.

Когда вы дата сайентисту даете задачи дата аналитика, он начинает грустить, так как обнаружит, что машинным обучением он почти не занимается. И просто сумасшествие ко многим задачам аналитиков данных пытаться прикручивать машинное обучение. Соответственно, бизнес аналитик может попросту не уметь работать с данными, это не его направление. Поэтому такая практика не имеет смысла.

Хотите разобраться в аналитических инструментах от Google Analytics и BI до SQL и Python? Записывайтесь на наш шестимесячный онлайн-курс «Профессия: Аналитик»! Узнать подробности

ссылка на оригинал статьи https://habr.com/ru/company/productstar/blog/508896/

Управленческая истерика. Нормальными словами

Раньше я был глупый и писал худлит. Теперь я всё понял, и худлит на Хабре не пишу. Но чего добру-то пропадать? Точнее, хочу понять, есть ли в том худлите добро, польза. Попробую взять худлитную статью и изложить её посыл нормальными словами. А чего получится — вам решать. Оригинал здесь.

С управленческой истерикой сталкивались, наверное, все – и подчиненные, и руководители.

Со стороны руководителя это состояние «как же вы меня достали, я из вас теперь душу выну!». Происходит, обычно, когда случается Нечто – какой-нибудь серьезный кризис, крупный косяк, потеря клиента, завал проекта. Или просто копится, копится, копится, и потом бац! – чья-нибудь малейшая провинность, и пошло-поехало.

Со стороны подчиненных, соответственно, истерика руководителя или объяснима, или нет. Когда косяк серьёзный и очевидный, всё понятно. Когда начальник взрывается из-за какой-то мелочи – остаётся только гадать, из чего и в течение какого времени складывался взрыв.

В психологии обыденной жизни такое явление объясняют подавлением эмоций и отсутствием выхода стресса. Ходит человек, улыбается, адекватно себя ведёт, а потом, необъяснимо, резко, вытворяет что-нибудь эдакое.

Смысл простой: человек ничего не делает со стрессом, т.е. с тем, что ему не нравится.
В управленческой истерике примерно так же, если она вызвана накопленным «закрыванием глаз» на косяки подчиненных. Тут простил, там не проговорил недовольство, здесь не скорректировал – считая, что, вроде, и так всё понятно, и люди должны по умолчанию, по своему собственному разумению действовать хорошо и правильно.

Молчать – опасная тактика. Считать, что подчиненные всё понимают – ещё опаснее. В этом смысле психология подчиненных – как у детей, или даже у собак. Если не заругался, значит, ничего страшного не произошло. Если не закричал, не натыкал носом, значит – граница где-то дальше, и можно продолжать в том же духе.

Истерика, вызванная крупным провалом – это, увы, следствие управленческой импотенции. Так бывает часто – руководитель не руководит процессом или проектом, а просто сидит рядом и ждёт, что получится. Не потому, что он такой эксперимент поставил, а потому, что не умеет руководить. Не знает, как понимать прогресс, всё ли идёт хорошо, нужны ли корректировки, да и как их делать. Поэтому делает вид, что у него всё под контролем до тех пор, пока не произойдет провал.

Когда произошел провал, в большинстве случаев ещё что-то можно исправить – но для этого, опять же, надо не быть управленческим импотентом. Нужно и знать менеджмент, и уметь руководить. Но если человек не умеет, ему остаётся только истерить – орать, ругаться, увольнять, раздавать бессмысленные указания. Самое традиционное – «предоставьте мне план мероприятий по недопущению подобной ситуации в дальнейшем». Зачем тебе этот план, дружище, если ты ничего делать не умеешь?

Есть еще непрогнозируемый, неуправляемый вариант истерики – внешняя причина. Нелады с вышестоящим начальством, с женой, соседом, алкашами у магазина, ДТП и т.д. Вообще, считается, что руководитель должен прятать ненужные эмоции и уметь изображать нужные, но это, опять же, приведёт к управленческой истерике первого типа. Все мы люди.
Что делать с управленческой истерикой?

Подчиненным рецепт один – молча пережить. Нет никакого смысла спорить, убеждать, оправдываться, нападать – это лишь разовьёт истерику, даст ей повод, топливо, цель и смысл. Отчасти, сопротивление истерике будет её оправдывать – раз спорят, значит я прав, задел за живое, нащупал слабость, реальные нарушения.

Молчать и исполнять. Надо начальнику мероприятия? Напишите. Не надо делать этого сломя голову – ровно в тот срок, который он просит. Чётко, аккуратно, достаточно подробно, но без конкретики. Надо ему анализ причин? Напишите что-нибудь обтекаемое. Хочет обсудить – жалко, что ли.

Главное, помните – через день-два истерика пройдет, и вам важно за это время не превратить игру в реальность. Например, если вы со слишком большим рвением напишете мероприятия по устранению, и через час после истерики вышлете их начальнику, то высока вероятность, что вас и назначат эти мероприятия исполнять. Если вы действительно этого хотите, то вперёд, в полный рост.

Но начальнику-то ваши мероприятия не нужны. Через два дня он забудет свою истерику, как запой, и за всё, что происходило в этот период, ему будет немного стыдно. В том числе, за мероприятия, под которые вы его подписали. Да, да, вы, а не он. Считайте, что уговорили пьяного человека взять микрозайм – это несложно, пока он в изменённом состоянии сознания, но расплачиваться-то ему. Он будет обязан теперь следить за исполнением выданных поручений, выслушивать ваши доклады, читать письма и т.д. Так вы его до новой, внеочередной истерики доведёте.

Руководителям совет простой: учитесь руководить. Тогда повода для истерик просто не будет.

Учитесь понимать состояние длительных проектов и процессов в любой момент времени. Для этого есть масса инструментов – как минимум контроллинг и гибкие методологии, коих сейчас пруд пруди. Их нужно не просто знать, в теории, но и уметь применять на практике. Например, можно поставить себе простую цель их использования: всегда понимать, в каком состоянии процесс или проект. Успеваем или нет, надо корректировать или всё идёт по плану. Ну или понимать это не всегда, а с понятной вам периодичностью – например, раз в неделю.

Ну и проще относиться надо к работе, как мне кажется. Это просто игра такая. Как любая другая игра, в которую играешь с увлечением, она даёт и положительные, и отрицательные эмоции. Но если вы от проигрыша начинаете крушить мебель и людей, то это уже проблема, и надо лечить абстиненцию.

ссылка на оригинал статьи https://habr.com/ru/post/508906/

Блокировка двойного клика. Велосипед?

С чего началось

В очередной раз копаясь с легаси кодом и борясь с утечкой контекста я сломал в приложении блокировку двойного клика на кнопке. Пришлось искать, что именно я сломал и как это было реализовано. С учетом того, что в основном для подобной блокировки предлогается либо отключать сам UI элемент либо просто игнорировать последующие клики в течении небольшого промежутка времени, существующее решение показалось мне довольно интересным с т.з. компоновки кода. Но оно все равно требовало создавать списки кнопок и писать довольно много поддерживающего кода. Создать экземпляр класса, который будет хранить список элементов, заполнить его, в каждом обработчике клика вызывать три метода. В общем, много мест, где можно что-нибудь забыть или перепутать. А я не люблю ничего помнить. Каждый раз когда мне кажется что я что-то помню, оказывается, что либо я помню неправильно, либо кто-то это уже переделал по другому.

Оставим за скобками вопрос о том, правильно ли так делать или надо просто качественно выносить реинтерабельные обработчики в бэкграундные потоки. Просто будем делать очередной велосипед, может быть немного более удобный.

В общем, природная лень заставила задуматься, а можно ли сделать без всего этого? Ну чтобы заблокировал кнопку и забыл. А оно там само дальше будет работать как надо. Сначала появилась мысль, что наверняка уже есть какая-нибудь библиотека, которую можно подключить и надо будет вызывать всего один метод типа — sdelayMneHorosho().

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

Например:

Раз

Два

И так далее…

Еще, наверное можно просто выключать элемент первой строкой в обработчике, а потом включать. Проблема только в том, что это потом наступает не всегда тривиально и в этом случае необходимо добавлять вызов кода «включения» в конце всех вариантов исполнения, к которым может привести нажатие кнопки. Неудивительно, что такие решения у меня с ходу не нагуглились. Очень уж запутанные они и поддерживать их крайне сложно.

Захотелось сделать проще, универсальнее, и чтобы помнить надо было как можно меньше.

Решение из проекта

Как я уже говорил, существующее решение было интересно скомпоновано, хотя и обладало всеми недостатками существующих решений. По крайней мере это было отдельный простой класс. Также он позволял делать разные списки отключаемых элементов, хотя я и не уверен, что в этом есть смысл.

Оригинальный класс для блокировки двойного нажатия

public class MultiClickFilter {     private static final long TEST_CLICK_WAIT = 500;     private ArrayList<View> buttonList = new ArrayList<>();     private long lastClickMillis = -1;      // User is responsible for setting up this list before using     public ArrayList<View> getButtonList() {         return buttonList;     }      public void lockButtons() {         lastClickMillis = System.currentTimeMillis();         for (View b : buttonList) {             disableButton(b);         }     }      public void unlockButtons() {         for (View b : buttonList) {             enableButton(b);         }     }      // function to help prevent execution of rapid multiple clicks on drive buttons     //     public boolean isClickedLately() {         return (System.currentTimeMillis() - lastClickMillis) < TEST_CLICK_WAIT;  // true will block execution of button function.     }      private void enableButton(View button) {         button.setClickable(true);         button.setEnabled(true);     }      private void disableButton(View button) {         button.setClickable(false);         button.setEnabled(false);     } } 

Пример использования:

public class TestFragment extends Fragment {  	<======= Кусь ========>  	private MultiClickFilter testMultiClickFilter = new MultiClickFilter();  	@Override 	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 		<======= Кусь ========>  		testMultiClickFilter.getButtonList().add(testButton); 		testMultiClickFilter.getButtonList().add(test2Button);  		<======= Кусь ========>  		testButton.setOnClickListener(new View.OnClickListener() { 			@Override 			public void onClick(View v) { 				if (testMultiClickFilter.isClickedLately()) { 					return; 				}  				testMultiClickFilter.lockButtons(); 				startTestPlayback(v); 				testMultiClickFilter.unlockButtons(); 			} 		});  		test2Button.setOnClickListener(new View.OnClickListener() { 			@Override 			public void onClick(View v) { 				if (testMultiClickFilter.isClickedLately()) { 					return; 				}  				testMultiClickFilter.lockButtons(); 				loadTestProperties(v); 				testMultiClickFilter.unlockButtons(); 			} 		});  		<======= Кусь ========> 	} 	 	<======= Кусь ========> } 

Класс небольшой и в принципе понятно что он делает. В двух словах, для того, чтобы заблокировать кнопку на какой-нибудь активити или фрагменте, нужно создать экземпляр класса MultiClickFilter и заполнить его список UI элементами, которые надо блокировать. Можно сделать несколько списков, но в этом случае обработчик каждого элемента должен «знать» какой экземпляр «кликфильтра» дергать.

Кроме того, он не позволяет просто проигнорировать клик. Для этого обязательно надо заблокировать весь список элементов, а потом, следовательно, его обязательно надо разблокировать. Это приводит к дополнительному коду который надо добавить в каждый обработчик. Да и в примере я бы метод unlockButtons поместил в блок finally, а то мало ли… В общем, это решение вызывает вопросы.

Новое решение

В общем, понимая, что наверное не будет какой-то серебряной пули, в качестве исходных посылок было принято:

  1. Разделять списки блокируемых кнопок не целесообразно. Ну не смог я придумать никакого примера, требующего такого разделения.
  2. Не отключать элемент (enabled/clickable) чтобы сохранить анимации и вообще живость элемента
  3. Блокировать клик в любом обработчике, который для этого предназначен, т.к. предполагается, что адекватный пользователь не кликает куда попало как из пулемета, а для предотвращения случайного «дребезга» достаточно просто отключить обработку кликов на несколько сотен миллисекунд «для всех»

Значит, в идеале, у нас должна быть одна точка в коде, где происходит вся обработка и один метод, который будет дергаться из любого места проекта в любом обработчике и будет блокировать обработку повторных нажатий. Предположим, что наш UI не подразумевает, что пользователь кликает чаще чем два раза в секунду. Не, если это необходимо, то видимо придется отдельно уделять внимание производительности, у нас же случай простой, чтобы дрожащими от восторга пальцами нельзя было уронить приложение на нереинтерабельной функции. А также, чтобы не надо было каждый раз париться над оптимизацией производительности простого перехода с одной активити на другую или каждый раз мелькать диалогом прогресса.

Все это у нас будет работать в главном потоке, так что нам не надо беспокоится о синхронизации. Также, фактически, мы можем перенести проверку на то, надо ли нам обработать клик или проигнорировать его, в этот самый метод. Ну, если получится, то можно было бы и сделать интервал блокировки настраиваемым. Так чтобы в совсем нехорошем случае можно было увеличить интервал для конкретного обработчика.

Возможно ли это?

Реализация оказалась на удивление простой и лаконичной

package com.ai.android.common;  import android.os.Handler; import android.os.Looper;  import androidx.annotation.MainThread;  public abstract class MultiClickFilter {     private static final int DEFAULT_LOCK_TIME_MS = 500;     private static final Handler uiHandler = new Handler(Looper.getMainLooper());      private static boolean locked = false;      @MainThread     public static boolean clickIsLocked(int lockTimeMs) {         if (locked)             return true;          locked = true;          uiHandler.postDelayed(() -> locked = false, lockTimeMs);          return false;     }      @MainThread     public static boolean clickIsLocked() {         return clickIsLocked(DEFAULT_LOCK_TIME_MS);     } } 

Пример использования:

public class TestFragment {  	<======= Кусь ========>  	private ListView devicePropertiesListView;  	@Override 	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 		devicePropertiesListView = view.findViewById(R.id.list_view); 		devicePropertiesListView.setOnItemClickListener(this::doOnItemClick);  		<======= Кусь ========>  		return view; 	}  	private void doOnItemClick(AdapterView<?> adapterView, View view, int position, long id) { 		if (MultiClickFilter.clickIsLocked(1000 * 2)) 			return;  		<======= Кусь ========> 	}  	<======= Кусь ========> } 

По большому счету теперь надо просто добавить в проект класс MultiClickFilter и в начале каждого обработчика клика проверять не заблокирован ли он:

        if (MultiClickFilter.clickIsLocked())             return; 

Если клик подлежит обработке, то установится блокировка на заданное время (или по умолчанию). Метод позволят не думать о списках элементов, не строить сложные проверки и не управлять доступностью UI элементов вручную. Предлагаю обсудить в комментариях эту реализацию, возможно есть лучшие варианты?

ссылка на оригинал статьи https://habr.com/ru/post/508908/

Как смотреть WWDC 2020, если ты не разработчик

Многим кажется, что WWDC — праздник только для разработчиков, и если ты дизайнер или маркетолог, то тебе там нечего ловить. На самом деле это не совсем так. Действительно, большая часть будет актуальна только разработчикам, но многое будет полезно не только им.

В этой статье расскажу, как сориентироваться в веренице всего и поделюсь нашим опытом работы с материалами конференции. Но для начала немного поговорим про то, что же такое WWDC.

Даб-даб ди си

Именно так произносится название WWDC или просто «Даб-даб». Это конференция для тех, кому не безразлична судьба продуктов Apple. С 1983 года они знакомят разработчиков со всего мира с программными новинками и технологиями. А также подводят итоги прошедшего года и делятся планами на следующий.

У большинства людей конференция ассоциируется с Keynote, на которой Apple рассказывает о программных новинках, для кого-то ещё и с Apple Design Awards, где награждают лучшие приложения по версии экспертов эппла. Но самое интересное начинается дальше. В течении одной недели абсолютно закрытая компания Apple приоткрывает завесу тайны над разработками и даёт пообщаться с инженерами и сотрудниками. Для этого проводится около 100 сессий, на которых инженеры рассказывают про различные аспекты связанные с анонсированными новинками и про то, как правильно разрабатывать свои продукты с их учётом. Если сессий недостаточно или есть вопросы, то на лабах можно задавать инженерам любые вопросы, связанные со своими проектами или с только анонсированными технологиями. Также проходит много событий, концертов встреч и подкастов вживую.

Если хочется окунуться в атмосферу, то можно почитать текстовую трансляцию Егора Толстого про поездку на WWDC 2017 года или ребят из RedMadRobot в прошлом году.

Но если раньше для этого нужно было испытать удачу и получить билет, прилететь в США, то в 2020 конференция стала ближе как никогда. И Apple подошли к этому основательно.

keynote_epic

Только посмотрите Keynote и Platform state of the Union, который из стандартной презентации со сцены и сменой ведущих сменился в шоу с эпичными переходами. Остальные сессии хоть и не такие эпичные, но стали заметно живее и теперь лучше смотрятся онлайн.

Где смотреть?

Но для начала определимся как смотреть.
В этом году Apple неплохо обновили своё приложение для разработчиков и добавили возможность просматривать сессии прямо в нём. Если по каким-то причинам официальное приложение не подходит, то все ещё актуально приложение WWDC для macOS.

Если приложения не для вас, то в браузере всё также доступно и все сессии можно посмотреть на странице в портале разработчиков.

Как правило, лекции содержат довольно много общей информации, поэтому большую часть можно смотреть в ускоренном формате или ограничиться просмотром слайдов, конспектами и транскриптами.

  • На сайте https://asciiwwdc.com собираются текстовые версии докладов. (Транскрипты обновляются обычно в течении месяца после завершения очередной WWDC)
  • Сообщество делится конспектами сессий на GitHub, например https://github.com/Blackjacx/WWDC и https://wwdcnotes.com

Что смотреть?

Чтобы не привязываться к конкретным ролям пройдёмся по основным этапам жизни любой фичи проекта:

  • Идея и гипотезы.
  • Проектирование и прототипирование.
  • Разработка и контроль качества.
  • Бета тестирования и релиз.

Для каждой мы подобрали топ наиболее интересных и полезных по нашему мнению сессий. Поехали!


Идея / гипотеза

Разработка фичи начинается с идеи или гипотезы. Следующие сессии помогут познакомиться с основными нововведениями, которые можно будет использовать в продуктах.

Проектирование и планирование

После того, как идея выживает, обретает смысл и начинает формализоваться требованиями её важно спроектировать и спрототипировать.

Всем кто отвечает и за эту стадию работы над проектом рекомендуем посмотреть:

Также все материалы про дизайн уже с любовью собраны Apple:

Разработка и контроль качества

Не хочется сильно подробно останавливаться на разработчиках, про WWDC для разработчиков традиционно много будет написано статей и гайдов. Уже появился неплохой обзор от Пола Хадсона и много ссылок в его репозитории на GitHub.

Если интересно узнать больше деталей и нововведений, которые не показали на сессиях, то начните с изучения изменений в документации, также интересно смотреть изменения в SDK, если хочется более структурированных данных.

Сессии про тестирование собраны, как и сессии для дизайнеров, в отдельный раздел и в подборке «The suite life of testing».

Бета тестирования и релиз

После разработки фичи, её следует проверять на небольшой аудитории и параллельно готовить маркетинговые материалы для релиза, описание приложений и компании.

Также на этой WWDC особое внимание было уделено работе Entreprise. Под это направление отвели отдельную подборку и анонсировали разные интересный фичи в iOS. Чего только стоит Local Push Connectivity, благодаря которому уведомления можно будет рассылать в рамках локальной сети.

Также будет полезен обновлённый раздел с гайдлайнами по ревью.

Наш топ сессий на эти тему тестирования:

Релиз – только начальный этап в разработке фичи. На этом этапе собираем аналитику и обратную связь от пользователей, фиксим критические баги, смотрим метрики, которые показывают, что критерии успешности фичи выполняются или не выполняются. В итоге появляются новые идеи и гипотезы, которые начинают новый виток в нашем цикле разработки.

Today@WWDC

Нововведение этого сезона – небольшие обзорные видео на каждый день WWDC. Благодаря которым за 8 минут можно бегло посмотреть что происходило в течении прошедшего дня.

А что, если нужно поддерживать старые iOS?

Это отличный повод пересмотреть сессии с WWDC 2-х летней давности.

Как правило, внедрение новинок в проекты компаний замедляются из-за поддержки старых версий iOS, но в то же время это даёт повод пересмотреть сессии и фичи прошлых лет и улучшить текущие продукты. В сети и на хабре можно найти много гайдов на эту тему. Вот неплохие на мой взгляд:

  • Гайды по просмотру WWDC 2016-2019 от UseYourLoaf.
  • Подборка нововведений WWDC 2019 от Патрика Балестры.

Как смотреть?

Количество информации очень много, а с каждой неделей после WWDC её становится ещё больше, так как появляется огромное количество статей и обзоров на вышедшие технологии.

Surf находится между двумя стихиями: большими, неповоротливыми бизнесами с одной стороны и ветряным, переменчивым миром новых технологий и платформ с другой. Именно поэтому нам нужно постоянно искать баланс, быть в курсе того, что происходит вокруг и предлагать заказчикам улучшения, которые помогут им находиться на волне.
Для этого, помимо постоянной разработки фич, важно стараться смотреть и пробовать новые технологии и подходы.

Чтобы не утонуть в потоке сессий и не смотреть всем одно и то же, темы распределяются по интересам между разработчиками. Для этого у нас есть отдельный канал в Slack и сводная таблица в Google Docs.

Также, мы дополнительно отранжировали темы по релевантности к нашим проектам и платформам.

После просмотра сессии пишется небольшое резюме в Slack, оценивается полезность, чем интересен и что хотелось бы применить на практике.

Пример обзора
Пример обзора

После просмотра большего числа сессий собираемся всей iOS командой, обсуждаем темы и примеряем к нашим проектам фичи и технологии, которые показали и отсортировали в зависимости от того, насколько они ложатся на наш текущий портфель проектов и сферу интересов.

Часть результатов прошлогоднего брейншторма
Часть результатов прошлогоднего брейншторма

Обсудить iOS командой — это, конечно же, хорошо, но без поддержки отдела продаж, дизайнеров и аналитиков, новинки вряд ли дойдут до релиза. Для этого мы проводим общестудийную встречу, на которой без технических деталей рассказываем, обсуждаем новинки и примеряем к своим проектам.

На вышеописанные процедуры в прошлом году у нас ушло около месяца: 2 недели на просмотр сессий, 1 неделя на обсуждение и отбор тем, пара дней на проведение общего мозгового штурма и оформление результатов. Тем самым мы продлили праздник WWDC у себя в компании ещё на один месяц после его завершения, получили на выходе большое количество идей для новых фич для интеграции в наши проекты и планов для внутренних исследований.

Надеемся, что наш гайд поможет в знакомстве с новинками представленными Apple и показала, что WWDC может быть полезна не только разработчикам. А если ты разработчик, то отправь его или расскажи про сессии своим коллегам.

Желаем приятного просмотра!

ссылка на оригинал статьи https://habr.com/ru/company/surfstudio/blog/508848/

Лабаем на MIDI-клавиатуре в Angular

Web MIDI API — интересный зверь. Хоть он и существует уже почти пять лет, его все еще поддерживает только Chromium. Но это не помешает нам создать полноценный синтезатор в Angular. Пора поднять Web Audio API на новый уровень!

Ранее я рассказывал про декларативную работу с Web Audio API в Angular.

Программировать музыку, конечно, весело, но что если мы хотим ее играть? В 80-е годы появился стандарт обмена сообщениями между электронными инструментами — MIDI. Он активно используется и по сей день, и Chrome поддерживает его на нативном уровне. Это значит, что, если у вас есть синтезатор или MIDI-клавиатура, вы можете подключить их к компьютеру и считывать то, что вы играете. Можно даже управлять устройствами с компьютера, посылая исходящие сообщения. Давайте разберемся, как это сделать по-хорошему в Angular.

Web MIDI API

В интернете не так много документации на тему этого API, не считая спецификации. Вы запрашиваете доступ к MIDI-устройствам через navigator и получаете Promise со всеми входами и выходами. Эти входы и выходы — еще их называют портами — являются нативными EventTargetами. Обмен данными осуществляется через MIDIMessageEventы, которые содержат Uint8Array сообщения. В каждом сообщении не более 3 байт. Первый элемент массива называется status byte. Каждое число означает конкретную роль сообщения, например нажатие клавиши или движение ползунка параметра. В случае нажатой клавиши второй байт отвечает за то, какая клавиша нажата, а третий — как громко нота была сыграна. Полное описание сообщений можно подсмотреть на официальном сайте MIDI. В Angular мы работаем с событиями через Observable, так что первым шагом станет приведение Web MIDI API к RxJs.

Dependency Injection

Чтобы подписаться на события, мы сначала должны получить MIDIAccess-объект, чтобы добраться до портов. navigator вернет нам Promise, а RxJs превратит его для нас в Observable. Мы можем создать для этого InjectionToken, используя NAVIGATOR из @ng-web-apis/common. Так мы не обращается к глобальному объекту напрямую:

export const MIDI_ACCESS = new InjectionToken<Promise<MIDIAccess>>(    'Promise for MIDIAccess object',    {        factory: () => {            const navigatorRef = inject(NAVIGATOR);             return navigatorRef.requestMIDIAccess                ? navigatorRef.requestMIDIAccess()                : Promise.reject(new Error('Web MIDI API is not supported'));        },    }, );

Теперь мы можем подписаться на все MIDI-события. Можно создать Observable одним из двух способов:

  1. Создать сервис, который наследуется от Observable, как мы делали в Geolocation API
  2. Создать токен с фабрикой, который будет транслировать этот Promise в Observable событий

Поскольку в этот раз нам понадобится совсем немного преобразований, токен вполне подойдет. С обработкой отказа код подписки на все события выглядит так:

export const MIDI_MESSAGES = new InjectionToken<Observable<MIDIMessageEvent>>(    'All incoming MIDI messages stream',    {        factory: () =>            from(inject(MIDI_ACCESS).catch((e: Error) => e)).pipe(                switchMap(access =>                    access instanceof Error                        ? throwError(access)                        : merge(                              ...Array.from(access.inputs).map(([_, input]) =>                                  fromEvent(                                      input as FromEventTarget<MIDIMessageEvent>,                                      'midimessage',                                  ),                              ),                          ),                ),                share(),            ),    }, );

Если нам нужен какой-то конкретный порт, например если мы хотим отправить исходящее сообщение, достанем его из MIDIAccess. Для этого добавим еще один токен и подготовим фабрику для удобного использования:

export function outputById(id: string): Provider[] {    return [        {            provide: MIDI_OUTPUT_QUERY,            useValue: id,        },        {            provide: MIDI_OUTPUT,            deps: [MIDI_ACCESS, MIDI_OUTPUT_QUERY],            useFactory: outputByIdFactory,        },    ]; }  export function outputByIdFactory(    midiAccess: Promise<MIDIAccess>,    id: string, ): Promise<MIDIOutput | undefined> {    return midiAccess.then(access => access.outputs.get(id)); }

Кстати, вы знали, что нет необходимости спрэдить массив Provider[], когда добавляете его в метаданные? Поле providers декоратора @Directive поддерживает многомерные массивы, так что можно писать просто:

providers: [   outputById(‘someId’),   ANOTHER_TOKEN,   SomeService, ]

Если вам интересны подобные практичные мелочи про Angular — приглашаю почитать нашу серию твитов с полезными советами.

Аналогичным образом можно добывать и входные порты, а также запрашивать их по имени.

Операторы

Для работы с потоком событий нам потребуется создать свои операторы. В конце концов, мы же не хотим каждый раз ковыряться в исходном массиве данных.
Операторы можно условно разделить на две группы:

  • Фильтрующие. Они отсеивает события, которые нас не интересуют. Например, если мы хотим слушать только сыгранные клавиши или ползунок громкости.
  • Преобразующие. Они будут преобразовывать значения для нас. Например, оставлять только массив данных сообщения, отбрасывая остальные поля события.

Вот так мы можем слушать события с определенного канала:

export function filterByChannel(    channel: MidiChannel, ): MonoTypeOperatorFunction<MIDIMessageEvent> {    return source => source.pipe(filter(({data}) => data[0] % 16 === channel)); }

Status byte организован группами по 16: 128—143 отвечают за нажатые клавиши (noteOn) на каждом из 16 каналов. 144—159 — за отпускание зажатых клавиш (noteOff). Таким образом, если мы возьмем остаток от деления этого байта на 16 — получим номер канала.

Если нас интересуют только сыгранные ноты, поможет такой оператор:

export function notes(): MonoTypeOperatorFunction<MIDIMessageEvent> {    return source =>        source.pipe(            filter(({data}) => between(data[0], 128, 159)),            map(event => {                if (between(event.data[0], 128, 143)) {                    event.data[0] += 16;                    event.data[2] = 0;                }                 return event;            }),        ); }

Некоторые MIDI-устройства отправляют явные noteOff-сообщения, когда вы отпускаете клавишу. Но некоторые вместо этого отправляют noteOn сообщение с нулевой громкостью. Этот оператор нормализует такое поведение, приводя все сообщения к noteOn. Мы просто смещаем status byte на 16, чтобы noteOff-сообщения перешли на территорию noteOn, и задаем нулевую громкость.

Теперь можно строить цепочки операторов, чтобы получить стрим, который нам нужен:

readonly notes$ = this.messages$.pipe(   catchError(() => EMPTY),   notes(),   toData(), );  constructor(   @Inject(MIDI_MESSAGES)   private readonly messages$: Observable<MIDIMessageEvent>, ) {}

Пора применить все это на практике!

Создаем синтезатор

С небольшой помощью библиотеки для Web Audio API, которую мы обсуждали ранеесоздадим приятно звучащий синтезатор всего за пару директив. Затем мы скормим ему ноты, которые играем через описанный выше стрим.

В качестве отправной точки используем последний кусок кода. Чтобы синтезатор был полифоническим, нужно отслеживать все сыгранные ноты. Для этого воспользуемся оператором scan:

readonly notes$ = this.messages$.pipe(   catchError(() => EMPTY),   notes(),   toData(),   scan(     (map, [_, note, volume]) => map.set(note, volume), new Map()   ), );

Чтобы звук не прерывался резко и не звучал всегда на одной громкости, создадим полноценный ADSR-пайп. В прошлой статье была его упрощенная версия. Напомню, идея ADSR — менять громкость звука следующим образом:

Чтобы нота начиналась не резко, удерживалась на определенной громкости, пока клавиша нажата, а потом плавно затухала.

@Pipe({     name: 'adsr', }) export class AdsrPipe implements PipeTransform {     transform(         value: number,         attack: number,         decay: number,         sustain: number,         release: number,     ): AudioParamInput {         return value             ? [                   {                       value: 0,                       duration: 0,                       mode: 'instant',                   },                   {                       value,                       duration: attack,                       mode: 'linear',                   },                   {                       value: sustain,                       duration: decay,                       mode: 'linear',                   },               ]             : {                   value: 0,                   duration: release,                   mode: 'linear',               };     } }

Теперь, когда мы нажимаем клавишу, громкость будет линейно нарастать за время attack. Затем она убавится до уровня sustain за время decay. А когда мы отпустим клавишу, громкость упадет до нуля за время release.

С таким пайпом мы можем набросать синтезатор в шаблоне:

<ng-container   *ngFor="let note of notes | keyvalue; trackBy: noteKey" >   <ng-container     waOscillatorNode     detune="5"     autoplay     [frequency]="toFrequency(note.key)"    >     <ng-container        waGainNode        gain="0"       [gain]="note.value | adsr: 0:0.1:0.02:1"     >       <ng-container waAudioDestinationNode></ng-container>     </ng-container>   </ng-container>    <ng-container     waOscillatorNode     type="sawtooth"     autoplay      [frequency]="toFrequency(note.key)"   >     <ng-container        waGainNode       gain="0"       [gain]="note.value | adsr: 0:0.1:0.02:1"     >       <ng-container waAudioDestinationNode></ng-container>       <ng-container [waOutput]="convolver"></ng-container>     </ng-container>   </ng-container> </ng-container> <ng-container   #convolver="AudioNode"   waConvolverNode   buffer="assets/audio/response.wav" >   <ng-container waAudioDestinationNode></ng-container> </ng-container>

Мы перебираем собранные ноты с помощью встроенного keyvalue пайпа, отслеживая их по номеру сыгранной клавиши. Затем у нас есть два осциллятора, играющих нужные частоты. А в конце — эффект реверберации с помощью ConvolverNode. Довольно нехитрая конструкция и совсем немного кода, но мы получим хорошо звучащий, готовый к использованию инструмент. Попробуйте демо в Chrome:

https://ng-web-apis.github.io/midi

Если у вас нет MIDI-клавиатуры — можете понажимать на ноты мышкой.

Живое демо доступно тут, однако браузер не позволит получить доступ к MIDI в iframe: https://stackblitz.com/edit/angular-midi

Заключение

В Angular мы привыкли работать с событиями с помощью RxJs. И Web MIDI API не сильно отличается от привычных DOM-событий. С помощью пары токенов и архитектурных решений мы смогли с легкостью добавить поддержку MIDI в наше Angular приложение. Описанное решение доступно в виде open-source библиотеки @ng-web-apis/midi. Она является частью большого проекта, под названием Web APIs for Angular. Наша цель — создание легковесных качественных оберток для использования нативного API в Angular приложениях. Так что если вам нужен, к примеру, Payment Request API или Intersection Observer — посмотрите все наши релизы.

Если вам любопытно, что же такого интересного можно сделать на Angular при помощи Web MIDI API — приглашаю вас научиться играть на клавишах в личном проекте Jamigo.app

ссылка на оригинал статьи https://habr.com/ru/company/tinkoff/blog/508788/