Home | History | Annotate | Download | only in dialog
      1 /*
      2  * Copyright (C) 2014 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 com.android.tv.settings.dialog;
     18 
     19 import android.content.Context;
     20 import android.content.Intent;
     21 import android.content.res.Resources;
     22 import android.graphics.drawable.Drawable;
     23 import android.net.Uri;
     24 import android.os.Bundle;
     25 import android.os.Parcel;
     26 import android.os.Parcelable;
     27 import android.text.TextUtils;
     28 import android.util.Log;
     29 
     30 import java.util.ArrayList;
     31 
     32 /**
     33  * A data class which represents a settings layout within an
     34  * {@link SettingsLayoutFragment}. Represents a list of choices the
     35  * user can make, a radio-button list of configuration options, or just a
     36  * list of information.
     37  */
     38 public class Layout implements Parcelable {
     39 
     40     public interface LayoutNodeRefreshListener {
     41         void onRefreshView();
     42         Node getSelectedNode();
     43     }
     44 
     45     public interface ContentNodeRefreshListener {
     46         void onRefreshView();
     47     }
     48 
     49     public interface Node {
     50         String getTitle();
     51     }
     52 
     53     private abstract static class LayoutTreeNode implements Node {
     54         LayoutTreeBranch mParent;
     55 
     56         void Log(int level) {
     57         }
     58     }
     59 
     60     private abstract static class LayoutTreeBranch extends LayoutTreeNode {
     61         ArrayList<LayoutTreeNode> mChildren;
     62         LayoutTreeBranch() {
     63             mChildren = new ArrayList<LayoutTreeNode>();
     64         }
     65     }
     66 
     67     public static class LayoutRow {
     68         public static final int NO_CHECK_SET = 0;
     69         public static final int VIEW_TYPE_ACTION = 0;
     70         public static final int VIEW_TYPE_STATIC = 1;
     71 
     72         private String mTitle;
     73         private StringGetter mDescription;
     74         private LayoutTreeNode mNode;
     75         private boolean mEnabled;
     76         private int mViewType;
     77         private boolean mChecked = false;
     78         private Drawable mIcon = null;
     79 
     80         public Node getNode() {
     81             return mNode;
     82         }
     83 
     84         public Uri getIconUri() {
     85             return null;
     86         }
     87 
     88         public Drawable getIcon() {
     89             return mIcon;
     90         }
     91 
     92         public int getCheckSetId() {
     93             return 0;
     94         }
     95 
     96         public boolean isChecked() {
     97             return mChecked;
     98         }
     99 
    100         public void setChecked(boolean v) {
    101             mChecked = v;
    102         }
    103 
    104         public boolean infoOnly() {
    105             return false;
    106         }
    107 
    108         public boolean isEnabled() {
    109             return mEnabled;
    110         }
    111 
    112         public boolean hasNext() {
    113             return false;
    114         }
    115 
    116         public boolean hasMultilineDescription() {
    117             return false;
    118         }
    119 
    120         public String getTitle() {
    121             return mTitle;
    122         }
    123 
    124         public StringGetter getDescription() {
    125             return mDescription;
    126         }
    127 
    128         public int getViewType() {
    129             return mViewType;
    130         }
    131 
    132         public boolean isGoBack() {
    133             if (mNode instanceof Action) {
    134                 Action a = (Action) mNode;
    135                 if (a.mActionId == Action.ACTION_BACK) {
    136                     return true;
    137                 }
    138             }
    139             return false;
    140         }
    141 
    142         public Action getUserAction() {
    143             if (mNode instanceof Action) {
    144                 Action a = (Action) mNode;
    145                 if (a.mActionId != Action.ACTION_NONE) {
    146                     return a;
    147                 }
    148             }
    149             return null;
    150         }
    151 
    152         public int getContentIconRes() {
    153             if (mNode instanceof Header) {
    154                 return ((Header) mNode).mContentIconRes;
    155             }
    156             return 0;
    157         }
    158 
    159         public LayoutRow(LayoutTreeNode node) {
    160             mNode = node;
    161             mViewType = VIEW_TYPE_ACTION;
    162             Appearence a;
    163             if (node instanceof Header) {
    164                 a = ((Header) node).mAppearence;
    165                 mEnabled = true;
    166             } else if (node instanceof Action) {
    167                 a = ((Action) node).mAppearence;
    168                 mEnabled = true;
    169             } else if (node instanceof Status) {
    170                 a = ((Status) node).mAppearence;
    171                 mEnabled = true;
    172             } else {
    173                 a = null;
    174                 mEnabled = false;
    175                 if (node instanceof Static) {
    176                     mViewType = VIEW_TYPE_STATIC;
    177                     Static s = (Static) node;
    178                     mTitle = s.mTitle;
    179                 }
    180             }
    181             if (a != null) {
    182                 mTitle = a.getTitle();
    183                 mDescription = a.mDescriptionGetter;
    184                 mIcon = a.getIcon();
    185                 mChecked = a.isChecked();
    186             }
    187         }
    188     }
    189 
    190     public abstract static class DrawableGetter {
    191         public abstract Drawable get();
    192 
    193         /**
    194          * Notification from client that antecedent data has changed and the drawable should be
    195          * redisplayed.
    196          */
    197         public void refreshView() {
    198             //TODO - When implementing, ensure that multiple updates from the same event do not
    199             // cause multiple view updates.
    200         }
    201     }
    202 
    203     public abstract static class StringGetter {
    204         private ContentNodeRefreshListener mListener;
    205 
    206         public void setListener(ContentNodeRefreshListener listener) {
    207             mListener = listener;
    208         }
    209 
    210         public abstract String get();
    211 
    212         /**
    213          * Notification from client that antecedent data has changed and the string should be
    214          * redisplayed.
    215          */
    216         public void refreshView() {
    217             if (mListener != null) {
    218                 mListener.onRefreshView();
    219             }
    220         }
    221     }
    222 
    223     /**
    224      * Implementation of "StringGetter" that stores and returns a literal string.
    225      */
    226     private static class LiteralStringGetter extends StringGetter {
    227         private final String mValue;
    228         public String get() {
    229             return mValue;
    230         }
    231         LiteralStringGetter(String value) {
    232             mValue = value;
    233         }
    234     }
    235 
    236     /**
    237      * Implementation of "StringGetter" that stores a string resource id and returns a string.
    238      */
    239     private static class ResourceStringGetter extends StringGetter {
    240         private final int mStringResourceId;
    241         private final Resources mRes;
    242         public String get() {
    243             return mRes.getString(mStringResourceId);
    244         }
    245         ResourceStringGetter(Resources res, int stringResourceId) {
    246             mRes = res;
    247             mStringResourceId = stringResourceId;
    248         }
    249     }
    250 
    251     public abstract static class LayoutGetter extends LayoutTreeNode {
    252         private LayoutNodeRefreshListener mListener;
    253 
    254         public void setListener(LayoutNodeRefreshListener listener) {
    255             mListener = listener;
    256         }
    257 
    258         public void notVisible() {
    259             mListener = null;
    260             onMovedOffScreen();
    261         }
    262 
    263         public abstract Layout get();
    264 
    265         public Node getSelectedNode() {
    266             if (mListener != null) {
    267                 return mListener.getSelectedNode();
    268             } else {
    269                 return null;
    270             }
    271         }
    272 
    273         /**
    274          * Notification to client that the list containting the contents of this getter is no longer
    275          * visible and background tasks to update the getter contents should be stopped until the
    276          * next "get()" call.
    277          */
    278         public void onMovedOffScreen() {
    279         }
    280 
    281         /**
    282          * Notification from client that antecedent data has changed and the list containing the
    283          * contents of this getter should be updated.
    284          */
    285         public void refreshView() {
    286             if (mListener != null) {
    287                 mListener.onRefreshView();
    288             }
    289         }
    290 
    291         @Override
    292         public String getTitle() {
    293             return null;
    294         }
    295 
    296         void Log(int level) {
    297             Log.d("Layout", indent(level) + "LayoutGetter");
    298             Layout l = get();
    299             l.Log(level + 1);
    300         }
    301     }
    302 
    303     private static class Appearence {
    304         private Drawable mIcon;
    305         private DrawableGetter mIconGetter;
    306         private String mTitle;
    307         private StringGetter mDescriptionGetter;
    308         private boolean mChecked = false;
    309 
    310         public String toString() {
    311             StringBuilder stringBuilder = new StringBuilder()
    312                 .append("'")
    313                 .append(mTitle)
    314                 .append("'");
    315             if (mDescriptionGetter != null) {
    316                 stringBuilder
    317                     .append(" : '")
    318                     .append(mDescriptionGetter.get())
    319                     .append("'");
    320             }
    321             stringBuilder
    322                 .append(" : '")
    323                 .append(mChecked)
    324                 .append("'");
    325             return stringBuilder.toString();
    326         }
    327 
    328         public String getTitle() {
    329             return mTitle;
    330         }
    331 
    332         public Drawable getIcon() {
    333             if (mIconGetter != null) {
    334                 return mIconGetter.get();
    335             } else {
    336                 return mIcon;
    337             }
    338         }
    339 
    340         public boolean isChecked() {
    341             return mChecked;
    342         }
    343     }
    344 
    345     /**
    346      * Header is a container for a sub-menu of "LayoutTreeNode" items.
    347      */
    348     public static class Header extends LayoutTreeBranch {
    349         private Appearence mAppearence = new Appearence();
    350         private int mSelectedIndex = 0;
    351         private String mDetailedDescription;
    352         private int mContentIconRes = 0;
    353 
    354         public static class Builder {
    355             private Resources mRes;
    356             private Header mHeader = new Header();
    357 
    358             public Builder(Resources res) {
    359                 mRes = res;
    360             }
    361 
    362             public Builder icon(int resId) {
    363                 mHeader.mAppearence.mIcon = mRes.getDrawable(resId);
    364                 return this;
    365             }
    366 
    367             public Builder icon(DrawableGetter drawableGetter) {
    368                 mHeader.mAppearence.mIconGetter = drawableGetter;
    369                 return this;
    370             }
    371 
    372             public Builder contentIconRes(int resId) {
    373                 mHeader.mContentIconRes = resId;
    374                 return this;
    375             }
    376 
    377             public Builder title(int resId) {
    378                 mHeader.mAppearence.mTitle = mRes.getString(resId);
    379                 return this;
    380             }
    381 
    382             public Builder description(int resId) {
    383                 mHeader.mAppearence.mDescriptionGetter = new ResourceStringGetter(mRes, resId);
    384                 return this;
    385             }
    386 
    387             public Builder title(String title) {
    388                 mHeader.mAppearence.mTitle = title;
    389                 return this;
    390             }
    391 
    392             public Builder description(String description) {
    393                 mHeader.mAppearence.mDescriptionGetter = new LiteralStringGetter(description);
    394                 return this;
    395             }
    396 
    397             public Builder description(StringGetter description) {
    398                 mHeader.mAppearence.mDescriptionGetter = description;
    399                 return this;
    400             }
    401 
    402             public Builder detailedDescription(int resId) {
    403                 mHeader.mDetailedDescription = mRes.getString(resId);
    404                 return this;
    405             }
    406 
    407             public Builder detailedDescription(String detailedDescription) {
    408                 mHeader.mDetailedDescription = detailedDescription;
    409                 return this;
    410             }
    411 
    412             public Header build() {
    413                 return mHeader;
    414             }
    415         }
    416 
    417         @Override
    418         public String getTitle() {
    419             return mAppearence.getTitle();
    420         }
    421 
    422         public Header add(LayoutTreeNode node) {
    423             node.mParent = this;
    424             mChildren.add(node);
    425             return this;
    426         }
    427 
    428         String getDetailedDescription() {
    429             return mDetailedDescription;
    430         }
    431 
    432         void Log(int level) {
    433             Log.d("Layout", indent(level) + "Header  " + mAppearence);
    434             for (LayoutTreeNode i : mChildren)
    435                 i.Log(level + 1);
    436         }
    437     }
    438 
    439     public static class Action extends LayoutTreeNode {
    440         public static final int ACTION_NONE = -1;
    441         public static final int ACTION_INTENT = -2;
    442         public static final int ACTION_BACK = -3;
    443         private int mActionId;
    444         private Intent mIntent;
    445         private Appearence mAppearence = new Appearence();
    446         private Bundle mActionData;
    447         private boolean mDefaultSelection = false;
    448 
    449         private Action(int id) {
    450             mActionId = id;
    451         }
    452 
    453         private Action(Intent intent) {
    454             mActionId = ACTION_INTENT;
    455             mIntent = intent;
    456         }
    457 
    458         public static class Builder {
    459             private Resources mRes;
    460             private Action mAction;
    461 
    462             public Builder(Resources res, int id) {
    463                 mRes = res;
    464                 mAction = new Action(id);
    465             }
    466 
    467             public Builder(Resources res, Intent intent) {
    468                 mRes = res;
    469                 mAction = new Action(intent);
    470             }
    471 
    472             public Builder title(int resId) {
    473                 mAction.mAppearence.mTitle = mRes.getString(resId);
    474                 return this;
    475             }
    476 
    477             public Builder description(int resId) {
    478                 mAction.mAppearence.mDescriptionGetter = new LiteralStringGetter(mRes.getString(
    479                         resId));
    480                 return this;
    481             }
    482 
    483             public Builder title(String title) {
    484                 mAction.mAppearence.mTitle = title;
    485                 return this;
    486             }
    487 
    488             public Builder description(String description) {
    489                 mAction.mAppearence.mDescriptionGetter = new LiteralStringGetter(description);
    490                 return this;
    491             }
    492 
    493             public Builder checked(boolean checked) {
    494                 mAction.mAppearence.mChecked = checked;
    495                 return this;
    496             }
    497 
    498             public Builder data(Bundle data) {
    499                 mAction.mActionData = data;
    500                 return this;
    501             }
    502 
    503             /*
    504              * Makes this action default initial selection when the list is displayed.
    505              */
    506             public Builder defaultSelection() {
    507                 mAction.mDefaultSelection = true;
    508                 return this;
    509             }
    510 
    511             public Action build() {
    512                 return mAction;
    513             }
    514         }
    515 
    516         void Log(int level) {
    517             Log.d("Layout", indent(level) + "Action  #" + mActionId + "  " + mAppearence);
    518         }
    519 
    520         public int getId() {
    521             return mActionId;
    522         }
    523 
    524         public Intent getIntent() {
    525             return mIntent;
    526         }
    527 
    528         @Override
    529         public String getTitle() {
    530             return mAppearence.getTitle();
    531         }
    532 
    533         public Bundle getData() {
    534             return mActionData;
    535         }
    536     }
    537 
    538     public static class Status extends LayoutTreeNode {
    539         private Appearence mAppearence = new Appearence();
    540 
    541         public static class Builder {
    542             private Resources mRes;
    543             private Status mStatus = new Status();
    544 
    545             public Builder(Resources res) {
    546                 mRes = res;
    547             }
    548 
    549             public Builder icon(int resId) {
    550                 mStatus.mAppearence.mIcon = mRes.getDrawable(resId);
    551                 return this;
    552             }
    553 
    554             public Builder title(int resId) {
    555                 mStatus.mAppearence.mTitle = mRes.getString(resId);
    556                 return this;
    557             }
    558 
    559             public Builder description(int resId) {
    560                 mStatus.mAppearence.mDescriptionGetter = new LiteralStringGetter(mRes.getString(
    561                         resId));
    562                 return this;
    563             }
    564 
    565             public Builder title(String title) {
    566                 mStatus.mAppearence.mTitle = title;
    567                 return this;
    568             }
    569 
    570             public Builder description(String description) {
    571                 mStatus.mAppearence.mDescriptionGetter = new LiteralStringGetter(description);
    572                 return this;
    573             }
    574 
    575             public Builder description(StringGetter description) {
    576                 mStatus.mAppearence.mDescriptionGetter = description;
    577                 return this;
    578             }
    579 
    580             public Status build() {
    581                 return mStatus;
    582             }
    583         }
    584 
    585         @Override
    586         public String getTitle() {
    587             return mAppearence.getTitle();
    588         }
    589 
    590         void Log(int level) {
    591             Log.d("Layout", indent(level) + "Status  " + mAppearence);
    592         }
    593     }
    594 
    595     public static class Static extends LayoutTreeNode {
    596         private String mTitle;
    597 
    598         public static class Builder {
    599             private Resources mRes;
    600             private Static mStatic = new Static();
    601 
    602             public Builder(Resources res) {
    603                 mRes = res;
    604             }
    605 
    606             public Builder title(int resId) {
    607                 mStatic.mTitle = mRes.getString(resId);
    608                 return this;
    609             }
    610 
    611             public Builder title(String title) {
    612                 mStatic.mTitle = title;
    613                 return this;
    614             }
    615 
    616             public Static build() {
    617                 return mStatic;
    618             }
    619         }
    620 
    621         @Override
    622         public String getTitle() {
    623             return mTitle;
    624         }
    625 
    626         void Log(int level) {
    627             Log.d("Layout", indent(level) + "Static  '" + mTitle + "'");
    628         }
    629     }
    630 
    631     /**
    632      * Pointer to currently visible item.
    633      */
    634     private Header mNavigationCursor;
    635 
    636     /**
    637      * Index of selected item when items are displayed. This is used by LayoutGetter to implemented
    638      * selection stability, where a LayoutGetter can arrange for a list that is refreshed regularly
    639      * to carry forward a selection.
    640      */
    641     private int mInitialItemIndex = -1;
    642     private final ArrayList<LayoutRow> mLayoutRows = new ArrayList<LayoutRow>();
    643     private final ArrayList<LayoutGetter> mVisibleLayoutGetters = new ArrayList<LayoutGetter>();
    644     private final ArrayList<LayoutTreeNode> mChildren = new ArrayList<LayoutTreeNode>();
    645     private String mTopLevelBreadcrumb = "";
    646     private LayoutNodeRefreshListener mListener;
    647 
    648     public ArrayList<LayoutRow> getLayoutRows() {
    649         return mLayoutRows;
    650     }
    651 
    652     public void setRefreshViewListener(LayoutNodeRefreshListener listener) {
    653         mListener = listener;
    654     }
    655 
    656     /**
    657      * Return the breadcrumb the user should see in the content pane.
    658      */
    659     public String getBreadcrumb() {
    660       if (mNavigationCursor.mParent == null) {
    661           // At the top level of the layout.
    662           return mTopLevelBreadcrumb;
    663       } else {
    664           // Showing a header down the hierarchy, breadcrumb is title of item above.
    665           return ((Header) (mNavigationCursor.mParent)).mAppearence.mTitle;
    666       }
    667     }
    668 
    669     /**
    670      * Navigate up one level, return true if a parent node is now visible. Return false if the
    671      * already at the top level node. The controlling fragment interprets a false return value as
    672      * "stop activity".
    673      */
    674     public boolean goBack() {
    675         if (mNavigationCursor.mParent != null) {
    676             Header u = (Header) mNavigationCursor.mParent;
    677             if (u != null) {
    678                 mNavigationCursor = u;
    679                 updateLayoutRows();
    680                 return true;
    681             }
    682         }
    683         return false;
    684     }
    685 
    686     /**
    687      * Parcelable implementation.
    688      */
    689     public Layout(Parcel in) {
    690     }
    691 
    692     public Layout() {
    693         mNavigationCursor = null;
    694     }
    695 
    696     @Override
    697     public int describeContents() {
    698         return 0;
    699     }
    700 
    701     @Override
    702     public void writeToParcel(Parcel out, int flags) {
    703     }
    704 
    705     public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
    706         public Layout createFromParcel(Parcel in) {
    707             return new Layout(in);
    708         }
    709 
    710         public Layout[] newArray(int size) {
    711             return new Layout[size];
    712         }
    713     };
    714 
    715     String getTitle() {
    716         return mNavigationCursor.mAppearence.mTitle;
    717     }
    718 
    719     Drawable getIcon() {
    720         return mNavigationCursor.mAppearence.getIcon();
    721     }
    722 
    723     String getDescription() {
    724         return mNavigationCursor.getDetailedDescription();
    725     }
    726 
    727     public void goToTitle(String title) {
    728         while (mNavigationCursor.mParent != null) {
    729             mNavigationCursor = (Header) (mNavigationCursor.mParent);
    730             if (TextUtils.equals(mNavigationCursor.mAppearence.mTitle, title)) {
    731                 break;
    732             }
    733         }
    734         updateLayoutRows();
    735     }
    736 
    737     /*
    738      * Respond to a user click on "layoutRow" and return "true" if the state of the display has
    739      * changed. A controlling fragment will respond to a "true" return by updating the view.
    740      */
    741     public boolean onClickNavigate(LayoutRow layoutRow) {
    742         LayoutTreeNode node = layoutRow.mNode;
    743         if (node instanceof Header) {
    744             mNavigationCursor.mSelectedIndex = mLayoutRows.indexOf(layoutRow);
    745             mNavigationCursor = (Header) node;
    746             updateLayoutRows();
    747             return true;
    748         }
    749         return false;
    750     }
    751 
    752     public void reloadLayoutRows() {
    753         updateLayoutRows();
    754     }
    755 
    756     public Layout add(Header header) {
    757         header.mParent = null;
    758         mChildren.add(header);
    759         return this;
    760     }
    761 
    762     public Layout add(LayoutTreeNode leaf) {
    763         leaf.mParent = null;
    764         mChildren.add(leaf);
    765         return this;
    766     }
    767 
    768     public Layout breadcrumb(String topLevelBreadcrumb) {
    769         mTopLevelBreadcrumb = topLevelBreadcrumb;
    770         return this;
    771     }
    772 
    773     /**
    774      * Sets the selected node to the first top level node with its title member equal to "title". If
    775      * "title" is null, empty, or there are no top level nodes with a title member equal to "title",
    776      * set the first node in the list as the selected.
    777      */
    778     public Layout setSelectedByTitle(String title) {
    779         for (int i = 0; i < mChildren.size(); ++i) {
    780             if (TextUtils.equals(mChildren.get(i).getTitle(), title)) {
    781                 mInitialItemIndex = i;
    782                 break;
    783             }
    784         }
    785         return this;
    786     }
    787 
    788     public void Log(int level) {
    789         for (LayoutTreeNode i : mChildren) {
    790             i.Log(level + 1);
    791         }
    792     }
    793 
    794     public void Log() {
    795         Log.d("Layout", "----- Layout");
    796         Log(0);
    797     }
    798 
    799     public void navigateToRoot() {
    800         if (mChildren.size() > 0) {
    801             mNavigationCursor = (Header) mChildren.get(0);
    802         } else {
    803             mNavigationCursor = null;
    804         }
    805         updateLayoutRows();
    806     }
    807 
    808     public int getSelectedIndex() {
    809         return mNavigationCursor.mSelectedIndex;
    810     }
    811 
    812     public void setSelectedIndex(int index) {
    813         mNavigationCursor.mSelectedIndex = index;
    814     }
    815 
    816     public void setParentSelectedIndex(int index) {
    817         if (mNavigationCursor.mParent != null) {
    818             Header u = (Header) mNavigationCursor.mParent;
    819             u.mSelectedIndex = index;
    820         }
    821     }
    822 
    823     private void addNodeListToLayoutRows(ArrayList<LayoutTreeNode> list) {
    824         for (LayoutTreeNode node : list) {
    825             if (node instanceof LayoutGetter) {
    826                 // Add subitems of "node" recursively.
    827                 LayoutGetter layoutGetter = (LayoutGetter) node;
    828                 layoutGetter.setListener(mListener);
    829                 mVisibleLayoutGetters.add(layoutGetter);
    830                 Layout layout = layoutGetter.get();
    831                 for (LayoutTreeNode child : layout.mChildren) {
    832                     child.mParent = mNavigationCursor;
    833                 }
    834                 int initialIndex = layout.mInitialItemIndex;
    835                 if (initialIndex != -1) {
    836                     mNavigationCursor.mSelectedIndex = mLayoutRows.size() + initialIndex;
    837                 }
    838                 addNodeListToLayoutRows(layout.mChildren);
    839             } else {
    840                 if (node instanceof Action && ((Action) node).mDefaultSelection) {
    841                     mNavigationCursor.mSelectedIndex = mLayoutRows.size();
    842                 }
    843                 mLayoutRows.add(new LayoutRow(node));
    844             }
    845         }
    846     }
    847 
    848     private void updateLayoutRows() {
    849         mLayoutRows.clear();
    850         for (LayoutGetter layoutGetter : mVisibleLayoutGetters) {
    851             layoutGetter.notVisible();
    852         }
    853         mVisibleLayoutGetters.clear();
    854         addNodeListToLayoutRows(mNavigationCursor.mChildren);
    855     }
    856 
    857     private static String indent(int level) {
    858         String s = new String();
    859         for (int i = 0; i < level; ++i) {
    860             s += "  ";
    861         }
    862         return s;
    863     }
    864 }
    865