إنشاء منتقي الأماكن الحالية على Android (Java)

1. قبل البدء

اطّلِع على طريقة استخدام "منصة خرائط Google" وحزمة تطوير البرامج (SDK) للأماكن التي تعمل على نظام التشغيل Android لتقديم قائمة للمستخدمين في "الأماكن" لتحديد مواقعهم الجغرافية الحالية.

bd07a9ad2cb27a06.png

المتطلّبات الأساسية

  • مهارات جافا الأساسية

الإجراءات التي ستنفذّها

  • إضافة خريطة إلى تطبيق Android
  • استخدام أذونات تحديد الموقع الجغرافي لتحديد موقع المستخدم.
  • جلب الأماكن بالقرب من الموقع الحالي للمستخدم.
  • مشاركة "الأماكن المحتملة" للمستخدم للتعرُّف على موقعه الجغرافي الحالي

العناصر التي سيتم إنشاؤها

يمكنك إنشاء تطبيقك المتوافق مع Android من البداية، ولكن يمكنك تنزيل نموذج الرمز للمقارنة عند تصحيح الأخطاء. نزِّل نموذج الرمز من GitHub، أو إذا كان قد تم إعداد Git لاستخدام سطر الأوامر، أدخِل ما يلي:

git clone https://github.com/googlecodelabs/current-place-picker-android.git

إذا واجهت أي مشاكل (أخطاء الترميز أو الأخطاء النحوية أو الصياغة غير الواضحة أو غير ذلك) أثناء العمل على هذا الدرس التطبيقي حول الترميز، يُرجى الإبلاغ عن المشكلة من خلال الرابط الإبلاغ عن خطأ في أسفل يمين الدرس التطبيقي حول الترميز.

2. البدء

قبل بدء هذا الدرس التطبيقي حول الترميز، عليك إعداد ما يلي:

استوديو Android

يمكنك تنزيل "استوديو Android" من https://developer.android.com/studio.

إذا كان لديك Android Studio، تأكّد من تثبيت أحدث إصدار من خلال النقر على Android Studio > Check for Updates....

1f36bae83b64e33.png

تمت كتابة هذا الدرس التطبيقي باستخدام "استوديو Android 3.4".

حزمة تطوير البرامج (SDK) لنظام التشغيل Android

في "استوديو Android"، يمكنك ضبط حِزم تطوير البرامج (SDK) المطلوبة باستخدام "مدير حِزم تطوير البرامج (SDK)". يستخدم هذا الدرس التطبيقي Android Q SDK.

  1. من شاشة الترحيب في Android Studio، انقر على Configure (ضبط) ومدير تطوير البرامج (SDK).

d3fa03c269ec231c.png

  1. ضع علامة في مربّع الاختيار حزمة تطوير البرامج (SDK) التي تريدها، ثم انقر على تطبيق.

إذا لم تكن لديك حزمة تطوير البرامج (SDK) حتى الآن، يؤدي ذلك إلى بدء تنزيل حزمة تطوير البرامج (SDK) إلى جهازك.

884e0aa1314f70d.png

خدمات Google Play

وعلى مدير SDK، عليك أيضًا تثبيت خدمات Google Play.

  1. انقر على علامة التبويب أدوات SDK وحدّد مربع الاختيار خدمات Google Play.

حدِّث إذا كانت الحالة هي التحديث متاح.

ad6211fd78f3b629.png

3- إعداد المحاكي

لتشغيل التطبيق، يمكنك ربط جهازك أو استخدام محاكي Android.

إذا كنت تستخدم جهازك الخاص، يمكنك التخطي إلى تعليمات الجهاز الحقيقي: تحديث خدمات Google Play في نهاية هذه الصفحة.

إضافة محاكي

  1. من شاشة الترحيب في Android Studio، انقر على Configure (ضبط) وAVD Manager (مدير AVD).

5dd2d14c9c56d3f9.png

سيؤدّي ذلك إلى فتح مربّع الحوار مدير الجهاز الافتراضي الذي يعمل بنظام التشغيل Android.

  1. انقر على إنشاء جهاز افتراضي... لفتح قائمة بالأجهزة التي يمكنك الاختيار من بينها.

2d44eada384f8b35.png

  1. اختَر جهازًا يحمل رمز d5722488d80cd6be.png Play في عمود متجر Play وانقر على التالي.

e0248f1c6e85ab7c.png

ستظهر لك مجموعة من صور النظام التي يمكنك تثبيتها. إذا كان Q الذي يستهدف Android 9.+ (Google Play) يحتوي على كلمة تنزيل بجانبه، انقر على تنزيل.

316d0d1efabd9f24.png

  1. انقر على التالي لمنح جهازك الافتراضي اسمًا، ثم انقر على إنهاء.

ستعود إلى قائمة الأجهزة الافتراضية.

  1. انقر على رمز بدء ba8adffe56d3b678.png بجانب جهازك الجديد:

7605864ed27f77ea.png

بعد بضع لحظات، يتم فتح المحاكي.

تعليمات المحاكي - تحديث خدمات Google Play

  1. بعد بدء تشغيل المحاكي، انقر على ... في شريط التنقل الذي يظهر**.**

2e1156e02643d018.png

ويؤدي هذا إلى فتح مربع الحوار عناصر التحكُّم الموسَّعة.

  1. انقر على Google Play في القائمة.

في حال توفّر تحديث، انقر على تحديث.

5afd2686c5cad0e5.png

  1. سجِّل الدخول إلى المحاكي باستخدام حساب Google.

يمكنك استخدام حسابك الخاص أو إنشاء حساب جديد مجانًا للإبقاء على اختبارك منفصلاً عن معلوماتك الشخصية.

وبعد ذلك، يتم فتح Google Play على خدمات Google Play.

  1. انقر على تحديث للحصول على أحدث إصدار من خدمات Google Play.

f4bc067e80630b9c.png

انقر على التخطّي إذا طُلب منك إكمال إعداد حسابك وإضافة خيار دفع.

ضبط الموقع الجغرافي في المحاكي

  1. بعد إطلاق المحاكي، اكتب "maps" في شريط البحث على الشاشة الرئيسية لعرض رمز تطبيق "خرائط Google".

2d996aadd53685a6.png

  1. انقر على الرمز لتشغيل التطبيق.

ستظهر لك خريطة تلقائية.

  1. في أسفل يسار الخريطة، انقر على موقعك الجغرافيc5b4e2fda57a7e71.png.

يُطلب منك منح أذونات الهاتف لاستخدام الموقع الجغرافي.

f2b68044eabca151.png

  1. انقر على ... لفتح قائمة عناصر التحكم الموسعة.
  2. انقر على علامة التبويب الموقع الجغرافي.
  3. أدخِل خط عرض وخط طول.

أدخل أي شيء تريده هنا، ولكن تأكد من أنه في منطقة بها الكثير من الأماكن.

(استخدِم خط العرض 20.7818 وخط الطول -156.4624 لمدينة كيهي على جزيرة ماوي في هاواي لتكرار النتائج من هذا الدرس التطبيقي حول الترميز.)

  1. انقر على إرسال وسيتم تحديث الخريطة باستخدام هذا الموقع الجغرافي.

f9576b35218f4187.png

أنت مستعد لتشغيل تطبيقك واختباره من خلال الموقع.

تعليمات الجهاز الحقيقية: تحديث خدمات Google Play

إذا كنت تستخدم جهازًا حقيقيًا يعمل بنظام التشغيل Android، يُرجى تنفيذ ما يلي:

  1. استخدِم شريط البحث على الشاشة الرئيسية للبحث عن خدمات Google Play وفتحها.
  2. انقر على المزيد من التفاصيل.

انقر على تحديث إذا كان متاحًا.

ad16cdb975b5c3f7.png baf0379ef8a9c88c.png

4. إنشاء هيكل التطبيق باستخدام نشاط على "خرائط Google"

  1. في شاشة الترحيب في Android Studio، اختَر بدء مشروع جديد في Android Studio.
  2. في علامة التبويب الهاتف والجهاز اللوحي، اختَر نشاط خرائط Google.

C9c80aa8211a8761.png

يظهر مربع الحوار Configure your project (ضبط مشروعك). في ما يلي مكان تسمية تطبيقك وإنشاء الحزمة بناءً على نطاقك.

في ما يلي إعدادات تطبيق يُسمى "المكان الحالي" والذي يتوافق مع حزمة com.google.codelab.currentplace.

37f5b93b94ee118c.png

  1. اختيار اللغة Java واختيار استخدام androidx. *.

الاحتفاظ بالالإعدادات التلقائية لبقية الإعدادات.

  1. انقر على إنهاء.

5. إضافة تبعيات خدمات Google إلى ملف إنشاء Gradle

للوصول إلى أذونات تحديد الموقع الجغرافي في Android، تحتاج إلى واجهة برمجة التطبيقات للموقع الجغرافي في Google وميزة "التعرف على الأنشطة" من خدمات Google Play. للحصول على مزيد من المعلومات حول إضافة واجهة برمجة تطبيقات أخرى في Google Play، يمكنك الاطّلاع على إعداد خدمات Google Play.

تتضمّن مشاريع Android Studio عادةً ملفَين (build.gradle). أحدهما مخصص للمشروع العام والآخر مخصص للتطبيق. إذا كان لديك مستكشف مشروع Android Studio في عرض Android، سيظهر كلاهما في مجلد Gradle Scripts. عليك تعديل ملف build.gradle (Module: app) لإضافة خدمات Google.

f3043429cf719c47.png

  1. أضف سطرين إلى القسم dependencies لإضافة خدمات Google للموقع وواجهة برمجة التطبيقات للأماكن ( نموذج رمز في السياق).

build.gradle (الوحدة: التطبيق)

plugins {
  id 'com.android.application'
}

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.google.codelab.currentplace"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'com.google.android.gms:play-services-maps:16.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'

    implementation 'com.google.android.gms:play-services-location:16.0.0'
    implementation 'com.google.android.libraries.places:places:1.1.0'
}

6- تفعيل واجهات برمجة تطبيقات "منصة خرائط Google" والحصول على مفتاح واجهة برمجة التطبيقات

بالنسبة إلى خطوة التفعيل التالية، عليك تفعيل حزمة تطوير البرامج (SDK) لتطبيق "خرائط Google" لنظام التشغيل Android وواجهة برمجة تطبيقات الأماكن.

إعداد "منصة خرائط Google"

إذا لم يكن لديك حساب على Google Cloud Platform ومشروع تم تفعيل الفوترة فيه، يُرجى الاطّلاع على دليل بدء استخدام "منصة خرائط Google" لإنشاء حساب فوترة ومشروع.

  1. في Cloud Console، انقر على القائمة المنسدلة للمشروع واختَر المشروع الذي تريد استخدامه لهذا الدرس التطبيقي.

  1. فعِّل واجهات برمجة تطبيقات ومنصة SDK لمنصة "خرائط Google" المطلوبة لهذا الدرس التطبيقي في Google Cloud Marketplace. ولإجراء ذلك، اتّبِع الخطوات الواردة في هذا الفيديو أو هذه المستندات.
  2. يمكنك إنشاء مفتاح واجهة برمجة تطبيقات في صفحة بيانات الاعتماد في Cloud Console. يمكنك اتّباع الخطوات الواردة في هذا الفيديو أو هذه المستندات. تتطلب جميع الطلبات إلى "منصة خرائط Google" مفتاح واجهة برمجة تطبيقات.

انسخ مفتاح واجهة برمجة التطبيقات الذي أنشأته للتو. عُد إلى Android Studio، وابحث عن الملف google_maps_api.xml ضمن Android > app > res > values.

استبدِل YOUR_KEY_HERE بمفتاح واجهة برمجة التطبيقات الذي نسخته.

aa576e551a7a1009.png

اكتمل إعداد تطبيقك.

7- تعديل ملف التنسيق

  1. في مستكشف المشروع، افتح ملف activity_maps.xml في نظام التشغيل Android &gt؛ app &gt؛ res &gt؛ layout.

4e0d986480c57efa.png

  1. ستظهر لك واجهة المستخدم الأساسية على يسار الشاشة، مع علامات تبويب في أسفل الشاشة تسمح لك باختيار التصميم أو محرِّر النصوص للتنسيق. اختر نص، واستبدل المحتوى الكامل لملف التنسيق بهذا:

activity_maps.xml

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:minHeight="?attr/actionBarSize"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:titleTextColor="@android:color/white"
        android:background="@color/colorPrimary" />

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

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

        <ListView
            android:id="@+id/listPlaces"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>

</LinearLayout>

وسيمنحك هذا واجهة مستخدم تبدو كما يلي:

1bf786808a4697ce.png

8- إعداد شريط التطبيقات

لمنح المستخدم زرًا للنقر عليه عندما يريد اختيار مكانه الحالي، أضف شريط تطبيقات برمز يعرض مكان المستخدم الحالي ويعرض الأماكن المحتملة القريبة. ستظهر

3a17c92b613a26c5.png

وعلى الهاتف، لا يتم عرض سوى الرمز. وفي الجهاز اللوحي الذي يحتوي على مساحة أكبر، يتم أيضًا تضمين النص.

إنشاء الرمز

  1. في مستكشف المشروع، انقر على Android > app، ثم انقر بزر الماوس الأيمن على مجلد res واختَر جديد &gt؛ مادة عرض الصورة.

سيظهر استوديو مواد العرض.

  1. في القائمة نوع الرمز، انقر على رموز الإجراءات وعلامة التبويب "شريط الإجراءات".
  2. أدخِل اسمًا لمادة العرض ic_geolocate.
  3. اختَر Clip Art كنوع مادة العرض**.**
  4. انقر على الرسم بجانب صورة مقطعية.

وسيؤدي ذلك إلى فتح نافذة اختيار الرمز.

  1. اختَر رمزًا.

ويمكنك استخدام شريط البحث للعثور على رموز ذات صلة بقصدك.

  1. ابحث عن location واختَر رمزًا مرتبطًا بالموقع الجغرافي.

يكون رمز موقعي هو نفسه الرمز المستخدم في تطبيق "خرائط Google" عندما يريد المستخدم التقاط الكاميرا إلى موقعه الحالي.

  1. انقر على حسنًا &gt؛ التالي &gt؛ إنهاء، وتأكد من توفّر مجلد جديد باسم drawable يحتوي على ملفات الرموز الجديدة.

b9e0196137ed18ae.png

إضافة موارد السلسلة

  1. في مستكشف المشروع، انقر على Android > app > res > values وافتح ملف strings.xml.
  2. أضف السطور التالية بعد <string name="title_activity_maps">Map</string>:

strings.xml

    <string name="action_geolocate">Pick Place</string>
    <string name="default_info_title">Default Location</string>
    <string name="default_info_snippet">No places found, because location permission is disabled.</string>

يُستخدم السطر الأول في شريط التطبيقات عند توفر مساحة لتضمين تصنيف نصي بجانب الرمز. يتم استخدام العلامات الأخرى لتحديد العلامات التي تضيفها إلى الخريطة.

يبدو الرمز في الملف الآن على النحو التالي:

<resources>
    <string name="app_name">Current Place</string>
    <string name="title_activity_maps">Map</string>
    <string name="action_geolocate">Pick Place</string>
    <string name="default_info_title">Default Location</string>
    <string name="default_info_snippet">No places found, because location permission is disabled.</string>
</resources>

إضافة شريط التطبيقات

  1. في مستكشف المشروع، انقر على Android &gt؛ app، ثم انقر بزر الماوس الأيمن على مجلد res واختَر جديد &gt؛ الدليل لإنشاء دليل فرعي جديد ضمن app/src/main/res.
  2. أدخِل اسمًا للدليل menu.
  3. انقر بزر الماوس الأيمن على المجلد menu واختَر جديد &gt؛ ملف.
  4. حدِّد اسمًا للملف menu.xml.
  5. الصق هذا الرمز:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!-- "Locate me", should appear as action button if possible -->
    <item
        android:id="@+id/action_geolocate"
        android:icon="@drawable/ic_geolocate"
        android:title="@string/action_geolocate"
        app:showAsAction="always|withText" />

</menu>

تعديل نمط شريط التطبيقات

  1. في مستكشف المشروع، وسِّع Android &gt؛ app &gt؛ res &gt؛ values، وافتح الملف styles.xml داخله.
  2. في العلامة <style>، عدِّل الموقع الرئيسي ليصبح "Theme.AppCompat.NoActionBar".
  3. سجّل السمة name التي تستخدمها في الخطوة التالية.

styles.xml

<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">

تعديل مظهر التطبيق في AndroidManifest.xml

  1. انقر على Android > app > manifests وافتح ملف AndroidManifest.xml.
  2. ابحث عن السطر android:theme وعدِّل القيمة لتصبح @style/AppTheme.

ملف AndroidManifest.xml

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

أنت الآن على استعداد لبدء الترميز.

9- إعداد التطبيق

  1. في مستكشف المشروع، ابحث عن ملف MapsActivity.java.

إنه متوفّر في المجلد المقابل للحزمة التي أنشأتها لتطبيقك في الخطوة 1.

8b0fa27d417f5f55.png

  1. افتح الملف وأنت في محرّر ترميز Java.

استيراد حزمة تطوير البرامج (SDK) للأماكن وغيرها من التبعيات

يمكنك إضافة هذه الأسطر في أعلى MapsActivity.java، مع استبدال كشوفات الاستيراد الحالية.

وتشمل عمليات الاستيراد الحالية وتضيف الكثير من القيم المستخدمة في الرمز في هذا الدرس التطبيقي حول الترميز.

MapsActivity.java

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

import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;
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.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import com.google.android.gms.tasks.OnCompleteListener;
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.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 java.util.Arrays;
import java.util.List;

تعديل توقيع الصف

تستخدم واجهة برمجة تطبيقات الأماكن مكونات AndroidX لدعم التوافق مع الأنظمة القديمة، لذا تحتاج إلى تعريفها لتوسيع AppCompatActivity. وهي تحل محل الإضافة FragmentActivity التي يتم تحديدها تلقائيًا لنشاط الخرائط.

public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback {

إضافة متغيرات الصف

بعد ذلك، أعلن عن متغيرات الفئة المختلفة المستخدمة في طرق الفئات المختلفة. ويتضمن ذلك عناصر واجهة المستخدم ورموز الحالة. يجب أن تكون أقل من تعريف المتغيّر للسمة GoogleMap mMap مباشرةً.

    // New variables for Current Place picker
    private static final String TAG = "MapsActivity";
    ListView lstPlaces;
    private PlacesClient mPlacesClient;
    private FusedLocationProviderClient mFusedLocationProviderClient;

    // The geographical location where the device is currently located. That is, the last-known
    // location retrieved by the Fused Location Provider.
    private Location mLastKnownLocation;

    // A default location (Sydney, Australia) and default zoom to use when location permission is
    // not granted.
    private final LatLng mDefaultLocation = new LatLng(-33.8523341, 151.2106085);
    private static final int DEFAULT_ZOOM = 15;
    private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;
    private boolean mLocationPermissionGranted;

    // Used for selecting the Current Place.
    private static final int M_MAX_ENTRIES = 5;
    private String[] mLikelyPlaceNames;
    private String[] mLikelyPlaceAddresses;
    private String[] mLikelyPlaceAttributions;
    private LatLng[] mLikelyPlaceLatLngs;

تعديل طريقة onCreate

يجب تحديث طريقة onCreate لمعالجة أذونات مستخدم وقت التشغيل لخدمات الموقع الجغرافي وإعداد عناصر واجهة المستخدم وإنشاء عميل واجهة برمجة تطبيقات الأماكن.

أضف أسطر الرموز التالية بشأن شريط أدوات الإجراءات وإعداد الملفات الشخصية وبرنامج الأماكن إلى نهاية طريقة onCreate() الحالية.

MapsActivity.java onCreate()

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        //
        // PASTE THE LINES BELOW THIS COMMENT
        //
        
        // Set up the action toolbar
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // Set up the views
        lstPlaces = (ListView) findViewById(R.id.listPlaces);

        // Initialize the Places client
        String apiKey = getString(R.string.google_maps_key);
        Places.initialize(getApplicationContext(), apiKey);
        mPlacesClient = Places.createClient(this);
        mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
    }

إضافة رمز لقائمة شريط التطبيقات

تضيف هاتان الطريقتان قائمة شريط التطبيقات (باستخدام عنصر واحد، وهو رمز اختيار المكان) وتتعامل مع نقرة المستخدم على الرمز.

انسخ هاتين الطريقتين إلى الملف بعد طريقة onCreate.

MapsActivity.java onCreateOptionsMenu() and onOptionsItemSelect()

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
           case R.id.action_geolocate:
                
                // COMMENTED OUT UNTIL WE DEFINE THE METHOD
                // Present the current place picker
                // pickCurrentPlace();
                return true;

            default:
                // If we got here, the user's action was not recognized.
                // Invoke the superclass to handle it.
                return super.onOptionsItemSelected(item);

        }
    }

اختباره

  1. من "استوديو Android"، انقر على تشغيل أو تشغيل القائمة > وتشغيل "التطبيق'".

28bea91c68c36fb2.png

  1. سيُطلب منك اختيار هدف النشر. من المفترض أن يظهر المحاكي قيد التشغيل في هذه القائمة. وبعد اختيار التطبيق، سينشر "استوديو Android" التطبيق على المحاكي.

f44658ca91f6f41a.png

يتم تشغيل التطبيق بعد بضع لحظات. يمكنك مشاهدة الخريطة في وسط سيدني، أستراليا، باستخدام زر واحد وقائمة أماكن غير مأهولة.

68eb8c70f4748350.png

لا ينتقل تركيز الخريطة إلى موقع المستخدم، ما لم تطلب إذنًا للوصول إلى موقع الجهاز.

10- طلب أذونات تحديد الموقع الجغرافي ومعالجتها

طلب أذونات تحديد الموقع الجغرافي بعد أن تصبح الخريطة جاهزة

  1. تحديد طريقة تُسمى getLocationPermission تطلب أذونات المستخدمين.

ألصِق هذا الرمز أسفل طريقة onOptionsSelected التي أنشأتها للتو.

MapsActivity.java getLocationPermission()

    private void getLocationPermission() {
        /*
         * Request location permission, so that we can get the location of the
         * device. The result of the permission request is handled by a callback,
         * onRequestPermissionsResult.
         */
        mLocationPermissionGranted = false;
        if (ContextCompat.checkSelfPermission(this.getApplicationContext(),
                android.Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            mLocationPermissionGranted = true;
        } else {
            ActivityCompat.requestPermissions(this,
                    new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
                    PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
        }
    }
  1. أضف سطرين إلى نهاية طريقة onMapReady الحالية لتفعيل عناصر التحكم في التكبير/التصغير وطلب أذونات تحديد الموقع الجغرافي من المستخدم.

MapsActivity.java onMapReady()

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

        // Add a marker in Sydney and move the camera
        LatLng sydney = new LatLng(-34, 151);
        mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
        mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));

        //
        // PASTE THE LINES BELOW THIS COMMENT
        //

        // Enable the zoom controls for the map
        mMap.getUiSettings().setZoomControlsEnabled(true);

        // Prompt the user for permission.
        getLocationPermission();

    }

معالجة النتيجة من الأذونات المطلوبة

عندما يرد المستخدم على مربّع حوار طلب الإذن، يتم استدعاء هذه المكالمة بواسطة Android.

ألصِق هذا الرمز بعد طريقة getLocationPermission():

MapsActivity.java onRequestPermissionResult()

   /**
     * Handles the result of the request for location permissions
     */
    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String permissions[],
                                           @NonNull int[] grantResults) {
        mLocationPermissionGranted = false;
        switch (requestCode) {
            case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    mLocationPermissionGranted = true;
                }
            }
        }
    }

11- الحصول على الموقع الجغرافي الحالي وجلب الأماكن المحتملة

عندما ينقر المستخدم على اختيار مكان في شريط التطبيقات، يستدعي التطبيق الطريقة pickCurrentPlace()، والتي تُسمى طريقة getDeviceLocation() التي حدّدتها سابقًا. تتطلب الطريقة getDeviceLocation استخدام طريقة أخرى، getCurrentPlaceLikelihoods, بعد استرداد آخر موقع للجهاز.

استدعاء واجهة برمجة تطبيقات FindCurrentPlace ومعالجة الاستجابة

ينشئ getCurrentPlaceLikelihoods findCurrentPlaceRequest يستدعي مهمة واجهة برمجة تطبيقات الأماكن findCurrentPlace. إذا كانت المهمة ناجحة، ستعرض findCurrentPlaceResponse، التي تحتوي على قائمة بكائنات placeLikelihood. ولكل منها عدد من الخصائص، بما في ذلك اسم المكان وعنوانه، واحتمال وجودك في ذلك المكان (قيمة مزدوجة من 0 إلى 1). تتعامل هذه الطريقة مع الاستجابة عن طريق إنشاء قوائم بتفاصيل الأماكن من placeLikelihoods.

يعمل هذا الرمز على تكرار الأماكن الخمسة الأكثر احتمالاً ويضيفها مع احتمالية أن تكون قيمتها أكبر من 0 إلى قائمة تعرضها بعد ذلك. إذا كنت تريد عرض أكثر أو أقل من خمس، عدِّل ثابت M_MAX_ENTRIES.

ألصِق هذا الرمز بعد طريقة onMapReady.

MapsActivity.java getCurrentPlaceLikeliitiess()

   private void getCurrentPlaceLikelihoods() {
        // Use fields to define the data types to return.
        List<Place.Field> placeFields = Arrays.asList(Place.Field.NAME, Place.Field.ADDRESS,
                Place.Field.LAT_LNG);

        // Get the likely places - that is, the businesses and other points of interest that
        // are the best match for the device's current location.
        @SuppressWarnings("MissingPermission") final FindCurrentPlaceRequest request =
                FindCurrentPlaceRequest.builder(placeFields).build();
        Task<FindCurrentPlaceResponse> placeResponse = mPlacesClient.findCurrentPlace(request);
        placeResponse.addOnCompleteListener(this,
                new OnCompleteListener<FindCurrentPlaceResponse>() {
                    @Override
                    public void onComplete(@NonNull Task<FindCurrentPlaceResponse> task) {
                        if (task.isSuccessful()) {
                            FindCurrentPlaceResponse response = task.getResult();
                            // Set the count, handling cases where less than 5 entries are returned.
                            int count;
                            if (response.getPlaceLikelihoods().size() < M_MAX_ENTRIES) {
                                count = response.getPlaceLikelihoods().size();
                            } else {
                                count = M_MAX_ENTRIES;
                            }

                            int i = 0;
                            mLikelyPlaceNames = new String[count];
                            mLikelyPlaceAddresses = new String[count];
                            mLikelyPlaceAttributions = new String[count];
                            mLikelyPlaceLatLngs = new LatLng[count];

                            for (PlaceLikelihood placeLikelihood : response.getPlaceLikelihoods()) {
                                Place currPlace = placeLikelihood.getPlace();
                                mLikelyPlaceNames[i] = currPlace.getName();
                                mLikelyPlaceAddresses[i] = currPlace.getAddress();
                                mLikelyPlaceAttributions[i] = (currPlace.getAttributions() == null) ?
                                        null : TextUtils.join(" ", currPlace.getAttributions());
                                mLikelyPlaceLatLngs[i] = currPlace.getLatLng();

                                String currLatLng = (mLikelyPlaceLatLngs[i] == null) ?
                                        "" : mLikelyPlaceLatLngs[i].toString();

                                Log.i(TAG, String.format("Place " + currPlace.getName()
                                        + " has likelihood: " + placeLikelihood.getLikelihood()
                                        + " at " + currLatLng));

                                i++;
                                if (i > (count - 1)) {
                                    break;
                                }
                            }


                            // COMMENTED OUT UNTIL WE DEFINE THE METHOD
                            // Populate the ListView
                            // fillPlacesList();
                        } else {
                            Exception exception = task.getException();
                            if (exception instanceof ApiException) {
                                ApiException apiException = (ApiException) exception;
                                Log.e(TAG, "Place not found: " + apiException.getStatusCode());
                            }
                        }
                    }
                });
    }

تحريك كاميرا الخريطة إلى الموقع الجغرافي الحالي للجهاز

عندما يمنح المستخدم الإذن، يجلب التطبيق أحدث موقع للمستخدم ويحرِّك الكاميرا إلى الوسط حول هذا الموقع.

وفي حال رفض المستخدم الإذن، ينقل التطبيق الكاميرا إلى الموقع التلقائي المحدَّد من بين ثوابت في هذه الصفحة (في نموذج الرمز، هي سيدني، أستراليا).

ألصِق هذا الرمز بعد طريقة getPlaceLikelihoods():

MapsActivity.java getDeviceLocation()

    private void getDeviceLocation() {
        /*
         * Get the best and most recent location of the device, which may be null in rare
         * cases when a location is not available.
         */
        try {
            if (mLocationPermissionGranted) {
                Task<Location> locationResult = mFusedLocationProviderClient.getLastLocation();
                locationResult.addOnCompleteListener(this, new OnCompleteListener<Location>() {
                    @Override
                    public void onComplete(@NonNull Task<Location> task) {
                        if (task.isSuccessful()) {
                            // Set the map's camera position to the current location of the device.
                            mLastKnownLocation = task.getResult();
                            Log.d(TAG, "Latitude: " + mLastKnownLocation.getLatitude());
                            Log.d(TAG, "Longitude: " + mLastKnownLocation.getLongitude());
                            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
                                    new LatLng(mLastKnownLocation.getLatitude(),
                                            mLastKnownLocation.getLongitude()), DEFAULT_ZOOM));
                        } else {
                            Log.d(TAG, "Current location is null. Using defaults.");
                            Log.e(TAG, "Exception: %s", task.getException());
                            mMap.moveCamera(CameraUpdateFactory
                                    .newLatLngZoom(mDefaultLocation, DEFAULT_ZOOM));
                        }

                       getCurrentPlaceLikelihoods();
                    }
                });
            }
        } catch (SecurityException e)  {
            Log.e("Exception: %s", e.getMessage());
        }
    }

التحقّق من أذونات تحديد الموقع الجغرافي عندما ينقر المستخدم على "اختيار مكان"

عندما ينقر المستخدم على اختيار مكان، تتحقق هذه الطريقة من أذونات تحديد الموقع الجغرافي وتطلب من المستخدم الإذن إذا لم يتم منحه.

في حال منح المستخدم الإذن، استدعاء الطريقة getDeviceLocation لبدء عملية الحصول على الأماكن المحتملة الحالية.

  1. إضافة هذه الطريقة بعد getDeviceLocation():

MapsActivity.java PickCurrentPlace()

   private void pickCurrentPlace() {
        if (mMap == null) {
            return;
        }

        if (mLocationPermissionGranted) {
            getDeviceLocation();
        } else {
            // The user has not granted permission.
            Log.i(TAG, "The user did not grant location permission.");

            // Add a default marker, because the user hasn't selected a place.
            mMap.addMarker(new MarkerOptions()
                    .title(getString(R.string.default_info_title))
                    .position(mDefaultLocation)
                    .snippet(getString(R.string.default_info_snippet)));

            // Prompt the user for permission.
            getLocationPermission();
        }
    }
  1. الآن بعد تحديد pickCurrentPlace، ابحث عن السطر في onOptionsItemSelected() الذي يتصل بـ pickCurrentPlace وألغِ تعليقه.

MapsActivity.java onOptionItemSelect()

           case R.id.action_geolocate:

                // COMMENTED OUT UNTIL WE DEFINE THE METHOD
                // Present the Current Place picker
                pickCurrentPlace();
                return true;

اختباره

إذا شغّلت التطبيق الآن وانقرت على اختيار مكان، من المفترض أن يطلب منك أذونات تحديد الموقع الجغرافي.

  • وفي حال منح الإذن بذلك، سيتم حفظ هذا الخيار المفضَّل ولن يُطلب منك ذلك. إذا رفضت الإذن، ستتلقى رسالة مطالبة في المرة التالية التي تنقر فيها على الزر.
  • على الرغم من جلب getPlaceLikelihoods الأماكن الحالية المحتملة، لا يعرض ListView الأماكن بعد. في Android Studio، يمكنك النقر على ⌘6 للتحقّق من السجلّات في Logcat لتحديد البيانات التي تم وضع علامة MapsActivity عليها للتحقّق من عمل الطرق الجديدة بشكلٍ صحيح.
  • في حال منح الإذن، تتضمن السجلات بيانًا لـ Latitude: وبيانًا لـ Longitude: يعرض الموقع الجغرافي للجهاز الذي تم رصده. إذا كنت تستخدم "خرائط Google" وقائمة المحاكي في وقت سابق لتحديد موقع للمحاكي، تعرض هذه البيانات هذا الموقع الجغرافي.
  • إذا تم الاتصال بـ findCurrentPlace بنجاح، ستتضمّن السجلّات خمس كشوفات تطبع أسماء الأماكن الخمسة والأماكن الأكثر احتمالاً.

d9896a245b81bf3.png

12- تعبئة منتقي المكان الحالي

إعداد معالج للأماكن المختارة

لنفكر في ما نريد حدوثه عندما ينقر المستخدم على أحد العناصر في ListView. لتأكيد اختيار المستخدم للمكان الذي يتواجد فيه حاليًا، يمكنك إضافة علامة إلى الخريطة في ذلك المكان. إذا نقر المستخدم على هذه العلامة، ستظهر نافذة معلومات تعرض اسم المكان وعنوانه.

الصق معالج النقر هذا بعد طريقة pickCurrentPlace.

MapsActivity.java listClickedHandler

    private AdapterView.OnItemClickListener listClickedHandler = new AdapterView.OnItemClickListener() {
        public void onItemClick(AdapterView parent, View v, int position, long id) {
            // position will give us the index of which place was selected in the array
            LatLng markerLatLng = mLikelyPlaceLatLngs[position];
            String markerSnippet = mLikelyPlaceAddresses[position];
            if (mLikelyPlaceAttributions[position] != null) {
                markerSnippet = markerSnippet + "\n" + mLikelyPlaceAttributions[position];
            }

            // Add a marker for the selected place, with an info window
            // showing information about that place.
            mMap.addMarker(new MarkerOptions()
                    .title(mLikelyPlaceNames[position])
                    .position(markerLatLng)
                    .snippet(markerSnippet));

           // Position the map's camera at the location of the marker.
            mMap.moveCamera(CameraUpdateFactory.newLatLng(markerLatLng));
        }
    };

تعبئة ViewList

الآن وبعد أن أصبحت لديك قائمة بالأماكن الأكثر احتمالاً التي يزورها المستخدم حاليًا، يمكنك تقديم هذه الخيارات للمستخدم في ListView. يمكنك أيضًا ضبط أداة معالجة النقر على ListView لاستخدام معالج النقرات الذي حدّدته للتو.

الصق هذه الطريقة بعد معالج النقر:

MapsActivity.javafillPlaceList()

    private void fillPlacesList() {
        // Set up an ArrayAdapter to convert likely places into TextViews to populate the ListView
        ArrayAdapter<String> placesAdapter =
                new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mLikelyPlaceNames);
        lstPlaces.setAdapter(placesAdapter);
        lstPlaces.setOnItemClickListener(listClickedHandler);
    }

الآن بعد تحديد fillPlacesList، ابحث عن السطر المتجه إلى نهاية findPlaceLikelihoods، والذي يتصل بـ fillPlacesList وإلغاء التعليق عليه.

MapsActivity.java التعبئة بيانات إبداء الإعجاب (())

               // COMMENTED OUT UNTIL WE DEFINE THE METHOD
                // Populate the ListView
                fillPlacesList();

هذا هو الرمز المطلوب لأداة اختيار المكان الحالي.

13- تشغيل التطبيق

اختبار اختيار مكان

  1. شغِّل التطبيق مرة أخرى.

هذه المرة عندما تنقر على اختيار مكان، يملأ التطبيق القائمة بالأماكن ذات الأسماء القريبة من الموقع الجغرافي. وعلى مقربة من هذا الموقع في جزيرة ماوي، تنتشر مطاعم، مثل متجر "أولولاني" الذي يقدّم أطباق الحلويات والآيس كريم في هاواي على الشاطئ. نظرًا لأن الأماكن المتعددة قريبة جدًا من إحداثيات الموقع الجغرافي، تكون هذه قائمة بالأماكن المحتملة التي قد تكون فيها.

  1. انقر على اسم مكان في ListView.

من المفترض أن ترى محدِّد موقع تمت إضافته إلى الخريطة.

  1. انقر على محدّد الموقع.

يمكنك الاطّلاع على تفاصيل المكان.

e52303cc0de6a513.png 864c74342fb52a01.png

اختبار موقع جغرافي مختلف

إذا كنت تريد تغيير موقعك وكنت تستخدم المحاكي، لن يتم تحديث الموقع الجغرافي للجهاز تلقائيًا عند تحديث إحداثيات الموقع الجغرافي في قائمة المحاكيات الموسّعة.

للتحايل على هذا، اتبع هذه الخطوات لاستخدام تطبيق "خرائط Google" الأصلي لفرض التحديثات على موقع المحاكي:

  1. افتح خرائط Google.
  2. انقر على ... > الموقع الجغرافي لتغيير خط الطول وخط العرض إلى الإحداثيات الجديدة، ثم انقر على إرسال.
  3. على سبيل المثال، يمكنك استخدام خط العرض: 49.2768 وخط الطول: -123.1142 لضبط الموقع على وسط مدينة فانكوفر بكندا.
  4. تأكّد من إعادة ضبط تطبيق "خرائط Google" على الإحداثيات الجديدة. قد تحتاج إلى النقر على زر موقعي في تطبيق "خرائط Google" لطلب التحديث.
  5. ارجع إلى تطبيق المكان الحالي وانقر على اختيار مكان للحصول على الخريطة على الإحداثيات الجديدة والاطلاع على قائمة جديدة بالأماكن الحالية التي يُحتمل أن تكون حالية.

9adb99d1ce25c184.png

وهكذا انتهى كل شيء! لقد أنشأت تطبيقًا بسيطًا يبحث عن الأماكن في الموقع الجغرافي الحالي ويمنحك احتمالاً أن تكون أنت. استمتع!

والآن، واصِل تشغيل التطبيق وتطبيق التعديلات التي أجريتها لإكمال هذه الخطوة الإضافية.

14- الخطوات اللاحقة

لمنع سرقة مفتاح واجهة برمجة التطبيقات، يجب تأمينه حتى يتمكن تطبيق Android فقط من استخدام المفتاح. وفي حال تم تركها بدون قيود، يمكن لأي شخص لديه المفتاح استخدامها للاتصال بواجهات برمجة التطبيقات لمنصّة Google Maps Platform ودفعك للفواتير.

الحصول على شهادة SHA-1

ستحتاج إلى ذلك لاحقًا عند تقييد مفاتيح واجهة برمجة التطبيقات. في ما يلي مجموعة من التعليمات للحصول على شهادة تصحيح الأخطاء.

بالنسبة إلى نظام التشغيل Linux أو macOS، افتح نافذة طرفية وأدخِل ما يلي:

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

بالنسبة إلى نظامي التشغيل Windows Vista وWindows 7، شغِّل الأمر التالي:

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

من المفترض أن تظهر لك نتيجة مثل ما يلي:

Alias name: androiddebugkey
Creation date: Jan 01, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 4aa9b300
Valid from: Mon Jan 01 08:04:04 UTC 2013 until: Mon Jan 01 18:04:04 PST 2033
Certificate fingerprints:
     MD5:  AE:9F:95:D0:A6:86:89:BC:A8:70:BA:34:FF:6A:AC:F9
     SHA1: BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:A1:66:6E:44:5D:75
     Signature algorithm name: SHA1withRSA
     Version: 3

يحتوي السطر الذي يبدأ SHA1 على بصمة إصبع SHA-1 للشهادة. بصمة الإصبع هي تسلسل من 20 رقمًا سداسيًا عشريًا ثنائيًا ومفصولاً بنقطتين.

عندما تكون مستعدًا لإصدار تطبيق ما، استخدم التعليمات الواردة في هذه المستندات لاسترداد شهادة الإصدار.

إضافة قيود إلى مفتاح واجهة برمجة التطبيقات

  1. في Cloud Console، انتقِل إلى APIs & Services > Credentials (بيانات الاعتماد).

يجب إدراج المفتاح الذي استخدمته لهذا التطبيق ضمن مفاتيح واجهة برمجة التطبيقات.

  1. انقر على 6454a04865d551e6.png لتعديل إعدادات المفتاح.

316b052c621ee91c.png

  1. في صفحة مفتاح واجهة برمجة التطبيقات، بعد القيود الأساسية، يمكنك إعداد قيود التطبيق من خلال إجراء ما يلي:
  2. اختَر تطبيقات Android واتّبِع التعليمات.
  3. انقر على إضافة عنصر.
  4. أدخِل اسم الحزمة والملف المرجعي لشهادة SHA-1 (تم استردادهما في القسم السابق).

مثلاً:

com.google.codelab.currentplace
BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:A1:66:6E:44:5D:75s
  1. للحصول على مزيد من الحماية، يمكنك ضبط قيود واجهة برمجة التطبيقات من خلال إجراء ما يلي.
  2. بعد قيود واجهة برمجة التطبيقات، اختَر تقييد المفتاح.
  3. حدّد حزمة تطوير البرامج (SDK) لتطبيق "خرائط Google" لنظام التشغيل Android وأماكن الأماكن.
  4. انقر على تم وحفظ.

15- تهانينا

لقد أنشأت تطبيقًا بسيطًا يبحث عن الأماكن الأكثر احتمالاً في الموقع الحالي ويضيف علامة إلى الخريطة للمكان الذي يحدده المستخدم.

مزيد من المعلومات