| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.chrome.browser.enhancedbookmarks; |
| |
| import android.app.Activity; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.graphics.Bitmap; |
| import android.net.Uri; |
| import android.os.AsyncTask; |
| import android.os.Bundle; |
| import android.provider.Browser; |
| import android.text.TextUtils; |
| |
| import org.chromium.base.ApiCompatibilityUtils; |
| import org.chromium.base.metrics.RecordHistogram; |
| import org.chromium.base.metrics.RecordUserAction; |
| import org.chromium.chrome.R; |
| import org.chromium.chrome.browser.ChromeBrowserProviderClient; |
| import org.chromium.chrome.browser.IntentHandler; |
| import org.chromium.chrome.browser.UrlConstants; |
| import org.chromium.chrome.browser.bookmark.BookmarksBridge; |
| import org.chromium.chrome.browser.bookmark.BookmarksBridge.BookmarkItem; |
| import org.chromium.chrome.browser.document.ChromeLauncherActivity; |
| import org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarksModel.AddBookmarkCallback; |
| import org.chromium.chrome.browser.favicon.FaviconHelper; |
| import org.chromium.chrome.browser.ntp.NewTabPageUma; |
| import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; |
| import org.chromium.chrome.browser.offlinepages.OfflinePageFreeUpSpaceCallback; |
| import org.chromium.chrome.browser.offlinepages.OfflinePageFreeUpSpaceDialog; |
| import org.chromium.chrome.browser.offlinepages.OfflinePageOpenStorageSettingsDialog; |
| import org.chromium.chrome.browser.offlinepages.OfflinePageStorageSpacePolicy; |
| import org.chromium.chrome.browser.offlinepages.OfflinePageUtils; |
| import org.chromium.chrome.browser.snackbar.Snackbar; |
| import org.chromium.chrome.browser.snackbar.SnackbarManager; |
| import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController; |
| import org.chromium.chrome.browser.tab.Tab; |
| import org.chromium.chrome.browser.util.FeatureUtilities; |
| import org.chromium.chrome.browser.util.MathUtils; |
| import org.chromium.components.bookmarks.BookmarkId; |
| import org.chromium.components.bookmarks.BookmarkType; |
| import org.chromium.content_public.browser.WebContents; |
| import org.chromium.ui.base.DeviceFormFactor; |
| |
| /** |
| * A class holding static util functions for enhanced bookmark. |
| */ |
| public class EnhancedBookmarkUtils { |
| |
| private static final String BOOKMARK_SAVE_NAME = "SaveBookmark"; |
| private static final int[] DEFAULT_BACKGROUND_COLORS = { |
| 0xFFE64A19, |
| 0xFFF09300, |
| 0xFFAFB42B, |
| 0xFF689F38, |
| 0xFF0B8043, |
| 0xFF0097A7, |
| 0xFF7B1FA2, |
| 0xFFC2185B |
| }; |
| |
| /** |
| * @return True if enhanced bookmark feature is enabled. |
| */ |
| public static boolean isEnhancedBookmarkEnabled() { |
| return BookmarksBridge.isEnhancedBookmarksEnabled(); |
| } |
| |
| /** |
| * If the tab has already been bookmarked, start {@link EnhancedBookmarkEditActivity} for the |
| * bookmark. If not, add the bookmark to bookmarkmodel, and show a snackbar notifying the user. |
| * @param idToAdd The bookmark ID if the tab has already been bookmarked. |
| * @param bookmarkModel The enhanced bookmark model. |
| * @param tab The tab to add or edit a bookmark. |
| * @param snackbarManager The SnackbarManager used to show the snackbar. |
| * @param activity Current activity. |
| */ |
| public static void addOrEditBookmark(long idToAdd, EnhancedBookmarksModel bookmarkModel, |
| Tab tab, SnackbarManager snackbarManager, Activity activity) { |
| if (idToAdd != ChromeBrowserProviderClient.INVALID_BOOKMARK_ID) { |
| startEditActivity(activity, new BookmarkId(idToAdd, BookmarkType.NORMAL), |
| tab.getWebContents()); |
| return; |
| } |
| |
| BookmarkId parent = bookmarkModel.getDefaultFolder(); |
| bookmarkModel.addBookmarkAsync(parent, bookmarkModel.getChildCount(parent), tab.getTitle(), |
| tab.getUrl(), tab.getWebContents(), tab.isShowingErrorPage(), |
| createAddBookmarkCallback(bookmarkModel, snackbarManager, activity)); |
| } |
| |
| /** |
| * Saves an offline copy for the specified tab that is bookmarked. A snackbar will be shown to |
| * notify the user. |
| * @param bookmarkId The bookmark ID for the tab. |
| * @param bookmarkModel The enhanced bookmark model. |
| * @param tab The bookmarked tab to save an offline copy. |
| * @param snackbarManager The SnackbarManager used to show the snackbar. |
| * @param activity Current activity. |
| */ |
| public static void saveBookmarkOffline(long bookmarkId, EnhancedBookmarksModel bookmarkModel, |
| Tab tab, final SnackbarManager snackbarManager, Activity activity) { |
| assert bookmarkId != ChromeBrowserProviderClient.INVALID_BOOKMARK_ID; |
| bookmarkModel.saveOfflinePage(new BookmarkId(bookmarkId, BookmarkType.NORMAL), |
| tab.getWebContents(), |
| createAddBookmarkCallback(bookmarkModel, snackbarManager, activity)); |
| } |
| |
| private static void showSnackbarForAddingBookmark(final EnhancedBookmarksModel bookmarkModel, |
| final SnackbarManager snackbarManager, final Activity activity, |
| final BookmarkId bookmarkId, final int saveResult, boolean isStorageAlmostFull) { |
| SnackbarController snackbarController = null; |
| int messageId; |
| int buttonId = 0; |
| |
| OfflinePageBridge offlinePageBridge = bookmarkModel.getOfflinePageBridge(); |
| if (offlinePageBridge == null) { |
| messageId = R.string.enhanced_bookmark_page_saved; |
| } else if (saveResult == AddBookmarkCallback.SKIPPED) { |
| messageId = R.string.offline_pages_page_skipped; |
| } else if (isStorageAlmostFull) { |
| messageId = saveResult == AddBookmarkCallback.SAVED |
| ? R.string.offline_pages_page_saved_storage_near_full |
| : R.string.offline_pages_page_failed_to_save_storage_near_full; |
| // Show "Free up space" button. |
| buttonId = R.string.offline_pages_free_up_space_title; |
| snackbarController = createSnackbarControllerForFreeUpSpaceButton( |
| bookmarkModel, snackbarManager, activity); |
| } else { |
| messageId = saveResult == AddBookmarkCallback.SAVED |
| ? R.string.offline_pages_page_saved |
| : R.string.offline_pages_page_failed_to_save; |
| } |
| |
| // Show "Edit" button when "Free up space" button is not desired, regardless |
| // whether the offline page was saved successfuly, because a bookmark was |
| // created and user might want to edit title. |
| if (buttonId == 0) { |
| buttonId = R.string.enhanced_bookmark_item_edit; |
| snackbarController = createSnackbarControllerForEditButton( |
| bookmarkModel, activity, bookmarkId); |
| } |
| |
| snackbarManager.showSnackbar( |
| Snackbar.make(activity.getString(messageId), snackbarController) |
| .setAction(activity.getString(buttonId), null) |
| .setSingleLine(false)); |
| } |
| |
| private static AddBookmarkCallback createAddBookmarkCallback( |
| final EnhancedBookmarksModel bookmarkModel, final SnackbarManager snackbarManager, |
| final Activity activity) { |
| return new AddBookmarkCallback() { |
| @Override |
| public void onBookmarkAdded(final BookmarkId bookmarkId, final int saveResult) { |
| // Shows the snackbar right away when offline pages feature is not enabled since |
| // there is no need to wait to get the storage info. |
| if (bookmarkModel.getOfflinePageBridge() == null) { |
| showSnackbarForAddingBookmark(bookmarkModel, snackbarManager, activity, |
| bookmarkId, saveResult, false); |
| return; |
| } |
| |
| // Gets the storage info asynchronously which is needed to produce the message for |
| // the snackbar. |
| new AsyncTask<Void, Void, Boolean>() { |
| @Override |
| protected Boolean doInBackground(Void... params) { |
| return OfflinePageUtils.isStorageAlmostFull(); |
| } |
| |
| @Override |
| protected void onPostExecute(Boolean isStorageAlmostFull) { |
| showSnackbarForAddingBookmark(bookmarkModel, snackbarManager, activity, |
| bookmarkId, saveResult, isStorageAlmostFull); |
| } |
| }.execute(); |
| } |
| }; |
| } |
| |
| /** |
| * Creates a snackbar controller for a case where "Edit" button is shown to edit the newly |
| * created bookmark. |
| */ |
| private static SnackbarController createSnackbarControllerForEditButton( |
| final EnhancedBookmarksModel bookmarkModel, final Activity activity, |
| final BookmarkId bookmarkId) { |
| return new SnackbarController() { |
| @Override |
| public void onDismissForEachType(boolean isTimeout) {} |
| |
| @Override |
| public void onDismissNoAction(Object actionData) { |
| RecordUserAction.record("EnhancedBookmarks.EditAfterCreateButtonNotClicked"); |
| // This method will be called only if the snackbar is dismissed by timeout. |
| bookmarkModel.destroy(); |
| } |
| |
| @Override |
| public void onAction(Object actionData) { |
| RecordUserAction.record("EnhancedBookmarks.EditAfterCreateButtonClicked"); |
| // Show edit activity with the name of parent folder highlighted. |
| startEditActivity(activity, bookmarkId, null); |
| bookmarkModel.destroy(); |
| } |
| }; |
| } |
| |
| /** |
| * Creates a snackbar controller for a case where "Free up space" button is shown to clean up |
| * space taken by the offline pages. |
| */ |
| private static SnackbarController createSnackbarControllerForFreeUpSpaceButton( |
| final EnhancedBookmarksModel bookmarkModel, final SnackbarManager snackbarManager, |
| final Activity activity) { |
| return new SnackbarController() { |
| @Override |
| public void onDismissForEachType(boolean isTimeout) {} |
| |
| @Override |
| public void onDismissNoAction(Object actionData) { |
| // This method will be called only if the snackbar is dismissed by timeout. |
| RecordUserAction.record( |
| "OfflinePages.SaveStatusSnackbar.FreeUpSpaceButtonNotClicked"); |
| bookmarkModel.destroy(); |
| } |
| |
| @Override |
| public void onAction(Object actionData) { |
| RecordUserAction.record("OfflinePages.SaveStatusSnackbar.FreeUpSpaceButtonClicked"); |
| OfflinePageStorageSpacePolicy policy = |
| new OfflinePageStorageSpacePolicy(bookmarkModel.getOfflinePageBridge()); |
| if (policy.hasPagesToCleanUp()) { |
| OfflinePageFreeUpSpaceCallback callback = new OfflinePageFreeUpSpaceCallback() { |
| @Override |
| public void onFreeUpSpaceDone() { |
| snackbarManager.showSnackbar( |
| OfflinePageFreeUpSpaceDialog.createStorageClearedSnackbar( |
| activity)); |
| bookmarkModel.destroy(); |
| } |
| @Override |
| public void onFreeUpSpaceCancelled() { |
| bookmarkModel.destroy(); |
| } |
| }; |
| OfflinePageFreeUpSpaceDialog dialog = OfflinePageFreeUpSpaceDialog.newInstance( |
| bookmarkModel.getOfflinePageBridge(), callback); |
| dialog.show(activity.getFragmentManager(), null); |
| } else { |
| OfflinePageOpenStorageSettingsDialog.showDialog(activity); |
| } |
| } |
| }; |
| } |
| |
| /** |
| * Shows enhanced bookmark main UI, if it is turned on. Does nothing if it is turned off. |
| * @return True if enhanced bookmark is on, false otherwise. |
| */ |
| public static boolean showEnhancedBookmarkIfEnabled(Activity activity) { |
| if (!isEnhancedBookmarkEnabled()) { |
| return false; |
| } |
| if (DeviceFormFactor.isTablet(activity)) { |
| openUrl(activity, UrlConstants.BOOKMARKS_URL); |
| } else { |
| activity.startActivity(new Intent(activity, EnhancedBookmarkActivity.class)); |
| } |
| return true; |
| } |
| |
| /** |
| * Starts an {@link EnhancedBookmarkEditActivity} for the given {@link BookmarkId}. |
| */ |
| public static void startEditActivity( |
| Context context, BookmarkId bookmarkId, WebContents webContents) { |
| Intent intent = new Intent(context, EnhancedBookmarkEditActivity.class); |
| intent.putExtra(EnhancedBookmarkEditActivity.INTENT_BOOKMARK_ID, bookmarkId.toString()); |
| if (webContents != null) { |
| intent.putExtra(EnhancedBookmarkEditActivity.INTENT_WEB_CONTENTS, webContents); |
| } |
| if (context instanceof EnhancedBookmarkActivity) { |
| ((EnhancedBookmarkActivity) context).startActivityForResult( |
| intent, EnhancedBookmarkActivity.EDIT_BOOKMARK_REQUEST_CODE); |
| } else { |
| context.startActivity(intent); |
| } |
| } |
| |
| /** |
| * Generate color based on bookmarked url's hash code. Same color will |
| * always be returned given same bookmark item. |
| * |
| * @param item bookmark the color represents for |
| * @return int for the generated color |
| */ |
| public static int generateBackgroundColor(BookmarkItem item) { |
| int normalizedIndex = MathUtils.positiveModulo(item.getUrl().hashCode(), |
| DEFAULT_BACKGROUND_COLORS.length); |
| return DEFAULT_BACKGROUND_COLORS[normalizedIndex]; |
| } |
| |
| /** |
| * Save the bookmark in bundle to save state of a fragment/activity. |
| * @param bundle Argument holder or savedInstanceState of the fragment/activity. |
| * @param bookmark The bookmark to save. |
| */ |
| public static void saveBookmarkIdToBundle(Bundle bundle, BookmarkId bookmark) { |
| bundle.putString(BOOKMARK_SAVE_NAME, bookmark.toString()); |
| } |
| |
| /** |
| * Retrieve the bookmark previously saved in the arguments bundle. |
| * @param bundle Argument holder or savedInstanceState of the fragment/activity. |
| * @return The ID of the bookmark to retrieve. |
| */ |
| public static BookmarkId getBookmarkIdFromBundle(Bundle bundle) { |
| return BookmarkId.getBookmarkIdFromString(bundle.getString(BOOKMARK_SAVE_NAME)); |
| } |
| |
| /** |
| * Opens a bookmark depending on connection status and reports UMA. |
| * @param model Enhanced bookmarks model to manage the bookmark. |
| * @param activity Activity requesting to open the bookmark. |
| * @param bookmarkId ID of the bookmark to be opened. |
| * @param launchLocation Location from which the bookmark is being opened. |
| * @return Whether the bookmark was successfully opened. |
| */ |
| public static boolean openBookmark(EnhancedBookmarksModel model, Activity activity, |
| BookmarkId bookmarkId, int launchLocation) { |
| if (model.getBookmarkById(bookmarkId) == null) return false; |
| |
| String url = model.getLaunchUrlAndMarkAccessed(activity, bookmarkId); |
| |
| // TODO(jianli): Notify the user about the failure. |
| if (TextUtils.isEmpty(url)) return false; |
| |
| NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_BOOKMARK); |
| if (url.startsWith("file:")) { |
| RecordHistogram.recordEnumeratedHistogram( |
| "OfflinePages.LaunchLocation", launchLocation, LaunchLocation.COUNT); |
| } else { |
| RecordHistogram.recordEnumeratedHistogram( |
| "Stars.LaunchLocation", launchLocation, LaunchLocation.COUNT); |
| } |
| |
| openUrl(activity, url); |
| return true; |
| } |
| |
| private static void openUrl(Activity activity, String url) { |
| Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); |
| intent.setClassName(activity.getApplicationContext().getPackageName(), |
| ChromeLauncherActivity.class.getName()); |
| intent.putExtra(Browser.EXTRA_APPLICATION_ID, |
| activity.getApplicationContext().getPackageName()); |
| intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| IntentHandler.startActivityForTrustedIntent(intent, activity); |
| } |
| |
| /** |
| * Get dominant color from bitmap. This function uses favicon helper to fulfil its task. |
| * @param bitmap The bitmap to extract color from. |
| * @return The dominant color in ARGB format. |
| */ |
| public static int getDominantColorForBitmap(Bitmap bitmap) { |
| int mDominantColor = FaviconHelper.getDominantColorForBitmap(bitmap); |
| // FaviconHelper returns color in ABGR format, do a manual conversion here. |
| int red = (mDominantColor & 0xff) << 16; |
| int green = mDominantColor & 0xff00; |
| int blue = (mDominantColor & 0xff0000) >> 16; |
| int alpha = mDominantColor & 0xff000000; |
| return alpha + red + green + blue; |
| } |
| |
| /** |
| * Updates the title of chrome shown in recent tasks. It only takes effect in document mode. |
| */ |
| public static void setTaskDescriptionInDocumentMode(Activity activity, String description) { |
| if (FeatureUtilities.isDocumentMode(activity)) { |
| // Setting icon to be null and color to be 0 will means "take no effect". |
| ApiCompatibilityUtils.setTaskDescription(activity, description, null, 0); |
| } |
| } |
| |
| /** |
| * Closes the EnhancedBookmark Activity on Phone. Does nothing on tablet. |
| */ |
| public static void finishActivityOnPhone(Context context) { |
| if (context instanceof EnhancedBookmarkActivity) { |
| ((Activity) context).finish(); |
| } |
| } |
| } |