# v3 Integration

## Giới thiệu

\- Sử dụng Webpack và TailwindCSS để tối ưu kích thước tập tin SDK cần nhúng\
\- Áp dụng Vanilla JS để xử lý hiển thị lẫn tính năng, đem lại hiệu năng tốt nhất\
\- Tự động giữ kết nối với tổng đài mỗi khi trạng thái kết nối mạng thay đổi\
\- Tính điểm chất lượng tín hiệu của cuộc gọi realtime

## Tích hợp

### Example

{% file src="<https://3454047468-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LukDt3aTxo8xMLonIwO%2Fuploads%2FEczF9RFVmb7EU5Kg89ev%2FDefault-UI.html.zip?alt=media&token=b2cd3640-0866-4529-98c8-234184dd8c18>" %}

Bên trên là code mẫu luồng tích hợp và sử dụng SDK với UI mặc định.

### Nhúng script

* **Cách 1**:  thông qua CDN, khai báo thẻ \<script>

```html
<body>
    <!-- Your app HTML -->
    <script
        omi-call-sdk
        type="text/javascript"
        src="https://cdn.omicrm.com/sdk/web/{{version}}/core.min.js"
    ></script>
</body>
```

Để lấy thông tin {{version}}, xem tại: [](https://api.omicall.com/sdk/web-sdk "mention") hoặc [v3-changelog](https://api.omicall.com/sdk/web-sdk/v3-changelog "mention")

* **Cách 2**: Thông qua `npm` : comming soon...

### Khởi tạo

Sau khi script `core.min.js` được load xong, sẽ có thêm biến global `window.OMICallSDK`

**`OMICallSDK`**: biến `global` chính của call SDK, quản lý kết nối tới tổng đài và xử lý các sự kiện của cuộc gọi.

**`OMICallUI`**: biến `global` khi sử dụng UI mặc định của SDK, có một số method để chủ động tương tác với UI mặc định.&#x20;

Dưới đây là mô tả các tuỳ chọn để khởi tạo SDK:

```groovy
type UIConfigs = {
    /*
     *  toggleDial: trạng thái hiển thị nút mở giao diện nhập số gọi ra
     * default: 'show'
     */
    toggleDial?: 'show' | 'hide';
    /*
     * dialPosition: vị trí hiển thị nút mở giao diện nhập số gọi ra
     * default: 'right'
     */
    dialPosition?: 'left' | 'right';
    /*
     * theme: màu sắc của giao diện SDK
     */
    theme?: {
        white: '#ffffff';
        black: '#000000';
        primary: '#1e3150';
        info: '#00b1ff';
        success: '#56cc6e';
        warning: '#fea220';
        error: '#ff5955';
        pink: '#e82a8f';
        purple: '#a540b8';
        blue: '#57bfdb';
    };
    /*
     * cấu hình tự động hiển thị cuộc gọi mới ở chế độ thu gọn
     * áp dụng cả cuộc gọi ra lẫn gọi vào
     * default: false
     */
    minimizeNewCall?: boolean;
};
```

```typescript
type CtaButtonOnClick = (callData: CallData) => void;

type CtaButton = {
    action: string;
    label: string;
    /*
     * get icon `code` from:
     * https://cdn.omicrm.com/common/icon/fluent-systems-filled/index.json
     */
    icon: string;
    /*
     * override callback chung cho riêng button này
     */
    onClick?: CtaButtonOnClick;
};

type CallTab = {
    id: string;
    label: string;
    /*
     * Các cách để xử lý hiển thị nội dung tuỳ biến:
     *
     * #1: set trực tiếp HTML/CSS cần thiết
     * tabNode.innerHTML = '<div>your HTML/CSS</div>';
     *
     * #2: với React 18+, dùng thư viện react-dom/client
     * createRoot(tabNode).render(<YourComponent />);
     *
     * #3: các framework khác tương tự ReactJS
     */
    render: (
        callData: CallData,
        tabNode: HTMLDivElement,
        tabDetail: {
            id: string;
            label: string;
        },
    ) => void;
};
```

```typescript
type SdkInitConfigs = {
    /*
     * lng: ngôn ngữ hiển thị trên UI
     * default: 'vi'
     */
    lng?: 'vi' | 'en' | 'km';
    /*
     * ui: cấu hình giao diện mặc định của SDK
     */
    ui?: UIConfigs;
    /*
     * control ringtone volume
     * default: 1
     */
    ringtoneVolume?: number /* 0 -> 1 */;
    /*
     * control phone number masking
     */
    phoneMasked?: {
        enable: boolean;
        /*
         * length: độ dài cho phép hiển thị số
         */
        len: number;
        /*
         * character: *, #, x, ✨, ...
         */
        char: string;
        /*
         * ltr: left-to-right
         */
        ltr: boolean;
    };
    /*
     * show list of CTA buttons, under remote contact info of calling dialog
     */
    ctaButtons?: {
        unknown?: CtaButton[]; // show when `remoteContact` is null
        exists?: CtaButton[]; // show when `remoteContact` is not null
        base?: CtaButton[]; // always show these buttons
        onClick?: CtaButtonOnClick; // callback chung cho tất cả cta button
    };
    /**
     * thay thế UI "Thông tin cuộc gọi" bằng UI dạng tabs
     * vẫn giữ lại giao diện cũ dưới dạng tab cuối cùng
     */
    callTabs?: CallTab[];
    /**
     * your main app's root body element
     * use for app need to passing a custom tab component into call dialog
     * use this option if your component required `HOCs` or `Context Provider`
     */
    rootBody?: HTMLElement;
    /**
     * custom search remote contact function
     */
    searchRemoteContact?: (callData: CallData) => Promise<RemoteContact | null>;
    /**
     * check the permission to make a call is allowed or not
     */
    validateCallOut?: (
        remoteNumber: string,
        options: MethodMakeCallOptions,
    ) => Promise<{ status: boolean; reason?: string }>;
    /**
     * list of custom icons using in default UI
     * will be loaded and caching
     */
    icons: string[];
    sounds?: Record<
        | 'ringtone' /* sound when have incoming call */
        | 'ringing' /* sound when outgoing call don't have "early-media" */
        | 'accepted'
        | 'ended',
        {
            src: string;
            volume: number /* 0 -> 1 */;
        }
    >;
    /**
     * control searching recent call from OMICall or not
     */
    searchRecentCall?: boolean;
};
```

<pre class="language-typescript"><code class="lang-typescript"><strong>const SdkInitConfigs: Partial&#x3C;SdkInitConfigs> = {
</strong>    /* tuỳ chỉnh cấu hình */
};
const initSuccess: Boolean = await OMICallSDK.init(SdkInitConfigs);
if (!initSuccess) {
    // nếu khởi tạo SDK có lỗi, không thực hiện register tổng đài
    return;
}
</code></pre>

### Kết nối

```typescript
type SdkRegisterConfigs = {
    /**
     * `isGuest`: skip all authentication and internal APIs
     * still allow to connect and make call with switchboard
     */
    isGuest?: boolean;
    sipRealm: string;
    sipUser: string;
    sipPassword: string;
};

type SdkRegisterResult = {
    status: boolean;
    message?: string;
    error?: string;
};

/* Thông tin số nội bộ */
const sipRealm = 'demo01'; // domain tổng đài
const sipUser = '100'; // số nội bộ
const sipPassword = 'ABC123!@#'; // mật khẩu của số nội bộ

// Để kết nối tới tổng đài
const registerStatus: SdkRegisterResult = await OMICallSDK.register({
    sipRealm,
    sipUser,
    sipPassword,
} as SdkRegisterConfigs);

// Để ngắt kết nối tới tổng đài
OMICallSDK.unregister();
```

Để lấy thông tin số nội bộ:

* **Cách 1**: dùng API public tại [#danh-sach-so-noi-bo](https://api.omicall.com/omicall-api/call-center#danh-sach-so-noi-bo "mention")
* **Cách 2**: truy cập màn hình **Cấu hình ▸ Tổng đài ▸ Số nội bộ** trong website OMICall

### Methods

1. Để gọi nhanh một số sđt, sử dụng function: <br>

   ```typescript
   type SipNumber = {
       number: string;
   };
   type RemoteContact = {
       name: string;
       avatar?: string; // URL | Base64
       gender?: 'male' | 'female' | 'other';
   };
   type MakeCallOptions = {
       isVideo?: boolean;
       sipNumber?: SipNumber;
       remoteContact?: RemoteContact;
       /* userData: custom data, will be attach to call history */
       userData?: string;
   };
   const remoteNumber: string = ''; /* phone number or Zalo User ID */
   const options: MakeCallOptions | null = null;
   OMICallSDK.makeCall(remoteNumber, options);
   ```
2. Để mở/ đóng cửa sổ nhập số gọi ra của UI mặc định: `OMICallUI.toggleDial();`
3. Để thực hiện tạo phiên gọi tới số điện thoại cho các phiên đăng nhập (ví dụ thao tác click-2-call từ web để gọi ra nhanh cho 1 SĐT bằng máy bàn):<br>

   ```javascript
   const remoteNumber = ''; // Số điện thoại cần gọi, bắt buộc
   const sipNumber = ''; // Số hotline muốn dùng để gọi ra, không bắt buộc
   OMICallSDK.remoteCall(remoteNumber, sipNumber);
   ```

### Sự kiện & Dữ liệu

```typescript
/*
  Một eventName có thể đăng ký bởi nhiều callback function, vậy nên khi huỷ đăng ký,
  cần truyền đúng eventCallback đã đăng ký trước đó
*/
OMICallSDK.on(eventName, eventCallback);
OMICallSDK.off(eventName, eventCallback);

type EventName =
  // event trạng thái kết nối tổng đài, callback data: `type RegisterData`
  'register'
  // các events của cuộc gọi, callback data: `type CallData`
  | 'connecting' // khi cuộc gọi ra bắt đầu kết nối
  | 'ringing' // khi cuộc gọi bắt đầu đổ chuông
  | 'on_ringing' // mỗi giây, khi đang đổ chuông
  | 'accepted' // khi cuộc gọi được bắt máy
  | 'on_calling' // mỗi giây, khi đang trong cuộc gọi đã bắt máy
  | 'set_sip_number' // khi có được số hotline đang dùng để gọi ra
  | 'ended' // khi cuộc gọi kết thúc
;

type RegisterData = {
    status: 'connecting' | 'connected' | 'disconnect';
    name: string; // đa ngôn ngữ của trạng thái kết nối
};

type CallData = {
    uid: string; // UUID tự sinh
    uuid?: string; // UUID từ tổng đài
    state: 'connecting' | 'ringing' | 'accepted' | 'ended'; // trạng thái hiện tại
    direction: 'outbound' | 'inbound';
    isVideo: boolean;
    isOutbound: boolean;
    isInternal: boolean;
    isHangup?: boolean; // khi người dùng chủ động từ chối cuộc gọi
    sipNumber?: SipNumber;
    remoteContact?: RemoteContact;
    remoteNumber: string;
    displayNumber: string; // áp dụng theo phân quyền ẩn sđt
    streams: Record<'local' | 'remote', MediaStream>;
    players: Record<'local' | 'remote', HTMLVideoElement>;
    startTs: number; // thời gian bắt đầu cuộc gọi, Timestamp
    startAt: string; // thời gian bắt đầu cuộc gọi, format: HH:mm:ss
    held: boolean; // trạng thái giữ cuộc gọi
    audio: boolean; // trạng thái bật/ tắt microphone
    video: boolean; // trạng thái bật/ tắt camera
    ua: string; // window.navigator.userAgent
    devices: {
        audio: boolean;
        video: boolean;
    }; // trạng thái khả dụng của thiết bị microphone/ camera
    rtcpStat: any; // thống kê chất lượng kết nối của cuộc gọi
    rejectCode?: string; // mã lỗi từ tổng đài
    blockedProvider?: string; // nhà mạng của SĐT từ chối cuộc gọi
    ringingDuration: CallDuration;
    callingDuration: CallDuration;
    userData?: string;
    // internal data
    isEnded?: boolean; // trạng thái cuộc gọi đã kết thúc
    // các function để tương tác với cuộc gọi
    end: () => void; // hành động chung để kết thúc cuộc gọi
    decline: () => void; // hoàn toàn kết thúc cuộc gọi tới, không tiếp tục phân bổ cuộc gọi
    accept: () => void;
    mute: (cb?: () => boolean) => void; // bật/ tắt microphone, `cb` trả về trạng thái mới
    camera: (cb?: () => boolean) => void; // bật/ tắt camera, `cb` trả về trạng thái mới
    hold: (cb?: () => boolean) => void; // bật/ tắt giữ cuộc gọi, `cb` trả về trạng thái mới
    dtmf: (tone: string) => void; // gửi tương tác bấm phím, mỗi `tone` là 1 ký tự [0-9*#]
    transfer: (target: string) => void; // target: số nội bộ, nhóm nội bộ, nhóm bên ngoài, tương tác bấm phím
    save: (
        info: {
            note?: string;
            tags?: string[];
        },
        onSave: () => void,
        onFinish: () => boolean
    ) => void; // gọi function này, khi bạn muốn lưu note hoặc tags vào OMICall
    /**
     * cho phép chủ động trigger việc phóng to/ thu nhỏ UI của call dialog
     * @param getCustomPosition: cho phép tuỳ biến vị trí tiếp theo sẽ hiển thị, sau khi cập nhật kích thước UI
     */
    minimize: (getCustomPosition?: (data: { width: number; height: number }) => { x: number; y: number }) => void;
};

type SipNumber = {
    // thông tin luôn có
    number: string;
    /*
        Các data khác chỉ có khi gọi bằng UI mặc định
        Cấu trúc riêng của OMICall, có thể thay đổi trong tương lai
        Không khuyến khích dử sụng
    */
};

type RemoteContact = {
    name: string;
    avatar?: string; // URL | Base64
    gender?: 'male' | 'female' | 'other';
};

type CallDuration = {
    value: number; // 0
    text: string; // '00:00'
};
```

### Giao diện tuỳ biến

Khi sử dụng SDK cho giao diện tuỳ biến, hãy đảm bảo thực hiện đủ các nghiệp vụ bên dưới:\
1\. Sau khi đã đảm bảo SDK đã được tải, hãy [#khoi-tao](#khoi-tao "mention") SDK \
2\. [#ket-noi](#ket-noi "mention") tới tổng đài\
3\. Đăng ký các sự kiện kết nối và trạng thái của cuộc gọi theo [#su-kien-and-du-lieu](#su-kien-and-du-lieu "mention")\
4\. Cache lại giá trị `Object` `CallData` trong event callback phía dưới:\
&#x20;  \- Event `connecting` cho cuộc gọi ra\
&#x20;  \- Event `ringing` cho cuộc gọi đến\
5\. Render 2 thẻ `<video />` ứng với 2 người trong cuộc gọi (nên để 2 thẻ này là children của dialog cuộc gọi tuỳ biến):

```html
<video 
    id="${callData.uid}-local"
    style="display:none"
    playsinline autoplay hidden mute
/>
<video
    id="${callData.uid}-remote"
    style="display:none"
    playsinline autoplay hidden
/>
```

6\. Dùng các method trong `CallData` để:\
&#x20;\- Nhận cuộc gọi: `callData.accept();`\
&#x20;\- Từ chối hoặc kết thúc cuộc gọi: `callData.end();`\
&#x20;\- Bật/ Tắt microphone: `callData.mute(cb);`\
&#x20;\- Bật/ Tắt camera: `callData.camera(cb);`\
&#x20;\- Bật/ Tắt giữ cuộc gọi: `callData.hold(cb);`\
&#x20;\- Gửi tương tác bấm phím: `callData.dtmf(tone);`\
&#x20;\- Chuyển tiếp cuộc gọi: `callData.transfer();`\
&#x20;\- Lưu ghi chú hoặc Tag vào OMICall: `callData.save({ note:'', tags: ['test'] });`

## Giao diện mặc định

### Screenshots

<div><figure><img src="https://3454047468-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LukDt3aTxo8xMLonIwO%2Fuploads%2FWsFDN1IPCIFmaFbRD1Gs%2F01_img_dial_dialog.png?alt=media&#x26;token=9f56dbf8-021e-481d-b6dd-a6ae123133c0" alt=""><figcaption><p>UI gọi ra</p></figcaption></figure> <figure><img src="https://3454047468-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LukDt3aTxo8xMLonIwO%2Fuploads%2FR1CwwXDMaQHdGJcc1AjW%2F02_img_dial_tab_call_history.png?alt=media&#x26;token=3f329c5e-bdb8-47be-9501-96eb26c83240" alt=""><figcaption><p>Tab lịch sử cuộc gọi</p></figcaption></figure></div>

<div><figure><img src="https://3454047468-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LukDt3aTxo8xMLonIwO%2Fuploads%2FktI5SX7JUSUn8wDTsLDL%2F03_img_dial_tab_internal_number.png?alt=media&#x26;token=ad0c37e9-9f94-47f3-bde5-e74f2fd497e4" alt=""><figcaption><p>Tab toàn bộ số nội bộ của doanh nghiệp</p></figcaption></figure> <figure><img src="https://3454047468-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LukDt3aTxo8xMLonIwO%2Fuploads%2FrXNoCxYIGHWkzfYFLboN%2F04_img_dial_tab_key_interaction.png?alt=media&#x26;token=8830a4de-885e-4de5-86cc-e30709080d51" alt=""><figcaption><p>Tab toàn bộ cấu hình tương tác phím</p></figcaption></figure></div>

<div><figure><img src="https://3454047468-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LukDt3aTxo8xMLonIwO%2Fuploads%2F0eKymIZf8OrEifKQpVEk%2F05_img_sip_number_menu.png?alt=media&#x26;token=7528e9df-7bea-4fdf-aed9-03b8632804e5" alt=""><figcaption><p>UI chọn đầu số gọi ra</p></figcaption></figure> <figure><img src="https://3454047468-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LukDt3aTxo8xMLonIwO%2Fuploads%2FK8zACMO8bcgTqwrCEbHl%2F06_img_agent_call_setting_menu.png?alt=media&#x26;token=50322176-8e0a-4acc-957e-3cc243c60093" alt=""><figcaption><p>UI cấu hình cuộc gọi của nhân viên (theo phân quyền)</p></figcaption></figure></div>

<figure><img src="https://3454047468-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LukDt3aTxo8xMLonIwO%2Fuploads%2F9nDTmobKIvTdkVb6bcTq%2F07_img_calling_dialog_1.png?alt=media&#x26;token=45bd436b-901c-4c4c-836e-87344264965f" alt=""><figcaption><p>UI trong cuộc gọi + Input tương tác bấm phím</p></figcaption></figure>

<figure><img src="https://3454047468-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LukDt3aTxo8xMLonIwO%2Fuploads%2Ft0gRZqHBITkrNybM9xzf%2F08_img_calling_dialog_2.png?alt=media&#x26;token=ca8112b6-206a-4e51-86e2-78e522b8d709" alt=""><figcaption><p>UI trong cuộc gọi + Popup chuyển tiếp cuộc gọi</p></figcaption></figure>

<figure><img src="https://3454047468-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LukDt3aTxo8xMLonIwO%2Fuploads%2FynSfMhO75R6Fp63yyOob%2F09_img_calling_dialog_minimized_1.png?alt=media&#x26;token=9401f52e-c0ac-4410-9ca6-923bcb335289" alt=""><figcaption><p>UI gọi khi đang thu nhỏ</p></figcaption></figure>

<figure><img src="https://3454047468-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LukDt3aTxo8xMLonIwO%2Fuploads%2FmANW8LfK3Ay9fTlgF89k%2F10_img_calling_dialog_minimized_2.png?alt=media&#x26;token=94e77633-6148-47bb-957b-78ea02d93d5e" alt=""><figcaption><p>UI Gom cụm các cuộc gọi đang thu nhỏ</p></figcaption></figure>

<figure><img src="https://3454047468-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LukDt3aTxo8xMLonIwO%2Fuploads%2F7Z3zbtS4UIrhU7tF0lkv%2Fpreview%20new%20callCtas%20%2B%20callTabs.webp?alt=media&#x26;token=575b2e29-a95d-45fc-be3b-808ad779063b" alt=""><figcaption><p>UI tuỳ biến nút CTAs dưới thông tin người gọi và Tabview cho thông tin cuộc gọi</p></figcaption></figure>

### Tính năng

* [x] Hỗ trợ đa ngôn ngữ:
  * [x] Tiếng Việt
  * [x] Tiếng Anh
  * [x] Tiếng Cambodia
* [x] Nút ẩn/ hiện cửa sổ gọi ra
  * [x] Đổi được vị trí hiển thị: `ui.dialPosition = left | right`
  * [x] Có thể ẩn được nút ẩn/ hiện: `ui.toggleDial = show | hiden`
  * [x] Xem được trạng thái kết nối tổng đài
  * [x] Đổi được trạng thái kết nối tổng đài
* [x] Giao diện cửa sổ gọi ra\
  Tái hiện toàn bộ giao diện gọi ra của web OMICall. Tuỳ biến một số sang diện danh sách và lịch sử cuộc gọi dành riêng cho SDK
  * [x] Đổi được vị trí hiển thị: `ui.dialPosition = left | right`
  * [x] Giao diện bàn phím số
  * [x] Giao diện lịch sử cuộc gọi:\
    \- Tối ưu cho giao diện nhỏ gọn của SDK từ phiên bản web\
    \- Hiển thị tối đa 50 cuộc gọi trong 7 ngày gần nhất\
    \- Hỗ trợ tìm kiếm theo số điện thoại hoặc tên người gọi\
    \- Hỗ trợ nghe lại file ghi âm của cuộc gọi\
    \- Bấm để nhập nhanh vào ô số cần gọi ra
  * [x] Giao diện danh sách số nội bộ:\
    \- Hiển thị tất cả số nội bộ của doanh nghiệp\
    \- Tự động hiển thị kết quả trong khi nhập tìm kiếm\
    \- Bấm để nhập nhanh vào ô số cần gọi ra
  * [x] Giao diện danh sách nhóm nội bộ:\
    \- Hiển thị tất cả nhóm nội bộ của doanh nghiệp\
    \- Tự động hiển thị kết quả trong khi nhập tìm kiếm\
    \- Bấm để nhập nhanh vào ô số cần gọi ra
  * [x] Giao diện danh sách nhóm bên ngoài:\
    \- Hiển thị tất cả nhóm nội bộ của doanh nghiệp\
    \- Tự động hiển thị kết quả trong khi nhập tìm kiếm\
    \- Bấm để nhập nhanh vào ô số cần gọi ra
  * [x] Giao diện danh sách tương tác bấm phím:\
    \- Hiển thị tất cả tương tác bấm phím của doanh nghiệp\
    \- Tự động hiển thị kết quả trong khi nhập tìm kiếm\
    \- Bấm để nhập nhanh vào ô số cần gọi ra
  * [x] Cấu hình đầu số gọi ra:\
    \- Hiển thị đầu số gọi ra hiện tại của số nội bộ\
    \- Cho phép đổi "tạm thời" số dùng để gọi ra trong phiên kết nối này
  * [x] Cấu hình cuộc gọi cá nhân:\
    \- Xem được các cấu hình cuộc gọi đang áp dụng của số nội bộ:\
    &#x20;  \+ Remote Call: Cuộc gọi sẽ thực hiện trực tiếp trên IP Phone, Softphone\
    \- Cập nhật "tạm thời" cấu hình "Remote Call" cho các cuộc gọi của phiên kết nối này
  * [x] Hỗ trợ cuộc gọi Audio
  * [x] Hỗ trợ cuộc gọi Video
  * [x] Hỗ trợ gọi ra bằng đầu số ZCC: có đủ UX/UI cấp quyền và kiểm tra trạng thái được phép gọi tới sđt của tài khoản Zalo
* [x] Hỗ trợ hiển thị nhiều dialog cuộc gọi đồng thời
* [x] Hỗ trợ di chuyển vị trí của dialog cuộc gọi
* [x] Giao diện trong cuộc gọi

  \- Tái hiện toàn bộ\* giao diện và tính năng trong cuộc gọi của web OMICall\
  &#x20;  *\*Ngoại trừ  duy nhất tính năng "LazyCall"*\
  \- Tối ưu giao diện gọi Video. Hỗ trợ thay đổi giao diện khi camera của người gọi xoay hướng\
  \- Tối ưu giao diện gom nhóm khi có nhiều của sổ cuộc gọi đang hiển thị\
  \- Áp dụng đầy đủ phân quyền và cấu hình cuộc gọi như ở web OMICall
