Все сказанное в статье будет интересно исключительно разработчикам 1С.

Сегодня мы разберем внешнюю компоненту для «1С Мобильное приложение». Эта статья появилась по двум причинам. Разрабатывать будем все под тот же АТОЛ Smart.Lite

  1. Разработка нативного приложения оказалась куда сложней, чем я думал изначально.
  2. Поступило несколько запросов именно на внешнюю компоненту для 1С

До этого у меня уже был опыт написания c++ внешних компонент. И даже есть шаблон под x86 платформу. Писалось вообще без понимания c++. Но тем не менее, в двух проектах работает, и не падает. Переходим к сути проблемы. Нужна нативная компонента для получения Broadcast сообщений в 1С. Пробежимся по интернету и поймем, что готовые решения есть. Но все они находятся на мной не очень любимом сайте, за чрезмерную жадность. А платить за черный ящик я не хотел. Тем не менее там попалась отличная статья «Внешние компоненты мобильной платформы 1С для ОС Андроид». В ней описывается как слепить мобильную версию компоненты, и что надо установить. Как я понимаю вот здесь лежат исходники выше упомянутой статьи. Огромное спасибо доброму человеку за труды. Очень помогло на живом примере понять что и как работает. Далее пришлось немного расширить свой кругозор как же работает JNI. Просто и понятно здесь и здесь. Рекомендую ознакомиться с ними. Уверен, что настоящим программистам c++ мой код не понравится. Прошу отнестись снисходительно и ткнуть, что можно улучшить и написать правильней.

Приступим. Я взял исходный код с репозитория который указывал раньше, и почти полностью его переделал под свои нужды. Взять можно здесь. Пробежимся по основным моментам. Главное процедурой у нас является startEventsWatch В ней мы проверяем, что у нас не подключен BroadcastReceiver и переопределяем функцию onReceive Там мы смотрим какое событие к нам пришло, заполняем поля, и вызываем функцию OnBroadcastReceive и вот она то и является связующей функцией между java и С++ и переносит нас из мира Android в мир 1С. Об этом чуть позже. Заветные строчки, что же мы хотим получать в 1С выглядят вот так.

filter.addAction("com.xcheng.scanner.action.BARCODE_DECODING_BROADCAST");
filter.addAction(NEW_KEY_UP);

Здесь описывается, что мы ожидаем событие(action) от сканера. В моем случае это com.xcheng.scanner.... В вашем случае в зависимости от сканера, будет другая строка. Соответственно и данные внутри сообщения, тоже будут другими. Как правило эти данные можно получить у производителя ТСД. Ну или посмотреть в logcat. Еще я хотел получать коды нажатия аппаратных кнопок. Но проблема в лоб не решилась. Простое добавления onKeyUP в код и отправка этого в sendBroadcast успехом не увенчались. Оно и не удивительно, наша Activity не на переднем плане. По этой причине пришлось быстренько накидать AccessibilityService

startEventsWatch
  public void startEventsWatch()
  {

    if (m_Receiver==null)
    {
      m_Receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
          if (intent != null) {
            String event, type, data;

            switch (intent.getAction()) {
              case "com.xcheng.scanner.action.BARCODE_DECODING_BROADCAST":
                event = "NewBarcode";
                type = intent.getStringExtra("EXTRA_BARCODE_DECODING_SYMBOLE");
                data = intent.getStringExtra("EXTRA_BARCODE_DECODING_DATA");
                OnBroadcastReceive(m_V8Object, event, type, data);
                break;
              case NEW_KEY_UP:
                event = "NewKeyUP";
                type = "key";
                data = intent.getStringExtra(KEY_CODE);
                OnBroadcastReceive(m_V8Object, event, type, data);
            }
          }
        }
      };

      IntentFilter filter = new IntentFilter();
      filter.addAction("com.xcheng.scanner.action.BARCODE_DECODING_BROADCAST");
      filter.addAction(NEW_KEY_UP);

      m_Activity.registerReceiver(m_Receiver, filter);
    }

  }


Теперь вернемся к нашей отправке данных в 1С. Наша OnBroadcastReceive вызывает процедуру extern "C" JNIEXPORT void JNICALL Java_org_innovait_atolsmartliteutils_MainApp_OnBroadcastReceive(JNIEnv* env, jclass jClass, jlong pObject, jstring j_event, jstring j_type, jstring j_data) Вот здесь мы можем добавить переменные, с которыми хотим работать со стороны Java. jstring j_event, jstring j_type, jstring j_data Это переменные в которых я передаю, событие, тип ШК, и сам ШК. Могут быть и другие данные.

Java_org_innovait_atolsmartliteutils_MainApp_OnBroadcastReceive
extern "C" JNIEXPORT void JNICALL Java_org_innovait_atolsmartliteutils_MainApp_OnBroadcastReceive(JNIEnv* env, jclass jClass, jlong pObject, jstring j_event, jstring j_type, jstring j_data) {
	IAddInDefBaseEx *pAddIn = (IAddInDefBaseEx *) pObject;
	if (pAddIn != nullptr) {
		
		std::wstring ws_event =ToWStringJni(j_event);
		std::wstring ws_type = ToWStringJni(j_type);
		std::wstring ws_data = ToWStringJni(j_data);

		std::wstring obj_data{};

		obj_data = L"{\"type\": \"" + ws_type + L"\", \"data\": \"" + ws_data + L"\"}";	

		WcharWrapper wdata((wchar_t*)obj_data.c_str());
		WcharWrapper wmsg((wchar_t*)ws_event.c_str());

		pAddIn->ExternalEvent(s_EventSource, wmsg, wdata);
	}
}


std::wstring ws_event =ToWStringJni(j_event); Этим мы переводим строку из jstring в std::wstring, и потом это все запаковываем для 1С WcharWrapper wmsg((wchar_t*)ws_event.c_str());

Спасибо умному человеку за функцию конвертации. Вторая функция идет из коробки в примере от 1С.

Заголовок спойлера
std::wstring ToWStringJni(jstring jstr)
{
	std::wstring ret;
	if (jstr)
	{
		JNIEnv* env = getJniEnv();
		const jchar* jChars = env->GetStringChars(jstr, NULL);
		jsize jLen = env->GetStringLength(jstr);
		ret.assign(jChars, jChars + jLen);
		env->ReleaseStringChars(jstr, jChars);
	}
	return ret;
}


Тем кто не хочет все устанавливать и сам компилировать. Вот готовые релизы.


На этом все. Жду комментариев.