반응형

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


사용한 원본 코드는 아래 링크에 있습니다. v.3.x BETA 버전과 v.2.x 버전 예제가 구분되어 존재합니다.  

https://github.com/googlemaps/android-places-demos 



2019. 08. 23 최초작성 - Places SDK for Android v.3.0.0 BETA 예제

2021. 01. 09  BETA 버전대신에 v.2.4로 변경

 

1. 실행 결과

1-1. CURRENT PLACE를 클릭합니다. 





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





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





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

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

 




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

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





1-5. Manually set Place Fields를 체크하고 FIND CURRENT PLACE를 터치합니다. 





1-6. 선택한 PLACE 필드만 선택하여 볼 수 있습니다. ADDRESS만 체크하고 DONE을 터치해봅니다. 





1-7. 주소 필드만 보이게 됩니다. 




이제 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. 탐색 메뉴(1)을 누르고 메뉴에서 API 및 서비스 > 사용자 인증 정보를 선택합니다. 





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





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





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





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


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

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


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





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





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





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



3. 안드로이드 코드 


3-1.  Places SDK for Android를 위한 dependency를 app을 위한 build.gradle에 추가하고 Sync Now를 클릭합니다.



dependencies {

    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'com.google.android.libraries.places:places:2.4.0'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}




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



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

    <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



다음 액티비티를 추가합니다.


       </activity>

        <activity android:name=".CurrentPlaceTestActivity" />
       
    </application>




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


<?xml version="1.0" encoding="utf-8"?>

<LinearLayout
    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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/current_place_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="2dp"
        android:text="Current Place"/>

</LinearLayout>




3-4. current_place_test_activity.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=".CurrentPlaceTestActivity">

    <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-5. FieldSelector.java 파일을 추가하고 다음 내용을 복사하여 붙여넣기 합니다. 


package com.tistory.webnautes.placeexample;

import android.content.Context;
import android.os.Bundle;
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.ListView;
import android.widget.TextView;

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

/** Helper class for selecting {@link Field} values. */
public final class FieldSelector {
    private static final String SELECTED_PLACE_FIELDS_KEY = "selected_place_fields";

    private final Map<Field, State> fieldStates;

    private final TextView outputView;

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

        return placeFields;
    }

    public FieldSelector(CheckBox enableView, TextView outputView, @Nullable Bundle savedState) {
        this(enableView, outputView, Arrays.asList(Field.values()), savedState);
    }

    public FieldSelector(
            CheckBox enableView,
            TextView outputView,
            List<Field> validFields,
            @Nullable Bundle savedState) {
        fieldStates = new HashMap<>();
        for (Field field : validFields) {
            fieldStates.put(field, new State(field));
        }

        if (savedState != null) {
            List<Integer> selectedFields = savedState.getIntegerArrayList(SELECTED_PLACE_FIELDS_KEY);
            if (selectedFields != null) {
                restoreState(selectedFields);
            }
            outputView.setText(getSelectedString());
        }

        outputView.setOnClickListener(
                v -> {
                    if (v.isEnabled()) {
                        showDialog(v.getContext());
                    }
                });

        enableView.setOnClickListener(
                view -> {
                    boolean isChecked = enableView.isChecked();
                    outputView.setEnabled(isChecked);
                    if (isChecked) {
                        showDialog(view.getContext());
                    } else {
                        outputView.setText("");
                        for (State state : fieldStates.values()) {
                            state.checked = false;
                        }
                    }
                });

        this.outputView = outputView;
    }

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

        new AlertDialog.Builder(context)
                .setTitle("Select Place Fields")
                .setPositiveButton(
                        "Done",
                        (dialog, which) -> {
                            outputView.setText(getSelectedString());
                        })
                .setView(listView)
                .show();
    }

    /**
    * Returns all {@link Field} that are selectable.
    */
    public List<Field> getAllFields() {
        return new ArrayList<>(fieldStates.keySet());
    }

    /**
    * Returns all {@link Field} values the user selected.
    */
    public List<Field> getSelectedFields() {
        List<Field> selectedList = new ArrayList<>();
        for (Map.Entry<Field, State> entry : fieldStates.entrySet()) {
            if (entry.getValue().checked) {
                selectedList.add(entry.getKey());
            }
        }

        return selectedList;
    }

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

        return builder.toString();
    }


    public void onSaveInstanceState(@NonNull Bundle bundle) {
        List<Field> fields = getSelectedFields();

        ArrayList<Integer> serializedFields = new ArrayList<>();
        for (Field field : fields) {
            serializedFields.add(field.ordinal());
        }
        bundle.putIntegerArrayList(SELECTED_PLACE_FIELDS_KEY, serializedFields);
    }

    private void restoreState(List<Integer> selectedFields) {
        for (Integer serializedField : selectedFields) {
            Field field = Field.values()[serializedField];
            State state = fieldStates.get(field);
            if (state != null) {
                state.checked = true;
            }
        }
    }

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

    /**
    * Holds selection state for a place field.
    */
    public static final class State {
        public final Field field;
        public boolean checked;

        public State(Field field) {
            this.field = field;
        }
    }

    private static final class PlaceFieldArrayAdapter extends ArrayAdapter<State>
            implements OnItemClickListener {

        public PlaceFieldArrayAdapter(Context context, Collection<State> states) {
            super(context, android.R.layout.simple_list_item_multiple_choice, new ArrayList<>(states));
        }

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

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

            return view;
        }

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




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


package com.tistory.webnautes.placeexample;

import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.libraries.places.api.model.AutocompletePrediction;
import com.google.android.libraries.places.api.model.Place;
import com.google.android.libraries.places.api.model.PlaceLikelihood;
import com.google.android.libraries.places.api.net.FetchPlaceResponse;
import com.google.android.libraries.places.api.net.FindAutocompletePredictionsResponse;
import com.google.android.libraries.places.api.net.FindCurrentPlaceResponse;

import android.graphics.Bitmap;
import android.text.TextUtils;
import android.widget.TextView;

import androidx.annotation.Nullable;

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

/**
* Utility class for converting objects to viewable strings and back.
*/
public final class StringUtil {

    private static final String FIELD_SEPARATOR = "\n\t";
    private static final String RESULT_SEPARATOR = "\n---\n\t";

    static void prepend(TextView textView, String prefix) {
        textView.setText(prefix + "\n\n" + textView.getText());
    }

    @Nullable
    static LatLngBounds convertToLatLngBounds(
            @Nullable String southWest, @Nullable String northEast) {
        LatLng soundWestLatLng = convertToLatLng(southWest);
        LatLng northEastLatLng = convertToLatLng(northEast);
        if (soundWestLatLng == null || northEast == null) {
            return null;
        }

        return new LatLngBounds(soundWestLatLng, northEastLatLng);
    }

    @Nullable
    static LatLng convertToLatLng(@Nullable String value) {
        if (TextUtils.isEmpty(value)) {
            return null;
        }

        String[] split = value.split(",", -1);
        if (split.length != 2) {
            return null;
        }

        try {
            return new LatLng(Double.parseDouble(split[0]), Double.parseDouble(split[1]));
        } catch (NullPointerException | NumberFormatException e) {
            return null;
        }
    }

    static List<String> countriesStringToArrayList(String countriesString) {
        // Allow these delimiters: , ; | / \
        List<String> countries = Arrays.asList(countriesString
                .replaceAll("\\s", "|")
                .split("[,;|/\\\\]",-1));

        return countries;
    }

    static String stringify(FindAutocompletePredictionsResponse response, boolean raw) {
        StringBuilder builder = new StringBuilder();

        builder
                .append(response.getAutocompletePredictions().size())
                .append(" Autocomplete Predictions Results:");

        if (raw) {
            builder.append(RESULT_SEPARATOR);
            appendListToStringBuilder(builder, response.getAutocompletePredictions());
        } else {
            for (AutocompletePrediction autocompletePrediction : response.getAutocompletePredictions()) {
                builder
                        .append(RESULT_SEPARATOR)
                        .append(autocompletePrediction.getFullText(/* matchStyle */ null));
            }
        }

        return builder.toString();
    }

    static String stringify(FetchPlaceResponse response, boolean raw) {
        StringBuilder builder = new StringBuilder();

        builder.append("Fetch Place Result:").append(RESULT_SEPARATOR);
        if (raw) {
            builder.append(response.getPlace());
        } else {
            builder.append(stringify(response.getPlace()));
        }

        return builder.toString();
    }

    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);
            appendListToStringBuilder(builder, response.getPlaceLikelihoods());
        } else {
            for (PlaceLikelihood placeLikelihood : response.getPlaceLikelihoods()) {
                builder
                        .append(RESULT_SEPARATOR)
                        .append("Likelihood: ")
                        .append(placeLikelihood.getLikelihood())
                        .append(FIELD_SEPARATOR)
                        .append("Place: ")
                        .append(stringify(placeLikelihood.getPlace()));
            }
        }

        return builder.toString();
    }

    static String stringify(Place place) {
        return place.getName()
                + " ("
                + place.getAddress()
                + ")"
                + " is open now? "
                + place.isOpen(System.currentTimeMillis());
    }

    static String stringify(Bitmap bitmap) {
        StringBuilder builder = new StringBuilder();

        builder
                .append("Photo size (width x height)")
                .append(RESULT_SEPARATOR)
                .append(bitmap.getWidth())
                .append(", ")
                .append(bitmap.getHeight());

        return builder.toString();
    }

    public static String stringifyAutocompleteWidget(Place place, boolean raw) {
        StringBuilder builder = new StringBuilder();

        builder.append("Autocomplete Widget Result:").append(RESULT_SEPARATOR);

        if (raw) {
            builder.append(place);
        } else {
            builder.append(stringify(place));
        }

        return builder.toString();
    }

    private static <T> void appendListToStringBuilder(StringBuilder builder, List<T> items) {
        if (items.isEmpty()) {
            return;
        }

        builder.append(items.get(0));
        for (int i = 1; i < items.size(); i++) {
            builder.append(RESULT_SEPARATOR);
            builder.append(items.get(i));
        }
    }
}




3-7. MainActivity.java를  다음 코드로 대체합니다.


package com.tistory.webnautes.placeexample;

import com.google.android.libraries.places.api.Places;

import android.content.Intent;
import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;
import androidx.annotation.Nullable;

public class MainActivity extends AppCompatActivity {

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

        final String apiKey = "API키";

        // Setup Places Client
        if (!Places.isInitialized()) {
            Places.initialize(getApplicationContext(), apiKey);
        }

        setLaunchActivityClickListener(R.id.current_place_button, CurrentPlaceTestActivity.class);

    }

    private void setLaunchActivityClickListener(
            int onClickResId, Class<? extends AppCompatActivity> activityClassToLaunch) {
        findViewById(onClickResId)
                .setOnClickListener(
                        v -> {
                            Intent intent = new Intent(MainActivity.this, activityClassToLaunch);
                            startActivity(intent);
                        });
    }
}




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


package com.tistory.webnautes.placeexample;

import com.google.android.gms.tasks.Task;
import com.google.android.libraries.places.api.Places;
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.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
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 CurrentPlaceTestActivity extends AppCompatActivity {

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

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.current_place_test_activity);

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

        // Set view objects
        List<Field> placeFields =
                FieldSelector.allExcept(
                        Field.ADDRESS_COMPONENTS,
                        Field.OPENING_HOURS,
                        Field.PHONE_NUMBER,
                        Field.UTC_OFFSET,
                        Field.WEBSITE_URI);
        fieldSelector =
                new FieldSelector(
                        findViewById(R.id.use_custom_fields),
                        findViewById(R.id.custom_fields_list),
                        placeFields,
                        savedInstanceState);
        responseView = findViewById(R.id.response);
        setLoading(false);

        // Set listeners for programmatic Find Current Place
        findViewById(R.id.find_current_place_button).setOnClickListener((view) -> findCurrentPlace());
    }

    @Override
    protected void onSaveInstanceState(@NonNull Bundle bundle) {
        super.onSaveInstanceState(bundle);
        fieldSelector.onSaveInstanceState(bundle);
    }

    /**
    * 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(
                (response) ->
                        responseView.setText(StringUtil.stringify(response, isDisplayRawResultsChecked())));

        currentPlaceTask.addOnFailureListener(
                (exception) -> {
                    exception.printStackTrace();
                    responseView.setText(exception.getMessage());
                });

        currentPlaceTask.addOnCompleteListener(task -> setLoading(false));
    }

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

    private List<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);
    }
}




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


public class MainActivity extends AppCompatActivity {

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

        final String apiKey = "API키";





참고

https://developers.google.com/places/android-sdk/start 


https://developers.google.com/places/android-sdk/get-api-key




반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.


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

+ Recent posts