1696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell/*
2696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell * Copyright (C) 2011 The Android Open Source Project
3696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell *
4696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell * Licensed under the Apache License, Version 2.0 (the "License");
5696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell * you may not use this file except in compliance with the License.
6696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell * You may obtain a copy of the License at
7696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell *
8696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell *      http://www.apache.org/licenses/LICENSE-2.0
9696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell *
10696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell * Unless required by applicable law or agreed to in writing, software
11696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell * distributed under the License is distributed on an "AS IS" BASIS,
12696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell * See the License for the specific language governing permissions and
14696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell * limitations under the License.
15696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell */
16696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
17696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powellpackage com.android.internal.view.menu;
18696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
19696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powellimport android.content.Context;
20696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powellimport android.view.LayoutInflater;
21696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powellimport android.view.View;
22696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powellimport android.view.ViewGroup;
23696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
24696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powellimport java.util.ArrayList;
25696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
26696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell/**
27696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell * Base class for MenuPresenters that have a consistent container view and item
28696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell * views. Behaves similarly to an AdapterView in that existing item views will
29696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell * be reused if possible when items change.
30696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell */
31696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powellpublic abstract class BaseMenuPresenter implements MenuPresenter {
32538e565c06e915b91e7e3a901f872ccdd9bccdd3Adam Powell    protected Context mSystemContext;
33696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    protected Context mContext;
34696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    protected MenuBuilder mMenu;
35538e565c06e915b91e7e3a901f872ccdd9bccdd3Adam Powell    protected LayoutInflater mSystemInflater;
36696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    protected LayoutInflater mInflater;
37696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    private Callback mCallback;
38696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
39696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    private int mMenuLayoutRes;
40696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    private int mItemLayoutRes;
41696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
42696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    protected MenuView mMenuView;
43696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
4411ed1d6cae9214335c92ac38498a4e6c7d1c8324Adam Powell    private int mId;
4511ed1d6cae9214335c92ac38498a4e6c7d1c8324Adam Powell
46696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    /**
47696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * Construct a new BaseMenuPresenter.
48696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     *
49538e565c06e915b91e7e3a901f872ccdd9bccdd3Adam Powell     * @param context Context for generating system-supplied views
50696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @param menuLayoutRes Layout resource ID for the menu container view
51696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @param itemLayoutRes Layout resource ID for a single item view
52696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     */
53538e565c06e915b91e7e3a901f872ccdd9bccdd3Adam Powell    public BaseMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes) {
54538e565c06e915b91e7e3a901f872ccdd9bccdd3Adam Powell        mSystemContext = context;
55538e565c06e915b91e7e3a901f872ccdd9bccdd3Adam Powell        mSystemInflater = LayoutInflater.from(context);
56696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        mMenuLayoutRes = menuLayoutRes;
57696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        mItemLayoutRes = itemLayoutRes;
58696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    }
59696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
60696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    @Override
61696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    public void initForMenu(Context context, MenuBuilder menu) {
62696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        mContext = context;
63696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        mInflater = LayoutInflater.from(mContext);
64696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        mMenu = menu;
65696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    }
66696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
67696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    @Override
68696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    public MenuView getMenuView(ViewGroup root) {
69696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        if (mMenuView == null) {
70538e565c06e915b91e7e3a901f872ccdd9bccdd3Adam Powell            mMenuView = (MenuView) mSystemInflater.inflate(mMenuLayoutRes, root, false);
71696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell            mMenuView.initialize(mMenu);
72696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell            updateMenuView(true);
73696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        }
74696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
75696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        return mMenuView;
76696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    }
77696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
78696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    /**
79696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * Reuses item views when it can
80696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     */
81696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    public void updateMenuView(boolean cleared) {
82696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        final ViewGroup parent = (ViewGroup) mMenuView;
83f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell        if (parent == null) return;
84f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell
85696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        int childIndex = 0;
86f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell        if (mMenu != null) {
87f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell            mMenu.flagActionItems();
88f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell            ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
89f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell            final int itemCount = visibleItems.size();
90f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell            for (int i = 0; i < itemCount; i++) {
91f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell                MenuItemImpl item = visibleItems.get(i);
92f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell                if (shouldIncludeItem(childIndex, item)) {
93f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell                    final View convertView = parent.getChildAt(childIndex);
9424340afbab8ad4c191784fda8023407205bc0a75Adam Powell                    final MenuItemImpl oldItem = convertView instanceof MenuView.ItemView ?
9524340afbab8ad4c191784fda8023407205bc0a75Adam Powell                            ((MenuView.ItemView) convertView).getItemData() : null;
96f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell                    final View itemView = getItemView(item, convertView, parent);
9724340afbab8ad4c191784fda8023407205bc0a75Adam Powell                    if (item != oldItem) {
9824340afbab8ad4c191784fda8023407205bc0a75Adam Powell                        // Don't let old states linger with new data.
9924340afbab8ad4c191784fda8023407205bc0a75Adam Powell                        itemView.setPressed(false);
10024340afbab8ad4c191784fda8023407205bc0a75Adam Powell                        itemView.jumpDrawablesToCurrentState();
10124340afbab8ad4c191784fda8023407205bc0a75Adam Powell                    }
102f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell                    if (itemView != convertView) {
103f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell                        addItemView(itemView, childIndex);
104f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell                    }
105f35d049b9953fbd1cd24887bac57b5e148c97846Adam Powell                    childIndex++;
106696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell                }
107696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell            }
108696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        }
109696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
110696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        // Remove leftover views.
111696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        while (childIndex < parent.getChildCount()) {
112696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell            if (!filterLeftoverView(parent, childIndex)) {
113696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell                childIndex++;
114696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell            }
115696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        }
116696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    }
117696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
118696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    /**
119696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * Add an item view at the given index.
120696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     *
121696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @param itemView View to add
122696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @param childIndex Index within the parent to insert at
123696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     */
124696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    protected void addItemView(View itemView, int childIndex) {
12545c515b0e962ee8ffc901872bcc9f25599ea0e78Adam Powell        final ViewGroup currentParent = (ViewGroup) itemView.getParent();
12645c515b0e962ee8ffc901872bcc9f25599ea0e78Adam Powell        if (currentParent != null) {
12745c515b0e962ee8ffc901872bcc9f25599ea0e78Adam Powell            currentParent.removeView(itemView);
12845c515b0e962ee8ffc901872bcc9f25599ea0e78Adam Powell        }
129696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        ((ViewGroup) mMenuView).addView(itemView, childIndex);
130696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    }
131696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
132696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    /**
133696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * Filter the child view at index and remove it if appropriate.
134696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @param parent Parent to filter from
135696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @param childIndex Index to filter
136696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @return true if the child view at index was removed
137696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     */
138696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    protected boolean filterLeftoverView(ViewGroup parent, int childIndex) {
139696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        parent.removeViewAt(childIndex);
140696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        return true;
141696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    }
142696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
143696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    public void setCallback(Callback cb) {
144696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        mCallback = cb;
145696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    }
146696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
147696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    /**
148696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * Create a new item view that can be re-bound to other item data later.
149696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     *
150696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @return The new item view
151696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     */
152696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    public MenuView.ItemView createItemView(ViewGroup parent) {
153538e565c06e915b91e7e3a901f872ccdd9bccdd3Adam Powell        return (MenuView.ItemView) mSystemInflater.inflate(mItemLayoutRes, parent, false);
154696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    }
155696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
156696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    /**
157696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * Prepare an item view for use. See AdapterView for the basic idea at work here.
158696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * This may require creating a new item view, but well-behaved implementations will
159696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * re-use the view passed as convertView if present. The returned view will be populated
160696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * with data from the item parameter.
161696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     *
162696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @param item Item to present
163696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @param convertView Existing view to reuse
164696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @param parent Intended parent view - use for inflation.
165696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @return View that presents the requested menu item
166696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     */
167696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) {
168696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        MenuView.ItemView itemView;
169696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        if (convertView instanceof MenuView.ItemView) {
170696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell            itemView = (MenuView.ItemView) convertView;
171696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        } else {
172696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell            itemView = createItemView(parent);
173696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        }
174696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        bindItemView(item, itemView);
175696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        return (View) itemView;
176696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    }
177696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
178696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    /**
179696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * Bind item data to an existing item view.
180696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     *
181696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @param item Item to bind
182696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @param itemView View to populate with item data
183696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     */
184696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    public abstract void bindItemView(MenuItemImpl item, MenuView.ItemView itemView);
185696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
186696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    /**
187696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * Filter item by child index and item data.
188696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     *
189696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @param childIndex Indended presentation index of this item
190696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @param item Item to present
191696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     * @return true if this item should be included in this menu presentation; false otherwise
192696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell     */
193696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
194696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        return true;
195696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    }
196696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
197696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
198696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        if (mCallback != null) {
199696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell            mCallback.onCloseMenu(menu, allMenusAreClosing);
200696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        }
201696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    }
202696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
203696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    public boolean onSubMenuSelected(SubMenuBuilder menu) {
204696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        if (mCallback != null) {
205696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell            return mCallback.onOpenSubMenu(menu);
206696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        }
207696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        return false;
208696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    }
209696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell
210696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    public boolean flagActionItems() {
211696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell        return false;
212696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell    }
2138d02deabac62c4a68a335a7b3141795466362b89Adam Powell
2148d02deabac62c4a68a335a7b3141795466362b89Adam Powell    public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
2158d02deabac62c4a68a335a7b3141795466362b89Adam Powell        return false;
2168d02deabac62c4a68a335a7b3141795466362b89Adam Powell    }
2178d02deabac62c4a68a335a7b3141795466362b89Adam Powell
2188d02deabac62c4a68a335a7b3141795466362b89Adam Powell    public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
2198d02deabac62c4a68a335a7b3141795466362b89Adam Powell        return false;
2208d02deabac62c4a68a335a7b3141795466362b89Adam Powell    }
22111ed1d6cae9214335c92ac38498a4e6c7d1c8324Adam Powell
22211ed1d6cae9214335c92ac38498a4e6c7d1c8324Adam Powell    public int getId() {
22311ed1d6cae9214335c92ac38498a4e6c7d1c8324Adam Powell        return mId;
22411ed1d6cae9214335c92ac38498a4e6c7d1c8324Adam Powell    }
22511ed1d6cae9214335c92ac38498a4e6c7d1c8324Adam Powell
22611ed1d6cae9214335c92ac38498a4e6c7d1c8324Adam Powell    public void setId(int id) {
22711ed1d6cae9214335c92ac38498a4e6c7d1c8324Adam Powell        mId = id;
22811ed1d6cae9214335c92ac38498a4e6c7d1c8324Adam Powell    }
229696cba573e651b0e4f18a4718627c8ccecb3bda0Adam Powell}
230