Съедобный джаз
К выставке итальянского кулинарного и национального искусства решили сделать красочные плакаты и соеденить всё это с джазом. Вышло весело и динамично. Джаз, еда и итальянцы)))
К выставке итальянского кулинарного и национального искусства решили сделать красочные плакаты и соеденить всё это с джазом. Вышло весело и динамично. Джаз, еда и итальянцы)))
С момента выхода первой части статьи из рубрики «сам себе экосистема» прошёл уже практически год! За это время, мы успели с вами реализовать клиенты VK и YouTube, которые работают на Android 2.2+, а также на Windows Phone 8, написать небольшую 2D-игру с нуля весом менее 1Мб, которая работает практически везде и довести существующее приложение до ума, дабы оно работало даже на смартфоне с дисплеем 240x320! Но на дворе 2024 год, люди стремительно переходят из соц. сетей в продвинутые мессенджеры и уже сложно себе представить современного человека, который не пользовался бы «телегой» или даже «вайбером» в качестве основного средства общения. Поэтому я решил реализовать клиент Telegram на смартфоне 14-летней давности на базе официальной реализации MTProto от команды Telegram — TDLib. Сегодня мы с вами: узнаем новые причины мотивации вернуть в строй смартфоны прошлых лет, напишем на C# реле-сервер, который обрабатывает пакеты MTProto и кодирует их в простой текстовый формат датасетов, который можно моментально обработать даже при нестабильном GPRS-соединении на 21-летнем Siemens C60, а также узнаем о разработке миниатюрных Android-приложений на базе «голого» API-системы, которые не тянут за собой никаких зависимостей, в том числе и AppCompat/androidx. Интересно? Тогда жду вас под катом!
На дворе уже стукнул 2024 год, современные смартфоны предлагают какие-то немыслимые мощности относительно тех, которые когда-то были в первых Android-девайсах. Сейчас за сотню баксов можно купить смартфон с хорошей 1080p IPS-матрицей, 4Гб ОЗУ и 8-ядерным шустрым чипсетом, который вполне способен плавно тянуть даже стремительно «жиреющие» на ресурсы клиенты социальных сетей, банков и прочие необходимые в повседневной жизни приложения.
И казалось бы: всё хорошо, покупай себе редмик раз в год или айфон раз в несколько лет и наслаждайся всеми прелестями работы современных приложений…
Для многих людей смартфон — это лишь инструмент, повседневный компаньон, который помогает облегчить выполнение каких-то задач. Им совершенно не важно, как он выглядит, как ощущается в руках, какой у него дисплей и железо «под капотом», лишь бы работал да и нормально. Но есть и другая категория людей, для которых телефоны, смартфоны и любые портативные гаджеты — это не просто утилитарный девайс, а настоящее инженерное произведение искусства, с которого буквально сдувают пылинки и стараются до последнего пользоваться ими как повседневными устройствами. Хотите пример? Смотрите ниже:
Фактически, среди современных смартфонов по сути и нет представителей такого нынче вымершего форм-фактора, как сайдслайдеры с физической QWERTY-клавиатурой, боковые раскладушки с двумя дисплеями и даже из QWERTY-моноблоков есть только смартфоны от Unihertz. Даже среди моноблоков с тачскринами нет никакого разнообразия, лишь без-рамочные одинаковые девайсы за исключением устройств от Sony.
Galaxy S Plus
Раньше меня часто спрашивали, мол, да как ты вообще можешь пользоваться смартфоном 10-летней давности, на котором давно нет официальных клиентов популярных сервисов и только недавно, с развитием блога, мне перестали задавать этот вопрос, поняв, что это бесполезно — ведь это дело принципа и порыва энтузиазма! Смотрите сами: у нас уже есть простенькие, но вполне рабочие клиенты ВК, YouTube, сейчас я допиливаю клиент «Сбера» на СМСках, реализую карты OpenStreetMap (правда пока без адекватной навигации), а в будущем планирую написать приложение для мониторинга погоды и трекинга посылок. Кроме того, в рамках этой статьи мы реализуем с вами клиент Telegram: так чем же это не функционал современного смартфона?
Но хорошо, с функционалом разобрались, однако для многих читателей слова «старый смартфон» это прямые синонимы «тормозной смартфон», мол «фуу, да как можно пользоваться этим тормозным кирпичом, он же лагает в последней версии моей ВКшечки!». Но давайте поставим вопрос ребром: может, это не столько девайсы немощные, сколько сами приложения, с кодовой базой, которая тянется более 10 лет, откровенно жиреют, обрастают костылями и хаками после далеко не одного поколения программистов, которые над ними работали? :) Один, вот, предпочитал пользоваться чистым AppCompat'ом, другой решил притащить зависимость, которая, например, оптимизирует виртуализацию ListView, третий решил заменить всю сериализацию Json со встроенных классов в Android на что-то стороннее и реализовал это костылями и вот так, по чуть-чуть изначально оптимальный и шустрый код превращается в неповоротливое УГ, которое не рефакторили кучу лет.
На видео Galaxy Pocket Neo — очень дешёвый Android-смартфон из 2011 года с 1-ядерным чипсетом на ~800МГц и 256Мб ОЗУ. При этом всём, Android софтварно рисует все анимации на процессоре, без участия GPU.
А значит у стареньких девайсов всё равно есть шанс быть полезными и стать полноценными повседневными смартфонами даже спустя более чем десять лет после выхода! И в сегодняшнем материале, я вам расскажу об особенностях разработки самопального клиента Telegram с собственным прокси-сервером, которое концептуально допускает реализацию даже на кнопочном Siemens C60 2003 года. Как? Читаем ниже!
В отличии от ВК (который разрабатывали те же самые люди, что и Telegram), API которого построено на базе REST-запросов и концепции Longpolling'а для моментального получения событий с сервера, Telegram построен на базе собственного протокола под названием MTProto, который может работать поверх любого «транспорта» (протокола нижнего уровня) — TCP, HTTP, WebSocket и т.п. Сам по себе MTProto в современном виде, разработка прожженного математика Николая Дурова и его команды — протокол относительно сложный для реализации «на коленке» и в первую очередь требует довольно серьезного понимания принципов работы современной криптографии, да и документирован он всё ещё не особо хорошо. Кроме того, у MTProto весьма интересный бинарный формат пакетов, эдакий велосипед Protobuf. В долгосрочной перспективе поддерживать свой велосипед MTProto может быть весьма проблематично, учитывая не самую лучшую документацию.
Но городить велосипед и не нужно, поскольку у команды Telegram есть официальная реализация MTProto — библиотека TDLib, которая инкапсулирует в себе не только детали реализации протокола, но и сетевой ввод/вывод и выбор транспорта, хранение базы данных сообщений и авторизации, автоматическую загрузку фото и видео, конвертация объектов из бинарного формата MTProto в JSON и полная многопоточность и частичная потоко-безопасность. С одной стороны это плюс — уже готовое решение для реализации клиента на новой поддерживаемой платформе, где есть OpenSSL (можно статически слинковать), zlib (линкуется статически), сокеты и файловый ввод/вывод, а также довольно неплохой механизм JSON-based API, которое позволяет использовать библиотеку в любом языке, который поддерживает вызов C-функций, а с другой и минус — библиотека довольно много весит, в одиночку прибавляя ~20Мб веса приложения для каждой архитектуры, у неё течёт память и у нее странный механизм получения данных с сервера (например, нельзя ответить на сообщение, зная его ID, если сообщение предварительно не загружено, при том что на сервере весь ответ — это просто ID, на какое сообщение прилетел ответ).
Понятное дело, что на стареньком смартфоне использовать оригинальный TDLib будет проблематичным — даже если собрать либы современным NDK и запилить JNI-интерфейс, библиотека «жрёт» много ОЗУ (20-100Мб «вхолостую», в зависимости от числа диалогов и частоты прилетающих событий, плюс со временем течет до 1-2Гб, если не использовать базу данных сообщений. Скорее всего, это косяк в реализации пулов, объекты из которых выгружаются при сбросе в базу, но не выгружаются при высоком потреблении ОЗУ) и уж тем-более TDLib не запустить на любимых кнопочных Java-сонериках! Поэтому я решил написать прокси-сервер, который отправляет команды, слушает ивенты TDLib и предоставляет REST-like API для клиентских программ, которые просто вызывают какой-либо метод, а в ответ получают простой и короткий строковой датасет только с необходимыми полями, весом до 10Кб (что позволяет его быстро загрузить даже с GPRS-интернетом), который можно быстро распарсить даже на преусловутом Siemens C60!
К сожалению, поскольку TDLib прожорлив, я не смогу захостить на своём сервере инстансы для читателей, которые хотят поюзать приложение, поэтому вам придется ставить и запускать сервер на своём VDS/компьютере с белым IP/роутере, если под него есть .NET Core :)
Клиентом же будет выступать Android-смартфон, где приложение будет фронтэндом данных с сервера. Ничего сложного на первое время нет: первое окно — это список диалогов, второе окно — список сообщений в диалоге + поле для написания сообщения, третье окно — информация о пользователе. Всё это я реализовал за три дня не-напряжной работы «на коленке».
Давайте же перейдем к реализации сервера!
Сервер я решил писать на C#, поскольку у .NET Core сейчас всё очень хорошо с кроссплатформенностью и производительностью. Его можно даже на Raspberry Pi запустить :)
Итак, какая-же архитектура такого сервера может быть? Программа инициализирует TDLib, начинает слушать её события в отдельном потоке, пока в основном потоке крутится HTTP-сервер, который обрабатывает каждый отдельный запрос с клиентского приложения. Почему синхронно? Потому что TDLib фактически не возвращает никаких идентификаторов для возвращаемых датасетов, дабы их можно было отличить друг от друга. Приведу пример: у нас есть метод getChatHistory, который возвращает n-сообщений. При этом TDLib сам определяет, сколько хочет сообщений вернуть (и в первый вызов возвращает одно сообщение вне зависимости от настрое и отправляем пакет message n-раз. При этом в пакете message нет какого-либо ID, который позволял бы ассоциировать текущий объект с какой-либо операцией. Увы!
Начинаем с коммуникации с TDLib. Для работы с библиотекой, мы будем использовать json-интерфейс. Для .NET есть биндинги через C++/CLI, но в таком случае, сервер не будет работать на Linux. Для работы с библиотекой хватит лишь три функции: CreateClientID, которая аллокейтит новый инстанс клиента, Send, которая асинхронно отправляет JSON-объект с командой, которую затем обработает TDLib и Receive, которая ждёт N-секунд и возвращает в виде ASCII-строки (!) JSON-объект с описанием события или данными после одного из запросов. За это у нас отвечает класс TDLibInterface, который объявляет PInvoke-методы для вызова нативных методов из библиотеки. .NET Core сам подгрузит библиотеку tdjson (причём на Linux он добавит ей префикс а-ля libtdjson.so, а на Windows загрузит tdjson.dll) и сам разберется с маршаллингом аргументов функций: например, string автоматически преобразует в const char*. Тем не менее, с const char* возвратами нужно быть аккуратнее — у меня был SIGSEGV, пока я ручками не конвертировал их в обычную строку.
З.Ы: На Пикабу нет отдельного тега для кода, а вставить листинги картинками я не могу из-за ограничения на 25 медиаэлементов. Так что листинги будут совсем без табов, но алгоритм их работы понять можно :)
[DllImport(Library, EntryPoint = "td_create_client_id", CallingConvention = CallingConvention.Cdecl)]
public static extern int CreateClientID();
[DllImport(Library, EntryPoint = "td_send", CallingConvention = CallingConvention.Cdecl)]
public static extern void Send(int id, string request);
[DllImport(Library, EntryPoint = "td_receive", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr RawReceive(double timeOut);
[DllImport(Library, EntryPoint = "td_execute")]
public static extern StringBuilder Execute(string request);
public static unsafe string Receive(double timeOut)
{
IntPtr str = RawReceive(timeOut);
return str != IntPtr.Zero ? new string((sbyte*)str.ToPointer()) : null;
}
Позволю себе чуточку критики в сторону TDLib. Во первых, почему нет s-версии функции с возможностью указать длину входной строки, а tdjson полагается исключительно на \0 в конце строки? Во вторых, почему const char*, а не wchar_t*? Сейчас юникод во входной строке приходится escape'ами превращать в \u-последовательности.
После этого, нам нужно написать обёртку над TDLib, которая будет вызывать для зарегистрированных событий специальные функции, называемые коллбэками. При этом закомментированный WriteLine снизу — это «дебаг» для того, чтобы узнать названия неизвестных мне ивентов :)
В каждом объекте, полученном с помощью receive, есть поле "@type", которое содержит в себе имя класса возвращаемого объекта. Первый же вопрос от читателей — почему я использую JObject с ручным дерганьем нужных полей и вручную пишу JSON в виде строковых литералов вместо нормальной сериализации/десериализации? Ответ прост: во-первых, для актуализации Data-классов придется писать кодогенератор из TL-схемы, а во-вторых иногда TDLib может возвращать немного разные объекты в JSON, из-за чего приходится мудрить с атрибутами на этих самых Data-классах, иначе десериализатор выбросит исключение. Это решается нормальными юнит-тестами на всех вариантах данных, но зачем себе в колени стрелять, если нужен конкретный фиксированный функционал и лишь малое число от всех полей, возвращаемых TDLib?
string recv = NativeInterface.Receive(10.0d);
if (recv != null)
{
JObject json = JObject.Parse(recv);
string type = json["@type"].ToString();
if (!handlers.ContainsKey(type))
{
//Console.WriteLine("Unknown event type: {0}", type);
continue;
}
handlers[type](recv, json);
}
Теперь переходим к самому интересному — обработке событий и реализации синхронного клиента, который позволяет без async/await просто запросить список сообщений и сразу же его получить (такой подход может быть полезен и юзерботам, которые не хотят размазывать стейты по всей программе). Почему без асинков? Честно сказать, мне они просто не нравятся: как привык к концепции wait/notify и коллбэков из Java, так их и юзаю всю жизнь :)
Сначала TDLib запрашивает параметры инициализации (стейт authorizationStateWaitTdlibParameters), затем если пользователь не авторизован — запрашивает номер телефона и код подтверждения (плюс дополнительные шаги для авторизации если они есть). В конце, TDLib возвращает стейт Ready, что означает готовность библиотеки к работе:
private void OnAuthState(string raw, JObject obj)
{
JObject authState = (JObject)obj["authorization_state"];
string type = authState["@type"].ToString();
if (type == "authorizationStateWaitTdlibParameters")
{
Console.WriteLine("Preparing TDLib parameters...");
NativeInterface.Send(InstanceID,
Utils.Format("{" +
"\"@type\": \"setTdlibParameters\", " +
"\"database_directory\": \"tdlib\", " +
"\"api_id\": {0}, " +
"\"api_hash\": \"{1}\", " +
"\"use_chat_info_database\": true," +
"\"use_file_database\": true," +
"\"use_message_database\": true," +
"\"system_language_code\": \"en\", " +
"\"device_model\": \"Phone\", " +
"\"application_version\": \"1.0\" " +
"}", APIId, APIHash));
}
if (type == "authorizationStateWaitPhoneNumber")
{
Console.WriteLine("Sending phone number");
NativeInterface.Send(InstanceID, Utils.Format("{\"@type\": \"setAuthenticationPhoneNumber\", \"phone_number\": \"{0}\" }", PhoneNumber));
}
if(type == "authorizationStateWaitCode")
{
NativeInterface.Send(InstanceID, Utils.Format("{\"@type\": \"checkAuthenticationCode\", \"code\": \"{0}\" }", WaitCode));
}
if(type == "authorizationStateReady")
{
Console.WriteLine("Authorized");
waitHandle.Set();
}
}
...
Client.AttachEventHandler("updateAuthorizationState", OnAuthState);
После этого, можно начать работу с данными. Обратите внимание, мой подход потоко-небезопасен, его нельзя дергать из нескольких потоков одновременно! В коде ниже, я вызываю метод для фетча сообщений, а затем в соответствующем коллбэке от TDLib обрабатываю данные (дабы статья не разрасталась на 20+ минут, я чуть урезал все листинги).
public List<Message> QueryMessagesInChat(long chatId, long lastMessage, int count)
{
messages.Clear();
requestMessageCount = count;
string json = Utils.Format("{\"@type\": \"getChatHistory\", \"chat_id\": \"{0}\", \"from_message_id\": {1}, \"limit\": {2} }", chatId, lastMessage, count);
NativeInterface.Send(InstanceID, json);
waitHandle.WaitOne();
return messages;
}
public User QueryUser(long userId)
{
string json = Utils.Format("{\"@type\": \"getUser\", \"user_id\": \"{0}\" }", userId);
NativeInterface.Send(InstanceID, json);
waitHandle.WaitOne();
return user;
}
Переходим к реализации самого сервера, для наших целей хватит обычного HttpListener. Сначала мы зарегистрируем все поддерживаемые методы и занесем их в ассоциативный список ключ-значение. Сами методы реализованы в виде делегатов, которые принимают лишь один аргумент — список параметров из строки запроса, а возвращают строку — все ответы, за исключением особых (связанных с загрузкой вложений) — текстовые.
private HttpListener listener;
private List<HttpMethodHandler> methods;
private ScheduledRestart restartManager;
private void AddMethod(HttpMethodHandler info)
{
if(info != null)
{
methods.Add(info);
Console.WriteLine("Registered method: {0}", info.Method.Name);
}
}
private void PrepareMethods()
{
AddMethod(Chats.QueryChats);
AddMethod(Chats.QueryMessages);
AddMethod(Chats.SendMessage);
AddMethod(Users.QueryUserInfo);
}
private void PrepareState()
{
// We should fetch dialog list due to TDLib nature of preloading-everything
Client.QueryChats(15);
}
public HttpServer()
{
listener = new HttpListener();
listener.Prefixes.Add("http://+:13377/");
Client = new SyncClient("test");
Client.Start();
Client.WaitUntilReady();
//restartManager = new ScheduledRestart(5);
//restartManager.Start();
methods = new List<HttpMethodHandler>();
PrepareMethods();
PrepareState();
}
...
public void Start()
{
listener.Start();
while(listener.IsListening)
{
HandleRequest(listener.GetContext());
}
}
Переходим к обработке запроса. Метод ищет, зарегистрирован ли запрошенный метод и если да, то парсит строку запроса, которая начинается с "?", которую затем передаёт в виде коллекции ключ->значения обработчику метода:
private void HandleRequest(HttpListenerContext ctx)
{
string method = ctx.Request.Url.LocalPath.Substring(1).ToLower();
if (method.Length < 0)
{
SendResponse(HttpGenericResponse.MethodRequired.ToString(), ctx);
return;
}
foreach(HttpMethodHandler handler in methods)
{
if(method == handler.Method.Name.ToLower())
{
string result = "";
if (ctx.Request.Url.Query.Length > 0)
{
string[] args = ctx.Request.Url.Query.Substring(1).Split('&', StringSplitOptions.RemoveEmptyEntries);
Dictionary<string, string> keyValuePairs = new Dictionary<string, string>();
foreach (string arg in args)
{
if (arg.IndexOf('=') >= 0)
keyValuePairs.Add(arg.Substring(0, arg.IndexOf('=')), arg.Substring(arg.IndexOf('=') + 1));
else
keyValuePairs.Add(arg.Substring(0, arg.IndexOf('=')), "");
}
result = handler(keyValuePairs);
if (result == null || result.Length < 1)
{
Console.WriteLine("Suspicious result from {0}", handler.Method.Name);
}
}
SendResponse(result, ctx);
return;
}
}
SendResponse(HttpGenericResponse.UnknownMethod.ToString(), ctx);
}
А сами методы, в свою очередь, дергают соответствующие функции из клиента и формируют на их основе датасет в примитивном формате:
public static string QueryChats(Dictionary<string, string> args)
{
if(args.ContainsKey("count"))
{
int count = int.Parse(args["count"]);
StringBuilder ret = new StringBuilder();
List<Chat> chats = HttpServer.Instance.Client.QueryChats(count);
ret.AppendLine(string.Format("Count={0}", chats.Count));
foreach(Chat chat in chats)
{
ret.AppendLine("Begin");
ret.AppendLine("ID=" + chat.ID);
ret.AppendLine("Date=" + chat.LastMessageDate);
ret.AppendLine("Name=" + chat.Name);
ret.AppendLine("Text=" + Uri.EscapeDataString(chat.LastMessageText));
ret.AppendLine("MsgId=" + chat.LastMessageID);
ret.AppendLine("End");
}
return ret.ToString();
}
return HttpGenericResponse.InternalException.ToString();
}
В результате получаем вот такой простой датасет, который, как я и говорил, легко распарсить и на Siemens C60, и на Atmega328 — да где угодно! В целом, такой сервер можно использовать для реализации бота в телеграме, который будет передавать показания каких-то датчиков, сигнализацию и прочие клевые штуки!
Переходим к реализации клиента, т.е. приложения на Android. Здесь будет не менее интересно!
В геймдеве есть своеобразный мем — некоторые инди-разработчики сначала начинают делать меню, вместо основного геймплея, что становится предметом насмешек среди других разработчиков. Но в разработке приложений для смартфонов всё по другому — здесь как-раз таки хорошо заранее продумывать макет будущего приложения!
Поскольку у нас с вами мессенджер, то главный экран должен представлять из себя список чатов (ListView) и верхнюю панельку, где в будущем могут разместиться настройки и свайп-менюшка:
Такой вот простой макет.
Каждый пункт меню — это тоже отдельный layout, в котором мы по шаблону строим внешний вид будущего элемента списка. На немолодых устройствах есть смысл использовать как можно меньше контейнеров в layout'е, поскольку пересчет позиций и размеров элементов — одна из самых «тяжелых» операций в UI-фреймворке вообще. Кроме того, не стоит использовать кучу картинок и drawable — в Android 2.x всё 2D рисуется софтварно, аппаратное ускорение появилось только в 3.0 (частично).
Но дабы в списке диалогов что-то появилось, нужно сначала реализовать фетчинг (получение) этих самых диалогов с сервера! Сам объект, который занимается обработкой запросов называется ClientManager и является синглтоном — он в единственном экземпляре на все время работы программы. Помимо менеджмента «ноды» (т.е. прокси-сервера), токена для авторизации и обработчика ошибок, ClientManager реализует метод для асинхронного запроса информации с сервера и, собственно, формирует строки запросов с помощью соответствующих методов:
public void queryChats(int count, Response resp) {
sendRequest(String.format("%s/QueryChats?count=%d&auth_key=%s", nodeAddress, count, token), resp);
}
Подгрузка чатов и сообщений реализована через Adapter — концепция «виртуальных» списков, которая предполагает что система создаст не 50 элементов интерфейса на каждую кнопку чата, а только 5 и будет их виртуально «мотать по кругу», обновляя только данные в уже существующих элементах. Это позволяет значительно ускорить отрисовку, учитывая то, что Android 2.x Canvas рисуется программно.
private void updateDialogList() {
ClientManager.getCurrent().queryChats(50, new ClientManager.Response() {
@override
public void onReady(String str) {
try {
List<Packets.Chat> chats = Packets.parseChatListFromQueryResponse(str);
DialogAdapter adapter = new DialogAdapter();
adapter.setChats(chats);
((ListView) findViewById(R.id.messages_view)).setAdapter(adapter);
} catch (Exception e) {
Toast.makeText(MainActivity.this, "Упс!", Toast.LENGTH_SHORT);
}
}
});
}
Ну вы уже явно замучились видеть простыни кода, давайте посмотрим что у нас вышло!
Шустренько, да? А ведь это ультрабюджетник Alcatel OT-916D, один из последних массовых дешевых QWERTY-смартфонов за 5 000 рублей из 2012 года. Кстати, смартфон подарил мне читатель chuvakoff с Хабра!
Переходим к окну чата. Основной макет почти такой-же, как и у основного окна: только добавилась панелька для ввода сообщения снизу.
Концептуально, всё тоже самое — запрашиваем данные с сервера, парсим их и загружаем в адаптер, благодаря чему мы сможем листать наш диалог. Однако в сообщения я добавил контекстное меню с стандартными фишками типа копирования, ответа и прочих подобных действий. Поскольку у нас нет ни пушей, ни еще каких-либо средств для поулчения данных о новых сообщениях, я раз в определенный интервал просто получаю сообщения — и если новый датасет отличается от старого — обновляю окошко чата.
view.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
// Reply to...
contextMenu.add(getString(R.string.reply)).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
setReplyContext((Packets.Message) view.getTag());
return true;
}
});
// Copy
contextMenu.add(getString(R.string.copy)).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
ViewGroup vg = (ViewGroup)view;
android.text.ClipboardManager manager = (android.text.ClipboardManager) view.getContext().getSystemService(CLIPBOARD_SERVICE);
manager.setText(((TextView)vg.findViewById(R.id.message_content)).getText());
return true;
}
});
// Send to...
contextMenu.add(getString(R.string.resend)).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
ViewGroup vg = (ViewGroup)view;
String text = ((TextView)vg.findViewById(R.id.message_content)).getText().toString();
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT, text);
intent.setType("text/plain");
startActivity(Intent.createChooser(intent, null));
return true;
}
});
}
});
Переходим к реализации поля для ввода сообщения. Здесь всё просто — на серверсайде за это отвечает метод SendMessage. Однако для того, чтобы с нашего клиента можно было ответить на другие сообщения, я ввёл также «контекст ответа», в котором запоминается сообщение, на которое мы хотим ответить. Telegram также поддерживает Markdown, однако его полная поддержка пока не реализована.
EditText editText = ((EditText)findViewById(R.id.message_text));
if(editText.getText().length() > 0) {
long replyTo = replyContext != null ? replyContext.ID : 0;
ClientManager.getCurrent().sendTextMessage(chat.ID, editText.getText().toString(), replyTo, new ClientManager.Response() {
@Override
public void onReady(String str) {
}
});
editText.setText("");
setReplyContext(null);
}
В остальном же, функционал конечно пока совсем базовый, однако клиент работает очень шустро даже бюджетной X10 Mini Pro и позволяет чатится с моими читателями в Telegram. В будущем хотелось бы допилить:
Поддержка картинок: Сейчас уже есть кривоватый механизм кэширования изображений на стороне сервера, который позволяет загружать аватарки чатов. В будущем, я добавлю поддержку «галерей» с картинками!
Поддержка голосовых сообщений: Не все их любят, но они порой удобны и выручают. Реализую как прослушивание, так и запись!
Подробный просмотр профилей и менеджмент чатов: Удаление сообщений, чатов и прочие фишечки из официальных клиентов.
Казалось бы — до официальных клиентов ещё очень далеко. Но сам факт, чтобы всё это работало достаточно шустро на девайсах, которым уже более 10 лет!
Тут всё очень и очень просто! В первую очередь, нам понадобится ПК с белым IP, роутер (если под него есть сборка dotnet), либо VDS. Виртуальные сервера сейчас стоят копейки, у ТаймВеба есть тариф за 188 рублей в месяц, которого с головой хватит для нашего сервера.
Такая вот рекламная интеграция (к слову, прокси для всех приложений уже более года крутятся именно на мощностях TimeWeb Cloud)!
Берём уже собранный TDLib и сервер под Windows, или собираем TDLib под Linux, накатываем .NET Core. Пример для Debian/Ubuntu:
sudo apt-get install dotnet
Затем запускаем сервер:
dotnet tdsrv.dll
Программа сначала запросит номер телефона, а затем код подтверждения Telegram. После этого будет создана папка tdlib/, где будут хранится данные вашей сессии, а также файл authkey.txt, где хранится случайный ключ для сессии (md5 phone_number + response code + псевдослучайное число). Не оставляйте его в /var/www/!
Если всё нормально, программа начнёт слушать порт 13377 на всех сетевых интерфейсах, в т.ч и в локальной сети. После этого, ставим уже предварительно собранный, либо собираем сами в Android Studio APK и в окне авторизации пишем адрес ноды и ключ авторизации. Если всё настроено верно — программа запомнит сервер и будет работать без проблем! Вот так всё легко :) Как видите — всё очень и очень просто!
Кроме того, буквально за пару дней до публикации статьи я сел вечерком из интереса что-нить под Java-телефоны попилить… и, как и обещал, реализовал Proof of Concept возможности работы Telegram даже на сонериках, которым скоро 20 лет стукнет! А ведь если ещё чуть заморочится, можно запустить приложение даже на преусловутых монохромных сименсах!
Вот такой у нас получился проект с реализацией лёгкого, примитивного, но тем не менее рабочего клиента Telegram, который на клиентской части вообще не использует никаких зависимостей. Вес собранного APK в release-версии — всего 54 килобайта! Понятное дело что с ростом функционала, вес программы будет увеличиваться, но я обещаю — больше 1Мб он не вырастет :)
Ну а вам, моим читателям, надеюсь было интересно прочитать такой «двойной материал» не только о разработке сетевой части без использования Apache/nginx/IIS, но и UI-фронтэнда для Android-смартфонов, которым уже более 10 лет!
Исходный код проекта можно найти на моём GitHub: как приложения, так и сервера, а также убедиться в отсутствии каких либо закладок и, если совсем не доверяете, собрать бинарники сами! Для сборки понадобится VS2017 или свежее, а также Android Studio 2.3.2 (если собираете для Android 2.1 и ниже).
Друзья! Сейчас на Хабре опросы сломаны, поэтому если у вас есть желание, вы можете проголосовать в комментариях: какой стиль статей вам больше нравится — где больше конкретики и кода с пояснением как конкретно работает та или иная часть программы, или наоборот стиль ближе к научпопу, где фрагментов кода нет, или их значительно меньше? Пишите своё мнение о проекте в комментариях!
Кроме того, у меня есть канал в Telegram, куда я публикую бэкстейдж статей, ссылки на новый материал, свои наработки, а также посты о ремонте девайсов и различные мысли.
Статья подготовлена при поддержке TimeWeb Cloud. Подписывайтесь на меня и @Timeweb.Cloud, чтобы не пропускать новые статьи каждую неделю!
Как вышло, что Nike носят все, от бедных подростков до президента США
Привет, это КОМРЕДА: истории. Вообще мы агентство контент-маркетинга, а истории рассказываем так, для души.
Сегодня о том, какие маркетинговые решения помогли компании Nike вырасти из перекупщиков китайских кроссовок в самый дорогой спортивный бренд в мире.
Nike никогда не рассказывает о том, какие крутые у них товары. В компании показывают, какими крутыми могут стать люди, которые эти товары покупают.
Поэтому в центре каждой рекламы Nike — человек со своими слабостями, страхами и силами. Кто бы ни был героем рекламного ролика, знаменитый спортсмен или ребёнок беднейшей страны мира, Nike показывают: возможности есть у каждого, вне зависимости от возраста, цвета кожи, страны и вида спорта.
Один из запоминающихся примеров такой рекламы — ролик Find Your Greatness 2012 года. Идея рекламы в том, что достичь величия могут не только талантливые и знаменитые спортсмены. Любой человек может стать великим, если будет работать над собой и преодолеет свои страхи.
Некоторые медиа назвали эту рекламную кампанию Nike самой человечной кампанией бренда.
Ещё один пример — рекламная кампания Write The Future, приуроченная к чемпионату мира по футболу — 2010. В ней показали, что каждый человек вправе сам быть автором своей судьбы, как это сделали известные футболисты.
Маркетинговые задачи такая реклама выполнила: ролик набрал более 50 миллионов просмотров и сделал Nike самым популярным брендом в интернете.
За полвека существования Nike разработали кроссовки для всех популярных видов спорта: лёгкой атлетики, футбола, тенниса, гольфа. Причём модели создавали и для профессионалов, и для спортсменов-любителей.
Такую универсальность бренд обеспечил себе благодаря технологиям: разработанная в 80-х годах модель Air с подушками амортизации стала основой для многих будущих коллекций.
Больше 40 лет кроссовки Nike делают по технологии Air
И снова покупателей вдохновляли уже известные или перспективные спортсмены: гольфист Тайгер Вудс, теннисист Андре Агасси, бейсболист Кен Гриффи-младший, футболисты Роналдиньо, Криштиану Роналду.
Тайгер Вудс рекламировал Nike в течение 27 лет, а Майкл Джордан заработал на коллаборации больше миллиарда долларов
Но самую большую известность бренду принесла баскетбольная обувь — серия кроссовок Nike Air Jordan. Решение открыть эту линию в компании приняли в 1984 году, когда Nike были малоизвестными. Кроссовками для баскетбола хотели привлечь подростковую аудиторию, которая любила этот вид спорта.
Рекламный контракт подписали с малоизвестным на тот момент Майклом Джорданом, игроком студенческой баскетбольной команды.
Коллекция Nike Air Jordan принесла компании около 14% от всей выручки за время существования бренда
Кроссовки продвигали через агрессивную рекламу в СМИ и провокации. Например, в НБЛ были запрещены яркие кроссовки. Но вопреки запрету Air Jordan сделали красно-чёрными, и компания оплачивала штрафы за играющего в них Джордана. Когда НБА вовсе запретила использовать модель в играх, интерес к обуви только возрос.
Так участие звёзд большого спорта в рекламе обеспечило бренду популярность: люди с интересом смотрели рекламу со своими кумирами и доверяли их выбору.
Nike не остаётся в стороне от общественно-политической ситуации. Напротив, бренд проводит рекламные акции, которые влияют на неё.
Например, девяностые были эрой супермоделей. Рекламные посылы Nike отражали моду на 90-60-90. В наши дни началась эпоха бодипозитива. Место андрогинных моделей заняли девушки неконвенциональной красоты. Они же заняли место в рекламе Nike.
Одним из амбассадоров рекламной кампании Own the Floor стала танцовщица Трина Николь, которая называет себя body confidence advocate — «защитницей уверенности в себе»
Можно посмотреть на такой подход бренда с другой стороны. По данным исследований, за последние 30 лет количество взрослых людей в Америке, страдающих ожирением, выросло с 12% до 42%. Это большая часть платёжеспособной аудитории, которая покупает обувь. Куда они пойдут за кроссовками? Конечно, к бренду, который принимает их такими, какими они есть.
Но, пожалуй, самым резонансным кейсом Nike стала коллаборация с Колином Каперником.
В 2016 году на матче по американскому футболу спортсмен отказался вставать с колен во время исполнения гимна США. Свой жест он объяснил протестом против расовой несправедливости в Америке. Такой шаг Каперника вызвал жаркие дискуссии в обществе и фактически завершил карьеру спортсмена.
Участие футболиста в рекламе Nike спустя два года тоже приняли неоднозначно. Мнения настолько разделились, что кто-то публично сжигал кроссовки Nike, а кто-то, напротив, боготворил бренд.
«Верьте во что-то. Даже если для этого придётся пожертвовать всем»
Можно по-разному относиться к общественной позиции компании, но такой подход дал результат: Nike знают, помнят, бренд всё время на виду в информационном поле. А значит, и в поле зрения своих покупателей.
Nike поддерживает разные социальные инициативы и помогает всем — от больных детей до вполне здоровых президентов.
Например, с 2004 года компания запустила программу поддержки детей с разными заболеваниями. Для этого разработали концепцию: дети, которые лечились в одном из американских госпиталей, придумывали дизайн кроссовок. Так у Nike появилась коллекция Doernbecher Freestyle и лояльные покупатели, которым важно участвовать в благотворительности.
18-ю коллекцию Doernbecher Freestyle выпустили в 2023 году. Фото с официального сайта
А в 2014 году в Nike выделили грант в 5 миллионов долларов на строительство спортивного корпуса в президентском центре Барака Обамы в Чикаго. До этого в 2009 году в компании разработали кроссовки специально для Обамы, ставшего президентом США.
Это была баскетбольная модель с номером 44 по бокам, на язычках президентский герб с орлом
И снова Nike дотянулись до самой разной аудитории, от преданного президентского электората до богатых благотворителей и родителей болеющих детей.
Американский журнал о рекламе Advertizing Age назвал фразу Just Do It одним из пяти главных слоганов 20 века. Считается, что его предложил Дэн Виден — директор Nike по рекламе. Однажды он прочитал в газете статью о последних минутах убийцы Гэри Гилмора: когда смертельный приговор собирались приводить в исполнение, убийца сказал: «Let’s do it».
Уже в 1988 году фраза перекочевала в рекламу. Она завершала ролик, в котором 80-летний спортсмен рассказывал, как бегает по 17 миль каждое утро.
Just Do It — один из самых долгоживущих слоганов, компания не меняет его уже почти 40 лет
Ещё один посыл, который Nike активно продвигает сегодня, — инклюзивность, то есть доступность товаров бренда всем без исключения. Для этого у компании тоже есть лозунг: «Если у тебя есть тело, ты уже атлет».
Такие слоганы в конечном итоге выглядят органичным продолжением всех маркетинговых решений:
Вдохновляют покупателей и вызывают положительные ассоциации с брендом.
Остаются актуальными в любое время для любого вида спорта.
Заявляют об общественной позиции бренда и его социальной ответственности.
Объединяют аудиторию.
Вот так, не продавая товары в лоб, но целясь в сердца покупателей, у Nike получилось залезть в их кошельки и стать самым дорогим спортивным брендом в мире.
Если вам понравился текст — вы можете заказать такой для своей компании.
Мы в агентстве КОМРЕДА делаем блоги, медиа, спецпроекты, соцсети и многое другое. А в телеграм-канале рассказываем, как выстраивать прибыльный контент-маркетинг.
Расскажем все, что важно знать о продвижении: как люди покупают одежду, как оформить сайт и соцсети, какую рекламу запускать.
Оцените текущую точку: какие каналы продвижения используются, откуда приходят посетители, что вы о них знаете. Оцените эффективность каждого канала, выделите рабочие инструменты и те, которые следует заменить.
Второй шаг: исследование целевой аудитории. Сначала сделайте анализ на основании статистики и отзывов о продукте. Потом - проведите интервью с покупателями. Узнайте, зачем им нужен продукт, какие потребности он закрывает, что стало определяющим фактором при покупке.
Оцениваем:
Социальные характеристики: пол, возраст, средний доход.
Запросы целевой аудитории
Что влияет на принятие решения о покупке: возможности и стоп-факторы
Что они больше всего ценят в одежде
Для чего им ваш продукт?
Когда я изучаю целевую аудиторию, то ищу потребность. Например, в проекте Inten, одежда помогала женщинам чувствовать себя уверенно за счет создания образа элитарности. Они искали не просто одежду, а способ подчеркнуть свой статус.
Виктория, аналитик.
Третий шаг: изучите конкурентов. Посмотреть, как они продвигаются, за счет чего получают клиентов, вокруг какого образа строят свой бренд. Мы в агентстве выбираем минимум 4 конкурента для анализа.
Последнее, что вам нужно - оценить насколько вы попали. Кроме шуток, - рассчитайте рекламный бюджет. О том, как это сделать поговорим чуть позже.
Можно пойти сложным путем - изучать кейсы, читать отзывы и статьи: “что лучше, соцсети или лендинг?” И получить случайный результат. Мы в агентстве случайностей не любим и опираемся на аналитику. Предлагаем вам, сделать то же самое.
Есть два кита. Первый - целевая аудитория. Второй - цели продвижения.
Тут вы скажете: “цель одна - прибыль”. Да, но прибыль бывает разная. В моменте, и в перспективе. Вопрос в том, какая нужна вам.
Сделать оборот и выйти в плюс (идеальный рецепт для молодых компаний и старта новой линейки) в приоритете будут перформанс стратегии. Нашли аудиторию - прогрели - продали. Для этой задачи подходят:
Маркетплейсы
Промостраницы яндекс (ведем на сайт)
Реклама VK Ads
2. Повысить узнаваемость бренда. Нужен бюджет на развитие и создание образа бренда в сети. Прогреваем - наш бренд начинают узнавать - появляется лояльная аудитория - растут продажи. Используем:
телеграмм
блог на сайте компании
Промостраницы яндекс (ведем на блог)
Пример, как не надо делать. Работая с проектом Inten мы сделали ошибку в цели. Повышение узнаваемости бренда - игра вдолгую. Оно повышает лояльность, и создает приток клиентов в перспективе. Но у нас была цель – создать поток продаж к Новому году. Поэтому использование этой стратегии дало более низкие результаты.
В выборе инструментов для продвижения женской одежды лучше ориентироваться на широкий сегмент. Их два типа.
Масс маркет. Здесь хорошо работают: маркетплейсы, яндекс директ и коллаборации с другими нишами, близкими вашей аудитории. Например, с брендом женских сумок или аксессуаров.
Премиум и Enterprise - продвижением через Яндекс маркет, Озон и полноценный магазин ВКонтакте. Для повышения узнаваемости бренда нужно использовать телеграмм канал и блог на сайте.
О чем писать в блоге?
Контент, который тесно связан с продукцией, но не говорит о ней напрямую. Например: как одеваться на деловые встречи, как найти стилиста, как выбрать макияж к образу. В конце статьи можно давать ссылку или сразу подтягивать каталог товаров. Скорее всего, читательница перейдет в него.
В канале бренда Inten мы сделали пост с карточками, куда можно сходить в Питере на выходных. На последней карточке предложили прийти в шоурум бренда. Контент сработал. В первую же неделю пришло 13 подписчиков.
Продает ли телеграм?
Мы провели исследование и спросили коллег. Телеграмм канал хорошо работает на узнаваемость бренда. Приводите туда лояльную аудиторию, тех, кто купил, хорошо знает бренд или относятся к категории: “сочувствующие”, например, друзья основателя. На базе этой аудитории можно наращивать обороты. В долгосрочной перспективе это поднимает узнаваемость бренда в социальных сетях и приводит подписчиков.
Для продаж “здесь и сейчас” в нише женской одежды телеграмм не эффективен.
Женщины покупают глазами и на ощупь, через монитор потрогать ткань не получится. Поэтому визуальный концепт решает.
Каталожные фотографии нужны, когда мы строим отношения с реализаторами. Именно так мы поступили в продвижении Inten.
Если вы продаете одежду напрямую - делайте живые фото, они демонстрируют повседневную ситуацию. Если девушка покупает повседневную одежду, даже премиум класса - она хочет понять, как наряд будет смотреться на ней.
Когда я выбираю фото, для главной страницы лендинга - я ищу реальные сценки из жизни. Девушка идет по улице, говорит по телефону, сидит с подругами в кафе, едет в такси домой ночью. Те сюжеты, где легко узнать себя и воскликнуть: “О! Я буду в этом круто выглядеть!
Юля, веб-дизайнер.
Текст должен быть коротким и емким. Иначе есть риск, что поисковая выдача приведет не тех людей, особенно если вы готовите карточки на маркетплейсах. Обязательно заполните параметры, влияющие на решение о покупке: цвет, размер, материал.
Лучше всего продают живые фото, где товар используется в повседневной жизни. Если есть возможность - дополните галерею инфографикой, например, укажите размерную сетку.
Берите все! И применяйте в зависимости от целей.
В ВКонтакте эффективно работают товары сообщества. По нашим исследованием в проекте Inten, этот раздел часто смотрела целевая аудитория. Можно делать посты и подтягивать в них карточки товаров вместо галереи. Лучше всего работает совмещение постов и рекламы через специальный инструмент в VK Ads.
Тематические подборки удобно размещать как в ВКонтакте, так и в Телеграмм. Главное правило - все товары в карусели должны соответствовать одной актуальной теме. Это может быть: создание цельного образа, одежда на сезон, варианты для события.
Видео работает в любом канале, начиная ВКонтакте, заканчивая Промостраницами яндекс. Мы в компании используем каскадную схему продвижения. Сначала снимаем и запускаем ролики на широкую аудиторию. Это повышает узнаваемость бренда и формирует лояльность.
Потом адаптируем видео под конкретные сегменты и задачи - напрямую поднимаем продажи через контент.
Как понять, что рекламная кампания не оставит вас без денег? Взять калькулятор и… посчитать.
Где брать данные для расчета? В интернете. Вбить в поиск “кейсы по продвижению женской одежды” и посмотреть, сколько стоит лид и продажа. Так, вы закрываете сразу две задачи:
находите подрядчиков, у которых уже есть кейсы в вашей нише
узнаете стоимость рекламы
Второй вариант - сэкономить время и узнать у нас в агентстве. Мы бесплатно рассчитаем рекламный бюджет и проведем первичную аналитику рынка.
Скажем страшное: гипотезы нужны, чтобы их провалить. По статистике, 70% гипотез оказывается нерабочими. Поэтому - готовьтесь заранее и не только морально.
На старте сформулируйте 10-15 как можно более конкретных гипотез. Пока тестируется один ряд - готовьте следующий. Это закроет три задачи:
расширит инструментарий
снизит риск того, что продвижение встанет по причине отсутствия гипотез
появляться данные для аналитики и улучшения рекламной кампании.
Среднее время на проверку 10 гипотез - две недели. (при условии что запускаются они одновременно) за месяц можно посмотреть примерно 30 гипотез, сформулировать вывод по каждой и скорректировать стратегию.
Рабочие гипотезы - обязательно нужно привязать к медиаплану. Подробнее про медиаплан и как рассчитать под него бюджет вы можете прочитать в нашей статье.
Сео работает, но есть нюанс.
Если ваша цель - продвижение товаров ради продаж - лучше выбрать другую стратегию. Например, маркетплейсы, там результат будет быстрее. Если у вас Премиум и Enterprise сегмент и вы решили повысить узнаваемость бренда - используйте сео-продвижение в своем блоге.
Выбор каналов и способов зависит от целей и аудитории. Для продаж лучше выбирать ВКонтакте и маркетплейсы. Для брендовых целей - телеграмм и ведение своего блога. Главное - внимательно просчитать бюджет и провести анализ рынка. Еще больше фишек и рекомендаций вы найдете в канале.
Некоторые бренды виртуозно реагируют на события. Это и есть ситуативный маркетинг — превращение инфоповода в способ продвижения. Российская авиакомпания S7 — один из сильнейших игроков, когда речь идёт про быстрое реагирование.
Вот два примера
1) В 2019 году S7 Airlines отреагировала на сибирские пожары (масштабные возгорания, ущерб которых составил более 7 млрд рублей). Авиакомпания запустила благотворительную рекламную кампанию: помогала собирать средства для возмещения ущерба от сибирских пожаров. Также временно вернула своё изначальное название — «Сибирь».
Итог: авиакомпания высадила миллион деревьев в Сибири. Количество клиентов увеличилось на 2 млн. рублей, а читатели National Geographic Traveler назвали S7 лучшей российской авиакомпанией.
Такие акции продумывают, делают и потом оценивают с точки зрения выгодности маркетологи. Познакомьтесь с профессией, узнайте, как работают с онлайн-аудиторией и привлекают новых клиентов с помощью рекламных кампаний на бесплатном курсе от Академии Eduson →
2) Годом позже S7 поддержала всех, кто остался дома во время локдауна. Компания предлагала своим клиентам копить мили за социальное дистанцирование. Необходимо было лишь зарегистрироваться на сайте и жать кнопку ежедневно.
Итог: в акции приняло участие более полумиллиона человек, которые с удовольствием тратили мили (и деньги) на билеты «S7» после открытия границ.
Реклама ООО «Эдюсон»
Мемный контент получает реакции, вирусится и запоминается, а учёные подтвердили, что мемы помогают справляться со стрессом. Бренды, которые умеют делать мемы, всегда на слуху, но не каждый копирайтер может придумать годный контент. А мемолог может
Привет! С вами КОМРЕДА — агентство контент-маркетинга, которое помогает бизнесу расти. Кроме редакторов, авторов и иллюстраторов в нашей команде есть мемолог. Мы привлекаем его не ко всем своим проектам, но для некоторых он очень полезен. Рассказываем почему.
Не все попытки завоевать аудиторию юмором бывают успешными. Есть много кринжовых мемов, назовём их условно «шутками за триста». Они не приносят бизнесу пользу и не формируют вокруг него комьюнити лояльных пользователей. Пример ниже:
Такие мемы любят размещать как наружную рекламу. Источник — паблик ВК «Подслушано Иваново»
Мемолог же делает так, чтобы о бизнесе говорили много и позитивно. Смешные картинки помогают решить репутационные задачи.
Мемы формируют образ бренда. Они помогают сформировать уникальный стиль общения с аудиторией, сделать его понятным и позитивным. Например, «ИКЕА» несколько раз троллила Balenciaga, делая упор на цены люксового бренда.
Икеа сделала фото с полотенцем за 10 долларов, которое повторяет образ модели в юбке-полотенце Balenciaga за 925 долларов. Источники — пресс-служба «ИКЕА» и Balenciaga
Мемы помогают быть на волне трендов и на слуху. Мемы часто завязаны на инфоповодах. Например, управляющая компания «Зелёный бульвар» из Владивостока опубликовала мем по мотивам нашумевшего сериала «Слово пацана».
Ситуативный мем от управляющей компании «Зелёный бульвар». Источник — телеграм-канал УК «Зеленый бульвар»
В отличие от большинства российских коммунальщиков, в канале «Зелёного бульвара» не нужно пробираться сквозь бюрократию и официоз. Наоборот, здесь любят шутки и мемы в коммуникации с жильцами домов.
Мемы создают эмоциональную связь с клиентом. Мемами делятся активнее, чем текстовыми постами. И это решает сразу две задачи бизнеса: повышает узнаваемость и поднимает охваты бренда в соцсетях.
Хорошие мемы приносят бизнесу подписчиков и лояльную аудиторию. Плюс мемов ещё в том, что их можно использовать на разных площадках, адаптируя под нужный формат. Например, Durex перед новогодними каникулами создали отпускные аватарки для подписчиков.
Компания затронула актуальную тему — работу в новогодние праздники — и дала готовое решение для подписчиков в своём стиле. Источник — телеграм-канал Durex
Мемы показывают клиентам боли и их решение. Мемы активно вирусятся и получают реакции, потому что многие узнают в них себя. Бизнесу это помогает дотянуться до болей целевой аудитории, в решении которых компания может помочь. Например, Генконсульство России в Дубае в своём телеграм-канале размещает полезные инструкции без лишней бюрократии.
В информационное сообщение добавили популярный мем с котом, который привлекает внимание. Источник — телеграм-канал Генконсульства России в Дубае
Мемы разбавляют продающий контент и помогают не душнить. Люди заходят в соцсети не только за покупками и полезным контентом. Ещё они хотят расслабиться, отвлечься, поднять себе настроение. Каким бы ценным ни был контент бизнеса, уставший пользователь, скорее всего, проскроллит его не читая. А вот мимо смешного мема вряд ли пройдёт.
У «Авиасейлс» почти 150 000 подписчиков в телеграм-канале. Мемы и шутки — стандартная форма общения для компании. Источник — телеграм-канал «Авиасейлс»
Мемы можно публиковать на всех площадках, куда люди заходят, чтобы отвлечься. Чаще это соцсети — Телеграм, YouTube, Twitter, запрещённая соцсеть с картинками.
Но соцсетями мемы не ограничиваются. Например, на vc.ru мемы часто используют, чтобы иллюстрировать мысли из текста. Если это не противоречит ToV бизнеса, мемы можно использовать в блоге компании для email-рассылки. Например, так «Авиасейлс» рассказывает новым подписчикам, какие письма они будут получать на почту.
В сообщении делают акцент на болях в типичных рассылках о билетах — высоких ценах и неинтересных статьях. Но пишут об этом с юмором. Источник — email-рассылка «Авиасейлс»
Многие думают, что мемолог — это очень творческая профессия: нужно просто сидеть и придумывать смешные видео и картинки. На самом деле даже в такой «несерьёзной» профессии всё начинается с понимания задачи и чёткого регламента работы.
Мы попросили нашего мемолога Катю рассказать, как она создаёт мемы, и вот что она ответила.
Работа по созданию мемов строится так:
→ Изучаю возможности клиента, его услуги и запрос, с которым он приходит в агентство.
→ Смотрю контент-план клиента и актуальные темы постов.
→ Формулирую идею мема.
→ Нахожу референсы и изучаю тренды. Это важно, потому что тренды меняются быстро, постоянно появляется что-то новое. Чтобы не быть в глазах подписчиков вымирающим мамонтом, нужно знать об актуальных инфоповодах и уметь подстраивать их под бизнес клиента.
→ Монтирую ролики, накладываю текст, музыку, видео — чтобы всё вместе смотрелось интересно и понятно.
→ Согласовываю мемы с клиентом.
Иногда заказчик вносит правки в мемы. Это тоже работа мемолога — сделать так, чтобы понравилось читателям, но не упустить пожелания заказчика
Катя Максимова, мемолог КОМРЕДЫ
Например, в конце 2023 года для клиента сделали мем на основе фрагмента хайпового сериала «Слово пацана». Вот что получилось:
Мы сравнили результаты продуктовых и мемных постов. Оказалось, что весёлый контент набирает в среднем в полтора раза больше реакций — лайков и просмотров. Например, если ваш продуктовый пост обычно набирает тысячу лайков, то мемный может принести полторы тысячи. Неплохо!
Так мы подтвердили гипотезу, что мемы работают на этапе вовлечения — когда нужно привлечь внимание к бренду. Но вряд ли для бизнеса, даже крупного, оправдан штатный мемолог. А вот для агентства контент-маркетинга такой специалист — хорошая боевая единица наряду с копирайтером и маркетологом. А что думаете вы?
Приходите к нам в КОМРЕДУ за контентом: статьями для vc.ru и блога, колонками в СМИ, SEO-текстами, постами для соцсетей. И за мемами тоже приходите, чтобы не душно общаться с аудиторией, наращивать охваты и популярность бренда.
Ещё мы ведём канал для маркетологов, где делимся полезным без воды. Присоединяйтесь!
Приходите к нам в КОМРЕДУ за контентом: статьями для vc.ru и блога, колонками в СМИ, SEO-текстами, постами для соцсетей. И за мемами тоже приходите, чтобы не душно общаться с аудиторией, наращивать охваты и популярность бренда.
Ещё мы ведём канал для маркетологов, где делимся полезным без воды. Присоединяйтесь!
У меня производство мебели для сохранения здоровья детей и взрослых в Санкт-Петербурге. Делаем растущие парты, растущие стульчики, конторки для работы стоя, нейротренажеры, настольные мольберты с регулировкой, мебель для малышей.
Нам постоянно пишут из благотворительных фондов или малообеспеченные семьи с просьбой подарить что-то из продукции, хотя бы с браком.
В прошлой году начали на постоянной основе проводить благотворительные акции в группе Вконтакте.
Заниматься благотворительностью или нет — личное дело каждого. Напишу, какие плюсы я нашел лично для себя как предприниматель.
Личное удовлетворение
Я не могу оставаться равнодушным к историям людей, которым нужна помощь. Поэтому пока компании позволяет финансовая ситуация, решил отдавать мебель, которую мог бы продать.
Пиар бренда
Если грамотно продумать механику благотворительной акции, лояльность существующей аудитории увеличится и даже появятся новые клиенты. Ниже приведу примеры наших механик и может напишу отдельную статью об этом.
Избавление от неликвида
Представьте, что вы сделали или закупили слишком много товара или у вас значительный процент брака. Вы можете попытаться продать товар с большой скидкой. Повезет — отобьете вложения, нет — получите много хейта (из-за брака) и пострадает репутация. Благодаря благотворительности можно и репутацию повысить, и избавиться от неликвидного товара. Главное — сообщить, что именно и с какими дефектами отдаете.
Получение контента
В благотворительной акции вы можете прописать условие: обязательно написать отзыв и прислать фото, видео. Разместите отзывы в соцсетях, запустите в рекламу, напишите статью (как это сделал я) — всё это положительно скажется на продвижении бренда. Даже без условий нам всегда присылают благодарности в виде отзывов и писем.
Расскажу, как можно проводить акции на своем примере.
Возвраты с маркетплейсов
80% продаж приходится на маркетплейсы. Естественно, ежемесячно приходит много возвратов. Часто не по нашей вине: повреждена упаковка, маркетплейс перепутал товар, клиент передумал. В отделе упаковки проверяют каждый возврат на наличие повреждений и следов сборки. Если в идеальном состоянии — перепаковывают, с дефектами — на уценку.
Мебель после съемок контента
Например, выпускаем в продажу новую модель парты. Собираем образцы каждого цвета, фотографируем, снимаем обзоры. Образцы уже не продать — есть следы сборки и использования. Поэтому 1 ставим в шоурум на производстве, остальные отправляем на склад.
Детали с производственными дефектами
Некоторые дефекты обнаруживаются на производстве: кривоватая фанера, видимые сучки на фанере, зашпаклеванные трещины, заплатки и тд. На функционал такие дефекты не влияют, но выглядят не очень эстетично.
Склад мебели с дефектами условно называем склад уценки.
(сделал ссылками, если захотите узнать подробнее о моделях)
Благотворительным фондам, многодетным семьям, семьям в трудной финансовой ситуации, школам, детским садам и центрам. Кто-то пишет сам, кого-то выбираем в акциях — об этом рассказываю дальше.
Сейчас вся мебель с уценкой складируется на выделенных для этого стеллажах. Однако продажи растут, возвраты тоже. Уценка копится стремительно. И если год назад можно было отдавать всю мебель раз в несколько месяцев, то сейчас за месяц стеллажи заполняются полностью.
Акции проводим в группе Вконтакте. Происходит это так:
Продумываем механику. Либо выбираем победителей случайным образом, либо выбирают подписчики группы, либо мы с коллегами.
Пишем пост о том, какие модели отдаем. Обязательно указываем состояние мебели и примерные дефекты. В последней акции отдавали 4 конторки, 14 настольных мольбертов и 6 балансбордов (указал ссылки на ОЗОН, кому интересно).
Прописываем условия участия. Например, рассказать, почему вы нуждаетесь в бесплатной мебели, сделать репост, после получения прислать отзыв с фото.
Выбираем, кто может участвовать. В одной акции это могут быть учебные заведения, в другой малоимущие семьи, в третьей — фонды.
Так выглядит пост Вконтакте.
Механика акции очень важна. От нее зависит, будет ли хейт после подведения итогов, все ли останутся довольны. Да, даже в таких делах много негатива)
Например, в декабре выбрали 9 финалистов и устроили голосование среди подписчиков нашей группы. Кто-то попросил проголосовать знакомых, кто-то опубликовал ссылку в крупном чате. Итог — куча негативных комментариев и обвинений в накрутке голосов.
Конечно, подобные механики помогают пиару компании. Тогда на нашу группу подписалось 200 человек за несколько дней, но репутация дороже — механику изменили.
За прошлый год мы провели 5 благотворительных акций и 1 конкурс для школ, в рамках которого мы полностью оборудовали школьный класс. Всего отдали 28 растущих стульчиков, 26 конторок и парт, 36 нейтротренажёров, 16 мольбертов и 9 видов других моделей.
Посты в группе Вконтакте с упоминанием благотворительности (сами акции, голосования, отзывы) набрали суммарный охват 196 тысяч. С Пикабу общий охват постов 283 тысячи. Публиковал 3 поста: настольные мольберты для бабушек, нейротренажеры для инвалидов, что мы делаем с уценкой.
Важны ли вам охваты — решайте сами.
В итоге благотворительность помогает нам повысить узнаваемость бренда и лояльность аудитории (старой и новой), а также отдать неликвидный товар. Это только с точки зрения бизнеса. Лично мне просто нравится помогать людям — этот принцип я заложил в основу бренда.
Возможно, кому-то из вас тоже подойдёт такой способ продвижения. Пользуйтесь и делитесь опытом в комментариях.
Их есть у нас! Красивая карта, целых три уровня и много жителей, которых надо осчастливить быстрым интернетом. Для этого придется немножко подумать, но оно того стоит: ведь тем, кто дойдет до конца, выдадим красивую награду в профиль!
Процесс создания представлен на фото:
Материалы: белая коробка (30x30x7), поролон SPG2240 толщиной 2см, линейка, маркер, канцелярский нож и ножницы.
P.S. В новой версии кейса для создания отверстий нужно поискать что-то более удобное, чем канцелярский нож.