1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php 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 package com.android.ide.eclipse.adt.internal.editors.layout.configuration; 17 18 import com.android.annotations.NonNull; 19 import com.android.annotations.Nullable; 20 import com.android.ide.common.resources.configuration.FolderConfiguration; 21 import com.android.resources.NightMode; 22 import com.android.resources.UiMode; 23 import com.android.sdklib.IAndroidTarget; 24 import com.android.sdklib.devices.Device; 25 import com.android.sdklib.devices.State; 26 import com.google.common.base.Objects; 27 28 /** 29 * An {@linkplain NestedConfiguration} is a {@link Configuration} which inherits 30 * all of its values from a different configuration, except for one or more 31 * attributes where it overrides a custom value. 32 * <p> 33 * Unlike a {@link VaryingConfiguration}, a {@linkplain NestedConfiguration} 34 * will always return the same overridden value, regardless of the inherited 35 * value. 36 * <p> 37 * For example, an {@linkplain NestedConfiguration} may fix the locale to always 38 * be "en", but otherwise inherit everything else. 39 */ 40 public class NestedConfiguration extends Configuration { 41 /** The configuration we are inheriting non-overridden values from */ 42 protected Configuration mParent; 43 44 /** Bitmask of attributes to be overridden in this configuration */ 45 private int mOverride; 46 47 /** 48 * Constructs a new {@linkplain NestedConfiguration}. 49 * Construct via 50 * 51 * @param chooser the associated chooser 52 * @param configuration the configuration to inherit from 53 */ NestedConfiguration( @onNull ConfigurationChooser chooser, @NonNull Configuration configuration)54 protected NestedConfiguration( 55 @NonNull ConfigurationChooser chooser, 56 @NonNull Configuration configuration) { 57 super(chooser); 58 mParent = configuration; 59 60 mFullConfig.set(mParent.mFullConfig); 61 if (mParent.getEditedConfig() != null) { 62 mEditedConfig = new FolderConfiguration(); 63 mEditedConfig.set(mParent.mEditedConfig); 64 } 65 } 66 67 /** 68 * Returns the override flags for this configuration. Corresponds to 69 * the {@code CFG_} flags in {@link ConfigurationClient}. 70 * 71 * @return the bitmask 72 */ getOverrideFlags()73 public int getOverrideFlags() { 74 return mOverride; 75 } 76 77 /** 78 * Creates a new {@linkplain NestedConfiguration} that has the same overriding 79 * attributes as the given other {@linkplain NestedConfiguration}, and gets 80 * its values from the given {@linkplain Configuration}. 81 * 82 * @param other the configuration to copy overrides from 83 * @param values the configuration to copy values from 84 * @param parent the parent to tie the configuration to for inheriting values 85 * @return a new configuration 86 */ 87 @NonNull create( @onNull NestedConfiguration other, @NonNull Configuration values, @NonNull Configuration parent)88 public static NestedConfiguration create( 89 @NonNull NestedConfiguration other, 90 @NonNull Configuration values, 91 @NonNull Configuration parent) { 92 NestedConfiguration configuration = 93 new NestedConfiguration(other.mConfigChooser, parent); 94 initFrom(configuration, other, values, true /*sync*/); 95 return configuration; 96 } 97 98 /** 99 * Initializes a new {@linkplain NestedConfiguration} with the overriding 100 * attributes as the given other {@linkplain NestedConfiguration}, and gets 101 * its values from the given {@linkplain Configuration}. 102 * 103 * @param configuration the configuration to initialize 104 * @param other the configuration to copy overrides from 105 * @param values the configuration to copy values from 106 * @param sync if true, sync the folder configuration from 107 */ initFrom(NestedConfiguration configuration, NestedConfiguration other, Configuration values, boolean sync)108 protected static void initFrom(NestedConfiguration configuration, 109 NestedConfiguration other, Configuration values, boolean sync) { 110 configuration.mOverride = other.mOverride; 111 configuration.setDisplayName(values.getDisplayName()); 112 configuration.setActivity(values.getActivity()); 113 114 if (configuration.isOverridingLocale()) { 115 configuration.setLocale(values.getLocale(), true); 116 } 117 if (configuration.isOverridingTarget()) { 118 configuration.setTarget(values.getTarget(), true); 119 } 120 if (configuration.isOverridingDevice()) { 121 configuration.setDevice(values.getDevice(), true); 122 } 123 if (configuration.isOverridingDeviceState()) { 124 configuration.setDeviceState(values.getDeviceState(), true); 125 } 126 if (configuration.isOverridingNightMode()) { 127 configuration.setNightMode(values.getNightMode(), true); 128 } 129 if (configuration.isOverridingUiMode()) { 130 configuration.setUiMode(values.getUiMode(), true); 131 } 132 if (sync) { 133 configuration.syncFolderConfig(); 134 } 135 } 136 137 /** 138 * Sets the parent configuration that this configuration is inheriting from. 139 * 140 * @param parent the parent configuration 141 */ setParent(@onNull Configuration parent)142 public void setParent(@NonNull Configuration parent) { 143 mParent = parent; 144 } 145 146 /** 147 * Creates a new {@linkplain Configuration} which inherits values from the 148 * given parent {@linkplain Configuration}, possibly overriding some as 149 * well. 150 * 151 * @param chooser the associated chooser 152 * @param parent the configuration to inherit values from 153 * @return a new configuration 154 */ 155 @NonNull create(@onNull ConfigurationChooser chooser, @NonNull Configuration parent)156 public static NestedConfiguration create(@NonNull ConfigurationChooser chooser, 157 @NonNull Configuration parent) { 158 return new NestedConfiguration(chooser, parent); 159 } 160 161 @Override 162 @Nullable getTheme()163 public String getTheme() { 164 // Never overridden: this is a static attribute of a layout, not something which 165 // varies by configuration or at runtime 166 return mParent.getTheme(); 167 } 168 169 @Override setTheme(String theme)170 public void setTheme(String theme) { 171 // Never overridden 172 mParent.setTheme(theme); 173 } 174 175 /** 176 * Sets whether the locale should be overridden by this configuration 177 * 178 * @param override if true, override the inherited value 179 */ setOverrideLocale(boolean override)180 public void setOverrideLocale(boolean override) { 181 mOverride |= CFG_LOCALE; 182 } 183 184 /** 185 * Returns true if the locale is overridden 186 * 187 * @return true if the locale is overridden 188 */ isOverridingLocale()189 public final boolean isOverridingLocale() { 190 return (mOverride & CFG_LOCALE) != 0; 191 } 192 193 @Override 194 @NonNull getLocale()195 public Locale getLocale() { 196 if (isOverridingLocale()) { 197 return super.getLocale(); 198 } else { 199 return mParent.getLocale(); 200 } 201 } 202 203 @Override setLocale(@onNull Locale locale, boolean skipSync)204 public void setLocale(@NonNull Locale locale, boolean skipSync) { 205 if (isOverridingLocale()) { 206 super.setLocale(locale, skipSync); 207 } else { 208 mParent.setLocale(locale, skipSync); 209 } 210 } 211 212 /** 213 * Sets whether the rendering target should be overridden by this configuration 214 * 215 * @param override if true, override the inherited value 216 */ setOverrideTarget(boolean override)217 public void setOverrideTarget(boolean override) { 218 mOverride |= CFG_TARGET; 219 } 220 221 /** 222 * Returns true if the target is overridden 223 * 224 * @return true if the target is overridden 225 */ isOverridingTarget()226 public final boolean isOverridingTarget() { 227 return (mOverride & CFG_TARGET) != 0; 228 } 229 230 @Override 231 @Nullable getTarget()232 public IAndroidTarget getTarget() { 233 if (isOverridingTarget()) { 234 return super.getTarget(); 235 } else { 236 return mParent.getTarget(); 237 } 238 } 239 240 @Override setTarget(IAndroidTarget target, boolean skipSync)241 public void setTarget(IAndroidTarget target, boolean skipSync) { 242 if (isOverridingTarget()) { 243 super.setTarget(target, skipSync); 244 } else { 245 mParent.setTarget(target, skipSync); 246 } 247 } 248 249 /** 250 * Sets whether the device should be overridden by this configuration 251 * 252 * @param override if true, override the inherited value 253 */ setOverrideDevice(boolean override)254 public void setOverrideDevice(boolean override) { 255 mOverride |= CFG_DEVICE; 256 } 257 258 /** 259 * Returns true if the device is overridden 260 * 261 * @return true if the device is overridden 262 */ isOverridingDevice()263 public final boolean isOverridingDevice() { 264 return (mOverride & CFG_DEVICE) != 0; 265 } 266 267 @Override 268 @Nullable getDevice()269 public Device getDevice() { 270 if (isOverridingDevice()) { 271 return super.getDevice(); 272 } else { 273 return mParent.getDevice(); 274 } 275 } 276 277 @Override setDevice(Device device, boolean skipSync)278 public void setDevice(Device device, boolean skipSync) { 279 if (isOverridingDevice()) { 280 super.setDevice(device, skipSync); 281 } else { 282 mParent.setDevice(device, skipSync); 283 } 284 } 285 286 /** 287 * Sets whether the device state should be overridden by this configuration 288 * 289 * @param override if true, override the inherited value 290 */ setOverrideDeviceState(boolean override)291 public void setOverrideDeviceState(boolean override) { 292 mOverride |= CFG_DEVICE_STATE; 293 } 294 295 /** 296 * Returns true if the device state is overridden 297 * 298 * @return true if the device state is overridden 299 */ isOverridingDeviceState()300 public final boolean isOverridingDeviceState() { 301 return (mOverride & CFG_DEVICE_STATE) != 0; 302 } 303 304 @Override 305 @Nullable getDeviceState()306 public State getDeviceState() { 307 if (isOverridingDeviceState()) { 308 return super.getDeviceState(); 309 } else { 310 State state = mParent.getDeviceState(); 311 if (isOverridingDevice()) { 312 // If the device differs, I need to look up a suitable equivalent state 313 // on our device 314 if (state != null) { 315 Device device = super.getDevice(); 316 if (device != null) { 317 return device.getState(state.getName()); 318 } 319 } 320 } 321 322 return state; 323 } 324 } 325 326 @Override setDeviceState(State state, boolean skipSync)327 public void setDeviceState(State state, boolean skipSync) { 328 if (isOverridingDeviceState()) { 329 super.setDeviceState(state, skipSync); 330 } else { 331 if (isOverridingDevice()) { 332 Device device = super.getDevice(); 333 if (device != null) { 334 State equivalentState = device.getState(state.getName()); 335 if (equivalentState != null) { 336 state = equivalentState; 337 } 338 } 339 } 340 mParent.setDeviceState(state, skipSync); 341 } 342 } 343 344 /** 345 * Sets whether the night mode should be overridden by this configuration 346 * 347 * @param override if true, override the inherited value 348 */ setOverrideNightMode(boolean override)349 public void setOverrideNightMode(boolean override) { 350 mOverride |= CFG_NIGHT_MODE; 351 } 352 353 /** 354 * Returns true if the night mode is overridden 355 * 356 * @return true if the night mode is overridden 357 */ isOverridingNightMode()358 public final boolean isOverridingNightMode() { 359 return (mOverride & CFG_NIGHT_MODE) != 0; 360 } 361 362 @Override 363 @NonNull getNightMode()364 public NightMode getNightMode() { 365 if (isOverridingNightMode()) { 366 return super.getNightMode(); 367 } else { 368 return mParent.getNightMode(); 369 } 370 } 371 372 @Override setNightMode(@onNull NightMode night, boolean skipSync)373 public void setNightMode(@NonNull NightMode night, boolean skipSync) { 374 if (isOverridingNightMode()) { 375 super.setNightMode(night, skipSync); 376 } else { 377 mParent.setNightMode(night, skipSync); 378 } 379 } 380 381 /** 382 * Sets whether the UI mode should be overridden by this configuration 383 * 384 * @param override if true, override the inherited value 385 */ setOverrideUiMode(boolean override)386 public void setOverrideUiMode(boolean override) { 387 mOverride |= CFG_UI_MODE; 388 } 389 390 /** 391 * Returns true if the UI mode is overridden 392 * 393 * @return true if the UI mode is overridden 394 */ isOverridingUiMode()395 public final boolean isOverridingUiMode() { 396 return (mOverride & CFG_UI_MODE) != 0; 397 } 398 399 @Override 400 @NonNull getUiMode()401 public UiMode getUiMode() { 402 if (isOverridingUiMode()) { 403 return super.getUiMode(); 404 } else { 405 return mParent.getUiMode(); 406 } 407 } 408 409 @Override setUiMode(@onNull UiMode uiMode, boolean skipSync)410 public void setUiMode(@NonNull UiMode uiMode, boolean skipSync) { 411 if (isOverridingUiMode()) { 412 super.setUiMode(uiMode, skipSync); 413 } else { 414 mParent.setUiMode(uiMode, skipSync); 415 } 416 } 417 418 /** 419 * Returns the configuration this {@linkplain NestedConfiguration} is 420 * inheriting from 421 * 422 * @return the configuration this configuration is inheriting from 423 */ 424 @NonNull getParent()425 public Configuration getParent() { 426 return mParent; 427 } 428 429 @Override 430 @Nullable getActivity()431 public String getActivity() { 432 return mParent.getActivity(); 433 } 434 435 @Override setActivity(String activity)436 public void setActivity(String activity) { 437 super.setActivity(activity); 438 } 439 440 /** 441 * Returns a computed display name (ignoring the value stored by 442 * {@link #setDisplayName(String)}) by looking at the override flags 443 * and picking a suitable name. 444 * 445 * @return a suitable display name 446 */ 447 @Nullable computeDisplayName()448 public String computeDisplayName() { 449 return computeDisplayName(mOverride, this); 450 } 451 452 /** 453 * Computes a display name for the given configuration, using the given 454 * override flags (which correspond to the {@code CFG_} constants in 455 * {@link ConfigurationClient} 456 * 457 * @param flags the override bitmask 458 * @param configuration the configuration to fetch values from 459 * @return a suitable display name 460 */ 461 @Nullable computeDisplayName(int flags, @NonNull Configuration configuration)462 public static String computeDisplayName(int flags, @NonNull Configuration configuration) { 463 if ((flags & CFG_LOCALE) != 0) { 464 return ConfigurationChooser.getLocaleLabel(configuration.mConfigChooser, 465 configuration.getLocale(), false); 466 } 467 468 if ((flags & CFG_TARGET) != 0) { 469 return ConfigurationChooser.getRenderingTargetLabel(configuration.getTarget(), false); 470 } 471 472 if ((flags & CFG_DEVICE) != 0) { 473 return ConfigurationChooser.getDeviceLabel(configuration.getDevice(), true); 474 } 475 476 if ((flags & CFG_DEVICE_STATE) != 0) { 477 State deviceState = configuration.getDeviceState(); 478 if (deviceState != null) { 479 return deviceState.getName(); 480 } 481 } 482 483 if ((flags & CFG_NIGHT_MODE) != 0) { 484 return configuration.getNightMode().getLongDisplayValue(); 485 } 486 487 if ((flags & CFG_UI_MODE) != 0) { 488 configuration.getUiMode().getLongDisplayValue(); 489 } 490 491 return null; 492 } 493 494 @Override toString()495 public String toString() { 496 return Objects.toStringHelper(this.getClass()) 497 .add("parent", mParent.getDisplayName()) //$NON-NLS-1$ 498 .add("display", getDisplayName()) //$NON-NLS-1$ 499 .add("overrideLocale", isOverridingLocale()) //$NON-NLS-1$ 500 .add("overrideTarget", isOverridingTarget()) //$NON-NLS-1$ 501 .add("overrideDevice", isOverridingDevice()) //$NON-NLS-1$ 502 .add("overrideDeviceState", isOverridingDeviceState()) //$NON-NLS-1$ 503 .add("persistent", toPersistentString()) //$NON-NLS-1$ 504 .toString(); 505 } 506 } 507