1984e52f31d596840cfa51b1238e1c43d2e1918f8saberian/*
2984e52f31d596840cfa51b1238e1c43d2e1918f8saberian * Copyright (C) 2012 The Android Open Source Project
3984e52f31d596840cfa51b1238e1c43d2e1918f8saberian *
4984e52f31d596840cfa51b1238e1c43d2e1918f8saberian * Licensed under the Apache License, Version 2.0 (the "License");
5984e52f31d596840cfa51b1238e1c43d2e1918f8saberian * you may not use this file except in compliance with the License.
6984e52f31d596840cfa51b1238e1c43d2e1918f8saberian * You may obtain a copy of the License at
7984e52f31d596840cfa51b1238e1c43d2e1918f8saberian *
8984e52f31d596840cfa51b1238e1c43d2e1918f8saberian *      http://www.apache.org/licenses/LICENSE-2.0
9984e52f31d596840cfa51b1238e1c43d2e1918f8saberian *
10984e52f31d596840cfa51b1238e1c43d2e1918f8saberian * Unless required by applicable law or agreed to in writing, software
11984e52f31d596840cfa51b1238e1c43d2e1918f8saberian * distributed under the License is distributed on an "AS IS" BASIS,
12984e52f31d596840cfa51b1238e1c43d2e1918f8saberian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13984e52f31d596840cfa51b1238e1c43d2e1918f8saberian * See the License for the specific language governing permissions and
14984e52f31d596840cfa51b1238e1c43d2e1918f8saberian * limitations under the License.
15984e52f31d596840cfa51b1238e1c43d2e1918f8saberian */
16984e52f31d596840cfa51b1238e1c43d2e1918f8saberian
17984e52f31d596840cfa51b1238e1c43d2e1918f8saberianpackage android.bordeaux.services;
18984e52f31d596840cfa51b1238e1c43d2e1918f8saberian
1947c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Linimport android.app.AlarmManager;
2047c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Linimport android.app.PendingIntent;
2147c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Linimport android.content.BroadcastReceiver;
22f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport android.content.Context;
2347c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Linimport android.content.Intent;
2447c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Linimport android.content.IntentFilter;
25f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport android.location.Criteria;
26f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport android.location.Location;
27f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport android.location.LocationListener;
28f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport android.location.LocationManager;
29f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport android.location.LocationProvider;
30f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport android.os.Bundle;
31f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport android.os.Handler;
32f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport android.os.HandlerThread;
33f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport android.os.Message;
34f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linimport android.os.Process;
3547c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Linimport android.os.SystemClock;
3647c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Linimport android.text.format.Time;
37984e52f31d596840cfa51b1238e1c43d2e1918f8saberianimport android.util.Log;
38984e52f31d596840cfa51b1238e1c43d2e1918f8saberianimport java.util.HashMap;
399c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Huaimport java.util.List;
40984e52f31d596840cfa51b1238e1c43d2e1918f8saberianimport java.util.Map;
41984e52f31d596840cfa51b1238e1c43d2e1918f8saberian
425d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin// TODO: add functionality to detect speed (use GPS) when needed
435d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin// withouth draining the battery quickly
44f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Linpublic class LocationStatsAggregator extends Aggregator {
45984e52f31d596840cfa51b1238e1c43d2e1918f8saberian    final String TAG = "LocationStatsAggregator";
46984e52f31d596840cfa51b1238e1c43d2e1918f8saberian    public static final String CURRENT_LOCATION = "Current Location";
475d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    public static final String CURRENT_SPEED = "Current Speed";
48c7c9cf164cc58d03156a53be35e58c3b75871a23Ruei-sung Lin    public static final String UNKNOWN_LOCATION = "Unknown Location";
495d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
5047c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin    private static final long REPEAT_INTERVAL = 120000;
515d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
5247c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin    private static final long FRESH_THRESHOLD = 90000;
53f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
545d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    private static final int LOCATION_CHANGE = 1;
55f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
565d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    // record time when the location provider is set
57f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    private long mProviderSetTime;
58f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
59f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    private Handler mHandler;
60f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    private HandlerThread mHandlerThread;
6147c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin    private AlarmManager mAlarmManager;
62f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    private LocationManager mLocationManager;
6347c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
64f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    private ClusterManager mClusterManager;
65f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
6647c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin    private Criteria mCriteria = new Criteria();
6747c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
6847c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin    private LocationUpdater mLocationUpdater;
6947c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
7047c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin    private Context mContext;
7147c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin    private PendingIntent mPendingIntent;
7247c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
739c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua    // Fake location, used for testing.
749c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua    private String mFakeLocation = null;
759c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua
76f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    public LocationStatsAggregator(final Context context) {
775d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        mLocationManager =
785d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin            (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
7947c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        mAlarmManager =
8047c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin            (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
8147c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
825d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        setClusteringThread(context);
8347c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
8447c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        mCriteria.setAccuracy(Criteria.ACCURACY_COARSE);
8547c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        mCriteria.setPowerRequirement(Criteria.POWER_LOW);
8647c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        /*
8747c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        mCriteria.setAltitudeRequired(false);
8847c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        mCriteria.setBearingRequired(false);
8947c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        mCriteria.setSpeedRequired(true);
9047c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        */
9147c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        mCriteria.setCostAllowed(true);
9247c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
9347c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
9447c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        IntentFilter filter = new IntentFilter(LocationUpdater.LOCATION_UPDATE);
9547c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        mLocationUpdater = new LocationUpdater();
9647c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        context.registerReceiver(mLocationUpdater, filter);
9747c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
9847c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        Intent intent = new Intent(LocationUpdater.LOCATION_UPDATE);
9947c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
10047c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        mContext = context;
10147c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        mPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
10247c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
10347c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
10447c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                                          SystemClock.elapsedRealtime() + 30000, //
10547c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                                          REPEAT_INTERVAL,
10647c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                                          mPendingIntent);
10747c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin    }
10847c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
10947c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin    public void release() {
11047c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        mContext.unregisterReceiver(mLocationUpdater);
11147c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        mAlarmManager.cancel(mPendingIntent);
112f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    }
113f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
114984e52f31d596840cfa51b1238e1c43d2e1918f8saberian    public String[] getListOfFeatures(){
1155d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        String[] list = { CURRENT_LOCATION } ;
116984e52f31d596840cfa51b1238e1c43d2e1918f8saberian        return list;
117984e52f31d596840cfa51b1238e1c43d2e1918f8saberian    }
118f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
119984e52f31d596840cfa51b1238e1c43d2e1918f8saberian    public Map<String,String> getFeatureValue(String featureName) {
120f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        HashMap<String,String> feature = new HashMap<String,String>();
1215d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin
122f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        if (featureName.equals(CURRENT_LOCATION)) {
12378a66d98346a69f65e9d38bb0c96a5418c007197Ruei-sung Lin            // TODO: check last known location first before sending out location request.
12478a66d98346a69f65e9d38bb0c96a5418c007197Ruei-sung Lin            /*
12578a66d98346a69f65e9d38bb0c96a5418c007197Ruei-sung Lin              Location location =
12678a66d98346a69f65e9d38bb0c96a5418c007197Ruei-sung Lin              mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
12778a66d98346a69f65e9d38bb0c96a5418c007197Ruei-sung Lin            */
128c7c9cf164cc58d03156a53be35e58c3b75871a23Ruei-sung Lin            String location = mClusterManager.getSemanticLocation();
129c7c9cf164cc58d03156a53be35e58c3b75871a23Ruei-sung Lin            if (!location.equals(UNKNOWN_LOCATION)) {
1309c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua                if (mFakeLocation != null) {
1319c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua                    feature.put(CURRENT_LOCATION, mFakeLocation);
1329c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua                } else {
1339c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua                    feature.put(CURRENT_LOCATION, location);
1349c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua                }
135c7c9cf164cc58d03156a53be35e58c3b75871a23Ruei-sung Lin            }
136f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        }
137f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        return (Map) feature;
138984e52f31d596840cfa51b1238e1c43d2e1918f8saberian    }
139f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
1409c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua    public List<String> getClusterNames() {
1419c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua        return mClusterManager.getClusterNames();
1429c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua    }
1439c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua
1449c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua    // set a fake location using cluster name.
1459c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua    // Set an empty string "" to disable the fake location
1469c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua    public void setFakeLocation(String name) {
1479c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua        if (name != null && name.length() != 0)
1489c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua            mFakeLocation = name;
1499c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua        else mFakeLocation = null;
1509c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua    }
1519c3a7dc466e2f8de02e15030b2b7f4096ba97e5aWei Hua
15247c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin    private Location getLastKnownLocation() {
15347c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        List<String> providers = mLocationManager.getAllProviders();
15447c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        Location bestResult = null;
15547c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        float bestAccuracy = Float.MAX_VALUE;
15647c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        long bestTime;
15747c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
15847c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        // get the latest location data
15947c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        long currTime =  System.currentTimeMillis();
16047c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        for (String provider : providers) {
16147c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin            Location location = mLocationManager.getLastKnownLocation(provider);
16247c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
16347c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin            if (location != null) {
16447c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                float accuracy = location.getAccuracy();
16547c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                long time = location.getTime();
16647c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
16747c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                if (currTime - time < FRESH_THRESHOLD && accuracy < bestAccuracy) {
16847c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                    bestResult = location;
16947c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                    bestAccuracy = accuracy;
17047c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                    bestTime = time;
17147c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                }
17247c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin            }
17347c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        }
17447c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        if (bestResult != null) {
17547c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin            Log.i(TAG, "found location for free: " + bestResult);
17647c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        }
17747c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        return bestResult;
17847c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin    }
17947c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
18047c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin    private class LocationUpdater extends BroadcastReceiver {
18147c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        String TAG = "LocationUpdater";
18247c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
18347c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        public static final String LOCATION_UPDATE = "android.bordeaux.services.LOCATION_UPDATE";
18447c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
18547c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        @Override
18647c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        public void onReceive(Context context, Intent intent) {
18747c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin            Location location = getLastKnownLocation();
18847c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
18947c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin            if (location == null) {
19047c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                String provider = mLocationManager.getBestProvider(mCriteria, true);
19147c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                Log.i(TAG, "Best Available Location Provider: " + provider);
19247c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                mLocationManager.requestSingleUpdate(provider, mLocationListener,
19347c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                                                     mHandlerThread.getLooper());
19447c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin            } else {
19547c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin                mHandler.sendMessage(mHandler.obtainMessage(LOCATION_CHANGE, location));
19647c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin            }
19747c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        }
19847c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin    }
19947c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
2005d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin    private void setClusteringThread(Context context) {
2015d42ffa9462f87edbbdc61a8719f6c521c700de5Ruei-sung Lin        mClusterManager = new ClusterManager(context);
202f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
203f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        mHandlerThread = new HandlerThread("Location Handler",
204f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin                Process.THREAD_PRIORITY_BACKGROUND);
205f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        mHandlerThread.start();
206f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        mHandler = new Handler(mHandlerThread.getLooper()) {
207f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
208f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin            @Override
209f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin            public void handleMessage(Message msg) {
210f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin                if (!(msg.obj instanceof Location)) {
211f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin                    return;
212f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin                }
213f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin                Location location = (Location) msg.obj;
214f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin                switch(msg.what) {
215f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin                    case LOCATION_CHANGE:
216f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin                        mClusterManager.addSample(location);
217f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin                        break;
218f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin                    default:
219f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin                        super.handleMessage(msg);
220f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin                }
221f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin            }
222f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        };
223f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    }
224f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
225f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    private final LocationListener mLocationListener = new LocationListener() {
22647c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin        private static final String TAG = "LocationListener";
22747c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin
228f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        public void onLocationChanged(Location location) {
229f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin            mHandler.sendMessage(mHandler.obtainMessage(LOCATION_CHANGE, location));
23047c0dc05cde9e9d9cc57e1393429006bf8b23b32Ruei-sung Lin            mLocationManager.removeUpdates(this);
231f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        }
232f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
233f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        public void onStatusChanged(String provider, int status, Bundle extras) { }
234f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
235f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        public void onProviderEnabled(String provider) { }
236f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin
237f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin        public void onProviderDisabled(String provider) { }
238f0f78449e8ab7d63894964c54b6ef390ca9ce044Ruei-sung Lin    };
239984e52f31d596840cfa51b1238e1c43d2e1918f8saberian}
240