All Products
Search
Document Center

Web Application Firewall:Integrasikan SDK untuk Android

Last Updated:Jul 29, 2026

Untuk mengonfigurasi aturan berbasis skenario anti-crawler pada Bot Management di Konsol, Anda harus mengintegrasikan WAF App Protection SDK. Topik ini menjelaskan cara mengintegrasikan SDK ke dalam aplikasi Android.

Latar Belakang

App Protection SDK menandatangani permintaan dari klien aplikasi. Server Web Application Firewall (WAF) memverifikasi tanda tangan tersebut untuk mengidentifikasi risiko, memblokir permintaan serangan, dan melindungi aplikasi.

Batasan

  • Untuk Android, SDK mendukung arsitektur arm64-v8a dan armeabi-v7a.

  • Level API Android minimal adalah 16.

  • Metode init dapat memakan waktu. Untuk memastikan perlindungan penuh, tunggu minimal 2 detik antara pemanggilan metode init dan metode vmpSign. Penundaan ini direkomendasikan demi perlindungan optimal, tetapi tidak wajib. Anda dapat menyesuaikan penundaan ini sesuai kebutuhan bisnis, meskipun penundaan yang lebih singkat mungkin mengurangi efektivitas fitur keamanan.

  • Saat menggunakan ProGuard untuk obfuscasi kode, gunakan opsi -keep untuk mempertahankan metode SDK. Contohnya:

    -keep class com.aliyun.TigerTally.** {*;}
    -keep class com.aliyun.captcha.* {*;}
    -keepclassmembers,allowobfuscation class * {
         @com.alibaba.fastjson.annotation.JSONField <fields>;
    }
    -keep class com.alibaba.fastjson.** {*;}

Prasyarat

  • Dapatkan SDK untuk aplikasi Android Anda.

    Untuk mendapatkan SDK, ajukan ticket kepada ahli teknis produk kami.

    Catatan

    SDK Android mencakup dua file AAR: AliTigerTally_X.Y.Z.aar dan AliCaptcha_X.Y.Z.aar, dengan X.Y.Z sebagai nomor versi.

  • Anda telah memperoleh kunci otentikasi SDK (juga dikenal sebagai appkey).

    Setelah mengaktifkan Bot Management, login ke Konsol Web Application Firewall 3.0. Dari bilah menu atas, pilih kelompok sumber daya dan wilayah (Chinese Mainland atau Outside Chinese Mainland) untuk instans WAF. Di panel navigasi sebelah kiri, pilih Protection Config > Bot Management > Bot Management Template. Buka tab App Protection dan klik Obtain and Copy AppKey di pojok kanan atas untuk mengambil kunci otentikasi SDK. Kunci ini diperlukan untuk inisialisasi SDK dan harus disertakan dalam kode integrasi Anda.

    Catatan

    Setiap Akun Alibaba Cloud memiliki appkey unik untuk semua nama domain yang dilindungi oleh WAF. appkey ini digunakan untuk integrasi SDK di aplikasi Android, iOS, dan HarmonyOS.

    Contoh kunci otentikasi: ****OpKLvM6zliu6KopyHIhmneb_****u4ekci2W8i6F9vrgpEezqAzEzj2ANrVUhvAXMwYzgY_****vc51aEQlRovkRoUhRlVsf4IzO9dZp6nN_****Wz8pk2TDLuMo4pVIQvGaxH3vrsnSQiK****.

Langkah 1: Buat proyek

Di Android Studio, ikuti wizard konfigurasi untuk membuat proyek Android baru. Struktur direktori proyek adalah sebagai berikut:


tigertally-demo-apk [TigerTallyDemo]
  .gradle
  .idea
  app
    build
    libs
      AliCaptcha_xxx.aar
      AliTigerTally_xxx.aar
    src
      androidTest
      main
      test [unitTest]
    .gitignore
    build.gradle
    proguard-rules.pro
  gradle
    wrapper
      gradle-wrapper.jar
      gradle-wrapper.properties
  .gitignore
  .java-version
  build.gradle
  gradle.properties
  gradlew
  gradlew.bat
  local.properties
  settings.gradle
  tigertally.jks
  External Libraries
  Scratches and Consoles

Langkah 2: Integrasikan AAR

  1. Ekstrak file SDK tigertally-X.Y.Z-xxxxxx-android.tgz, lalu salin semua file AAR dari folder hasil ekstraksi ke direktori libs modul utama Anda (jalur pastinya tergantung pada konfigurasi proyek Anda).

  2. Buka file build.gradle aplikasi Anda dan tambahkan dependensi terhadap AliTigerTally_X.Y.Z.aar dan AliCaptcha_X.Y.Z.aar dari direktori libs.

    Penting

    Ganti X.Y.Z dalam nama file AliTigerTally_X.Y.Z.aar dan AliCaptcha_X.Y.Z.aar dengan nomor versi file AAR Anda.

    Konfigurasinya sebagai berikut:

    dependencies {
        // ...
        implementation files('libs/AliTigerTally_X.Y.Z.aar')
        implementation files('libs/AliCaptcha_X.Y.Z.aar')
      
        // dependensi library pihak ketiga
        implementation 'com.alibaba:fastjson:1.2.83_noneautotype'
        implementation 'com.squareup.okhttp3:okhttp:3.11.0'
        implementation 'com.squareup.okio:okio:1.14.0'
    }

Langkah 3: Filter arsitektur CPU SO

Jika proyek Anda belum menggunakan file SO, tambahkan konfigurasi berikut ke file build.gradle Anda.

android {
    defaultConfig {
        ndk {
            abiFilters 'arm64-v8a', 'armeabi-v7a'
        }
    }
}

Langkah 4: Minta izin

  • Izin wajib

    <uses-permission android:name="android.permission.INTERNET"/>
  • Izin opsional

    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Catatan

Untuk Android 6.0 dan versi lebih baru, Anda harus meminta izin android.permission.READ_EXTERNAL_STORAGE dan android.permission.WRITE_EXTERNAL_STORAGE secara dinamis.

Langkah 5: Tambahkan kode integrasi

1. Tambahkan file header

import com.alibaba.fastjson.*;
import com.aliyun.tigertally.*;

2. Siapkan penandatanganan data

  1. Tetapkan ID kustom untuk setiap pengguna akhir. Hal ini memungkinkan Anda mengonfigurasi kebijakan mitigasi WAF secara lebih fleksibel.

    /**
    * Menetapkan akun pengguna.
    *
    * @param account Akun pengguna.
    * @return Kode kesalahan.
    */
    public static int setAccount(String account)
    • Parameter:

      • account: String. String yang mengidentifikasi pengguna. Kami merekomendasikan penggunaan format yang telah didesensitisasi.

    • Nilai kembali: int. Mengembalikan 0 jika berhasil, atau -1 jika gagal.

    • Kode contoh:

      // Untuk tamu, Anda dapat melewatkan setAccount dan langsung menginisialisasi SDK. Setelah pengguna login, panggil setAccount dan inisialisasi ulang.
      String account = "user001";
      TigerTallyAPI.setAccount(account);
  2. Inisialisasi SDK dan lakukan pengumpulan data awal.

    Pengumpulan data awal mengumpulkan informasi perangkat satu kali. Anda dapat memanggil fungsi init lagi untuk melakukan pengumpulan baru sesuai kebutuhan bisnis Anda. Tersedia tiga mode pengumpulan: pengumpulan lengkap, pengumpulan privasi kustom, dan pengumpulan non-privasi. Mode non-privasi tidak mengumpulkan data dari bidang yang terkait privasi pengguna akhir, termasuk: imei, imsi, simSerial, wifiMac, wifiList, bluetoothMac, dan androidId.

    Catatan

    Pilih mode pengumpulan yang sesuai dengan persyaratan kepatuhan Anda dan pastikan integritas data. Data lengkap meningkatkan identifikasi risiko.

    // Callback inisialisasi
    public interface TTInitListener {
        // Parameter code menunjukkan kode status pemanggilan antarmuka.
        void onInitFinish(int code);
    }
    
    /**
     * Menginisialisasi SDK dengan callback.
     *
     * @param context Konteks aplikasi.
     * @param appkey Appkey SDK.
     * @param collectType Mode pengumpulan data.
     * @param otherOptions Parameter opsional.
     * @param listener Callback inisialisasi.
     * @return Kode kesalahan.
     */
    public static int init(Context context, String appkey, int collectType,
                           Map<String, String> otherOptions, TTInitListener listener);
    • Parameter:

      • context: Context. Konteks aplikasi Anda.

      • appkey: String. Appkey SDK Anda.

      • collectType: int. Mode pengumpulan. Nilai valid:

        Parameter

        Deskripsi

        Contoh

        TT_DEFAULT

        Mengumpulkan semua data.

        TigerTallyAPI.TT_DEFAULT

        TT_NO_BASIC_DATA

        Tidak mengumpulkan data perangkat dasar.

        Mencakup: nama perangkat (Build.DEVICE), versi Android (Build.VERSION#RELEASE), dan resolusi layar.

        TigerTallyAPI.X | TigerTallyAPI.Y

        (Menunjukkan bahwa X maupun Y tidak dikumpulkan. X dan Y merepresentasikan nama bidang item tertentu.)

        TT_NO_IDENTIFY_DATA

        Tidak mengumpulkan data pengenal perangkat.

        Mencakup: IMEI, IMSI, SimSerial, BuildSerial (SN), dan alamat MAC.

        TT_NO_UNIQUE_DATA

        Tidak mengumpulkan data pengenal unik.

        Mencakup: OAID, Google Advertising ID, dan Android ID.

        TT_NO_EXTRA_DATA

        Tidak mengumpulkan data perangkat tambahan.

        Mencakup: daftar aplikasi berbahaya/abu-abu, IP LAN, IP DNS, informasi Wi-Fi yang terhubung (SSID, BSSID), daftar Wi-Fi terdekat, informasi lokasi, dan informasi sensor.

        TT_NOT_GRANTED

        Tidak mengumpulkan data privasi apa pun yang disebutkan sebelumnya.

        TigerTallyAPI.TT_NOT_GRANTED

      • otherOptions: Map<String, String>. Parameter opsional untuk pengumpulan data. Bisa bernilai null. Parameter yang tersedia:

        Parameter

        Deskripsi

        Contoh

        IPv6

        Menentukan apakah akan menggunakan nama domain IPv6 untuk melaporkan informasi perangkat.

        • 0 (default): Gunakan nama domain IPv4.

        • 1: Gunakan nama domain IPv6.

        1

        Intl

        Menentukan apakah akan melaporkan informasi perangkat ke wilayah luar Tiongkok daratan.

        • 0 (default): Laporkan ke Tiongkok daratan.

        • 1: Laporkan ke wilayah luar Tiongkok daratan.

        1

        CustomUrl

        Nama domain server pelaporan data.

        https://cloudauth-device.us-west-1.aliyuncs.com

        CustomHost

        Host server pelaporan data.

        cloudauth-device.us-west-1.aliyuncs.com

        Catatan

        Untuk sebagian besar situs internasional, cukup atur parameter Intl. Untuk melaporkan data ke situs tertentu, atur parameter CustomUrl dan CustomHost. Situs yang tersedia meliputi:

        • Jika Intl diatur ke 0, data dilaporkan ke situs default di Tiongkok (Shanghai): https://cloudauth-device.cn-shanghai.aliyuncs.com

        • Jika Intl diatur ke 1:

          • Situs default adalah Singapura: https://cloudauth-device.ap-southeast-1.aliyuncs.com

          • Indonesia (Jakarta): https://cloudauth-device.ap-southeast-5.aliyuncs.com

          • AS (Silicon Valley): https://cloudauth-device.us-west-1.aliyuncs.com

          • Jerman (Frankfurt): https://cloudauth-device.eu-central-1.aliyuncs.com

          • Tiongkok (Hong Kong): https://cloudauth-device.cn-hongkong.aliyuncs.com

      • listener: TTInitListener. Callback inisialisasi SDK. Gunakan callback ini untuk memeriksa status inisialisasi. Bisa bernilai null.

        TTCode

        Kode

        Deskripsi

        TT_SUCCESS

        0

        SDK berhasil diinisialisasi.

        TT_NOT_INIT

        -1

        SDK belum diinisialisasi.

        TT_NOT_PERMISSION

        -2

        SDK belum diberikan izin Android yang diperlukan.

        TT_UNKNOWN_ERROR

        -3

        Kesalahan sistem tidak dikenal.

        TT_NETWORK_ERROR

        -4

        Kesalahan jaringan.

        TT_NETWORK_ERROR_EMPTY

        -5

        Kesalahan jaringan: respons kosong.

        TT_NETWORK_ERROR_INVALID

        -6

        Format respons jaringan tidak valid.

        TT_PARSE_SRV_CFG_ERROR

        -7

        Gagal mengurai konfigurasi server.

        TT_NETWORK_RET_CODE_ERROR

        -8

        Gateway mengembalikan respons kegagalan.

        TT_APPKEY_EMPTY

        -9

        Appkey kosong.

        TT_PARAMS_ERROR

        -10

        Parameter tidak valid.

        TT_FGKEY_ERROR

        -11

        Kesalahan perhitungan kunci.

        TT_APPKEY_ERROR

        -12

        Ketidakcocokan antara versi SDK dan appkey.

    • Nilai kembali: int. Mengembalikan 0 jika berhasil, atau -1 jika gagal.

    • Kode contoh:

      // Appkey adalah kunci otentikasi yang ditetapkan di platform Alibaba Cloud.
      final String appkey="******";
      // Parameter opsional untuk mengonfigurasi IPv6 dan pelaporan internasional.
      Map<String, String> options = new HashMap<>();
      options.put("IPv6", "0");   // Gunakan IPv4.
      options.put("Intl", "0");   // Laporkan ke Tiongkok daratan.
      //options.put("Intl", "1"); // Laporkan ke wilayah luar Tiongkok daratan.
      
      // Laporkan ke AS (Silicon Valley).
      //options.put("CustomUrl", "https://cloudauth-device.us-west-1.aliyuncs.com"); 
      //options.put("CustomHost", "cloudauth-device.us-west-1.aliyuncs.com"); 
      
      // Pengumpulan data awal mengumpulkan informasi perangkat satu kali. Anda dapat memanggil fungsi init lagi untuk melakukan pengumpulan baru sesuai kebutuhan bisnis Anda.
      // Pengumpulan lengkap.
      int ret = TigerTallyAPI.init(this.getApplicationContext(), appkey, TigerTallyAPI.TT_DEFAULT, options, null);
      
      // Tentukan data privasi yang akan dikumpulkan. Anda dapat menggabungkan flag berbeda dengan operator "|".
      int privacyFlag = TigerTallyAPI.TT_NO_BASIC_DATA | TigerTallyAPI.TT_NO_UNIQUE_DATA;
      int ret = TigerTallyAPI.init(this.getApplicationContext(), appkey, privacyFlag, options, null);
      
      // Jangan kumpulkan bidang privasi.
      int ret = TigerTallyAPI.init(this.getApplicationContext(), appkey, TigerTallyAPI.TT_NOT_GRANTED, options, null);
      Log.d("AliSDK", "ret:" + ret);
  3. Hash data.

    Metode penandatanganan kustom ini menghitung hash dari data masukan dan mengembalikan string whash sebagai data tanda tangan kustom. Untuk permintaan POST, PUT, dan PATCH, berikan badan permintaan. Untuk permintaan GET dan DELETE, berikan URL lengkap. String whash harus ditambahkan ke bidang ali_sign_whash dalam Header permintaan HTTP.

    // Jenis permintaan:
    public enum RequestType { GET, POST, PUT, PATCH, DELETE }
    
    /**
     *  Hash data untuk tanda tangan kustom.
     *
     * @param type Jenis permintaan.
     * @param input Data yang akan di-hash.
     * @return String whash.
     */
    public static String vmpHash(RequestType type, byte[] input);
    • Parameter:

    • type: RequestType. Jenis permintaan. Nilai valid:

      • GET: Permintaan GET.

      • POST: Permintaan POST.

      • PUT: Permintaan PUT.

      • PATCH: Permintaan PATCH.

      • DELETE: Permintaan DELETE.

    • input: byte[]. Data yang akan di-hash.

    • Nilai kembali: String. Mengembalikan string whash.

    • Kode contoh:

      // Permintaan GET
      String url = "https://tigertally.aliyun.com/apptest";
      String whash = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.GET, url.getBytes());
      Log.d("AliSDK", "whash:" + whash);
      
      // Permintaan POST
      String body = "hello world";
      String whash = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.POST, body.getBytes());
      Log.d("AliSDK", "whash:" + whash);
      Catatan

      Metode ini tidak diperlukan untuk penandatanganan default. Untuk penandatanganan kustom, panggil metode ini untuk meng-hash data sebelum menandatangani.

  4. Menandatangani data.

    Metode ini menggunakan teknologi VMP untuk menandatangani data masukan dan mengembalikan string wtoken untuk otentikasi permintaan.

    /**
     * Menandatangani data.
     *
     * @param type Jenis tanda tangan.
     * @param input Data yang akan ditandatangani.
     * @return String wtoken.
     */
    public static String vmpSign(int type, byte[] input);
    • Parameter:

      • type: int. Jenis penandatanganan data. Nilainya harus 1.

      • input: byte[]. Data yang akan ditandatangani. Biasanya berupa seluruh badan permintaan atau string whash dari penandatanganan kustom.

    • Nilai kembali: String. Mengembalikan string wtoken.

    • Kode contoh:

      // Gunakan kode ini jika penandatanganan default dikonfigurasi di Konsol (penandatanganan kustom tidak dipilih).
      String body = "i am the request body, encrypted or not!";
      String wtoken = TigerTallyAPI.vmpSign(1, body.getBytes("UTF-8"));
      Log.d("AliSDK", "wToken:" + wtoken);
      
      // Gunakan kode ini jika penandatanganan kustom dikonfigurasi di Konsol.
      // Permintaan GET
      String url = "https://tigertally.aliyun.com/apptest";
      String whash = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.GET, url.getBytes());
      String wtoken = TigerTallyAPI.vmpSign(1, whash.getBytes());
      Log.d("AliSDK", "whash:" + whash + ", wtoken:" + wtoken);
      
      // Permintaan POST
      String body = "hello world";
      String whash = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.POST, body.getBytes());
      String wtoken = TigerTallyAPI.vmpSign(1, whash.getBytes());
      Log.d("AliSDK", "whash:" + whash + ", wtoken:" + wtoken);
      Catatan
      • Saat Anda memanggil vmpHash untuk penandatanganan kustom, parameter input metode vmpSign adalah string whash yang dihasilkan. Saat mengonfigurasi kebijakan anti-bot aplikasi, nilai Custom Signature Field harus diatur ke ali_sign_whash.

      • Saat Anda memanggil vmpHash untuk menghasilkan string whash untuk permintaan GET, pastikan URL input identik dengan URL akhir yang digunakan dalam permintaan jaringan. Perhatikan khusus encoding URL, karena beberapa framework secara otomatis meng-encode karakter Tionghoa atau parameter.

      • Parameter input metode vmpHash tidak mendukung array byte kosong atau string kosong. Jika input berupa URL, URL tersebut harus berisi path atau parameter.

      • Saat Anda memanggil vmpSign, jika badan permintaan kosong (misalnya, pada permintaan GET atau permintaan POST dengan badan kosong), berikan objek null atau nilai byte dari string kosong, seperti "".getBytes("UTF-8").

      • Jika nilai whash atau wtoken adalah salah satu string berikut, SDK telah mengembalikan kesalahan:

        • you must call init first: SDK belum diinisialisasi. Panggil init() terlebih dahulu.

        • you must input correct data: Data masukan tidak valid.

        • you must input correct type: Jenis input tidak valid.

3. Lakukan verifikasi sekunder

  1. Evaluasi hasil.

    Periksa bidang cookie dan badan dalam respons untuk menentukan apakah verifikasi sekunder diperlukan. Sebelum memanggil metode ini, gabungkan beberapa entri Set-Cookie menjadi satu string cookie.

    /**
     * Menentukan apakah perlu melakukan verifikasi sekunder.
     *
     * @param cookie String cookie.
     * @param body String badan.
     * @return 0 untuk lolos, atau 1 untuk verifikasi sekunder diperlukan.
     */
    public static int cptCheck(String cookie, String body)
    • Parameter:

      • cookie: String. Semua cookie dari respons HTTP.

      • body: String. Seluruh body dari tanggapan HTTP.

    • Nilai kembali: int. Mengembalikan 0 jika permintaan lolos, atau 1 jika verifikasi sekunder diperlukan.

    • Kode contoh:

      String cookie = "key1=value1;kye2=value2;";
      String body = "....";
      int recheck = TigerTallyAPI.cptCheck(cookie, body);
      Log.d("AliSDK", "recheck:" + recheck);
  2. Buat slider.

    Jika cptCheck mengembalikan 1, buat objek slider. Objek TTCaptcha menyediakan metode show() dan dismiss() untuk menampilkan dan menyembunyikan jendela slider. TTOption membungkus parameter yang dapat dikonfigurasi untuk slider, dan TTListener menyediakan callback untuk status sukses dan gagal. Untuk menggunakan jendela slider kustom, berikan URL halaman kustom. File HTML lokal maupun halaman remote didukung.

    /**
     * Membuat objek slider.
     *
     * @param activity Aktivitas tempat slider ditampilkan.
     * @param option Parameter slider.
     * @param listener Callback untuk slider.
     * @return Objek verifikasi slider.
     */
    public static TTCaptcha cptCreate(Activity activity, TTOption option, TTListener listener);
    
    
    /**
     * Objek slider.
     */
    public class TTCaptcha {
        /**
         * Menampilkan slider.
         */
        public void show();
    
        /**
         * Menyembunyikan slider.
         */
        public void dismiss();
    
        /**
         * Mendapatkan traceId slider untuk statistik data.
         */
        public String getTraceId();
    }
    
    /**
     * Parameter slider.
     */
    public static class TTOption {
        // Menentukan apakah slider dapat ditutup dengan mengklik area kosong.
        public boolean cancelable;
    
        // Halaman kustom. File HTML lokal dan URL remote didukung.
        public String customUri;
    
        // Bahasa.
        public String language;
    }
    
    /**
     * Callback untuk event slider.
     */
    public interface TTListener {
        /**
         * Dipanggil saat verifikasi berhasil.
         *
         * @param captcha Objek slider.
         * @param data Token. Default-nya adalah certifyId.
         */
        void success(TTCaptcha captcha, String data);
    
        /**
         * Dipanggil saat verifikasi gagal.
         *
         * @param captcha Objek slider.
         * @param code Kode kesalahan.
         */
        void failed(TTCaptcha captcha, String code);
    }
    • Parameter:

      • activity: Activity. Aktivitas halaman saat ini.

      • option: TTOption. Parameter konfigurasi slider.

      • listener: TTListener. Callback status slider.

    • Nilai kembali: TTCaptcha. Objek slider.

    • Kode contoh:

    TTCaptcha.TTOption option = new TTCaptcha.TTOption();
    // option.customUri  = "file:///android_asset/ali-tt-captcha-demo.html";
    option.language   = "cn";
    option.cancelable = false;
    
    TTCaptcha captcha = TigerTallyAPI.cptCreate(this, option, new TTCaptcha.TTListener() {
        @Override
        public void success(TTCaptcha captcha, String data) {
            Log.d(TAG, "captcha check success:" + data);
        }
        @Override
        public void failed(TTCaptcha captcha, String code) {
            Log.d(TAG, "captcha check failed:" + code);
        }
    });
    captcha.show();
    Catatan

    Kegagalan verifikasi menunjukkan bahwa anomali terdeteksi setelah pengguna menyelesaikan tantangan slider.

    Daftar berikut menjelaskan kode kesalahan:

    • 1001: Verifikasi gagal.

    • 1002: Pengecualian sistem.

    • 1003: Parameter tidak valid.

    • 1005: Verifikasi dibatalkan.

    • 8001: Gagal menampilkan slider.

    • 8002: Data verifikasi slider abnormal.

    • 8003: Pengecualian internal verifikasi slider.

    • 8004: Kesalahan jaringan.

Contoh praktik terbaik

package com.aliyun.tigertally.apk;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.aliyun.TigerTally.TigerTallyAPI;
import com.aliyun.TigerTally.captcha.api.TTCaptcha;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class DemoActivity extends AppCompatActivity {
    private final static String TAG = "TigerTally-Demo";

    private final static String APP_HOST = "******";
    private final static String APP_URL  = "******";
    private final static String APP_KEY  = "******";

    private final static OkHttpClient okHttpClient = new OkHttpClient();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);

        doTest();
    }

    private void doTest() {
        Log.d(TAG, "captcha flow");
        new Thread(() -> {
            // Inisialisasi SDK.
            Map<String, String> options = new HashMap<>();
            //options.put("Intl", "1"); // Untuk mengaktifkan pelaporan internasional.
            // Gunakan mode pengumpulan data lengkap.
            int ret = TigerTallyAPI.init(this, APP_KEY, TigerTallyAPI.TT_DEFAULT, options, null);
            // Atau, jangan kumpulkan bidang privasi.
            // int ret = TigerTallyAPI.init(this, APP_KEY, TigerTallyAPI.TT_NOT_GRANTED, null, null);
            Log.d(TAG, "tiger tally init: " + ret);

            // Tunggu hingga inisialisasi selesai.
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


            // Tandatangani data.
            String data = "hello world";
            String whash = null, wtoken = null;
            // Gunakan penandatanganan kustom.
            whash = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.POST, data.getBytes());
            wtoken = TigerTallyAPI.vmpSign(1, whash.getBytes());
            Log.d(TAG, "tiger tally vmp: " + whash + ", " + wtoken);

            // Gunakan penandatanganan standar.
            // wtoken = TigerTallyAPI.vmpSign(1, data.getBytes());
            // Log.d(TAG, "tiger tally vmp: " + wtoken);


            // Kirim permintaan.
            doPost(APP_URL, APP_HOST, whash, wtoken, data, (code, cookie, body) -> {
                // Periksa apakah slider diperlukan.
                int recheck = TigerTallyAPI.cptCheck(cookie, body);
                Log.d(TAG, "captcha check result: " + recheck);

                if (recheck == 0) return;
                this.runOnUiThread(this::doShow);
            });
        }).start();
    }

    // Tampilkan slider.
    public void doShow() {
        Log.d(TAG, "captcha show");

        TTCaptcha.TTOption option = new TTCaptcha.TTOption();
        // option.customUri = "file:///android_asset/ali-tt-captcha-demo.html";
        option.language   = "cn";
        option.cancelable = false;

        TTCaptcha captcha = TigerTallyAPI.cptCreate(this, option, new TTCaptcha.TTListener() {
            @Override
            public void success(TTCaptcha captcha, String data) {
                Log.d(TAG, "captcha check success:" + data);
            }

            @Override
            public void failed(TTCaptcha captcha, String code) {
                Log.d(TAG, "captcha check failed:" + code);
            }
        });

        captcha.show();
    }

    // Kirim permintaan.
    public static void doPost(String url, String host, String whash, String wtoken, String body, Callback callback) {
        Log.d(TAG, "start request post");

        int responseCode = 0;
        String responseBody = "";
        StringBuilder responseCookie = new StringBuilder();
        try {
            Request.Builder builder = new Request.Builder()
                    .url(url)
                    .addHeader("wToken", wtoken)
                    .addHeader("Host",   host)
                    .post(RequestBody.create(MediaType.parse("text/x-markdown"), body.getBytes()));

            if (whash != null) {
                builder.addHeader("ali_sign_whash", whash);
            }
            Response response = okHttpClient.newCall(builder.build()).execute();

            responseCode = response.code();
            responseBody = response.body() == null ? "" : response.body().string();
            for (String item : response.headers("Set-Cookie")) {
                responseCookie.append(item).append(";");
            }

            Log.d(TAG, "response code:" + responseCode);
            Log.d(TAG, "response cookie:" + responseCookie);
            Log.d(TAG, "response body:" + (responseBody.length() > 100 ? responseBody.substring(0, 100) : ""));

            if (response.isSuccessful()) {
                Log.d(TAG, "success: " + response.code() + ", " + response.message());
            } else {
                Log.e(TAG, "failed: " + response.code() + ", " + response.message());
            }

            response.close();
        } catch (Exception e) {
            e.printStackTrace();
            responseCode = -1;
            responseBody = e.toString();
        } finally {
            if (callback != null) {
                callback.onResponse(responseCode, responseCookie.toString(), responseBody);
            }
        }
    }

    public interface Callback {
        void onResponse(int code, String cookie, String body);
    }
}