1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 package org.mozilla.gecko.home; 7 8 import java.util.EnumSet; 9 import java.util.List; 10 11 import android.util.Log; 12 import org.mozilla.gecko.R; 13 import org.mozilla.gecko.Telemetry; 14 import org.mozilla.gecko.TelemetryContract; 15 import org.mozilla.gecko.db.BrowserContract; 16 import org.mozilla.gecko.db.BrowserContract.Bookmarks; 17 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; 18 19 import android.content.Context; 20 import android.database.Cursor; 21 import android.util.AttributeSet; 22 import android.view.KeyEvent; 23 import android.view.View; 24 import android.widget.AdapterView; 25 import android.widget.HeaderViewListAdapter; 26 import android.widget.ListAdapter; 27 28 import org.mozilla.gecko.reader.SavedReaderViewHelper; 29 import org.mozilla.gecko.util.NetworkUtils; 30 31 /** 32 * A ListView of bookmarks. 33 */ 34 public class BookmarksListView extends HomeListView 35 implements AdapterView.OnItemClickListener { 36 public static final String LOGTAG = "GeckoBookmarksListView"; 37 BookmarksListView(Context context)38 public BookmarksListView(Context context) { 39 this(context, null); 40 } 41 BookmarksListView(Context context, AttributeSet attrs)42 public BookmarksListView(Context context, AttributeSet attrs) { 43 this(context, attrs, R.attr.bookmarksListViewStyle); 44 } 45 BookmarksListView(Context context, AttributeSet attrs, int defStyle)46 public BookmarksListView(Context context, AttributeSet attrs, int defStyle) { 47 super(context, attrs, defStyle); 48 } 49 50 @Override onAttachedToWindow()51 public void onAttachedToWindow() { 52 super.onAttachedToWindow(); 53 54 setOnItemClickListener(this); 55 56 setOnKeyListener(new View.OnKeyListener() { 57 @Override 58 public boolean onKey(View v, int keyCode, KeyEvent event) { 59 final int action = event.getAction(); 60 61 // If the user hit the BACK key, try to move to the parent folder. 62 if (action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) { 63 return getBookmarksListAdapter().moveToParentFolder(); 64 } 65 return false; 66 } 67 }); 68 } 69 70 /** 71 * Get the appropriate telemetry extra for a given folder. 72 * 73 * baseFolderID is the ID of the first-level folder in the parent stack, i.e. the first folder 74 * that was selected from the root hierarchy (e.g. Desktop, Reading List, or any mobile first-level 75 * subfolder). If the current folder is a first-level folder, then the fixed root ID may be used 76 * instead. 77 * 78 * We use baseFolderID only to distinguish whether or not we're currently in a desktop subfolder. 79 * If it isn't equal to FAKE_DESKTOP_FOLDER_ID we know we're in a mobile subfolder, or one 80 * of the smartfolders. 81 */ getTelemetryExtraForFolder(int folderID, int baseFolderID)82 private String getTelemetryExtraForFolder(int folderID, int baseFolderID) { 83 if (folderID == Bookmarks.FAKE_DESKTOP_FOLDER_ID) { 84 return "folder_desktop"; 85 } else if (folderID == Bookmarks.FIXED_SCREENSHOT_FOLDER_ID) { 86 return "folder_screenshots"; 87 } else if (folderID == Bookmarks.FAKE_READINGLIST_SMARTFOLDER_ID) { 88 return "folder_reading_list"; 89 } else { 90 // The stack depth is 2 for either the fake desktop folder, or any subfolder of mobile 91 // bookmarks, we subtract these offsets so that any direct subfolder of mobile 92 // has a level equal to 1. (Desktop folders will be one level deeper due to the 93 // fake desktop folder, hence subtract 2.) 94 if (baseFolderID == Bookmarks.FAKE_DESKTOP_FOLDER_ID) { 95 return "folder_desktop_subfolder"; 96 } else { 97 return "folder_mobile_subfolder"; 98 } 99 } 100 } 101 102 @Override onItemClick(AdapterView<?> parent, View view, int position, long id)103 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 104 final BookmarksListAdapter adapter = getBookmarksListAdapter(); 105 if (adapter.isShowingChildFolder()) { 106 if (position == 0) { 107 // If we tap on an opened folder, move back to parent folder. 108 109 final List<BookmarksListAdapter.FolderInfo> parentStack = ((BookmarksListAdapter) getAdapter()).getParentStack(); 110 if (parentStack.size() < 2) { 111 throw new IllegalStateException("Cannot move to parent folder if we are already in the root folder"); 112 } 113 114 // The first item (top of stack) is the current folder, we're returning to the next one 115 BookmarksListAdapter.FolderInfo folder = parentStack.get(1); 116 final int parentID = folder.id; 117 final int baseFolderID; 118 if (parentStack.size() > 2) { 119 baseFolderID = parentStack.get(parentStack.size() - 2).id; 120 } else { 121 baseFolderID = Bookmarks.FIXED_ROOT_ID; 122 } 123 124 final String extra = getTelemetryExtraForFolder(parentID, baseFolderID); 125 126 // Move to parent _after_ retrieving stack information 127 adapter.moveToParentFolder(); 128 129 Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.LIST_ITEM, extra); 130 return; 131 } 132 133 // Accounting for the folder view. 134 position--; 135 } 136 137 final Cursor cursor = adapter.getCursor(); 138 if (cursor == null) { 139 return; 140 } 141 142 cursor.moveToPosition(position); 143 144 if (adapter.getOpenFolderType() == BookmarksListAdapter.FolderType.SCREENSHOTS) { 145 Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.LIST_ITEM, "bookmarks-screenshot"); 146 147 final String fileUrl = "file://" + cursor.getString(cursor.getColumnIndex(BrowserContract.UrlAnnotations.VALUE)); 148 getOnUrlOpenListener().onUrlOpen(fileUrl, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB)); 149 return; 150 } 151 152 int type = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.TYPE)); 153 if (type == Bookmarks.TYPE_FOLDER) { 154 // If we're clicking on a folder, update adapter to move to that folder 155 final int folderId = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID)); 156 final String folderTitle = adapter.getFolderTitle(parent.getContext(), cursor); 157 adapter.moveToChildFolder(folderId, folderTitle); 158 159 final List<BookmarksListAdapter.FolderInfo> parentStack = ((BookmarksListAdapter) getAdapter()).getParentStack(); 160 161 final int baseFolderID; 162 if (parentStack.size() > 2) { 163 baseFolderID = parentStack.get(parentStack.size() - 2).id; 164 } else { 165 baseFolderID = Bookmarks.FIXED_ROOT_ID; 166 } 167 168 final String extra = getTelemetryExtraForFolder(folderId, baseFolderID); 169 Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.LIST_ITEM, extra); 170 } else { 171 // Otherwise, just open the URL 172 final String url = cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.URL)); 173 174 final SavedReaderViewHelper rvh = SavedReaderViewHelper.getSavedReaderViewHelper(getContext()); 175 176 final String extra; 177 if (rvh.isURLCached(url)) { 178 extra = "bookmarks-reader"; 179 } else { 180 extra = "bookmarks"; 181 } 182 183 Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.LIST_ITEM, extra); 184 Telemetry.addToHistogram("FENNEC_LOAD_SAVED_PAGE", NetworkUtils.isConnected(getContext()) ? 2 : 3); 185 186 // This item is a TwoLinePageRow, so we allow switch-to-tab. 187 getOnUrlOpenListener().onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB)); 188 } 189 } 190 191 @Override onItemLongClick(AdapterView<?> parent, View view, int position, long id)192 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { 193 // Adjust the item position to account for the parent folder row that is inserted 194 // at the top of the list when viewing the contents of a folder. 195 final BookmarksListAdapter adapter = getBookmarksListAdapter(); 196 if (adapter.isShowingChildFolder()) { 197 position--; 198 } 199 200 // Temporarily prevent crashes until we figure out what we actually want to do here (bug 1252316). 201 if (adapter.getOpenFolderType() == BookmarksListAdapter.FolderType.SCREENSHOTS) { 202 return false; 203 } 204 205 return super.onItemLongClick(parent, view, position, id); 206 } 207 getBookmarksListAdapter()208 private BookmarksListAdapter getBookmarksListAdapter() { 209 BookmarksListAdapter adapter; 210 ListAdapter listAdapter = getAdapter(); 211 if (listAdapter instanceof HeaderViewListAdapter) { 212 adapter = (BookmarksListAdapter) ((HeaderViewListAdapter) listAdapter).getWrappedAdapter(); 213 } else { 214 adapter = (BookmarksListAdapter) listAdapter; 215 } 216 return adapter; 217 } 218 } 219