Chapter 24 ] 맵서비스 (24-1 위치제공자, 위치 기반 서비스, 위치 제공자, 위치 조사, 도착 알림)
오늘은 카메라와 위치정보를 이용하여 특정지역에 진입하였을 경우 알려주는 테스트 어플을 제작해 보았다.
우선은 매니페스트 부터 훑어보자.
*. AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="lee.hyeontae.C25_Camera" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <!-- Activity --> <activity android:name=".C25_Camera" android:label="@string/app_name" android:screenOrientation="landscape"> <intent-filter> <action android:name="android.intent.action.MAIN"
/> <category android:name="android.intent.category.LAUNCHER"
/> </intent-filter> </activity> <activity android:name=".LocationAlert" android:theme="@android:style/Theme.Dialog" android:screenOrientation="landscape"
/> <receiver android:name=".LocationAlertReceiver"> <intent-filter> <action android:name="lee.hyeontae.C25_Camera"
/> </intent-filter> </receiver> </application> <!--
Permission --> <uses-permission android:name="android.permission.CAMERA"
/> <uses-feature android:name="android.hardware.camera"
/> <uses-feature android:name="android.hardware.camera.autofocus"
/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
/> <uses-permission android:name="android.permission.LOCATION"
/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
/> <uses-permission android:name="android.permission.VIBRATE"
/> <!--
Screen Size Support --> <supports-screens android:largeScreens="true" android:normalScreens="true" android:smallScreens="true" android:resizeable="true" android:anyDensity="true"
/> <uses-sdk android:minSdkVersion="8"
/> </manifest>
Activity 두개 Receiver하나 설정했다. C25_Camera Activity는 메인 Activity이고, LocationAlert 는 팝업을 출력할때 사용할 테마가 적용된 Activity이다. LocationAlertReceiver 는 특정지역에 진입하였을 경우 BroadCast를 수신받도록한 Receiver이다. |
*. C25_Camera.java
package lee.hyeontae.C25_Camera; import java.io.File; import java.io.FileOutputStream; import java.util.List; import android.app.Activity; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.drawable.Drawable; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.PictureCallback; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.SubMenu; import android.view.View; import android.view.Window; import android.widget.Button; import android.widget.ImageButton; import android.widget.TextView; import android.widget.Toast; public class C25_Camera extends Activity { SharedPreferences preferences; SharedPreferences.Editor editor; MyCameraSuface mSurface; ImageButton mShutter; TextView providerTextView, locationTextView, currentDestination; public static LocationManager LM; String mBestProvider; int mCount=0; public static PendingIntent mPending; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.main); InitViews(); preferences =
getSharedPreferences("destination", Service.MODE_PRIVATE); LocationProviderList(); locationAlert(); /** * @description 카메라 촬영 버튼을 눌렀을 경우 동작. */ mShutter.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View v) { mShutter.setEnabled(false); mSurface.mCamera.autoFocus(mAutoFocus); handler.sendEmptyMessageDelayed(0, 1000); } }); } /** * @description 카메라 촬영시 Focus조정후 1초뒤 자동으로 촬영함. */ Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); mSurface.mCamera.takePicture(null, null, mPicture); } }; /** * @description Focus조정을 위한 Callback */ AutoFocusCallback mAutoFocus = new AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { mShutter.setEnabled(success); } }; /** * @description 카메라 영상을 JPG파일로 저장. */ PictureCallback mPicture = new PictureCallback() { @Override public void onPictureTaken(byte[] data,
Camera camera) { String sd = Environment.getExternalStorageDirectory() .getAbsolutePath(); String path = sd + "/cameratest.jpg"; File file = new File(path); try { FileOutputStream fos
= new FileOutputStream(file); fos.write(data); fos.flush(); fos.close(); } catch (Exception e) { Toast.makeText(C25_Camera.this, "파일 저장 중 에러 발생 : " + e.getMessage(), 0).show(); return; } Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); Uri uri = Uri.parse("file://" + path); intent.setData(uri); sendBroadcast(intent); Toast.makeText(C25_Camera.this, "사진 저장 완료 : " + path, 0).show(); mSurface.mCamera.startPreview(); } }; /** * @description 위치정보를 구하기에 최적의 Provider를 선택. */ @SuppressWarnings("static-access") private void LocationProviderList() { LM =
(LocationManager) this.getSystemService(Context.LOCATION_SERVICE); List<String> arProvider = LM.getProviders(false); String result = ""; for (int i = 0; i < arProvider.size(); i++) { result += ("Provider " + i + " : " +
arProvider.get(i) + "\n"); } Criteria crit = new Criteria(); crit.setAccuracy(Criteria.NO_REQUIREMENT); crit.setPowerRequirement(Criteria.NO_REQUIREMENT); crit.setAltitudeRequired(false); crit.setCostAllowed(false); mBestProvider = LM.getBestProvider(crit, true); result += ("\nBest Provider : " + mBestProvider + "\n\n"); result += LM.GPS_PROVIDER + " :
" + LM.isProviderEnabled(LocationManager.GPS_PROVIDER) + "\n"; result += LM.NETWORK_PROVIDER + " :
" + LM.isProviderEnabled(LocationManager.NETWORK_PROVIDER) + "\n"; providerTextView.setText(result); } /** * @description 모든 View를 초기화함. */ private void InitViews() { mSurface =
(MyCameraSuface) findViewById(R.id.preview); mShutter =
(ImageButton) findViewById(R.id.shutter); Drawable alpha1 = mShutter.getBackground(); alpha1.setAlpha(0); providerTextView =
(TextView) findViewById(R.id.providerTextView); locationTextView =
(TextView) findViewById(R.id.currentLocationTextView); currentDestination = (TextView)findViewById(R.id.currentDestinationTextView); } /** * @description 단말의 현재 위치를 구함. */ LocationListener mListener = new LocationListener() { @Override public void onLocationChanged(Location location) { mCount++; String sloc = String.format("수신회수:%d\n위도:%f\n경도:%f\n고도:%f", mCount, location.getLatitude(),
location.getLongitude(), location.getAltitude()); locationTextView.setText(sloc); } @Override public void onProviderDisabled(String provider) { } @Override public void onProviderEnabled(String provider) { } @Override public void onStatusChanged(String provider, int status, Bundle extras) { } }; /** * @description 특정 지역 접근시 알림 팝업 출력. 예) 가산 디지털 단지역 */ private void locationAlert() { Log.d("dodo4989","locationAlert"); Intent intent = new Intent(this, LocationAlertReceiver.class); intent.setAction("lee.hyeontae.C25_Camera"); mPending =
PendingIntent.getBroadcast(this, 0, intent, 0); Log.d("dodo4989","preferences.getInt="+preferences.getInt("where", 4)); switch (preferences.getInt("where", 4)){ case 1: LM.addProximityAlert(37.48030511658541,
126.8828335404396, 500, -1, mPending); currentDestination.setText("가산디지털단지역"); break; case 2: LM.addProximityAlert(37.4994995976215,
126.9287234544754, 500, -1, mPending); currentDestination.setText("신대방삼거리역"); break; case 3: LM.addProximityAlert(37.483957534386015,
126.93013697862625, 500, -1, mPending); currentDestination.setText("신림역"); break; case 4: LM.addProximityAlert(37.487049,
126.878930, 500, -1, mPending); currentDestination.setText("회사"); break; } } /** * @description 메뉴 버튼 Create */ @Override public boolean onCreateOptionsMenu(Menu menu) { SubMenu sub = menu.addSubMenu("목적지설정"); sub.add(0,1,0,"가산디지털단지역"); sub.add(0,2,0,"신대방삼거리역"); sub.add(0,3,0,"신림역"); sub.add(0,4,0,"회사"); return super.onCreateOptionsMenu(menu); } /** * @description 메뉴에서 각 옵션 선택시 동작 구현. */ @Override public boolean onOptionsItemSelected(MenuItem item) { editor = preferences.edit(); Intent intent = new Intent(this, LocationAlertReceiver.class); intent.setAction("lee.hyeontae.C25_Camera"); mPending =
PendingIntent.getBroadcast(this, 0, intent, 0); switch(item.getItemId()){ case 1: Log.d("dodo4989","1"); editor.putInt("where", 1); LM.addProximityAlert(37.48030511658541,
126.8828335404396, 500, -1, mPending); currentDestination.setText("가산디지털단지역"); break; case 2: Log.d("dodo4989","2"); editor.putInt("where", 2); LM.addProximityAlert(37.4994995976215,
126.9287234544754, 500, -1, mPending); currentDestination.setText("신대방삼거리역"); break; case 3: Log.d("dodo4989","3"); editor.putInt("where", 3); LM.addProximityAlert(37.483957534386015,
126.93013697862625, 500, -1, mPending); currentDestination.setText("신림역"); break; case 4: Log.d("dodo4989","4"); editor.putInt("where", 4); LM.addProximityAlert(37.487049,
126.878930, 500, -1, mPending); currentDestination.setText("회사"); break; } editor.commit(); return super.onOptionsItemSelected(item); } @Override protected void onResume() { super.onResume(); LM.requestLocationUpdates(mBestProvider, 1000, 1, mListener); // 1초마다 위치정보를 받아옴, 1미터 변경시 위치정보를 받아옴. } @Override protected void onPause() { super.onPause(); LM.removeUpdates(mListener); LM.removeProximityAlert(mPending); } @Override protected void onDestroy() { super.onDestroy(); } }
|
*. main.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <lee.hyeontae.C25_Camera.MyCameraSuface android:id="@+id/preview" android:layout_width="fill_parent" android:layout_height="fill_parent"
/> <TextView android:id="@+id/currentDestinationTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20dip" android:textColor="#0000FF" android:layout_gravity="center_horizontal" /> <ImageButton android:id="@+id/shutter" android:src="@drawable/camera_shutter_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|right"
android:layout_marginRight="5dip" /> <TextView android:id="@+id/providerTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|left"
/> <TextView android:id="@+id/currentLocationTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right"
android:textColor="#FF0000" /> </FrameLayout>
|
*. MyCameraSuface.java
package lee.hyeontae.C25_Camera; import java.util.List; import android.content.Context; import android.graphics.PixelFormat; import android.hardware.Camera; import android.hardware.Camera.Size; import android.util.AttributeSet; import android.view.SurfaceHolder; import android.view.SurfaceView; public class MyCameraSuface extends SurfaceView
implements SurfaceHolder.Callback{ SurfaceHolder mHolder; Camera mCamera; public
MyCameraSuface(Context context, AttributeSet attrs) { super(context,
attrs); mHolder =
getHolder(); mHolder.addCallback(this); mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Camera.Parameters params = mCamera.getParameters(); List<Size> arSize =
params.getSupportedPreviewSizes(); if(arSize == null){ params.setPreviewSize(width,
height); } else { int diff =
10000; Size opti = null; for(Size s :
arSize) { if(Math.abs(s.height - height)
< diff) { diff = Math.abs(s.height - height); opti = s; } } params.setPreviewSize(opti.width, opti.height); } params.setPictureFormat(PixelFormat.JPEG); mCamera.setParameters(params); mCamera.startPreview(); } @Override public void surfaceCreated(SurfaceHolder holder) { mCamera = Camera.open(); try { mCamera.setPreviewDisplay(mHolder); } catch (Exception
e) { mCamera.release(); mCamera = null; } } @Override public void surfaceDestroyed(SurfaceHolder holder) { if(mCamera != null) { mCamera.stopPreview(); mCamera.release(); mCamera = null; } } }
쓸데없는 카메라 촬영기능도 있다^^ |
*. LocationAlertReceiver.java
package lee.hyeontae.C25_Camera; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.location.LocationManager; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Vibrator; import android.util.Log; import android.widget.Toast; /** * @description 특정지역 접근시 날라온 BroadCast를 Receive하여 처리함. */ public class LocationAlertReceiver extends BroadcastReceiver{ Beeper DingDong; Vibrator vibrator; @Override public void onReceive(Context context, Intent intent) { Log.d("dodo4989","onReceive"); if(!MannerMode(context)){ DingDong = new Beeper(context, R.raw.dingdong); DingDong.play(); } vibrator =
(Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); vibrator.vibrate(1000); boolean bEnter =
intent.getBooleanExtra(LocationManager.KEY_PROXIMITY_ENTERING, true); // true이면 특정지역 접근, false이면 특정지역에서 해제 Toast.makeText(context,
bEnter ? "아싸 도착!!" : "나갔네!!", Toast.LENGTH_LONG).show(); // 토스트 팝업 말고 Activity팝업 띄우기 // if(bEnter){ // Intent alert_intent = new
Intent(context, LocationAlert.class); // alert_intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // context.startActivity(alert_intent); // } } /** * @description 매너모드 인지 체크함. * @param context * @return 매너모드이면 true, 일반모드이면 false */ private boolean MannerMode(Context context){ AudioManager mAudioManager =
(AudioManager)context.getSystemService(Context.AUDIO_SERVICE); switch
(mAudioManager.getRingerMode()){ case
AudioManager.RINGER_MODE_SILENT: case
AudioManager.RINGER_MODE_VIBRATE: return true; } return false; } } /** * @description 비프음을 출력하기 위한 클래스. */ class Beeper{ MediaPlayer player; Beeper(Context context, int id) { player =
MediaPlayer.create(context, id); } void play() { player.seekTo(0); player.start(); } }
특정 지역에 진입했을때 날라온 BroadCast를 Receive한다. 이때 토스트도 띄우고, 진동도 울리고, 소리도 나고^^
매너모드를 체크하는것도 잊지 않았지!!!
|
*. LocationAlert.java
package lee.hyeontae.C25_Camera; import android.R.style; import android.app.Activity; import android.content.res.Resources.Theme; import android.os.Bundle; import android.view.Window; /** * @description 특정지역 접근시 알림 팝업 출력 */ public class LocationAlert extends Activity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.location_alert); } @Override protected void onApplyThemeResource(Theme theme, int resid, boolean first) { super.onApplyThemeResource(theme,
resid, first); theme.applyStyle(style.Theme_Panel, true); } @Override protected void onPause() { super.onPause(); //C25_Camera.LM.removeProximityAlert(C25_Camera.mPending); } }
요거 띄우니까 동작이 이상해져서 일단 패스함. |
*. location_alert.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="100dip" android:layout_height="100dip"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="아싸 도착!!!" android:textSize="30dip"
/> </LinearLayout>
|
카메라 공부만 해볼려고한건데...LBS도 공부하는구나~~~~ㅋㅋㅋ