/**
* Radius Networks, Inc.
* http://www.radiusnetworks.com
*
* @author David G. Young
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.radiusnetworks.ibeacon;
import android.util.Log;
/**
* This class represents a criteria of fields used to match iBeacons. The strange name
* comes from the iOS implementation, where the idea of a "Region" is also used for a geofence.
* The idea is that a grouping of one or more iBeacons are analogous to a geofence region.
*
* The uniqueId field is used to distinguish this Region in the system. When you set up
* monitoring or ranging based on a Region and later want to stop monitoring or ranging,
* you must do so by passing a Region object that has the same uniqueId field value. If it
* doesn't match, you can't cancel the operation. There is no other purpose to this field.
*
* The other fields: proximityUuid, major and minor are a three part unique identifier for
* a single iBeacon. When constructing a range, any or all of these fields may be set to null,
* which indicates that they are a wildcard and will match any value. Note that this differs
* from the iOS implementation that does not let you set a wildcard on the proximityUuid field.
*
* @author dyoung
*
*/
public class Region {
private static final String TAG = "Region";
/**
* Part 2 of 3 of an iBeacon identifier. A 16 bit integer typically used to identify a common grouping of iBeacons.
*/
protected Integer major;
/**
* Part 3 of 3 of an iBeacon identifier. A 16 bit integer typically used to identify an individual iBeacon within a group.
*/
protected Integer minor;
/**
* Part 1 of 3 of an iBeacon identifier. A 26 byte UUID typically used to identify the company that owns a set of iBeacons.
*/
protected String proximityUuid;
/**
* A unique identifier used to later cancel Ranging and Monitoring, or change the region being Ranged/Monitored
*/
protected String uniqueId;
/**
* Constructs a new Region object to be used for Ranging or Monitoring
* @param uniqueId - A unique identifier used to later cancel Ranging and Monitoring, or change the region being Ranged/Monitored
* @param proximityUuid
* @param major
* @param minor
*/
public Region(String uniqueId, String proximityUuid, Integer major, Integer minor) {
this.major = major;
this.minor = minor;
this.proximityUuid = normalizeProximityUuid(proximityUuid);
this.uniqueId = uniqueId;
if (uniqueId == null) {
throw new NullPointerException("uniqueId may not be null");
}
}
/**
* @see #major
* @return major
*/
public Integer getMajor() {
return major;
}
/**
* @see #minor
* @return minor
*/
public Integer getMinor() {
return minor;
}
/**
* @see #proximityUuid
* @return proximityUuid
*/
public String getProximityUuid() {
return proximityUuid;
}
/**
* @see #uniqueId
* @return uniqueId
*/
public String getUniqueId() {
return uniqueId;
}
/**
* Checks to see if an IBeacon object is included in the matching criteria of this Region
* @param iBeacon the iBeacon to check to see if it is in the Region
* @return true if is covered
*/
public boolean matchesIBeacon(IBeacon iBeacon) {
if (proximityUuid != null && !iBeacon.getProximityUuid().equals(proximityUuid)) {
if (IBeaconManager.debug) Log.d(TAG, "unmatching proxmityUuids: "+iBeacon.getProximityUuid()+" != "+proximityUuid);
return false;
}
if (major != null && iBeacon.getMajor() != major) {
if (IBeaconManager.debug) Log.d(TAG, "unmatching major: "+iBeacon.getMajor()+" != "+major);
return false;
}
if (minor != null && iBeacon.getMinor() != minor) {
if (IBeaconManager.debug) Log.d(TAG, "unmatching minor: "+iBeacon.getMajor()+" != "+minor);
return false;
}
return true;
}
protected Region(Region otherRegion) {
major = otherRegion.major;
minor = otherRegion.minor;
proximityUuid = otherRegion.proximityUuid;
uniqueId = otherRegion.uniqueId;
}
protected Region() {
}
@Override
public int hashCode() {
return this.uniqueId.hashCode();
}
public boolean equals(Object other) {
if (other instanceof Region) {
return ((Region)other).uniqueId.equals(this.uniqueId);
}
return false;
}
public String toString() {
return "proximityUuid: "+proximityUuid+" major: "+major+" minor:"+minor;
}
/**
* Puts string to a normalized UUID format, or throws a runtime exception if it contains non-hex digits
* other than dashes or spaces, or if it doesn't contain exactly 32 hex digits
* @param proximityUuid uuid with any combination of upper/lower case hex characters, dashes and spaces
* @return a normalized string, all lower case hex characters with dashes in the form e2c56db5-dffb-48d2-b060-d0f5a71096e0
*/
public static String normalizeProximityUuid(String proximityUuid) {
if (proximityUuid == null) {
return null;
}
String dashlessUuid = proximityUuid.toLowerCase().replaceAll("[\\-\\s]", "");
if (dashlessUuid.length() != 32) {
// TODO: make this a specific exception
throw new RuntimeException("UUID: "+proximityUuid+" is too short. Must contain exactly 32 hex digits, and there are this value has "+dashlessUuid.length()+" digits.");
}
if (!dashlessUuid.matches("^[a-fA-F0-9]*$")) {
// TODO: make this a specific exception
throw new RuntimeException("UUID: "+proximityUuid+" contains invalid characters. Must be dashes, a-f and 0-9 characters only.");
}
StringBuilder sb = new StringBuilder();
sb.append(dashlessUuid.substring(0,8));
sb.append('-');
sb.append(dashlessUuid.substring(8,12));
sb.append('-');
sb.append(dashlessUuid.substring(12,16));
sb.append('-');
sb.append(dashlessUuid.substring(16,20));
sb.append('-');
sb.append(dashlessUuid.substring(20,32));
return sb.toString();
}
@Override
public Object clone() {
return new Region(this);
}
}