Детальный разбор TaxOid [1/2]
ДИСКЛЕЙМЕР: Всё нижесказанное относится, в большей степени к TaxOid 2.6. Все фрагменты кода я публикую в виде Java-псевдокода на JaDX
Общая структура
/assets/
— история изменений в формате HTML (ru + uk) / Лицензия/fabric/
— служебные файлы библиотеки fabric (статистика и отчёты об ошибках)/lib/
— скомпилированные нативные библиотеки (libyandexmapkit.so
) для работы карт Яндекс в приложении/META-INF/
— подпись приложения/okhttp3/
— служебные файлы библиотеки OkHttp v3 (работа с сетью)/res/
— ресурсы приложения (иконки/разметка/звуки и т.д.)AndroidManifest.xml
— паспорт приложенияclasses.dex
— скомпилированный код приложенияresources.arsc
— строковые ресурсы приложения
Код
Основной код приложения находится в classes.dex, а именно, в двух пакетах:
/com/pk/
/com/a/
Рассмотрим поподробнее:
Анимация интерфейса
com/pk/taxoid/a/e.smali
— класс управляющий интерфейсом списка заказов. Отвечает за отображения готовых заказов, выводит предупреждения о том, что приложение устарело/позывной заблокирован/сеанс уже запущен на другом устройстве.
Для удобства я буду предоставлять более понятный и удобный Java-код, вместо smali:
private void e(JSONObject jSONObject) throws Exception { String string; int i = 0; this.l.setVisibility(0); g(); if (this.e.d()) { this.i.setCurAddress(this.e.a()); } if (jSONObject.has("error")) { string = jSONObject.getString("error"); Object obj = -1; int hashCode = string.hashCode(); if (hashCode != 327580607) { if (hashCode == 2099550724 && string.equals("wrong_session")) { obj = null; } } else if (string.equals("driver_not_found")) { obj = 1; } switch (obj) { case null: i(); return; case 1: j(); return; } } if (jSONObject.has("road_from") && !jSONObject.isNull("road_from")) { string = jSONObject.getString("road_from"); if (!this.c.U().equals(string)) { this.c.a(string); } } JSONArray jSONArray = jSONObject.getJSONArray("status"); int i2 = 0; while (i2 < jSONArray.length()) { JSONObject jSONObject2 = jSONArray.getJSONObject(i2); com.pk.taxoid.c.b.a aVar = (com.pk.taxoid.c.b.a) this.f.get(i2); if (jSONObject2.getString("order").equals("locked")) { CharSequence charSequence = BuildConfig.FLAVOR; try { charSequence = jSONObject2.getString("fmanid"); } catch (Exception unused) { if (!charSequence.equals(BuildConfig.FLAVOR)) { while (i < this.f.size()) { if (((com.pk.taxoid.c.b.a) this.f.get(i)).c().contains(charSequence)) { aVar = (com.pk.taxoid.c.b.a) this.f.get(i); } else { i++; } } } a(aVar.f(), jSONObject2.getString("reason"), Long.valueOf(!jSONObject2.isNull("ftime_unlock") ? jSONObject2.getLong("ftime_unlock") : 0)); return; } } else if (jSONObject2.isNull("order")) { if (!(jSONObject2.isNull("message") || this.c.O())) { a(jSONObject2.getString("message"), aVar); } i2++; } else { a(jSONObject2, aVar); return; } } this.d.a(jSONObject.getJSONObject("orders"), this.f, getActivity()); h(); this.u.removeCallbacks(this.t); this.u.postDelayed(this.t, 2000); if (this.c.c() == 3) { Iterator it = this.d.iterator(); while (it.hasNext()) { com.pk.taxoid.c.b.b bVar = (com.pk.taxoid.c.b.b) it.next(); if (!this.d.d(bVar)) { if (VERSION.SDK_INT >= 21) { this.n.speak(bVar.t(), 0, null, null); } else { this.n.speak(bVar.t(), 0, null); } bVar.f(true); this.d.c(bVar); } } } if (this.c.B()) { f(jSONObject); } }
Этот метод работает с уже готовыми данными, которые поступают в виде входного параметра jSONObject:
private void e(JSONObject jSONObject) throws Exception {
Но не это главное, вот 3 интересных строчки:
this.d.a(jSONObject.getJSONObject("orders"), this.f, getActivity()); h(); this.u.removeCallbacks(this.t); this.u.postDelayed(this.t, 2000);
Коротко о том, что здесь происходит:
- Вызывается метод
a()
из классаd
, который получает на вход список заказов - Вызывается метод
h()
- Удаляются каллбэки изпеременной
t
- Через 2 секунды объект
t
начинает работу по-новой
Четвертая строчка самая интересная. Но что такое t? Где и как его посмотреть?
Смотрим объявление в классе:
private Runnable t = new -$Lambda$e$HKo-aGZ4iWu9ypto2NekUDTU9n4(this);
t - это анонимный класс -$Lambda$e$HKo-aGZ4iWu9ypto2NekUDTU9n4
переходим в com/pk/taxoid/a/-$Lambda$e$HKo-aGZ4iWu9ypto2NekUDTU9n4.smali
package com.pk.taxoid.a; /* compiled from: lambda */ public final /* synthetic */ class -$Lambda$e$HKo-aGZ4iWu9ypto2NekUDTU9n4 implements Runnable { private final /* synthetic */ e f$0; public /* synthetic */ -$Lambda$e$HKo-aGZ4iWu9ypto2NekUDTU9n4(e eVar) { this.f$0 = eVar; } public final void run() { this.f$0.l(); } }
Что происходит здесь? Вызывается метод l()
из класса e
Возвращаемся в класс e
:
private /* synthetic */ void l() { this.l.setVisibility(4); }
this.l
- это прогресс-бар (полоска сразу под тулбаром). Т.е. каждые 2 секунды полоска становится видимо. Чисто интерфейс. Скорость загрузки заказов не изменяется
Подготовка заказов
Класс com/pk/taxoid/a/c.smali
отвечает за детали заказа (время, начальная точка, конечная точка и т.д.)
Тут приложение получает данные о заказе, такие как время, точка "A", точка "B" и т.д.
public void a(JSONObject jSONObject) { try { TextView textView; CharSequence a; String string = jSONObject.getString("point"); if (string.equals("0")) { a(); } else if (string.equals("A")) { b(); } this.H = string; JSONObject jSONObject2 = jSONObject.getJSONObject("order"); Long valueOf = Long.valueOf(jSONObject2.getLong("F0") * 1000); this.j.setVisibility(0); this.j.setText(d.a(getString(R.string.time_of_get_order), d.a("HH:mm:ss", valueOf.longValue()))); if (jSONObject2.isNull("FA")) { long j = jSONObject.getLong("server_time") * 1000; if (!this.E) { this.D = ((long) this.B) - (j - valueOf.longValue()); } if (this.D <= 60000 && !this.E) { if (!(this.w.getVisibility() == 0 || this.y)) { this.w.setVisibility(0); this.x.setBackgroundColor(-65536); } if (this.D <= 0) { this.E = true; d.a((int) R.raw.driver_late); } if (this.D > 0 && this.D < 6000) { d.a((int) R.raw.beep_sound); } this.v.setTextColor(-65536); } if (this.E) { this.D = (j - valueOf.longValue()) - ((long) this.B); } textView = this.v; a = d.a("mm:ss", this.D); } else { this.k.setVisibility(0); textView = this.k; a = d.a(getString(R.string.time_of_point_a), d.a("HH:mm:ss", jSONObject2.getLong("FA"))); } textView.setText(a); } catch (JSONException unused) { this.b.a(this.A, "state"); } }
Формирование запроса на сервер
Класс com/pk/taxoid/network/c.smali
Запрос на сервер:
protected JSONObject a() { do { this.c = null; c localc1; if (!d()) { localc1 = c.b; } Object localObject; for (;;) { this.c = localc1; break; if (b.a(this.a).d()) { break; } try { b.a(this.a).a(); } catch (IOException localIOException) { c localc2 = c.a; } catch (ConnectException localConnectException) { c localc3 = c.d; } catch (UnknownHostException localUnknownHostException) { localObject = c.c; } } if (this.c == null) { try { if (c()) { return null; } b.a(this.a).a(this.d); localObject = a(b.a(this.a).c()); return (JSONObject)localObject; } catch (Exception localException) { b.a(this.a).b(); } } b(this.c); } while (!c()); return null; }
И еще (берется информация о водителе):
public void a(com.pk.taxoid.c.b.a aVar) { try { JSONObject jSONObject = new JSONObject(); jSONObject.put("action", "message_state"); jSONObject.put("fmanid", aVar.c()); jSONObject.put("ftaxi", aVar.e()); jSONObject.put("fdri", aVar.f()); a(jSONObject); } catch (JSONException e) { e.printStackTrace(); } }
И еще (палится id сессии/ос/версия приложения)
public void a(com.pk.taxoid.c.b.b bVar) { try { com.pk.taxoid.c.b.a E = bVar.E(); JSONObject jSONObject = new JSONObject(); jSONObject.put("action", "assign"); jSONObject.put("fmanid", E.c()); jSONObject.put("session", E.d()); jSONObject.put("ftaxi", E.e()); jSONObject.put("order_id", bVar.h()); jSONObject.put("os", "android"); jSONObject.put("version", 39); if (this.d != null) { this.d.interrupt(); } a(jSONObject); } catch (JSONException e) { e.printStackTrace(); } }
public void a(com.pk.taxoid.c.b.b bVar, String str, long j) { if (str.equals("assign") && this.g) { b(bVar, str); return; } if (str.equals("assign")) { this.g = true; } if (str.equals("state")) { this.g = false; } try { com.pk.taxoid.c.b.a E = bVar.E(); JSONObject jSONObject = new JSONObject(); jSONObject.put("action", str); jSONObject.put("fmanid", E.c()); jSONObject.put("session", E.d()); jSONObject.put("ftaxi", E.e()); jSONObject.put("order_id", bVar.h()); jSONObject.put("os", "android"); jSONObject.put("version", 39); if (this.d != null) { this.d.interrupt(); } if (j == 0) { a(jSONObject); } else { a(jSONObject, j); } } catch (JSONException e) { e.printStackTrace(); } }
А вот тут палят логин и пароль:
public void a(String str, String str2, String str3) { try { JSONObject jSONObject = new JSONObject(); jSONObject.put("action", str3); jSONObject.put("login", str); jSONObject.put("password", str2); a(jSONObject); } catch (JSONException e) { e.printStackTrace(); } }
Еще одна версия (палят логин/пароль/имей/мак-адрес/модель):
public void d() { try { JSONObject jSONObject = new JSONObject(); JSONArray jSONArray = new JSONArray(); Iterator it = this.a.iterator(); while (it.hasNext()) { com.pk.taxoid.c.b.a aVar = (com.pk.taxoid.c.b.a) it.next(); JSONObject jSONObject2 = new JSONObject(); jSONObject2.put("login", aVar.a()); jSONObject2.put("password", aVar.b()); jSONArray.put(jSONObject2); } Object obj = BuildConfig.FLAVOR; if (this.a.size() > 0) { obj = com.pk.taxoid.b.d.a((com.pk.taxoid.c.b.a) this.a.get(0)); } jSONObject.put("action", "get_session"); jSONObject.put("data", jSONArray); jSONObject.put("imei", com.pk.taxoid.b.d.a()); jSONObject.put("number", com.pk.taxoid.b.d.b()); jSONObject.put("model", com.pk.taxoid.b.d.d()); jSONObject.put("mac", com.pk.taxoid.b.d.c()); jSONObject.put("os", "android"); jSONObject.put("android", VERSION.RELEASE); jSONObject.put("version", 39); jSONObject.put("version_name", "2.6.0"); jSONObject.put("push_endpoint", this.b.ab()); jSONObject.put("city", obj); a(jSONObject, 1000); } catch (JSONException e) { e.printStackTrace(); } }
Вот метод a(), который принимает рандомное число, и стартит метод d.start()
c определенной задержкой (но на скорость загрузки заказов он не влияет)
private void a(JSONObject jSONObject, long j) { int nextInt; this.d = new a(jSONObject.toString()); if (j != 0) { Random random = this.f; int i = 1172; while (true) { nextInt = random.nextInt(i); if (nextInt >= 817) { break; } random = this.f; i = 1151; } } else { nextInt = 150; } this.d.a((long) nextInt); this.d.start(); }