定義兩個回呼事件,供定位提供者狀態變更時通知 UI:
package com.nokia.example.location.tourist.model;
public interface ProviderStatusListener {
/** 定位提供者已被選取 */
void providerSelectedEvent();
/** 首次定位更新完成 */
void firstLocationUpdateEvent();
}
TouristData 實作 LocationListener 與 ProximityListener,負責:
package com.nokia.example.location.tourist.model;
import java.util.Enumeration;
import javax.microedition.location.*;
import com.nokia.example.location.tourist.ui.TouristUI;
public class TouristData implements LocationListener, ProximityListener {
private TouristUI touristUI = null;
public static final float PROXIMITY_RADIUS = 100.0f; // 公尺
private Coordinates prevCoordinates = null;
private boolean firstLocationUpdate = false;
private ProviderStatusListener statusListener = null;
// ...
public TouristData(ProviderStatusListener listener) {
statusListener = listener;
ConfigurationProvider config = ConfigurationProvider.getInstance();
// 1. 註冊 LocationListener
LocationProvider provider = config.getSelectedProvider();
if (provider != null) {
provider.setLocationListener(this, -1, 0, 0); // 預設間隔、逾時、最大壽命
}
// 2. 為每個地標註冊 ProximityListener
ControlPoints cp = ControlPoints.getInstance();
Enumeration enumeration = cp.getLandMarks();
if (enumeration != null) {
while (enumeration.hasMoreElements()) {
Landmark lm = (Landmark) enumeration.nextElement();
createProximityListener(lm.getQualifiedCoordinates());
}
}
}
public void createProximityListener(Coordinates coordinates) {
try {
LocationProvider.addProximityListener(this, coordinates, PROXIMITY_RADIUS);
} catch (LocationException e) {
// 資源不足或不支援鄰近監測
}
}
啟動新執行緒解析 Location 物件,提取地址、合格座標、速度並更新 UI:
public void locationUpdated(LocationProvider provider, final Location location) {
if (!firstLocationUpdate) {
firstLocationUpdate = true;
statusListener.firstLocationUpdateEvent();
}
if (touristUI != null) {
new Thread() {
public void run() {
if (location != null && location.isValid()) {
AddressInfo address = location.getAddressInfo();
QualifiedCoordinates coord = location.getQualifiedCoordinates();
float speed = location.getSpeed();
touristUI.setInfo(address, coord, speed);
touristUI.setProviderState("Available");
} else {
touristUI.setProviderState("Not valid location data");
}
touristUI.repaint();
}
}.start();
}
}
public void providerStateChanged(LocationProvider provider, final int newState) {
if (touristUI != null) {
new Thread() {
public void run() {
switch (newState) {
case LocationProvider.AVAILABLE:
touristUI.setProviderState("Available"); break;
case LocationProvider.OUT_OF_SERVICE:
touristUI.setProviderState("Out of service"); break;
case LocationProvider.TEMPORARILY_UNAVAILABLE:
touristUI.setProviderState("Temporarily unavailable"); break;
default:
touristUI.setProviderState("Unknown"); break;
}
touristUI.repaint();
}
}.start();
}
}
當裝置進入地標鄰近範圍時觸發,查找最近地標並顯示資訊:
public void proximityEvent(Coordinates coordinates, Location location) {
if (touristUI != null) {
touristUI.setProviderState("Control point found!");
Landmark lm = ControlPoints.getInstance().findNearestLandMark(coordinates);
if (lm != null) {
touristUI.setInfo(lm.getAddressInfo(), lm.getQualifiedCoordinates(), location.getSpeed());
} else {
touristUI.setInfo(location.getAddressInfo(), location.getQualifiedCoordinates(), location.getSpeed());
}
touristUI.repaint();
}
}
public void monitoringStateChanged(boolean isMonitoringActive) {
if (touristUI != null) {
touristUI.setProximityState(isMonitoringActive ? "Active" : "Off");
touristUI.repaint();
}
}
}
設計重點: 所有 Listener 回呼方法均啟動獨立執行緒處理 UI 更新,確保符合 JSR-179 規範「Listener 必須快速返回」的要求。