Home | History | Annotate | Download | only in configuration
      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      */
     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      */
     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
     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      */
    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      */
    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
    156     public static NestedConfiguration create(@NonNull ConfigurationChooser chooser,
    157             @NonNull Configuration parent) {
    158         return new NestedConfiguration(chooser, parent);
    159     }
    160 
    161     @Override
    162     @Nullable
    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
    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      */
    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      */
    189     public final boolean isOverridingLocale() {
    190         return (mOverride & CFG_LOCALE) != 0;
    191     }
    192 
    193     @Override
    194     @NonNull
    195     public Locale getLocale() {
    196         if (isOverridingLocale()) {
    197             return super.getLocale();
    198         } else {
    199             return mParent.getLocale();
    200         }
    201     }
    202 
    203     @Override
    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      */
    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      */
    226     public final boolean isOverridingTarget() {
    227         return (mOverride & CFG_TARGET) != 0;
    228     }
    229 
    230     @Override
    231     @Nullable
    232     public IAndroidTarget getTarget() {
    233         if (isOverridingTarget()) {
    234             return super.getTarget();
    235         } else {
    236             return mParent.getTarget();
    237         }
    238     }
    239 
    240     @Override
    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      */
    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      */
    263     public final boolean isOverridingDevice() {
    264         return (mOverride & CFG_DEVICE) != 0;
    265     }
    266 
    267     @Override
    268     @Nullable
    269     public Device getDevice() {
    270         if (isOverridingDevice()) {
    271             return super.getDevice();
    272         } else {
    273             return mParent.getDevice();
    274         }
    275     }
    276 
    277     @Override
    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      */
    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      */
    300     public final boolean isOverridingDeviceState() {
    301         return (mOverride & CFG_DEVICE_STATE) != 0;
    302     }
    303 
    304     @Override
    305     @Nullable
    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
    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      */
    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      */
    358     public final boolean isOverridingNightMode() {
    359         return (mOverride & CFG_NIGHT_MODE) != 0;
    360     }
    361 
    362     @Override
    363     @NonNull
    364     public NightMode getNightMode() {
    365         if (isOverridingNightMode()) {
    366             return super.getNightMode();
    367         } else {
    368             return mParent.getNightMode();
    369         }
    370     }
    371 
    372     @Override
    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      */
    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      */
    395     public final boolean isOverridingUiMode() {
    396         return (mOverride & CFG_UI_MODE) != 0;
    397     }
    398 
    399     @Override
    400     @NonNull
    401     public UiMode getUiMode() {
    402         if (isOverridingUiMode()) {
    403             return super.getUiMode();
    404         } else {
    405             return mParent.getUiMode();
    406         }
    407     }
    408 
    409     @Override
    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
    425     public Configuration getParent() {
    426         return mParent;
    427     }
    428 
    429     @Override
    430     @Nullable
    431     public String getActivity() {
    432         return mParent.getActivity();
    433     }
    434 
    435     @Override
    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
    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
    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
    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