반응형



현재 위치 주변의 장소정보를 가져오는 Places SDK for Android 3.0.0 베타의 예제를 실행시켜 보았습니다.

아직 베타버전이라 그런지 일부 정보만 보여주기 때문에 실제로 사용하기에는 무리가 있을 듯합니다.

정식 버전이 얼른 나오기를 기대해봅니다. 


사용한 원본 코드는 아래 링크에 있습니다.

https://github.com/googlemaps/android-places-demos/tree/maps-v3-beta



최초작성 2019. 8. 23

 

1. 실행 결과


1-1. FIND CURRENT PLACE를 터치하면 





1-2. 런타임 퍼미션을 물어봅니다.  허용을 터치하고 





1-3. FIND CURRENT PLACE를 다시 터치하면 현재 위치 주변 정보를 20개 가져와 보여줍니다. 

주변 장소의 이름과 주소를 보여줍니다.

 




1-4. Display raw results를 체크하고 다시 FIND CURRENT PLACE를 터치하면 

RAW 데이터로 주변 장소 정보를 보여줍니다.

아직 베타라서 그런지 주변 정보 항목중 대부분이 null 입니다. 




이제 API키를 생성하는 방법을 설명합니다.


2. API 키 생성하기 

2-1. Google Developers Console 사이트 ( https://console.developers.google.com/apis/dashboard  )에 접속하여 만들기를 클릭합니다.


자주 구성이 바뀌는 편이어서 포스팅에서 진행한 캡쳐 화면과 차이가 있을 수 있습니다.





2-2. 프로젝트 이름을 적어주고 만들기를 클릭합니다.





2-3. API 및 서비스 사용 설정을 클릭합니다.





2-4. 검색창에 places를 입력하면 보이는 Places API를 클릭합니다. 





2-5. 사용 설정을 클릭합니다. 





2-6. 사용자 인증 정보를 클릭합니다. 





2-7. API 및 서비스의 사용자 인증 정보를 클릭합니다. 





2-8. 사용자 인증 정보 만들기를 클릭하고 API 키를 클릭합니다. 





2-9. 키 제한을 클릭합니다.





2-10. Android 앱을 체크하고, 항목 추가를 클릭합니다.





2-11. 다음 두 항목을 붙여넣고 완료를 클릭합니다. 


안드로이드 프로젝트를 생성하여 패키지 이름을 복사하여 붙여넣기합니다.

명령 프롬프트에서 다음 명령을 실행하여 SHA1 값을 복사하여 붙여넣기합니다.


keytool -list -v -keystore "%USERPROFILE%\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android





2-12. 키 제한을 클릭하고 콤보박스에서 Places API를 선택 후, 저장을 클릭합니다. 





2-13. API 키를 복사해둡니다.





이제 할당받은 API키를 사용하여 안드로이드 코드를 실행시켜 봅니다.



3. 안드로이드 코드 

3-1.  다음 2개의 라이브러리를 다운로드 합니다. 


https://dl.google.com/dl/geosdk/maps-sdk-3.0.0-beta.aar 


https://dl.google.com/dl/geosdk/places-maps-sdk-3.0.0-beta.aar 




3-2. 프로젝트 창을 Project뷰로 바꾼후,  app/libs에 두 개의 라이브러리 파일을 복사해줍니다.





3-3. build.gradle(Project: ) 에 다음을 추가하고 Sync Now를 클릭합니다. 



allprojects {
    repositories {
        google()
        jcenter()
        flatDir {
            dirs 'libs'
        }
    }
}




3-4. build.gradle(app: )에서 targetSdkVersion와 compileSdkVersion는 28이상,  minSdkVersion는 16이상 이어야 합니다. 


apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.tistory.webnautes.maptest"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"




3-5.  build.gradle(app: )의 dependencies에 다음 항목을 추가하고 Sync Now를 클릭합니다.




dependencies에 다음을 추가하고 Sync Now를 클릭합니다. 


dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation name:'maps-sdk-3.0.0-beta', ext:'aar'
    implementation name:'places-maps-sdk-3.0.0-beta', ext:'aar'
    implementation 'com.google.android.gms:play-services-basement:17.1.0'
    implementation 'com.google.android.gms:play-services-base:17.1.0'
    implementation 'com.google.android.gms:play-services-gcm:17.0.0'
    implementation 'com.google.android.gms:play-services-location:17.0.0'
    implementation 'com.google.android.gms:play-services-clearcut:17.0.0'

    implementation 'com.github.bumptech.glide:glide:4.3.1'
    implementation 'com.android.volley:volley:1.1.1'
    implementation 'com.google.code.gson:gson:2.8.5'
}



3-6. AndroidManifest.xml에 다음 권한들을 추가합니다.



<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tistory.webnautes.maptest">

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

    <application




3-7. activity_main.xml 파일의 내용을 다음으로 대체합니다.


<?xml version="1.0" encoding="utf-8"?>
<ScrollView
    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"
    android:layout_margin="4dp"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <CheckBox
                android:id="@+id/use_custom_fields"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Manually set Place Fields?"/>

            <TextView
                android:id="@+id/custom_fields_list"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1" />

        </LinearLayout>

        <Button
            android:id="@+id/find_current_place_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Find Current Place"/>

        <CheckBox
            android:id="@+id/display_raw_results"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="false"
            android:text="Display raw results?"/>

        <ProgressBar
            android:id="@+id/loading"
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:visibility="invisible"/>

        <TextView
            android:id="@+id/response"
            android:textIsSelectable="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    </LinearLayout>
</ScrollView>






3-8. FieldSelector.java 파일을 추가하고 다음 내용을 복사하여 붙여넣기 합니다. 


/*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import com.google.android.libraries.places.api.model.Place;

import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CheckedTextView;
import android.widget.CompoundButton;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;

/**
* Helper class for selecting {@link Place.Field} values.
*/
final class FieldSelector {

    private final List<PlaceField> placeFields;
    private final TextView outputView;

    public FieldSelector(CheckBox enableView, TextView outputView) {
        this(enableView, outputView, Arrays.asList(Place.Field.values()));
    }

    public FieldSelector(CheckBox enableView, final TextView outputView, List<Place.Field> validFields) {
        placeFields = new ArrayList<>();
        for (Place.Field field : validFields) {
            placeFields.add(new PlaceField(field));
        }

        outputView.setOnClickListener(
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (v.isEnabled()) {
                            FieldSelector.this.showDialog(v.getContext());
                        }
                    }
                });

        enableView.setOnCheckedChangeListener(
                new CompoundButton.OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                        outputView.setEnabled(isChecked);
                        if (isChecked) {
                            FieldSelector.this.showDialog(buttonView.getContext());
                        } else {
                            outputView.setText("");
                            for (PlaceField placeField : placeFields) {
                                placeField.checked = false;
                            }
                        }
                    }
                });

        this.outputView = outputView;
    }

    /**
    * Returns all {@link Place.Field} values except those passed in.
    *
    * <p>Convenience method for when most {@link Place.Field} values are desired. Useful for APIs
    * that do no support all {@link Place.Field} values.
    */
    static List<Place.Field> getPlaceFields(Place.Field... placeFieldsToOmit) {
        // Arrays.asList is immutable, create a mutable list to allow removing fields
        List<Place.Field> placeFields = new ArrayList<>(Arrays.asList(Place.Field.values()));
        placeFields.removeAll(Arrays.asList(placeFieldsToOmit));

        return placeFields;
    }

    /**
    * Shows dialog to allow user to select {@link Place.Field} values they want.
    */
    public void showDialog(Context context) {
        ListView listView = new ListView(context);
        PlaceFieldArrayAdapter adapter = new PlaceFieldArrayAdapter(context, placeFields);
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(adapter);

        new AlertDialog.Builder(context)
                .setTitle("Select Place Fields")
                .setPositiveButton(
                        "Done",
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                outputView.setText(FieldSelector.this.getSelectedString());
                            }
                        })
                .setView(listView)
                .show();
    }

    /**
    * Returns all {@link Place.Field} that are selectable.
    */
    public List<Place.Field> getAllFields() {
        List<Place.Field> list = new ArrayList<>();
        for (PlaceField placeField : placeFields) {
            list.add(placeField.field);
        }

        return list;
    }

    /**
    * Returns all {@link Place.Field} values the user selected.
    */
    public List<Place.Field> getSelectedFields() {
        List<Place.Field> selectedList = new ArrayList<>();
        for (PlaceField placeField : placeFields) {
            if (placeField.checked) {
                selectedList.add(placeField.field);
            }
        }

        return selectedList;
    }

    /**
    * Returns a String representation of all selected {@link Place.Field} values. See {@link
    * #getSelectedFields()}.
    */
    public String getSelectedString() {
        StringBuilder builder = new StringBuilder();
        for (Place.Field field : getSelectedFields()) {
            builder.append(field).append("\n");
        }

        return builder.toString();
    }

    //////////////////////////
    // Helper methods below //
    //////////////////////////

    private static class PlaceField {
        final Place.Field field;
        boolean checked;

        public PlaceField(Place.Field field) {
            this.field = field;
        }
    }

    private static class PlaceFieldArrayAdapter extends ArrayAdapter<PlaceField>
            implements OnItemClickListener {

        public PlaceFieldArrayAdapter(Context context, List<PlaceField> placeFields) {
            super(context, android.R.layout.simple_list_item_multiple_choice, placeFields);
        }

        @NonNull
        @Override
        public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
            View view = super.getView(position, convertView, parent);
            PlaceField placeField = getItem(position);
            updateView(view, placeField);

            return view;
        }

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            PlaceField placeField = getItem(position);
            placeField.checked = !placeField.checked;
            updateView(view, placeField);
        }

        private void updateView(View view, PlaceField placeField) {
            if (view instanceof CheckedTextView) {
                CheckedTextView checkedTextView = (CheckedTextView) view;
                checkedTextView.setText(placeField.field.toString());
                checkedTextView.setChecked(placeField.checked);
            }
        }
    }
}





3-9. MainActivity.java를  다음 코드로 대체하세요.


/*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.android.libraries.places.api.Places;
import com.google.android.libraries.places.api.model.Place;
import com.google.android.libraries.places.api.model.Place.Field;
import com.google.android.libraries.places.api.model.PlaceLikelihood;
import com.google.android.libraries.places.api.net.FindCurrentPlaceRequest;
import com.google.android.libraries.places.api.net.FindCurrentPlaceResponse;
import com.google.android.libraries.places.api.net.PlacesClient;


import android.Manifest.permission;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;
import java.util.Locale;

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

import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.ACCESS_WIFI_STATE;



public class MainActivity extends AppCompatActivity {

    private PlacesClient placesClient;
    private TextView responseView;
    private FieldSelector fieldSelector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (!Places.isInitialized()) {
            Places.initialize(getApplicationContext(), "API키", Locale.KOREA);
        }


        // Retrieve a PlacesClient (previously initialized - see MainActivity)
        placesClient = Places.createClient(this);

        // Set view objects
        List<Place.Field> placeFields =
                FieldSelector.getPlaceFields(
                        Field.ADDRESS_COMPONENTS, Field.PHONE_NUMBER, Field.WEBSITE_URI, Field.OPENING_HOURS,
                        Field.UTC_OFFSET, Field.TYPES);

        CheckBox checkBox = findViewById(R.id.use_custom_fields);
        TextView textView = findViewById(R.id.custom_fields_list);
        fieldSelector =
                new FieldSelector(
                        checkBox,
                        textView,
                        placeFields);
        responseView = findViewById(R.id.response);
        setLoading(false);

        // Set listeners for programmatic Find Current Place
        findViewById(R.id.find_current_place_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                MainActivity.this.findCurrentPlace();
            }
        });
    }



    /**
    * Fetches a list of {@link PlaceLikelihood} instances that represent the Places the user is
    * most
    * likely to be at currently.
    */
    private void findCurrentPlace() {
        if (ContextCompat.checkSelfPermission(this, permission.ACCESS_WIFI_STATE)
                != PackageManager.PERMISSION_GRANTED
                || ContextCompat.checkSelfPermission(this, permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(
                    this,
                    "Both ACCESS_WIFI_STATE & ACCESS_FINE_LOCATION permissions are required",
                    Toast.LENGTH_SHORT)
                    .show();
        }

        // Note that it is not possible to request a normal (non-dangerous) permission from
        // ActivityCompat.requestPermissions(), which is why the checkPermission() only checks if
        // ACCESS_FINE_LOCATION is granted. It is still possible to check whether a normal permission
        // is granted or not using ContextCompat.checkSelfPermission().
        if (checkPermission(ACCESS_FINE_LOCATION)) {
            findCurrentPlaceWithPermissions();
        }
    }

    /**
    * Fetches a list of {@link PlaceLikelihood} instances that represent the Places the user is
    * most
    * likely to be at currently.
    */
    @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE})
    private void findCurrentPlaceWithPermissions() {
        setLoading(true);

        FindCurrentPlaceRequest currentPlaceRequest =
                FindCurrentPlaceRequest.newInstance(getPlaceFields());
        Task<FindCurrentPlaceResponse> currentPlaceTask =
                placesClient.findCurrentPlace(currentPlaceRequest);

        currentPlaceTask.addOnSuccessListener(
                new OnSuccessListener<FindCurrentPlaceResponse>() {
                    @Override
                    public void onSuccess(FindCurrentPlaceResponse response) {
                        responseView.setText(stringify(response, MainActivity.this.isDisplayRawResultsChecked()));
                    }
                });

        currentPlaceTask.addOnFailureListener(
                new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception exception) {
                        exception.printStackTrace();
                        responseView.setText(exception.getMessage());
                    }
                });

        currentPlaceTask.addOnCompleteListener(new OnCompleteListener<FindCurrentPlaceResponse>() {
            @Override
            public void onComplete(@NonNull Task<FindCurrentPlaceResponse> task) {
                MainActivity.this.setLoading(false);
            }
        });
    }

    //////////////////////////
    // Helper methods below //
    //////////////////////////

    private List<Place.Field> getPlaceFields() {
        if (((CheckBox) findViewById(R.id.use_custom_fields)).isChecked()) {
            return fieldSelector.getSelectedFields();
        } else {
            return fieldSelector.getAllFields();
        }
    }

    private boolean checkPermission(String permission) {
        boolean hasPermission =
                ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED;
        if (!hasPermission) {
            ActivityCompat.requestPermissions(this, new String[]{permission}, 0);
        }
        return hasPermission;
    }

    private boolean isDisplayRawResultsChecked() {
        return ((CheckBox) findViewById(R.id.display_raw_results)).isChecked();
    }

    private void setLoading(boolean loading) {
        findViewById(R.id.loading).setVisibility(loading ? View.VISIBLE : View.INVISIBLE);
    }


    private static final String FIELD_SEPARATOR = "\n\t";
    private static final String RESULT_SEPARATOR = "\n---\n\t";
    static String stringify(FindCurrentPlaceResponse response, boolean raw) {
        StringBuilder builder = new StringBuilder();

        builder.append(response.getPlaceLikelihoods().size()).append(" Current Place Results:");

        if (raw) {
            builder.append(RESULT_SEPARATOR);
            builder.append(TextUtils.join(RESULT_SEPARATOR, response.getPlaceLikelihoods()));
        } else {
            for (PlaceLikelihood placeLikelihood : response.getPlaceLikelihoods()) {;

                Place place = placeLikelihood.getPlace();

                builder.append(RESULT_SEPARATOR)
                        .append("Likelihood: ")
                        .append(placeLikelihood.getLikelihood())
                        .append(FIELD_SEPARATOR)
                        .append("Place: "+ place.getName() + " (" + place.getAddress() + ")");
            }
        }
        return builder.toString();
    }

}




3-10. MainActivity.java에서 다음 부분의 API키를 앞에서 생성한 API키로 대체합니다.


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (!Places.isInitialized()) {
            Places.initialize(getApplicationContext(), "API키", Locale.KOREA);
        }





참고

https://developers.google.com/maps/documentation/android-sdk/v3-client-migration 


반응형

포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
댓글로 알려주시면 빠른 시일내에 답변을 드리겠습니다.

여러분의 응원으로 좋은 컨텐츠가 만들어집니다.
지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 : )

유튜브 구독하기


제가 쓴 책도 한번 검토해보세요.

+ Recent posts