지난번, 네이버 음성인식 API를 다루는 포스팅을 

1. Android(7) - 네이버 음성인식 API 이용하기 (1) : http://jwprogramming.tistory.com/168

2. Android(8) - 네이버 음성인식 API 이용하기 (2) : http://jwprogramming.tistory.com/169

에서 다루었습니다.


지난 번에는 코드를 직접 옮기면서 조금은 복잡하게 설명드렸다고 생각되어 조금 더 이해하기 쉽게 다시 포스팅하게 되었습니다. 

실질적으로 본인의 Android Studio 툴에서, 네이버 음성인식 API를 바로 테스트해보겠습니다.

우리는 이전 포스팅에서 네이버 음성인식 라이브러리를 다운받은 적이 있습니다. 압축을 풀어줍시다. (* 이전 포스팅들에서 네이버 개발자 센터에서 음성인식 API를 신청하고, Client ID와 Secret을 얻는 것까지는 진행해주셔야 합니다.!)


Import Project 를 통하여 훨씬 쉽고 간편하게 바로 테스트 해보실 수 있습니다.

처음 프로젝트 생성 시, New Project 외에 여러가지 메뉴가 있습니다. 여기서 Import Project를 클릭하여 이전에 다운받은 라이브러리를 import 해주어야 합니다.

보시는 바와 같이 다운받고, 압축을 풀어준 폴더를 찾아서, NaverspeechClient-android-studio 를 선택 후, OK 버튼을 눌러주면 아래와 같이 Android Studio Project가 새로 생성됩니다.



이렇게 네이버에서 제공하는 라이브러리를 이용하여 바로 테스트 해볼 수 있습니다.

단, Package name은 신경 써야하는데, Android_Manifest.xml 내의 

[Package name]

package="com.naver.naverspeech.client"

부분과, 

Gradle Scripts 내의 build.gradle(Module:app)

[applicationId]

defaultConfig {
applicationId "com.naver.naverspeech.client"
targetSdkVersion 23
}

부분을 자신이 네이버 개발자센터에서 API신청을 할 때, Android package name 을 등록한 부분과 맞춰주어야 합니다.


그 외, 프로젝트 내에서 각 소스들 상단의 package 이름이

package com.naver.naverspeech.client;

와 같이 되어있을 것입니다.

이 부분을 자신의 package 로 전부 바꿔주시고, 마지막으로 MainActivity.java 에서 CLIENT_ID를 자신의 CLIENT_ID 로 바꿔주신 후 테스트하면 될 것입니다.


WRITTEN BY
SiriusJ

,

[MainActivity.java]

public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}


[activity_main.xml]

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<TextView
android:id="@+id/label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Type here:"/>

<EditText
android:id="@+id/entry"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:drawable/editbox_background"
android:textColor="#000"
android:layout_below="@id/label"/>

<Button
android:id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/entry"
android:layout_alignParentRight="true"
android:layout_marginLeft="10dip"
android:text="OK" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/ok"
android:layout_alignTop="@id/ok"
android:text="Cancel" />

</RelativeLayout>



WRITTEN BY
SiriusJ

,

본 포스팅에서 네이버 음성인식API를 이용하는데에 필요한 코드는

MainActivity.java, NaverTalkActivity.java, NaverRecognizer.java, AudioWriterPCM.java, activity_main.xml, activity_talk.xml 입니다.

(포스팅에서 다루는 저의 경우이니, 사용하시는 분에 따라서 변경될 수 있겠습니다.)

///////////////////////////////////////////////////////////////////////////////////////////////////

지난 포스팅에 이어 이제 NaverTalkActivity 를 만들어줄 차례입니다. 군데군데 수정만 해주시면 될것입니다.


이전에 다운받았던 SDK로 이동하여, 

naverspeech-sdk-android-master\naverspeech-sdk-android-master\sample\NaverspeechClient-android-studio\app\src\main\java\com\naver\naverspeech\client 로 이동하면 아래와 같이 3가지 소스가 있습니다.

( MainActivity의 내용을 조금 수정하여 NaverTalkActivity를 만들어 줄 것입니다. 이 부분은 아래에서! )

우선, utils의 내용은 같으므로, 본인의 안드로이드 스튜디오에서 utils 패키지를 만들어 준 후, AudioWriterPCM.java 를 그대로 옮겨줍니다. 패키지 이름에 계속 신경써줍시다.

그리고, NaverRecognizer.java 또한 같으므로, 그대로 본인의 프로젝트로 옮겨줍니다.

지난 포스팅에서 저는 naverTalk라는 패키지를 하나 만들어서 그 안에 넣어주었으므로 그렇게 따라하시면 됩니다.

(만약, MainActivity와 같은 패키지에 하셔도 상관없지만, 이전 포스팅에서 Manifest에서 activity의 경로는 수정해주셔야 됩니다.)

naverTalk라는 패키지에 이제 NaverTalkActivity.java 를 생성해줍시다.


/////////////////////////////////////////////////////////////////////////////////

[NaverTalkActivity.java]

import java.lang.ref.WeakReference;

import com.naver.naverspeech.client.utils.AudioWriterPCM;

import com.naver.speech.clientapi.SpeechConfig;

//다른 import 또한 추가해주시면 됩니다.


public class MainActivity extends Activity {

private static final String CLIENT_ID = "Your Client ID"; // "내 애플리케이션"에서 Client ID를 확인해서 이곳에 적어주세요.

private static final SpeechConfig SPEECH_CONFIG = SpeechConfig.OPENAPI_KR; // or SpeechConfig.OPENAPI_EN


private RecognitionHandler handler;

private NaverRecognizer naverRecognizer;

private TextView txtResult;

private Button btnStart;

private String mResult;

private AudioWriterPCM writer;

private boolean isRunning;


// Handle speech recognition Messages.

private void handleMessage(Message msg) {

switch (msg.what) {

case R.id.clientReady:

// Now an user can speak.

txtResult.setText("Connected");

writer = new AudioWriterPCM(

Environment.getExternalStorageDirectory().getAbsolutePath() + "/NaverSpeechTest");

writer.open("Test");

break;


case R.id.audioRecording:

writer.write((short[]) msg.obj);

break;


case R.id.partialResult:

// Extract obj property typed with String.

mResult = (String) (msg.obj);

txtResult.setText(mResult);

break;


case R.id.finalResult:

// Extract obj property typed with String array.

// The first element is recognition result for speech.

String[] results = (String[]) msg.obj;

mResult = results[0];

txtResult.setText(mResult);

break;


case R.id.recognitionError:

if (writer != null) {

writer.close();

}


mResult = "Error code : " + msg.obj.toString();

txtResult.setText(mResult);

btnStart.setText(R.string.str_start);

btnStart.setEnabled(true);

isRunning = false;

break;


case R.id.clientInactive:

if (writer != null) {

writer.close();

}


btnStart.setText(R.string.str_start);

btnStart.setEnabled(true);

isRunning = false;

break;

}

}


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_talk);


txtResult = (TextView) findViewById(R.id.txt_result);

btnStart = (Button) findViewById(R.id.btn_start);


handler = new RecognitionHandler(this);

naverRecognizer = new NaverRecognizer(this, handler, CLIENT_ID, SPEECH_CONFIG);


btnStart.setOnClickListener(new OnClickListener() {


@Override

public void onClick(View v) {

if (!isRunning) {

// Start button is pushed when SpeechRecognizer's state is inactive.

// Run SpeechRecongizer by calling recognize().

mResult = "";

txtResult.setText("Connecting...");

btnStart.setText(R.string.str_listening);

isRunning = true;


naverRecognizer.recognize();

} else {

// This flow is occurred by pushing start button again

// when SpeechRecognizer is running.

// Because it means that a user wants to cancel speech

// recognition commonly, so call stop().

btnStart.setEnabled(false);


naverRecognizer.getSpeechRecognizer().stop();

}

}

});

}


@Override

protected void onResume() {

super.onResume();

// initialize() must be called on resume time.

naverRecognizer.getSpeechRecognizer().initialize();


mResult = "";

txtResult.setText("");

btnStart.setText(R.string.str_start);

btnStart.setEnabled(true);

}


@Override

protected void onPause() {

super.onPause();

// release() must be called on pause time.

naverRecognizer.getSpeechRecognizer().stopImmediately();

naverRecognizer.getSpeechRecognizer().release();

isRunning = false;

}


// Declare handler for handling SpeechRecognizer thread's Messages.

static class RecognitionHandler extends Handler {

private final WeakReference<MainActivity> mActivity;


RecognitionHandler(MainActivity activity) {

mActivity = new WeakReference<MainActivity>(activity);

}


@Override

public void handleMessage(Message msg) {

MainActivity activity = mActivity.get();

if (activity != null) {

activity.handleMessage(msg);

}

}

}

}


/////////////////////////////////////////////////////////////////////////////////

위에서, NaverTalkActivity.java를 만들어주었다면. 이제 onCreate에서 해당 레이아웃을 꾸며주어 버튼을 클릭했을 때에, 음성인식을 받도록 기능을 구현하게 합니다.

activity_talk.xml의 소스는 간단합니다. 클릭할 버튼 하나와, 음성인식한 텍스트를 보여줄 TextView 하나만 있으면 됩니다.


[activity_talk.xml]

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:paddingBottom="@dimen/activity_vertical_margin"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@dimen/activity_vertical_margin"

    tools:context="com.naver.recognition.client.MainActivity" >


    <Button

        android:id="@+id/btn_start"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerHorizontal="true"

        android:text="@string/str_start" />


    <TextView

        android:id="@+id/txt_result"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_below="@id/btn_start"

        android:layout_centerHorizontal="true"

        android:singleLine="false" />

</LinearLayout>



/////////////////////////////////////////////////////////////////////////////////

그리고, 이제 본 애플리케이션의 MainActivity와 activity_main.xml 을 꾸며봅시다!

MainActivity에서 임의의 버튼을 클릭했을 때, NaverTalkActivity로 넘어가도록 할 것입니다.


[activity_main.xml]

기본으로 생성되어져 있는 레이아웃 안에 button만 하나 추가해줍시다.

<Button

                android:id="@+id/btn_voice"

                android:drawableLeft="@android:drawable/ic_btn_speak_now"

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:layout_gravity="center"

                android:gravity="center"

                android:textSize="10dp"

                android:text="음성 제어">

</Button>

/////////////////////////////////////////////////////////////////////////////////

[MainActivity.java]

public class MainActivity extends Activity {

private Button voiceBtn;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

voiceBtn = (Button)findViewById(R.id.btn_voice);


voiceBtn.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                Intent intent = new Intent(MainActivity.this, NaverTalkActivity.class);

                startActivity(intent);

            }

        });

}

}


이와 같이 MainActivity.java 를 구성하게 되면 마무리 되어집니다.

간단하게 보이고자 레이아웃을 매우 간단하게 꾸몄으니, 차후 레이아웃 디자인은 각자 예쁘게 꾸미시면 되겠습니다.

사실 네이버 음성인식API에 대한 부분은 워낙 코드도 간단하고 쉬운데다 가이드도 이해하기 쉽게 되어있어서 그대로 따라하셔도 초보자라도 30분~1시간이면 바로 테스트해볼 수 있으실 겁니다.


WRITTEN BY
SiriusJ

,

오늘은 네이버에서 제공하는 음성인식 API를 이용해보도록 하겠습니다.

아직은 Beta버전이지만 인식은 무리없이 잘 되는 것 같습니다.


자세한 API이용 설명은 개발가이드에서 제공해주는 https://developers.naver.com/docs/labs/vrecog 를 참고하셔도 금방 할 수 있으실 것입니다.


먼저 https://developers.naver.com/products/vrecog 에 네이버 개발자 센터에서 음성인식(Beta)를 선택하여 네이버에서 제공하는 음성인식API를 이용해봅시다. 

현재는 한국어와 영어를 제공해주고 있으며, 하루에 1000개까지 처리가 가능하다고 합니다.

-> 화면에서 오픈 API이용 신청을 클릭합니다.


애플리케이션 이름을 자유롭게 적어주시고, 카테고리를 설정하신 후, 음성인식API를 사용하기 위해 비로그인 오픈API 를 선택합니다. 

또한 다른 API(지도, 검색, 음성합성 등) 를 사용하고 싶다면, 추가적으로 선택해주시면 됩니다.


저는 안드로이드 스튜디오를 이용하여 애플리케이션을 제작 후, 네이버음성인식을 테스트해보고자 합니다.

따라서 안드로이드 스튜디오에서 처음 프로젝트를 만들 때, 입력한 패키지 이름을 이곳에 적어주시면 됩니다.

** 반드시 본인의 애플리케이션 패키지 이름과 동일하게 해주어야 합니다. ** - 패키지이름정도는 기억해줍시다~

다음으로 동의에 체크해 준 후, 등록하기를 눌러줍니다.


화면처럼, Client ID와 Client Secret 키가 생성된 것을 확인할 수 있습니다. 기억해주세요.


이제, SDK를 다운받아야 합니다. https://github.com/naver/naverspeech-sdk-android 이곳에 들어가시면 아래와 같은 화면에서, 초록색버튼 Clone or Download 를 클릭합시다.

Download ZIP 을 선택하여 코드를 다운받고, 압축을 풀어줍니다.


해당 폴더에 들어가서 sample로 이동하시면 위 화면으로 개발툴이 android-studio와 eclipse 로 나뉩니다. 

저는 Android Studio를 이용하기 때문에 해당 디렉토리로 이동합니다.

이동하셔서 바로 아래의 build.gradle 코드를 (본인의 Android Studio 툴 내에 있는 build.gradle)에 추가해줍니다.


다음으로 app 디렉토리로 이동하여 build.gradle에 있는 아래 코드를 (본인의 Android studio툴 내에서 기존에 존재하는 app-> build.gradle 파일에다가) 중복되는 부분을 제외한 나머지 코드만 추가해주면 됩니다.


그리고, 현 디렉토리(\naverspeech-sdk-android-master\naverspeech-sdk-android-master\sample\NaverspeechClient-android-studio\app) 에 build.gradle 외에 libs 라는 폴더도 있을 것입니다.

그대로 자신의 안드로이드 스튜디오에서 생성한 프로젝트의 똑같은 디렉토리에 그대로 폴더 자체를 복사하여 붙여넣어 줍니다. (libs에는 jar 파일이 들어있습니다.)


위까지 진행 하셨으면 이제 src->main 폴더로 이동하여 jniLibs 라는 폴더 또한 자신의 프로젝트의 해당 디렉토리에 복붙해줍니다.


//////////////////////////////////

이제는 코드에서 처리하도록 할 차례입니다. 먼저 Manifest에서 아래와 같이 작성해주면 됩니다. package=" " 에는 위에서 네이버 개발자센터에서 음성인식API를 등록할 때 설정해주었던 패키지 이름을 적어주시면 됩니다. 자신의 프로젝트 패키지이름과 같을 것입니다.

그리고 4개 permission을 추가해주면 됩니다.

[AndroidManifest.xml]

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="당신의 패키지 이름을 적어주세요!">
<!-- Naver Talk 부분 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />


그리고 Activity를 하나 생성해야 하므로 activity 또한 추가해줍니다. (저는 안드로이드 스튜디오 내에서 naverTalk라는 패키지를 만들어서 그 안에 NaverTalkActivity 의 이름으로 만들어주었습니다. )

<!-- Naver Talk -->
<activity android:name=".naverTalk.NaverTalkActivity" />


다음 포스팅에서는 본격적으로 소스들을 수정해주는 부분을 살펴보도록 하겠습니다.


WRITTEN BY
SiriusJ

,

이번에는 Main화면에서 버튼을 클릭했을 때에 이미지가 다른 이미지로 전환되거나, 나타났다가, 사라졌다가 하는 기능을 구현해보겠습니다.

(* 저의 테스트에서는 대표적으로 해 / 구름 의 이미지를 추가로 첨부하셔야 합니다.)


[MainActivity.class]

public class MainActivity extends Activity{

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);


        Button sunbtn = (Button)findViewById(R.id.btnSunshine);

        Button cloudbtn = (Button)findViewById(R.id.btnCloudy);

        Button invibtn = (Button)findViewById(R.id.invi);


        View.OnClickListener listener = new View.OnClickListener() {

            ImageView imgsun = (ImageView)findViewById(R.id.imageSun);

            ImageView imgcloud = (ImageView)findViewById(R.id.imageCloud);

            @Override

            public void onClick(View v) {

                if(v.getId() == R.id.btnSunshine) {

 //btnSunshine을 클릭하게 되면, 해당 ImageView의 이미지는 VISIBLE로 set함으로써 보이게 되고, cloud의 이미지는 INVISIBLE로 설정되어 안보이게 됩니다.      

                    imgsun.setVisibility(View.VISIBLE);

                    imgcloud.setVisibility(View.INVISIBLE);

                } else if(v.getId() == R.id.btnCloudy) {

//btnCloudy 버튼을 눌렀을 때에는, 위와 반대가 됩니다. sun이미지는 안보이고, cloud이미지만 보이게 됩니다.

                    imgcloud.setVisibility(View.VISIBLE);

                    imgsun.setVisibility(View.INVISIBLE);

                } else if(v.getId() == R.id.invi) {

//만약 둘다 안보이게 하려면, 둘 다 INVISIBLE로 설정해주면 됩니다.

                    imgcloud.setVisibility(View.INVISIBLE);

                    imgsun.setVisibility(View.INVISIBLE);

                }

            }

        };


        sunbtn.setOnClickListener(listener);           

        cloudbtn.setOnClickListener(listener);

        invibtn.setOnClickListener(listener);

    }

}


///////////////////////////////////////////////////////////////////////////////////////////


[activity_main.xml]
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="horizontal">

<!-- 아래의 id가 btnSunshine, btnCloudy, invi 인 사용자로부터 클릭할 수 있게 하는 총 세 개의 버튼을 만들어 줍니다. -->
    <Button
        android:id="@+id/btnSunshine"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Sun"/>

    <Button
        android:id="@+id/btnCloudy"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Cloud"/>


<!-- 아래의 버튼은 두 이미지 다 안보이도록 하는 버튼입니다. -->
        <Button
            android:id="@+id/invi"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="INVISIBLE"/>
    </LinearLayout>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="3">

<!-- ImageView를 이용하여 이미지 두 개를 전환하거나 보이거나, 안보이도록 해보는 테스트입니다. drawable에 sunshine, cloudy라는 이름으로 이미지파일을 두 개 아무거나 받으셔서 넣어주면 됩니다. -->
        <ImageView
            android:id="@+id/imageSun"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/sunshine"/>
           <!-- android:visibility="invisible"-->

        <ImageView
            android:id="@+id/imageCloud"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/cloudy"
            android:visibility="invisible"/>
    </FrameLayout>

(** 바로 위의 ImageView에서 가장 밑에 줄의 android:visibility="invisible" 을 설정하면, 기본적으로 이미지가 안보이는 상태가 됩니다. 보기와 같이 위의 imageSun 이라는 id 를 가지고 있는 image가 invisible이 주석처리 되어있으므로, 처음 애플리케이션을 실행하면 해당 ImageView의 이미지만 보이고, imageCloud는 숨겨진 상태가 됩니다.)



WRITTEN BY
SiriusJ

,

이번에는 애플리케이션을 실행하여 버튼을 눌렀을 때, 메시지 화면으로 이동하고, 메시지를 작성하여 다른 사람에게 보내는 것을 해보도록 하겠습니다.

또한 다른 사람으로부터 문자가 왔을 때, 기존의 문자앱에서 보여지는 것 외에, 애플리케이션에서 또한 해당 문자에 대한 내용을 알려주도록 하는 것을 구성해보도록 하겠습니다.


[AndroidManifest.xml]

user-permission 으로 아래와 같이 추가해주면 됩니다.

<!-- SMS 부분 -->
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />


그리고 Activity로 SMSActivity, ShowSMSActivity를 아래와 같이 추가해줍니다. ".sms.SMSActivity" 는, 안드로이드 스튜디오에서 sms라는 패키지를 새로 만들어 준 후, 그 안에 SMSActivity를 추가해주었기 때문입니다. 만약 SMSActivity를 MainActivity와 같은 패키지에 만들어 주었다면 ".SMSActivity" 로 해주시면 됩니다.

<activity android:name=".sms.SMSActivity" />
<activity android:name=".sms.ShowSMSActivity" />


또한, Receiver를 등록하여 다른 사람들로부터 SMS가 왔을 때에도 서비스를 해주어야 하므로

다음과 같이 receiver를 등록해줍니다.

<!-- Receiver 부분 -->
<!-- SMS 부분 -->
<receiver android:name=".sms.SMSBroadcast">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>


////////////////////////////////////////////////////////////////////////////////////////////////


[MainActivity.class]

public class MainActivity extends Activity {

private Button smsBtn;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);                

//activity_main 레이아웃에서 버튼을 하나 만들어주면 됩니다! 버튼의 id는 아래와 같이 btn_sms 로 해주시면 됩니다.


smsBtn = (Button)findViewById(R.id.btn_sms);


smsBtn.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Intent intent = new Intent(MainActivity.this, SMSActivity.class);

startActivity(intent);

}

});

}

}


////////////////////////////////////////////////////////////////////////////////////////////////


[sms 패키지 내에 있는 SMSActivity.class]

public class SMSActivity extends Activity {
//SMS
Context mContext;
EditText smsNumber, smsTextContext;

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

mContext = this;

smsNumber = (EditText)findViewById(R.id.smsNumber);
smsTextContext = (EditText)findViewById(R.id.smsText);
}

//SMS Send 해주는 부분!
public void sendSMS(View v){
String smsNum = smsNumber.getText().toString();
String smsText = smsTextContext.getText().toString();

if (smsNum.length()>0 && smsText.length()>0){
sendSMS(smsNum, smsText);
} else{
Toast.makeText(this, "모두 입력해주세요", Toast.LENGTH_SHORT).show();
}
}

public void sendSMS(String smsNumber, String smsText){
PendingIntent sentIntent = PendingIntent.getBroadcast(this, 0, new Intent("SMS_SENT_ACTION"), 0);
PendingIntent deliveredIntent = PendingIntent.getBroadcast(this, 0, new Intent("SMS_DELIVERED_ACTION"), 0);

registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch(getResultCode()){
case Activity.RESULT_OK:
Toast.makeText(mContext, "전송 완료", Toast.LENGTH_SHORT).show();
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
Toast.makeText(mContext, "전송 실패", Toast.LENGTH_SHORT).show();
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
Toast.makeText(mContext, "서비스 지역이 아닙니다", Toast.LENGTH_SHORT).show();
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
Toast.makeText(mContext, "무선(Radio)가 꺼져있습니다", Toast.LENGTH_SHORT).show();
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
Toast.makeText(mContext, "PDU Null", Toast.LENGTH_SHORT).show();
break;
}
}
}, new IntentFilter("SMS_SENT_ACTION"));

registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (getResultCode()) {
case Activity.RESULT_OK:
Toast.makeText(mContext, "SMS 도착 완료", Toast.LENGTH_SHORT).show();
break;
case Activity.RESULT_CANCELED:
Toast.makeText(mContext, "SMS 도착 실패", Toast.LENGTH_SHORT).show();
break;
}
}
}, new IntentFilter("SMS_DELIVERED_ACTION"));

SmsManager mSmsManager = SmsManager.getDefault();
mSmsManager.sendTextMessage(smsNumber, null, smsText, sentIntent, deliveredIntent);
}
}


////////////////////////////////////////////////////////////////////////////////////////////////


[sms패키지 내에 위치한 SMSBroadcast.class]

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class SMSBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context mContext, Intent intent) {
String action = intent.getAction();

if("android.provider.Telephony.SMS_RECEIVED".equals(action)){
Bundle bundle = intent.getExtras();
Object messages[] = (Object[])bundle.get("pdus");
SmsMessage smsMessage[] = new SmsMessage[messages.length];

for(int i = 0; i < messages.length; i++) {
smsMessage[i] = SmsMessage.createFromPdu((byte[]) messages[i]);
}

Date curDate = new Date(smsMessage[0].getTimestampMillis());
SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy년 MM월 HH시 mm분 ss초 ", Locale.KOREA);

String originDate = mDateFormat.format(curDate);
String origNumber = smsMessage[0].getOriginatingAddress();
String Message = smsMessage[0].getMessageBody().toString();

Intent showSMSIntent = new Intent(mContext, ShowSMSActivity.class);
showSMSIntent.putExtra("originNum", origNumber);
showSMSIntent.putExtra("smsDate", originDate);
showSMSIntent.putExtra("originText", Message);
showSMSIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

mContext.startActivity(showSMSIntent);
}
}
}


[sms패키지 내에 위치한 ShowSMSActivity.class]

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;


public class ShowSMSActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_sms);

TextView smsDate = (TextView) findViewById(R.id.smsDate);
TextView originNum = (TextView) findViewById(R.id.originNum);
TextView originText = (TextView) findViewById(R.id.originText);

Intent smsIntent = getIntent();

String originNumber = smsIntent.getStringExtra("originNum");
String originDate = smsIntent.getStringExtra("smsDate");
String originSmsText = smsIntent.getStringExtra("originText");

originNum.setText(originNumber);
smsDate.setText(originDate);
originText.setText(originSmsText);
}
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


[activity_show_sms.xml]

<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".ShowSMSActivity" >

<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="발신자" />

<TextView
android:id="@+id/originNum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp" />
</TableRow>

<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="수신 시각" />

<TextView
android:id="@+id/smsDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp" />
</TableRow>

<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="수신 내용" />

<TextView
android:id="@+id/originText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp" />
</TableRow>
</TableLayout>


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


[activity_sms.xml]

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="받는사람" />

<EditText
android:id="@+id/smsNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="phone" >
<requestFocus />
</EditText>

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="링크를 전송합니다" />

<EditText
android:id="@+id/smsText"
android:layout_width="match_parent"
android:layout_height="200dp"
android:ems="10" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:onClick="sendSMS"
android:text="Send" />
</LinearLayout>



WRITTEN BY
SiriusJ

,

보통 애플리케이션 실행 시, 바로 메인화면으로 한방에 넘어가는 것보다 보통 2~3초의 짧은 시간동안 애플리케이션 소개화면이 잠깐 지나치는 것을 많이 경험하셨을 것입니다.

앱의 완성도를 조금 더 높여주도록 Splash창을 구성해보도록 하겠습니다.


////////////////////////////////////////////////////////////////////////////////////////////

[MainActivity.java]

public class MainActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);


startActivity(new Intent(this, SplashActivity.class));

//Intent창을 새로 만들어서 먼저 SplashActivity를 시작하도록 합니다.

}

}

////////////////////////////////////////////////////////////////////////////////////////////

이제, 위에서 MainActivity가 실행되면, SplashActivity를 시작할테니, SplashActivity를 만들어주어야 겠죠.


[SplashActivity.java]

import android.app.Activity;

import android.os.Bundle;

import android.os.Handler;


public class SplashActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_splash);        

//activity_splash 레이아웃을 구성함으로써 어떤 화면을 보여줄 지, 직접 사용자가 해당 레이아웃에서 구현해주면 됩니다.


Handler hd = new Handler();

hd.postDelayed(new Runnable() {

public void run() {

finish();            //2초동안 보여준 후, SplashActivity를 종료한 후 MainActivity로 돌아갑니다.

}

}, 2000);                //2000millis. 즉 2초동안 해당 화면을 보여준 후, finish() 됩니다.

}

}

////////////////////////////////////////////////////////////////////////////////////////////

//layout에 activity_splash.xml을 만들어주어 사용자에게 보여 줄 SplashView를 구성해주면 됩니다.

/* 일단, 간단한 확인만을 위하여 TextView를 추가하여 text로 Splash창이라는 것을 확인하도록 합니다.

나중에, 예쁘게 꾸미기 위하여 ImageView나 다른 것들을 추가하여 실질적으로 꾸며주시면 됩니다. */


[activity_splash.xml]

<TextView

android:layout_width="match_parent"

android:layout_height="match_parent"

android:textSize="30dp"

android:text="Splash 창 입니다!" />



[AndroidManifest.xml]

<application
android:allowBackup="true"
android:icon="@mipmap/logo"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity android:name=".SplashActivity" />

위와 같이 아마 보여질 것입니다. (android:icon="@mipmap/logo" 부분은 제가 임의로 아이콘을 만들어 준 부분이므로 신경쓰시지 않으셔도 됩니다. 위 부분은 애플리케이션을 실행 시, MainActivity로 시작하겠다는 뜻입니다.)


보시는 것과 같이 <activity android:name=".SplashActivity" /> 를 한 줄 추가해주면 됩니다.


WRITTEN BY
SiriusJ

,

특정 버튼을 클릭했을 때의 종료 이벤트를 발생시켜서 애플리케이션이 종료하게 하면 됩니다.


[MainActivity.java]

public class MainActivity extends Activity {

private Button exitBtn;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);


exitBtn = (Button)findViewById(R.id.btn_exit);


exitBtn.setOnClickListener(new View.OnClickListener() {        //버튼을 클릭했을 때, Click이벤트를 발생

@Override

public void onClick(View v) {            //아래에서 클릭했을 때의 수행되는 부분으로, 원하는 대로 작성하면 됩니다.

//저는 Dialog를 통하여 종료이벤트를 수행하게 하였습니다.

new AlertDialog.Builder(MainActivity.this)

.setTitle("Application 종료")

.setMessage("애플리케이션을 종료하시겠습니까?")

.setPositiveButton("OK", new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

Toast.makeText(MainActivity.this, "애플리케이션이 종료되었습니다.", Toast.LENGTH_SHORT).show();

finish();

}

})

.setNegativeButton("NO", null)        

//"NO"를 사용자가 클릭했을 때에는 null. 즉 아무 작업도 수행하지 않고 다시 main화면으로 돌아가게 됩니다.

.setIcon(android.R.drawble.ic_dialog_alert)        

//Icon은 기존 제공하는 이미지를 사용하였습니다.

.show();

}

});

});

}


//////////////////////////////////////////////////////////////////////////////////////////////////////////

[activity_main.xml]

<Button

android:id="@+id/btn_exit"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="종료"

android:textSize="10dp"> </Button>


다음과 같이 id는 btn_exit로 하여 버튼을 하나 추가시켜주면 됩니다.


WRITTEN BY
SiriusJ

,