본문 바로가기

마스크 판매 위치찾기 앱

반응형

요즘

코로나 19 때문에 마스크를 찾는 일이 빈번해졌다.

 

이러한 이유로 내 주변 마스크 판매 장소와 수량을 알려주는 앱을 만들어 봤다.

(한 번 설치하고 조금 지나야 GPS가 정상 작동한다.)

 

마스크 판매 위치와 수량은 정부에서 제공한 API를 기반으로 했다.

https://www.data.go.kr/dataset/15043025/openapi.do#

 

공공데이터포털

국가에서 보유하고 있는 다양한 데이터를『공공데이터의 제공 및 이용 활성화에 관한 법률(제11956호)』에 따라 개방하여 국민들이 보다 쉽고 용이하게 공유•활용할 수 있도록 공공데이터(Dataset)와 Open API로 제공하는 사이트입니다.

www.data.go.kr

 

 

 

 

사용한 아이콘은

저번에 올린 서울시 공공자전거 따릉이 여행북 프로젝트 때 사용한 것을

그대로 사용했다.

(2020/03/17/00:00AM 오류 수정)

app-release (3).zip
2.01MB

API를 불러오고 JSON파싱하는 코드는 아래의 블로그를 참고하여 만들었다.

https://blog.naver.com/mark8864/221855158676

 

개발(1) 마스크 앱

//밑의 문장들은 모두 정확한 정보가 아닐 수 있음을 밝힙니다.//글쓴이의 주관적인 생각이 담긴 내용입니...

blog.naver.com

 

구글 지도를 불러오는 코드는 아래의 블로그를 참고했다.

(API를 발급 받아야 구글 지도를 사용할 수 있다.)

https://webnautes.tistory.com/647

 

Google Maps Android API 사용 방법 및 예제

Google Maps Android API를 사용하는 기본적인 방법과 사용시 발생할 수 있는 문제점에 대해 다룹니다. 1. 간단한 안드로이드 구글맵 예제 동작시키기 2. Google Maps Android API 예제 코드 설명 3. Google Maps..

webnautes.tistory.com

 

전체코드

MainActivity.java

더보기
package com.example.maskfinder;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;

import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.material.snackbar.Snackbar;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;


public class MainActivity extends AppCompatActivity
        implements OnMapReadyCallback,
        ActivityCompat.OnRequestPermissionsResultCallback{

    ArrayList storeName = new ArrayList<>();
    ArrayList storeLat = new ArrayList<>();
    ArrayList storeLng = new ArrayList<>();
    ArrayList storeType = new ArrayList<>();
    ArrayList storeRemain = new ArrayList<>();
    ArrayList storeMarkers= new ArrayList<>();
    private GoogleMap mMap;
    private Marker currentMarker = null;

    private static final String TAG = "googlemap_example";
    private static final int GPS_ENABLE_REQUEST_CODE = 2001;
    private static final int UPDATE_INTERVAL_MS = 30000;  // 30초
    private static final int FASTEST_UPDATE_INTERVAL_MS = 10000; // 10초


    private static final int PERMISSIONS_REQUEST_CODE = 100;
    boolean needRequest = false;


    String[] REQUIRED_PERMISSIONS  = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};


    Location mCurrentLocatiion;


    private FusedLocationProviderClient mFusedLocationClient;
    private LocationRequest locationRequest;
    private Location location;


    private View mLayout;

    Bitmap green, yellow, red,gray;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.activity_main);

        mLayout = findViewById(R.id.layout_main);

        locationRequest = new LocationRequest()
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                .setInterval(UPDATE_INTERVAL_MS)
                .setFastestInterval(FASTEST_UPDATE_INTERVAL_MS);


        LocationSettingsRequest.Builder builder =
                new LocationSettingsRequest.Builder();

        builder.addLocationRequest(locationRequest);


        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);


        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        green = BitmapFactory.decodeResource(getResources(), R.drawable.location_icon_green);
        gray = BitmapFactory.decodeResource(getResources(), R.drawable.location_icon_gray);
        yellow = BitmapFactory.decodeResource(getResources(), R.drawable.location_icon_yellow);
        red = BitmapFactory.decodeResource(getResources(), R.drawable.location_icon_red);
    }

    @Override
    public void onMapReady(final GoogleMap googleMap) {
        mMap = googleMap;

        setDefaultLocation();

        int hasFineLocationPermission = ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION);
        int hasCoarseLocationPermission = ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_COARSE_LOCATION);



        if (hasFineLocationPermission == PackageManager.PERMISSION_GRANTED &&
                hasCoarseLocationPermission == PackageManager.PERMISSION_GRANTED   ) {

            startLocationUpdates();

        } else {
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, REQUIRED_PERMISSIONS[0])) {
                Snackbar.make(mLayout, "이 앱을 실행하려면 위치 접근 권한이 필요합니다.",
                        Snackbar.LENGTH_INDEFINITE).setAction("확인", new View.OnClickListener() {

                    @Override
                    public void onClick(View view) {
                        ActivityCompat.requestPermissions( MainActivity.this, REQUIRED_PERMISSIONS,
                                PERMISSIONS_REQUEST_CODE);
                    }
                }).show();
            } else {
                ActivityCompat.requestPermissions( this, REQUIRED_PERMISSIONS,
                        PERMISSIONS_REQUEST_CODE);
            }

        }



        mMap.getUiSettings().setMyLocationButtonEnabled(true);
        mMap.getUiSettings().setZoomControlsEnabled(true);
        mMap.getUiSettings().setZoomGesturesEnabled(true);
        mMap.animateCamera(CameraUpdateFactory.zoomTo(15));
    }

    final LocationCallback locationCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            super.onLocationResult(locationResult);

            List locationList = locationResult.getLocations();

            if (locationList.size() > 0) {
                location = locationList.get(locationList.size() - 1);

                mCurrentLocatiion = location;
                getData(mCurrentLocatiion, 500);
                for(int i = 0; i < storeMarkers.size(); i++) storeMarkers.get(i).remove();
                storeMarkers.clear();
                if(storeRemain.size()*storeLat.size()*storeLng.size() != 0) {
                    for(int i = 0; i < storeLat.size()-1; i++) {

                        LatLng temp = new LatLng(storeLat.get(i), storeLng.get(i));
                        MarkerOptions markerOptions = new MarkerOptions();
                        markerOptions.position(temp);
                        markerOptions.title(storeName.get(i));
                        if (storeRemain.get(i).equals("empty")) {
                            markerOptions.icon(BitmapDescriptorFactory.fromBitmap(gray));
                            markerOptions.snippet("0개");
                        }
                        else if (storeRemain.get(i).equals("plenty")) {
                            markerOptions.icon(BitmapDescriptorFactory.fromBitmap(green));
                            markerOptions.snippet("100개 이상");
                        }
                        else if (storeRemain.get(i).equals("some")) {
                            markerOptions.icon(BitmapDescriptorFactory.fromBitmap(yellow));
                            markerOptions.snippet("30개 이상 100개 미만");
                        }
                        else if (storeRemain.get(i).equals("few")) {
                            markerOptions.icon(BitmapDescriptorFactory.fromBitmap(red));
                            markerOptions.snippet("1개 이상 30개 미만");
                        }
                        else {
                            markerOptions.icon(BitmapDescriptorFactory.fromBitmap(gray));
                            markerOptions.snippet("0개");
                        }

                        markerOptions.draggable(true);

                        storeMarkers.add(mMap.addMarker(markerOptions));
                    }
                }
            }
        }

    };



    private void startLocationUpdates() {

        if (!checkLocationServicesStatus()) {
            showDialogForLocationServiceSetting();
        }else {

            int hasFineLocationPermission = ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_FINE_LOCATION);
            int hasCoarseLocationPermission = ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_COARSE_LOCATION);



            if (hasFineLocationPermission != PackageManager.PERMISSION_GRANTED ||
                    hasCoarseLocationPermission != PackageManager.PERMISSION_GRANTED   ) {

                Log.d(TAG, "startLocationUpdates : 퍼미션 안가지고 있음");
                return;
            }


            Log.d(TAG, "startLocationUpdates : call mFusedLocationClient.requestLocationUpdates");

            mFusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());

            if (checkPermission())
                mMap.setMyLocationEnabled(true);

        }

    }


    @Override
    protected void onStart() {
        super.onStart();

        if (checkPermission()) {
            Log.d(TAG, "onStart : call mFusedLocationClient.requestLocationUpdates");
            mFusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null);

            if (mMap!=null)
                mMap.setMyLocationEnabled(true);
        }
    }


    @Override
    protected void onStop() {

        super.onStop();

        if (mFusedLocationClient != null) {

            Log.d(TAG, "onStop : call stopLocationUpdates");
            mFusedLocationClient.removeLocationUpdates(locationCallback);
        }
    }
    public boolean checkLocationServicesStatus() {
        LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

        return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
                || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    }



    public void JsonParse(String str) {
        JSONObject obj;
        try {
            obj = new JSONObject(str);
            JSONArray arr = obj.getJSONArray("stores");
            int count = obj.getInt("count");
            for(int i = 0; i < count; i++) {
                JSONObject dataObj = arr.getJSONObject(i);
                storeLat.add((float)dataObj.getDouble("lat"));
                storeLng.add((float)dataObj.getDouble("lng"));
                storeName.add(dataObj.getString("name"));
                storeRemain.add(dataObj.getString("remain_stat"));
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }

    }
    public void getData(final Location location, final int meter) {
        storeLat.clear();
        storeLng.clear();
        storeName.clear();
        Thread readData = new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    URL url = new URL(
                            "https://8oi9s0nnth.apigw.ntruss.com/corona19-masks/v1/storesByGeo/json?lat="
                                    + location.getLatitude()
                                    + "&lng="
                                    + location.getLongitude()
                                    + "&m="
                                    + meter);

                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setDoInput(true);
                    InputStream is = connection.getInputStream();
                    StringBuilder sb = new StringBuilder();
                    String result;
                    BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
                    while((result = br.readLine())!=null){
                        sb.append(result+"\n");
                    }
                    result = sb.toString();
                    JsonParse(result);


                } catch (Exception e) {}
            }
        });
        readData.start();
        try{
            readData.join();
        } catch (Exception e){}
    }


    public void setDefaultLocation() {
        LatLng DEFAULT_LOCATION = new LatLng(37.56, 126.97);
        CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(DEFAULT_LOCATION, 15);
        mMap.moveCamera(cameraUpdate);
    }
    private boolean checkPermission() {

        int hasFineLocationPermission = ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION);
        int hasCoarseLocationPermission = ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_COARSE_LOCATION);



        if (hasFineLocationPermission == PackageManager.PERMISSION_GRANTED &&
                hasCoarseLocationPermission == PackageManager.PERMISSION_GRANTED   ) {
            return true;
        }

        return false;

    }



    /*
     * ActivityCompat.requestPermissions를 사용한 퍼미션 요청의 결과를 리턴받는 메소드입니다.
     */
    @Override
    public void onRequestPermissionsResult(int permsRequestCode,
                                           @NonNull String[] permissions,
                                           @NonNull int[] grandResults) {

        if ( permsRequestCode == PERMISSIONS_REQUEST_CODE && grandResults.length == REQUIRED_PERMISSIONS.length) {

            // 요청 코드가 PERMISSIONS_REQUEST_CODE 이고, 요청한 퍼미션 개수만큼 수신되었다면

            boolean check_result = true;


            // 모든 퍼미션을 허용했는지 체크합니다.

            for (int result : grandResults) {
                if (result != PackageManager.PERMISSION_GRANTED) {
                    check_result = false;
                    break;
                }
            }


            if ( check_result ) {

                // 퍼미션을 허용했다면 위치 업데이트를 시작합니다.
                startLocationUpdates();
            }
            else {
                // 거부한 퍼미션이 있다면 앱을 사용할 수 없는 이유를 설명해주고 앱을 종료합니다.2 가지 경우가 있습니다.

                if (ActivityCompat.shouldShowRequestPermissionRationale(this, REQUIRED_PERMISSIONS[0])
                        || ActivityCompat.shouldShowRequestPermissionRationale(this, REQUIRED_PERMISSIONS[1])) {


                    // 사용자가 거부만 선택한 경우에는 앱을 다시 실행하여 허용을 선택하면 앱을 사용할 수 있습니다.
                    Snackbar.make(mLayout, "퍼미션이 거부되었습니다. 앱을 다시 실행하여 퍼미션을 허용해주세요. ",
                            Snackbar.LENGTH_INDEFINITE).setAction("확인", new View.OnClickListener() {

                        @Override
                        public void onClick(View view) {

                            finish();
                        }
                    }).show();

                }else {


                    // "다시 묻지 않음"을 사용자가 체크하고 거부를 선택한 경우에는 설정(앱 정보)에서 퍼미션을 허용해야 앱을 사용할 수 있습니다.
                    Snackbar.make(mLayout, "퍼미션이 거부되었습니다. 설정(앱 정보)에서 퍼미션을 허용해야 합니다. ",
                            Snackbar.LENGTH_INDEFINITE).setAction("확인", new View.OnClickListener() {

                        @Override
                        public void onClick(View view) {

                            finish();
                        }
                    }).show();
                }
            }

        }
    }


    //여기부터는 GPS 활성화를 위한 메소드들
    private void showDialogForLocationServiceSetting() {

        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setTitle("위치 서비스 비활성화");
        builder.setMessage("앱을 사용하기 위해서는 위치 서비스가 필요합니다.\n"
                + "위치 설정을 수정하실래요?");
        builder.setCancelable(true);
        builder.setPositiveButton("설정", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int id) {
                Intent callGPSSettingIntent
                        = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                startActivityForResult(callGPSSettingIntent, GPS_ENABLE_REQUEST_CODE);
            }
        });
        builder.setNegativeButton("취소", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
            }
        });
        builder.create().show();
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        switch (requestCode) {

            case GPS_ENABLE_REQUEST_CODE:

                //사용자가 GPS 활성 시켰는지 검사
                if (checkLocationServicesStatus()) {
                    if (checkLocationServicesStatus()) {

                        Log.d(TAG, "onActivityResult : GPS 활성화 되있음");


                        needRequest = true;

                        return;
                    }
                }

                break;
        }
    }

}

activity_main.xml

더보기
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/layout_main"
    xmlns:tools="http://schemas.android.com/tools">

    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/map"
        tools:context=".MapsActivity"
        android:name="com.google.android.gms.maps.SupportMapFragment"/>




</androidx.appcompat.widget.LinearLayoutCompat>

AndroidManifext.xml

더보기
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.maskfinder">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="??????????????????????????"/>
        <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>

(Androidmanifest의 value값은 구글 맵 api이기 때문에 직접 얻길 바란다.)

 

 

코드 설명

onCreate

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.activity_main);

        mLayout = findViewById(R.id.layout_main);

        locationRequest = new LocationRequest()
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                .setInterval(UPDATE_INTERVAL_MS)
                .setFastestInterval(FASTEST_UPDATE_INTERVAL_MS);


        LocationSettingsRequest.Builder builder =
                new LocationSettingsRequest.Builder();

        builder.addLocationRequest(locationRequest);


        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);


        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        green = BitmapFactory.decodeResource(getResources(), R.drawable.location_icon_green);
        gray = BitmapFactory.decodeResource(getResources(), R.drawable.location_icon_gray);
        yellow = BitmapFactory.decodeResource(getResources(), R.drawable.location_icon_yellow);
        red = BitmapFactory.decodeResource(getResources(), R.drawable.location_icon_red);
    }

앱이 실행되고 화면이 켜질 때 자동으로 실행되는 매소드이다.

화면이 안 꺼지도록 했으며,

GPS관련 초기 설정은 여기서 한다.

GPS는 고정밀, 늦으며 30초 빠르면 10초마다 값을 바꾸도록 했다.

activity_main.xml에서 만든 Fragment에 구글 맵을 띄우도록했으며,

미리 저장된(drawble에 저장해놨다.) 이미지 파일 초기화도 한다.

onMapReady

    @Override
    public void onMapReady(final GoogleMap googleMap) {
        mMap = googleMap;

        setDefaultLocation();

        int hasFineLocationPermission = ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION);
        int hasCoarseLocationPermission = ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_COARSE_LOCATION);



        if (hasFineLocationPermission == PackageManager.PERMISSION_GRANTED &&
                hasCoarseLocationPermission == PackageManager.PERMISSION_GRANTED   ) {

            startLocationUpdates();

        } else {
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, REQUIRED_PERMISSIONS[0])) {
                Snackbar.make(mLayout, "이 앱을 실행하려면 위치 접근 권한이 필요합니다.",
                        Snackbar.LENGTH_INDEFINITE).setAction("확인", new View.OnClickListener() {

                    @Override
                    public void onClick(View view) {
                        ActivityCompat.requestPermissions( MainActivity.this, REQUIRED_PERMISSIONS,
                                PERMISSIONS_REQUEST_CODE);
                    }
                }).show();
            } else {
                ActivityCompat.requestPermissions( this, REQUIRED_PERMISSIONS,
                        PERMISSIONS_REQUEST_CODE);
            }

        }



        mMap.getUiSettings().setMyLocationButtonEnabled(true);
        mMap.getUiSettings().setZoomControlsEnabled(true);
        mMap.getUiSettings().setZoomGesturesEnabled(true);
        mMap.animateCamera(CameraUpdateFactory.zoomTo(15));
    }

구글 맵을 사용할 수 있는 환경이 만들지면,

자동으로 호출되는 매소드이다.

 

setDefaultLocation으로 GPS값을 받아오기 전(또는 GPS오류) 지도의 중심 위치를 설정한다.

권한 설정 여부도 확인하며, 권한이 있는 경우 GPS를 가동한다.

권한이없을 경우 권한을 요청하는 Snackbar도 띄우게했다.

 

GPS관련


    final LocationCallback locationCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            super.onLocationResult(locationResult);

            List<Location> locationList = locationResult.getLocations();

            if (locationList.size() > 0) {
                location = locationList.get(locationList.size() - 1);

                mCurrentLocatiion = location;
                getData(mCurrentLocatiion, 500);
                for(int i = 0; i < storeMarkers.size(); i++) storeMarkers.get(i).remove();
                storeMarkers.clear();
                if(storeRemain.size()*storeLat.size()*storeLng.size() != 0) {
                    for(int i = 0; i < storeLat.size()-1; i++) {

                        LatLng temp = new LatLng(storeLat.get(i), storeLng.get(i));
                        MarkerOptions markerOptions = new MarkerOptions();
                        markerOptions.position(temp);
                        markerOptions.title(storeName.get(i));
                        if (storeRemain.get(i).equals("empty")) {
                            markerOptions.icon(BitmapDescriptorFactory.fromBitmap(gray));
                            markerOptions.snippet("0개");
                        }
                        else if (storeRemain.get(i).equals("plenty")) {
                            markerOptions.icon(BitmapDescriptorFactory.fromBitmap(green));
                            markerOptions.snippet("100개 이상");
                        }
                        else if (storeRemain.get(i).equals("some")) {
                            markerOptions.icon(BitmapDescriptorFactory.fromBitmap(yellow));
                            markerOptions.snippet("30개 이상 100개 미만");
                        }
                        else if (storeRemain.get(i).equals("few")) {
                            markerOptions.icon(BitmapDescriptorFactory.fromBitmap(red));
                            markerOptions.snippet("1개 이상 30개 미만");
                        }
                        else {
                            markerOptions.icon(BitmapDescriptorFactory.fromBitmap(gray));
                            markerOptions.snippet("0개");
                        }

                        markerOptions.draggable(true);

                        storeMarkers.add(mMap.addMarker(markerOptions));
                    }
                }
            }
        }

    };

GPS에관한 객체를 선언하고,
설정된 조건이 되었을 때 자동으로 호출되는 객체

지도이 있는 모든 마커들을 제거한 후,
getData매소드로 API데이터를 읽는다.

읽은 정보를 기반으로 마커의 색, 위치등을 지정하고
읽은 정보를 storeMarkers에 저장한다.

마지막으로 storeMarkers를 다시 지도 위에 띄운다.

 

 

startLocationUpdates

    private void startLocationUpdates() {

        if (!checkLocationServicesStatus()) {
            showDialogForLocationServiceSetting();
        }else {

            int hasFineLocationPermission = ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_FINE_LOCATION);
            int hasCoarseLocationPermission = ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_COARSE_LOCATION);



            if (hasFineLocationPermission != PackageManager.PERMISSION_GRANTED ||
                    hasCoarseLocationPermission != PackageManager.PERMISSION_GRANTED   ) {

                Log.d(TAG, "startLocationUpdates : 퍼미션 안가지고 있음");
                return;
            }


            Log.d(TAG, "startLocationUpdates : call mFusedLocationClient.requestLocationUpdates");

            mFusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());

            if (checkPermission())
                mMap.setMyLocationEnabled(true);

        }

    }

GPS 서비스 상태를 파악한 후
비활성화 상태이면
서비스 세팅 매소드(showDialogForLocationServiceSetting)을 호출한다.

그런 다음
GPS권한을 확인하고 GPS값을 업데이트한다.

 

onStart(), onStop()

이 둘은 화면이 다시 시작되고(핸드폰을 잠근상태에서 되돌아올 때), 앱을 중단했을 때(핸드폰을 잠궜을 때)와 관련된

매소드이다.

 

 

checkLocationServicesStatus

public boolean checkLocationServicesStatus() {
        LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

        return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
                || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    }

GPS 서비스 상태를 반환하는 매소드이다.

 

 

JsonParse

public void JsonParse(String str) {
        JSONObject obj;
        try {
            obj = new JSONObject(str);
            JSONArray arr = obj.getJSONArray("stores");
            int count = obj.getInt("count");
            for(int i = 0; i < count; i++) {
                JSONObject dataObj = arr.getJSONObject(i);
                storeLat.add((float)dataObj.getDouble("lat"));
                storeLng.add((float)dataObj.getDouble("lng"));
                storeName.add(dataObj.getString("name"));
                storeRemain.add(dataObj.getString("remain_stat"));
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }

    }

JSON 라이브러리를 이용하여 만들었으며
json형식인 str에서
마커 표시에 필요한 경도, 위도, 이름(약국), 재고를 등의 데이터를 파싱하여 저장한다.

 

 

 

 

 

getData

    public void getData(final Location location, final int meter) {
        storeLat.clear();
        storeLng.clear();
        storeName.clear();
        Thread readData = new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    URL url = new URL(
                            "https://8oi9s0nnth.apigw.ntruss.com/corona19-masks/v1/storesByGeo/json?lat="
                                    + location.getLatitude()
                                    + "&lng="
                                    + location.getLongitude()
                                    + "&m="
                                    + meter);

                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setDoInput(true);
                    InputStream is = connection.getInputStream();
                    StringBuilder sb = new StringBuilder();
                    String result;
                    BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
                    while((result = br.readLine())!=null){
                        sb.append(result+"\n");
                    }
                    result = sb.toString();
                    JsonParse(result);


                } catch (Exception e) {}
            }
        });
        readData.start();
        try{
            readData.join();
        } catch (Exception e){}
    }

마스크 정보를 일단 초기화하고
API를 통해 다시 저장한다.

인터넷을 사용하는 것이기 때문에
Thread를 사용했다.

경도, 위도, 반경을 보내면
주변 마스크 판매 정보가 담긴 json(result 변수)값을
JsonParse에 넘긴다.


Thread를 사용했기 때문에
마커를 업데이트하는 순간에 오류가 발생하는 경우가 있어
.join을통해 Thread작업이 끝날 때까지 기다리도록했다.

 

setDefaultLocation

    public void setDefaultLocation() {
        LatLng DEFAULT_LOCATION = new LatLng(37.56, 126.97);
        CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(DEFAULT_LOCATION, 15);
        mMap.moveCamera(cameraUpdate);
    }

GPS가 활성화 되기 전(또는 오류 상황)에서
지도의 중심 좌표를 설정하는 매소드이다.

 

checkPermission

    private boolean checkPermission() {

        int hasFineLocationPermission = ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION);
        int hasCoarseLocationPermission = ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_COARSE_LOCATION);



        if (hasFineLocationPermission == PackageManager.PERMISSION_GRANTED &&
                hasCoarseLocationPermission == PackageManager.PERMISSION_GRANTED   ) {
            return true;
        }

        return false;

    }

권한을 설정여부를 판단하는 매소드
GPS와 network위치기반 서비스 모두판단

 

 

완성된 화면

 

 

반응형

'Android Studio' 카테고리의 다른 글

나의 식물일지 어플리케이션  (1) 2020.09.09
마스크 판매 위치찾기 앱 ver 1.4.2  (2) 2020.03.20
스마트 시티  (0) 2020.02.06