1 package org.robolectric.shadows; 2 3 import android.location.GnssStatus; 4 import android.os.Build; 5 import androidx.annotation.Nullable; 6 import com.google.auto.value.AutoValue; 7 import java.util.ArrayList; 8 import java.util.Arrays; 9 import java.util.Collection; 10 import java.util.List; 11 import org.robolectric.util.ReflectionHelpers; 12 import org.robolectric.util.ReflectionHelpers.ClassParameter; 13 14 /** 15 * Builder for {@link GnssStatus} objects, since they have a hidden constructor. 16 * 17 * @deprecated Use {@link GnssStatus.Builder} instead where possible. 18 */ 19 @Deprecated 20 public final class GnssStatusBuilder { 21 /** Information about a single satellite in a {@link GnssStatus}. */ 22 @AutoValue 23 public abstract static class GnssSatelliteInfo { 24 /** 25 * Gets the {@link GnssStatus#getConstellationType(int) GNSS constellation} of the satellite. 26 */ getConstellation()27 public abstract int getConstellation(); 28 29 /** Gets the {@link GnssStatus#getSvid(int) identification number} of the satellite. */ getSvid()30 public abstract int getSvid(); 31 32 /** Gets the {@link GnssStatus#getCn0DbHz(int) carrier-to-noise density} of the satellite. */ getCn0DbHz()33 public abstract float getCn0DbHz(); 34 35 /** 36 * Gets the {@link GnssStatus#getElevationDegrees(int) elevation} of the satellite, in degrees. 37 */ getElevation()38 public abstract float getElevation(); 39 40 /** Gets the {@link GnssStatus#getAzimuthDegrees(int) azimuth} of the satellite, in degrees. */ getAzimuth()41 public abstract float getAzimuth(); 42 43 /** Gets whether the satellite {@link GnssStatus#hasEphemerisData(int) has ephemeris data}. */ getHasEphemeris()44 public abstract boolean getHasEphemeris(); 45 46 /** Gets whether the satellite {@link GnssStatus#hasAlmanacData(int) has almanac data}. */ getHasAlmanac()47 public abstract boolean getHasAlmanac(); 48 49 /** 50 * Gets whether the satellite {@link GnssStatus#usedInFix(int) was used in the most recent 51 * position fix}. 52 */ isUsedInFix()53 public abstract boolean isUsedInFix(); 54 55 /** 56 * Gets the {@link GnssStatus#getCarrierFrequencyHz(int) carrier frequency} of the satellite, in 57 * Hz, if present; if {@code null}, indicates that the carrier frequency {@link 58 * GnssStatus#hasCarrierFrequencyHz(int) is not available}. 59 */ 60 @Nullable getCarrierFrequencyHz()61 public abstract Float getCarrierFrequencyHz(); 62 builder()63 public static Builder builder() { 64 return new AutoValue_GnssStatusBuilder_GnssSatelliteInfo.Builder(); 65 } 66 67 /** Builder for {@link GnssSatelliteInfo}. */ 68 @AutoValue.Builder 69 public abstract static class Builder { 70 /** 71 * Sets the {@link GnssStatus#getConstellationType(int) GNSS constellation} of the satellite. 72 */ setConstellation(int constellation)73 public abstract Builder setConstellation(int constellation); 74 75 /** Sets the {@link GnssStatus#getSvid(int) identification number} of the satellite. */ setSvid(int svid)76 public abstract Builder setSvid(int svid); 77 78 /** Gets the {@link GnssStatus#getCn0DbHz(int) carrier-to-noise density} of the satellite. */ setCn0DbHz(float cn0DbHz)79 public abstract Builder setCn0DbHz(float cn0DbHz); 80 81 /** 82 * Sets the {@link GnssStatus#getElevationDegrees(int) elevation} of the satellite, in 83 * degrees. 84 */ setElevation(float elevation)85 public abstract Builder setElevation(float elevation); 86 87 /** 88 * Sets the {@link GnssStatus#getAzimuthDegrees(int) azimuth} of the satellite, in degrees. 89 */ setAzimuth(float azimuth)90 public abstract Builder setAzimuth(float azimuth); 91 92 /** Sets whether the satellite {@link GnssStatus#hasEphemerisData(int) has ephemeris data}. */ setHasEphemeris(boolean hasEphemeris)93 public abstract Builder setHasEphemeris(boolean hasEphemeris); 94 95 /** Sets whether the satellite {@link GnssStatus#hasAlmanacData(int) has almanac data}. */ setHasAlmanac(boolean hasAlmanac)96 public abstract Builder setHasAlmanac(boolean hasAlmanac); 97 98 /** 99 * Sets whether the satellite {@link GnssStatus#usedInFix(int) was used in the most recent 100 * position fix}. 101 */ setUsedInFix(boolean usedInFix)102 public abstract Builder setUsedInFix(boolean usedInFix); 103 104 /** 105 * Sets the {@link GnssStatus#getCarrierFrequencyHz(int) carrier frequency} of the satellite, 106 * in Hz, if present; if {@code null}, indicates that the carrier frequency {@link 107 * GnssStatus#hasCarrierFrequencyHz(int) is not available}. 108 */ setCarrierFrequencyHz(@ullable Float carrierFrequencyHz)109 public abstract Builder setCarrierFrequencyHz(@Nullable Float carrierFrequencyHz); 110 111 /** Builds the {@link GnssSatelliteInfo}. */ build()112 public abstract GnssSatelliteInfo build(); 113 } 114 } 115 GnssStatusBuilder()116 private GnssStatusBuilder() {} 117 118 /** Creates a new {@link GnssStatusBuilder}. */ create()119 public static GnssStatusBuilder create() { 120 return new GnssStatusBuilder(); 121 } 122 123 private final List<GnssSatelliteInfo> satelliteInfos = new ArrayList<>(); 124 125 /** Adds a satellite to the {@link GnssStatus} being built. */ addSatellite(GnssSatelliteInfo satelliteInfo)126 public GnssStatusBuilder addSatellite(GnssSatelliteInfo satelliteInfo) { 127 satelliteInfos.add(satelliteInfo); 128 return this; 129 } 130 131 /** Adds a collection of satellites to the {@link GnssStatus} being built. */ addAllSatellites(Collection<GnssSatelliteInfo> satelliteInfos)132 public GnssStatusBuilder addAllSatellites(Collection<GnssSatelliteInfo> satelliteInfos) { 133 this.satelliteInfos.addAll(satelliteInfos); 134 return this; 135 } 136 137 /** Builds the {@link GnssStatus} from the satellites previously added. */ build()138 public GnssStatus build() { 139 return createFrom(satelliteInfos); 140 } 141 142 /** Convenience method to create a {@link GnssStatus} directly from known satellite info. */ buildFrom(GnssSatelliteInfo... satelliteInfos)143 public static GnssStatus buildFrom(GnssSatelliteInfo... satelliteInfos) { 144 return createFrom(Arrays.asList(satelliteInfos)); 145 } 146 147 private static final int GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA = 148 (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) 149 ? ReflectionHelpers.getStaticField(GnssStatus.class, "GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA") 150 : 0; 151 private static final int GNSS_SV_FLAGS_HAS_ALMANAC_DATA = 152 (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) 153 ? ReflectionHelpers.getStaticField(GnssStatus.class, "GNSS_SV_FLAGS_HAS_ALMANAC_DATA") 154 : 0; 155 private static final int GNSS_SV_FLAGS_USED_IN_FIX = 156 (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) 157 ? ReflectionHelpers.getStaticField(GnssStatus.class, "GNSS_SV_FLAGS_USED_IN_FIX") 158 : 0; 159 private static final int GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY = 160 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O 161 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) 162 ? ReflectionHelpers.getStaticField( 163 GnssStatus.class, "GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY") 164 : 0; 165 private static final boolean SUPPORTS_CARRIER_FREQUENCY = 166 (GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY != 0); 167 168 private static final int SVID_SHIFT_WIDTH = 169 ReflectionHelpers.getStaticField(GnssStatus.class, "SVID_SHIFT_WIDTH"); 170 private static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 171 ReflectionHelpers.getStaticField(GnssStatus.class, "CONSTELLATION_TYPE_SHIFT_WIDTH"); 172 private static final int CONSTELLATION_TYPE_MASK = 173 ReflectionHelpers.getStaticField(GnssStatus.class, "CONSTELLATION_TYPE_MASK"); 174 createFrom(List<GnssSatelliteInfo> satelliteInfos)175 private static GnssStatus createFrom(List<GnssSatelliteInfo> satelliteInfos) { 176 int svCount = satelliteInfos.size(); 177 int[] svidWithFlags = new int[svCount]; 178 float[] cn0DbHz = new float[svCount]; 179 float[] elevations = new float[svCount]; 180 float[] azimuths = new float[svCount]; 181 float[] carrierFrequencies = new float[svCount]; 182 183 for (int i = 0; i < svCount; i++) { 184 GnssSatelliteInfo info = satelliteInfos.get(i); 185 186 int packedSvid = 187 (info.getSvid() << SVID_SHIFT_WIDTH) 188 | (info.getConstellation() & CONSTELLATION_TYPE_MASK) 189 << CONSTELLATION_TYPE_SHIFT_WIDTH; 190 191 if (info.getHasEphemeris()) { 192 packedSvid |= GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA; 193 } 194 if (info.getHasAlmanac()) { 195 packedSvid |= GNSS_SV_FLAGS_HAS_ALMANAC_DATA; 196 } 197 if (info.isUsedInFix()) { 198 packedSvid |= GNSS_SV_FLAGS_USED_IN_FIX; 199 } 200 if (SUPPORTS_CARRIER_FREQUENCY && info.getCarrierFrequencyHz() != null) { 201 packedSvid |= GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY; 202 carrierFrequencies[i] = info.getCarrierFrequencyHz(); 203 } 204 svidWithFlags[i] = packedSvid; 205 206 cn0DbHz[i] = info.getCn0DbHz(); 207 elevations[i] = info.getElevation(); 208 azimuths[i] = info.getAzimuth(); 209 } 210 211 List<ClassParameter<?>> classParameters = new ArrayList<>(); 212 classParameters.add(ClassParameter.from(int.class, svCount)); 213 classParameters.add(ClassParameter.from(int[].class, svidWithFlags)); 214 classParameters.add(ClassParameter.from(float[].class, cn0DbHz)); 215 classParameters.add(ClassParameter.from(float[].class, elevations)); 216 classParameters.add(ClassParameter.from(float[].class, azimuths)); 217 218 if (SUPPORTS_CARRIER_FREQUENCY) { 219 classParameters.add(ClassParameter.from(float[].class, carrierFrequencies)); 220 } 221 222 return ReflectionHelpers.callConstructor( 223 GnssStatus.class, classParameters.toArray(new ClassParameter<?>[0])); 224 } 225 } 226