Support Wide-Angle Camera in Android

Basic Information

본 가이드는 LG Dual Camera SDK를 기준으로 하며, 현재 SDK의 공식 지원 범위는 다음과 같습니다.

  • G5 전면 카메라 1 후면 카메라 2
  • V10 전면 카메라 2 후면 카메라 1
  • V20 전면 카메라 1 후면 카메라 2
  • G6 전면 카메라 2 후면 카메라 1

일반 / 광각 카메라를 지원하는 SDK는 별도 SDK가 아닌 Android의 SDK를 따른다.

따라서 기존 앱에 Camera API를 사용하고 있거나 Camera2를 사용하고 있거나 상관 없이 모두 지원 된다.

용어 정리

  1. CameraId: 카메라 하드웨어의 고유 번호. 0 부터 시작하며 0은 후면 카메라, 1은 전면 카메라인 경우가 상당히 많다.
  2. front-facing: 전면 카메라
  3. back-facing: 후면 카메라
  4. View Angle: 카메라에서 보여지는 각도 수치

광각 모드 / 일반 모드 차이

현재 LG Dual Camera SDK의 Accessing Camera 섹션에 자세한 내용이 나와있다.

여기서는 LG G6 기준으로 살펴보자.

  • CameraId: 0, Main Camera (back-facing), 13MP, 71′
  • CameraId: 1, Selfie Camera (front-facing), 5MP, 100′
  • CameraId: 2, Dual Camera (back-facing), 13MP, 125′

ID 1번의 경우 전면 카메라, ID 0과 2번은 후면 카메라인데 각도에 큰 차이가 보인다. 기본적으로 각도가 크면 클 수록 카메라 렌즈에 많은 방향을 담을 수 있는 이야기와 일치하며, 이를 광각 카메라 라고 부르는 것이다.

구현 상세

카메라 객체를 얻어올 때 Camera.open(cameraId) 메서드로 불러오는데, 이 CameraId에 어떤 값이 들어가는지에 대하여 받아올 카메라 렌즈가 달라진다.

기본으로는 후면 카메라의 경우 0, 전면 카메라인 경우 1 를 넣어서 불러왔지만 어느 ID가 광각 카메라 렌즈이고, 어느 ID가 일반 카메라 렌즈인지는 아래 메서드를 사용한다.

/**
 * Identifies dual camera IDs
 */
private void findDualCameraId() {
    Camera.getCameraInfo(0, cameraInfo1);
    Camera.getCameraInfo(1, cameraInfo2);
    Camera.getCameraInfo(2, cameraInfo3);

    // examine which camera devices have the same facing direction
    if (cameraInfo1.facing == cameraInfo2.facing) {
        cameraId1 = 1;
        cameraId2 = 0;
    } else if (cameraInfo1.facing == cameraInfo3.facing) {
        cameraId1 = 2;
        cameraId2 = 0;
    } else {
        cameraId1 = 2;
        cameraId2 = 1;
    }

    Log.i(TAG, "1st dual camera ID: " + cameraId1);
    Log.i(TAG, "2nd dual camera ID: " + cameraId2);
}

해당 카메라의 바라보는 방향(전면, 후면) 인지를 판단해서 아이디를 얻어오는 방식이다.

전면 카메라만 광각인 V10을 제외하고, 후면 카메라가 광각인 G5, V20, G6의 경우 ID 2가 광각, ID 0가 일반으로 잡히게 된다.

주의할 점은, G5의 경우 객체를 가져올 때에나 해제할 때에 광각 카메라 객체를 먼저 가져오고, 일반 카메라 객체를 가져와야 한다. 이렇게 해야 전체 카메라 해상도를 사용할 수 있으며, 카메라 앱이 종료되는 현상을 방지할 수 있다.

출처

Android Builder 패턴 사용하기 :: Replace Constructor with Builder

자, 여기 흔한 데이터 클래스가 있다.

package pyxis.uzuki.live.shopper.item;

import static pyxis.uzuki.live.shopper.Constants.STATE_NOT_ADDED;

/**
 * Shopper
 * Class: ShopperItem
 * Created by pyxis on 11/11/2017.
 * <p>
 * Description:
 */

public class ShopperItem extends BaseItem {
    private String name;

    private int count;
    private float price;
    private int state = STATE_NOT_ADDED;

    public ShopperItem() {

    }

    public ShopperItem(String name, int count, float price, int state) {
        this.name = name;
        this.count = count;
        this.price = price;
        this.state = state;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

}

갑자기 이 클래스를 빌더 패턴을 사용해서 작성해야 된다고 할 때가 있다고 해보자.

FreeBuilderImmutables 를 사용해도 되지만,  우리가 사용하는 Android Studio, 즉 IntelliJ 에는 Refactor 라는 기능이 있다.

그 기능 중에 생성자를 기반으로 Builder / Factory Pattern 을 만들어주는 기능이 있는데, 그것이 바로 Replace Constructor with Builder 이다.

사용법

맨 위 메뉴 -> Refactor 에서 선택하거나, Shift를 두 번 눌러서 Search Everywhere 기능으로 Replace Constructor with Builder 를 검색해서 엔터를 누른다.

그렇게 되면 위와 같은 창이 뜨는데, 위 표에는 파라미터, 필드 이름, 생성될 setter, 기본값, 선택가능한 setter 등을 각 필드마다 설정할 수 있다.

그리고 밑에는 생성할 빌더 클래스 이름과 패키지, 타겟 폴더를 설정하거나 기존에 존재하는 클래스에 넣을 수도 있다.

위 상태에서 적절히 기본값을 채우고 Refactor를 누르자.

그러면 ShopperItemBuilder 란 이름으로 완성된 빌더 클래스가 나온다.

package pyxis.uzuki.live.shopper.item;

public class ShopperItemBuilder {
    private String name;
    private int count = 0;
    private float price = 0.0f;
    private int state = 0;

    public ShopperItemBuilder setName(String name) {
        this.name = name;
        return this;
    }

    public ShopperItemBuilder setCount(int count) {
        this.count = count;
        return this;
    }

    public ShopperItemBuilder setPrice(float price) {
        this.price = price;
        return this;
    }

    public ShopperItemBuilder setState(int state) {
        this.state = state;
        return this;
    }

    public ShopperItem createShopperItem() {
        return new ShopperItem(name, count, price, state);
    }
}

마무리

아직까진 코틀린 클래스에는 작동하지 않는 것 같지만… 자바로 짤 때는 매우 쓸만한 기능이 될 것 같다.