summaryrefslogtreecommitdiffstats
path: root/mobile/android/base/java/org/mozilla/gecko/db/LocalUrlAnnotations.java
blob: 9df41a16910665731fdd850aaad975f877b742c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.gecko.db;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;

import org.json.JSONException;
import org.mozilla.gecko.annotation.RobocopTarget;
import org.mozilla.gecko.db.BrowserContract.UrlAnnotations.Key;
import org.mozilla.gecko.feeds.subscriptions.FeedSubscription;

public class LocalUrlAnnotations implements UrlAnnotations {
    private static final String LOGTAG = "LocalUrlAnnotations";

    private Uri urlAnnotationsTableWithProfile;

    public LocalUrlAnnotations(final String profile) {
        urlAnnotationsTableWithProfile = DBUtils.appendProfile(profile, BrowserContract.UrlAnnotations.CONTENT_URI);
    }

    /**
     * Get all feed subscriptions.
     */
    @Override
    public Cursor getFeedSubscriptions(ContentResolver cr) {
        return queryByKey(cr,
                Key.FEED_SUBSCRIPTION,
                new String[] { BrowserContract.UrlAnnotations.URL, BrowserContract.UrlAnnotations.VALUE },
                null);
    }

    /**
     * Insert mapping from website URL to URL of the feed.
     */
    @Override
    public void insertFeedUrl(ContentResolver cr, String originUrl, String feedUrl) {
        insertAnnotation(cr, originUrl, Key.FEED, feedUrl);
    }

    @Override
    public boolean hasAcceptedOrDeclinedHomeScreenShortcut(ContentResolver cr, String url) {
        return hasResultsForSelection(cr,
                BrowserContract.UrlAnnotations.URL + " = ?",
                new String[]{url});
    }

    @Override
    public void insertHomeScreenShortcut(ContentResolver cr, String url, boolean hasCreatedShortCut) {
        insertAnnotation(cr, url, Key.HOME_SCREEN_SHORTCUT, String.valueOf(hasCreatedShortCut));
    }

    /**
     * Returns true if there's a mapping from the given website URL to a feed URL. False otherwise.
     */
    @Override
    public boolean hasFeedUrlForWebsite(ContentResolver cr, String websiteUrl) {
        return hasResultsForSelection(cr,
                BrowserContract.UrlAnnotations.URL + " = ? AND " + BrowserContract.UrlAnnotations.KEY + " = ?",
                new String[]{websiteUrl, Key.FEED.getDbValue()});
    }

    /**
     * Returns true if there's a website URL with this feed URL. False otherwise.
     */
    @Override
    public boolean hasWebsiteForFeedUrl(ContentResolver cr, String feedUrl) {
        return hasResultsForSelection(cr,
                BrowserContract.UrlAnnotations.VALUE + " = ? AND " + BrowserContract.UrlAnnotations.KEY + " = ?",
                new String[]{feedUrl, Key.FEED.getDbValue()});
    }

    /**
     * Delete the feed URL mapping for this website URL.
     */
    @Override
    public void deleteFeedUrl(ContentResolver cr, String websiteUrl) {
        deleteAnnotation(cr, websiteUrl, Key.FEED);
    }

    /**
     * Get website URLs that are mapped to the given feed URL.
     */
    @Override
    public Cursor getWebsitesWithFeedUrl(ContentResolver cr) {
        return cr.query(urlAnnotationsTableWithProfile,
                new String[] { BrowserContract.UrlAnnotations.URL },
                BrowserContract.UrlAnnotations.KEY + " = ?",
                new String[] { Key.FEED.getDbValue() },
                null);
    }

    /**
     * Returns true if there's a subscription for this feed URL. False otherwise.
     */
    @Override
    public boolean hasFeedSubscription(ContentResolver cr, String feedUrl) {
        return hasResultsForSelection(cr,
                BrowserContract.UrlAnnotations.URL + " = ? AND " + BrowserContract.UrlAnnotations.KEY + " = ?",
                new String[]{feedUrl, Key.FEED_SUBSCRIPTION.getDbValue()});
    }

    /**
     * Insert the given feed subscription (Mapping from feed URL to the subscription object).
     */
    @Override
    public void insertFeedSubscription(ContentResolver cr, FeedSubscription subscription) {
        try {
            insertAnnotation(cr, subscription.getFeedUrl(), Key.FEED_SUBSCRIPTION, subscription.toJSON().toString());
        } catch (JSONException e) {
            Log.w(LOGTAG, "Could not serialize subscription");
        }
    }

    /**
     * Update the feed subscription with new values.
     */
    @Override
    public void updateFeedSubscription(ContentResolver cr, FeedSubscription subscription) {
        try {
            updateAnnotation(cr, subscription.getFeedUrl(), Key.FEED_SUBSCRIPTION, subscription.toJSON().toString());
        } catch (JSONException e) {
            Log.w(LOGTAG, "Could not serialize subscription");
        }
    }

    /**
     * Delete the subscription for the feed URL.
     */
    @Override
    public void deleteFeedSubscription(ContentResolver cr, FeedSubscription subscription) {
        deleteAnnotation(cr, subscription.getFeedUrl(), Key.FEED_SUBSCRIPTION);
    }

    private int deleteAnnotation(final ContentResolver cr, final String url, final Key key) {
        return cr.delete(urlAnnotationsTableWithProfile,
                BrowserContract.UrlAnnotations.KEY + " = ? AND " + BrowserContract.UrlAnnotations.URL + " = ?",
                new String[] { key.getDbValue(), url  });
    }

    private int updateAnnotation(final ContentResolver cr, final String url, final Key key, final String value) {
        ContentValues values = new ContentValues();
        values.put(BrowserContract.UrlAnnotations.VALUE, value);
        values.put(BrowserContract.UrlAnnotations.DATE_MODIFIED, System.currentTimeMillis());

        return cr.update(urlAnnotationsTableWithProfile,
                values,
                BrowserContract.UrlAnnotations.KEY + " = ? AND " + BrowserContract.UrlAnnotations.URL + " = ?",
                new String[]{key.getDbValue(), url});
    }

    private void insertAnnotation(final ContentResolver cr, final String url, final Key key, final String value) {
        insertAnnotation(cr, url, key.getDbValue(), value);
    }

    @RobocopTarget
    @Override
    public void insertAnnotation(final ContentResolver cr, final String url, final String key, final String value) {
        final long creationTime = System.currentTimeMillis();
        final ContentValues values = new ContentValues(5);
        values.put(BrowserContract.UrlAnnotations.URL, url);
        values.put(BrowserContract.UrlAnnotations.KEY, key);
        values.put(BrowserContract.UrlAnnotations.VALUE, value);
        values.put(BrowserContract.UrlAnnotations.DATE_CREATED, creationTime);
        values.put(BrowserContract.UrlAnnotations.DATE_MODIFIED, creationTime);
        cr.insert(urlAnnotationsTableWithProfile, values);
    }

    /**
     * @return true if the table contains rows for the given selection.
     */
    private boolean hasResultsForSelection(ContentResolver cr, String selection, String[] selectionArgs) {
        Cursor cursor = cr.query(urlAnnotationsTableWithProfile,
                new String[] { BrowserContract.UrlAnnotations._ID },
                selection,
                selectionArgs,
                null);
        if (cursor == null) {
            return false;
        }

        try {
            return cursor.getCount() > 0;
        } finally {
            cursor.close();
        }
    }

    private Cursor queryByKey(final ContentResolver cr, @NonNull final Key key, @Nullable final String[] projections,
                @Nullable final String sortOrder) {
        return cr.query(urlAnnotationsTableWithProfile,
                projections,
                BrowserContract.UrlAnnotations.KEY + " = ?", new String[] { key.getDbValue() },
                sortOrder);
    }

    @Override
    public Cursor getScreenshots(ContentResolver cr) {
        return queryByKey(cr,
                Key.SCREENSHOT,
                new String[] {
                        BrowserContract.UrlAnnotations._ID,
                        BrowserContract.UrlAnnotations.URL,
                        BrowserContract.UrlAnnotations.KEY,
                        BrowserContract.UrlAnnotations.VALUE,
                        BrowserContract.UrlAnnotations.DATE_CREATED,
                },
                BrowserContract.UrlAnnotations.DATE_CREATED + " DESC");
    }

    public void insertScreenshot(final ContentResolver cr, final String pageUrl, final String screenshotPath) {
        insertAnnotation(cr, pageUrl, Key.SCREENSHOT.getDbValue(), screenshotPath);
    }

    @Override
    public void insertReaderViewUrl(final ContentResolver cr, final String pageUrl) {
        insertAnnotation(cr, pageUrl, Key.READER_VIEW.getDbValue(), BrowserContract.UrlAnnotations.READER_VIEW_SAVED_VALUE);
    }

    @Override
    public void deleteReaderViewUrl(ContentResolver cr, String pageURL) {
        deleteAnnotation(cr, pageURL, Key.READER_VIEW);
    }

    public int getAnnotationCount(ContentResolver cr, Key key) {
        final String countColumnname = "count";
        final Cursor c = queryByKey(cr,
                key,
                new String[] {
                        "COUNT(*) AS " + countColumnname
                },
                null);

        try {
            if (c != null && c.moveToFirst()) {
                return c.getInt(c.getColumnIndexOrThrow(countColumnname));
            } else {
                return 0;
            }
        } finally {
            if (c != null) {
                c.close();
            }
        }
    }
}