mobile/android/base/home/MostRecentPanel.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 package org.mozilla.gecko.home;
michael@0 7
michael@0 8 import java.util.Date;
michael@0 9 import java.util.EnumSet;
michael@0 10
michael@0 11 import org.mozilla.gecko.R;
michael@0 12 import org.mozilla.gecko.Telemetry;
michael@0 13 import org.mozilla.gecko.TelemetryContract;
michael@0 14 import org.mozilla.gecko.db.BrowserContract.Combined;
michael@0 15 import org.mozilla.gecko.db.BrowserDB;
michael@0 16 import org.mozilla.gecko.db.BrowserDB.URLColumns;
michael@0 17 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
michael@0 18
michael@0 19 import android.app.Activity;
michael@0 20 import android.content.ContentResolver;
michael@0 21 import android.content.Context;
michael@0 22 import android.database.Cursor;
michael@0 23 import android.os.Bundle;
michael@0 24 import android.support.v4.app.LoaderManager.LoaderCallbacks;
michael@0 25 import android.support.v4.content.Loader;
michael@0 26 import android.util.SparseArray;
michael@0 27 import android.view.LayoutInflater;
michael@0 28 import android.view.View;
michael@0 29 import android.view.ViewGroup;
michael@0 30 import android.view.ViewStub;
michael@0 31 import android.widget.AdapterView;
michael@0 32 import android.widget.ImageView;
michael@0 33 import android.widget.TextView;
michael@0 34
michael@0 35 /**
michael@0 36 * Fragment that displays recent history in a ListView.
michael@0 37 */
michael@0 38 public class MostRecentPanel extends HomeFragment {
michael@0 39 // Logging tag name
michael@0 40 private static final String LOGTAG = "GeckoMostRecentPanel";
michael@0 41
michael@0 42 // Cursor loader ID for history query
michael@0 43 private static final int LOADER_ID_HISTORY = 0;
michael@0 44
michael@0 45 // Adapter for the list of search results
michael@0 46 private MostRecentAdapter mAdapter;
michael@0 47
michael@0 48 // The view shown by the fragment.
michael@0 49 private HomeListView mList;
michael@0 50
michael@0 51 // Reference to the View to display when there are no results.
michael@0 52 private View mEmptyView;
michael@0 53
michael@0 54 // Callbacks used for the search and favicon cursor loaders
michael@0 55 private CursorLoaderCallbacks mCursorLoaderCallbacks;
michael@0 56
michael@0 57 // On URL open listener
michael@0 58 private OnUrlOpenListener mUrlOpenListener;
michael@0 59
michael@0 60 public static MostRecentPanel newInstance() {
michael@0 61 return new MostRecentPanel();
michael@0 62 }
michael@0 63
michael@0 64 public MostRecentPanel() {
michael@0 65 mUrlOpenListener = null;
michael@0 66 }
michael@0 67
michael@0 68 @Override
michael@0 69 public void onAttach(Activity activity) {
michael@0 70 super.onAttach(activity);
michael@0 71
michael@0 72 try {
michael@0 73 mUrlOpenListener = (OnUrlOpenListener) activity;
michael@0 74 } catch (ClassCastException e) {
michael@0 75 throw new ClassCastException(activity.toString()
michael@0 76 + " must implement HomePager.OnUrlOpenListener");
michael@0 77 }
michael@0 78 }
michael@0 79
michael@0 80 @Override
michael@0 81 public void onDetach() {
michael@0 82 super.onDetach();
michael@0 83 mUrlOpenListener = null;
michael@0 84 }
michael@0 85
michael@0 86 @Override
michael@0 87 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
michael@0 88 return inflater.inflate(R.layout.home_most_recent_panel, container, false);
michael@0 89 }
michael@0 90
michael@0 91 @Override
michael@0 92 public void onViewCreated(View view, Bundle savedInstanceState) {
michael@0 93 mList = (HomeListView) view.findViewById(R.id.list);
michael@0 94 mList.setTag(HomePager.LIST_TAG_MOST_RECENT);
michael@0 95
michael@0 96 mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
michael@0 97 @Override
michael@0 98 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
michael@0 99 position -= mAdapter.getMostRecentSectionsCountBefore(position);
michael@0 100 final Cursor c = mAdapter.getCursor(position);
michael@0 101 final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
michael@0 102
michael@0 103 Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
michael@0 104
michael@0 105 // This item is a TwoLinePageRow, so we allow switch-to-tab.
michael@0 106 mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
michael@0 107 }
michael@0 108 });
michael@0 109
michael@0 110 mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
michael@0 111 @Override
michael@0 112 public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
michael@0 113 final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
michael@0 114 info.url = cursor.getString(cursor.getColumnIndexOrThrow(Combined.URL));
michael@0 115 info.title = cursor.getString(cursor.getColumnIndexOrThrow(Combined.TITLE));
michael@0 116 info.display = cursor.getInt(cursor.getColumnIndexOrThrow(Combined.DISPLAY));
michael@0 117 info.historyId = cursor.getInt(cursor.getColumnIndexOrThrow(Combined.HISTORY_ID));
michael@0 118 final int bookmarkIdCol = cursor.getColumnIndexOrThrow(Combined.BOOKMARK_ID);
michael@0 119 if (cursor.isNull(bookmarkIdCol)) {
michael@0 120 // If this is a combined cursor, we may get a history item without a
michael@0 121 // bookmark, in which case the bookmarks ID column value will be null.
michael@0 122 info.bookmarkId = -1;
michael@0 123 } else {
michael@0 124 info.bookmarkId = cursor.getInt(bookmarkIdCol);
michael@0 125 }
michael@0 126 return info;
michael@0 127 }
michael@0 128 });
michael@0 129 registerForContextMenu(mList);
michael@0 130 }
michael@0 131
michael@0 132 @Override
michael@0 133 public void onDestroyView() {
michael@0 134 super.onDestroyView();
michael@0 135 mList = null;
michael@0 136 mEmptyView = null;
michael@0 137 }
michael@0 138
michael@0 139 @Override
michael@0 140 public void onActivityCreated(Bundle savedInstanceState) {
michael@0 141 super.onActivityCreated(savedInstanceState);
michael@0 142
michael@0 143 // Intialize adapter
michael@0 144 mAdapter = new MostRecentAdapter(getActivity());
michael@0 145 mList.setAdapter(mAdapter);
michael@0 146
michael@0 147 // Create callbacks before the initial loader is started
michael@0 148 mCursorLoaderCallbacks = new CursorLoaderCallbacks();
michael@0 149 loadIfVisible();
michael@0 150 }
michael@0 151
michael@0 152 @Override
michael@0 153 protected void load() {
michael@0 154 getLoaderManager().initLoader(LOADER_ID_HISTORY, null, mCursorLoaderCallbacks);
michael@0 155 }
michael@0 156
michael@0 157 private static class MostRecentCursorLoader extends SimpleCursorLoader {
michael@0 158 // Max number of history results
michael@0 159 private static final int HISTORY_LIMIT = 100;
michael@0 160
michael@0 161 public MostRecentCursorLoader(Context context) {
michael@0 162 super(context);
michael@0 163 }
michael@0 164
michael@0 165 @Override
michael@0 166 public Cursor loadCursor() {
michael@0 167 final ContentResolver cr = getContext().getContentResolver();
michael@0 168 return BrowserDB.getRecentHistory(cr, HISTORY_LIMIT);
michael@0 169 }
michael@0 170 }
michael@0 171
michael@0 172 private void updateUiFromCursor(Cursor c) {
michael@0 173 if (c != null && c.getCount() > 0) {
michael@0 174 return;
michael@0 175 }
michael@0 176
michael@0 177 // Cursor is empty, so set the empty view if it hasn't been set already.
michael@0 178 if (mEmptyView == null) {
michael@0 179 // Set empty panel view. We delay this so that the empty view won't flash.
michael@0 180 final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub);
michael@0 181 mEmptyView = emptyViewStub.inflate();
michael@0 182
michael@0 183 final ImageView emptyIcon = (ImageView) mEmptyView.findViewById(R.id.home_empty_image);
michael@0 184 emptyIcon.setImageResource(R.drawable.icon_most_recent_empty);
michael@0 185
michael@0 186 final TextView emptyText = (TextView) mEmptyView.findViewById(R.id.home_empty_text);
michael@0 187 emptyText.setText(R.string.home_most_recent_empty);
michael@0 188
michael@0 189 mList.setEmptyView(mEmptyView);
michael@0 190 }
michael@0 191 }
michael@0 192
michael@0 193 private static class MostRecentAdapter extends MultiTypeCursorAdapter {
michael@0 194 private static final int ROW_HEADER = 0;
michael@0 195 private static final int ROW_STANDARD = 1;
michael@0 196
michael@0 197 private static final int[] VIEW_TYPES = new int[] { ROW_STANDARD, ROW_HEADER };
michael@0 198 private static final int[] LAYOUT_TYPES = new int[] { R.layout.home_item_row, R.layout.home_header_row };
michael@0 199
michael@0 200 // For the time sections in history
michael@0 201 private static final long MS_PER_DAY = 86400000;
michael@0 202 private static final long MS_PER_WEEK = MS_PER_DAY * 7;
michael@0 203
michael@0 204 // The time ranges for each section
michael@0 205 private static enum MostRecentSection {
michael@0 206 TODAY,
michael@0 207 YESTERDAY,
michael@0 208 WEEK,
michael@0 209 OLDER
michael@0 210 };
michael@0 211
michael@0 212 private final Context mContext;
michael@0 213
michael@0 214 // Maps headers in the list with their respective sections
michael@0 215 private final SparseArray<MostRecentSection> mMostRecentSections;
michael@0 216
michael@0 217 public MostRecentAdapter(Context context) {
michael@0 218 super(context, null, VIEW_TYPES, LAYOUT_TYPES);
michael@0 219
michael@0 220 mContext = context;
michael@0 221
michael@0 222 // Initialize map of history sections
michael@0 223 mMostRecentSections = new SparseArray<MostRecentSection>();
michael@0 224 }
michael@0 225
michael@0 226 @Override
michael@0 227 public Object getItem(int position) {
michael@0 228 final int type = getItemViewType(position);
michael@0 229
michael@0 230 // Header items are not in the cursor
michael@0 231 if (type == ROW_HEADER) {
michael@0 232 return null;
michael@0 233 }
michael@0 234
michael@0 235 return super.getItem(position - getMostRecentSectionsCountBefore(position));
michael@0 236 }
michael@0 237
michael@0 238 @Override
michael@0 239 public int getItemViewType(int position) {
michael@0 240 if (mMostRecentSections.get(position) != null) {
michael@0 241 return ROW_HEADER;
michael@0 242 }
michael@0 243
michael@0 244 return ROW_STANDARD;
michael@0 245 }
michael@0 246
michael@0 247 @Override
michael@0 248 public boolean isEnabled(int position) {
michael@0 249 return (getItemViewType(position) == ROW_STANDARD);
michael@0 250 }
michael@0 251
michael@0 252 @Override
michael@0 253 public int getCount() {
michael@0 254 // Add the history section headers to the number of reported results.
michael@0 255 return super.getCount() + mMostRecentSections.size();
michael@0 256 }
michael@0 257
michael@0 258 @Override
michael@0 259 public Cursor swapCursor(Cursor cursor) {
michael@0 260 loadMostRecentSections(cursor);
michael@0 261 Cursor oldCursor = super.swapCursor(cursor);
michael@0 262 return oldCursor;
michael@0 263 }
michael@0 264
michael@0 265 @Override
michael@0 266 public void bindView(View view, Context context, int position) {
michael@0 267 final int type = getItemViewType(position);
michael@0 268
michael@0 269 if (type == ROW_HEADER) {
michael@0 270 final MostRecentSection section = mMostRecentSections.get(position);
michael@0 271 final TextView row = (TextView) view;
michael@0 272 row.setText(getMostRecentSectionTitle(section));
michael@0 273 } else {
michael@0 274 // Account for the most recent section headers
michael@0 275 position -= getMostRecentSectionsCountBefore(position);
michael@0 276 final Cursor c = getCursor(position);
michael@0 277 final TwoLinePageRow row = (TwoLinePageRow) view;
michael@0 278 row.updateFromCursor(c);
michael@0 279 }
michael@0 280 }
michael@0 281
michael@0 282 private String getMostRecentSectionTitle(MostRecentSection section) {
michael@0 283 switch (section) {
michael@0 284 case TODAY:
michael@0 285 return mContext.getString(R.string.history_today_section);
michael@0 286 case YESTERDAY:
michael@0 287 return mContext.getString(R.string.history_yesterday_section);
michael@0 288 case WEEK:
michael@0 289 return mContext.getString(R.string.history_week_section);
michael@0 290 case OLDER:
michael@0 291 return mContext.getString(R.string.history_older_section);
michael@0 292 }
michael@0 293
michael@0 294 throw new IllegalStateException("Unrecognized history section");
michael@0 295 }
michael@0 296
michael@0 297 private int getMostRecentSectionsCountBefore(int position) {
michael@0 298 // Account for the number headers before the given position
michael@0 299 int sectionsBefore = 0;
michael@0 300
michael@0 301 final int historySectionsCount = mMostRecentSections.size();
michael@0 302 for (int i = 0; i < historySectionsCount; i++) {
michael@0 303 final int sectionPosition = mMostRecentSections.keyAt(i);
michael@0 304 if (sectionPosition > position) {
michael@0 305 break;
michael@0 306 }
michael@0 307
michael@0 308 sectionsBefore++;
michael@0 309 }
michael@0 310
michael@0 311 return sectionsBefore;
michael@0 312 }
michael@0 313
michael@0 314 private static MostRecentSection getMostRecentSectionForTime(long from, long time) {
michael@0 315 long delta = from - time;
michael@0 316
michael@0 317 if (delta < 0) {
michael@0 318 return MostRecentSection.TODAY;
michael@0 319 }
michael@0 320
michael@0 321 if (delta < MS_PER_DAY) {
michael@0 322 return MostRecentSection.YESTERDAY;
michael@0 323 }
michael@0 324
michael@0 325 if (delta < MS_PER_WEEK) {
michael@0 326 return MostRecentSection.WEEK;
michael@0 327 }
michael@0 328
michael@0 329 return MostRecentSection.OLDER;
michael@0 330 }
michael@0 331
michael@0 332 private void loadMostRecentSections(Cursor c) {
michael@0 333 // Clear any history sections that may have been loaded before.
michael@0 334 mMostRecentSections.clear();
michael@0 335
michael@0 336 if (c == null || !c.moveToFirst()) {
michael@0 337 return;
michael@0 338 }
michael@0 339
michael@0 340 final Date now = new Date();
michael@0 341 now.setHours(0);
michael@0 342 now.setMinutes(0);
michael@0 343 now.setSeconds(0);
michael@0 344
michael@0 345 final long today = now.getTime();
michael@0 346 MostRecentSection section = null;
michael@0 347
michael@0 348 do {
michael@0 349 final int position = c.getPosition();
michael@0 350 final long time = c.getLong(c.getColumnIndexOrThrow(URLColumns.DATE_LAST_VISITED));
michael@0 351 final MostRecentSection itemSection = MostRecentAdapter.getMostRecentSectionForTime(today, time);
michael@0 352
michael@0 353 if (section != itemSection) {
michael@0 354 section = itemSection;
michael@0 355 mMostRecentSections.append(position + mMostRecentSections.size(), section);
michael@0 356 }
michael@0 357
michael@0 358 // Reached the last section, no need to continue
michael@0 359 if (section == MostRecentSection.OLDER) {
michael@0 360 break;
michael@0 361 }
michael@0 362 } while (c.moveToNext());
michael@0 363 }
michael@0 364 }
michael@0 365
michael@0 366 private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> {
michael@0 367 @Override
michael@0 368 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
michael@0 369 return new MostRecentCursorLoader(getActivity());
michael@0 370 }
michael@0 371
michael@0 372 @Override
michael@0 373 public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
michael@0 374 mAdapter.swapCursor(c);
michael@0 375 updateUiFromCursor(c);
michael@0 376 }
michael@0 377
michael@0 378 @Override
michael@0 379 public void onLoaderReset(Loader<Cursor> loader) {
michael@0 380 mAdapter.swapCursor(null);
michael@0 381 }
michael@0 382 }
michael@0 383 }

mercurial