1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package com.android.server.location;
19
20import android.app.PendingIntent;
21import android.location.Geofence;
22import android.location.Location;
23
24/**
25 * Represents state associated with a geofence
26 */
27public class GeofenceState {
28    public final static int FLAG_ENTER = 0x01;
29    public final static int FLAG_EXIT = 0x02;
30
31    private static final int STATE_UNKNOWN = 0;
32    private static final int STATE_INSIDE = 1;
33    private static final int STATE_OUTSIDE = 2;
34
35    public final Geofence mFence;
36    private final Location mLocation;
37    public final long mExpireAt;
38    public final String mPackageName;
39    public final PendingIntent mIntent;
40
41    int mState;  // current state
42    double mDistanceToCenter;  // current distance to center of fence
43
44    public GeofenceState(Geofence fence, long expireAt,
45            String packageName, PendingIntent intent) {
46        mState = STATE_UNKNOWN;
47        mDistanceToCenter = Double.MAX_VALUE;
48
49        mFence = fence;
50        mExpireAt = expireAt;
51        mPackageName = packageName;
52        mIntent = intent;
53
54        mLocation = new Location("");
55        mLocation.setLatitude(fence.getLatitude());
56        mLocation.setLongitude(fence.getLongitude());
57    }
58
59    /**
60     * Process a new location.
61     * @return FLAG_ENTER or FLAG_EXIT if the fence was crossed, 0 otherwise
62     */
63    public int processLocation(Location location) {
64        mDistanceToCenter = mLocation.distanceTo(location);
65
66        int prevState = mState;
67        //TODO: inside/outside detection could be made more rigorous
68        boolean inside = mDistanceToCenter <= Math.max(mFence.getRadius(), location.getAccuracy());
69        if (inside) {
70            mState = STATE_INSIDE;
71            if (prevState != STATE_INSIDE) {
72                return FLAG_ENTER; // return enter if previously exited or unknown
73            }
74        } else {
75            mState = STATE_OUTSIDE;
76            if (prevState == STATE_INSIDE) {
77                return FLAG_EXIT; // return exit only if previously entered
78            }
79        }
80        return 0;
81    }
82
83    /**
84     * Gets the distance from the current location to the fence's boundary.
85     * @return The distance or {@link Double#MAX_VALUE} if unknown.
86     */
87    public double getDistanceToBoundary() {
88        if (Double.compare(mDistanceToCenter, Double.MAX_VALUE) == 0) {
89            return Double.MAX_VALUE;
90        } else {
91            return Math.abs(mFence.getRadius() - mDistanceToCenter);
92        }
93    }
94
95    @Override
96    public String toString() {
97        String state;
98        switch (mState) {
99            case STATE_INSIDE:
100                state = "IN";
101                break;
102            case STATE_OUTSIDE:
103                state = "OUT";
104                break;
105            default:
106                state = "?";
107        }
108        return String.format("%s d=%.0f %s", mFence.toString(), mDistanceToCenter, state);
109    }
110}
111