mobile/android/base/TabsAccessor.java

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mobile/android/base/TabsAccessor.java	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,195 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +package org.mozilla.gecko;
     1.9 +
    1.10 +import org.mozilla.gecko.db.BrowserContract;
    1.11 +import org.mozilla.gecko.util.ThreadUtils;
    1.12 +import org.mozilla.gecko.util.UiAsyncTask;
    1.13 +
    1.14 +import org.json.JSONArray;
    1.15 +import org.json.JSONException;
    1.16 +
    1.17 +import android.content.ContentResolver;
    1.18 +import android.content.ContentValues;
    1.19 +import android.content.Context;
    1.20 +import android.database.Cursor;
    1.21 +import android.net.Uri;
    1.22 +import android.util.Log;
    1.23 +
    1.24 +import java.util.ArrayList;
    1.25 +import java.util.Collections;
    1.26 +import java.util.List;
    1.27 +
    1.28 +public final class TabsAccessor {
    1.29 +    private static final String LOGTAG = "GeckoTabsAccessor";
    1.30 +
    1.31 +    private static final String[] CLIENTS_AVAILABILITY_PROJECTION = new String[] {
    1.32 +                                                                        BrowserContract.Clients.GUID
    1.33 +                                                                    };
    1.34 +
    1.35 +    private static final String[] TABS_PROJECTION_COLUMNS = new String[] {
    1.36 +                                                                BrowserContract.Tabs.TITLE,
    1.37 +                                                                BrowserContract.Tabs.URL,
    1.38 +                                                                BrowserContract.Clients.GUID,
    1.39 +                                                                BrowserContract.Clients.NAME
    1.40 +                                                            };
    1.41 +
    1.42 +    // Projection column numbers
    1.43 +    public static enum TABS_COLUMN {
    1.44 +        TITLE,
    1.45 +        URL,
    1.46 +        GUID,
    1.47 +        NAME
    1.48 +    };
    1.49 +
    1.50 +    private static final String CLIENTS_SELECTION = BrowserContract.Clients.GUID + " IS NOT NULL";
    1.51 +    private static final String TABS_SELECTION = BrowserContract.Tabs.CLIENT_GUID + " IS NOT NULL";
    1.52 +
    1.53 +    private static final String LOCAL_CLIENT_SELECTION = BrowserContract.Clients.GUID + " IS NULL";
    1.54 +    private static final String LOCAL_TABS_SELECTION = BrowserContract.Tabs.CLIENT_GUID + " IS NULL";
    1.55 +
    1.56 +    public static class RemoteTab {
    1.57 +        public String title;
    1.58 +        public String url;
    1.59 +        public String guid;
    1.60 +        public String name;
    1.61 +    }
    1.62 +
    1.63 +    public interface OnQueryTabsCompleteListener {
    1.64 +        public void onQueryTabsComplete(List<RemoteTab> tabs);
    1.65 +    }
    1.66 +
    1.67 +    // This method returns all tabs from all remote clients, 
    1.68 +    // ordered by most recent client first, most recent tab first 
    1.69 +    public static void getTabs(final Context context, final OnQueryTabsCompleteListener listener) {
    1.70 +        getTabs(context, 0, listener);
    1.71 +    }
    1.72 +
    1.73 +    // This method returns limited number of tabs from all remote clients, 
    1.74 +    // ordered by most recent client first, most recent tab first 
    1.75 +    public static void getTabs(final Context context, final int limit, final OnQueryTabsCompleteListener listener) {
    1.76 +        // If there is no listener, no point in doing work.
    1.77 +        if (listener == null)
    1.78 +            return;
    1.79 +
    1.80 +        (new UiAsyncTask<Void, Void, List<RemoteTab>>(ThreadUtils.getBackgroundHandler()) {
    1.81 +            @Override
    1.82 +            protected List<RemoteTab> doInBackground(Void... unused) {
    1.83 +                Uri uri = BrowserContract.Tabs.CONTENT_URI;
    1.84 +
    1.85 +                if (limit > 0) {
    1.86 +                    uri = uri.buildUpon()
    1.87 +                             .appendQueryParameter(BrowserContract.PARAM_LIMIT, String.valueOf(limit))
    1.88 +                             .build();
    1.89 +                }
    1.90 +                    
    1.91 +                Cursor cursor =  context.getContentResolver().query(uri,
    1.92 +                                                                    TABS_PROJECTION_COLUMNS,
    1.93 +                                                                    TABS_SELECTION,
    1.94 +                                                                    null,
    1.95 +                                                                    null);
    1.96 +                
    1.97 +                if (cursor == null)
    1.98 +                    return null;
    1.99 +                
   1.100 +                RemoteTab tab;
   1.101 +                final ArrayList<RemoteTab> tabs = new ArrayList<RemoteTab> ();
   1.102 +                try {
   1.103 +                    while (cursor.moveToNext()) {
   1.104 +                        tab = new RemoteTab();
   1.105 +                        tab.title = cursor.getString(TABS_COLUMN.TITLE.ordinal());
   1.106 +                        tab.url = cursor.getString(TABS_COLUMN.URL.ordinal());
   1.107 +                        tab.guid = cursor.getString(TABS_COLUMN.GUID.ordinal());
   1.108 +                        tab.name = cursor.getString(TABS_COLUMN.NAME.ordinal());
   1.109 +                
   1.110 +                        tabs.add(tab);
   1.111 +                    }
   1.112 +                } finally {
   1.113 +                    cursor.close();
   1.114 +                }
   1.115 +
   1.116 +                return Collections.unmodifiableList(tabs);
   1.117 +           }
   1.118 +
   1.119 +            @Override
   1.120 +            protected void onPostExecute(List<RemoteTab> tabs) {
   1.121 +                listener.onQueryTabsComplete(tabs);
   1.122 +            }
   1.123 +        }).execute();
   1.124 +    }
   1.125 +
   1.126 +    // Updates the modified time of the local client with the current time.
   1.127 +    private static void updateLocalClient(final ContentResolver cr) {
   1.128 +        ContentValues values = new ContentValues();
   1.129 +        values.put(BrowserContract.Clients.LAST_MODIFIED, System.currentTimeMillis());
   1.130 +        cr.update(BrowserContract.Clients.CONTENT_URI, values, LOCAL_CLIENT_SELECTION, null);
   1.131 +    }
   1.132 +
   1.133 +    // Deletes all local tabs.
   1.134 +    private static void deleteLocalTabs(final ContentResolver cr) {
   1.135 +        cr.delete(BrowserContract.Tabs.CONTENT_URI, LOCAL_TABS_SELECTION, null);
   1.136 +    }
   1.137 +
   1.138 +    /**
   1.139 +     * Tabs are positioned in the DB in the same order that they appear in the tabs param.
   1.140 +     *   - URL should never empty or null. Skip this tab if there's no URL.
   1.141 +     *   - TITLE should always a string, either a page title or empty.
   1.142 +     *   - LAST_USED should always be numeric.
   1.143 +     *   - FAVICON should be a URL or null.
   1.144 +     *   - HISTORY should be serialized JSON array of URLs.
   1.145 +     *   - POSITION should always be numeric.
   1.146 +     *   - CLIENT_GUID should always be null to represent the local client.
   1.147 +     */
   1.148 +    private static void insertLocalTabs(final ContentResolver cr, final Iterable<Tab> tabs) {
   1.149 +        // Reuse this for serializing individual history URLs as JSON.
   1.150 +        JSONArray history = new JSONArray();
   1.151 +        ArrayList<ContentValues> valuesToInsert = new ArrayList<ContentValues>();
   1.152 +
   1.153 +        int position = 0;
   1.154 +        for (Tab tab : tabs) {
   1.155 +            // Skip this tab if it has a null URL or is in private browsing mode
   1.156 +            String url = tab.getURL();
   1.157 +            if (url == null || tab.isPrivate())
   1.158 +                continue;
   1.159 +
   1.160 +            ContentValues values = new ContentValues();
   1.161 +            values.put(BrowserContract.Tabs.URL, url);
   1.162 +            values.put(BrowserContract.Tabs.TITLE, tab.getTitle());
   1.163 +            values.put(BrowserContract.Tabs.LAST_USED, tab.getLastUsed());
   1.164 +
   1.165 +            String favicon = tab.getFaviconURL();
   1.166 +            if (favicon != null)
   1.167 +                values.put(BrowserContract.Tabs.FAVICON, favicon);
   1.168 +            else
   1.169 +                values.putNull(BrowserContract.Tabs.FAVICON);
   1.170 +
   1.171 +            // We don't have access to session history in Java, so for now, we'll
   1.172 +            // just use a JSONArray that holds most recent history item.
   1.173 +            try {
   1.174 +                history.put(0, tab.getURL());
   1.175 +                values.put(BrowserContract.Tabs.HISTORY, history.toString());
   1.176 +            } catch (JSONException e) {
   1.177 +                Log.w(LOGTAG, "JSONException adding URL to tab history array.", e);
   1.178 +            }
   1.179 +
   1.180 +            values.put(BrowserContract.Tabs.POSITION, position++);
   1.181 +
   1.182 +            // A null client guid corresponds to the local client.
   1.183 +            values.putNull(BrowserContract.Tabs.CLIENT_GUID);
   1.184 +
   1.185 +            valuesToInsert.add(values);
   1.186 +        }
   1.187 +
   1.188 +        ContentValues[] valuesToInsertArray = valuesToInsert.toArray(new ContentValues[valuesToInsert.size()]);
   1.189 +        cr.bulkInsert(BrowserContract.Tabs.CONTENT_URI, valuesToInsertArray);
   1.190 +    }
   1.191 +
   1.192 +    // Deletes all local tabs and replaces them with a new list of tabs.
   1.193 +    public static synchronized void persistLocalTabs(final ContentResolver cr, final Iterable<Tab> tabs) {
   1.194 +        deleteLocalTabs(cr);
   1.195 +        insertLocalTabs(cr, tabs);
   1.196 +        updateLocalClient(cr);
   1.197 +    }
   1.198 +}

mercurial