이번에는 간단한 오버레이 뷰를 만들어 보도록 하자


안드로이드 6.0이후의 기기는 오버레이 기능을 위해 퍼미션을 받아야 한다.


하지만 그 방식은 다른 퍼미션을 받는 방식과는 약간 다르다.


다른 퍼미션(카메라나 저장장치)는 퍼미션 허가창만 띄우면 가능하지만 오버레이는 설정창을 띄워주어야 한다.


코드로 확인한다면 아래와 같다.


public void openView() {
if(Settings.canDrawOverlays(this))
startService(new Intent(this, FloatingViewService.class));
else
onObtainingPermissionOverlayWindow();
}
public void onObtainingPermissionOverlayWindow() {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQ_CODE_OVERLAY_PERMISSION);
}

오버레이 뷰를 실행할 때는  openView()함수를 호출할 예정이다.


Settings.canDrawOverlays(this)를 호출하면 현재 앱이 오버레이를 띄울수 있는지 확인하는 단계이다.


만일 현재 오버레이를 띄울수 있다면 오버레이를 호출하고, 띄울수없다면 오버레이 퍼이션을 받아야 한다.


오버레이 퍼미션은 onObtainingPermissionOverlayWindow()함수 내부 처럼 두줄로 받을 수 있다.


오버레이 퍼미션의 더 자세한 내용은 아래 블로그에서 확인 가능하다. 


꿈 많은 개발자가 되자 : http://thdev.tech/androiddev/2017/01/30/Android-Overlay-Permission.html





오버레이 뷰는 다른 액티비티가 종료되어도 동작해야하기 때문에 Service에서 동작시킨다. 서비스가 실행된다면 오버레이 뷰를 실행시킨다.



@Override
public void onCreate() {
super.onCreate();

LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
onTopView = inflater.inflate(R.layout.always_on_top_layout, null);
onTopView.setOnTouchListener(this);

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);

params.gravity = Gravity.LEFT | Gravity.TOP;

manager = (WindowManager) getSystemService(WINDOW_SERVICE);
manager.addView(onTopView, params);

Button closeBtn = onTopView.findViewById(R.id.close_this_window);
closeBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
manager.removeView(onTopView);
onTopView = null;
stopSelf();
}
});
}

위는 서비스의 onCreate이다. 서비스가 시작되면 레이아웃을 설정하고 레이아웃 파라메터를 설정해주면 된다.


그리고 버튼은 현재 뷰를 지워주는 역활을 하게되는데 뷰와 서비스를 종료하면 된다.





마지막으로 뷰의 이동을 구현한다.


switch (action) {
case MotionEvent.ACTION_DOWN:
if (pointerCount == 1) {
xpos = motionEvent.getRawX();
ypos = motionEvent.getRawY();
}
break;
case MotionEvent.ACTION_MOVE:
if (pointerCount == 1) {
WindowManager.LayoutParams lp = (WindowManager.LayoutParams) view.getLayoutParams();
float dx = xpos - motionEvent.getRawX();
float dy = ypos - motionEvent.getRawY();
xpos = motionEvent.getRawX();
ypos = motionEvent.getRawY();

Log.d(TAG, "lp.x : " + lp.x + ", dx : " + dx + "lp.y : " + lp.y + ", dy : " + dy);

lp.x = (int) (lp.x - dx);
lp.y = (int) (lp.y - dy);

manager.updateViewLayout(view,lp);
return true;
}
break;

}

onTouch내부에  switch함수를 넣어 구성하였다. layoutparam을 변경해주어 이동시키면 된다.



GitHub : https://github.com/pchan1401-ICIL/AlwaysOnTopViewTest

개발자가 실내에서 GPS를 이용한 프로그램을 개발하는 경우 GPS를 가상위치에 놓아야 하는 상황이 발생한다. 


이럴때 가장 쉬운 방법은 가상GPS프로그램을 사용하면 된다.


하지만, 이러한 프로그램은 자신의 위치를 계속적으로 컨트롤 하거나 개발자가 필요한 기능을 가지지 않는 경우가 있다.


예를들어 반복적인 위치 움직임을 필요로하는 경우가 있다. 이러한 프로그램은 찾아도 없었다. 따라서 직접 만들어보았다.





해당 프로그램은 백그라운드 작업을 필요로한다. 액티비티가 종료되어도 사용자의 위치는 계속적으로 나타나져야 한다. 따라서 아래와 같이 서비스를 이용하였다.

public class LocationUpdateService extends IntentService




아래는 본격적인 가상위치를 제공하는 코드이다.

public MockLocationProvider(String name, Context ctx) {
providerName = name;
mctx = ctx;

LocationManager lm = (LocationManager) ctx.getSystemService(
Context.LOCATION_SERVICE);
lm.addTestProvider(providerName, true, true, true, true, true,
true, true, Criteria.NO_REQUIREMENT, Criteria.ACCURACY_FINE);
lm.setTestProviderEnabled(providerName, true);
}

가상위치를 설정해주기 위하여 초기화하는 부분이다. addTestProvider함수로 가상위치를 사용할 준비를 한다.


addTestProvider함수는 여러변수를 가지는데 위치제공자의 이름, 필요한 것, 제공하는 값을 나타낸다. 자세한 내용은 아래 링크를 참조하면 된다.



https://developer.android.com/reference/android/location/LocationManager.html#addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int)





아래는 가상위치의 값들을 설정하는 코드이다.

public void pushLocation(double lat, double lon, double alt) {
try {
LocationManager lm = (LocationManager) mctx.getSystemService(
Context.LOCATION_SERVICE);

Location mockLocation = new Location(providerName);
long currentTime = System.currentTimeMillis();
mockLocation.setLatitude(lat);
mockLocation.setLongitude(lon);
mockLocation.setAltitude(alt);
mockLocation.setTime(currentTime);
mockLocation.setAccuracy(1.0f);
mockLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());

lm.setTestProviderStatus(providerName, LocationProvider.AVAILABLE, mockLocation.getExtras(), currentTime);
lm.setTestProviderLocation(providerName, mockLocation);
} catch(RuntimeException e){
e.printStackTrace();
}
}

여러 값을 설정하고 이 값들을 setTestProviderLocation에 넣으면 GPS값이 출력된다.


pushLocation을 지속적으로 호출하여 사용자의 위치를 계속 갱신한다.






마지막으로 Manifest파일에 가상위치를 사용한다고 권한을 설정해주어야한다.

<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_MOCK_LOCATION"/>

ACCES_COARSE_LOCATION과 ACCES_FINE_LOCATION은 위치제공자를 사용하기 위해 필요하다.


ACCES_MOCK_LOCATION은 가상위치를 사용하기 위해 필요하다.






테스트를 하기 위하여 위치사용허가를 해야한다. 


설정 > 시스템 > 개발자옵션 > 모의위치 앱에서 TestMockLocation을 선택한다.


이 내용은 안드로이드 버전에서 약간씩 다르다.






아래 화면에서 버튼을 누르면 가상 위치가 실행된다.


버튼을 누르면 실행


이 상태에서 지도(카카오맵)을 켜면 사용자의 위치가 나타난다.


카카오맵


위치값을 읽는 코드를 이용하여 제대로 작동하는지 확인한다.


이 코드는 아래 github링크를 놓겠다.


받는 데이터




마지막 사진에서 가상위치라고 isFromMockProvider가 true로 나온다. 실제 위치일 경우 false가 나타난다.




이처럼 개발자는 사무실에 있는데 광화문 한복판에 있다.


이러한 기능을 이용하여 GPS를 사용한 앱의 디버깅을 쉽게 할 수 있다.






아래는 참고한 웹페이지이다.


https://mobiarch.wordpress.com/2012/07/17/testing-with-mock-location-data-in-android/





Github


가상위치 : https://github.com/pchan1401-ICIL/TestMockLocation


GPS읽기 : https://github.com/pchan1401-ICIL/LocationReader










코드 : https://github.com/pchan1401-ICIL/Camera1_2_Compare



이번 글은 단순히 Camera2와 Camera API의 Fov를 비교하는 글이다.


최근에 Camera2를 사용하던 코드른 Camera API로 변환하는 과정에서 어떠한 값을 나타내는지 비교하는 것이다.


Camera 코드의 Fov를 구하는 코드는 많이 있으니 생략한다.


두 API의 차이점은 아래와 같다.


 - Camera : API가 이미 존재하여 값을 불러온다.

 - Camera2 : 센서의 크기를 이용하여 계산한다.


그리고 결과는 아래와 같다.


갤럭시탭 S3


한번 다른 기기를 이용해보았다


갤럭시 노트4




삼성기기만 그러한지는 모르겠으나 두 기기를 비교해보면 Camera와 Camera2의 FOV출력은 정확히 같은계산을 하는 것 같다.






+ Recent posts