/* * Tint Browser for Android * * Copyright (C) 2012 - to infinity and beyond J. Devauchelle and contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 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. */ package com.seal.model; import android.content.Context; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.DateSorter; import android.widget.BaseExpandableListAdapter; import android.widget.CheckBox; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.ImageView; import android.widget.TextView; import com.seal.R; import com.seal.providers.BookmarksProvider; /** * Custom adapter for displaying history, splitted in bins. Adapted from: https://github.com/CyanogenMod/android_packages_apps_Browser/blob/gingerbread/src/com/android/browser/BrowserHistoryPage.java http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android-apps/2.2_r1.1/com/android/browser/DateSortedExpandableListAdapter.java/?v=source */ public class HistoryAdapter extends BaseExpandableListAdapter { private LayoutInflater mInflater = null; private int[] mItemMap; private int mNumberOfBins; private DateSorter mDateSorter; private int mIdIndex; private Context mContext; private Cursor mCursor; private int mDateIndex; private int mFaviconSize; private OnCheckedChangeListener mBookmarkStarChangeListener; /** * Constructor. * * @param context * The current context. * @param cursor * The data cursor. * @param dateIndex * The date index ? */ public HistoryAdapter(Context context, OnCheckedChangeListener bookmarksChangeListener, int faviconSize) { mContext = context; mBookmarkStarChangeListener = bookmarksChangeListener; mCursor = null; mDateIndex = -1; mFaviconSize = faviconSize; mDateSorter = new DateSorter(mContext); mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } public void changeCursor(Cursor cursor) { if (mCursor == cursor) { return; } mCursor = cursor; if (mCursor != null) { mIdIndex = mCursor.getColumnIndexOrThrow(BookmarksProvider.Columns._ID); mDateIndex = mCursor.getColumnIndexOrThrow(BookmarksProvider.Columns.VISITED_DATE); buildMap(); notifyDataSetChanged(); } else { mIdIndex = -1; mDateIndex = -1; notifyDataSetInvalidated(); } } /** * Get a long-typed data from mCursor. * * @param cursorIndex * The column index. * @return The long data. */ private long getLong(int cursorIndex) { return mCursor.getLong(cursorIndex); } /** * Split the data in the cursor into several "bins": today, yesterday, last 7 days, last month, older. */ private void buildMap() { int[] array = new int[DateSorter.DAY_COUNT]; // Zero out the array. for (int j = 0; j < DateSorter.DAY_COUNT; j++) { array[j] = 0; } mNumberOfBins = 0; int dateIndex = -1; if (mCursor.moveToFirst() && mCursor.getCount() > 0) { while (!mCursor.isAfterLast()) { long date = getLong(mDateIndex); int index = mDateSorter.getIndex(date); if (index > dateIndex) { mNumberOfBins++; if (index == DateSorter.DAY_COUNT - 1) { // We are already in the last bin, so it will // include all the remaining items array[index] = mCursor.getCount() - mCursor.getPosition(); break; } dateIndex = index; } array[dateIndex]++; mCursor.moveToNext(); } } mItemMap = array; } /** * Translates from a group position in the ExpandableList to a bin. This is necessary because some groups have no history items, so we do not include those in the ExpandableList. * * @param groupPosition * Position in the ExpandableList's set of groups * @return The corresponding bin that holds that group. */ private int groupPositionToBin(int groupPosition) { if (groupPosition < 0 || groupPosition >= DateSorter.DAY_COUNT) { throw new AssertionError("group position out of range"); } if (DateSorter.DAY_COUNT == mNumberOfBins || 0 == mNumberOfBins) { // In the first case, we have exactly the same number of bins // as our maximum possible, so there is no need to do a // conversion // The second statement is in case this method gets called when // the array is empty, in which case the provided groupPosition // will do fine. return groupPosition; } int arrayPosition = -1; while (groupPosition > -1) { arrayPosition++; if (mItemMap[arrayPosition] != 0) { groupPosition--; } } return arrayPosition; } /** * Move the cursor to the record corresponding to the given group position and child position. * * @param groupPosition * The group position. * @param childPosition * The child position. * @return True if the move has succeeded. */ private boolean moveCursorToChildPosition(int groupPosition, int childPosition) { if (mCursor.isClosed()) { return false; } groupPosition = groupPositionToBin(groupPosition); int index = childPosition; for (int i = 0; i < groupPosition; i++) { index += mItemMap[i]; } return mCursor.moveToPosition(index); } /** * Create a new child view. * * @return The created view. */ private View getCustomChildView() { return mInflater.inflate(R.layout.history_row, null, false); } @Override public Object getChild(int groupPosition, int childPosition) { moveCursorToChildPosition(groupPosition, childPosition); return new BookmarkHistoryItem(mCursor.getLong(mCursor.getColumnIndex(BookmarksProvider.Columns._ID)), mCursor.getString(mCursor .getColumnIndex(BookmarksProvider.Columns.TITLE)), mCursor.getString(mCursor.getColumnIndex(BookmarksProvider.Columns.URL)), mCursor.getInt(mCursor.getColumnIndex(BookmarksProvider.Columns.BOOKMARK)) >= 1 ? true : false, mCursor.getInt(mCursor .getColumnIndex(BookmarksProvider.Columns.IS_FOLDER)) >= 1 ? true : false, mCursor.getLong(mCursor .getColumnIndex(BookmarksProvider.Columns.PARENT_FOLDER_ID)), mCursor.getBlob(mCursor.getColumnIndex(BookmarksProvider.Columns.FAVICON))); } @Override public long getChildId(int groupPosition, int childPosition) { if (moveCursorToChildPosition(groupPosition, childPosition)) { return getLong(mIdIndex); } return 0; } @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { View view = getCustomChildView(); TextView titleView = (TextView) view.findViewById(R.id.HistoryRow_Title); BookmarkHistoryItem item = (BookmarkHistoryItem) getChild(groupPosition, childPosition); titleView.setText(item.getTitle()); TextView urlView = (TextView) view.findViewById(R.id.HistoryRow_Url); urlView.setText(item.getUrl()); CheckBox bookmarkStar = (CheckBox) view.findViewById(R.id.HistoryRow_BookmarkStar); bookmarkStar.setTag(item.getId()); bookmarkStar.setOnCheckedChangeListener(null); bookmarkStar.setChecked(item.isBookmark()); bookmarkStar.setOnCheckedChangeListener(mBookmarkStarChangeListener); ImageView faviconView = (ImageView) view.findViewById(R.id.HistoryRow_Thumbnail); Bitmap favicon = item.getFavicon(); if (favicon != null) { BitmapDrawable icon = new BitmapDrawable(view.getResources(), favicon); Bitmap bm = Bitmap.createBitmap(mFaviconSize, mFaviconSize, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bm); icon.setBounds(0, 0, mFaviconSize, mFaviconSize); icon.draw(canvas); faviconView.setImageBitmap(bm); } else { faviconView.setImageResource(R.drawable.app_web_browser_sm); } return view; } @Override public int getChildrenCount(int groupPosition) { if (mItemMap != null) { return mItemMap[groupPositionToBin(groupPosition)]; } else { return 0; } } @Override public Object getGroup(int groupPosition) { int binIndex = groupPositionToBin(groupPosition); switch (binIndex) { case 0: return mContext.getResources().getString(R.string.HistoryToday); case 1: return mContext.getResources().getString(R.string.HistoryYesterday); case 2: return mContext.getResources().getString(R.string.HistoryLastSevenDays); case 3: return mContext.getResources().getString(R.string.HistoryLastMonth); default: return mContext.getResources().getString(R.string.HistoryOlder); } } @Override public int getGroupCount() { return mNumberOfBins; } @Override public long getGroupId(int groupPosition) { return groupPosition; } @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { TextView item; if ((convertView == null) || (!(convertView instanceof TextView))) { LayoutInflater factory = LayoutInflater.from(mContext); item = (TextView) factory.inflate(R.layout.expandable_list_header, null); } else { item = (TextView) convertView; } item.setText(getGroup(groupPosition).toString()); return item; } @Override public boolean hasStableIds() { return true; } @Override public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } }