ในงาน Google I/O 2016 ที่ผ่านมา กูเกิ้ลมีการประกาศเปิดตัว API ตัวใหม่ที่น่าสนใจมากนามว่า Awareness API อันนี้เป็นวีดีโอ Session ในงานความยาว 35 นาทีครับ
และมาวันนี้ กูเกิ้ลก็ปล่อย API ชุดนี้ให้เราได้ใช้งานกันแล้ว หลังจากลองเล่นแล้วก็ถือว่าน่าสนใจทีเดียวเลยเขียนเป็นบล็อกให้อ่านกันครับ
อะไรคือ Awareness API?
คำนิยามของ Awareness API ที่กูเกิ้ลให้ไว้คือ
A unified sensing platform enabling applications to be aware of multiple aspects of a users context, while managing battery and memory health.
ฟังดูงงๆยากๆแต่ความจริงมันเป็น API ที่ตรงไปตรงมาและง่ายมากคือ มันเป็น API ที่ชุดคำสั่งสามารถดึงข้อมูลที่อิงกับบริบท(Context)ของผู้ใช้ในขณะนั้นๆได้ หรือหากไม่เข้าใจคำว่าบริบท จริงๆมันก็คือการดึงข้อมูลที่อิงกับสถานการณ์รอบข้างของผู้ใช้คนนั้นๆนั่นเอง ยกตัวอย่างเช่น
- เวลาท้องถิ่นของตำแหน่งที่ผู้ใช้คนนั้นๆอยู่
- สถานที่ที่ผู้ใช้คนนั้นๆอยู่อุณหภูมิกี่องศา
- คนๆนั้นอยู่เฉยๆ เดินอยู่ วิ่งอยู่หรือขี่จักรยานอยู่
- ผู้ใช้เข้าใกล้พิกัดที่กำหนดรึเปล่า
- ตรวจสอบว่าหูฟังเสียบอยู่รึเปล่า
จริงๆ API หลายๆตัวของแอนดรอยด์ก็ทำได้อยู่แล้วเช่นอยากรู้อุณหภูมิก็ดึงพิกัดแล้วไปดึงอุณหภูมิจาก Weather API หรืออยากรู้ว่าคนๆนั้นอยู่เฉยๆหรือวิ่งอยู่เราก็สามารถดึงพิกัดแล้วคำนวณหาความเร็วของคนๆนั้นได้
แต่ก็จะเห็นว่ามันเป็นงานที่ค่อนข้างวุ่นวายและอาจเกิดความผิดพลาดได้ แต่ด้วย Awareness API จะทำให้การกระทำเหล่านี้ง่ายขึ้นแบบมหาศาลเหลือเพียงไม่กี่บรรทัด ความแม่นยำของข้อมูลที่ได้ก็สูงมากเพราะผ่านการประมวลผลแล้วไม่ใช่เป็นเพียงข้อมูลดิบๆแต่อย่างใด นอกจากนั้นปัญหาสามัญอย่างเรื่องการกินแบตเตอรี่ก็ไม่ใช่ปัญหาอีกต่อไปเพราะ Awareness API ทำการ Optimize ทุกอย่างมาดีแล้วจึงสามารถใช้งานได้อย่างไม่ต้องกังวลใดๆ
Awareness API เลยเป็นอีกหนึ่ง API สุดเทพที่ถึงจะดูเหมือนไม่มีอะไรเพราะมันเรียกใช้ง่ายมากและตรงไปตรงมา แต่ถ้าแอปของท่านมีการใช้ฟีเจอร์เหล่านี้หละก็ มันจะกลายเป็น API ที่มีประโยชน์มากเลยทีเดียว
ประเภทของบริบท(Context)ที่สามารถใช้งานได้
แล้วเราสามารถเล่นกับ Context อะไรของผู้ใช้ได้บ้าง? สำหรับ Awareness API เราสามารถเล่นได้ถึง 7 ชนิดด้วยกันได้แก่
Time - ดึงเวลาท้องถิ่นของผู้ใช้คนนั้นๆ
Location - ดึงพิกัด Lat/Long ที่ผู้ใช้อยู่
Place - เข้าถึงข้อมูลสถานที่รอบๆตัวของผู้ใช้
Activity - ดูสถานะการเคลื่อนที่ของผู้ใช้เช่น อยู่เฉยๆ เดินอยู่ วิ่งอยู่หรือว่าขี่จักรยานอยู่
Beacons - ดูว่ารอบตัวมี Beacon อะไรวางอยู่บ้าง
Headphones - ดูว่าผู้ใช้เสียบหูฟังไว้หรือไม่
Weather - เข้าถึงข้อมูลสภาพภูมิอากาศของพื้นที่ที่ผู้ใช้คนนั้นๆอยู่
ซึ่ง 7 อย่างนี้ก็เรียกว่าครอบคลุมงานที่เราใช้กันทั่วไปได้หมดแล้ว
API สองชุดของ Awareness API
ใน Awareness API มีกลุ่มของ APIs อยู่ทั้งหมดสองชุดด้วยกันได้แก่
Snapshot API - การ "ดึงข้อมูลตาม Context" ตามที่ลิสต์ไว้ด้านบนเอามาใช้งาน
Fence API - การ "ตรวจจับการเปลี่ยนแปลง Context" ของผู้ใช้ เช่น ถ้าผู้ใช้เสียบหูฟังหรือถ้าผู้ใช้เข้าใกล้พิกัดที่กำหนดให้เรียก BroadcastReceiver ที่กำหนด
จริงๆ API ทั้งสองชุดก็เข้าถึงข้อมูลชุดเดียวกันนั่นแหละ แต่ตัวนึงเป็นการดึงข้อมูลส่วนอีกตัวเป็นการจับการเปลี่ยนแปลง ทางกูเกิ้ลแยกไว้ให้สื่อสารง่ายขึ้นเท่านั้นเอง
Get Started
Awareness API ถูกบรรจุอยู่ใน Google Play Services 9.2 เป็นต้นไป การจะใช้งาน API เหล่านี้เราเลยต้องดึง GMS เข้ามาในโปรเจคด้วย เริ่มต้นจากการสร้างโปรเจคใน Google Developer Console ก่อน
1) สร้าง Project ขึ้นมาใน https://console.developers.google.com (หรือจะใช้โปรเจคตัวเดิมที่มีอยู่แล้วก็ได้)
2) เข้าหน้า API Manager และค้นหาคำว่า Awareness และกดที่ Awareness API
3) กด Enable
4) ไปที่หน้า Credentials แล้วกด Create credentials -> API key -> Android key ใส่ชื่อที่ต้องการ (เช่น Android Key) แล้วกด Create ได้เลย (หรือถ้ามี Android key ที่ถูกสร้างไว้อยู่แล้วก่อนหน้านี้ก็ข้ามขั้นตอนนี้แล้วใช้รหัสเก่าได้เลย)
5) จากนั้นจะได้ API key ที่หน้าตาประมาณว่า AIzaSyBdVl-cTICSwYKrZ95LoVuw7dbMuDt1KG0
ให้ก็อปเก็บไว้ เราจะใช้มันในโปรเจคแอนดรอยด์ครับ
6) เปิด Android SDK Manager แล้วอัปเดต Google Play Services และ Google Repository เป็นเวอร์ชันล่าสุด
7) สลับมาที่ Android Studio สร้างโปรเจคใหม่ขึ้นมาให้เรียบร้อยแล้วเปิดไฟล์ build.gradle
ของโมดูลแอปที่ต้องการแล้วใส่ dependency ไปว่า
dependencies {
compile 'com.google.android.gms:play-services-contextmanager:9.2.0'
}
แล้ว Sync Gradle ให้เรียบร้อย
7) เปิดไฟล์ AndroidManifest.xml
แล้วเพิ่ม meta-data เข้าไปใน <application>
tag ดังนี้
<meta-data
android:name="com.google.android.awareness.API_KEY"
android:value="YOUR_KEY" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_KEY" />
<meta-data
android:name="com.google.android.nearby.messages.API_KEY"
android:value="YOUR_KEY" />
และอย่าลืมแทน YOUR_KEY ด้วย API key ที่ได้ด้านบนด้วย
โดยตัวแรกให้ใส่เสมอ ส่วนตัวที่สองเอาไว้ใช้ในการเข้าถึง Place (สถานที่) และตัวสุดท้ายเอาไว้เข้าถึง Beacon หากไม่ได้ใช้ตัวไหนสามารถเอาออกได้
8) เพิ่ม Permission ในไฟล์ AndroidManifest.xml
ตัวเดิมไว้สองตัวภายใน <manifest>
tag
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
โดยตัวบนเอาไว้เพื่อเข้าถึงพิกัด GPS และตัวล่างเอาไว้ตรวจจับการเคลื่อนไหวของผู้ใช้
9) ใน MainActivity.java
ให้ทำการ Initialize GoogleApiClient เพื่อให้เรียกใช้ Google Play Services ได้ โดยใส่โค้ดไว้ใน onCreate
ดังนี้
public class MainActivity extends AppCompatActivity {
private GoogleApiClient mGoogleApiClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGoogleApiClient = new GoogleApiClient.Builder(MainActivity.this)
.addApi(Awareness.API)
.build();
mGoogleApiClient.connect();
}
}
เป็นอันเรียบร้อย โปรเจคของท่านพร้อมใช้งาน Awareness API แล้วครับ จากนี้จะเป็นการทดสอบการเรียกใช้ API แต่ละตัวซึ่งการรันคำสั่งเหล่านี้เครื่องที่ใช้ทดสอบจำเป็นต้องมี Google Play Services เวอร์ชั่น 9.2 เป็นต้นไป ให้อัปเดต Google Play Services บนเครื่องทดสอบหรืออีมูเลเตอร์เป็นตัวล่าสุดด้วยไม่งั้นจะรันไม่ได้ครับ
Snapshot API
เริ่มต้นจากสิ่งง่ายๆอย่างการ "ดึงข้อมูลมาดู" ก่อน ซึ่งในที่นี้ก็คือ Snapshot API นั่นเอง วางโครงโค้ดไว้ก่อนดังนี้
private static final String TAG = "Awareness";
@Override
protected void onCreate(Bundle savedInstanceState) {
...
initSnapshots();
}
private void initSnapshots() {
}
จากนั้นโค้ดทั้งหมดจากนี้ให้ใส่ไว้ในคำสั่ง initSnapshots()
นะครับ จะได้ทำงานทันทีตอนที่ Activity ถูกเปิดมา
โดยโค้ดในกลุ่ม Snapshot API จะเรียกผ่านคลาส Awareness.SnapshotApi
กันครับ เริ่มเลยละกันนะ
Activity
เริ่มจากการทดสอบ "การเคลื่อนไหว" (Activity) ก่อนเลย นี่คือโค้ดครับ
Awareness.SnapshotApi.getDetectedActivity(mGoogleApiClient)
.setResultCallback(new ResultCallback<DetectedActivityResult>() {
@Override
public void onResult(@NonNull DetectedActivityResult detectedActivityResult) {
if (!detectedActivityResult.getStatus().isSuccess()) {
Log.e(TAG, "Could not get the current activity.");
return;
}
ActivityRecognitionResult ar = detectedActivityResult.getActivityRecognitionResult();
DetectedActivity probableActivity = ar.getMostProbableActivity();
Log.i(TAG, probableActivity.toString());
}
});
ก็ค่อนข้างตรงไปตรงมามาก เรียก getDetectedActivity
แล้วรอผลลัพธ์กลับมาทาง Callback แล้วก็เช็คว่าสำเร็จมั้ย ถ้าสำเร็จก็ดึงผลลัพธ์มาแสดงผล นี่คือตัวอย่างผลลัพธ์จากบรรทัด Log.i
ครับ
I/Awareness: DetectedActivity [type=STILL, confidence=100]
ก็จะเห็นว่าเราสามารถเช็คได้ว่าผู้ใช้กำลังเคลื่อนไหวแบบไหนอยู่ โดยเราสามารถเช็คจากคำสั่ง probableActivity.getType()
ซึ่งจะได้ผลกลับมาเป็น int ค่าตามนี้
public static final int IN_VEHICLE = 0;
public static final int ON_BICYCLE = 1;
public static final int ON_FOOT = 2;
public static final int STILL = 3;
public static final int UNKNOWN = 4;
public static final int TILTING = 5;
public static final int WALKING = 7;
public static final int RUNNING = 8;
นอกจากนั้นยังสามารถเช็คความมั่นใจของผลลัพธ์ได้จาก probableActivity.getConfidence()
โดยค่าจะอยู่ในช่วง 0-100 ครับ
คำสั่งอื่นๆก็จะเรียกในรูปแบบเดียวกันนี้ งั้นไปตัวต่อไปเลยละกัน
Headphones
ตรวจเช็คสถานะการเสียบหูฟัง โค้ดตามนี้ครับ
Awareness.SnapshotApi.getHeadphoneState(mGoogleApiClient)
.setResultCallback(new ResultCallback<HeadphoneStateResult>() {
@Override
public void onResult(@NonNull HeadphoneStateResult headphoneStateResult) {
if (!headphoneStateResult.getStatus().isSuccess()) {
Log.e(TAG, "Could not get headphone state.");
return;
}
HeadphoneState headphoneState = headphoneStateResult.getHeadphoneState();
if (headphoneState.getState() == HeadphoneState.PLUGGED_IN) {
Log.i(TAG, "Headphones are plugged in.\n");
} else {
Log.i(TAG, "Headphones are NOT plugged in.\n");
}
}
});
ทุกอย่างอยู่ในรูปแบบเดิมและเช็คผลลัพธ์ว่าเสียบหูฟังหรือไม่ผ่านคำสั่ง headphoneState.getState()
โดยเป็นไปได้สองค่าด้วยกัน ได้แก่ HeadphoneState.PLUGGED_IN
และ HeadphoneState.UNPLUGGED
ส่วนความหมายก็ตรงตัวครับ
Location
ตรวจสอบพิกัด Lat/Long โค้ดตามนี้ครับ
Awareness.SnapshotApi.getLocation(mGoogleApiClient)
.setResultCallback(new ResultCallback<LocationResult>() {
@Override
public void onResult(@NonNull LocationResult locationResult) {
if (!locationResult.getStatus().isSuccess()) {
Log.e(TAG, "Could not get location.");
return;
}
Location location = locationResult.getLocation();
Log.i(TAG, "Lat: " + location.getLatitude() + ", Lon: " + location.getLongitude());
}
});
สามารถดึงค่า Lat/Long จากคำสั่ง location.getLatitude()
และ location.getLongitude()
นอกจากนั้นยังสามารถดึงค่าอื่นๆเช่น Altitude และมีคำสั่งสำหรับคำนวณต่างๆอีกหลายตัว สามารถอ่านเพิ่มเติมได้จากเอกสารของคลาส Location ครับ
* สำหรับการเรียกใช้คำสั่ง Location บน Android M เป็นต้นไป เราจำเป็นต้องใส่โค้ด Runtime Permission เพื่อขออนุญาตผู้ใช้ในการเข้าถึง GPS ด้วย Permission ACCESS_FINE_LOCATION ก่อนด้วย โดยโค้ดการขอ Permission หน้าตาจะเป็นประมาณนี้
if (ContextCompat.checkSelfPermission(
MainActivity.this,
android.Manifest.permission.ACCESS_FINE_LOCATION) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
MainActivity.this,
new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
12345
);
}
ให้ Implement ให้เรียบร้อยมิฉะนั้นจะใช้งานไม่ได้ครับ สำหรับเรื่องของ Runtime Permission สามารถอ่านเพิ่มได้จากบล็อก สิ่งที่นักพัฒนาต้องรู้เกี่ยวกับระบบ Runtime Permission ใหม่ของแอนดรอยด์ รีบปรับโค้ดก่อนจะสายเกินไป
Places
อันนี้เป็นการขอสถานที่รอบตัวครับ ซึ่งการเข้าถึง Places เราจะต้องไป Enable Google Places for Android ใน Google Developer Console ก่อนให้เรียบร้อยด้วย จากนั้นโค้ดตัวอย่างนี้จะลิสต์ผลลัพธ์ 5 ที่แรกมาให้ (ถ้ามี)
Awareness.SnapshotApi.getPlaces(mGoogleApiClient)
.setResultCallback(new ResultCallback<PlacesResult>() {
@Override
public void onResult(@NonNull PlacesResult placesResult) {
if (!placesResult.getStatus().isSuccess()) {
Log.e(TAG, "Could not get places.");
return;
}
List<PlaceLikelihood> placeLikelihoodList = placesResult.getPlaceLikelihoods();
// Show the top 5 possible location results.
if (placeLikelihoodList != null) {
for (int i = 0; i < 5 && i < placeLikelihoodList.size(); i++) {
PlaceLikelihood p = placeLikelihoodList.get(i);
Log.i(TAG, p.getPlace().getName().toString() + ", likelihood: " + p.getLikelihood());
}
} else {
Log.e(TAG, "Place is null.");
}
}
});
ซึ่งเราสามารถดึงรายละเอียดของสถานที่จาก p.getPlace()
ซึ่งมีอยู่หลายอย่างเช่น getAddress()
สำหรับการดึงที่อยู่ getPhoneNumber(
) สำหรับดึงเบอร์โทร getPlaceTypes()
เพื่อดูว่าสถานที่นั้นๆเป็นสถานที่ประเภทไหน เป็นต้น สำหรับรายละเอียดเพิ่มเติมให้อ่านเอกสารของคลาส Place ได้ครับ
* สำหรับการดึงค่า Places บน Android M เป็นต้นไป เราจำเป็นต้องใส่โค้ด Runtime Permission เพื่อขออนุญาตผู้ใช้ในการเข้าถึง GPS ด้วย Permission ACCESS_FINE_LOCATION ก่อนด้วย สามารถดูโค้ดจากหัวข้อด้านบนได้
Weather
ดึงสภาพภูมิอากาศของสถานที่ที่ผู้ใช้อยู่ โค้ดตามนี้ครับ
Awareness.SnapshotApi.getWeather(mGoogleApiClient)
.setResultCallback(new ResultCallback<WeatherResult>() {
@Override
public void onResult(@NonNull WeatherResult weatherResult) {
if (!weatherResult.getStatus().isSuccess()) {
Log.e(TAG, "Could not get weather.");
return;
}
Weather weather = weatherResult.getWeather();
Log.i(TAG, "Weather: " + weather);
}
});
ซึ่งเราสามารถดึงผลลัพธ์ของสภาพอากาศได้หลายอย่างได้แก่
getTemperature()
เพื่อดึงค่าอุณหภูมิจริงออกมา
getFeelsLikeTemperature()
เพื่อดึงค่าอุณหภูมิที่รู้สึกได้
getHumidity()
ดึงค่าความชื้น
getDewPoint()
ดึงอุณหภูมิที่ไอน้ำเริ่มควบแน่น
getConditions()
ดูว่าสภาพอากาศโดยรวมเป็นอย่างไร เช่น Clear, Cloudy, Foggy เป็นต้น
สามารถอ่านเอกสารเพิ่มเติมโดยละเอียดจากคลาส Weather ได้ครับ
* สำหรับการดึงค่า Weathers บน Android M เป็นต้นไป เราจำเป็นต้องใส่โค้ด Runtime Permission เพื่อขออนุญาตผู้ใช้ในการเข้าถึง GPS ด้วย Permission ACCESS_FINE_LOCATION ก่อนด้วย สามารถดูโค้ดจากหัวข้อด้านบนได้
Beacon
ตรวจสอบสถานะของ Beacon ที่อยู่บริเวณนั้น โดยการเรียกใช้ API นี้เราจะต้องไป Enable Nearby Messages API ใน Google Developer Console ก่อนให้เรียบร้อย จากนั้นอย่างแรกที่ต้องทำคือประกาศรายละเอียดของ Beacon ทั้งหมดดังนี้
List BEACON_TYPE_FILTERS = Arrays.asList(
BeaconState.TypeFilter.with(
"my.beacon.namespace",
"my-attachment-type"),
BeaconState.TypeFilter.with(
"my.other.namespace",
"my-attachment-type"));
จากนั้นก็ดึงสถานะผ่านคำสั่ง getBeaconState
Awareness.SnapshotApi.getBeaconState(mGoogleApiClient, BEACON_TYPE_FILTERS)
.setResultCallback(new ResultCallback<BeaconStateResult>() {
@Override
public void onResult(@NonNull BeaconStateResult beaconStateResult) {
if (!beaconStateResult.getStatus().isSuccess()) {
Log.e(TAG, "Could not get beacon state.");
return;
}
BeaconState beaconState = beaconStateResult.getBeaconState();
// Get info from the BeaconState.
}
});
สามารถดูผล State ของ Beacon แต่ละตัวผ่านคำสั่ง beaconState.getBeaconInfo()
ได้ทันที สามารถอ่านรายละเอียดของคลาส BeaconInfo ได้จาก BeaconState.BeaconInfo ครับ
* สำหรับการดึงค่า Places บน Android M เป็นต้นไป เราจำเป็นต้องใส่โค้ด Runtime Permission เพื่อขออนุญาตผู้ใช้ในการเข้าถึง GPS ด้วย Permission ACCESS_FINE_LOCATION ก่อนด้วย สามารถดูโค้ดจากหัวข้อด้านบนได้
Time
สำหรับเวลาไม่มีอยู่ใน Snapshot API เพราะสามารถเรียกผ่านคำสั่งพื้นฐานของแอนดรอยด์ได้
ก็เป็นอันเสร็จเรียบร้อยสำหรับ Snapshot API จะเห็นว่าทุกอย่างตรงไปตรงมาและเรียบง่ายแต่ก็มีประโยชน์มากครับ ลองเล่นดูได้
Fence API
สำหรับ Fence เป็นการเล่นคำมาจาก Geofencing ซึ่งเป็นการส่งสัญญาณบอกว่าแอปเราเมื่อผู้ใช้อยู่ภายในบริเวณที่ระบุ แต่ใน Fence API ทำได้มากกว่านั้นก็คือสามารถตรวจจับการเปลี่ยนแปลงสถานะของ Context ที่ Awareness API ใช้งานได้ เรามาลองกันเลยละกันนะ
ก่อนอื่นก็วางโครงก่อน โดยกลไกการรับสัญญาณกลับมาเมื่อมีการเปลี่ยนแปลงของสถานะเราจะรับด้วย BroadcastReceiver ผ่านทาง Intent ซึ่งถูกฝากไว้ใน PendingIntent ดังนั้นเราขอสร้าง BroadcastReceiver, Intent และ PendingIntent ไว้ดังนี้ครับ
public class MainActivity extends AppCompatActivity {
private static final String FENCE_RECEIVER_ACTION = "FENCE_RECEIVE";
private HeadphoneFenceBroadcastReceiver fenceReceiver;
private PendingIntent mFencePendingIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
fenceReceiver = new HeadphoneFenceBroadcastReceiver();
Intent intent = new Intent(FENCE_RECEIVER_ACTION);
mFencePendingIntent = PendingIntent.getBroadcast(MainActivity.this,
10001,
intent,
0);
}
private void registerFences() {
// Create a fence.
}
private void unregisterFence() {
}
@Override
protected void onStart() {
super.onStart();
registerFences();
registerReceiver(fenceReceiver, new IntentFilter(FENCE_RECEIVER_ACTION));
}
@Override
protected void onStop() {
super.onStop();
unregisterFences();
unregisterReceiver(fenceReceiver);
}
class HeadphoneFenceBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
}
}
จากนั้นโค้ดส่วนการขอรับสถานะการเปลี่ยนแปลงเราจะใส่ไว้ใน registerFences()
และการยกเลิกจะใส่ไว้ใน unregisterFences()
กันครับ
ตรวจจับการเปลี่ยนแปลงสถานะการเสียบหูฟัง
เราจะขอทดสอบกับตัวอย่างที่ง่ายที่สุดถ้างั้นขอเป็นเรื่องการตรวจสอบการเปลี่ยนแปลง "สถานะการเสียบหูฟัง" (Headphone State) ละกันนะครับ ให้ใส่โค้ดใน registerFences()
ไว้ดังนี้ เริ่มต้นจากการประกาศ AwarenessFence เพื่อบอกว่าเราต้องการจะตรวจจับอะไร ยกตัวอย่างเช่นหากเราต้องการตรวจจับการเปลี่ยนสถานะการเสียบหูฟังให้สร้างผ่านคลาส HeadphoneFence
ดังนี้
AwarenessFence headphoneFence = HeadphoneFence.during(HeadphoneState.PLUGGED_IN);
จากนั้นให้สั่ง Awareness.FenceApi.updateFences(...)
เพื่อร้องขอการรับการเปลี่ยนแปลงสถานะดังนี้
Awareness.FenceApi.updateFences(
mGoogleApiClient,
new FenceUpdateRequest.Builder()
.addFence("headphoneFenceKey", headphoneFence, mFencePendingIntent)
.build())
.setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(@NonNull Status status) {
if (status.isSuccess()) {
Log.i(TAG, "Fence was successfully registered.");
} else {
Log.e(TAG, "Fence could not be registered: " + status);
}
}
});
โดยบรรทัดสำคัญคือ .addFence(...)
หากมีการเปลี่ยนแปลงตาม AwarenessFence ที่กำหนด (ในที่นี้คือ headphoneFence
) ตัว PendingIntent ที่กำหนด (ในที่นี้คือ mFencePendingIntent
) จะถูกยิงออกมาและ BroadcastReceiver (ในที่นี้คือ fenceReceiver
) ที่ผูกไว้ก็จะถูกเรียก
และเมื่อมีการ register ก็ต้องมีการ unregister เพื่อไม่ให้เกิด Memory Leak ดังนั้นในคำสั่ง unregisterFences()
ให้ใส่โค้ดไว้ดังนี้
private void unregisterFences() {
Awareness.FenceApi.updateFences(
mGoogleApiClient,
new FenceUpdateRequest.Builder()
.removeFence("headphoneFenceKey")
.build()).setResultCallback(new ResultCallbacks<Status>() {
@Override
public void onSuccess(@NonNull Status status) {
Log.i(TAG, "Fence " + "headphoneFenceKey" + " successfully removed.");
}
@Override
public void onFailure(@NonNull Status status) {
Log.i(TAG, "Fence " + "headphoneFenceKey" + " could NOT be removed.");
}
});
}
เมื่อการ register/unregister เสร็จแล้ว ต่อไปเรามา Handle ที่ HeadphoneFenceBroadcastReceiver
กันต่อ เพิ่มโค้ดในส่วน onReceive
ตามนี้ครับ
class HeadphoneFenceBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
FenceState fenceState = FenceState.extract(intent);
Log.d(TAG, "Fence Receiver Received");
if (TextUtils.equals(fenceState.getFenceKey(), "headphoneFenceKey")) {
switch (fenceState.getCurrentState()) {
case FenceState.TRUE:
Log.i(TAG, "Fence > Headphones are plugged in.");
break;
case FenceState.FALSE:
Log.i(TAG, "Fence > Headphones are NOT plugged in.");
break;
case FenceState.UNKNOWN:
Log.i(TAG, "Fence > The headphone fence is in an unknown state.");
break;
}
}
}
}
หลักการทำงานค่อนข้างตรงไปตรงมา เมื่อมีการเปลี่ยนแปลงตาม Fence ที่กำหนด onReceive
จะถูกเรียกและเราสามารถดึงผลลัพธ์จากคำสั่ง
FenceState fenceState = FenceState.extract(intent);
และจากนั้นก็เช็ค Key ว่าเป็นการเปลี่ยนแปลงสถานะของอะไรด้วยคำสั่ง
if (TextUtils.equals(fenceState.getFenceKey(), "headphoneFenceKey")) {
โดย Key ที่ว่านี้เป็น Key ที่เรากำหนดไว้ตรงคำสั่ง addFence
นั่นเอง
จากนั้นก็เช็คเงื่อนไขจาก fenceState.getFenceState()
ว่าเงื่อนไขที่กำหนดเป็น TRUE หรือเปล่าตามโค้ดด้านบนนั่นเอง
ทดสอบ
ตอนนี้โค้ดด้านบนสามารถใช้งานได้แล้ว ลองกดรันแล้วดูผลการทำงานได้เลย โดยอย่างแรกให้ลองเสียบหูฟังเข้าไปในช่องหูฟัง ในช่อง Logcat จะขึ้นดังนี้
D/Awareness: Fence Receiver Received
I/Awareness: Fence > Headphones are plugged in.
และถ้าถอดหูฟังก็จะขึ้นดังนี้
D/Awareness: Fence Receiver Received
I/Awareness: Fence > Headphones are NOT plugged in.
เรียบร้อยครับ ใช้งานได้แล้ว ง่ายใช่ม้า =D
Fence อื่นๆ
เราสามารถตรวจสอบสถานะของ Context ได้ 5 อย่าง โดยใช้คลาสดังต่อไปนี้
DetectedActivityFence - ตรวจจับการเปลี่ยนแปลงรูปแบบการเคลื่อนไหวของผู้ใช้
HeadphoneFence - ตรวจจับการเปลี่ยนแปลงสถานะการเสียบหูฟัง
TimeFence - ตรวจจับเวลาเมื่ออยู่ในช่วงที่กำหนด
LocationFence - ตรวจจับว่าผู้ใช้เข้าหรือออกพิกัดที่กำหนด
BeaconFence - ตรวจจับสถานะการเจอหรือไม่เจอ Beacon ที่กำหนด
สำหรับ Fence แต่ละตัวจะมีคำสั่งให้เลือกเยอะพอสมควร สามารถกดลิงก์แต่ละตัวดูได้เลยครับ เขียนได้ไม่หมดจริงๆ เยอะมาก แต่ถ้าแนวคิดได้เข้าใจโค้ดด้านบนแล้วก็จะใช้งานเป็นทุก Fence แล้วครับ
ส่วน Weather กับ Place ไม่มี Fence API ให้ใช้ครับผม (เพราะไม่รู้ว่าจะเปลี่ยนแปลงอะไรนั่นเอง)
การรับเงื่อนไขหลายๆ Fence ร่วมกัน
ในหลายๆกรณีเราต้องการสร้างเงื่อนไข Fence หลายๆอย่างให้ทำงานร่วมกันเช่น
- ถ้าผู้ใช้วิ่งและเสียบหูฟังอยู่
- ถ้าผู้ใช้เดินเข้าพื้นที่ที่กำหนดและเป็นเวลาช่วงบ่าย
ตรงนี้ทำได้ไม่ยากเพราะเราสามารถสร้าง AwarenessFence
หลายๆตัวขึ้นมา เช่น
AwarenessFence headphoneFence = HeadphoneFence.during(HeadphoneState.PLUGGED_IN);
AwarenessFence detectedActivityFence = DetectedActivityFence.during(DetectedActivityFence.RUNNING);
และทำการ And กันด้วยคำสั่ง AwarenessFence.and
ดังนี้
AwarenessFence andFence = AwarenessFence.and(headphoneFence, detectedActivityFence);
แล้วตอนสั่ง updateFences ให้โยน andFence ตัวนี้เข้าไปแทนก็เป็นอันเรียบร้อยครับ
ซึ่งนอกจาก and แล้วก็ยังมี AwarenessFence.or
และ AwarenessFence.not
ให้ใช้งานอีกด้วย สามารถประยุกต์ใช้ตามลอจิคที่ต้องการได้เลยครับ
Use Case
มีแอปบางตัวเริ่มเอา Awareness API ไปใช้จริงแล้ว ยกตัวอย่างเช่น
Superplayer Music ที่สามารถแนะนำเพลงจากสภาพภูมิอากาศได้ (ฟังเพลงเศร้าๆตอนฝนตกมันจะอินใช่ม้า)
Trulia แอปของเว็บขายบ้านที่สามารถเด้ง Notification บอกผู้ใช้ได้ทันทีที่ผู้ใช้เดินเข้าใกล้บริเวณบ้านที่มีการประกาศขายและอากาศดีพอจะแวะดูได้
ก็ลองประยุกต์ใช้ในแอปของท่านได้ครับ เมื่อเล่นกับบริบทของผู้ใช้ได้แอปจะเท่ขึ้นเยอะมาก
สรุป
ก็เป็นอันเรียบร้อย Awareness API ถือว่าเป็น API ที่มีประโยชน์มากต่อการเพิ่มฟีเจอร์เจ๋งๆให้แอปเราด้วยโค้ดเพียงไม่กี่บรรทัด ข้อจำกัดเดียวก็คือ API จะรันบนเครื่องที่มี Google Play Services เท่านั้น เครื่องจีนจะทำงานไม่ได้ ซึ่งถ้าไม่แคร์อะไรก็สามารถใช้งานได้ครับ
ก็หวังว่าบล็อกนี้จะมีประโยชน์ครับ ขอให้มีความสุขกับการเล่นกับ Context ของผู้ใช้จ้า =D
ผู้เขียน: nuuneoi (Android GDE, CTO & CEO at The Cheese Factory) นักพัฒนาแบบ Full-Stack ที่มีประสบการณ์ในการพัฒนาแอพฯแอนดรอยด์มากว่า 6 ปีและอยู่ในวงการพัฒนาแอพฯมือถือมากว่า 12 ปี มีความสนใจทางด้าน Infrastucture, Service Side, Design, UI&UX, Hardware, Optimization, Cooking, Photographing, Blogging, Training, Public Speaking และรักที่จะแชร์เรื่องราวให้ผู้คนได้อ่านได้ฟังกันผ่าน Blog
|