1 /* 2 * Copyright (C) 2021 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.window; 18 19 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; 20 import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.res.Configuration; 25 import android.os.Parcelable; 26 import android.util.SparseIntArray; 27 28 import com.android.internal.util.DataClass; 29 30 import java.util.Arrays; 31 32 /** 33 * Contains size-configuration buckets used to prevent excessive configuration changes during 34 * resize. 35 * 36 * These configurations are collected from application's resources based on size-sensitive 37 * qualifiers. For example, layout-w800dp will be added to mHorizontalSizeConfigurations as 800 38 * and drawable-sw400dp will be added to both as 400. 39 * 40 * @hide 41 */ 42 @DataClass(genAidl = true) 43 public final class SizeConfigurationBuckets implements Parcelable { 44 45 /** Horizontal (screenWidthDp) buckets */ 46 @Nullable 47 private final int[] mHorizontal; 48 49 /** Vertical (screenHeightDp) buckets */ 50 @Nullable 51 private final int[] mVertical; 52 53 /** Smallest (smallestScreenWidthDp) buckets */ 54 @Nullable 55 private final int[] mSmallest; 56 SizeConfigurationBuckets(Configuration[] sizeConfigurations)57 public SizeConfigurationBuckets(Configuration[] sizeConfigurations) { 58 SparseIntArray horizontal = new SparseIntArray(); 59 SparseIntArray vertical = new SparseIntArray(); 60 SparseIntArray smallest = new SparseIntArray(); 61 for (int i = sizeConfigurations.length - 1; i >= 0; i--) { 62 Configuration config = sizeConfigurations[i]; 63 if (config.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { 64 vertical.put(config.screenHeightDp, 0); 65 } 66 if (config.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) { 67 horizontal.put(config.screenWidthDp, 0); 68 } 69 if (config.smallestScreenWidthDp != Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { 70 smallest.put(config.smallestScreenWidthDp, 0); 71 } 72 } 73 mHorizontal = horizontal.copyKeys(); 74 mVertical = vertical.copyKeys(); 75 mSmallest = smallest.copyKeys(); 76 } 77 78 /** 79 * Get the changes between two configurations but don't count changes in sizes if they don't 80 * cross boundaries that are important to the app. 81 * 82 * This is a static helper to deal with null `buckets`. When no buckets have been specified, 83 * this actually filters out all 3 size-configs. This is legacy behavior. 84 */ filterDiff(int diff, Configuration oldConfig, Configuration newConfig, @Nullable SizeConfigurationBuckets buckets)85 public static int filterDiff(int diff, Configuration oldConfig, Configuration newConfig, 86 @Nullable SizeConfigurationBuckets buckets) { 87 if (buckets == null) { 88 return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE); 89 } 90 if ((diff & CONFIG_SCREEN_SIZE) != 0) { 91 final boolean crosses = buckets.crossesHorizontalSizeThreshold(oldConfig.screenWidthDp, 92 newConfig.screenWidthDp) 93 || buckets.crossesVerticalSizeThreshold(oldConfig.screenHeightDp, 94 newConfig.screenHeightDp); 95 if (!crosses) { 96 diff &= ~CONFIG_SCREEN_SIZE; 97 } 98 } 99 if ((diff & CONFIG_SMALLEST_SCREEN_SIZE) != 0) { 100 final int oldSmallest = oldConfig.smallestScreenWidthDp; 101 final int newSmallest = newConfig.smallestScreenWidthDp; 102 if (!buckets.crossesSmallestSizeThreshold(oldSmallest, newSmallest)) { 103 diff &= ~CONFIG_SMALLEST_SCREEN_SIZE; 104 } 105 } 106 return diff; 107 } 108 crossesHorizontalSizeThreshold(int firstDp, int secondDp)109 private boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) { 110 return crossesSizeThreshold(mHorizontal, firstDp, secondDp); 111 } 112 crossesVerticalSizeThreshold(int firstDp, int secondDp)113 private boolean crossesVerticalSizeThreshold(int firstDp, int secondDp) { 114 return crossesSizeThreshold(mVertical, firstDp, secondDp); 115 } 116 crossesSmallestSizeThreshold(int firstDp, int secondDp)117 private boolean crossesSmallestSizeThreshold(int firstDp, int secondDp) { 118 return crossesSizeThreshold(mSmallest, firstDp, secondDp); 119 } 120 121 /** 122 * The purpose of this method is to decide whether the activity needs to be relaunched upon 123 * changing its size. In most cases the activities don't need to be relaunched, if the resize 124 * is small, all the activity content has to do is relayout itself within new bounds. There are 125 * cases however, where the activity's content would be completely changed in the new size and 126 * the full relaunch is required. 127 * 128 * The activity will report to us vertical and horizontal thresholds after which a relaunch is 129 * required. These thresholds are collected from the application resource qualifiers. For 130 * example, if application has layout-w600dp resource directory, then it needs a relaunch when 131 * we resize from width of 650dp to 550dp, as it crosses the 600dp threshold. However, if 132 * it resizes width from 620dp to 700dp, it won't be relaunched as it stays on the same side 133 * of the threshold. 134 */ crossesSizeThreshold(int[] thresholds, int firstDp, int secondDp)135 private static boolean crossesSizeThreshold(int[] thresholds, int firstDp, 136 int secondDp) { 137 if (thresholds == null) { 138 return false; 139 } 140 for (int i = thresholds.length - 1; i >= 0; i--) { 141 final int threshold = thresholds[i]; 142 if ((firstDp < threshold && secondDp >= threshold) 143 || (firstDp >= threshold && secondDp < threshold)) { 144 return true; 145 } 146 } 147 return false; 148 } 149 150 @Override toString()151 public String toString() { 152 return Arrays.toString(mHorizontal) + " " + Arrays.toString(mVertical) + " " 153 + Arrays.toString(mSmallest); 154 } 155 156 157 158 // Code below generated by codegen v1.0.22. 159 // 160 // DO NOT MODIFY! 161 // CHECKSTYLE:OFF Generated code 162 // 163 // To regenerate run: 164 // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/window/SizeConfigurationBuckets.java 165 // 166 // To exclude the generated code from IntelliJ auto-formatting enable (one-time): 167 // Settings > Editor > Code Style > Formatter Control 168 //@formatter:off 169 170 171 /** 172 * Creates a new SizeConfigurationBuckets. 173 * 174 * @param horizontal 175 * Horizontal (screenWidthDp) buckets 176 * @param vertical 177 * Vertical (screenHeightDp) buckets 178 * @param smallest 179 * Smallest (smallestScreenWidthDp) buckets 180 */ 181 @DataClass.Generated.Member SizeConfigurationBuckets( @ullable int[] horizontal, @Nullable int[] vertical, @Nullable int[] smallest)182 public SizeConfigurationBuckets( 183 @Nullable int[] horizontal, 184 @Nullable int[] vertical, 185 @Nullable int[] smallest) { 186 this.mHorizontal = horizontal; 187 this.mVertical = vertical; 188 this.mSmallest = smallest; 189 190 // onConstructed(); // You can define this method to get a callback 191 } 192 193 /** 194 * Horizontal (screenWidthDp) buckets 195 */ 196 @DataClass.Generated.Member getHorizontal()197 public @Nullable int[] getHorizontal() { 198 return mHorizontal; 199 } 200 201 /** 202 * Vertical (screenHeightDp) buckets 203 */ 204 @DataClass.Generated.Member getVertical()205 public @Nullable int[] getVertical() { 206 return mVertical; 207 } 208 209 /** 210 * Smallest (smallestScreenWidthDp) buckets 211 */ 212 @DataClass.Generated.Member getSmallest()213 public @Nullable int[] getSmallest() { 214 return mSmallest; 215 } 216 217 @Override 218 @DataClass.Generated.Member writeToParcel(@onNull android.os.Parcel dest, int flags)219 public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { 220 // You can override field parcelling by defining methods like: 221 // void parcelFieldName(Parcel dest, int flags) { ... } 222 223 byte flg = 0; 224 if (mHorizontal != null) flg |= 0x1; 225 if (mVertical != null) flg |= 0x2; 226 if (mSmallest != null) flg |= 0x4; 227 dest.writeByte(flg); 228 if (mHorizontal != null) dest.writeIntArray(mHorizontal); 229 if (mVertical != null) dest.writeIntArray(mVertical); 230 if (mSmallest != null) dest.writeIntArray(mSmallest); 231 } 232 233 @Override 234 @DataClass.Generated.Member describeContents()235 public int describeContents() { return 0; } 236 237 /** @hide */ 238 @SuppressWarnings({"unchecked", "RedundantCast"}) 239 @DataClass.Generated.Member SizeConfigurationBuckets(@onNull android.os.Parcel in)240 /* package-private */ SizeConfigurationBuckets(@NonNull android.os.Parcel in) { 241 // You can override field unparcelling by defining methods like: 242 // static FieldType unparcelFieldName(Parcel in) { ... } 243 244 byte flg = in.readByte(); 245 int[] horizontal = (flg & 0x1) == 0 ? null : in.createIntArray(); 246 int[] vertical = (flg & 0x2) == 0 ? null : in.createIntArray(); 247 int[] smallest = (flg & 0x4) == 0 ? null : in.createIntArray(); 248 249 this.mHorizontal = horizontal; 250 this.mVertical = vertical; 251 this.mSmallest = smallest; 252 253 // onConstructed(); // You can define this method to get a callback 254 } 255 256 @DataClass.Generated.Member 257 public static final @NonNull Parcelable.Creator<SizeConfigurationBuckets> CREATOR 258 = new Parcelable.Creator<SizeConfigurationBuckets>() { 259 @Override 260 public SizeConfigurationBuckets[] newArray(int size) { 261 return new SizeConfigurationBuckets[size]; 262 } 263 264 @Override 265 public SizeConfigurationBuckets createFromParcel(@NonNull android.os.Parcel in) { 266 return new SizeConfigurationBuckets(in); 267 } 268 }; 269 270 @DataClass.Generated( 271 time = 1615845864280L, 272 codegenVersion = "1.0.22", 273 sourceFile = "frameworks/base/core/java/android/window/SizeConfigurationBuckets.java", 274 inputSignatures = "private final @android.annotation.Nullable int[] mHorizontal\nprivate final @android.annotation.Nullable int[] mVertical\nprivate final @android.annotation.Nullable int[] mSmallest\npublic static int filterDiff(int,android.content.res.Configuration,android.content.res.Configuration,android.window.SizeConfigurationBuckets)\nprivate boolean crossesHorizontalSizeThreshold(int,int)\nprivate boolean crossesVerticalSizeThreshold(int,int)\nprivate boolean crossesSmallestSizeThreshold(int,int)\nprivate static boolean crossesSizeThreshold(int[],int,int)\npublic @java.lang.Override java.lang.String toString()\nclass SizeConfigurationBuckets extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true)") 275 @Deprecated __metadata()276 private void __metadata() {} 277 278 279 //@formatter:on 280 // End of generated code 281 282 } 283