Index: chrome/android/java_staging/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkThumbnailWidgetService.java |
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkThumbnailWidgetService.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkThumbnailWidgetService.java |
deleted file mode 100644 |
index 8ba1827d1e0883215f9c04849187da555fccbe67..0000000000000000000000000000000000000000 |
--- a/chrome/android/java_staging/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkThumbnailWidgetService.java |
+++ /dev/null |
@@ -1,388 +0,0 @@ |
-// 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.bookmarkswidget; |
- |
-import android.appwidget.AppWidgetManager; |
-import android.content.Context; |
-import android.content.Intent; |
-import android.content.SharedPreferences; |
-import android.graphics.Bitmap.Config; |
-import android.graphics.BitmapFactory; |
-import android.graphics.BitmapFactory.Options; |
-import android.net.Uri; |
-import android.os.AsyncTask; |
-import android.os.Binder; |
-import android.provider.Browser.BookmarkColumns; |
-import android.text.TextUtils; |
-import android.util.Log; |
-import android.widget.RemoteViews; |
-import android.widget.RemoteViewsService; |
- |
-import com.google.android.apps.chrome.appwidget.bookmarks.BookmarkThumbnailWidgetProvider; |
- |
-import org.chromium.base.ThreadUtils; |
-import org.chromium.base.annotations.SuppressFBWarnings; |
-import org.chromium.base.library_loader.ProcessInitException; |
-import org.chromium.chrome.R; |
-import org.chromium.chrome.browser.ChromeBrowserProvider.BookmarkNode; |
-import org.chromium.chrome.browser.ChromeBrowserProviderClient; |
-import org.chromium.chrome.browser.ChromiumApplication; |
-import org.chromium.chrome.browser.util.IntentUtils; |
-import org.chromium.sync.AndroidSyncSettings; |
- |
-/** |
- * Service to support bookmarks on the Android home screen |
- */ |
-public class BookmarkThumbnailWidgetService extends RemoteViewsService { |
- |
- static final String TAG = "BookmarkThumbnailWidgetService"; |
- static final String ACTION_CHANGE_FOLDER_SUFFIX = ".CHANGE_FOLDER"; |
- static final String STATE_CURRENT_FOLDER = "current_folder"; |
- |
- @Override |
- public RemoteViewsFactory onGetViewFactory(Intent intent) { |
- int widgetId = IntentUtils.safeGetIntExtra(intent, AppWidgetManager.EXTRA_APPWIDGET_ID, -1); |
- if (widgetId < 0) { |
- Log.w(TAG, "Missing EXTRA_APPWIDGET_ID!"); |
- return null; |
- } |
- return new BookmarkFactory(this, widgetId); |
- } |
- |
- static String getChangeFolderAction(Context context) { |
- return context.getPackageName() + ACTION_CHANGE_FOLDER_SUFFIX; |
- } |
- |
- private static SharedPreferences getWidgetState(Context context, int widgetId) { |
- return context.getSharedPreferences( |
- String.format("widgetState-%d", widgetId), |
- Context.MODE_PRIVATE); |
- } |
- |
- static void deleteWidgetState(Context context, int widgetId) { |
- // Android Browser's widget used private API methods to access the shared prefs |
- // files and deleted them. This is the best we can do with the public API. |
- SharedPreferences preferences = getWidgetState(context, widgetId); |
- if (preferences != null) preferences.edit().clear().commit(); |
- } |
- |
- static void changeFolder(Context context, Intent intent) { |
- int widgetId = IntentUtils.safeGetIntExtra(intent, AppWidgetManager.EXTRA_APPWIDGET_ID, -1); |
- long folderId = IntentUtils.safeGetLongExtra(intent, BookmarkColumns._ID, |
- ChromeBrowserProviderClient.INVALID_BOOKMARK_ID); |
- if (widgetId >= 0 && folderId >= 0) { |
- SharedPreferences prefs = getWidgetState(context, widgetId); |
- prefs.edit().putLong(STATE_CURRENT_FOLDER, folderId).commit(); |
- AppWidgetManager.getInstance(context) |
- .notifyAppWidgetViewDataChanged(widgetId, R.id.bookmarks_list); |
- } |
- } |
- |
- static class BookmarkFactory implements RemoteViewsService.RemoteViewsFactory, |
- BookmarkWidgetUpdateListener.UpdateListener { |
- |
- private final ChromiumApplication mContext; |
- private final int mWidgetId; |
- private final SharedPreferences mPreferences; |
- private BookmarkWidgetUpdateListener mUpdateListener; |
- private BookmarkNode mCurrentFolder; |
- private final Object mLock = new Object(); |
- |
- public BookmarkFactory(Context context, int widgetId) { |
- mContext = (ChromiumApplication) context.getApplicationContext(); |
- mWidgetId = widgetId; |
- mPreferences = getWidgetState(mContext, mWidgetId); |
- } |
- |
- private static long getFolderId(BookmarkNode folder) { |
- return folder != null ? folder.id() : ChromeBrowserProviderClient.INVALID_BOOKMARK_ID; |
- } |
- |
- @SuppressFBWarnings("DM_EXIT") |
- @Override |
- public void onCreate() { |
- // Required to be applied here redundantly to prevent crashes in the cases where the |
- // package data is deleted or the Chrome application forced to stop. |
- try { |
- mContext.startBrowserProcessesAndLoadLibrariesSync(true); |
- } catch (ProcessInitException e) { |
- Log.e(TAG, "Failed to start browser process.", e); |
- // Since the library failed to initialize nothing in the application |
- // can work, so kill the whole application not just the activity |
- System.exit(-1); |
- } |
- mUpdateListener = new BookmarkWidgetUpdateListener(mContext, this); |
- } |
- |
- @Override |
- public void onDestroy() { |
- if (mUpdateListener != null) mUpdateListener.destroy(); |
- deleteWidgetState(mContext, mWidgetId); |
- } |
- |
- @Override |
- public void onBookmarkModelUpdated() { |
- refreshWidget(); |
- } |
- |
- @Override |
- public void onSyncEnabledStatusUpdated(boolean enabled) { |
- synchronized (mLock) { |
- // Need to operate in a separate thread as it involves queries to our provider. |
- new SyncEnabledStatusUpdatedTask(enabled, getFolderId(mCurrentFolder)).execute(); |
- } |
- } |
- |
- @Override |
- public void onThumbnailUpdated(String url) { |
- synchronized (mLock) { |
- if (mCurrentFolder == null) return; |
- |
- for (BookmarkNode child : mCurrentFolder.children()) { |
- if (child.isUrl() && url.equals(child.url())) { |
- refreshWidget(); |
- break; |
- } |
- } |
- } |
- } |
- |
- void refreshWidget() { |
- mContext.sendBroadcast(new Intent( |
- BookmarkThumbnailWidgetProviderBase.getBookmarkAppWidgetUpdateAction(mContext), |
- null, mContext, BookmarkThumbnailWidgetProvider.class) |
- .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mWidgetId)); |
- } |
- |
- void requestFolderChange(long folderId) { |
- mContext.sendBroadcast(new Intent(getChangeFolderAction(mContext)) |
- .setClass(mContext, BookmarkWidgetProxy.class) |
- .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mWidgetId) |
- .putExtra(BookmarkColumns._ID, folderId)); |
- } |
- |
- // Performs the required checks to trigger an update of the widget after changing the sync |
- // enable settings. The required provider methods cannot be accessed in the UI thread. |
- private class SyncEnabledStatusUpdatedTask extends AsyncTask<Void, Void, Void> { |
- private final boolean mEnabled; |
- private final long mCurrentFolderId; |
- |
- public SyncEnabledStatusUpdatedTask(boolean enabled, long currentFolderId) { |
- mEnabled = enabled; |
- mCurrentFolderId = currentFolderId; |
- } |
- |
- @Override |
- protected Void doInBackground(Void... params) { |
- // If we're in the Mobile Bookmarks folder the icon to go up the hierarchy |
- // will either appear or disappear. Need to refresh. |
- long mobileBookmarksFolderId = |
- ChromeBrowserProviderClient.getMobileBookmarksFolderId(mContext); |
- if (mCurrentFolderId == mobileBookmarksFolderId) { |
- refreshWidget(); |
- return null; |
- } |
- |
- // If disabling sync, we need to move to the Mobile Bookmarks folder if we're |
- // not inside that branch of the bookmark hierarchy (will become not accessible). |
- if (!mEnabled && !ChromeBrowserProviderClient.isBookmarkInMobileBookmarksBranch( |
- mContext, mCurrentFolderId)) { |
- requestFolderChange(mobileBookmarksFolderId); |
- } |
- |
- return null; |
- } |
- } |
- |
- // ---------------------------------------------------------------- // |
- // ------- Methods below this line run in different thread -------- // |
- // ---------------------------------------------------------------- // |
- |
- private void syncState() { |
- long currentFolderId = mPreferences.getLong(STATE_CURRENT_FOLDER, |
- ChromeBrowserProviderClient.INVALID_BOOKMARK_ID); |
- |
- // Keep outside the synchronized block to avoid deadlocks in case loading the folder |
- // triggers an update that locks when trying to read mCurrentFolder. |
- BookmarkNode newFolder = loadBookmarkFolder(currentFolderId); |
- |
- synchronized (mLock) { |
- mCurrentFolder = |
- getFolderId(newFolder) != ChromeBrowserProviderClient.INVALID_BOOKMARK_ID |
- ? newFolder : null; |
- } |
- |
- mPreferences.edit() |
- .putLong(STATE_CURRENT_FOLDER, getFolderId(mCurrentFolder)) |
- .apply(); |
- } |
- |
- private BookmarkNode loadBookmarkFolder(long folderId) { |
- if (ThreadUtils.runningOnUiThread()) { |
- Log.e(TAG, "Trying to load bookmark folder from the UI thread."); |
- return null; |
- } |
- |
- // If the current folder id doesn't exist (it was deleted) try the current parent. |
- // If this fails too then fallback to Mobile Bookmarks. |
- if (!ChromeBrowserProviderClient.bookmarkNodeExists(mContext, folderId)) { |
- folderId = mCurrentFolder != null ? getFolderId(mCurrentFolder.parent()) |
- : ChromeBrowserProviderClient.INVALID_BOOKMARK_ID; |
- if (!ChromeBrowserProviderClient.bookmarkNodeExists(mContext, folderId)) { |
- folderId = ChromeBrowserProviderClient.INVALID_BOOKMARK_ID; |
- } |
- } |
- |
- // Need to verify this always because the package data might be cleared while the |
- // widget is in the Mobile Bookmarks folder with sync enabled. In that case the |
- // hierarchy up folder would still work (we can't update the widget) but the parent |
- // folders should not be accessible because sync has been reset when clearing data. |
- if (folderId != ChromeBrowserProviderClient.INVALID_BOOKMARK_ID |
- && !AndroidSyncSettings.isSyncEnabled(mContext) |
- && !ChromeBrowserProviderClient.isBookmarkInMobileBookmarksBranch( |
- mContext, folderId)) { |
- folderId = ChromeBrowserProviderClient.INVALID_BOOKMARK_ID; |
- } |
- |
- // Use the Mobile Bookmarks folder by default. |
- if (folderId < 0 || folderId == ChromeBrowserProviderClient.INVALID_BOOKMARK_ID) { |
- folderId = ChromeBrowserProviderClient.getMobileBookmarksFolderId(mContext); |
- if (folderId == ChromeBrowserProviderClient.INVALID_BOOKMARK_ID) return null; |
- } |
- |
- return ChromeBrowserProviderClient.getBookmarkNode(mContext, folderId, |
- ChromeBrowserProviderClient.GET_PARENT |
- | ChromeBrowserProviderClient.GET_CHILDREN |
- | ChromeBrowserProviderClient.GET_FAVICONS |
- | ChromeBrowserProviderClient.GET_THUMBNAILS); |
- } |
- |
- private BookmarkNode getBookmarkForPosition(int position) { |
- if (mCurrentFolder == null) return null; |
- |
- // The position 0 is saved for an entry of the current folder used to go up. |
- // This is not the case when the current node has no parent (it's the root node). |
- return (mCurrentFolder.parent() == null) |
- ? mCurrentFolder.children().get(position) |
- : (position == 0 |
- ? mCurrentFolder : mCurrentFolder.children().get(position - 1)); |
- } |
- |
- @Override |
- public void onDataSetChanged() { |
- long token = Binder.clearCallingIdentity(); |
- syncState(); |
- Binder.restoreCallingIdentity(token); |
- } |
- |
- @Override |
- public int getViewTypeCount() { |
- return 2; |
- } |
- |
- @Override |
- public boolean hasStableIds() { |
- return false; |
- } |
- |
- @Override |
- public int getCount() { |
- if (mCurrentFolder == null) return 0; |
- return mCurrentFolder.children().size() + (mCurrentFolder.parent() != null ? 1 : 0); |
- } |
- |
- @Override |
- public long getItemId(int position) { |
- return getFolderId(getBookmarkForPosition(position)); |
- } |
- |
- @Override |
- public RemoteViews getLoadingView() { |
- return new RemoteViews(mContext.getPackageName(), |
- R.layout.bookmark_thumbnail_widget_item); |
- } |
- |
- @Override |
- public RemoteViews getViewAt(int position) { |
- if (mCurrentFolder == null) { |
- Log.w(TAG, "No current folder data available."); |
- return null; |
- } |
- |
- BookmarkNode bookmark = getBookmarkForPosition(position); |
- if (bookmark == null) { |
- Log.w(TAG, "Couldn't get bookmark for position " + position); |
- return null; |
- } |
- |
- if (bookmark == mCurrentFolder && bookmark.parent() == null) { |
- Log.w(TAG, "Invalid bookmark data: loop detected."); |
- return null; |
- } |
- |
- String title = bookmark.name(); |
- String url = bookmark.url(); |
- long id = (bookmark == mCurrentFolder) ? bookmark.parent().id() : bookmark.id(); |
- |
- // Two layouts are needed because RemoteView does not supporting changing the scale type |
- // of an ImageView: boomarks crop their thumbnails, while folders stretch their icon. |
- RemoteViews views = !bookmark.isUrl() |
- ? new RemoteViews(mContext.getPackageName(), |
- R.layout.bookmark_thumbnail_widget_item_folder) |
- : new RemoteViews(mContext.getPackageName(), |
- R.layout.bookmark_thumbnail_widget_item); |
- |
- // Set the title of the bookmark. Use the url as a backup. |
- views.setTextViewText(R.id.label, TextUtils.isEmpty(title) ? url : title); |
- |
- if (!bookmark.isUrl()) { |
- int thumbId = (bookmark == mCurrentFolder) |
- ? R.drawable.thumb_bookmark_widget_folder_back_holo |
- : R.drawable.thumb_bookmark_widget_folder_holo; |
- views.setImageViewResource(R.id.thumb, thumbId); |
- views.setImageViewResource(R.id.favicon, |
- R.drawable.ic_bookmark_widget_bookmark_holo_dark); |
- } else { |
- // RemoteViews require a valid bitmap config. |
- Options options = new Options(); |
- options.inPreferredConfig = Config.ARGB_8888; |
- |
- byte[] favicon = bookmark.favicon(); |
- if (favicon != null && favicon.length > 0) { |
- views.setImageViewBitmap(R.id.favicon, |
- BitmapFactory.decodeByteArray(favicon, 0, favicon.length, options)); |
- } else { |
- views.setImageViewResource(R.id.favicon, |
- org.chromium.chrome.R.drawable.globe_favicon); |
- } |
- |
- byte[] thumbnail = bookmark.thumbnail(); |
- if (thumbnail != null && thumbnail.length > 0) { |
- views.setImageViewBitmap(R.id.thumb, |
- BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length, options)); |
- } else { |
- views.setImageViewResource(R.id.thumb, R.drawable.browser_thumbnail); |
- } |
- } |
- |
- Intent fillIn; |
- if (!bookmark.isUrl()) { |
- fillIn = new Intent(getChangeFolderAction(mContext)) |
- .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mWidgetId) |
- .putExtra(BookmarkColumns._ID, id); |
- } else { |
- fillIn = new Intent(Intent.ACTION_VIEW); |
- if (!TextUtils.isEmpty(url)) { |
- fillIn = fillIn.addCategory(Intent.CATEGORY_BROWSABLE) |
- .setData(Uri.parse(url)); |
- } else { |
- fillIn = fillIn.addCategory(Intent.CATEGORY_LAUNCHER); |
- } |
- } |
- views.setOnClickFillInIntent(R.id.list_item, fillIn); |
- return views; |
- } |
- } |
-} |