UIInstructions Polluting your JSF 2 Component Tree

Posted on Updated on

If you are ever debugging a JSF 2 application, are seeing some “UIInstructions” components mysteriously polluting the component tree, and want to get rid of them (e.g. breaking desired sole-child configurations for layout geometry reasons), this tip is for you…

These UIInstructions components are likely due to the presence of an HTML DOCTYPE (or other content) appearing in one or more of your JSF fragment files. These fragment files are likely missing a ui:composition wrapper.

More info on why you might want the HTML DOCTYPE (or other content):
http://download.oracle.com/javaee/6/javaserverfaces/2.0/docs/pdldocs/facelets/ui/composition.html

Browsers and the HTML accessKey

Posted on Updated on

Background

As browsers evolve, changes have been made with how the HTML “accessKey” attribute works. There are differences across platforms for the same browser version as well as differences across browser versions on the same platform.  There are even some cases where the exact same browser treats accessKey differently for button elements vs. anchor elements!  This post shows some examples of how to invoke the accessKey and what the behavior is.

Sample button and anchor HTML using the “accessKey” attribute

<html>
<body>
<button onclick="javascript:alert('hi button with onclick')"
        accessKey="r">Button t<u style="color:red">r</u>y me</button>
<a href="javascript:;" onclick="alert('hi anchor with onclick');return false;"
        accessKey="o">Anchor with <b style="color:red">o</b>nclick</a>
</body>
</html>

How different browsers handle the above accessKey letters for button and anchor elements

Browser Operating System Key Combination Button Behavior Anchor Behavior
Chrome 7.0.517.41 Linux Alt + letter Clicks the button (3) Clicks the anchor (3)
Chrome 7.0.517.41 Mac OS X Control + Option + letter Clicks the button (3) Clicks the anchor (3)
Chrome 7.0.517.41 Windows Alt + letter Clicks the button (3) Clicks the anchor (3)
Firefox 1.5 Windows Alt + letter Clicks the button (1) Unknown
Firefox 2 Linux Alt + Shift + letter Clicks the button (1) Clicks the anchor
Firefox 2 Mac OS X Control + letter Clicks the button (1) Clicks the anchor
Firefox 2 Windows Alt + Shift + letter Clicks the button (1) Unknown
Firefox 3 Linux Alt + Shift + letter Clicks the button (1) Clicks the anchor
Firefox 3 Mac OS X Control + letter Clicks the button (1) Clicks the anchor
Firefox 3 Windows Alt + Shift + letter Clicks the button (1) Clicks the anchor
Internet Explorer 6 Windows Alt + letter Sets focus on the button (2) Unknown
Internet Explorer 7 Windows Alt + letter Sets focus on the button (2) Sets focus on the anchor (2)
Internet Explorer 8 Windows Alt + letter Clicks the button (3) Sets focus on the anchor (2)
Internet Explorer 9 (beta) Windows Alt + letter Clicks the button (3) Sets focus on the anchor (2)
Safari 3.1.2 Mac OS X Control + Option + letter Clicks the button (3) Clicks the anchor (3)
Safari 3.1.2 Windows Alt + letter Clicks the button (3) Clicks the anchor (3)
Safari 5.0.2 Mac OS X Control + Option + letter Clicks the button (3) Clicks the anchor (3)
Safari 5.0.2 Windows Alt + letter Clicks the button (3) Clicks the anchor (3)

 

  • Red = Different behavior for buttons and anchors
  • Comment 1 = To just set focus on the button/anchor, change your about:config setting for the “accessibility.accesskeycausesactivation” user preference.
  • Comment 2 = Press Enter to click the button/anchor
  • Comment 3 = There appears to be no built-in mechanism to just set focus on the button/anchor.  The component handling the click event would be responsible for setting focus while handling the click.

Defining an attribute-class for an array of objects

Posted on Updated on

When defining the attributes for an ADF Faces af:pageTemplate or af:declarativeComponent inside of an af:xmlContent, you need to provide an attribute-class definition. This looks something like:

<attribute-class>java.lang.String</attribute-class>

but what if you needed an array of Strings? That has a bit stranger syntax:

<attribute-class>[[Ljava.lang.String;</attribute-class>

Why is this not just “java.lang.String[]”? These attribute-class entries are the result of calling Class.forName.  The Class.forName on an array of objects looks like “[[Lsome.package.SomeClass;”.

One-off ADF component skin appearance

Posted on Updated on

Have you ever wanted to make an ADF Faces Rich Client component have a unique appearance but not change all other instances of that component? You can as long as the things you want to change are not skin properties or skin icons (server-side aspects like properties and icons are not part of the client-side generated style sheet so this client-side solution will not allow you to change them).

Let’s start with an interactive example; the skinning demo pages for the ADF Faces Rich Client:
http://jdevadf.oracle.com/adf-richclient-demo/components/skinningKeys/inputText.jspx

On this page, there is a right-hand side where you can see some example inputText components.  When you click on the checkboxes on the left-hand side of this page, the appearance of those start to change.  However, the inputText for the find field at the top of the page does not change.  This is because of some containment selector definitions in the skin used on this page that confines the custom styling to instances of the inputText that are contained in another component that has a particular style class.  Since the find field is not inside of that container, these custom styles do not apply to that field.

Let’s say your page has a structure like this:

<af:panelGroupLayout id="pgl1" layout="scroll" styleClass="SpecialMarker">
  <af:inputText id="it1" label="Name"/>
</af:panelGroupLayout>

To style that inputText specially in this container, you would create skinning definitions like this:

.SpecialMarker af|inputText {background-color:pink}
.SpecialMarker af|inputText::access-key {color: aqua;}
.SpecialMarker af|inputText::content {background-color:red}
.SpecialMarker af|inputText::label {font-weight: bold}

Alternatively, you could put your marker directly on the inputText like this:

<af:inputText id="it1" label="Name" styleClass="SpecialMarker"/>

and have skinning definitions like this:

af|inputText.SpecialMarker {background-color:pink}
af|inputText.SpecialMarker af|inputText::access-key {color: aqua;}
af|inputText.SpecialMarker af|inputText::content {background-color:red}
af|inputText.SpecialMarker af|inputText::label {font-weight: bold}

Deploying your application to a reserved port number

Posted on Updated on

Ensure that you avoid reserved port numbers when deploying your application. Some application servers will fail to prevent you from deploying your application on a reserved server port (e.g. 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601, 636, 993, 995, 2049, 3659, 4045, 6000, 6665, 6666, 6667, 6668, 6669). If you use one of these reserved ports, some web browsers may honor it but at least WebKit-based browsers (e.g. Safari and Google Chrome) will not. You may see some page content but all resources (images and styles) will fail to load.

NFS Network URLs on a Mac

Posted on Updated on

In Mac OS X, if someone gives you a Linux path that looks like:

/net/server/path/to/some/folder/

you can load that folder by swapping out “/net/” with “nfs://” and using a feature of the Mac OS X Finder.  In the Finder, go to:

Go - Connect to server...

And then specify the server address of:

nfs://server/path/to/some/folder/

If you are allowed to connect, a drive will be mounted on your desktop.

On a related note.  If you are using Firefox and come across one of these “nfs://” URLs, Firefox may not automatically mount the drive for you or even prompt you for an application to handle that URL.  Instead, you will need to tell Firefox that you want the Finder to handle those kinds of URLs.

I have tested these instructions on Firefox 3.6:

  1. In Firefox’s address bar, go to:
    about:config
  2. If you have never been there before, it might warn you about editing these settings.
  3. Once you get past the warning and can see a table listing all of your browser settings, right-click anywhere in that table and choose:
    New - Boolean
  4. For the name, specify:
    network.protocol-handler.expose.nfs
  5. For the value, specify:
    false

The next time you try to click one of those links, Firefox will prompt you for a program to handle it and then that program will launch the appropriate network drive.  At least on a Mac, you will need to choose:

/System/Library/CoreServices/Finder.app

Additional information on protocol handling in Firefox can be found at:

http://kb.mozillazine.org/Register_protocol

Custom Java Bind Variable in a Where Clause of an ADF View Object

Posted on Updated on

You can make a JDeveloper model View Object (VO) use a data query that has a custom variable accessor in Java.  This is useful for cases where you want your where clause to select based on the current user name from the ADF Security context.

Go to the “Query” tab of your view object:


Add a new Bind Variable and make sure to specify “Expression” and give the value the Groovy expression “adf.object.viewObject.yourPropertyName”.  The “adf.object.viewObject” portion is essentially like saying “this object” and then it will call getYourPropertyName() to fetch the value:


Back in your query expression, refer to this new bind variable by its name but prepend “:” to indicate that it is a bind variable as shown above.

Select the “Java” tab of your view object and then click the pencil icon to edit the settings:


Select the “Generate View Object Class” and “Include bind variable accessors” options:


This will generate a new “.java” entry under your view object in the JDeveloper application navigator:


Finally, open that Java file and change the implementation of your get accessor as you wish.  In my case, I am simply returning the user name property from the ADF security context.  I also changed my set accessor to do nothing:

Decrypting Margin, Border, and Padding Styles

Posted on Updated on

When working with ADF Faces skins, JSF inlineStyles, or just CSS in general, you may have come across some explicit style definitions like this which is very easy to understand:

padding-top: 15px;

However, you may have also come across more cryptic style definitions like these but wonder which measurement applies to which side of the element:

padding: 1px 2px 3px 4px;
padding: 5px 6px 7px;
padding: 8px 9px;
padding: 10px;

Here’s a key for decoding these definitions:

padding:   top   right   bottom   left;
padding:   top   right/left   bottom;
padding:   top/bottom   right/left;
padding:   top/bottom/right/left;

This same pattern applies to margins and borders.

Aside from the cryptic definitions, see my other post displaying how the CSS Box Model treats margins, borders, and padding with respect to width and height dimensions.  If you are an ADF Faces application developer be sure to also read the Layout Basics page.

Adding Skin and Settings menus to your JDeveloper ADF application

Posted on Updated on

Ever want to display menus in your Oracle JDeveloper 11g ADF Faces rich client application so users can easily change skins or other settings on the fly?  Perhaps you don’t want to expose these menus to your end users but at least want some easy way for you to test these different options while you develop or debug your application.  This post will show you how to get it done similar to what you see on the hosted ADF Faces Rich Client demos.

Screen shot of skin and settings menus
Example skin and settings menus

Before we begin with the changes, let’s start with some background:

Skins

A skin is an Apache MyFaces Trinidad construct that is also used in ADF Faces.  A skin is very much like a standard CSS style sheet that you use to define styles used by an HTML page.  Skins abstract implementation details in the same manner that components in JavaServer Faces (JSF) abstract you from underlying HTML implementation details.  The syntax of a skin follows that of the CSS 3 spec.  Skins can extend other skins, have blocks for user agent/browser-specific definitions, accessibility-specific definitions, and other properties that would normally not be available in a CSS file.

If you are interested in creating your own skin then I would recommend reviewing these pages:

Settings

The ADF Faces framework uses settings defined in the WEB-INF/trinidad-config.xml file of your application.  This file allows you to control settings such as:

  • Which skin used by your application
  • Whether a screen reader is used
  • Whether the page should be optimized for high contrast colors
  • Whether the page should be optimized for large fonts
  • Whether animation should be enabled in component interactions

See the configuration documentation for more details on this file.

Now here are the details for how to create these menus:

The Configuration File

If your application does not already have one, create a new WEB-INF/trinidad-config.xml file.  We want to make these settings dynamic so let’s use EL expressions for the values like this:

<?xml version="1.0" encoding="UTF-8"?>
<trinidad-config xmlns="http://myfaces.apache.org/trinidad/config">
 <skin-family>#{skins.skinFamily}</skin-family>
 <accessibility-mode>#{accessibility.accessibilityMode}</accessibility-mode>
 <accessibility-profile>#{accessibility.accessibilityProfile}</accessibility-profile>
 <animation-enabled>#{animation.animationEnabled}</animation-enabled>
</trinidad-config>

I am using separate managed beans here primarily for organization but you could just use a single bean.  The skin-family specifies what the active skin is.  The accessibility-mode specifies whether the page should be targeted for a screen reader.  The accessibility-profile specifies whether high contrast colors and whether large fonts should be targeted for the page.  The animation-enabled specifies whether the user will see animations in components that provide them.

The Menu Components

Some af:menuBar (most likely one that exists in your application’s page template) will need some af:menu components.  In this example, we have one menu that lists all of the skins that we want to expose and another menu that lists the settings.

ADF Faces comes with some built-in skins that have an inheritance structure like this:

  • simple — plain, nearly void of color, basic appearance
    • blafplus-medium — rectangular, solid blue colors
      • blafplus-rich — rounded, solid blue colors
    • fusion — blue colors with a rich three-dimensional appearance
      • fusion-projector — similar to the fusion skin but with colors suited for poor quality screen projectors

We will make our skin menu use radio menu items to display which skin is active:

 <af:menu text="#{bundle.MENU_SKIN}" id="skins">
   <af:commandMenuItem
     id="skin1"
     text="blafplus-rich"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='blafplus-rich'}"/>
   <af:commandMenuItem
     id="skin2"
     text="blafplus-medium"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='blafplus-medium'}"/>
   <af:commandMenuItem
     id="skin3"
     text="fusion"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='fusion'}"/>
   <af:commandMenuItem
     id="skin4"
     text="fusion-projector"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='fusion-projector'}"/>
   <af:commandMenuItem
     id="skin5"
     text="simple"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='simple'}"/>
 </af:menu>

The settings menu items will be presented as individual check mark options.  I am using a group to separate the animation setting from the others that are more accessibility-related:

 <af:menu text="#{bundle.MENU_SETTINGS}" id="config">
   <af:group id="acc">
     <af:commandMenuItem
       id="accMode"
       text="#{bundle.ACCESSIBILITY_LABEL_SCREEN_READER}"
       shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_SCREEN_READER}"
       selected="#{accessibility.screenReader}"
       type="check"
       actionListener="#{accessibility.modeMenuAction}"/>
     <af:commandMenuItem
       id="contrast"
       text="#{bundle.ACCESSIBILITY_LABEL_HIGH_CONTRAST}"
       shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_HIGH_CONTRAST}"
       selected="#{accessibility.highContrast}"
       type="check"
       actionListener="#{accessibility.colorContrastMenuAction}"/>
     <af:commandMenuItem
       id="fonts"
       text="#{bundle.ACCESSIBILITY_LABEL_LARGE_FONTS}"
       shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_LARGE_FONTS}"
       selected="#{accessibility.largeFonts}"
       type="check"
       actionListener="#{accessibility.fontSizeMenuAction}"/>
   </af:group>
   <af:commandMenuItem
     id="animate"
     text="#{bundle.MENU_ITEM_ENABLE_ANIMATIONS}"
     type="check"
     selected="#{animation.animationEnabled=='true'}"
     actionListener="#{animation.animationMenuAction}"/>
 </af:menu>

Managed Beans

We could have hard-coded the values we were after in the configuration file but then you’d have to restart your application when you make changes.  Since we want these settings to be dynamic, we use EL to point these values to managed beans.  These same beans are used to handle the menu item actions as well as convey their selected states.

The managed beans can be defined in the faces-config.xml like this (using session scope to preserve the settings for the span of the user’s session):

<!-- A managed bean that we use to track the user's skin preference. -->
<managed-bean>
  <managed-bean-name>skins</managed-bean-name>
  <managed-bean-class>foo.SkinPreferences</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

<!-- A managed bean that we use to track the user's accessibility preference. -->
<managed-bean>
  <managed-bean-name>accessibility</managed-bean-name>
  <managed-bean-class>foo.AccessibilityPreferences</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

<!-- A managed bean that we use to track the user's animation preference. -->
<managed-bean>
  <managed-bean-name>animation</managed-bean-name>
  <managed-bean-class>foo.AnimationPreferences</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

The session-scoped SkinPreferences managed bean defines your default skin and looks like this:

public class SkinPreferences implements Serializable
{
 public String getSkinFamily()
 {
   return _skinFamily;
 }

 public void setSkinFamily(String family)
 {
   _skinFamily = family;
 }

 public void skinMenuAction(ActionEvent ae)
 {
   RichCommandMenuItem menuItem = (RichCommandMenuItem)ae.getComponent();
   setSkinFamily(menuItem.getText());
   reloadThePage();
 }

 public static void reloadThePage()
 {
   // Since the page might be using PPR navigation,
   // we need to issue a redirect to ensure the entire page redraws:
   FacesContext fContext =
     FacesContext.getCurrentInstance();
   String viewId =
     fContext.getViewRoot().getViewId();
   String actionUrl =
     fContext.getApplication().getViewHandler().getActionURL(fContext, viewId);
   try
   {
     ExternalContext eContext = fContext.getExternalContext();
     String resourceUrl = actionUrl;
     eContext.redirect(resourceUrl);
   }
   catch (IOException ioe)
   {
     System.err.println("Problem trying to reload the page:");
     ioe.printStackTrace();
   }
 }

 private String _cachedTabBarHeight = null;
 private String _skinFamily = _DEFAULT_SKIN;
 private static final String _DEFAULT_SKIN = "fusion";
 private static final long serialVersionUID = 1L;
}

The session-scoped AccessibilityPreferences managed bean looks like this:

public class AccessibilityPreferences implements Serializable
{
  public String getAccessibilityMode()
  {
    return _screenReader ? "screenReader" : "default";
  }

  public AccessibilityProfile getAccessibilityProfile()
  {
    // Use safe double-check locking just in case we have multiple threads coming
    // through.
    if (_profile == null)
    {
      synchronized (this)
      {
        if (_profile == null)
          _profile = _createAccessibilityProfile();
      }
    }
    return _profile;
  }

  public boolean isScreenReader()
  {
    return _screenReader;
  }

  public boolean isHighContrast()
  {
    return _highContrast;
  }

  public boolean isLargeFonts()
  {
    return _largeFonts;
  }

  public void setScreenReader(boolean screenReader)
  {
    _screenReader = screenReader;
  }

  public synchronized void setHighContrast(boolean highContrast)
  {
    _highContrast = highContrast;

    // We need to re-create the AccessibilityProfile instance if any of the profile
    // properties change.
    // Null out our old cached copy.
    // It will be re-created the next time that getAccessibilityProfile() is called.
    _profile = null;
  }

  public synchronized void setLargeFonts(boolean largeFonts)
  {
    _largeFonts = largeFonts;

    // We need to re-create the AccessibilityProfile instance if any of the profile
    // properties change.
    // Null out our old cached copy.
    // It will be re-created the next time that getAccessibilityProfile() is called.
    _profile = null;
  }

  public void modeMenuAction(ActionEvent ae)
  {
    setScreenReader(!isScreenReader());
    SkinPreferences.reloadThePage();
  }

  public void colorContrastMenuAction(ActionEvent ae)
  {
    setHighContrast(!isHighContrast());
    SkinPreferences.reloadThePage();
  }

  public void fontSizeMenuAction(ActionEvent ae)
  {
    setLargeFonts(!isLargeFonts());
    SkinPreferences.reloadThePage();
  }

  // Creates the AccessibilityProfile instance based on the user's current
  // accessibility preferences.
  private AccessibilityProfile _createAccessibilityProfile()
  {
    AccessibilityProfile.ColorContrast colorContrast = isHighContrast() ?
      AccessibilityProfile.ColorContrast.HIGH :
      AccessibilityProfile.ColorContrast.STANDARD;

    AccessibilityProfile.FontSize fontSize = isLargeFonts() ?
      AccessibilityProfile.FontSize.LARGE :
      AccessibilityProfile.FontSize.MEDIUM;

    return AccessibilityProfile.getInstance(colorContrast, fontSize);
  }

  // We hold a reference to a cached copy of the AccessibilityProfile so that we do
  // not need to re-create this object each time getAccessibilityProfile() is
  // called.
  // Note that we mark this as volatile so that we can safely perform double-checked
  // locking on this variable.
  private volatile AccessibilityProfile _profile;

  private boolean _screenReader;
  private boolean _highContrast;
  private boolean _largeFonts;

  private static final long serialVersionUID = 1L;
}

The session-scoped AnimationPreferences managed bean looks like this:

public class AnimationPreferences implements Serializable
{
  public boolean isAnimationEnabled()
  {
    return _animationEnabled;
  }

  public void setAnimationEnabled(boolean enabled)
  {
    _animationEnabled = enabled;
  }

  public void animationMenuAction(ActionEvent ae)
  {
    setAnimationEnabled(!isAnimationEnabled());
    SkinPreferences.reloadThePage();
  }

  private boolean _animationEnabled = true;
  private static final long serialVersionUID = 1L;
}

Adding an Accessibility Page

If you wanted to take this a step further (and do not have built-in, predefined user-specific settings after a user sign in screen), you should provide an “Accessibility” link.  This link should be exposed as early as possible in your page so that screen reader users can easily identify that you are providing them options.  This page should then provide a reduced user interface for simple settings configuration.

Screen shot of the accessibility preferences page
The accessibility preferences page

Extra faces-config.xml changes:

<!--
  A managed bean that holds onto the user's accessibility preferences while
  editing these value on the Accessibility Preferences page.  Note that unlike
  the session-scoped AccessibilityPreferences bean, this bean only requires
  request-scope, since it is only holding the values for a short time (for
  the life of one request).
  -->
<managed-bean>
  <managed-bean-name>accessibilityHolder</managed-bean-name>
  <managed-bean-class>foo.AccessibilityPreferencesHolder</managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
  <!--
    We use a managed-property to inject the AccessibilityPreferences
    instance into our holder.  This allows the holder both to determine
    its initial values as well as apply back any changes.
    -->
  <managed-property>
    <property-name>accessibilityPreferences</property-name>
    <value>#{accessibility}</value>
  </managed-property>
</managed-bean>

The accessibility page should contain content like this:

<af:panelHeader id="ph1" text="#{bundle.ACCESSIBILITY_TITLE}">
  <af:panelFormLayout id="pfl1">
    <f:facet name="footer">
      <af:panelGroupLayout id="pgl2" layout="horizontal">
        <f:facet name="separator">
          <af:spacer id="s1" width="4px"/>
        </f:facet>
        <af:commandButton
          text="#{bundle.OK}"
          id="ok"
          action="#{accessibilityHolder.ok}"/>
        <af:commandButton
          text="#{bundle.CANCEL}"
          id="cancel"
          action="#{accessibilityHolder.cancel}"/>
      </af:panelGroupLayout>
    </f:facet>
    <af:selectBooleanCheckbox
      value="#{accessibilityHolder.screenReader}"
      id="sbc1"
      text="#{bundle.ACCESSIBILITY_LABEL_SCREEN_READER}"
      shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_SCREEN_READER}"/>
    <af:selectBooleanCheckbox
      value="#{accessibilityHolder.highContrast}"
      id="sbc2"
      text="#{bundle.ACCESSIBILITY_LABEL_HIGH_CONTRAST}"
      shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_HIGH_CONTRAST}"/>
    <af:selectBooleanCheckbox
      value="#{accessibilityHolder.largeFonts}"
      id="sbc3"
      text="#{bundle.ACCESSIBILITY_LABEL_LARGE_FONTS}"
      shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_LARGE_FONTS}"/>
  </af:panelFormLayout>
</af:panelHeader>

The request-scoped AccessibilityPreferencesHolder managed bean looks like this:

/**
 * AccessibilityPreferencesHolder is a request-scope managed bean that temporarily
 * holds on to accessibility preferences while the end user is editing them in the
 * accessibility preferences page.  While interacting with the accessibility page,
 * we avoid pushing values directly into the AccessibilityPreferences instance,
 * since it is possible that the end user may leave the page without accepting these
 * changes.
 *
 * The values stored in this temporary object are only pushed into the
 * AccessibilityPreferences instance when the user commits the changes by clicking
 * the "OK" button
 * on the accessibility preferences page.  When leaving the page via any other means
 * (Cancel button or other command components), any changes stored in the
 * AccessibilityPreferencesHolder are discarded.
 *
 * This object is exposed as a rqeuest-scope managed bean with the name
 * "accessibilityHolder" (see faces-config.xml).  It can be accessed via the EL
 * expression "#{accessibilityHolder}".
 */
public class AccessibilityPreferencesHolder
{
  /**
   * Tests whether the user prefers screen reader-optimized content.
   */
  public boolean isScreenReader()
  {
    return _screenReader;
  }

  /**
   * Tests whether the user prefers high contrast-optimized content.
   */
  public boolean isHighContrast()
  {
    return _highContrast;
  }

  /**
   * Tests whether the user prefers content optimized for large fonts.
   */
  public boolean isLargeFonts()
  {
    return _largeFonts;
  }

  /**
   * Stores the screen reader preference.
   */
  public void setScreenReader(boolean screenReader)
  {
    _screenReader = screenReader;
  }

  /**
   * Stores the high contrast preference.
   */
  public void setHighContrast(boolean highContrast)
  {
    _highContrast = highContrast;
  }

  /**
   * Stores the large fonts preference.
   */
  public void setLargeFonts(boolean largeFonts)
  {
    _largeFonts = largeFonts;
  }

  /**
   * Setter for injecting the AccessibilityPreferences managed-property.
   * The managed-bean definition specifies that the AccessibilityPreferences
   * instance should be injected into this object via the
   * setAccessibilityPreferences() method. (See faces-config.xml.)
   */
  public void setAccessibilityPreferences(AccessibilityPreferences preferences)
  {
    // Use the session-scoped AccessibilityPreferences to initialize our
    // values to the user's current preferences.
    _largeFonts = preferences.isLargeFonts();
    _highContrast = preferences.isHighContrast();
    _screenReader = preferences.isScreenReader();

    // Keep a reference to the AccessibilityPreferences instance.  We'll need
    // access to this object in order to commit the user's new preferences.
    _preferences = preferences;
  }

  /**
   * Handles OK button clicks.
   */
  public String ok()
  {
    // Store the user's new preferences, making them active.
    _preferences.setScreenReader(isScreenReader());
    _preferences.setHighContrast(isHighContrast());
    _preferences.setLargeFonts(isLargeFonts());

    // Specify where to navigate to next.
    return "home"; // change this for your app's needs
  }

  /**
   * Handles cancel button clicks.
   */
  public String cancel()
  {
    // Don't save any new values - just return.
    return "home"; // change this for your app's needs
  }

  // User's current preferences.
  private boolean _largeFonts;
  private boolean _highContrast;
  private boolean _screenReader;

  // Injected AccessbilityPreferences instance.
  private AccessibilityPreferences _preferences;
}

Keeping your iTunes Library Fresh

Posted on Updated on

Are you getting bored with shuffle? Are your 5-star songs getting overplayed? Do you want to hear some of the old songs from time to time but not too much? The remedy is to create a “Good Mix” smart playlist and here’s how!

You don’t need iTunes 9 to do this but the steps are much simpler in iTunes 9. It introduces a new grouping feature for smart playlist rules.  Unfortunately, even with these new grouping abilities, there are still things that cannot be achieve without nesting playlists. You’ll need to create a total of 6 smart playlists–but don’t worry, it will be worth it!

Step 1 – Create the “Mix Prerequisites” playlist

This smart playlist will be the basis for all of the other smart playlists that we will be creating.  The goal of this playlist is to filter out files and genres that won’t be everyday music files.  For my tastes, this means “Classical” and “Holiday” music won’t be on my normal listening schedule but feel free adjust this as applicable to your tastes.

  1. Match = all
  2. Name – does not contain – “Digital Booklet -“
  3. Genre – is not – “Classical”
  4. Genre – is not – “Holiday”
  5. Genre – is not – “Podcast”
  6. Kind – is not – “MPEG audio stream”
  7. Kind – contains – “audio”
  8. Genre – is not – “Audiobooks”
  9. Limit to – unchecked
  10. Match only checked items – unchecked
  11. Live updating – checked

Mix-Prerequisites

Step 2 – Create the “Mix 4 Star Fill” playlist

This smart playlist is also a building-block playlist that will be consumed by another smart playlist.

  1. Match = all
  2. Playlist – is – Mix Prerequisites
  3. Rating – is – 4 stars
  4. Last Played – is not in the last – 14 days
  5. Limit to – unchecked
  6. Match only checked items – unchecked
  7. Live updating – checked

Mix 4 Star Fill

Step 3 – Create the “Mix 3 Star Fill” playlist

This smart playlist is also a building-block playlist that will be consumed by another smart playlist.

  1. Match = all
  2. Playlist – is – Mix Prerequisites
  3. Rating – is – 3 stars
  4. Last Played – is not in the last – 2 months
  5. Limit to – checked – 40 – items – selected by random
  6. Match only checked items – unchecked
  7. Live updating – checked

Mix 3 Star Fill

Step 4 – Create the “Mix 2 Star Fill” playlist

This smart playlist is also a building-block playlist that will be consumed by another smart playlist.

  1. Match = all
  2. Playlist – is – Mix Prerequisites
  3. Rating – is – 2 stars
  4. Last Played – is not in the last – 20 months
  5. Limit to – checked – 20 – items – selected by random
  6. Match only checked items – unchecked
  7. Live updating – checked

Mix 2 Star Fill

Step 5 – Create the “Mix 1 Star Fill” playlist

This smart playlist is also a building-block playlist that will be consumed by another smart playlist.

  1. Match = all
  2. Playlist – is – Mix Prerequisites
  3. Rating – is – 1 star
  4. Last Played – is not in the last – 30 months
  5. Limit to – checked – 5 – items – selected by random
  6. Match only checked items – unchecked
  7. Live updating – checked

Mix 1 Star Fill

Step 6 – Create the “Good Mix” playlist

This smart playlist is the final playlist and the one that you will be listening to.

  1. Match = all
  2. Playlist – is – Mix Prerequisites
  3. Click the “…” button to create a match grouping and set its match type to “all of the following”
  4. Rating – is – 5 stars
  5. Rating – is – no stars (will encourage you to rate songs you just purchased)
  6. Playlist – is – Mix 4 Star Fill
  7. Playlist – is – Mix 3 Star Fill
  8. Playlist – is – Mix 2 Star Fill
  9. Playlist – is – Mix 1 Star Fill
  10. Limit to – unchecked
  11. Match only checked items – checked
  12. Live updating – checked

Good-Mix

Step 7 – Shuffle and Enjoy

Now that all of the playlists have been created, you are almost ready to play music.

  1. Select the “Good Mix” playlist
  2. Select the “turn shuffle on” icon at the bottom-left of iTunes to make sure that the songs that get compiled by this playlist will be shuffled
  3. Play!

Maintenance

If your music tastes change, be sure to change the rating of your songs!

  • If a song feels overplayed to you?  Knock it down a star.
  • Is an old gem coming back to you?  Give it some extra stars.

If you notice that you have too many or not enough songs of a particular rating, you can change the corresponding “fill” playlist by adjusting:

  • The “limit to” setting so that more or less are included into the final compilation
  • The “Last Played – is not in the last” so that songs will be repeated more or less frequently

Holiday Variation

  1. Edit the “Mix Prerequisites” smart playlist
  2. Take out the “Genre – is not – Holiday” rule
  3. Edit the “Good Mix” smart playlist
  4. Add a “Genre – is not – Holiday” rule at the top of the list of rules
  5. Duplicate the “Good Mix” smart playlist and name the new one “Holiday Mix”
  6. Edit the “Holiday Mix” smart playlist
  7. Change the “Genre – is not – Holiday” rule to “Genre – is – Holiday”