Детальный разбор 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();
}