diff options
Diffstat (limited to 'library/main/src/com')
10 files changed, 475 insertions, 39 deletions
diff --git a/library/main/src/com/android/setupwizardlib/SetupWizardItemsLayout.java b/library/main/src/com/android/setupwizardlib/SetupWizardItemsLayout.java index fbf6618..0a03878 100644 --- a/library/main/src/com/android/setupwizardlib/SetupWizardItemsLayout.java +++ b/library/main/src/com/android/setupwizardlib/SetupWizardItemsLayout.java @@ -26,6 +26,8 @@ import com.android.setupwizardlib.items.ItemInflater; public class SetupWizardItemsLayout extends SetupWizardListLayout { + private ItemAdapter mAdapter; + public SetupWizardItemsLayout(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs, 0); @@ -42,8 +44,13 @@ public class SetupWizardItemsLayout extends SetupWizardListLayout { int xml = a.getResourceId(R.styleable.SuwSetupWizardItemsLayout_android_entries, 0); if (xml != 0) { ItemGroup inflated = (ItemGroup) new ItemInflater(context).inflate(xml); - setAdapter(ItemAdapter.create(inflated)); + mAdapter = new ItemAdapter(inflated); + setAdapter(mAdapter); } a.recycle(); } + + public ItemAdapter getAdapter() { + return mAdapter; + } } diff --git a/library/main/src/com/android/setupwizardlib/items/AbstractItem.java b/library/main/src/com/android/setupwizardlib/items/AbstractItem.java new file mode 100644 index 0000000..047d18a --- /dev/null +++ b/library/main/src/com/android/setupwizardlib/items/AbstractItem.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.setupwizardlib.items; + +import android.content.Context; +import android.util.AttributeSet; + +/** + * Abstract implementation of an item, which implements {@link IItem} and takes care of implementing + * methods for {@link ItemHierarchy} for items representing itself. + */ +public abstract class AbstractItem extends AbstractItemHierarchy implements IItem { + + public AbstractItem() { + super(); + } + + public AbstractItem(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public int getCount() { + return 1; + } + + @Override + public IItem getItemAt(int position) { + return this; + } + + @Override + public ItemHierarchy findItemById(int id) { + if (id == getId()) { + return this; + } + return null; + } +} diff --git a/library/main/src/com/android/setupwizardlib/items/AbstractItemHierarchy.java b/library/main/src/com/android/setupwizardlib/items/AbstractItemHierarchy.java new file mode 100644 index 0000000..c56e3bc --- /dev/null +++ b/library/main/src/com/android/setupwizardlib/items/AbstractItemHierarchy.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.setupwizardlib.items; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +import com.android.setupwizardlib.R; + +import java.util.ArrayList; + +/** + * An abstract item hierarchy; provides default implementation for ID and observers. + */ +public abstract class AbstractItemHierarchy implements ItemHierarchy { + + private ArrayList<Observer> mObservers = new ArrayList<>(); + private int mId = 0; + + public AbstractItemHierarchy() { + } + + public AbstractItemHierarchy(Context context, AttributeSet attrs) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuwAbstractItem); + mId = a.getResourceId(R.styleable.SuwAbstractItem_android_id, 0); + a.recycle(); + } + + public void setId(int id) { + mId = id; + } + + public int getId() { + return mId; + } + + @Override + public void registerObserver(Observer observer) { + mObservers.add(observer); + } + + @Override + public void unregisterObserver(Observer observer) { + mObservers.remove(observer); + } + + public void notifyChanged() { + for (Observer observer : mObservers) { + observer.onChanged(this); + } + } +} diff --git a/library/main/src/com/android/setupwizardlib/items/GenericInflater.java b/library/main/src/com/android/setupwizardlib/items/GenericInflater.java index af4ffcc..f3b6e19 100644 --- a/library/main/src/com/android/setupwizardlib/items/GenericInflater.java +++ b/library/main/src/com/android/setupwizardlib/items/GenericInflater.java @@ -39,8 +39,6 @@ import android.view.InflateException; * implementing {@link #onAddChildItem(Object, Object)}. * * @param <T> Type of the items to inflate - * - * Modified from android.preference.GenericInflater */ public abstract class GenericInflater<T> { diff --git a/library/main/src/com/android/setupwizardlib/items/IItem.java b/library/main/src/com/android/setupwizardlib/items/IItem.java new file mode 100644 index 0000000..26391dc --- /dev/null +++ b/library/main/src/com/android/setupwizardlib/items/IItem.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.setupwizardlib.items; + +import android.view.View; + +/** + * Representation of an item in an {@link ItemHierarchy}. + */ +public interface IItem { + + /** + * Get the Android resource ID for locating the layout for this item. + * + * @return Resource ID for the layout of this item. This layout will be used to inflate the View + * passed to {@link #onBindView(android.view.View)}. + */ + int getLayoutResource(); + + /** + * Called by items framework to display the data specified by this item. This method should + * update {@code view} to reflect its data. + * + * @param view A view inflated from {@link #getLayoutResource()}, which should be updated to + * display data from this item. This view may be recycled from other items with the + * same layout resource. + */ + void onBindView(View view); + + /** + * @return True if this item is enabled. + */ + boolean isEnabled(); +} diff --git a/library/main/src/com/android/setupwizardlib/items/Item.java b/library/main/src/com/android/setupwizardlib/items/Item.java index 63baa11..e208848 100644 --- a/library/main/src/com/android/setupwizardlib/items/Item.java +++ b/library/main/src/com/android/setupwizardlib/items/Item.java @@ -30,24 +30,24 @@ import com.android.setupwizardlib.R; * Definition of an item in SetupWizardItemsLayout. An item is usually defined in XML and inflated * using {@link ItemInflater}. */ -public class Item { +public class Item extends AbstractItem { private boolean mEnabled = true; private Drawable mIcon; - private int mId = 0; private int mLayoutRes; private CharSequence mSummary; private CharSequence mTitle; public Item() { + super(); mLayoutRes = getDefaultLayoutResource(); } public Item(Context context, AttributeSet attrs) { + super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuwItem); mEnabled = a.getBoolean(R.styleable.SuwItem_android_enabled, true); mIcon = a.getDrawable(R.styleable.SuwItem_android_icon); - mId = a.getResourceId(R.styleable.SuwItem_android_id, 0); mTitle = a.getText(R.styleable.SuwItem_android_title); mSummary = a.getText(R.styleable.SuwItem_android_summary); mLayoutRes = a.getResourceId(R.styleable.SuwItem_android_layout, @@ -63,6 +63,7 @@ public class Item { mEnabled = enabled; } + @Override public boolean isEnabled() { return mEnabled; } @@ -75,18 +76,11 @@ public class Item { return mIcon; } - public void setId(int id) { - mId = id; - } - - public int getId() { - return mId; - } - public void setLayoutResource(int layoutResource) { mLayoutRes = layoutResource; } + @Override public int getLayoutResource() { return mLayoutRes; } @@ -107,6 +101,7 @@ public class Item { return mTitle; } + @Override public void onBindView(View view) { TextView label = (TextView) view.findViewById(R.id.suw_items_title); label.setText(getTitle()); @@ -124,6 +119,7 @@ public class Item { final Drawable icon = getIcon(); if (icon != null) { final ImageView iconView = (ImageView) view.findViewById(R.id.suw_items_icon); + iconView.setImageState(icon.getState(), false /* merge */); iconView.setImageLevel(icon.getLevel()); iconView.setImageDrawable(icon); iconContainer.setVisibility(View.VISIBLE); diff --git a/library/main/src/com/android/setupwizardlib/items/ItemAdapter.java b/library/main/src/com/android/setupwizardlib/items/ItemAdapter.java index 3fad091..669d104 100644 --- a/library/main/src/com/android/setupwizardlib/items/ItemAdapter.java +++ b/library/main/src/com/android/setupwizardlib/items/ItemAdapter.java @@ -23,31 +23,29 @@ import android.view.ViewGroup; import android.widget.BaseAdapter; /** - * An adapter typically used with ListView to display a list of Items. The list of items used to - * create this adapter can be inflated by {@link ItemInflater} from XML. + * An adapter typically used with ListView to display an + * {@link com.android.setupwizardlib.items.ItemHierarchy}. The item hierarchy used to create this + * adapter can be inflated by {@link ItemInflater} from XML. */ -public class ItemAdapter extends BaseAdapter { +public class ItemAdapter extends BaseAdapter implements ItemHierarchy.Observer { - private final Item[] mItems; + private final ItemHierarchy mItemHierarchy; private ViewTypes mViewTypes = new ViewTypes(); - public static ItemAdapter create(ItemGroup items) { - return new ItemAdapter(items.getChildren()); - } - - public ItemAdapter(Item[] items) { - mItems = items; + public ItemAdapter(ItemHierarchy hierarchy) { + mItemHierarchy = hierarchy; + mItemHierarchy.registerObserver(this); refreshViewTypes(); } @Override public int getCount() { - return mItems.length; + return mItemHierarchy.getCount(); } @Override - public Item getItem(int position) { - return mItems[position]; + public IItem getItem(int position) { + return mItemHierarchy.getItemAt(position); } @Override @@ -57,7 +55,8 @@ public class ItemAdapter extends BaseAdapter { @Override public int getItemViewType(int position) { - int layoutRes = getItem(position).getLayoutResource(); + IItem item = getItem(position); + int layoutRes = item.getLayoutResource(); return mViewTypes.get(layoutRes); } @@ -66,15 +65,16 @@ public class ItemAdapter extends BaseAdapter { return mViewTypes.size(); } - public void refreshViewTypes() { - for (Item item : mItems) { + private void refreshViewTypes() { + for (int i = 0; i < getCount(); i++) { + IItem item = getItem(i); mViewTypes.add(item.getLayoutResource()); } } @Override public View getView(int position, View convertView, ViewGroup parent) { - Item item = getItem(position); + IItem item = getItem(position); if (convertView == null) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); convertView = inflater.inflate(item.getLayoutResource(), parent, false); @@ -83,6 +83,25 @@ public class ItemAdapter extends BaseAdapter { return convertView; } + @Override + public void onChanged(ItemHierarchy hierarchy) { + refreshViewTypes(); + notifyDataSetChanged(); + } + + @Override + public boolean isEnabled(int position) { + return getItem(position).isEnabled(); + } + + public ItemHierarchy findItemById(int id) { + return mItemHierarchy.findItemById(id); + } + + public ItemHierarchy getRootItemHierarchy() { + return mItemHierarchy; + } + /** * A helper class to pack a sparse set of integers (e.g. resource IDs) to a contiguous list of * integers (e.g. adapter positions), providing mapping to retrieve the original ID from a given diff --git a/library/main/src/com/android/setupwizardlib/items/ItemGroup.java b/library/main/src/com/android/setupwizardlib/items/ItemGroup.java index 3752e68..3e500bb 100644 --- a/library/main/src/com/android/setupwizardlib/items/ItemGroup.java +++ b/library/main/src/com/android/setupwizardlib/items/ItemGroup.java @@ -18,23 +18,196 @@ package com.android.setupwizardlib.items; import android.content.Context; import android.util.AttributeSet; +import android.util.SparseIntArray; import java.util.ArrayList; import java.util.List; -public class ItemGroup extends Item { +public class ItemGroup extends AbstractItemHierarchy implements ItemHierarchy.Observer { - private List<Item> mItems = new ArrayList<>(); + /* static section */ + + /** + * Binary search for the closest value that's smaller than or equal to {@code value}, and + * return the corresponding key. + */ + private static int binarySearch(SparseIntArray array, int value) { + final int size = array.size(); + int lo = 0; + int hi = size - 1; + + while (lo <= hi) { + final int mid = (lo + hi) >>> 1; + final int midVal = array.valueAt(mid); + + if (midVal < value) { + lo = mid + 1; + } else if (midVal > value) { + hi = mid - 1; + } else { + return array.keyAt(mid); // value found + } + } + // Value not found. Return the last item before our search range, which is the closest + // value smaller than the value we are looking for. + return array.keyAt(lo - 1); + } + + /* non-static section */ + + private List<ItemHierarchy> mChildren = new ArrayList<>(); + + /** + * A mapping from the index of an item hierarchy in mChildren, to the first position in which + * the corresponding child hierarchy represents. For example: + * + * ItemHierarchy Item Item Position + * Index + * + * 0 [ Wi-Fi AP 1 ] 0 + * | Wi-Fi AP 2 | 1 + * | Wi-Fi AP 3 | 2 + * | Wi-Fi AP 4 | 3 + * [ Wi-Fi AP 5 ] 4 + * + * 1 [ <Empty Item Hierarchy> ] + * + * 2 [ Use cellular data ] 5 + * + * 3 [ Don't connect ] 6 + * + * For this example of Wi-Fi screen, the following mapping will be produced: + * [ 0 -> 0 | 2 -> 5 | 3 -> 6 ] + * + * Also note how ItemHierarchy index 1 is not present in the map, because it is empty. + * + * ItemGroup uses this map to look for which ItemHierarchy an item at a given position belongs + * to. + */ + private SparseIntArray mHierarchyStart = new SparseIntArray(); + + private int mCount = 0; + private boolean mDirty = false; + + public ItemGroup() { + super(); + } public ItemGroup(Context context, AttributeSet attrs) { + // Constructor for XML inflation super(context, attrs); } - public void addChild(Item child) { - mItems.add(child); + /** + * Add a child hierarchy to this item group. + */ + public void addChild(ItemHierarchy child) { + mChildren.add(child); + child.registerObserver(this); + onHierarchyChanged(); + } + + /** + * Remove a previously added child from this item group. + * + * @return True if there is a match for the child and it is removed. False if the child could + * not be found in our list of child hierarchies. + */ + public boolean removeChild(ItemHierarchy child) { + if (mChildren.remove(child)) { + child.unregisterObserver(this); + onHierarchyChanged(); + return true; + } + return false; + } + + /** + * Remove all children from this hierarchy. + */ + public void clear() { + if (mChildren.size() == 0) { + return; + } + + for (ItemHierarchy item : mChildren) { + item.unregisterObserver(this); + } + mChildren.clear(); + onHierarchyChanged(); + } + + @Override + public int getCount() { + updateDataIfNeeded(); + return mCount; + } + + @Override + public IItem getItemAt(int position) { + int itemIndex = getItemIndex(position); + ItemHierarchy item = mChildren.get(itemIndex); + int subpos = position - mHierarchyStart.get(itemIndex); + return item.getItemAt(subpos); + } + + @Override + public void onChanged(ItemHierarchy hierarchy) { + // Need to set dirty, because our children may have gotten more items. + mDirty = true; + notifyChanged(); + } + + private void onHierarchyChanged() { + onChanged(null); + } + + @Override + public ItemHierarchy findItemById(int id) { + if (id == getId()) { + return this; + } + for (ItemHierarchy child : mChildren) { + ItemHierarchy childFindItem = child.findItemById(id); + if (childFindItem != null) { + return childFindItem; + } + } + return null; + } + + /** + * If dirty, this method will recalculate the number of items and mHierarchyStart. + */ + private void updateDataIfNeeded() { + if (mDirty) { + mCount = 0; + mHierarchyStart.clear(); + for (int itemIndex = 0; itemIndex < mChildren.size(); itemIndex++) { + ItemHierarchy item = mChildren.get(itemIndex); + if (item.getCount() > 0) { + mHierarchyStart.put(itemIndex, mCount); + } + mCount += item.getCount(); + } + mDirty = false; + } } - public Item[] getChildren() { - return mItems.toArray(new Item[mItems.size()]); + /** + * Use binary search to locate the item hierarchy a position is contained in. + * + * @return Index of the item hierarchy which is responsible for the item at {@code position}. + */ + private int getItemIndex(int position) { + updateDataIfNeeded(); + if (position < 0 || position >= mCount) { + throw new IndexOutOfBoundsException("size=" + mCount + "; index=" + position); + } + int result = binarySearch(mHierarchyStart, position); + if (result < 0) { + throw new IllegalStateException("Cannot have item start index < 0"); + } + return result; } } diff --git a/library/main/src/com/android/setupwizardlib/items/ItemHierarchy.java b/library/main/src/com/android/setupwizardlib/items/ItemHierarchy.java new file mode 100644 index 0000000..40a56d5 --- /dev/null +++ b/library/main/src/com/android/setupwizardlib/items/ItemHierarchy.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.setupwizardlib.items; + +/** + * Representation of zero or more items in a list. Each instance of ItemHierarchy should be capable + * of being wrapped in ItemAdapter and be displayed. + * + * For example, {@link com.android.setupwizardlib.items.Item} is a representation of a single item, + * typically with data provided from XML. {@link com.android.setupwizardlib.items.ItemGroup} + * represents a list of child item hierarchies it contains, but itself does not do any display. + */ +public interface ItemHierarchy { + + /** + * Observer for any changes in this hierarchy. If anything updated that causes this hierarchy to + * show different content, this observer should be called. + */ + interface Observer { + /** + * Called when an underlying data update that can cause this hierarchy to show different + * content has occurred. + */ + void onChanged(ItemHierarchy itemHierarchy); + } + + /** + * Register an observer to observe changes for this item hierarchy. + */ + void registerObserver(Observer observer); + + /** + * Unregister a previously registered observer. + */ + void unregisterObserver(Observer observer); + + /** + * @return the number of items this item hierarchy represent. + */ + int getCount(); + + /** + * Get the item at position. + * + * @param position An integer from 0 to {@link #getCount()}}, which indicates the position in + * this item hierarchy to get the child item. + * @return A representation of the item at {@code position}. Must not be {@code null}. + */ + IItem getItemAt(int position); + + /** + * Find an item hierarchy within this hierarchy which has the given ID. Or null if no match is + * found. This hierarchy will be returned if our ID matches. Same restrictions for Android + * resource IDs apply to this ID. In fact, typically this ID is a resource ID generated from + * XML. + * + * @param id An ID to search for in this item hierarchy. + * @return An ItemHierarchy which matches the given ID. + */ + ItemHierarchy findItemById(int id); +} diff --git a/library/main/src/com/android/setupwizardlib/items/ItemInflater.java b/library/main/src/com/android/setupwizardlib/items/ItemInflater.java index e25e2ad..6bd77ac 100644 --- a/library/main/src/com/android/setupwizardlib/items/ItemInflater.java +++ b/library/main/src/com/android/setupwizardlib/items/ItemInflater.java @@ -23,7 +23,7 @@ import android.content.Context; * * Modified from android.support.v7.preference.PreferenceInflater */ -public class ItemInflater extends GenericInflater<Item> { +public class ItemInflater extends GenericInflater<ItemHierarchy> { private static final String TAG = "ItemInflater"; @@ -49,7 +49,7 @@ public class ItemInflater extends GenericInflater<Item> { } @Override - protected void onAddChildItem(Item parent, Item child) { + protected void onAddChildItem(ItemHierarchy parent, ItemHierarchy child) { if (parent instanceof ItemGroup) { ((ItemGroup) parent).addChild(child); } else { |