Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(118)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProvider.java

Issue 10912172: [Android] Upstream ChromeBrowserProvider, the Android port ContentProvider implementation. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: temporarily removing tests until we can compile and run them (soon) Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser;
6
7 import android.app.Activity;
8 import android.app.SearchManager;
9 import android.content.ContentProvider;
10 import android.content.ContentUris;
11 import android.content.ContentValues;
12 import android.content.Context;
13 import android.content.Intent;
14 import android.content.SharedPreferences;
15 import android.content.UriMatcher;
16 import android.database.Cursor;
17 import android.database.MatrixCursor;
18 import android.graphics.Bitmap;
19 import android.net.Uri;
20 import android.os.Build;
21 import android.os.Bundle;
22 import android.os.Looper;
23 import android.os.Message;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.os.Parcelable.Creator;
27 import android.preference.PreferenceManager;
28 import android.provider.BaseColumns;
29 import android.provider.Browser;
30 import android.provider.Browser.BookmarkColumns;
31 import android.provider.Browser.SearchColumns;
32 import android.text.TextUtils;
33 import android.util.Log;
34
35 import org.chromium.base.CalledByNative;
36 import org.chromium.base.CalledByNativeUnchecked;
37 import org.chromium.base.ThreadUtils;
38 import org.chromium.chrome.browser.database.SQLiteCursor;
39
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.HashMap;
43 import java.util.List;
44 import java.util.Vector;
45 import java.util.concurrent.atomic.AtomicBoolean;
46
47 /**
48 * This class provides various information of Chrome, like bookmarks, most
49 * visited page etc. It is used to support android.provider.Browser.
50 *
51 */
52 public class ChromeBrowserProvider extends ContentProvider {
53 private static final String TAG = "ChromeBrowserProvider";
54
55 // Defines the API methods that the Client can call by name.
56 static final String CLIENT_API_BOOKMARK_NODE_EXISTS = "BOOKMARK_NODE_EXISTS" ;
57 static final String CLIENT_API_CREATE_BOOKMARKS_FOLDER_ONCE = "CREATE_BOOKMA RKS_FOLDER_ONCE";
58 static final String CLIENT_API_GET_BOOKMARK_FOLDER_HIERARCHY = "GET_BOOKMARK _FOLDER_HIERARCHY";
59 static final String CLIENT_API_GET_BOOKMARK_NODE = "GET_BOOKMARK_NODE";
60 static final String CLIENT_API_GET_DEFAULT_BOOKMARK_FOLDER = "GET_DEFAULT_BO OKMARK_FOLDER";
61 static final String CLIENT_API_GET_MOBILE_BOOKMARKS_FOLDER_ID =
62 "GET_MOBILE_BOOKMARKS_FOLDER_ID";
63 static final String CLIENT_API_IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH =
64 "IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH";
65 static final String CLIENT_API_RESULT_KEY = "result";
66
67
68 // Defines Chrome's API authority, so it can be run and tested
69 // independently.
70 private static final String API_AUTHORITY_SUFFIX = ".browser";
71
72 private static final String BROWSER_CONTRACT_API_AUTHORITY =
73 "com.google.android.apps.chrome.browser-contract";
74
75 // These values are taken from android.provider.BrowserContract.java since
76 // that class is hidden from the SDK.
77 private static final String BROWSER_CONTRACT_AUTHORITY = "com.android.browse r";
78 private static final String BROWSER_CONTRACT_HISTORY_CONTENT_TYPE =
79 "vnd.android.cursor.dir/browser-history";
80 private static final String BROWSER_CONTRACT_HISTORY_CONTENT_ITEM_TYPE =
81 "vnd.android.cursor.item/browser-history";
82
83 // This Authority is for internal interface. It's concatenated with
84 // Context.getPackageName() so that we can install different channels
85 // SxS and have different authorities.
86 private static final String AUTHORITY_SUFFIX = ".ChromeBrowserProvider";
87 private static final String BOOKMARKS_PATH = "bookmarks";
88 private static final String SEARCHES_PATH = "searches";
89 private static final String HISTORY_PATH = "history";
90 private static final String COMBINED_PATH = "combined";
91 private static final String BOOKMARK_FOLDER_PATH = "hierarchy";
92
93 public static final Uri BROWSER_CONTRACTS_BOOKMAKRS_API_URI = buildContentUr i(
94 BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH);
95
96 public static final Uri BROWSER_CONTRACTS_SEARCHES_API_URI = buildContentUri (
97 BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH);
98
99 public static final Uri BROWSER_CONTRACTS_HISTORY_API_URI = buildContentUri(
100 BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH);
101
102 public static final Uri BROWSER_CONTRACTS_COMBINED_API_URI = buildContentUri (
103 BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH);
104
105 /** The parameter used to specify a bookmark parent ID in ContentValues. */
106 public static final String BOOKMARK_PARENT_ID_PARAM = "parentId";
107
108 /** The parameter used to specify whether this is a bookmark folder. */
109 public static final String BOOKMARK_IS_FOLDER_PARAM = "isFolder";
110
111 /** Invalid id value for the Android ContentProvider API calls. */
112 public static final long INVALID_CONTENT_PROVIDER_ID = 0;
113
114 // ID used to indicate an invalid id for bookmark nodes.
115 // Client API queries should use ChromeBrowserProviderClient.INVALID_BOOMARK _ID.
116 static final long INVALID_BOOKMARK_ID = -1;
117
118 private static final String LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY = "last_boo kmark_folder_id";
119
120 private static final int URI_MATCH_BOOKMARKS = 0;
121 private static final int URI_MATCH_BOOKMARKS_ID = 1;
122 private static final int URL_MATCH_API_BOOKMARK = 2;
123 private static final int URL_MATCH_API_BOOKMARK_ID = 3;
124 private static final int URL_MATCH_API_SEARCHES = 4;
125 private static final int URL_MATCH_API_SEARCHES_ID = 5;
126 private static final int URL_MATCH_API_HISTORY_CONTENT = 6;
127 private static final int URL_MATCH_API_HISTORY_CONTENT_ID = 7;
128 private static final int URL_MATCH_API_BOOKMARK_CONTENT = 8;
129 private static final int URL_MATCH_API_BOOKMARK_CONTENT_ID = 9;
130 private static final int URL_MATCH_BOOKMARK_SUGGESTIONS_ID = 10;
131 private static final int URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID = 11;
132
133 // TODO : Using Android.provider.Browser.HISTORY_PROJECTION once THUMBNAIL,
134 // TOUCH_ICON, and USER_ENTERED fields are supported.
135 private static final String[] BOOKMARK_DEFAULT_PROJECTION = new String[] {
136 BookmarkColumns._ID, BookmarkColumns.URL, BookmarkColumns.VISITS,
137 BookmarkColumns.DATE, BookmarkColumns.BOOKMARK, BookmarkColumns.TITL E,
138 BookmarkColumns.FAVICON, BookmarkColumns.CREATED};
139
140 private static final String[] SUGGEST_PROJECTION = new String[] {
141 BookmarkColumns._ID,
142 BookmarkColumns.TITLE,
143 BookmarkColumns.URL,
144 BookmarkColumns.DATE,
145 BookmarkColumns.BOOKMARK
146 };
147
148 private final Object mInitializeUriMatcherLock = new Object();
149 private final Object mLoadNativeLock = new Object();
150 private UriMatcher mUriMatcher;
151 private long mLastModifiedBookmarkFolderId = INVALID_BOOKMARK_ID;
152 private int mNativeChromeBrowserProvider;
153 private BookmarkNode mMobileBookmarksFolder;
154
155 synchronized private void ensureUriMatcherInitialized() {
156 synchronized (mInitializeUriMatcherLock) {
157 if (mUriMatcher != null) return;
158
159 mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
160 // The internal URIs
161 String authority = getContext().getPackageName() + AUTHORITY_SUFFIX;
162 mUriMatcher.addURI(authority, BOOKMARKS_PATH, URI_MATCH_BOOKMARKS);
163 mUriMatcher.addURI(authority, BOOKMARKS_PATH + "/#", URI_MATCH_BOOKM ARKS_ID);
164 // The internal authority for public APIs
165 String apiAuthority = getContext().getPackageName() + API_AUTHORITY_ SUFFIX;
166 mUriMatcher.addURI(apiAuthority, BOOKMARKS_PATH, URL_MATCH_API_BOOKM ARK);
167 mUriMatcher.addURI(apiAuthority, BOOKMARKS_PATH + "/#", URL_MATCH_AP I_BOOKMARK_ID);
168 mUriMatcher.addURI(apiAuthority, SEARCHES_PATH, URL_MATCH_API_SEARCH ES);
169 mUriMatcher.addURI(apiAuthority, SEARCHES_PATH + "/#", URL_MATCH_API _SEARCHES_ID);
170 mUriMatcher.addURI(apiAuthority, HISTORY_PATH, URL_MATCH_API_HISTORY _CONTENT);
171 mUriMatcher.addURI(apiAuthority, HISTORY_PATH + "/#", URL_MATCH_API_ HISTORY_CONTENT_ID);
172 mUriMatcher.addURI(apiAuthority, COMBINED_PATH, URL_MATCH_API_BOOKMA RK);
173 mUriMatcher.addURI(apiAuthority, COMBINED_PATH + "/#", URL_MATCH_API _BOOKMARK_ID);
174 // The internal authority for BrowserContracts
175 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH,
176 URL_MATCH_API_HISTORY_CONTENT);
177 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH + "/ #",
178 URL_MATCH_API_HISTORY_CONTENT_ID);
179 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH,
180 URL_MATCH_API_BOOKMARK);
181 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH + " /#",
182 URL_MATCH_API_BOOKMARK_ID);
183 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH,
184 URL_MATCH_API_SEARCHES);
185 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH + " /#",
186 URL_MATCH_API_SEARCHES_ID);
187 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH,
188 URL_MATCH_API_BOOKMARK_CONTENT);
189 mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH + "/#",
190 URL_MATCH_API_BOOKMARK_CONTENT_ID);
191 // Added the Android Framework URIs, so the provider can easily swit ched
192 // by adding 'browser' and 'com.android.browser' in manifest.
193 // The Android's BrowserContract
194 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, HISTORY_PATH,
195 URL_MATCH_API_HISTORY_CONTENT);
196 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, HISTORY_PATH + "/#",
197 URL_MATCH_API_HISTORY_CONTENT_ID);
198 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, "combined", URL_MATCH _API_BOOKMARK);
199 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, "combined/#", URL_MAT CH_API_BOOKMARK_ID);
200 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, SEARCHES_PATH, URL_MA TCH_API_SEARCHES);
201 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, SEARCHES_PATH + "/#",
202 URL_MATCH_API_SEARCHES_ID);
203 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, BOOKMARKS_PATH,
204 URL_MATCH_API_BOOKMARK_CONTENT);
205 mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, BOOKMARKS_PATH + "/#" ,
206 URL_MATCH_API_BOOKMARK_CONTENT_ID);
207 // For supporting android.provider.browser.BookmarkColumns and
208 // SearchColumns
209 mUriMatcher.addURI("browser", BOOKMARKS_PATH, URL_MATCH_API_BOOKMARK );
210 mUriMatcher.addURI("browser", BOOKMARKS_PATH + "/#", URL_MATCH_API_B OOKMARK_ID);
211 mUriMatcher.addURI("browser", SEARCHES_PATH, URL_MATCH_API_SEARCHES) ;
212 mUriMatcher.addURI("browser", SEARCHES_PATH + "/#", URL_MATCH_API_SE ARCHES_ID);
213
214 mUriMatcher.addURI(apiAuthority,
215 BOOKMARKS_PATH + "/" + SearchManager.SUGGEST_URI_ PATH_QUERY,
216 URL_MATCH_BOOKMARK_SUGGESTIONS_ID);
217 mUriMatcher.addURI(apiAuthority,
218 SearchManager.SUGGEST_URI_PATH_QUERY,
219 URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID);
220 }
221 }
222
223 @Override
224 public boolean onCreate() {
225 SharedPreferences sharedPreferences =
226 PreferenceManager.getDefaultSharedPreferences(getContext());
227 mLastModifiedBookmarkFolderId = sharedPreferences.getLong(
228 LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY, INVALID_BOOKMARK_ID);
229 return true;
230 }
231
232 private String buildSuggestWhere(String selection, int argc) {
233 StringBuilder sb = new StringBuilder(selection);
234 for (int i = 0; i < argc - 1; i++) {
235 sb.append(" OR ");
236 sb.append(selection);
237 }
238 return sb.toString();
239 }
240
241 private Cursor getBookmarkHistorySuggestions(String selection, String[] sele ctionArgs,
242 String sortOrder, boolean excludeHistory) {
243 boolean matchTitles = false;
244 Vector<String> args = new Vector();
245 String like = selectionArgs[0] + "%";
246 if (selectionArgs[0].startsWith("http") || selectionArgs[0].startsWith(" file")) {
247 args.add(like);
248 } else {
249 // Match against common URL prefixes.
250 args.add("http://" + like);
251 args.add("https://" + like);
252 args.add("http://www." + like);
253 args.add("https://www." + like);
254 args.add("file://" + like);
255 matchTitles = true;
256 }
257
258 StringBuilder urlWhere = new StringBuilder("(");
259 urlWhere.append(buildSuggestWhere(selection, args.size()));
260 if (matchTitles) {
261 args.add(like);
262 urlWhere.append(" OR title LIKE ?");
263 }
264 urlWhere.append(")");
265
266 if (excludeHistory) {
267 urlWhere.append(" AND bookmark=?");
268 args.add("1");
269 }
270
271 selectionArgs = args.toArray(selectionArgs);
272 Cursor cursor = queryBookmarkFromAPI(SUGGEST_PROJECTION, urlWhere.toStri ng(),
273 selectionArgs, sortOrder);
274 return new ChromeBrowserProviderSuggestionsCursor(cursor);
275 }
276
277 @Override
278 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
279 String sortOrder) {
280 // We can not run in UI thread, do nothing for the official release.
281 if (isInUiThread()) return null;
282 if (!ensureNativeChromeLoaded()) return null;
283
284 // Check for invalid id values if provided.
285 // If it represents a bookmark node then it's the root node. Don't provi de access here.
286 // Otherwise it represents a SQLite row id, so 0 is invalid.
287 long bookmarkId = INVALID_CONTENT_PROVIDER_ID;
288 try {
289 bookmarkId = ContentUris.parseId(uri);
290 if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return null;
291 } catch (Exception e) {
292 }
293
294 int match = mUriMatcher.match(uri);
295 Cursor cursor = null;
296 switch (match) {
297 case URL_MATCH_BOOKMARK_SUGGESTIONS_ID:
298 cursor = getBookmarkHistorySuggestions(selection, selectionArgs, sortOrder, true);
299 break;
300 case URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID:
301 cursor = getBookmarkHistorySuggestions(selection, selectionArgs, sortOrder, false);
302 break;
303 case URL_MATCH_API_BOOKMARK:
304 cursor = queryBookmarkFromAPI(projection, selection, selectionAr gs, sortOrder);
305 break;
306 case URL_MATCH_API_BOOKMARK_ID:
307 cursor = queryBookmarkFromAPI(projection, buildWhereClause(bookm arkId, selection),
308 selectionArgs, sortOrder);
309 break;
310 case URL_MATCH_API_SEARCHES:
311 cursor = querySearchTermFromAPI(projection, selection, selection Args, sortOrder);
312 break;
313 case URL_MATCH_API_SEARCHES_ID:
314 cursor = querySearchTermFromAPI(projection, buildWhereClause(boo kmarkId, selection),
315 selectionArgs, sortOrder);
316 break;
317 case URL_MATCH_API_HISTORY_CONTENT:
318 cursor = queryBookmarkFromAPI(projection, buildHistoryWhereClaus e(selection),
319 selectionArgs, sortOrder);
320 break;
321 case URL_MATCH_API_HISTORY_CONTENT_ID:
322 cursor = queryBookmarkFromAPI(projection,
323 buildHistoryWhereClause(bookmarkId, selection), selectio nArgs, sortOrder);
324 break;
325 case URL_MATCH_API_BOOKMARK_CONTENT:
326 cursor = queryBookmarkFromAPI(projection, buildBookmarkWhereClau se(selection),
327 selectionArgs, sortOrder);
328 break;
329 case URL_MATCH_API_BOOKMARK_CONTENT_ID:
330 cursor = queryBookmarkFromAPI(projection,
331 buildBookmarkWhereClause(bookmarkId, selection), selecti onArgs, sortOrder);
332 break;
333 default:
334 throw new IllegalArgumentException(TAG + ": query - unknown URL uri = " + uri);
335 }
336 if (cursor == null) {
337 cursor = new MatrixCursor(new String[] { });
338 }
339 cursor.setNotificationUri(getContext().getContentResolver(), uri);
340 return cursor;
341 }
342
343 @Override
344 public Uri insert(Uri uri, ContentValues values) {
345 // We can not run in UI thread, do nothing for the official release.
346 if (isInUiThread()) return null;
347 if (!ensureNativeChromeLoaded()) return null;
348
349 int match = mUriMatcher.match(uri);
350 Uri res = null;
351 long id;
352 switch (match) {
353 case URI_MATCH_BOOKMARKS:
354 id = addBookmark(values);
355 if (id == INVALID_BOOKMARK_ID) return null;
356 break;
357 case URL_MATCH_API_BOOKMARK_CONTENT:
358 values.put(BookmarkColumns.BOOKMARK, 1);
359 //$FALL-THROUGH$
360 case URL_MATCH_API_BOOKMARK:
361 case URL_MATCH_API_HISTORY_CONTENT:
362 id = addBookmarkFromAPI(values);
363 if (id == INVALID_CONTENT_PROVIDER_ID) return null;
364 break;
365 case URL_MATCH_API_SEARCHES:
366 id = addSearchTermFromAPI(values);
367 if (id == INVALID_CONTENT_PROVIDER_ID) return null;
368 break;
369 default:
370 throw new IllegalArgumentException(TAG + ": insert - unknown URL " + uri);
371 }
372
373 res = ContentUris.withAppendedId(uri, id);
374 getContext().getContentResolver().notifyChange(res, null);
375 return res;
376 }
377
378 @Override
379 public int delete(Uri uri, String selection, String[] selectionArgs) {
380 // We can not run in UI thread, do nothing for the official release.
381 if (isInUiThread()) return 0;
382 if (!ensureNativeChromeLoaded()) return 0;
383
384 // Check for invalid id values if provided.
385 // If it represents a bookmark node then it's the root node and not muta ble.
386 // Otherwise it represents a SQLite row id, so 0 is invalid.
387 long bookmarkId = INVALID_CONTENT_PROVIDER_ID;
388 try {
389 bookmarkId = ContentUris.parseId(uri);
390 if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0;
391 } catch (Exception e) {
392 }
393
394 int match = mUriMatcher.match(uri);
395 int result;
396 switch (match) {
397 case URI_MATCH_BOOKMARKS_ID :
398 result = nativeRemoveBookmark(mNativeChromeBrowserProvider, book markId);
399 break;
400 case URL_MATCH_API_BOOKMARK_ID:
401 result = removeBookmarkFromAPI(
402 buildWhereClause(bookmarkId, selection), selectionArgs);
403 break;
404 case URL_MATCH_API_BOOKMARK:
405 result = removeBookmarkFromAPI(selection, selectionArgs);
406 break;
407 case URL_MATCH_API_SEARCHES_ID:
408 result = removeSearchFromAPI(buildWhereClause(bookmarkId, select ion),
409 selectionArgs);
410 break;
411 case URL_MATCH_API_SEARCHES:
412 result = removeSearchFromAPI(selection, selectionArgs);
413 break;
414 case URL_MATCH_API_HISTORY_CONTENT:
415 result = removeHistoryFromAPI(selection, selectionArgs);
416 break;
417 case URL_MATCH_API_HISTORY_CONTENT_ID:
418 result = removeHistoryFromAPI(buildWhereClause(bookmarkId, selec tion),
419 selectionArgs);
420 break;
421 case URL_MATCH_API_BOOKMARK_CONTENT:
422 result = removeBookmarkFromAPI(buildBookmarkWhereClause(selectio n), selectionArgs);
423 break;
424 case URL_MATCH_API_BOOKMARK_CONTENT_ID:
425 result = removeBookmarkFromAPI(buildBookmarkWhereClause(bookmark Id, selection),
426 selectionArgs);
427 break;
428 default:
429 throw new IllegalArgumentException(TAG + ": delete - unknown URL " + uri);
430 }
431 if (result != 0) {
432 getContext().getContentResolver().notifyChange(uri, null);
433 }
434 return result;
435 }
436
437 @Override
438 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
439 // We can not run in UI thread, do nothing for the official release.
440 if (isInUiThread()) return 0;
441 if (!ensureNativeChromeLoaded()) return 0;
442
443 // Check for invalid id values if provided.
444 // If it represents a bookmark node then it's the root node and not muta ble.
445 // Otherwise it represents a SQLite row id, so 0 is invalid.
446 long bookmarkId = INVALID_CONTENT_PROVIDER_ID;
447 try {
448 bookmarkId = ContentUris.parseId(uri);
449 if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0;
450 } catch (Exception e) {
451 }
452
453 int match = mUriMatcher.match(uri);
454 int result;
455 switch (match) {
456 case URI_MATCH_BOOKMARKS_ID:
457 String url = null;
458 if (values.containsKey(Browser.BookmarkColumns.URL)) {
459 url = values.getAsString(Browser.BookmarkColumns.URL);
460 }
461 String title = values.getAsString(Browser.BookmarkColumns.TITLE) ;
462 long parentId = INVALID_BOOKMARK_ID;
463 if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) {
464 parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM);
465 }
466 result = nativeUpdateBookmark(mNativeChromeBrowserProvider, book markId, url, title,
467 parentId);
468 updateLastModifiedBookmarkFolder(parentId);
469 break;
470 case URL_MATCH_API_BOOKMARK_ID:
471 result = updateBookmarkFromAPI(values, buildWhereClause(bookmark Id, selection),
472 selectionArgs);
473 break;
474 case URL_MATCH_API_BOOKMARK:
475 result = updateBookmarkFromAPI(values, selection, selectionArgs) ;
476 break;
477 case URL_MATCH_API_SEARCHES_ID:
478 result = updateSearchTermFromAPI(values, buildWhereClause(bookma rkId, selection),
479 selectionArgs);
480 break;
481 case URL_MATCH_API_SEARCHES:
482 result = updateSearchTermFromAPI(values, selection, selectionArg s);
483 break;
484 case URL_MATCH_API_HISTORY_CONTENT:
485 result = updateBookmarkFromAPI(values, buildHistoryWhereClause(s election),
486 selectionArgs);
487 break;
488 case URL_MATCH_API_HISTORY_CONTENT_ID:
489 result = updateBookmarkFromAPI(values,
490 buildHistoryWhereClause(bookmarkId, selection), selectio nArgs);
491 break;
492 case URL_MATCH_API_BOOKMARK_CONTENT:
493 result = updateBookmarkFromAPI(values, buildBookmarkWhereClause( selection),
494 selectionArgs);
495 break;
496 case URL_MATCH_API_BOOKMARK_CONTENT_ID:
497 result = updateBookmarkFromAPI(values,
498 buildBookmarkWhereClause(bookmarkId, selection), selecti onArgs);
499 break;
500 default:
501 throw new IllegalArgumentException(TAG + ": update - unknown URL " + uri);
502 }
503 if (result != 0) {
504 getContext().getContentResolver().notifyChange(uri, null);
505 }
506 return result;
507 }
508
509 @Override
510 public String getType(Uri uri) {
511 ensureUriMatcherInitialized();
512 int match = mUriMatcher.match(uri);
513 switch (match) {
514 case URI_MATCH_BOOKMARKS:
515 case URL_MATCH_API_BOOKMARK:
516 return "vnd.android.cursor.dir/bookmark";
517 case URI_MATCH_BOOKMARKS_ID:
518 case URL_MATCH_API_BOOKMARK_ID:
519 return "vnd.android.cursor.item/bookmark";
520 case URL_MATCH_API_SEARCHES:
521 return "vnd.android.cursor.dir/searches";
522 case URL_MATCH_API_SEARCHES_ID:
523 return "vnd.android.cursor.item/searches";
524 case URL_MATCH_API_HISTORY_CONTENT:
525 return BROWSER_CONTRACT_HISTORY_CONTENT_TYPE;
526 case URL_MATCH_API_HISTORY_CONTENT_ID:
527 return BROWSER_CONTRACT_HISTORY_CONTENT_ITEM_TYPE;
528 default:
529 throw new IllegalArgumentException(TAG + ": getType - unknown UR L " + uri);
530 }
531 }
532
533 private long addBookmark(ContentValues values) {
534 String url = values.getAsString(Browser.BookmarkColumns.URL);
535 String title = values.getAsString(Browser.BookmarkColumns.TITLE);
536 boolean isFolder = false;
537 if (values.containsKey(BOOKMARK_IS_FOLDER_PARAM)) {
538 isFolder = values.getAsBoolean(BOOKMARK_IS_FOLDER_PARAM);
539 }
540 long parentId = INVALID_BOOKMARK_ID;
541 if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) {
542 parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM);
543 }
544 long id = nativeAddBookmark(mNativeChromeBrowserProvider, url, title, is Folder, parentId);
545 if (id == INVALID_BOOKMARK_ID) return id;
546
547 if (isFolder) {
548 updateLastModifiedBookmarkFolder(id);
549 } else {
550 updateLastModifiedBookmarkFolder(parentId);
551 }
552 return id;
553 }
554
555 private void updateLastModifiedBookmarkFolder(long id) {
556 if (mLastModifiedBookmarkFolderId == id) return;
557
558 mLastModifiedBookmarkFolderId = id;
559 SharedPreferences sharedPreferences =
560 PreferenceManager.getDefaultSharedPreferences(getContext());
561 sharedPreferences.edit()
562 .putLong(LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY, mLastModifiedBook markFolderId)
563 .apply();
564 }
565
566 public static String getApiAuthority(Context context) {
567 return context.getPackageName() + API_AUTHORITY_SUFFIX;
568 }
569
570 public static String getInternalAuthority(Context context) {
571 return context.getPackageName() + AUTHORITY_SUFFIX;
572 }
573
574 public static Uri getBookmarksUri(Context context) {
575 return buildContentUri(getInternalAuthority(context), BOOKMARKS_PATH);
576 }
577
578 public static Uri getBookmarkFolderUri(Context context) {
579 return buildContentUri(getInternalAuthority(context), BOOKMARK_FOLDER_PA TH);
580 }
581
582 public static Uri getBookmarksApiUri(Context context) {
583 return buildContentUri(getApiAuthority(context), BOOKMARKS_PATH);
584 }
585
586 public static Uri getSearchesApiUri(Context context) {
587 return buildContentUri(getApiAuthority(context), SEARCHES_PATH);
588 }
589
590 private boolean bookmarkNodeExists(long nodeId) {
591 if (nodeId < 0) return false;
592 return nativeBookmarkNodeExists(mNativeChromeBrowserProvider, nodeId);
593 }
594
595 private long createBookmarksFolderOnce(String title, long parentId) {
596 return nativeCreateBookmarksFolderOnce(mNativeChromeBrowserProvider, tit le, parentId);
597 }
598
599 private BookmarkNode getBookmarkFolderHierarchy() {
600 return nativeGetAllBookmarkFolders(mNativeChromeBrowserProvider);
601 }
602
603 protected BookmarkNode getBookmarkNode(long nodeId, boolean getParent, boole an getChildren,
604 boolean getFavicons, boolean getThumbnails) {
605 BookmarkNode node = nativeGetBookmarkNode(mNativeChromeBrowserProvider, nodeId, getParent,
606 getChildren);
607 if (!getFavicons && !getThumbnails) return node;
608
609 // Favicons and thumbnails need to be populated separately as they are p rovided
610 // asynchronously by Chromium services other than the bookmark model.
611 if (node.parent() != null) populateNodeImages(node.parent(), getFavicons , getThumbnails);
612 for (BookmarkNode child : node.children()) {
613 populateNodeImages(child, getFavicons, getThumbnails);
614 }
615
616 return node;
617 }
618
619 private BookmarkNode getDefaultBookmarkFolder() {
620 // Try to access the bookmark folder last modified by us. If it doesn't exist anymore
621 // then use the synced node (Mobile Bookmarks).
622 BookmarkNode lastModified = getBookmarkNode(mLastModifiedBookmarkFolderI d, false, false,
623 false, false);
624 if (lastModified == null) {
625 lastModified = getMobileBookmarksFolder();
626 mLastModifiedBookmarkFolderId = lastModified != null ? lastModified. id() :
627 INVALID_BOOKMARK_ID;
628 }
629 return lastModified;
630 }
631
632 private void populateNodeImages(BookmarkNode node, boolean favicon, boolean thumbnail) {
633 if (node == null || node.type() != Type.URL) return;
634
635 if (favicon) {
636 node.setFavicon(nativeGetFaviconOrTouchIcon(mNativeChromeBrowserProv ider, node.url()));
637 }
638
639 if (thumbnail) {
640 node.setThumbnail(nativeGetThumbnail(mNativeChromeBrowserProvider, n ode.url()));
641 }
642 }
643
644 private BookmarkNode getMobileBookmarksFolder() {
645 if (mMobileBookmarksFolder == null) {
646 mMobileBookmarksFolder = nativeGetMobileBookmarksFolder(mNativeChrom eBrowserProvider);
647 }
648 return mMobileBookmarksFolder;
649 }
650
651 protected long getMobileBookmarksFolderId() {
652 BookmarkNode mobileBookmarks = getMobileBookmarksFolder();
653 return mobileBookmarks != null ? mobileBookmarks.id() : INVALID_BOOKMARK _ID;
654 }
655
656 private boolean isBookmarkInMobileBookmarksBranch(long nodeId) {
657 if (nodeId <= 0) return false;
658 return nativeIsBookmarkInMobileBookmarksBranch(mNativeChromeBrowserProvi der, nodeId);
659 }
660
661 static String argKey(int i) {
662 return "arg" + i;
663 }
664
665 @Override
666 public Bundle call(String method, String arg, Bundle extras) {
667 if (isInUiThread()) return null;
668 if (!ensureNativeChromeLoaded()) return null;
669 if (method == null || extras == null) return null;
670
671 Bundle result = new Bundle();
672 if (CLIENT_API_BOOKMARK_NODE_EXISTS.equals(method)) {
673 result.putBoolean(CLIENT_API_RESULT_KEY,
674 bookmarkNodeExists(extras.getLong(argKey(0))));
675 } else if (CLIENT_API_CREATE_BOOKMARKS_FOLDER_ONCE.equals(method)) {
676 result.putLong(CLIENT_API_RESULT_KEY,
677 createBookmarksFolderOnce(extras.getString(argKey(0)),
678 extras.getLong(argKey(1))));
679 } else if (CLIENT_API_GET_BOOKMARK_FOLDER_HIERARCHY.equals(method)) {
680 result.putParcelable(CLIENT_API_RESULT_KEY, getBookmarkFolderHierarc hy());
681 } else if (CLIENT_API_GET_BOOKMARK_NODE.equals(method)) {
682 result.putParcelable(CLIENT_API_RESULT_KEY,
683 getBookmarkNode(extras.getLong(argKey(0)),
684 extras.getBoolean(argKey(1)),
685 extras.getBoolean(argKey(2)),
686 extras.getBoolean(argKey(3)),
687 extras.getBoolean(argKey(4))));
688 } else if (CLIENT_API_GET_DEFAULT_BOOKMARK_FOLDER.equals(method)) {
689 result.putParcelable(CLIENT_API_RESULT_KEY, getDefaultBookmarkFolder ());
690 } else if (method.equals(CLIENT_API_GET_MOBILE_BOOKMARKS_FOLDER_ID)) {
691 result.putLong(CLIENT_API_RESULT_KEY, getMobileBookmarksFolderId());
692 } else if (CLIENT_API_IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH.equals(meth od)) {
693 result.putBoolean(CLIENT_API_RESULT_KEY,
694 isBookmarkInMobileBookmarksBranch(extras.getLong(argKey(0))) );
695 } else {
696 Log.w(TAG, "Received invalid method " + method);
697 return null;
698 }
699
700 return result;
701 }
702
703 /**
704 * The type of a BookmarkNode.
705 */
706 public enum Type {
707 URL,
708 FOLDER,
709 BOOKMARK_BAR,
710 OTHER_NODE,
711 MOBILE
712 }
713
714 /**
715 * Simple Data Object representing the chrome bookmark node.
716 */
717 public static class BookmarkNode implements Parcelable {
718 private final long mId;
719 private final String mName;
720 private final String mUrl;
721 private final Type mType;
722 private final BookmarkNode mParent;
723 private final List<BookmarkNode> mChildren = new ArrayList<BookmarkNode> ();
724
725 // Favicon and thumbnail optionally set in a 2-step procedure.
726 private byte[] mFavicon;
727 private byte[] mThumbnail;
728
729 /** Used to pass structured data back from the native code. */
730 // TODO(leandrogracia): remove public when VisibleForTesting works.
731 // @VisibleForTesting
732 public BookmarkNode(long id, Type type, String name, String url, Bookmar kNode parent) {
733 mId = id;
734 mName = name;
735 mUrl = url;
736 mType = type;
737 mParent = parent;
738 }
739
740 /**
741 * @return The id of this bookmark entry.
742 */
743 public long id() {
744 return mId;
745 }
746
747 /**
748 * @return The name of this bookmark entry.
749 */
750 public String name() {
751 return mName;
752 }
753
754 /**
755 * @return The URL of this bookmark entry.
756 */
757 public String url() {
758 return mUrl;
759 }
760
761 /**
762 * @return The type of this bookmark entry.
763 */
764 public Type type() {
765 return mType;
766 }
767
768 /**
769 * @return The bookmark favicon, if any.
770 */
771 public byte[] favicon() {
772 return mFavicon;
773 }
774
775 /**
776 * @return The bookmark thumbnail, if any.
777 */
778 public byte[] thumbnail() {
779 return mThumbnail;
780 }
781
782 /**
783 * @return The parent folder of this bookmark entry.
784 */
785 public BookmarkNode parent() {
786 return mParent;
787 }
788
789 /**
790 * Adds a child to this node.
791 *
792 * <p>
793 * Used solely by the native code.
794 */
795 // TODO(leandrogracia): remove public when VisibleForTesting works.
796 // @VisibleForTesting
797 @CalledByNativeUnchecked("BookmarkNode")
798 public void addChild(BookmarkNode child) {
799 mChildren.add(child);
800 }
801
802 /**
803 * @return The child bookmark nodes of this node.
804 */
805 public List<BookmarkNode> children() {
806 return mChildren;
807 }
808
809 /**
810 * @return Whether this node represents a bookmarked URL or not.
811 */
812 public boolean isUrl() {
813 return mUrl != null;
814 }
815
816 /**
817 * @return true if the two individual nodes contain the same information .
818 * The existence of parent and children nodes is checked, but their cont ents are not.
819 */
820 public boolean equalContents(BookmarkNode node) {
821 return node != null &&
822 mId == node.mId &&
823 !(mName == null ^ node.mName == null) &&
824 (mName == null || mName.equals(node.mName)) &&
825 !(mUrl == null ^ node.mUrl == null) &&
826 (mUrl == null || mUrl.equals(node.mUrl)) &&
827 mType == node.mType &&
828 byteArrayEqual(mFavicon, node.mFavicon) &&
829 byteArrayEqual(mThumbnail, node.mThumbnail) &&
830 !(mParent == null ^ node.mParent == null) &&
831 children().size() == node.children().size();
832 }
833
834 private static boolean byteArrayEqual(byte[] byte1, byte[] byte2) {
835 if (byte1 == null && byte2 != null) return byte2.length == 0;
836 if (byte2 == null && byte1 != null) return byte1.length == 0;
837 return Arrays.equals(byte1, byte2);
838 }
839
840 @CalledByNative("BookmarkNode")
841 private static BookmarkNode create(
842 long id, int type, String name, String url, BookmarkNode parent) {
843 return new BookmarkNode(id, Type.values()[type], name, url, parent);
844 }
845
846 // TODO(leandrogracia): remove public when VisibleForTesting works.
847 // @VisibleForTesting
848 public void setFavicon(byte[] favicon) {
849 mFavicon = favicon;
850 }
851
852 // TODO(leandrogracia): remove public when VisibleForTesting works.
853 // @VisibleForTesting
854 public void setThumbnail(byte[] thumbnail) {
855 mThumbnail = thumbnail;
856 }
857
858 @Override
859 public int describeContents() {
860 return 0;
861 }
862
863 @Override
864 public void writeToParcel(Parcel dest, int flags) {
865 // Write the current node id.
866 dest.writeLong(mId);
867
868 // Serialize the full hierarchy from the root.
869 getHierarchyRoot().writeNodeContentsRecursive(dest);
870 }
871
872 // TODO(leandrogracia): remove public when VisibleForTesting works.
873 // @VisibleForTesting
874 public BookmarkNode getHierarchyRoot() {
875 BookmarkNode root = this;
876 while (root.parent() != null) {
877 root = root.parent();
878 }
879 return root;
880 }
881
882 private void writeNodeContentsRecursive(Parcel dest) {
883 writeNodeContents(dest);
884 dest.writeInt(mChildren.size());
885 for (BookmarkNode child : mChildren) {
886 child.writeNodeContentsRecursive(dest);
887 }
888 }
889
890 private void writeNodeContents(Parcel dest) {
891 dest.writeLong(mId);
892 dest.writeString(mName);
893 dest.writeString(mUrl);
894 dest.writeInt(mType.ordinal());
895 dest.writeByteArray(mFavicon);
896 dest.writeByteArray(mThumbnail);
897 dest.writeLong(mParent != null ? mParent.mId : INVALID_BOOKMARK_ID);
898 }
899
900 public static final Creator<BookmarkNode> CREATOR = new Creator<Bookmark Node>() {
901 private HashMap<Long, BookmarkNode> mNodeMap;
902
903 @Override
904 public BookmarkNode createFromParcel(Parcel source) {
905 mNodeMap = new HashMap<Long, BookmarkNode>();
906 long currentNodeId = source.readLong();
907 readNodeContentsRecursive(source);
908 BookmarkNode node = getNode(currentNodeId);
909 mNodeMap.clear();
910 return node;
911 }
912
913 @Override
914 public BookmarkNode[] newArray(int size) {
915 return new BookmarkNode[size];
916 }
917
918 private BookmarkNode getNode(long id) {
919 if (id == INVALID_BOOKMARK_ID) return null;
920 Long nodeId = Long.valueOf(id);
921 if (!mNodeMap.containsKey(nodeId)) {
922 Log.e(TAG, "Invalid BookmarkNode hierarchy. Unknown id " + i d);
923 return null;
924 }
925 return mNodeMap.get(nodeId);
926 }
927
928 private BookmarkNode readNodeContents(Parcel source) {
929 long id = source.readLong();
930 String name = source.readString();
931 String url = source.readString();
932 int type = source.readInt();
933 byte[] favicon = source.createByteArray();
934 byte[] thumbnail = source.createByteArray();
935 long parentId = source.readLong();
936 if (type < 0 || type >= Type.values().length) {
937 Log.w(TAG, "Invalid node type ordinal value.");
938 return null;
939 }
940
941 BookmarkNode node = new BookmarkNode(id, Type.values()[type], na me, url,
942 getNode(parentId));
943 node.setFavicon(favicon);
944 node.setThumbnail(thumbnail);
945 return node;
946 }
947
948 private BookmarkNode readNodeContentsRecursive(Parcel source) {
949 BookmarkNode node = readNodeContents(source);
950 if (node == null) return null;
951
952 Long nodeId = Long.valueOf(node.id());
953 if (mNodeMap.containsKey(nodeId)) {
954 Log.e(TAG, "Invalid BookmarkNode hierarchy. Duplicate id " + node.id());
955 return null;
956 }
957 mNodeMap.put(nodeId, node);
958
959 int numChildren = source.readInt();
960 for (int i = 0; i < numChildren; ++i) {
961 node.addChild(readNodeContentsRecursive(source));
962 }
963
964 return node;
965 }
966 };
967 }
968
969 private long addBookmarkFromAPI(ContentValues values) {
970 BookmarkRow row = BookmarkRow.fromContentValues(values);
971 if (row.url == null) {
972 throw new IllegalArgumentException("Must have a bookmark URL");
973 }
974 return nativeAddBookmarkFromAPI(mNativeChromeBrowserProvider,
975 row.url, row.created, row.isBookmark, row.date, row.favicon,
976 row.title, row.visits, row.parentId);
977 }
978
979 private Cursor queryBookmarkFromAPI(String[] projectionIn, String selection,
980 String[] selectionArgs, String sortOrder) {
981 String[] projection = null;
982 if (projectionIn == null || projectionIn.length == 0) {
983 projection = BOOKMARK_DEFAULT_PROJECTION;
984 } else {
985 projection = projectionIn;
986 }
987
988 return nativeQueryBookmarkFromAPI(mNativeChromeBrowserProvider, projecti on, selection,
989 selectionArgs, sortOrder);
990 }
991
992 private int updateBookmarkFromAPI(ContentValues values, String selection,
993 String[] selectionArgs) {
994 BookmarkRow row = BookmarkRow.fromContentValues(values);
995 return nativeUpdateBookmarkFromAPI(mNativeChromeBrowserProvider,
996 row.url, row.created, row.isBookmark, row.date,
997 row.favicon, row.title, row.visits, row.parentId, selection, sel ectionArgs);
998 }
999
1000 private int removeBookmarkFromAPI(String selection, String[] selectionArgs) {
1001 return nativeRemoveBookmarkFromAPI(mNativeChromeBrowserProvider, selecti on, selectionArgs);
1002 }
1003
1004 private int removeHistoryFromAPI(String selection, String[] selectionArgs) {
1005 return nativeRemoveHistoryFromAPI(mNativeChromeBrowserProvider, selectio n, selectionArgs);
1006 }
1007
1008 @CalledByNative
1009 private void onBookmarkChanged() {
1010 getContext().getContentResolver().notifyChange(
1011 buildAPIContentUri(getContext(), BOOKMARKS_PATH), null);
1012 }
1013
1014 @CalledByNative
1015 private void onSearchTermChanged() {
1016 getContext().getContentResolver().notifyChange(
1017 buildAPIContentUri(getContext(), SEARCHES_PATH), null);
1018 }
1019
1020 private long addSearchTermFromAPI(ContentValues values) {
1021 SearchRow row = SearchRow.fromContentValues(values);
1022 if (row.term == null) {
1023 throw new IllegalArgumentException("Must have a search term");
1024 }
1025 return nativeAddSearchTermFromAPI(mNativeChromeBrowserProvider, row.term , row.date);
1026 }
1027
1028 private int updateSearchTermFromAPI(ContentValues values, String selection,
1029 String[] selectionArgs) {
1030 SearchRow row = SearchRow.fromContentValues(values);
1031 return nativeUpdateSearchTermFromAPI(mNativeChromeBrowserProvider,
1032 row.term, row.date, selection, selectionArgs);
1033 }
1034
1035 private Cursor querySearchTermFromAPI(String[] projectionIn, String selectio n,
1036 String[] selectionArgs, String sortOrder) {
1037 String[] projection = null;
1038 if (projectionIn == null || projectionIn.length == 0) {
1039 projection = android.provider.Browser.SEARCHES_PROJECTION;
1040 } else {
1041 projection = projectionIn;
1042 }
1043 return nativeQuerySearchTermFromAPI(mNativeChromeBrowserProvider, projec tion, selection,
1044 selectionArgs, sortOrder);
1045 }
1046
1047 private int removeSearchFromAPI(String selection, String[] selectionArgs) {
1048 return nativeRemoveSearchTermFromAPI(mNativeChromeBrowserProvider,
1049 selection, selectionArgs);
1050 }
1051
1052 private static boolean isInUiThread() {
1053 if (!ThreadUtils.runningOnUiThread()) return false;
1054
1055 if (!"REL".equals(Build.VERSION.CODENAME)) {
1056 throw new IllegalStateException("Shouldn't run in the UI thread");
1057 }
1058
1059 Log.w(TAG, "ChromeBrowserProvider methods cannot be called from the UI t hread.");
1060 return true;
1061 }
1062
1063 private static Uri buildContentUri(String authority, String path) {
1064 return Uri.parse("content://" + authority + "/" + path);
1065 }
1066
1067 private static Uri buildAPIContentUri(Context context, String path) {
1068 return buildContentUri(context.getPackageName() + API_AUTHORITY_SUFFIX, path);
1069 }
1070
1071 private static String buildWhereClause(long id, String selection) {
1072 StringBuffer sb = new StringBuffer();
1073 sb.append(BaseColumns._ID);
1074 sb.append(" = ");
1075 sb.append(id);
1076 if (!TextUtils.isEmpty(selection)) {
1077 sb.append(" AND (");
1078 sb.append(selection);
1079 sb.append(")");
1080 }
1081 return sb.toString();
1082 }
1083
1084 private static String buildHistoryWhereClause(long id, String selection) {
1085 return buildWhereClause(id, buildBookmarkWhereClause(selection, false));
1086 }
1087
1088 private static String buildHistoryWhereClause(String selection) {
1089 return buildBookmarkWhereClause(selection, false);
1090 }
1091
1092 /**
1093 * @return a SQL where class which is inserted the bookmark condition.
1094 */
1095 private static String buildBookmarkWhereClause(String selection, boolean is_ bookmark) {
1096 StringBuffer sb = new StringBuffer();
1097 sb.append(BookmarkColumns.BOOKMARK);
1098 sb.append(is_bookmark ? " = 1 " : " = 0");
1099 if (!TextUtils.isEmpty(selection)) {
1100 sb.append(" AND (");
1101 sb.append(selection);
1102 sb.append(")");
1103 }
1104 return sb.toString();
1105 }
1106
1107 private static String buildBookmarkWhereClause(long id, String selection) {
1108 return buildWhereClause(id, buildBookmarkWhereClause(selection, true));
1109 }
1110
1111 private static String buildBookmarkWhereClause(String selection) {
1112 return buildBookmarkWhereClause(selection, true);
1113 }
1114
1115 // Wrap the value of BookmarkColumn.
1116 private static class BookmarkRow {
1117 Boolean isBookmark;
1118 Long created;
1119 String url;
1120 Long date;
1121 byte[] favicon;
1122 String title;
1123 Integer visits;
1124 long parentId;
1125
1126 static BookmarkRow fromContentValues(ContentValues values) {
1127 BookmarkRow row = new BookmarkRow();
1128 if (values.containsKey(BookmarkColumns.URL)) {
1129 row.url = values.getAsString(BookmarkColumns.URL);
1130 }
1131 if (values.containsKey(BookmarkColumns.BOOKMARK)) {
1132 row.isBookmark = values.getAsInteger(BookmarkColumns.BOOKMARK) ! = 0;
1133 }
1134 if (values.containsKey(BookmarkColumns.CREATED)) {
1135 row.created = values.getAsLong(BookmarkColumns.CREATED);
1136 }
1137 if (values.containsKey(BookmarkColumns.DATE)) {
1138 row.date = values.getAsLong(BookmarkColumns.DATE);
1139 }
1140 if (values.containsKey(BookmarkColumns.FAVICON)) {
1141 row.favicon = values.getAsByteArray(BookmarkColumns.FAVICON);
1142 // We need to know that the caller set the favicon column.
1143 if (row.favicon == null) {
1144 row.favicon = new byte[0];
1145 }
1146 }
1147 if (values.containsKey(BookmarkColumns.TITLE)) {
1148 row.title = values.getAsString(BookmarkColumns.TITLE);
1149 }
1150 if (values.containsKey(BookmarkColumns.VISITS)) {
1151 row.visits = values.getAsInteger(BookmarkColumns.VISITS);
1152 }
1153 if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) {
1154 row.parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM);
1155 }
1156 return row;
1157 }
1158 }
1159
1160 // Wrap the value of SearchColumn.
1161 private static class SearchRow {
1162 String term;
1163 Long date;
1164
1165 static SearchRow fromContentValues(ContentValues values) {
1166 SearchRow row = new SearchRow();
1167 if (values.containsKey(SearchColumns.SEARCH)) {
1168 row.term = values.getAsString(SearchColumns.SEARCH);
1169 }
1170 if (values.containsKey(SearchColumns.DATE)) {
1171 row.date = values.getAsLong(SearchColumns.DATE);
1172 }
1173 return row;
1174 }
1175 }
1176
1177 /**
1178 * Returns true if the native side of the class is initialized.
1179 */
1180 protected boolean isNativeSideInitialized() {
1181 return mNativeChromeBrowserProvider != 0;
1182 }
1183
1184 /**
1185 * Make sure chrome is running. This method mustn't run on UI thread.
1186 *
1187 * @return Whether the native chrome process is running successfully once th is has returned.
1188 */
1189 private boolean ensureNativeChromeLoaded() {
1190 ensureUriMatcherInitialized();
1191
1192 synchronized(mLoadNativeLock) {
1193 if (mNativeChromeBrowserProvider != 0) return true;
1194
1195 final AtomicBoolean retVal = new AtomicBoolean(true);
1196 ThreadUtils.runOnUiThreadBlocking(new Runnable() {
1197 @Override
1198 public void run() {
1199 retVal.set(ensureNativeChromeLoadedOnUIThread());
1200 }
1201 });
1202 return retVal.get();
1203 }
1204 }
1205
1206 /**
1207 * This method should only run on UI thread.
1208 */
1209 protected boolean ensureNativeChromeLoadedOnUIThread() {
1210 if (isNativeSideInitialized()) return true;
1211 mNativeChromeBrowserProvider = nativeInit();
1212 return isNativeSideInitialized();
1213 }
1214
1215 @Override
1216 protected void finalize() throws Throwable {
1217 try {
1218 // Tests might try to destroy this in the wrong thread.
1219 ThreadUtils.runOnUiThreadBlocking(new Runnable() {
1220 @Override
1221 public void run() {
1222 ensureNativeChromeDestroyedOnUIThread();
1223 }
1224 });
1225 } finally {
1226 super.finalize();
1227 }
1228 }
1229
1230 /**
1231 * This method should only run on UI thread.
1232 */
1233 private void ensureNativeChromeDestroyedOnUIThread() {
1234 if (isNativeSideInitialized()) {
1235 nativeDestroy(mNativeChromeBrowserProvider);
1236 mNativeChromeBrowserProvider = 0;
1237 }
1238 }
1239
1240 /**
1241 * Call to get the intent to create a bookmark shortcut on homescreen.
1242 */
1243 public static Intent getShortcutToBookmark(String url, String title, Bitmap favicon, int rValue,
1244 int gValue, int bValue, Activity activity) {
1245 return BookmarkUtils.createAddToHomeIntent(activity, url, title, favicon , rValue, gValue,
1246 bValue);
1247 }
1248
1249 private native int nativeInit();
1250 private native void nativeDestroy(int nativeChromeBrowserProvider);
1251
1252 // Public API native methods.
1253 private native long nativeAddBookmark(int nativeChromeBrowserProvider,
1254 String url, String title, boolean isFolder, long parentId);
1255
1256 private native int nativeRemoveBookmark(int nativeChromeBrowserProvider, lon g id);
1257
1258 private native int nativeUpdateBookmark(int nativeChromeBrowserProvider,
1259 long id, String url, String title, long parentId);
1260
1261 private native long nativeAddBookmarkFromAPI(int nativeChromeBrowserProvider ,
1262 String url, Long created, Boolean isBookmark, Long date, byte[] favi con,
1263 String title, Integer visits, long parentId);
1264
1265 private native SQLiteCursor nativeQueryBookmarkFromAPI(int nativeChromeBrows erProvider,
1266 String[] projection, String selection, String[] selectionArgs, Strin g sortOrder);
1267
1268 private native int nativeUpdateBookmarkFromAPI(int nativeChromeBrowserProvid er,
1269 String url, Long created, Boolean isBookmark, Long date, byte[] favi con,
1270 String title, Integer visits, long parentId, String selection, Strin g[] selectionArgs);
1271
1272 private native int nativeRemoveBookmarkFromAPI(int nativeChromeBrowserProvid er,
1273 String selection, String[] selectionArgs);
1274
1275 private native int nativeRemoveHistoryFromAPI(int nativeChromeBrowserProvide r,
1276 String selection, String[] selectionArgs);
1277
1278 private native long nativeAddSearchTermFromAPI(int nativeChromeBrowserProvid er,
1279 String term, Long date);
1280
1281 private native SQLiteCursor nativeQuerySearchTermFromAPI(int nativeChromeBro wserProvider,
1282 String[] projection, String selection, String[] selectionArgs, Strin g sortOrder);
1283
1284 private native int nativeUpdateSearchTermFromAPI(int nativeChromeBrowserProv ider,
1285 String search, Long date, String selection, String[] selectionArgs);
1286
1287 private native int nativeRemoveSearchTermFromAPI(int nativeChromeBrowserProv ider,
1288 String selection, String[] selectionArgs);
1289
1290 // Client API native methods.
1291 private native boolean nativeBookmarkNodeExists(int nativeChromeBrowserProvi der, long id);
1292
1293 private native long nativeCreateBookmarksFolderOnce(int nativeChromeBrowserP rovider,
1294 String title, long parentId);
1295
1296 private native BookmarkNode nativeGetAllBookmarkFolders(int nativeChromeBrow serProvider);
1297
1298 private native BookmarkNode nativeGetBookmarkNode(int nativeChromeBrowserPro vider,
1299 long id, boolean getParent, boolean getChildren);
1300
1301 private native BookmarkNode nativeGetMobileBookmarksFolder(int nativeChromeB rowserProvider);
1302
1303 private native boolean nativeIsBookmarkInMobileBookmarksBranch(int nativeChr omeBrowserProvider,
1304 long id);
1305
1306 private native byte[] nativeGetFaviconOrTouchIcon(int nativeChromeBrowserPro vider, String url);
1307
1308 private native byte[] nativeGetThumbnail(int nativeChromeBrowserProvider, St ring url);
1309 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698