8.앱 개발 및 실행 (안드로이드)

Edit

8.1앱 개발 환경 설정

넥사크로플랫폼 안드로이드 앱은 안드로이드 앱 개발 도구인 안드로이드 스튜디오를 이용하여 개발하거나 넥사크로플랫폼 제품군의 하나인 앱 빌더(App Builder)를 이용하여 개발할 수 있습니다. 이번 장에서는 안드로이드 스튜디오를 이용하여 앱을 개발하는 방법을 설명합니다. 기본적인 개발 절차는 일반적인 안드로이드 앱을 만드는 방법과 동일합니다.

이미 앱 개발 환경이 설정되어 있다면 앱 개발 환경 설정은 건너뛰어도 됩니다.

안드로이드 스튜디오 설치파일은 기본적으로 SDK 도구를 포함하고 있습니다. 아래 사이트에서 환경에 맞는 설치파일을 내려받을 수 있습니다.

https://developer.android.com/studio/index.html

이번 장에서는 안드로이드 스튜디오 3.5.2 버전 설치를 기준으로 작성했습니다.

8.2앱 프로젝트 개발

앱 개발 환경이 설정되었다면 안드로이드 스튜디오를 실행해서 안드로이드 운영체제에서 동작하는 앱을 만들 수 있습니다. 앱 프로젝트를 진행하기 전에 넥사크로 스튜디오에서 생성한 모듈, 소스, 리소스 파일은 지정된 경로에 위치해야 합니다.

8.2.1프로젝트 생성

1

기존에 열어놓은 프로젝트가 없다면 빠른 시작(Quick Start) 창에서 새로운 프로젝트를 생성합니다. 아래 그림에서 [Start a new Android Studio project]를 선택합니다.

그림 8-1android_studio_3.0_open_project

다른 프로젝트 작업 중에 새로운 프로젝트를 생성하고자 한다면 아래 메뉴에서 생성할 수 있습니다.

File > New > New Project

2

안드로이드에서 제공하는 프로젝트 템플릿을 선택합니다.

선택하는 템플릿에 따라 구조, 화면 레이아웃, 파일 등의 구성이 달라집니다. 여기서는 넥사크로 앱을 담기 위한 기본적인 앱 형태면 충분하므로 Empty Activity를 선택한 다음 [Next] 버튼을 클릭합니다.

그림 8-2android_studio_add_an_activity_empty

3

앱의 이름, 패키지 이름 그리고 프로젝트의 경로를 설정합니다.

그림 8-3android_studio_create_project


항목

설명

1

Name

생성할 앱 이름을 입력합니다.

2

Package Name

패키지 이름을 입력합니다.

3

Save location

프로젝트가 저장될 위치를 지정합니다.

4

Language

개발 언어를 선택합니다. 기본 설정값은 kotlin이지만, 이번 장에서는 Java로 설정합니다.

5

Minimum API

최소 사양 SDK를 설정합니다. SDK 항목을 변경하면 하단에서 해당 버전 API 레벨의 디바이스 호환성에 관한 정보를 확인할 수 있습니다.

넥사크로플랫폼에서 지원하는 안드로이드 최소 지원 사양은 5.0 버전입니다. 따라서 SDK 설정시 API 레벨 21 이상을 지원하는 SDK를 사용해야 합니다.

4

[Finish] 버튼을 클릭하면 프로젝트가 생성됩니다.

프로젝트 창이 열리면서 추가적으로 필요한 파일은 자동으로 내려받아 설치합니다.

5

안드로이드 스튜디오에 포함된 JDK를 설정합니다.

메뉴 [File > Project Structure]에서 JDK location 항목을 선택하고 "Embedded JDK"를 선택합니다.

8.2.2넥사크로플랫폼 라이브러리 설정

앱 개발시 사용할 수 있도록 제공되는 넥사크로플랫폼 자바 아카이브(JAR, Java Archive) 파일과 32/64비트 공유 라이브러리(SO, Shared Library) 파일을 안드로이드 프로젝트에 설정합니다. 별도의 설정은 필요치 않으며 정해진 경로에 복사해주면 됩니다.

파일

경로

자바 아카이브(nexacro17.android.jar)

[프로젝트 경로]\app\libs\

32비트 공유 라이브러리(libnexacro17.so)

[프로젝트 경로]\app\src\main\jniLibs\armeabi-v7a\

64비트 공유 라이브러리(libnexacro17.so)

[프로젝트 경로]\app\src\main\jniLibs\arm64-v8a\

라이브러리 파일을 실제 프로젝트 경로의 정해진 위치에 복사하려면 프로젝트 윈도우 상단에서 뷰 옵션을 [Project]로 변경합니다. 안드로이드 스튜디오를 설치하면 기본적으로 뷰 모드가 [Android]로 되어 있는데 이를 [Project]로 설정해야 모든 폴더 구조를 확인할 수 있습니다.

그림 8-4android_studio_project_explorer

nexacro17.android.jar 파일은 [프로젝트 경로 > app > libs] 폴더로 복사합니다. libnexacro17.so 파일은 [프로젝트 경로 > app > src > main] 폴더 아래 32비트용 [jniLibs > armeabi-v7a] 폴더와 64비트용 [jniLibs > arm64-v8a] 폴더를 새로 만들어 복사합니다. 생성된 폴더 구조는 아래와 같습니다.

그림 8-5archive_library_설정

복사 작업은 파일탐색기에서 안드로이드 스튜디오로 바로 복사/붙여넣기가 가능합니다. 각각의 경로에 파일을 붙여넣기 할 때 아래 그림처럼 파일명과 경로를 변경할 수 있는 Copy 윈도우가 나타나는데 내용 변경없이 [OK] 버튼을 클릭합니다.

그림 8-6so파일복사

프로젝트의 라이브러리 경로에 복사한 자바 아카이브 파일은 프로젝트에서 참조할 수 있도록 라이브러리에 추가합니다. nexacro17.android.jar 파일을 선택하고 마우스 오른쪽 버튼을 클릭하여 컨텍스트 메뉴에서 [Add As Library] 항목을 선택합니다.

그림 8-7nexacro_library_add_as_library

라이브러리가 정상적으로 추가되었는지 확인하려면 [프로젝트 > app] 항목을 선택하고 컨텍스트 메뉴에서 [Open Module Settings] 항목을 선택합니다.

그림 8-8nexacro_library_open_module_settings

[Dependencies] 항목을 선택하면 라이브러리가 추가된 것을 확인할 수 있습니다.

그림 8-9nexacro_library_open_module_settings_project_structure_empty

구글의 정책에 따라 2019년 8월 1일부터 Google Play에 게시하는 앱은 64비트 아키텍처를 지원해야 합니다.

https://developer.android.com/distribute/best-practices/develop/64-bit


32비트 아키텍처 CPU를 사용하는 모바일 장치에서는 64비트 공유 라이브러리는 동작하지 않습니다. 32비트와 64비트 공유 라이브러리가 모두 존재할 경우에는 64비트 라이브러리가 우선 사용됩니다.

8.2.3리소스 설정

앱에서 사용할 로딩 이미지, 아이콘, 메시지, 레이아웃 등을 설정하는 단계입니다. 리소스 파일을 단말기의 화면 크기, 밀도에 따라 만든 후 적절한 폴더에 배치하면 안드로이드 시스템은 사용자에게 최적화된 뷰를 제공합니다.

안드로이드 시스템은 단말기의 화면이나 밀도에 따라 적절한 리소스를 판단해 사용합니다. 그러나 단말기 화면에 맞는 리소스가 없을 경우에는 기본 리소스를 화면에 맞게 확대/축소하여 사용합니다.

리소스 폴더는 프로젝트 생성시 자동으로 생성되며 일반적으로 아래와 같은 구조를 가집니다. 개발자는 리소스의 종류에 따라 적절한 폴더에 이미지 및 XML 파일을 배치합니다.

그림 8-10resource_directory

리소스 폴더는 <리소스 명>-<구성 한정자> 형태로 구성됩니다. 구성 한정자(Configuration Qualifier)는 종류에 따라 크기(small, normal, large..), 밀도(ldpi, mdpi, hdpi, xhdpi..), 방향(land, port), 화면비(long, notlong) 등이 될 수 있습니다. 예를 들어, drawable-xxhdpi는 초고밀도(Extra High Density) 이미지와 XML 리소스 파일을 배치하는 폴더입니다. drawable과 같이 구성 한정자가 붙어 있지 않은 폴더는 시스템에서 화면을 그릴 때 기준이 되는 기본 리소스를 배치하는 폴더입니다.

리소스에 관한 더 상세한 내용은 안드로이드 매뉴얼을 참조하시기 바랍니다.

이미지 설정

앱에서 사용하는 이미지는 res 폴더 아래에 밀도에 따라 알맞은 폴더에 지정합니다. 예를 들어, 로딩 이미지는 drawable 폴더로, 앱 아이콘은 mipmap 폴더로 지정합니다. 적용할 이미지 파일은 앱에서 지원하는 해상도에 따라 폴더를 달리 설정해야 합니다.

항목

설명

ic_launcher.png

바탕화면에 설치되는 앱 아이콘 이미지입니다.

AndroidManifest.xml 파일에서 파일명을 변경할 수 있습니다.

splashimage_phone_landscape.png

안드로이드 폰 가로 방향 스플래시 이미지입니다(모두 소문자).

splashimage_phone_portrait.png

안드로이드 폰 세로 방향 스플래시 이미지입니다(모두 소문자).

splashimage_pad_landscape.png

안드로이드 태블릿 가로 방향 스플래시 이미지입니다(모두 소문자).

splashimage_pad_portrait.png

안드로이드 태블릿 세로 방향 스플래시 이미지입니다(모두 소문자).

런처 아이콘, 로딩 이미지 등을 별도로 지정하지 않는 경우에는 기본 설정이 적용되므로 앱 동작에는 아무런 문제가 없습니다.

아이콘 파일은 프로젝트 생성 후 수정할 수 있습니다. [프로젝트 > app] 항목 선택 후 컨텍스트 메뉴에서 [New > Image Asset] 항목을 선택한 후 나타난 [Asset Studio]에서 아이콘 파일 관련 설정을 변경합니다.

그림 8-11cnofigure_image_asset_empty

앱이 구동될 때 보여지는 로딩 화면을 간단히 설정할 수 있습니다. 위 표의 'splashimage_'로 시작하는 파일이 넥사크로 앱에서 사용하는 로딩 이미지입니다. 사용자는 로딩 화면으로 사용할 이미지를 /res/drawable 폴더에 복사한 후 파일명 다음과 같이 변경해 줍니다.

그림 8-12splashimage

문자열 설정

앱 실행 시 사용할 문자열을 설정합니다. 프로젝트 생성 시 만들어지는 res 폴더 아래 values 폴더에 있는 strings.xml 파일을 아래와 같이 수정합니다.

아래는 넥사크로 앱에서 사용하는 기본 문자열입니다. 문자열은 사용자의 상황에 맞게 수정할 수 있으나 문자열 name은 앱에서 참조하여 사용하므로 수정하지 않습니다. 넥사크로 앱의 기본 문자열을 설정하지 않으면 앱이 정상적으로 동작하지 않을 수 있습니다.

strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- app_name은 실제 디바이스에 설치되는 앱의 이름을 정의합니다. 이를 수정하면 화면에 표시되는 앱의 이름을 변경할 수 있습니다. -->
    <string name="app_name">HelloAndroid</string>
    <string name="action_settings">Settings</string>
    
    <!-- Clipboard -->
    <string name="copy">Copy</string>
    <string name="paste">Paste</string>
    <string name="cut">Cut</string>
    <string name="close">Close</string>
    <string name="selectall">Select All</string>
    <string name="selectword">Select Word</string>
 
    <string name="needupdate">It needs an update. \nAfter completion, Please restart.</string>
    <string name="loadingFail">First loading fails. \nPlease restart.</string>
    <string name="updateFail">Update fails. \nPlease restart.</string>
    <string name="notexist">Start file does NOT exist. \nPlease restart.</string>
    <string name="BeingUpdated">Being updated.</string>
    <string name="wantreplace">There is a file of the same name. Do you want to replace? </string>
    <string name="ok">OK</string>
    <string name="cancel">Cancel</string>
    <string name="move">Move</string>
    <string name="upper">Upper</string>
    <string name="filter">Filter</string>
    <string name="home">Home</string>
    <string name="nofilename">No File Name.</string>     
    <string name="checkforupdates">Check for update.</string>   
    <string name="installforupdates">Install update.</string>  
    <string name="downloadingforupdates">Downloading update.</string>    
    <string name="force_close">Update is Completed.</string> 
    <string name="LoadingPleaseWait">Loading&#8230; \nPlease Wait&#8230;</string> 
    
    <!-- Accessibility widget role -->
    <string name="alert">alert</string>
    <string name="pushbutton">pushbutton</string>
    <string name="combobox">combobox</string>
    <string name="checkbutton">checkbutton</string>
    <string name="radiobutton">radiobutton</string>
    <string name="text">text</string>
    <string name="list">list</string>
    <string name="listitem">listitem</string>
    <string name="statictext"></string>
    <string name="graphic">image</string>
    <string name="spacebar">space bar</string>
    <string name="textdeleted">deleted</string>
    
    <!-- Accessibility widget statue -->
    <string name="checked">checked</string>
    <string name="unchecked">unchecked</string>
    
    <!-- Accessibility reaction -->
    <string name="click">click</string>
    
    <!-- Splash message  -->
    <string name="start_initialize">Start Initialization</string> 
    <string name="check_license">Check License</string>
    <string name="load_frameworkmodule">Load Framework Module</string>
    <string name="load_extendmodule">Load Extended Module</string>
    <string name="execute_frameworkscript">Execute Framework Script</string>
    <string name="load_application">Load Application</string>
    <string name="load_failapplication">Fail to Initialize Application. Click And Close</string>
    
</resources>

문자열은 사용자의 상황에 맞게 수정할 수 있습니다. 그러나 위에 정의된 문자열 name은 앱에서 고정적으로 사용하므로 수정하지 않습니다.

영문 문자열 외 한국어나 일본어 등의 문자열을 추가하려면 res 폴더 아래 values-ko, values-ja 와 같은 식으로 폴더를 추가하고 strings.xml 파일을 추가합니다. 더 상세한 내용은 안드로이드 매뉴얼을 참조하시기 바랍니다.

레이아웃 설정

단말기에서 보이는 앱의 레이아웃을 지정합니다. 안드로이드 앱에서 넥사크로플랫폼 앱을 감싸기 위한 용도이므로 기본적인 상태로 설정합니다. 레이아웃을 설정하지 않으면 앱 실행시 넥사크로플랫폼 앱이 정상적으로 화면에 출력되지 않습니다.

res 폴더 아래에 있는 layout 폴더에 nexacro_app.xml 파일을 새로 만들어 아래의 같이 수정합니다. 레이아웃 파일을 새로 추가하려면 [File > New > XML > Layout XML File]을 선택합니다.

nexacro_app.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- nexacro_app.xml -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nexacro_activity"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent"
    tools:context="com.nexacro.deviceAPI.FileDialogActivity" >

    <com.nexacro.view.NexacroLayout
        android:id="@+id/nexacro_layout"
        android:background="@android:color/transparent"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </com.nexacro.view.NexacroLayout>

</FrameLayout>

레이아웃을 설정하지 않으면 화면이 정상적으로 표시되지 않거나 에러가 발생할 수 있습니다.

8.2.4Config 설정

넥사크로플랫폼에서 제공하는 기능을 앱에서 사용할 수 있도록 nexacro_config.xml 파일을 설정합니다. 앱 업데이트, Notification, 에러 정보 처리 등의 기능을 활성화 할 수 있습니다. nexacro_config.xml 파일은 사용자가 직접 생성해야 하며 [프로젝트 > app > src > main > res > xml] 폴더 아래에 배치합니다. 이 파일은 안드로이드 스튜디오에서 앱 빌드시 프로젝트에 포함되어 있어야 합니다.

nexacro_config.xml 파일이 없으면 기본 설정이 적용되므로 앱 실행에는 아무런 지장이 없습니다.

nexacro_config.xml 파일은 XML 형식이며 다음 예와 같이 작성합니다.

<?xml version="1.0" encoding="UTF-8"?>
<nexacro-config>
    <application dialog-position="center" file-logging="true" quiet="false"/>
    <updator cancelable="true" force="true" errormsg="true" quiet="false"/>
    <xpush-server requestMissingMessage="true"/>
    <notification enable="true" handler="com.nexacro.notification.DefaultHandler"/>
</nexacro-config>

nexacro_config.xml에 설정할 수 있는 기능은 다음과 같습니다.

17.0.0.1800 이후 버전에서는 Firebase Cloud Messaging(FCM) 서비스를 사용하면서 project-number, ui-handler 속성은 더 이상 사용하지 않으며 해당 속성에 대한 설명을 삭제합니다.

기능

속성

설정 값

설명

application

dialog-position

"top" | "center" | "bottom"

앱 업데이트 진행 정보를 표시할 팝업 위치를 설정합니다.

file-logging

"true" | "false"

로딩 에러 정보를 파일로 저장할지 설정합니다.

"true"로 설정시 외부저장소(context.getExternalCacheDir()) > 내부저장소(context.getCacheDir()) 순서로 파일 저장을 시도합니다.

파일은 [외부/내부저장소 경로]/logs/yyyy_MM_dd.txt 형태로 저장됩니다.

quiet

"true" | "false"

앱 실행 팝업을 표시할지 설정합니다.

"true"로 설정시 "Loading Application" 실행 메시지가 출력되지 않습니다.

updator

force

"true" | "false"

start_android.json에 업데이트 파일 정보 존재시 팝업으로 표시할지 설정합니다.

"true"로 설정시 팝업 알림없이 강제로 업데이트를 진행합니다.

cancelable

"true" | "false"

업데이트 파일 존재시 팝업에 업데이트 취소 버튼을 표시할지 설정합니다.

취소 버튼을 클릭하면 업데이트를 진행하지 않고 앱을 실행합니다.

errormsg

"true" | "false"

앱 로딩에 실패했을 때 에러 정보를 팝업으로 표시할지 설정합니다.

quiet

"true" | "false"

업데이트 팝업을 표시할지 설정합니다.

"true"로 설정해도 업데이트 파일 존재시 진행 단계는 표시됩니다.

xpush-server

requestMissingMessage

"true" | "false"

xpush 서버로 미수신 메시지를 자동으로 요청할지 설정합니다.

notification

enable

"true" | "false"

알림기능을 사용할지 설정합니다.

handler

“[함수 이름]”

알림 수신시 메시지 혹은 데이터 처리를 위한 함수를 설정합니다.

기본 값은 “com.nexacro.notification.DefaultHandler” 입니다.

8.2.5빌드 환경 설정

안드로이드 프로젝트를 생성하면서 기본으로 만들어진 MainActivity.java 파일과 AndroidManifest.xml 파일을 넥사크로플랫폼 환경에 맞게 수정합니다.

MainActivity.java

프로젝트 생성 시 액티비티 이름을 지정해주는데 별도로 수정하지 않는다면 MainActivity.java 라는 이름으로 생성됩니다. [프로젝트 > app > src > main > java] 폴더에서 지정된 패키지 이름 아래에 MainActivity.java 파일을 선택하고 아래와 같이 수정해줍니다.

MainActivity.java

package com.example.helloandroid;

import com.nexacro.NexacroResourceManager;
import com.nexacro.NexacroUpdatorActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends NexacroUpdatorActivity {

    public MainActivity() {
        super();

        setBootstrapURL("http://[URL]/start_android.json");
        setProjectURL("http://[URL]/");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        NexacroResourceManager.createInstance(this);
        NexacroResourceManager.getInstance().setDirect(false);

        Intent intent = getIntent();
        if(intent != null) {
            String bootstrapURL = intent.getStringExtra("bootstrapURL");
            String projectUrl = intent.getStringExtra("projectUrl");
            if(bootstrapURL != null) {
                setBootstrapURL(bootstrapURL);
                setProjectURL(projectUrl);
            }
        }

        super.onCreate(savedInstanceState);
    }

    @Override
    public void setContentView(View view) {
        super.setContentView(view);
    }

}

위의 코드에서 굵은 글씨로 표시된 부분은 사용자가 환경에 맞게 수정해야 합니다.

15~16번 줄은 부트스트랩 주소와 프로젝트 소스의 주소를 설정하는 부분입니다. setBootstrapURL 메소드의 인수는 넥사크로 스튜디오에서 만든 start_android.json 파일이 배치된 서버 URL을 지정하고 setProjectURL 메소드의 인수는 해당 프로젝트 소스가 제너레이트된 경로를 지정합니다. 제너레이트 소스의 경로 끝에는 '/'를 붙여줍니다.

15 setBootstrapURL("http://192.168.0.1:8080/nexacro/_android_/start_android.json");
16 setProjectURL("http://192.168.0.1:8080/nexacro/_android_/");

22~23번 줄은 앱의 Update Type에 따라 수정합니다. Update Type은 넥사크로 스튜디오에서 Packing(Archive&Update)시 설정하는데 Update Type이 'Server'인 경우에는 23번 줄의 setDirect 메소드의 인수를 아래와 같이 'true'로 설정합니다. Update Type이 'Update(Local+Server)' 혹은 'Local' 타입인 경우에는 'false'로 설정합니다.

22 NexacroResourceManager.createInstance(this);
23 NexacroResourceManager.getInstance().setDirect(true);

부트스트랩 URL, 프로젝트 URL 그리고 리소스 매니저를 잘못 설정하면 앱이 정상적으로 동작하지 않을 수 있습니다.

17.0.0.2100 이상 버전에서는 onCreate 내에서 setScreenid 메소드를 사용하면 screenid를 원하는 값으로 지정할 수 있습니다.

public void onCreate(Bundle savedInstanceState) {
    NexacroResourceManager.createInstance(this);
    NexacroResourceManager.getInstance().setScreenid("Tablet_screen");
}

AndroidManifest.xml

AndroidManifest.xml 파일은 [프로젝트 > app > src > main]에 위치하며 앱에 대한 기본적인 정보와 특정 기능 실행 시 필요한 정보를 담고 있습니다. 기본적으로 필요한 정보는 아래 코드를 참고해 작성합니다. 아래 내용 중 굵게 표시된 package, MainActivity 에 대한 정보는 프로젝트 설정에 따라 지정해야 합니다.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.helloandroid">

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <!-- network access Permission -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

    <!-- storage access permission -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity
            android:name="com.example.helloandroid.MainActivity"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.NoTitleBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name="com.nexacro.NexacroActivity"
            android:alwaysRetainTaskState="true"
            android:configChanges="orientation|keyboardHidden|screenSize"
            android:launchMode="singleTop"
            android:theme="@android:style/Theme.NoTitleBar"
            android:windowSoftInputMode="adjustResize" >
        </activity>

        <!-- FileDialog -->
        <activity android:name="com.nexacro.deviceAPI.FileDialogActivity" android:screenOrientation="sensor">
            <intent-filter>
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="google_play_services_version" />

    </application>

</manifest>

targetsdk 버전을 28 이상으로 지정한 경우에 HTTP 통신을 유지하기 위해서는 usesCleartextTraffic 값을 true로 지정해주어야 합니다.

해당 설정이 없을 경우 default 값이 false로 적용되어 HTTP 통신이 허용되지 않습니다.

https://developer.android.com/guide/topics/manifest/application-element#usesCleartextTraffic


<application

android:allowBackup="true"

android:usesCleartextTraffic="true"

배포할 앱의 기능에 따라 필요한 권한 및 기타 정보를 추가로 지정합니다. 예를 들어, 카메라 기능을 사용한다면 아래와 같은 항목이 추가되어야 합니다.

...
<uses-permission android:name="android.permission.CAMERA" />
<activity android:name="com.nexacro.deviceAPI.CameraListener"
    android:screenOrientation="landscape">
    <intent-filter>
           <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus"
    android:required="false"/>
...

다음은 많이 사용되는 기능의 권한 설정 코드입니다. 필요한 항목만 AndroidManifest.xml 파일에 추가합니다. 이 외에 안드로이드에서 정의한 모든 권한 목록을 보려면 Manifest.permission을 참조하시기 바랍니다.

기능에 필요한 권한을 설정하지 않으면 앱 실행시 해당 권한이 없다는 메시지와 함께 기능이 동작하지 않습니다.

<!-- AudioPlayer, AudioRecorder -->
<uses-permission android:name="android.permission.RECORD_AUDIO" /> 
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!-- Call -->
<uses-permission android:name="android.permission.CALL_PHONE" />
<!-- Contact -->
<uses-permission android:name="android.permission.READ_CONTACTS" /> 
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<!-- Camera -->
<uses-permission android:name="android.permission.CAMERA" /> 
<activity android:name="com.nexacro.deviceAPI.CameraListener" android:screenOrientation="landscape">
    <intent-filter>
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<!-- Geolocation -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<!-- Map -->
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<uses-library android:name="com.google.android.maps" /> 
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="user value" />

구글맵을 사용하려면 API KEY 값을 받아서 입력해야 합니다. 자세한 내용은 다음 링크를 참조하십시오.

https://developers.google.com/maps/documentation/android/start#obtain_a_google_maps_api_key

17.0.0.1800 이후 버전에서는 Firebase Cloud Messaging(FCM) 서비스를 사용합니다.

구글 정책에 따라 2019년 4월 11일 이후에는 이전 Notification 기능(GCM, Google Cloud Messaging)을 사용할 수 없습니다. 넥사크로플랫폼 버전을 17.0.0.1800 이후 버전으로 업데이트하고 Firebase Cloud Messaging(FCM) 서비스를 사용하는 것으로 설정을 변경해야 합니다.

https://developers.google.com/cloud-messaging/

About FCM messages

https://firebase.google.com/docs/cloud-messaging/concept-options

<!-- Adding NexacroNotificationService -->
<service android:name="com.nexacro.notification.NexacroNotificationService">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

<!-- Adding NexacroActivity intent-filter -->
<activity android:name="com.nexacro.NexacroActivity"
    android:launchMode="singleTask"
    android:noHistory="false"
    android:hardwareAccelerated="true"
    android:windowSoftInputMode="adjustResize"
    android:configChanges="orientation|keyboard|keyboardHidden|screenSize|navigation|uiMode">
    <intent-filter>
        <action android:name="OPEN_NEXACRO_ACTIVITY_EXAMPLE" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

FCM 서비스를 이용하는 경우에는 build.gradle 설정에 아래 항목을 추가해주어야 합니다. 안드로이드 스튜디오에서 자동으로 설정되지 않는다면 확인 후 추가해주고 Firebase 콘솔에서 생성한 "google-services.json" 파일을 모듈 경로에 복사합니다.

Android 프로젝트에 Firebase 추가

https://firebase.google.com/docs/android/setup

buildscript {
    // Adding repositories 
    repositories {
        google()  
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        // Adding google service
        classpath 'com.google.gms:google-services:4.0.1'
    }
}

allprojects {
    // Adding repositories 
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support:support-v4:27.1.1'
    implementation 'com.google.android.gms:play-services-maps:16.0.0'
    implementation 'com.google.android.gms:play-services-location:16.0.0'
    implementation 'com.google.firebase:firebase-core:16.0.6'
    implementation 'com.google.firebase:firebase-messaging:17.3.4'
    compile 'com.android.support:multidex:1.0.3'
    implementation 'com.android.support:design:27.1.1'
    implementation files('libs/nexacro17.android.jar')
}
apply plugin: 'com.google.gms.google-services'

targetsdk 버전을 28 이상으로 지정한 경우에는 play-services 버전도 같이 수정해주어야 합니다.

implementation 'com.google.android.gms:play-services-maps:16.1.0'

implementation 'com.google.android.gms:play-services-location:16.1.0'

<!-- SMS -->
<uses-permission android:name="android.permission.SEND_SMS" /> 
<uses-permission android:name="android.permission.RECEIVE_SMS" /> 
<uses-permission android:name="android.permission.READ_SMS" /> 
<uses-permission android:name="android.permission.WRITE_SMS" />

<receiver android:name="com.nexacro.deviceAPI.SmsRecv">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
        <data android:mimeType="application/vnd.wap.mms-message" />
    </intent-filter>
</receiver>
<!-- Vibrator -->
<uses-permission android:name="android.permission.VIBRATE" />
<!-- FileDialog -->
<activity android:name="com.nexacro.deviceAPI.FileDialogActivity" android:screenOrientation="sensor">
    <intent-filter>
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<!-- Bluetooth -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<service
    android:name="com.nexacro.deviceAPI.BluetoothLEAdapterService"
    android:enabled="true"
    android:exported="true">
</service>
<!-- ExternalAPI -->
<uses-permission android:name="android.permission.GET_TASKS" />
<!-- Etc -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

8.2.6네트워크 연결 없는 앱 실행 설정

넥사크로 스튜디오에서 앱을 패킹할 때 Update Type을 Local이나 Update(Local+Server)으로 설정하면 네트워크 연결 없이도 앱을 실행 할 수 있습니다. 일반적으로 네트워크 연결이 필요 없는 경우에는 Local 타입을 사용하고, 평소에는 서버와 통신이 필요하지만 연결에 문제가 생길 시에도 앱이 동작해야 할 경우에는 Update(Local+Server) 타입을 사용합니다. 이처럼 네트워크 연결이 필요 없거나 문제 발생 시 앱 단독으로 실행이 가능하게 하려면 앱 빌드 시 실행에 필요한 파일을 적재하여 배포해야 합니다.

앱 실행에 필요한 파일은 모듈, 소스, 리소스 그리고 start_android.json으로 넥사크로 스튜디오에서 Packing(Archive&Update)을 수행하여 얻을 수 있습니다. 이 파일을 안드로이드 프로젝트에서 애셋(Asset)의 archive 폴더에 적재한 후 앱을 빌드하면 네트워크 연결 없이도 앱을 실행할 수 있습니다.

넥사크로 스튜디오에서 Packing(Archive&Update)시 Update Type을 Local로 설정한 때도 안드로이드 프로젝트의 MainActivity.java에서 부트스트랩 URL은 반드시 설정해야 합니다.

예를 들면, 다음과 같이 가상의 주소나 실제 사용하지 않는 임의의 주소로 설정해도 무방합니다.

setBootstrapURL("http://192.168.0.1/_android_/start_android.json");

setProjectURL("http://192.168.0.1/_android_/");

1

안드로이드 스튜디오 프로젝트에서 assets 폴더를 생성합니다.

애셋은 안드로이드 앱에서 사용할 수 있는 논리적인 저장 공간입니다. 안드로이드 프로젝트에서 assets 폴더를 만들고 앱에서 사용할 파일을 복사한 후 APK를 생성하면 앱 실행 시 그 파일을 사용할 수 있습니다. 애셋은 읽기 전용 공간으로 사용되므로 앱 실행 중 변경되지 않을 파일을 이곳에 놓고 사용합니다.

먼저 프로젝트의 app을 선택하고 마우스 오른쪽 버튼을 클릭합니다. 다음과 같이 콘텍스트 메뉴에서 [New > Folder > Assets Folder]를 선택하여 [프로젝트 > app > src > main] 하위에 Assets 폴더를 생성합니다.

그림 8-13create_assets_1

2

assets 폴더 하위에 archive 폴더를 생성합니다.

[프로젝트 > app > src > main > assets] 폴더를 선택한 후 [New > Directory] 메뉴를 사용해 archive 폴더를 assets 폴더 하위에 생성합니다.

3

모듈, 소스, 리소스 파일을 assets/archive 폴더에 복사합니다.

모듈, 소스, 리소스 파일을 [프로젝트 > app > src > main > assets > archive] 폴더로 복사합니다. 윈도우 탐색기에서 파일을 선택한 후 안드로이드 스튜디오로 복사/붙여넣기 합니다.

그림 8-14copy_archive_files

4

폴더 및 파일이 프로젝트에 정상적으로 추가되었는지 확인합니다.

안드로이드 스튜디오의 프로젝트 창에서 assets/archive 폴더 및 파일들이 정상적으로 추가되었는지 확인합니다.

그림 8-15asset_경로

8.3빌드

8.3.1앱 테스트

  1. 안드로이드 단말기를 PC에 USB로 연결해 바로 테스트할 수 있습니다. 단말기를 직접 연결하는 경우에는 단말기에 개발자 옵션인 [USB 디버깅] 항목을 설정하고 단말기에 맞는 드라이버를 설치해주어야 합니다.

USB 디버깅 옵션 선택 방법은 아래 정보를 참고하세요.

https://developer.android.com/studio/debug/dev-options

  1. 안드로이드 스튜디오에서 Run 메뉴를 수행합니다. Run 메뉴는 APK를 생성 및 단말기에 설치하고 실행까지 할 수 있는 기능입니다. 실행 명령을 내리면 어느 단말기에서 실행할지 선택할 수 있습니다.

Run > Run 'App'

넥서스 원(Nexus One) 또는 옵티머스 원(Optimus One) 같은 경우 내장 메모리에 사용자가 쓸 수 있는 공간이 없어 외장 메모리 카드를 추가하지 않았다면 앱이 정상적으로 설치되지 않습니다.

안드로이드 가상 디바이스를 이용한 테스트는 지원하지 않습니다.

그림 8-16select_deployment_target

앱 실행 로그는 안드로이드 스튜디오 하단의 Logcat을 통해 확인할 수 있습니다.

그림 8-17logcat

8.3.2설치 파일 생성

완성된 앱을 배포하기 위해서는 앱을 인식할 수 있는 keystore를 생성하고 서명된 APK 파일을 생성해야 합니다. keystore 파일은 최초 1회 생성하며 이후 앱이 업데이트되었을때는 최초 생성된 keystore 파일을 사용합니다.

keystore 생성 및 Signed APK 를 생성하는 방법은 구글의 웹페이지를 참조하십시오.

상단 메뉴에서 [Build > Generate Singed Bundle/APK] 항목을 선택합니다.

그림 8-18generate_signed_apk_menu