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 int mAllowedResolutionLevel;
39    public final int mUid;
40    public final String mPackageName;
41    public final PendingIntent mIntent;
42
43    int mState;  // current state
44    double mDistanceToCenter;  // current distance to center of fence
45
46    public GeofenceState(Geofence fence, long expireAt,
47            int allowedResolutionLevel, int uid, String packageName, PendingIntent intent) {
48        mState = STATE_UNKNOWN;
49        mDistanceToCenter = Double.MAX_VALUE;
50
51        mFence = fence;
52        mExpireAt = expireAt;
53        mAllowedResolutionLevel = allowedResolutionLevel;
54        mUid = uid;
55        mPackageName = packageName;
56        mIntent = intent;
57
58        mLocation = new Location("");
59        mLocation.setLatitude(fence.getLatitude());
60        mLocation.setLongitude(fence.getLongitude());
61    }
62
63    /**
64     * Process a new location.
65     * @return FLAG_ENTER or FLAG_EXIT if the fence was crossed, 0 otherwise
66     */
67    public int processLocation(Location location) {
68        mDistanceToCenter = mLocation.distanceTo(location);
69
70        int prevState = mState;
71        //TODO: inside/outside detection could be made more rigorous
72        boolean inside = mDistanceToCenter <= Math.max(mFence.getRadius(), location.getAccuracy());
73        if (inside) {
74            mState = STATE_INSIDE;
75            if (prevState != STATE_INSIDE) {
76                return FLAG_ENTER; // return enter if previously exited or unknown
77            }
78        } else {
79            mState = STATE_OUTSIDE;
80            if (prevState == STATE_INSIDE) {
81                return FLAG_EXIT; // return exit only if previously entered
82            }
83        }
84        return 0;
85    }
86
87    /**
88     * Gets the distance from the current location to the fence's boundary.
89     * @return The distance or {@link Double#MAX_VALUE} if unknown.
90     */
91    public double getDistanceToBoundary() {
92        if (Double.compare(mDistanceToCenter, Double.MAX_VALUE) == 0) {
93            return Double.MAX_VALUE;
94        } else {
95            return Math.abs(mFence.getRadius() - mDistanceToCenter);
96        }
97    }
98
99    @Override
100    public String toString() {
101        String state;
102        switch (mState) {
103            case STATE_INSIDE:
104                state = "IN";
105                break;
106            case STATE_OUTSIDE:
107                state = "OUT";
108                break;
109            default:
110                state = "?";
111        }
112        return String.format("%s d=%.0f %s", mFence.toString(), mDistanceToCenter, state);
113    }
114}
115