BookmarkThumbnailWidgetService.java revision 99c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2
1df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck/*
2df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck * Copyright (C) 2010 The Android Open Source Project
3df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck *
4df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck * Licensed under the Apache License, Version 2.0 (the "License");
5df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck * you may not use this file except in compliance with the License.
6df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck * You may obtain a copy of the License at
7df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck *
8df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck *      http://www.apache.org/licenses/LICENSE-2.0
9df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck *
10df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck * Unless required by applicable law or agreed to in writing, software
11df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck * distributed under the License is distributed on an "AS IS" BASIS,
12df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck * See the License for the specific language governing permissions and
14df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck * limitations under the License.
15df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck */
16df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
17df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckpackage com.android.browser.widget;
18df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
1999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reckimport com.android.browser.BookmarkUtils;
209291bd1c9b5d1398e14d80ddea91cebf574cc63eJohn Reckimport com.android.browser.BrowserActivity;
21df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport com.android.browser.R;
22df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
23df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.appwidget.AppWidgetManager;
246eb77c79f9a88ea0e76eb07ea0412bbd2446a110John Reckimport android.content.ContentUris;
25df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.content.Context;
26df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.content.Intent;
276eb77c79f9a88ea0e76eb07ea0412bbd2446a110John Reckimport android.content.SharedPreferences;
28df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.database.Cursor;
2999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reckimport android.database.MergeCursor;
30df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.graphics.Bitmap;
31df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.graphics.Bitmap.Config;
32df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.graphics.BitmapFactory;
33df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.graphics.BitmapFactory.Options;
34df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.net.Uri;
3599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reckimport android.os.Binder;
36df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.provider.BrowserContract;
37df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.provider.BrowserContract.Bookmarks;
38df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.text.TextUtils;
39df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.util.Log;
40df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.widget.RemoteViews;
41df63d2ff2e9881706eef78796d05078d0ffb7f60John Reckimport android.widget.RemoteViewsService;
42df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
4399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reckimport java.io.File;
4499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reckimport java.io.FilenameFilter;
4599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reckimport java.util.Arrays;
4699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reckimport java.util.HashSet;
4799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reckimport java.util.regex.Matcher;
4899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reckimport java.util.regex.Pattern;
49df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
501537b774a62042a8b86100e1e6b0988bed70c385John Reckpublic class BookmarkThumbnailWidgetService extends RemoteViewsService {
51df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
521537b774a62042a8b86100e1e6b0988bed70c385John Reck    static final String TAG = "BookmarkThumbnailWidgetService";
53df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck    static final String ACTION_CHANGE_FOLDER
54df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            = "com.android.browser.widget.CHANGE_FOLDER";
55df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
5699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    static final String STATE_CURRENT_FOLDER = "current_folder";
5799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    static final String STATE_ROOT_FOLDER = "root_folder";
5899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck
59df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck    private static final String[] PROJECTION = new String[] {
60df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            BrowserContract.Bookmarks._ID,
61df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            BrowserContract.Bookmarks.TITLE,
62df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            BrowserContract.Bookmarks.URL,
63df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            BrowserContract.Bookmarks.FAVICON,
64df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            BrowserContract.Bookmarks.IS_FOLDER,
651537b774a62042a8b86100e1e6b0988bed70c385John Reck            BrowserContract.Bookmarks.POSITION, /* needed for order by */
6699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            BrowserContract.Bookmarks.THUMBNAIL,
6799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            BrowserContract.Bookmarks.PARENT};
68023a60e37c0f3374aed57531ab11b7f20ece232bJohn Reck    private static final int BOOKMARK_INDEX_ID = 0;
69023a60e37c0f3374aed57531ab11b7f20ece232bJohn Reck    private static final int BOOKMARK_INDEX_TITLE = 1;
70023a60e37c0f3374aed57531ab11b7f20ece232bJohn Reck    private static final int BOOKMARK_INDEX_URL = 2;
71023a60e37c0f3374aed57531ab11b7f20ece232bJohn Reck    private static final int BOOKMARK_INDEX_FAVICON = 3;
72023a60e37c0f3374aed57531ab11b7f20ece232bJohn Reck    private static final int BOOKMARK_INDEX_IS_FOLDER = 4;
7399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    private static final int BOOKMARK_INDEX_THUMBNAIL = 6;
7499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    private static final int BOOKMARK_INDEX_PARENT_ID = 7;
75df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
76df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck    @Override
7799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    public RemoteViewsFactory onGetViewFactory(Intent intent) {
7899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
7999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        if (widgetId < 0) {
8099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            Log.w(TAG, "Missing EXTRA_APPWIDGET_ID!");
8199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            return null;
8299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        }
8399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        return new BookmarkFactory(getApplicationContext(), widgetId);
84df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck    }
85df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
8699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    static SharedPreferences getWidgetState(Context context, int widgetId) {
8799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        return context.getSharedPreferences(
8899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                String.format("widgetState-%d", widgetId),
8999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                Context.MODE_PRIVATE);
90df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck    }
91df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
9299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    static void deleteWidgetState(Context context, int widgetId) {
9399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        File file = context.getSharedPrefsFile(
9499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                String.format("widgetState-%d", widgetId));
9599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        if (file.exists()) {
9699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            if (!file.delete()) {
9799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                file.deleteOnExit();
9899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            }
9999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        }
100df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck    }
101df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
10299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    static void changeFolder(Context context, Intent intent) {
10399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        int wid = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
10499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        long fid = intent.getLongExtra(Bookmarks._ID, -1);
10599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        if (wid >= 0 && fid >= 0) {
10699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            SharedPreferences prefs = getWidgetState(context, wid);
10799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            prefs.edit().putLong(STATE_CURRENT_FOLDER, fid).commit();
10899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            AppWidgetManager.getInstance(context)
10999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    .notifyAppWidgetViewDataChanged(wid, R.id.bookmarks_list);
110df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
11199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    }
112df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
11399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    static void clearWidgetState(Context context, int widgetId) {
11499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        SharedPreferences pref = getWidgetState(context, widgetId);
11599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        pref.edit()
11699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            .remove(STATE_CURRENT_FOLDER)
11799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            .remove(STATE_ROOT_FOLDER)
11899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            .commit();
11999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    }
120df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
12199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    /**
12299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck     *  Checks for any state files that may have not received onDeleted
12399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck     */
12499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    static void removeOrphanedStates(Context context, int[] widgetIds) {
12599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        File prefsDirectory = context.getSharedPrefsFile("null").getParentFile();
12699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        File[] widgetStates = prefsDirectory.listFiles(new StateFilter(widgetIds));
12799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        for (File f : widgetStates) {
12899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            Log.w(TAG, "Found orphaned state: " + f.getName());
12999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            if (!f.delete()) {
13099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                f.deleteOnExit();
1317c11d28643f571669b59ef0d700392c45256ce97John Reck            }
132df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
133df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck    }
134df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
13599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    static class StateFilter implements FilenameFilter {
13699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck
13799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        static final Pattern sStatePattern = Pattern.compile("widgetState-(\\d+)\\.xml");
13899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        HashSet<Integer> mWidgetIds;
13999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck
14099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        StateFilter(int[] ids) {
14199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            mWidgetIds = new HashSet<Integer>();
14299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            for (int id : ids) {
14399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                mWidgetIds.add(id);
1447c11d28643f571669b59ef0d700392c45256ce97John Reck            }
145df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
146df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
14799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        @Override
14899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        public boolean accept(File dir, String filename) {
14999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            Matcher m = sStatePattern.matcher(filename);
15099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            if (m.matches()) {
15199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                int id = Integer.parseInt(m.group(1));
15299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                if (!mWidgetIds.contains(id)) {
15399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    return true;
15499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                }
15599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            }
15699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            return false;
1576eb77c79f9a88ea0e76eb07ea0412bbd2446a110John Reck        }
15899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck
1596eb77c79f9a88ea0e76eb07ea0412bbd2446a110John Reck    }
1606eb77c79f9a88ea0e76eb07ea0412bbd2446a110John Reck
16199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck    static class BookmarkFactory implements RemoteViewsService.RemoteViewsFactory {
16299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        private Cursor mBookmarks;
163df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        private Context mContext;
164df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        private int mWidgetId;
16599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        private long mCurrentFolder = -1;
16699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        private long mRootFolder = -1;
16799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        private SharedPreferences mPreferences = null;
168df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
169df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        public BookmarkFactory(Context context, int widgetId) {
170df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            mContext = context;
171df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            mWidgetId = widgetId;
172df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
173df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
17499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        void syncState() {
17599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            if (mPreferences == null) {
17699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                mPreferences = getWidgetState(mContext, mWidgetId);
17799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            }
17899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            long currentFolder = mPreferences.getLong(STATE_CURRENT_FOLDER, -1);
17999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            mRootFolder = mPreferences.getLong(STATE_ROOT_FOLDER, -1);
18099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            if (currentFolder != mCurrentFolder) {
18199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                resetBookmarks();
18299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                mCurrentFolder = currentFolder;
1836eb77c79f9a88ea0e76eb07ea0412bbd2446a110John Reck            }
18499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        }
1856eb77c79f9a88ea0e76eb07ea0412bbd2446a110John Reck
18699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        void saveState() {
18799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            if (mPreferences == null) {
18899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                mPreferences = getWidgetState(mContext, mWidgetId);
1896eb77c79f9a88ea0e76eb07ea0412bbd2446a110John Reck            }
19099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            mPreferences.edit()
19199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                .putLong(STATE_CURRENT_FOLDER, mCurrentFolder)
19299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                .putLong(STATE_ROOT_FOLDER, mRootFolder)
19399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                .commit();
194df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
195df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
196df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        @Override
197df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        public int getCount() {
198df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            if (mBookmarks == null)
199df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck                return 0;
20099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            return mBookmarks.getCount();
201df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
202df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
203df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        @Override
204df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        public long getItemId(int position) {
205df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            return position;
206df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
207df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
208df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        @Override
209df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        public RemoteViews getLoadingView() {
21099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            return new RemoteViews(
21199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    mContext.getPackageName(), R.layout.bookmarkthumbnailwidget_item);
212df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
213df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
214df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        @Override
215df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        public RemoteViews getViewAt(int position) {
21699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            if (!mBookmarks.moveToPosition(position)) {
217df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck                return null;
218df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            }
219df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
22099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            long id = mBookmarks.getLong(BOOKMARK_INDEX_ID);
22199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            String title = mBookmarks.getString(BOOKMARK_INDEX_TITLE);
22299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            String url = mBookmarks.getString(BOOKMARK_INDEX_URL);
22399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            boolean isFolder = mBookmarks.getInt(BOOKMARK_INDEX_IS_FOLDER) != 0;
224df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
225df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            RemoteViews views = new RemoteViews(
2261537b774a62042a8b86100e1e6b0988bed70c385John Reck                    mContext.getPackageName(), R.layout.bookmarkthumbnailwidget_item);
227df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            // Set the title of the bookmark. Use the url as a backup.
22899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            String displayTitle = title;
229df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            if (TextUtils.isEmpty(displayTitle)) {
230df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck                // The browser always requires a title for bookmarks, but jic...
23199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                displayTitle = url;
232df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            }
233df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            views.setTextViewText(R.id.label, displayTitle);
23499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            if (isFolder) {
23599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                if (id == mCurrentFolder) {
23699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    id = mBookmarks.getLong(BOOKMARK_INDEX_PARENT_ID);
2371537b774a62042a8b86100e1e6b0988bed70c385John Reck                    views.setImageViewResource(R.id.thumb, R.drawable.thumb_bookmark_widget_folder_back_holo);
238df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck                } else {
2391537b774a62042a8b86100e1e6b0988bed70c385John Reck                    views.setImageViewResource(R.id.thumb, R.drawable.thumb_bookmark_widget_folder_holo);
240df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck                }
2411537b774a62042a8b86100e1e6b0988bed70c385John Reck                views.setImageViewResource(R.id.favicon, R.drawable.ic_bookmark_widget_bookmark_holo_dark);
242f95eb7410686ee1ea4b7f583f03b642c30551b79John Reck                views.setDrawableParameters(R.id.thumb, true, 0, -1, null, -1);
243df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            } else {
24499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                // RemoteViews require a valid bitmap config
24599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                Options options = new Options();
24699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                options.inPreferredConfig = Config.ARGB_8888;
24799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                Bitmap thumbnail = null, favicon = null;
24899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                byte[] blob = mBookmarks.getBlob(BOOKMARK_INDEX_THUMBNAIL);
249f95eb7410686ee1ea4b7f583f03b642c30551b79John Reck                views.setDrawableParameters(R.id.thumb, true, 255, -1, null, -1);
25099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                if (blob != null && blob.length > 0) {
25199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    thumbnail = BitmapFactory.decodeByteArray(
25299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                            blob, 0, blob.length, options);
25399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    views.setImageViewBitmap(R.id.thumb, thumbnail);
254df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck                } else {
255df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck                    views.setImageViewResource(R.id.thumb,
256df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck                            R.drawable.browser_thumbnail);
257df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck                }
25899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                blob = mBookmarks.getBlob(BOOKMARK_INDEX_FAVICON);
25999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                if (blob != null && blob.length > 0) {
26099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    favicon = BitmapFactory.decodeByteArray(
26199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                            blob, 0, blob.length, options);
26299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    views.setImageViewBitmap(R.id.favicon, favicon);
2631537b774a62042a8b86100e1e6b0988bed70c385John Reck                } else {
2641537b774a62042a8b86100e1e6b0988bed70c385John Reck                    views.setImageViewResource(R.id.favicon,
2651537b774a62042a8b86100e1e6b0988bed70c385John Reck                            R.drawable.app_web_browser_sm);
2661537b774a62042a8b86100e1e6b0988bed70c385John Reck                }
267df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            }
26899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            Intent fillin;
26999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            if (isFolder) {
27099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                fillin = new Intent(ACTION_CHANGE_FOLDER)
27199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                        .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mWidgetId)
27299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                        .putExtra(Bookmarks._ID, id);
27399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            } else {
27499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                if (!TextUtils.isEmpty(url)) {
27599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    fillin = new Intent(Intent.ACTION_VIEW)
27699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                            .addCategory(Intent.CATEGORY_BROWSABLE)
27799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                            .setData(Uri.parse(url));
27899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                } else {
27999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    fillin = new Intent(BrowserActivity.ACTION_SHOW_BROWSER);
28099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                }
28199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            }
28299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            views.setOnClickFillInIntent(R.id.list_item, fillin);
283df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            return views;
284df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
285df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
286df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        @Override
287df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        public int getViewTypeCount() {
288df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            return 1;
289df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
290df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
291df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        @Override
292df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        public boolean hasStableIds() {
293df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            return false;
294df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
295df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
296df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        @Override
297df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        public void onCreate() {
298df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
299df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
300df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        @Override
301df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        public void onDestroy() {
30299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            if (mBookmarks != null) {
30399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                mBookmarks.close();
30499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                mBookmarks = null;
30573eb105e5bb53356e171837bd6b13b29661806e3John Reck            }
30699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            deleteWidgetState(mContext, mWidgetId);
307df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
308df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
309df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        @Override
310df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        public void onDataSetChanged() {
31199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            long token = Binder.clearCallingIdentity();
31299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            syncState();
31399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            if (mRootFolder < 0 || mCurrentFolder < 0) {
31499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                // Our state has been zero'd, reset (account change most likely)
31599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                mRootFolder = getRootFolder();
31699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                mCurrentFolder = mRootFolder;
31799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                saveState();
3187c11d28643f571669b59ef0d700392c45256ce97John Reck            }
31999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            loadBookmarks();
32099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            Binder.restoreCallingIdentity(token);
321df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
322df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
32399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        private void resetBookmarks() {
32499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            if (mBookmarks != null) {
32599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                mBookmarks.close();
32699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                mBookmarks = null;
3277c11d28643f571669b59ef0d700392c45256ce97John Reck            }
3287c11d28643f571669b59ef0d700392c45256ce97John Reck        }
3297c11d28643f571669b59ef0d700392c45256ce97John Reck
33099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        long getRootFolder() {
33199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            Uri uri = Uri.withAppendedPath(
33299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER, "id");
33399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            uri = BookmarkUtils.addAccountInfo(mContext, uri.buildUpon()).build();
33499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            Cursor c = mContext.getContentResolver().query(
33599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    uri, null, null, null, null);
336df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            try {
33799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                c.moveToFirst();
33899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                return c.getLong(0);
339df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            } finally {
34099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                c.close();
341df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck            }
342df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck        }
343df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
34499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck        void loadBookmarks() {
34599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            resetBookmarks();
34699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck
34799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            Uri uri = ContentUris.withAppendedId(
34899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER,
34999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    mCurrentFolder);
35099c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            uri = BookmarkUtils.addAccountInfo(mContext, uri.buildUpon()).build();
35199c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            mBookmarks = mContext.getContentResolver().query(uri, PROJECTION,
35299c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                    null, null, null);
35399c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck            if (mCurrentFolder != mRootFolder) {
35499c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                uri = ContentUris.withAppendedId(
35599c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                        BrowserContract.Bookmarks.CONTENT_URI,
35699c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                        mCurrentFolder);
35799c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                Cursor c = mContext.getContentResolver().query(uri, PROJECTION,
35899c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                        null, null, null);
35999c6d333106cc0fb3c028b5f8d1ff1f5cf726ca2John Reck                mBookmarks = new MergeCursor(new Cursor[] { c, mBookmarks });
3607c11d28643f571669b59ef0d700392c45256ce97John Reck            }
3617c11d28643f571669b59ef0d700392c45256ce97John Reck        }
362df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck    }
363df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck
364df63d2ff2e9881706eef78796d05078d0ffb7f60John Reck}
365