출처: http://cocomo.tistory.com/420
비콘이란?
반경 50~70m 범위 안에 있는 사용자의 위치를 찾아 메시지 전송, 모바일 결제 등을 가능하게 해주는
스마트폰 근거리통신 기술입니다. NFC(근접무선통신)보다 가용거리가 길어 온라인과 오프라인을 연결하는
O2O 서비스에 적합합니다. 이 기술을 이용하면 특정 장소에서 안내 서비스, 모바일 쿠폰 등을 이용할 수 있게 됩니다.
저전력으로 스마트폰의 배터리 소모량도 적으며, 실내에서는 GPS보다 정교한 위치 파악이 가능하다는 장점이 있습니다.
그러나 스마트폰 사용자의 위치를 정확히 파악할 수 있어 개인정보 수집에 활용될 가능성이 있으며, 사용자의 동의가 필요 없다는 특성상 원치 않는 스팸들이 사용자에게 전송될 수 있습니다.
프로젝트 생성
My Beacon 이란 이름으로 안드로이드 프로젝트를 생성합니다.
최소 버전은 16(젤리빈), 레이아웃은 Empty로 설정합니다.
Dependency 추가
Gradle Scripts > build.gradle (Module: app) 파일을 열겠습니다.
비콘 사용을 위해 Dependency, Repository 추가하겠습니다.
repositories 부분과 dependencies 에서 compile 'com'estimote:sdk:0.10.4@aar' 을 추가했습니다.
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.ktds.cocomo.mybeacon"
minSdkVersion 16
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
repositories {
jcenter()
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.estimote:sdk:0.10.4@aar'
}
MyApplication.java
MyApplication이라는 이름으로 자바 클래스 파일을 만듭니다.
extends Application은 어플리케이션이 설치되고 최초로 실행될 때 수행되는 코드를 의미합니다.
앱이 최초 실행된 이후에 비콘을 만나면 비콘에게 아이디, Major, Minor를 보냅니다.
Manifest에 추가
방금만든 MyApplication을 manifest에 추가합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.ktds.cocomo.mybeacon"> <application android:name=".MyApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" 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> </application> </manifest> | cs |
MainActivity 수정
아래와 같이 작성합니다.
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.estimote.sdk.SystemRequirementsChecker;
public class MainActivity extends AppCompatActivity {
// private BeaconManager beaconManager;
// private Region region;
// private TextView tvId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// tvId = (TextView) findViewById(R.id.tvId);
// beaconManager = new BeaconManager(this);
//
// // add this below:
// beaconManager.setRangingListener(new BeaconManager.RangingListener() {
// @Override
// public void onBeaconsDiscovered(Region region, List list) {
// if (!list.isEmpty()) {
// Beacon nearestBeacon = list.get(0);
// Log.d("Airport", "Nearest places: " + nearestBeacon.getRssi());
// tvId.setText(nearestBeacon.getRssi() + "");
// }
// }
// });
//
// region = new Region("ranged region",
// UUID.fromString("74278BDA-B644-4520-8F0C-720EAF059935"), null, null); // 본인이 연결할 Beacon의 ID와 Major / Minor Code를 알아야 한다.
}
@Override
protected void onResume() {
super.onResume();
// 블루투스 권한 및 활성화 코드드
SystemRequirementsChecker.checkWithDefaultDialogs(this);
// beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
// @Override
// public void onServiceReady() {
// beaconManager.startRanging(region);
// }
// });
}
@Override
protected void onPause() {
//beaconManager.stopRanging(region);
super.onPause();
}
}
activity_main.xml 수정
TextView에 tvId라고 아이디를 줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 | <RelativeLayout 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" tools:context="com.ktds.cocomo.mybeacon.MainActivity"> <TextView android:id="@+id/tvId" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" /> </RelativeLayout> | cs |
실행해보겠습니다. 아래와같이 실행되면서 여러 권한에 대한 여부가 뜹니다.
비콘 영역에 들어갔더니 아래와같이 Notification이 떳습니다.
화면에 비콘과의 거리 출력
이제 MainActivity에서 주석을 모두 풀겠습니다. 단, 마지막 onPause() 메소드에 있는 //beaconManager.stopRanging(region);
는 주석을 풀지 않겠습니다. 이건 모니터링을 하지 않겠다는 코드입니다. 주석을 풀고 다시 실행시켜보겠습니다.
아래처럼 비콘과의 거리가 실시간으로 뜹니다.
수신강도에 따른 알림창 띄우기
수신강도에 따라 "연결되었습니다." 또는 "끊어졌습니다." 알림창을 띄워보겠습니다.
MainActivity 에서 onCreate메소드를 아래와 같이 수정합니다.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvId = (TextView) findViewById(R.id.tvId);
beaconManager = new BeaconManager(this);
isConnected = false;
// add this below:
beaconManager.setRangingListener(new BeaconManager.RangingListener() {
@Override
public void onBeaconsDiscovered(Region region, List list) {
if (!list.isEmpty()) {
Beacon nearestBeacon = list.get(0);
Log.d("Airport", "Nearest places: " + nearestBeacon.getRssi());
// nearestBeacon.getRssi() : 비콘의 수신 강도
tvId.setText(nearestBeacon.getRssi() + "");
// 수신강도가 -70 이상일때 알림창을 띄운다.
if( !isConnected && nearestBeacon.getRssi() > -70) {
isConnected = true;
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
dialog.setTitle("알림")
.setMessage("비콘이 연결되었습니다.")
.setPositiveButton("확인", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.create().show();
}
else if( isConnected && nearestBeacon.getRssi() < -70 ) {
Toast.makeText(MainActivity.this, "연결이 끊어졌습니다.", Toast.LENGTH_SHORT).show();
isConnected = false;
}
}
}
});
region = new Region("ranged region",
UUID.fromString("74278BDA-B644-4520-8F0C-720EAF059935"), null, null); // 본인이 연결할 Beacon의 ID와 Major / Minor Code를 알아야 한다.
}
알림창이 떴습니다.
비콘의 범위를 벗어나면 연결 끊겼다는 Notification이 옵니다.
Activity 이동시키기
MainActivity를 다 지우고 다음과 같이 두고 새롭게 시작해보겠습니다.
어플리케이션을 실행했을 때와 비콘을 통해 어플리케이션이 실행되었을 때 각각 다른 Activity를 띄우겠습니다.
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.estimote.sdk.SystemRequirementsChecker;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
super.onResume();
// 블루투스 권한 및 활성화 코드드
SystemRequirementsChecker.checkWithDefaultDialogs(this);
}
}
SplashActivity 만들기
재미있는 구성을 위해 로딩화면(스플래시)을 만들어보겠습니다.
SplashActivity 라는 이름으로 새로운 Activity를 만듭니다.
레이아웃은 Empty로 설정합니다.
Manifest
Manifest.xml에서 MainActivity 안에 있던 intent-filter를 방금 만든 SplashActivity쪽으로 옮깁니다.
그리고 SplashActivity의 테마를 NoActionBar로 바꿉니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.ktds.cocomo.mybeacon"> <application android:name=".MyApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"></activity> <activity android:name=".SplashActivity" android:theme="@style/Theme.AppCompat.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> | cs |
로고 다운받기
구글에서 원하는 로고를 다운받습니다.
검색도구를 눌러서 투명에 체크하시면 배경이 없는 로고를 찾을 수 있습니다.
받은 이미지를 프로젝트의 res > drawble 폴더에 넣습니다.
이때! 이미지의 이름은 소문자! logo_image.jpg로 해줍니다.
activity_splash.xml 레이아웃을 수정합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:gravity="center" android:background="#FFCC00" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.ktds.cocomo.mybeacon.SplashActivity"> <ImageView android:id="@+id/ivLogo" android:src="@drawable/logo_image" android:layout_width="300dp" android:layout_height="300dp" /> <LinearLayout android:orientation="horizontal" android:gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:text="LG " android:textSize="30dp" android:gravity="center" android:textStyle="bold" android:textColor="#DD880000" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:text="T" android:textColor="#333333" android:textStyle="bold" android:textSize="30dp" android:gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:text="wins" android:textColor="#333333" android:textSize="30dp" android:gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> </LinearLayout> | cs |
SplashActivity 수정
핸들러를 만들어서 postDelayed 메소드를 통해 3초동안 화면을 보여주고 MainActivity로 이동하고 자신을 끝내도록합니다.
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
public class SplashActivity extends AppCompatActivity {
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
}, 3000);
}
}
MyApplication 수정
MyApplication에서 setMonitoringListener 부분을 수정해보겠습니다.
비콘에 의해서 실행 되도록합니다.
// Android 단말이 Beacon 의 송신 범위에 들어가거나, 나왔을 때를 체크한다.
beaconManager.setMonitoringListener(new BeaconManager.MonitoringListener() {
@Override
public void onEnteredRegion(Region region, List list) {
// list.get(0).getRssi()은 수신강도입니다.
// 0에 가까울수록 거리가 가깝습니다.
// showNotification은 아래 메소드를 호출하는겁니다.
// showNotification("들어옴", "비콘 연결됨" + list.get(0).getRssi());
Intent intent = new Intent(getApplicationContext(), SplashActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
getApplicationContext().startActivity(intent);
}
@Override
public void onExitedRegion(Region region) {
// showNotification("나감", "비콘 연결끊김");
}
});
비콘에 의해서 Activity가 실행됐다는걸 알리기
비콘에 의해서 Activity가 실행되었다는걸 알리기 위해선 MainActivity로 가는 intent에 putExtra를 통해
데이터를 데이터를 넣어주면됩니다.
// Android 단말이 Beacon 의 송신 범위에 들어가거나, 나왔을 때를 체크한다.
beaconManager.setMonitoringListener(new BeaconManager.MonitoringListener() {
@Override
public void onEnteredRegion(Region region, List list) {
// list.get(0).getRssi()은 수신강도입니다.
// 0에 가까울수록 거리가 가깝습니다.
// showNotification은 아래 메소드를 호출하는겁니다.
// showNotification("들어옴", "비콘 연결됨" + list.get(0).getRssi());
Intent intent = new Intent(getApplicationContext(), SplashActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra("executeType", "beacon");
getApplicationContext().startActivity(intent);
}
@Override
public void onExitedRegion(Region region) {
// showNotification("나감", "비콘 연결끊김");
}
});
SplashActivity에서 어플리케이션이 비콘에 의해서 실행되었을 때와 아닐때 이동될 Activity를 다르게 줄 수 있습니다.
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
public class SplashActivity extends AppCompatActivity {
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
final String executeType = getIntent().getStringExtra("executeType");
handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = null;
// 비콘에의해 실행될 때와 아닐때 이동될 Activity를 다르게 줄 수 있습니다.
if(executeType != null && executeType.equals("beacon")) {
intent = new Intent(SplashActivity.this, MainActivity.class);
} else {
intent = new Intent(SplashActivity.this, MainActivity.class);
}
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
}, 3000);
}
}
확인해보기 위해 CouponActivity라고 만들어보겠습니다. 만들고 레이아웃을 수정하겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:gravity="center" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.ktds.cocomo.mybeacon.CouponActivity"> <TextView android:text="사용 가능한 쿠폰이 30장 입니다." android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> | cs |
SplashActivity에서 어플리케이션이 비콘에 의해 실행되었을 때 CouponActivity로 이동하도록 하겠습니다.
// 비콘에의해 실행될 때와 아닐때 이동될 Activity를 다르게 줄 수 있습니다.
if(executeType != null && executeType.equals("beacon")) {
intent = new Intent(SplashActivity.this, CouponActivity.class);
} else {
intent = new Intent(SplashActivity.this, MainActivity.class);
}
실행화면입니다.
이미 어플리케이션이 실행 되어있다면...
앱이 실행되지 않은 상태에서는 CouponActivity로 이동하지만 이미 실행되어있는 경우에는 그냥 Notification만
보여주도록 해보겠습니다. MyApplication에서 Notification 부분을 수정하겠습니다.
Notification에서 Ticker를 이용해보겠습니다. Ticker를 이용하려면 setPriority가 꼭 필요합니다!
import android.app.ActivityManager;
import android.app.Application;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import com.estimote.sdk.Beacon;
import com.estimote.sdk.BeaconManager;
import com.estimote.sdk.Region;
import java.util.List;
import java.util.UUID;
/**
* Application 설치가 끝나고 최초로 실행될 때 수행되는 코드
* Created by MinChang Jang on 2016-06-23.
*/
public class MyApplication extends Application {
private BeaconManager beaconManager;
/**
* Application을 설치할 때 실행됨.
*/
@Override
public void onCreate() {
super.onCreate();
beaconManager = new BeaconManager(getApplicationContext());
// Application 설치가 끝나면 Beacon Monitoring Service를 시작한다.
// Application을 종료하더라도 Service가 계속 실행된다.
beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
@Override
public void onServiceReady() {
beaconManager.startMonitoring(new Region(
"monitored region",
// 본인이 연결 할 비콘의 아이디 부분
UUID.fromString("74278BDA-B644-4520-8F0C-720EAF059935"),
// 본인이 연결 할 비콘의 Major ID
0,
// 본인이 연결 할 비콘의 Minor ID
0));
/**
* 비콘에게 아이디, Major, Minor를 보낼 것이다.
*/
}
});
// Android 단말이 Beacon 의 송신 범위에 들어가거나, 나왔을 때를 체크한다.
beaconManager.setMonitoringListener(new BeaconManager.MonitoringListener() {
@Override
public void onEnteredRegion(Region region, List list) {
// list.get(0).getRssi()은 수신강도입니다.
// 0에 가까울수록 거리가 가깝습니다.
// showNotification은 아래 메소드를 호출하는겁니다.
// showNotification("들어옴", "비콘 연결됨" + list.get(0).getRssi());
// 이미 앱이 실행중이면 Notification만 줍니다.
if( !isAlreadyRunActivity() ) {
Intent intent = new Intent(getApplicationContext(), SplashActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("executeType", "beacon");
getApplicationContext().startActivity(intent);
} else {
showNotification("LG Twins", "사용 가능한 쿠폰이 있습니다.");
}
}
@Override
public void onExitedRegion(Region region) {
// showNotification("나감", "비콘 연결끊김");
}
});
}
/**
* Notification으로 Beacon 의 신호가 연결되거나 끊겼음을 알림.
*
* @param title
* @param message
*/
public void showNotification(String title, String message) {
Intent notifyIntent = new Intent(this, MainActivity.class);
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivities(
this, 0, new Intent[]{notifyIntent}
, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(this)
.setContentTitle(title)
.setContentText(message)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setTicker("[LG Twins] 사용 가능한 쿠폰이 있습니다.")
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.setPriority(Notification.PRIORITY_HIGH)
.build();
notification.defaults |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_SOUND;
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(1, notification);
}
/**
*
* @return
*/
private boolean isAlreadyRunActivity() {
ActivityManager activity_manager =
(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List task_info =
activity_manager.getRunningTasks(9999);
String activityName = "";
for( int i = 0; i < task_info.size(); i++ ) {
activityName = task_info.get(i).topActivity.getPackageName();
if( activityName.startsWith("com.ktds.cocomo.mybeacon") ) {
return true;
}
}
return false;
}
}
그리고 CustomAppCompatActivity를 만들어서 Activity를 종료하면 어플리케이션이 종료되도록 하겠습니다.
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.estimote.sdk.BeaconManager;
import com.estimote.sdk.Region;
import com.estimote.sdk.SystemRequirementsChecker;
import java.util.UUID;
/**
* Created by COCOMO on 2016-06-24.
*/
public class CustomAppCompatActivity extends AppCompatActivity {
private BeaconManager beaconManager;
private Region region;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
beaconManager = new BeaconManager(this);
region = new Region("ranged region",
UUID.fromString("74278BDA-B644-4520-8F0C-720EAF059935"), null, null);
}
@Override
protected void onResume() {
super.onResume();
SystemRequirementsChecker.checkWithDefaultDialogs(this);
}
@Override
protected void onPause() {
beaconManager.stopRanging(region);
super.onPause();
}
}
이젠 MainActivity, CouponActivity를 수정합니다.
public class MainActivity extends CustomAppCompatActivity {
public class CouponActivity extends CustomAppCompatActivity {
실행화면입니다.
이런걸 잘 이용하면 괜찮아 보이겠죠?
일반 매장에서 사용하는 비콘은 초음파(마이크로웨이브)를 이용한 비콘인데 가격이 저렴하지 않습니다.
저렴한 비콘을 하나 구입해서 연습해보면 좋을거 같아요
이상으로 비콘 알아보기! 마치겠습니다~
'IT_Programming > Android_Java' 카테고리의 다른 글
[펌] Android Studio에서 Debug Key MD5, SHA-1 쉽게 확인하기 (0) | 2016.07.12 |
---|---|
[펌] Making Multi-process Android applications. (0) | 2016.07.12 |
[펌] 안드로이드 간단한 BlurDrawable 구현 / 안드로이드 Blur 효과 내기 (0) | 2016.06.13 |
MEDIAPLAYER 음악 재생하기 (0) | 2016.06.07 |
[펌_Android Material Design] EditText의 업글버전? (TextInputEditText+TextInputLayout) 을 써보자 (0) | 2016.06.07 |