@BinderThread private BookmarkFolder loadBookmarks(final BookmarkId folderId) { final LinkedBlockingQueue<BookmarkFolder> resultQueue = new LinkedBlockingQueue<>(1); //A reference of BookmarkLoader is needed in binder thread to //prevent it from being garbage collected. final BookmarkLoader bookmarkLoader = new BookmarkLoader(); ThreadUtils.runOnUiThread(new Runnable() { @Override public void run() { bookmarkLoader.initialize(mContext, folderId, new BookmarkLoaderCallback() { @Override public void onBookmarksLoaded(BookmarkFolder folder) { resultQueue.add(folder); } }); } }); try { return resultQueue.take(); } catch (InterruptedException e) { return null; } }
@BinderThread @Override public int getCount() { //On some Sony devices, getCount() could be called before onDatasetChanged() //returns. If it happens, refresh widget until the bookmarks are all loaded. if (mCurrentFolder == null || !mPreferences.getString(PREF_CURRENT_FOLDER, "") .equals(mCurrentFolder.folder.id.toString())) { ThreadUtils.runOnUiThread(new Runnable() { @Override public void run() { refreshWidget(); } }); } if (mCurrentFolder == null) { return 0; } return mCurrentFolder.children.size() + (mCurrentFolder.parent != null ? 1 : 0); }
@BinderThread private Bookmark 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). if (mCurrentFolder.parent != null) { if (position == 0) return mCurrentFolder.folder; position--; } // This is necessary because when Chrome is cleared from Application settings, Bookmark // widget will not be notified and it causes inconsistency between model and widget. // Then if the widget is quickly scrolled down, this has an IndexOutOfBound error. if (mCurrentFolder.children.size() <= position) return null; return mCurrentFolder.children.get(position); }
@BinderThread @Override public void onDestroy() { ThreadUtils.runOnUiThread(new Runnable() { @Override public void run() { if (mBookmarkModel != null) mBookmarkModel.destroy(); } }); deleteWidgetState(mContext, mWidgetId); }
@BinderThread private void updateBookmarkList() { BookmarkId folderId = BookmarkId .getBookmarkIdFromString(mPreferences.getString(PREF_CURRENT_FOLDER, null)); mCurrentFolder = loadBookmarks(folderId); mPreferences.edit().putString(PREF_CURRENT_FOLDER, mCurrentFolder.folder.id.toString()) .apply(); }
@BinderThread private Bookmark 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). if (mCurrentFolder.parent != null) { if (position == 0) return mCurrentFolder.folder; position--; } return mCurrentFolder.children.get(position); }
@BinderThread @Override public long getItemId(int position) { Bookmark bookmark = getBookmarkForPosition(position); if (bookmark == null) return BookmarkId.INVALID_FOLDER_ID; return bookmark.id.getId(); }
@Override @BinderThread public void onButtonEvent(ButtonEvent event) throws RemoteException { synchronized (lockListener) { if (eventListenerThreadHandler != null) { eventListenerThreadHandler.post(new ButtonEventRunnable(event)); } } }
@Override @BinderThread public void onJoystickEvent(JoystickEvent event) throws RemoteException { // TODO link the chain of events in order to receive ButtonEvents for Dpad only if the // corresponding joystick has been ignored synchronized (lockListener) { if (eventListenerThreadHandler != null) { eventListenerThreadHandler.post(new JoystickEventRunnable(event)); } } }
@BinderThread @Override public void onDataSetChanged() { updateBookmarkList(); }
@BinderThread @Override public int getViewTypeCount() { return 2; }
@BinderThread @Override public boolean hasStableIds() { return false; }
@BinderThread @Override public long getItemId(int position) { return getBookmarkForPosition(position).id.getId(); }
@BinderThread @Override public RemoteViews getLoadingView() { return new RemoteViews(mContext.getPackageName(), R.layout.bookmark_widget_item); }
@BinderThread @Override public RemoteViews getViewAt(int position) { if (mCurrentFolder == null) { Log.w(TAG, "No current folder data available."); return null; } Bookmark bookmark = getBookmarkForPosition(position); if (bookmark == null) { Log.w(TAG, "Couldn't get bookmark for position %d", position); return null; } String title = bookmark.title; String url = bookmark.url; BookmarkId id = (bookmark == mCurrentFolder.folder) ? mCurrentFolder.parent.id : bookmark.id; RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.bookmark_widget_item); // Set the title of the bookmark. Use the url as a backup. views.setTextViewText(R.id.title, TextUtils.isEmpty(title) ? url : title); if (bookmark == mCurrentFolder.folder) { views.setImageViewResource(R.id.favicon, R.drawable.back_normal); } else if (bookmark.isFolder) { views.setImageViewResource(R.id.favicon, R.drawable.bookmark_folder); } else { views.setImageViewBitmap(R.id.favicon, bookmark.favicon); } Intent fillIn; if (bookmark.isFolder) { fillIn = new Intent(getChangeFolderAction(mContext)) .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mWidgetId) .putExtra(EXTRA_FOLDER_ID, id.toString()); } 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; }