/* * AndFHEM - Open Source Android application to control a FHEM home automation * server. * * Copyright (c) 2012, Matthias Klass or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU GENERAL PUBLICLICENSE, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU GENERAL PUBLIC LICENSE * for more details. * * You should have received a copy of the GNU GENERAL PUBLIC LICENSE * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor */ package li.klass.fhem.widget; import android.content.Context; import android.database.DataSetObserver; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import com.google.common.collect.Maps; import java.util.List; import java.util.Map; import java.util.Set; import static com.google.common.collect.Lists.newArrayList; public abstract class GridViewWithSectionsAdapter<P, C> extends BaseAdapter { public static final String TAG = GridViewWithSectionsAdapter.class.getName(); private Map<Integer, P> parentPositions; private int totalNumberOfItems; protected final Context context; protected final LayoutInflater layoutInflater; private int currentRowIndex; private int currentRowParentIndex; private int currentRowHeight; private List<View> currentRowViews = newArrayList(); private int numberOfColumns = -1; public GridViewWithSectionsAdapter(Context context) { this.context = context; layoutInflater = LayoutInflater.from(context); } public void updateParentPositions() { List<P> parents = getDeviceGroupParents(); Log.v(TAG, "updating parent positions for parent count " + parents.size()); parentPositions = Maps.newHashMap(); int numberOfColumns = getNumberOfColumns(); int currentPosition = 0; for (P parent : parents) { parentPositions.put(currentPosition, parent); // add all the children plus an offset to complete the grid row int childCount = getChildrenCountForParent(parent); int filledItemsWithinTheRow = childCount % numberOfColumns; int childOffset = filledItemsWithinTheRow == 0 ? 0 : numberOfColumns - filledItemsWithinTheRow; currentPosition += childCount + childOffset; // add the parent row currentPosition += numberOfColumns; } totalNumberOfItems = currentPosition; Log.v(TAG, "found " + totalNumberOfItems + " items"); } @Override public int getCount() { Log.v(TAG, "returning totalNumberOfItems: " + totalNumberOfItems); return totalNumberOfItems; } @Override public Object getItem(int position) { P parent = getParentForPosition(position); if (parent != null) { return parent; } int parentPosition = findParentPositionForChildPosition(position); parent = parentPositions.get(parentPosition); int relativeChildPosition = position - parentPosition; return getChildForParentAndChildPosition(parent, relativeChildPosition); } public int findParentPositionForChildPosition(int flatPosition) { Set<Integer> keyPositions = parentPositions.keySet(); int bestKeyMatch = 0; int bestKeyDiff = -1; for (int keyPosition : keyPositions) { int diff = flatPosition - keyPosition; if (diff >= 0 && (bestKeyDiff == -1 || diff < bestKeyDiff)) { bestKeyDiff = diff; bestKeyMatch = keyPosition; } } return bestKeyMatch; } @Override public long getItemId(int i) { return i; } @Override public View getView(int flatPosition, View view, ViewGroup viewGroup) { Log.v(TAG, "drawing flatPosition " + flatPosition + "/" + totalNumberOfItems); try { int parentBasePosition = getParentBasePosition(flatPosition); if (parentBasePosition != -1) { P parent = parentPositions.get(parentBasePosition); int parentOffset = flatPosition - parentBasePosition; return getParentView(parent, parentOffset, view, viewGroup); } else { int parentPosition = findParentPositionForChildPosition(flatPosition); P parent = parentPositions.get(parentPosition); int relativeChildPosition = flatPosition - parentPosition - getNumberOfColumns(); C child = getChildForParentAndChildPosition(parent, relativeChildPosition); View childView = getChildView(parent, parentPosition, child, view, viewGroup); if (getNumberOfColumns() > 1) { updateChildrenRowHeight(getNumberOfColumns(), parentPosition, relativeChildPosition, childView); } return childView; } } catch (Exception e) { Log.e(TAG, "error occurred", e); return null; } } private void updateChildrenRowHeight(int columns, int parentIndex, int childOffset, View childView) { int rowIndex = getRowForChildOffset(columns, childOffset); if (currentRowParentIndex != parentIndex || currentRowIndex != rowIndex) { currentRowViews.clear(); currentRowHeight = 0; } int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(500, View.MeasureSpec.EXACTLY); int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); childView.measure(widthMeasureSpec, heightMeasureSpec); int measuredHeight = childView.getMeasuredHeight(); currentRowViews.add(childView); if (measuredHeight > currentRowHeight) { currentRowHeight = measuredHeight; } setHeightForViews(currentRowHeight, currentRowViews); currentRowIndex = rowIndex; currentRowParentIndex = parentIndex; } private int getRowForChildOffset(int columns, int childOffset) { return childOffset / columns; } private void setHeightForViews(int height, List<View> views) { for (View view : views) { ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); if (layoutParams == null) { layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } layoutParams.height = height; view.setLayoutParams(layoutParams); } } protected P getParentForPosition(int position) { int basePosition = getParentBasePosition(position); if (basePosition == -1) return null; return parentPositions.get(basePosition); } protected int getParentBasePosition(int position) { int numberOfColumns = getNumberOfColumns(); for (Integer key : parentPositions.keySet()) { if (key <= position && key + numberOfColumns > position) { return key; } } return -1; } protected void updateData() { updateParentPositions(); notifyDataSetChanged(); } public int findOriginalParentPosition(int flatPosition) { P parent = getParentForPosition(flatPosition); if (parent != null) { parent = parentPositions.get(flatPosition); } else { parent = parentPositions.get(findParentPositionForChildPosition(flatPosition)); } List<P> parents = getDeviceGroupParents(); for (int i = 0; i < parents.size(); i++) { if (parents.get(i).equals(parent)) { return i; } } return -1; } @Override public boolean isEmpty() { return totalNumberOfItems == 0; } protected int getNumberOfColumns() { if (numberOfColumns <= 0) return 1; return numberOfColumns; } public void setNumberOfColumns(int numberOfColumns) { this.numberOfColumns = numberOfColumns; Log.d(TAG, "set grid view to " + numberOfColumns + " columns"); updateParentPositions(); notifyDataSetChanged(); } protected int getFlatPositionForParentAndChild(P parent, C child) { int parentPosition = getParentPositionFor(parent); if (parentPosition == -1) return -1; int count = getChildrenCountForParent(parent); for (int i = 0; i < count; i++) { C aChild = getChildForParentAndChildPosition(parent, i); if (aChild.equals(child)) { return parentPosition + i; } } return -1; } protected int getParentPositionFor(P parent) { for (Integer position : parentPositions.keySet()) { if (parentPositions.get(position).equals(parent)) { return position; } } return -1; } @Override public void unregisterDataSetObserver(DataSetObserver observer) { // Workaround for a silly bug in Android 4 // see http://code.google.com/p/android/issues/detail?id=22946 for details if (observer != null) { super.unregisterDataSetObserver(observer); } } protected abstract C getChildForParentAndChildPosition(P parent, int childPosition); protected abstract int getChildrenCountForParent(P parent); protected abstract View getParentView(P parent, int parentOffset, View view, ViewGroup viewGroup); protected abstract View getChildView(P parent, int parentPosition, C child, View view, ViewGroup viewGroup); protected abstract List<P> getDeviceGroupParents(); protected abstract int getRequiredColumnWidth(); }