반응형

PC가 아닌 환경에서 작동하는 프로그램을 만든다는건 내게 낮설게만 느껴지는 일이었다. 일반적으로 사용하지 않는, 특별한 용도의 장비(항공, 우주, 의료, 군사 분야 등등..)에나 필요한 전문분야로 생각했기 때문이다. 하지만 스마트폰의 인기와 함께 널리 보급된 안드로이드, iOS는 프로그래머도 아닌 내게 새로운 도전거리(골치거리)를 선물해 주었다. 스마트폰용 앱을 개발해 보는 일이 그것이었다.

 

RTOS중 하나인 LynxOS 로고

 

재미있게도 전부터 '이게 왜 없을까? 정말 없다면 내가 만들면 좋을거 같은데..' 하고 생각 했던 앱이 있긴 했다. 내비게이션을 사용하며 운전하다 보면 목적지에 도착하기 10~15분쯤 전 상대방에게 미리 연락을 하고 싶어진다. 지금 어디쯤 왔으니 한 10분 후에 도착 할거 같다고. 정말 없는건지 나만 못찾는건지는 모르겠지만 어쨌든 보이지 않으니 내가 만들면 되는거 아닌가? (이 앱을 '다왔어'라고 부르기로 하자)

 

기기야 셀 수 없이 많지만 크게 두 가지로 나눌 수 있는 OS의 선택은 굉장히 쉬웠다. 안드로이드. 애플 제품을 좋아하지 않는 나의 유일한 선택지였다. 바로 인터넷에서 안드로이드 관련 내용을 찾아 읽어 보면서 무엇을 어떻게 공부해야 할지 생각했다. 그리고 몇 주간 안드로이드 개발 관련 서적을 공부하며 필요한 내용을 머릿속에 정리하기 시작 했다. 내 위치에서 상대방까지의 거리를 계산하기 위한 GPS기능, 메세지를 보내기 위한 SMS기능, 스마트폰에서 상대방 전화 번호 가져오기 기능 등등.

 

 

공부하기 전에는 알지 못했던 여러가지 권한 주기라든가 안드로이드 버전에 따라 없어지거나 변경된 기능들, 에뮬레이터에서는 되지만 실제 기기에서는 제대로 작동 하지 않는 내용들을 하나 하나 정리해 가며 천천히 준비 했다. 역시 무엇보다도 프로그래밍과는 무관한 회사를 다니며 앱 개발을 준비한다는건 시간적으로나 체력적으로도 쉽지 않은 일이었다. 하지만 시간이 많으면 오히려 정신적으로 나태해지기 쉽다는걸 알기 때문에 내 상황에 불만을 가지지는 않았다.

 

어쨌든 공부해서 대충 완성은 했고.. 간단히 다왔어의 코드를 살펴 보자.

 

1
2
3
4
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
    <uses-permission android:name="android.permission.SEND_SMS"/>
 

우선 다왔어 실행에 필요한 권한을 요청하기 위한 준비를 해 준다. 요청할 이름에서도 알 수 있다시피 위치, 연락처, 그리고 SMS 권한이다.

 

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
public void checkPermissions(String[] permissionRequests) {
    final ArrayList<String> permissionRequestList = new ArrayList<String>();
 
    for (final String request : permissionRequests) {
        if (ContextCompat.checkSelfPermission(this, request) != PackageManager.PERMISSION_GRANTED) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, request)) {   // Redundant in this case
                permissionRequestList.add(request);
            } else {
                permissionRequestList.add(request);
            }
        }
    }
 
    if (!permissionRequestList.isEmpty()) {
        final String[] results = new String[permissionRequestList.size()];
        permissionRequestList.toArray(results);
 
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("info");
 
        String msg = "This app won't work properly unless you grant below permissions.";
        for (String str : results)
            msg += ("\n- "+ str);
 
        builder.setMessage(msg);
        builder.setIcon(android.R.drawable.ic_dialog_info);
 
        builder.setNeutralButton("OK"new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                ActivityCompat.requestPermissions(MainActivity.this, results, MY_PERMISSION_REQUESTS);
            }
        });
 
        AlertDialog dialog = builder.create();
        dialog.show();
    }
}
 
 

앱 시작시 권한을 요청할 함수이다. 여러가지 권한을 한 번에 요청 할 수 있도록 ArrayList를 사용한다.

 

 

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
findButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String search = targetText.getText().toString();
        List<Address> addressList;
        try {
            addressList = geocoder.getFromLocationName(search, 10);
            String[] split = addressList.get(0).toString().split(",");
            String address = split[0].substring(split[0].indexOf("\""+ 1split[0].length() - 2);
            String latitude = split[10].substring(split[10].indexOf("="+ 1);  // 위도(수평선)
            String longitude = split[12].substring(split[12].indexOf("="+ 1); // 경도(수직선)
 
            String data = "목적지: " + address;
            targetView.setText(data);
            targetLocation.setLatitude(Double.parseDouble(latitude));
            targetLocation.setLongitude(Double.parseDouble(longitude));
 
            float[] distance = new float[1];
            Location.distanceBetween(myLocation.getLatitude(), myLocation.getLongitude(), targetLocation.getLatitude(), targetLocation.getLongitude(), distance);
            distanceView.setText("현재 위치와의 거리: " + distance[0/ 1000.0 + "km");
 
            isStarted = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
});
 
 

내 위치와 목적지를 파악해 거리를 계산하는 함수이다. 구글 Geocoder를 사용하기 때문에 이 함수 사용시에는 인터넷 연결이 필요하다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void getContacts(String id) {
    Cursor cursor;
 
    try {
        cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                null,
                ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?",
                new String[] {id},
                null);
 
        if (cursor.moveToFirst()) {
            recipientName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
            recipientNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
            recipientView.setText("받는 사람: " + recipientName + '\n' + "전화번호: " + recipientNumber + '\n');
 
            cursor.close();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
 
 

스마트폰의 전화번호부를 검색해 이름과 전화번호를 가져오는 함수이다. 공부했던 책의 내용이 에뮬레이터에서는 정상 작동 하지만 실제 기기에서 제대로 작동하지 않아 버그 잡는데 1시간이 넘게 걸렸다.

 

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
public void onLocationChanged(Location location) {
    if (isStarted) {
        double latitude = location.getLatitude();
        double longitude = location.getLongitude();
 
        myLocation.setLatitude(latitude);
        myLocation.setLongitude(longitude);
 
        float[] distance = new float[1];
        Location.distanceBetween(myLocation.getLatitude(), myLocation.getLongitude(), targetLocation.getLatitude(), targetLocation.getLongitude(), distance);
        distanceView.setText("현재 위치와의 거리: " + distance[0/ 1000.0 + "km");
 
        if (distance[0/ 1000.0f < Float.parseFloat(distanceText.getText().toString())){
            try {
                SmsManager smsManager = SmsManager.getDefault();
                smsManager.sendTextMessage(recipientNumber, null, smsText.getText().toString(), nullnull);
                // 문자 전송 알림음
                Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
                Ringtone ringtone = RingtoneManager.getRingtone(getApplicationContext(), uri);
                ringtone.play();
 
                Toast.makeText(getApplicationContext(), "메세지를 보냈습니다. 앱을 종료 합니다.", Toast.LENGTH_SHORT).show();
            } catch (Exception e) {
                Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
            }
 
            //  프로그램 종료하기
            isStarted = false;
 
            finish();
        }
    }
}
 
 

내 위치가 바뀔때마다 목적지와의 거리를 다시 계산하고 출력해 주는 함수이다. 지정한 거리 내로 들어오면 메세지를 보내고 앱을 종료한다. 에뮬레이터에서는 이 함수가 3초 정도에 한 번씩 자동 호출 되지만 기기에서는 실제 위치가 바뀔때만 호출되는 차이가 있다는걸 알지 못해 버그 잡는데 시간이 많이 걸렸다.

 

 

단순한 내용의 앱이기 때문에 추가 Fragment나 Activity 없이 단 한 개의 Activity만으로 디자인 했다.

 

 

첫 실행 화면

 

내 스마트폰에 설치하고 직접 테스트하는 영상. 물론 결과는 성공.

 

실제 사용해 보면 알게 되지만 이 앱은 몇 가지 단점이 있다.

 

실제 이동 거리를 계산하는 일반적인 거리 개념이다. 

 

다왔어의 거리 계산 방법. 내 현재 위치와 목적지의 직선 거리를 계산 한다.

 

구글 맵이나 내비게이션 API를 사용하면 실제 이동 거리로 거리 계산을할 수 있을거 같다. 또, 일반적으로 일정 시간이 지나면 스마트폰의 화면이 꺼지게 되는데 다왔어는 화면이 꺼지는 경우 더 이상 작동하지 않는다. 화면을 켜면 다시 거리 계산을 시작하고 작동 한다. 이 점을 방지하기 위해 자동으로 화면이 꺼지지 않도록 했는데 사용자가 전원 버튼을 눌러 화면을 끄거나 다른 앱으로 전환하는 경우 다시 다왔어로 돌아 오지 않는다면 결국 작동 하지 않는다.

 

사실 그 동안 공부한 내용을 빨리 적용해 보고 싶은 마음에 완성도 높은 앱으로 마무리 하지는 못했다. 알고 싶은게 있으면 알 때까지 공부하지만 알게되면 흥미가 좀 떨어져버리는 내 성격 때문인거 같다. 나중에 필요하다면 좀 더 보완해서 완성도를 높이겠지만 그게 언제가 될지는 모르겠다.

 

그 전에 궁금한 사람이 있다면 직접 설치해서 사용해 볼 수 있도록 apk 파일은 아래에 공유한다.

almost.apk
다운로드

 

반응형
Posted by J-sean
:
반응형

Create a simple drawing app for Android.


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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new MyView(this));
    }
 
    public class MyView extends View {
        Paint paint = new Paint();
        boolean isDrawing = false;
        ArrayList<Float> coordinateList = new ArrayList<>(100);
        // Each ArrayList instance has a capacity. The capacity is the size of the array used to store
        // the elements in the list. It is always at least as large as the list size. As elements are
        //  added to an ArrayList, its capacity grows automatically. The details of the growth policy
        //  are not specified beyond the fact that adding an element has constant amortized time cost.
 
        MyView(Context context)
        {
            super(context);
            paint.setColor(Color.BLUE); // 0xff0000ff
            paint.setStrokeWidth(8);
        }
 
        @Override
        public void onDraw(Canvas canvas)
        {
            // ArrayList, Float[], float[]은 서로 직접 호환되지 않음.
            Float[] coordinateFloat = coordinateList.toArray(new Float[coordinateList.size()]);
            float[] coordinate = new float[coordinateFloat.length];
 
            for (int i = 0; i < coordinateFloat.length; i++)
            {
                coordinate[i] = coordinateFloat[i].floatValue();
            }
 
            // 특정 색으로 화면을 채우는 메서드
            // void drawRGB(int r, int g, int b)
            // void drawARGB(int a, int r, int g, int b)
            // void drawColor(int color)
            // void drawPaint(Paint paint)
            canvas.drawARGB(2552552550); // Yellow
            canvas.drawLines(coordinate, paint);
            // Draw a series of lines. Each line is taken from 4 consecutive values in the pts array.
            // Thus to draw 1 line, the array must contain at least 4 values. This is logically the
            // same as drawing the array as follows: drawLine(pts[0], pts[1], pts[2], pts[3]) followed
            // by drawLine(pts[4], pts[5], pts[6], pts[7]) and so on.
        }
 
        // drawLines()는 4개의 좌표를 이용해 1개의 직선을 그린다(x1, y1, x2, y2)
        // 1개의 직선을 그리고 다음 직선의 시작 좌표(x1, y1)에 이전 끝 좌표(x2, y2)가
        // 없다면 끊어진 선으로 그려지게 된다. 마우스를 뗄때는 다음 직선의 시작 좌표로
        // 넣어준 값 두 개(x1, y1)를 삭제 해야 한다.
        @Override
        public boolean onTouchEvent(MotionEvent event)
        {
            switch (event.getAction())
            {
                case MotionEvent.ACTION_DOWN:
                    coordinateList.add(event.getX());
                    coordinateList.add(event.getY());
                    isDrawing = true;
                    break;
 
                case MotionEvent.ACTION_MOVE:
                    if (isDrawing)  // 이 코드에서는 별 의미는 없는 if
                    {
                        coordinateList.add(event.getX());
                        coordinateList.add(event.getY());
 
                        coordinateList.add(event.getX());
                        coordinateList.add(event.getY());
                    }
 
                    invalidate();
                    break;
 
                case MotionEvent.ACTION_UP:
                    coordinateList.remove(coordinateList.size() -1);
                    coordinateList.remove(coordinateList.size() -1);
                    isDrawing = false;
                    break;
 
                default:
                    break;
            }
 
            return true;
        }
    }



Run the app and draw.



To simplify the code, you can also use Path. The result is the same.

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
51
52
53
54
55
56
57
public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new MyView(this));
    }
 
    public class MyView extends View {
        Paint paint = new Paint();
        boolean isDrawing = false;
        Path path = new Path();
 
        MyView(Context context)
        {
            super(context);
            paint.setColor(Color.BLUE); // 0xff0000ff
            paint.setStyle(Paint.Style.STROKE); // Paint.Style.FILL(기본값)로 하면 채워진 도형이 그려진다
            paint.setStrokeWidth(8);
        }
 
        @Override
        public void onDraw(Canvas canvas)
        {
            canvas.drawARGB(2552552550);
            canvas.drawPath(path, paint);
        }
 
        @Override
        public boolean onTouchEvent(MotionEvent event)
        {
            switch (event.getAction())
            {
                case MotionEvent.ACTION_DOWN:
                    path.moveTo(event.getX(), event.getY());
                    isDrawing = true;
                    break;
 
                case MotionEvent.ACTION_MOVE:
                    if (isDrawing)
                    {
                        path.lineTo(event.getX(), event.getY());
                        invalidate();
                    }
                    break;
 
                case MotionEvent.ACTION_UP:
                    isDrawing = false;
                    break;
 
                default:
                    break;
            }
 
            return true;
        }
    }



반응형
Posted by J-sean
:
반응형

웹서버에 데이터를 요청하고 app에서 응답 받아 처리할 수 있다.


서버에서 전달 되는 데이터 형식(Json)

1
2
3
4
5
6
7
8
9
10
11
12
13
{
    "LastUpdate":"2019.10.12",
    "SalesRecord":
    [
        {"date":"2016.03.08","item":"apple","price":2400,"quantity":84,"total":201600},
        {"date":"2016.07.29","item":"grape","price":3100,"quantity":37,"total":114700},
        {"date":"2017.10.25","item":"peach","price":4600,"quantity":55,"total":253000},
        {"date":"2018.12.08","item":"banana","price":1500,"quantity":83,"total":124500},
        {"date":"2019.05.09","item":"melon","price":7200,"quantity":75,"total":540000},
        {"date":"2019.10.17","item":"coconut","price":6800,"quantity":59,"total":401200},
        {"date":"2019.11.07","item":"strawberry","price":4900,"quantity":30,"total":147000}
    ]
}



<AndroidManifest.xml>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    <uses-permission android:name="android.permission.INTERNET"/>
 
    <application
        android:usesCleartextTraffic="true">
        <!--
        android:usesCleartextTraffic
        Indicates whether the app intends to use cleartext network traffic, such as cleartext HTTP.
        The default value for apps that target API level 27 or lower is "true". Apps that target
        API level 28 or higher default to "false". When the attribute is set to "false", platform
        components (for example, HTTP and FTP stacks, DownloadManager, and MediaPlayer) will refuse
        the app's requests to use cleartext traffic. Third-party libraries are strongly encouraged
        to honor this setting as well. The key reason for avoiding cleartext traffic is the lack of
        confidentiality, authenticity, and protections against tampering; a network attacker can
        eavesdrop on transmitted data and also modify it without being detected.
        -->



<MainActivity.java>

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public class MainActivity extends AppCompatActivity {
 
    EditText editText;
    TextView textView;
 
    static RequestQueue requestQueue;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        editText = findViewById(R.id.editText);
        textView = findViewById(R.id.textView);
 
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                makeRequest();
            }
        });
 
        if (requestQueue == null) {
            requestQueue = Volley.newRequestQueue(getApplicationContext());
        }
    }
 
    public void makeRequest() {
        String url = editText.getText().toString();
        StringRequest request = new StringRequest(Request.Method.GET, url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        try {
                            //Server(Ubuntu) data encoding
                            response = new String(response.getBytes("iso-8859-1"), "utf-8");
                            //textView.append("Raw data: " + response);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
 
                        Gson gson = new Gson();
                        RequestResult requestResult = gson.fromJson(response, RequestResult.class);
                        textView.setText("Items count: " + requestResult.SalesRecord.size() + '\n');
                        textView.append("Last Update: " + requestResult.LastUpdate + '\n');
                        ArrayList<Data> items = requestResult.SalesRecord;
                        for (int i = 0; i < items.size(); i++) {
                            textView.append(String.format("■ Date: %s, Item: %s, Price: %s, Quantity: %s, Total: %s\n",
                                    items.get(i).date, items.get(i).item, items.get(i).price, items.get(i).quantity, items.get(i).total));
                        }
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        textView.append("Error: " + error.getMessage());
                    }
                }
        ) {
            @Override
            protected Map<StringString> getParams() throws AuthFailureError {
                Map<StringString> params = new HashMap<StringString>();
 
                return params;
            }
        };
 
        request.setShouldCache(false);
        requestQueue.add(request);
        textView.setText("Request sent.");
    }
}




<RequestResult.java>

1
2
3
4
public class RequestResult {
    String LastUpdate;
    ArrayList<Data> SalesRecord = new ArrayList<Data>();
}


서버 데이터의 key와 같은 이름의 변수를 만들어야 한다.


<Data.java>

1
2
3
4
5
6
7
public class Data {
    String date;
    String item;
    String price;
    String quantity;
    String total;
}


서버 데이터의 key와 같은 이름의 변수를 만들어야 한다


실행 화면. 서버 주소를 입력하고 DATA REQUEST 버튼을 클릭한다.


서버부터 전달된 데이터가 지정한 대로 표시된다.


반응형
Posted by J-sean
:
반응형

Timer와 TimerTask를 이용해 정해진 시간에 반복적인 작업을 할 수 있다.


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
public class MainActivity extends AppCompatActivity {
 
    TextView textView;
    Timer timer;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        textView = findViewById(R.id.textView);
        final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy.MM.dd EEE HH:mm:ss");
 
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                // Only the original thread that created a view hierarchy can touch its views.
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        String currentTime = simpleDateFormat.format(new Date());
                        textView.setText(currentTime);
                    }
                });
            }
        };
 
        timer = new Timer();
        timer.schedule(timerTask, 01000);
    }
 
    @Override
    protected void onDestroy() {
        timer.cancel();
        super.onDestroy();
    }
}




반응형
Posted by J-sean
:
반응형

Location과 Geocoder를 이용해 내 현재 위치와 다른 위치 사이의 거리를 구할 수 있다.

 

<AndroidManifest.xml>

1
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
 

 

<MainActivity.java>

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
public class MainActivity extends AppCompatActivity {
 
    final int MY_PERMISSION_REQUEST_GPS = 1001;
 
    Button button;
    Button button2;
    Button button3;
 
    EditText editText;
 
    TextView textView;
    TextView textView2;
    TextView textView3;
 
    Location myLocation;
    Location tarLocation;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        tarLocation = new Location("Target location");
        myLocation = new Location("My location");
 
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
 
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle("info");
                builder.setMessage("This app won't work properly unless you grant GPS permission.");
                builder.setIcon(android.R.drawable.ic_dialog_info);
 
                builder.setNeutralButton("OK"new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ActivityCompat.requestPermissions(MainActivity.thisnew String[] {Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSION_REQUEST_GPS);
                    }
                });
 
                AlertDialog dialog = builder.create();
                dialog.show();
            } else {
                ActivityCompat.requestPermissions(thisnew String[] {Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSION_REQUEST_GPS);
            }
        }
 
        textView = findViewById(R.id.textView);
        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startLocationService();
            }
        });
 
        final Geocoder geocoder = new Geocoder(this);
        editText = findViewById(R.id.editText);
        textView2 = findViewById(R.id.textView2);
        button2 = findViewById(R.id.button2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String search = editText.getText().toString();
                List<Address> addressList;
                try {
                    addressList = geocoder.getFromLocationName(search, 10);
                    String[] split = addressList.get(0).toString().split(",");
                    String address = split[0].substring(split[0].indexOf("\""+ 1split[0].length() - 2);
                    String latitude = split[10].substring(split[10].indexOf("="+ 1);  // 위도(수평선)
                    String longitude = split[12].substring(split[12].indexOf("="+ 1); // 경도(수직선)
 
                    String data = "Address: " + address + "\n" + "Latitude: " + latitude + "\n" + "Longitude: " + longitude;
                    textView2.setText(data);
                    tarLocation.setLatitude(Double.parseDouble(latitude));
                    tarLocation.setLongitude(Double.parseDouble(longitude));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
 
        textView3 = findViewById(R.id.textView3);
        button3 = findViewById(R.id.button3);
        button3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                float[] distance = new float[1];
                Location.distanceBetween(myLocation.getLatitude(), myLocation.getLongitude(), tarLocation.getLatitude(), tarLocation.getLongitude(), distance);
                textView3.setText("Distance: " + distance[0/ 1000.0f + "km");
                //textView3.setText("Distance: " + String.valueOf(myLocation.distanceTo(tarLocation) / 1000.0f) + "km");
            }
        });
    }
 
    public void startLocationService() {
        LocationManager manager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
        try {
            Location location = manager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
            if (location != null) {
                double latitude = location.getLatitude();
                double longitude = location.getLongitude();
                
                myLocation.setLatitude(latitude);
                myLocation.setLongitude(longitude);
                
                String message = "My last location\nLatitude: " + latitude + "\nLongitude: " + longitude;
                Log.d("App""Last location request");
                textView.setText(message);
            } else
            {
                Toast.makeText(this"location is null", Toast.LENGTH_SHORT).show();
            }
 
            GPSListener gpsListener = new GPSListener();
            long minTime = 3000;
            float minDistance = 0;
 
            manager.requestLocationUpdates(LocationManager.GPS_PROVIDER, minTime, minDistance, gpsListener);
            Log.d("App""Current location request");
        }catch (SecurityException e) {
            e.printStackTrace();
        }
    }
 
    class GPSListener implements LocationListener {
        public void onLocationChanged(Location location) {
            double latitude = location.getLatitude();
            double longitude = location.getLongitude();
            String message = "My current location\nLatitude: " + latitude + "\nLongitude: " + longitude;
            textView.setText(message);
 
            myLocation.setLatitude(latitude);
            myLocation.setLongitude(longitude);
        }
 
        @Override
        public void onProviderEnabled(String provider) {
        }
 
        @Override
        public void onProviderDisabled(String provider) {
        }
 
        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
        }
    }
 
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSION_REQUEST_GPS: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0 && grantResults[0== PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this,"Permission granted.",Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(this,"Permission denied.",Toast.LENGTH_SHORT).show();
                }
            }
        }
    }
}
 

 

 

FIND MY CURRENT LOCATION 버튼을 클릭 한다. (내 위치는 분당 서현역)

 

주소를 입력하고 FIND ADDRESS LOCATION 버튼을 클릭 한다.

 

CALCULATE DISTANCE 버튼을 클릭 한다.

 

현재 위치(서현역)와 동탄역 사이의 (직선)거리가 표시 된다.

 

 

Google Map에서 계산한 거리와 거의 같다.

 

 

반응형
Posted by J-sean
:
반응형

Geocoder를 사용하면 주소로 위경도를, 위경도로 주소를 확인 할 수 있다.


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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public class MainActivity extends AppCompatActivity {
 
    Geocoder geocoder;
 
    EditText editText;
    Button button;
    TextView textView;
 
    EditText editText2;
    EditText editText3;
    Button button2;
    TextView textView2;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        geocoder = new Geocoder(this);
 
        editText = findViewById(R.id.editText);
        textView = findViewById(R.id.textView);
        button = findViewById(R.id.button);
 
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                List<Address> addressList;
                String search = editText.getText().toString();
 
                try {
                    addressList = geocoder.getFromLocationName(search, 10);
                    String[] split = addressList.get(0).toString().split(",");
                    String address = split[0].substring(split[0].indexOf("\""+ 1split[0].length() - 2);
                    String latitude = split[10].substring(split[10].indexOf("="+ 1);  // 위도(수평선)
                    String longitude = split[12].substring(split[12].indexOf("="+ 1); // 경도(수직선)
 
                    String data = "Address: " + address + "\n" + "Latitude: " + latitude + "\n" + "Longitude: " + longitude;
                    textView.setText(data);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
 
        editText2 = findViewById(R.id.editText2);
        editText3 = findViewById(R.id.editText3);
        textView2 = findViewById(R.id.textView2);
        button2 = findViewById(R.id.button2);
 
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                List<Address> addressList;
                double latitude = Double.parseDouble(editText2.getText().toString());
                double longitude = Double.parseDouble(editText3.getText().toString());
 
                try {
                    addressList = geocoder.getFromLocation(latitude, longitude, 10);
                    textView2.setText("Address: " + addressList.get(0).getAddressLine(0));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}




실행 화면


간단한 주소를 입력하고 SEARCH GEOGRAPHIC COORDINATE 버튼을 클릭 하면 정확한 주소와 위경도가 표시 된다.


위경도를 입력하고 SEARCH ADDRESS 버튼을 클릭 하면 주소가 표시된다.


주소로 서현역을 검색 하거나 서현역 위경도로 검색하면 addressList[0]에 입력되는 내용은 아래와 같다. (똑같은 양식으로 입력 된다)


Address[addressLines=[0:"대한민국 경기도 성남시 분당구 서현동 성남대로 지하 601 서현"],feature=서현,admin=경기도,sub-admin=null,locality=성남시,thoroughfare=null,postalCode=463-050,countryCode=KR,countryName=대한민국,hasLatitude=true,latitude=37.384938999999996,hasLongitude=true,longitude=127.12326300000001,phone=null,url=null,extras=null]


반응형
Posted by J-sean
: