1 /* 2 * Copyright (C) 2018 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 package android.car.drivingstate; 18 19 import android.annotation.IntDef; 20 import android.os.Parcel; 21 import android.os.Parcelable; 22 23 import java.lang.annotation.Retention; 24 import java.lang.annotation.RetentionPolicy; 25 26 /** 27 * Car UX Restrictions event. This contains information on the set of UX restrictions 28 * that is in place due to the car's driving state. 29 * <p> 30 * The restriction information is organized as follows: 31 * <ul> 32 * <li> When there are no restrictions in place, for example when the car is parked, 33 * <ul> 34 * <li> {@link #mRequiresDistractionOptimization} is set to false. Apps can display activities 35 * that are not distraction optimized. 36 * <li> {@link #mActiveRestrictions} should contain UX_RESTRICTIONS_UNRESTRICTED. Apps don't 37 * have to check for this since {@code mRequiresDistractionOptimization} is false. 38 * </ul> 39 * <li> When the driving state changes, causing the UX restrictions to come in effect, 40 * <ul> 41 * <li> {@code mRequiresDistractionOptimization} is set to true. Apps can only display 42 * activities that are distraction optimized. Distraction optimized activities follow the base 43 * design guidelines that provide a distraction free driving user experience. 44 * <li> In addition, apps will have to check for the content of mActiveRestrictions. 45 * {@code mActiveRestrictions} will have additional granular information on the set of UX 46 * restrictions that are in place for the current driving state. The content of 47 * {@code mActiveRestrictions}, for the same driving state of the vehicle, could vary depending 48 * on the car maker and the market. For example, when the car is idling, the set of active 49 * UX restrictions contained in the {@code mActiveRestrictions} will depend on the car maker 50 * and the safety standards of the market that the vehicle is deployed in. 51 * </ul> 52 * </ul> 53 * <p> 54 * Apps that intend to be run when the car is being driven need to 55 * <ul> 56 * <li> Comply with the general distraction optimization guidelines. 57 * <li> Listen and react to the UX restrictions changes as detailed above. Since the restrictions 58 * could vary depending on the market, apps are expected to react to the restriction information 59 * and not to the absolute driving state. 60 * </ul> 61 */ 62 public class CarUxRestrictions implements Parcelable { 63 64 // Default fallback values for the restriction related parameters if the information is 65 // not available from the underlying service. 66 private static final int DEFAULT_MAX_LENGTH = 120; 67 private static final int DEFAULT_MAX_CUMULATIVE_ITEMS = 21; 68 private static final int DEFAULT_MAX_CONTENT_DEPTH = 3; 69 70 /** 71 * No specific restrictions in place, but baseline distraction optimization guidelines need to 72 * be adhered to when {@link #isRequiresDistractionOptimization()} is true. 73 */ 74 public static final int UX_RESTRICTIONS_BASELINE = 0; 75 76 // Granular UX Restrictions that are imposed when distraction optimization is required. 77 /** 78 * No dialpad for the purpose of initiating a phone call. 79 */ 80 public static final int UX_RESTRICTIONS_NO_DIALPAD = 1; 81 82 /** 83 * No filtering a list. 84 */ 85 public static final int UX_RESTRICTIONS_NO_FILTERING = 0x1 << 1; 86 87 /** 88 * General purpose strings length cannot exceed the character limit provided by 89 * {@link #getMaxRestrictedStringLength()} 90 */ 91 public static final int UX_RESTRICTIONS_LIMIT_STRING_LENGTH = 0x1 << 2; 92 93 /** 94 * No text entry for the purpose of searching etc. 95 */ 96 public static final int UX_RESTRICTIONS_NO_KEYBOARD = 0x1 << 3; 97 98 /** 99 * No video - no animated frames > 1fps. 100 */ 101 public static final int UX_RESTRICTIONS_NO_VIDEO = 0x1 << 4; 102 103 /** 104 * Limit the number of items displayed on the screen. 105 * Refer to {@link #getMaxCumulativeContentItems()} and 106 * {@link #getMaxContentDepth()} for the upper bounds on content 107 * serving. 108 */ 109 public static final int UX_RESTRICTIONS_LIMIT_CONTENT = 0x1 << 5; 110 111 /** 112 * No setup that requires form entry or interaction with external devices. 113 */ 114 public static final int UX_RESTRICTIONS_NO_SETUP = 0x1 << 6; 115 116 /** 117 * No Text Message (SMS, email, conversational, etc.) 118 */ 119 public static final int UX_RESTRICTIONS_NO_TEXT_MESSAGE = 0x1 << 7; 120 121 /** 122 * No text transcription (live or leave behind) of voice can be shown. 123 */ 124 public static final int UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION = 0x1 << 8; 125 126 127 /** 128 * All the above restrictions are in effect. 129 */ 130 public static final int UX_RESTRICTIONS_FULLY_RESTRICTED = 131 UX_RESTRICTIONS_NO_DIALPAD | UX_RESTRICTIONS_NO_FILTERING 132 | UX_RESTRICTIONS_LIMIT_STRING_LENGTH | UX_RESTRICTIONS_NO_KEYBOARD 133 | UX_RESTRICTIONS_NO_VIDEO | UX_RESTRICTIONS_LIMIT_CONTENT 134 | UX_RESTRICTIONS_NO_SETUP | UX_RESTRICTIONS_NO_TEXT_MESSAGE 135 | UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION; 136 137 @IntDef(flag = true, 138 prefix = {"UX_RESTRICTIONS_"}, 139 value = {UX_RESTRICTIONS_BASELINE, 140 UX_RESTRICTIONS_NO_DIALPAD, 141 UX_RESTRICTIONS_NO_FILTERING, 142 UX_RESTRICTIONS_LIMIT_STRING_LENGTH, 143 UX_RESTRICTIONS_NO_KEYBOARD, 144 UX_RESTRICTIONS_NO_VIDEO, 145 UX_RESTRICTIONS_LIMIT_CONTENT, 146 UX_RESTRICTIONS_NO_SETUP, 147 UX_RESTRICTIONS_NO_TEXT_MESSAGE, 148 UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION}) 149 @Retention(RetentionPolicy.SOURCE) 150 public @interface CarUxRestrictionsInfo { 151 } 152 153 private final long mTimeStamp; 154 private final boolean mRequiresDistractionOptimization; 155 @CarUxRestrictionsInfo 156 private final int mActiveRestrictions; 157 // Restriction Parameters 158 private final int mMaxStringLength; 159 private final int mMaxCumulativeContentItems; 160 private final int mMaxContentDepth; 161 162 /** 163 * Builder class for {@link CarUxRestrictions} 164 */ 165 public static class Builder { 166 private final long mTimeStamp; 167 private final boolean mRequiresDistractionOptimization; 168 @CarUxRestrictionsInfo 169 private final int mActiveRestrictions; 170 // Restriction Parameters 171 private int mMaxStringLength = DEFAULT_MAX_LENGTH; 172 private int mMaxCumulativeContentItems = DEFAULT_MAX_CUMULATIVE_ITEMS; 173 private int mMaxContentDepth = DEFAULT_MAX_CONTENT_DEPTH; 174 Builder(boolean reqOpt, @CarUxRestrictionsInfo int restrictions, long time)175 public Builder(boolean reqOpt, @CarUxRestrictionsInfo int restrictions, long time) { 176 mRequiresDistractionOptimization = reqOpt; 177 mActiveRestrictions = restrictions; 178 mTimeStamp = time; 179 } 180 181 /** 182 * Set the maximum length of general purpose strings that can be displayed when 183 * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_STRING_LENGTH} is imposed. 184 */ setMaxStringLength(int length)185 public Builder setMaxStringLength(int length) { 186 mMaxStringLength = length; 187 return this; 188 } 189 190 /** 191 * Set the maximum number of cumulative content items that can be displayed when 192 * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_CONTENT} is imposed. 193 */ setMaxCumulativeContentItems(int number)194 public Builder setMaxCumulativeContentItems(int number) { 195 mMaxCumulativeContentItems = number; 196 return this; 197 } 198 199 /** 200 * Set the maximum number of levels that the user can navigate to when 201 * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_CONTENT} is imposed. 202 */ setMaxContentDepth(int depth)203 public Builder setMaxContentDepth(int depth) { 204 mMaxContentDepth = depth; 205 return this; 206 } 207 208 /** 209 * Build and return the {@link CarUxRestrictions} object 210 */ build()211 public CarUxRestrictions build() { 212 return new CarUxRestrictions(this); 213 } 214 215 } 216 217 /** 218 * Time at which this UX restriction event was deduced based on the car's driving state. 219 * 220 * @return Elapsed time in nanoseconds since system boot. 221 */ getTimeStamp()222 public long getTimeStamp() { 223 return mTimeStamp; 224 } 225 226 /** 227 * Conveys if the foreground activity needs to be distraction optimized. 228 * Activities that can handle distraction optimization need to be tagged as a distraction 229 * optimized in the app's manifest. 230 * <p> 231 * If the app has a foreground activity that has not been distraction optimized, the app has 232 * to switch to another activity that is distraction optimized. Failing that, the system will 233 * stop the foreground activity. 234 * 235 * @return true if distraction optimization is required, false if not 236 */ isRequiresDistractionOptimization()237 public boolean isRequiresDistractionOptimization() { 238 return mRequiresDistractionOptimization; 239 } 240 241 /** 242 * A combination of the Car UX Restrictions that is active for the current state of driving. 243 * 244 * @return A combination of the above {@code @CarUxRestrictionsInfo} 245 */ 246 @CarUxRestrictionsInfo getActiveRestrictions()247 public int getActiveRestrictions() { 248 return mActiveRestrictions; 249 } 250 251 /** 252 * Get the maximum length of general purpose strings that can be displayed when 253 * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_STRING_LENGTH} is imposed. 254 * 255 * @return the maximum length of string that can be displayed 256 */ getMaxRestrictedStringLength()257 public int getMaxRestrictedStringLength() { 258 return mMaxStringLength; 259 } 260 261 /** 262 * Get the maximum allowable number of content items that can be displayed to a user during 263 * traversal through any one path in a single task, when 264 * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_CONTENT} is imposed. 265 * <p> 266 * For example, if a task involving only one view, this represents the maximum allowable number 267 * of content items in this single view. 268 * <p> 269 * However, if the task involves selection of a content item in an originating view that then 270 * surfaces a secondary view to the user, then this value represents the maximum allowable 271 * number of content items between the originating and secondary views combined. 272 * <p> 273 * Specifically, if the maximum allowable value was 60 and a task involved browsing a list of 274 * countries and then viewing the top songs within a country, it would be acceptable to do 275 * either of the following: 276 * <ul> 277 * <li> list 10 countries, and then display the top 50 songs after country selection, or 278 * <li> list 20 countries, and then display the top 40 songs after country selection. 279 * </ul> 280 * <p> 281 * Please refer to this and {@link #getMaxContentDepth()} to know the upper bounds on the 282 * content display when the restriction is in place. 283 * 284 * @return maximum number of cumulative items that can be displayed 285 */ getMaxCumulativeContentItems()286 public int getMaxCumulativeContentItems() { 287 return mMaxCumulativeContentItems; 288 } 289 290 /** 291 * Get the maximum allowable number of content depth levels or view traversals through any one 292 * path in a single task. This is applicable when 293 * {@link CarUxRestrictions#UX_RESTRICTIONS_LIMIT_CONTENT} is imposed. 294 * <p> 295 * For example, if a task involves only selecting an item from a single list on one view, 296 * the task's content depth would be considered 1. 297 * <p> 298 * However, if the task involves selection of a content item in an originating view that then 299 * surfaces a secondary view to the user, the task's content depth would be considered 2. 300 * <p> 301 * Specifically, if a task involved browsing a list of countries, selecting a genre within the 302 * country, and then viewing the top songs within a country, the task's content depth would be 303 * considered 3. 304 * <p> 305 * Please refer to this and {@link #getMaxCumulativeContentItems()} to know the upper bounds on 306 * the content display when the restriction is in place. 307 * 308 * @return maximum number of cumulative items that can be displayed 309 */ getMaxContentDepth()310 public int getMaxContentDepth() { 311 return mMaxContentDepth; 312 } 313 314 @Override describeContents()315 public int describeContents() { 316 return 0; 317 } 318 319 @Override writeToParcel(Parcel dest, int flags)320 public void writeToParcel(Parcel dest, int flags) { 321 dest.writeInt(mActiveRestrictions); 322 dest.writeLong(mTimeStamp); 323 dest.writeInt(mRequiresDistractionOptimization ? 1 : 0); 324 dest.writeInt(mMaxStringLength); 325 dest.writeInt(mMaxCumulativeContentItems); 326 dest.writeInt(mMaxContentDepth); 327 } 328 329 public static final Parcelable.Creator<CarUxRestrictions> CREATOR 330 = new Parcelable.Creator<CarUxRestrictions>() { 331 public CarUxRestrictions createFromParcel(Parcel in) { 332 return new CarUxRestrictions(in); 333 } 334 335 public CarUxRestrictions[] newArray(int size) { 336 return new CarUxRestrictions[size]; 337 } 338 }; 339 CarUxRestrictions(CarUxRestrictions uxRestrictions)340 public CarUxRestrictions(CarUxRestrictions uxRestrictions) { 341 mTimeStamp = uxRestrictions.getTimeStamp(); 342 mRequiresDistractionOptimization = uxRestrictions.isRequiresDistractionOptimization(); 343 mActiveRestrictions = uxRestrictions.getActiveRestrictions(); 344 mMaxStringLength = uxRestrictions.mMaxStringLength; 345 mMaxCumulativeContentItems = uxRestrictions.mMaxCumulativeContentItems; 346 mMaxContentDepth = uxRestrictions.mMaxContentDepth; 347 } 348 CarUxRestrictions(Builder builder)349 private CarUxRestrictions(Builder builder) { 350 mTimeStamp = builder.mTimeStamp; 351 mActiveRestrictions = builder.mActiveRestrictions; 352 mRequiresDistractionOptimization = builder.mRequiresDistractionOptimization; 353 mMaxStringLength = builder.mMaxStringLength; 354 mMaxCumulativeContentItems = builder.mMaxCumulativeContentItems; 355 mMaxContentDepth = builder.mMaxContentDepth; 356 } 357 CarUxRestrictions(Parcel in)358 private CarUxRestrictions(Parcel in) { 359 mActiveRestrictions = in.readInt(); 360 mTimeStamp = in.readLong(); 361 mRequiresDistractionOptimization = in.readInt() != 0; 362 mMaxStringLength = in.readInt(); 363 mMaxCumulativeContentItems = in.readInt(); 364 mMaxContentDepth = in.readInt(); 365 } 366 367 @Override toString()368 public String toString() { 369 return "DO: " + mRequiresDistractionOptimization + " UxR: " + mActiveRestrictions 370 + " time: " + mTimeStamp; 371 } 372 373 /** 374 * Compares if the restrictions are the same. Doesn't compare the timestamps. 375 * 376 * @param other the other CarUxRestrictions object 377 * @return true if the restrictions are same, false otherwise 378 */ isSameRestrictions(CarUxRestrictions other)379 public boolean isSameRestrictions(CarUxRestrictions other) { 380 if (other == null) { 381 return false; 382 } 383 if (other == this) { 384 return true; 385 } 386 return other.mRequiresDistractionOptimization == mRequiresDistractionOptimization 387 && other.mActiveRestrictions == mActiveRestrictions; 388 } 389 } 390