Java tutorial
/* * Copyright (c) 2016 Jonas Kalderstam. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.nononsenseapps.feeder.db; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.net.Uri; import android.util.Log; import org.joda.time.DateTime; import org.json.JSONException; import org.json.JSONObject; /** * SQL which handles items belonging to a Feed */ public class FeedItemSQL { // SQL convention says Table name should be "singular" public static final String TABLE_NAME = "FeedItem"; // URIs public static final Uri URI_FEED_ITEMS = Uri .withAppendedPath(Uri.parse(RssContentProvider.SCHEME + RssContentProvider.AUTHORITY), TABLE_NAME); // URI codes, must be unique public static final int URICODE = 201; public static final int ITEMCODE = 202; private static final String TAG = "FeedItemSQL"; public static void addMatcherUris(UriMatcher sURIMatcher) { sURIMatcher.addURI(RssContentProvider.AUTHORITY, URI_FEED_ITEMS.getPath(), URICODE); sURIMatcher.addURI(RssContentProvider.AUTHORITY, URI_FEED_ITEMS.getPath() + "/#", ITEMCODE); } // Naming the id column with an underscore is good to be consistent // with other Android things. This is ALWAYS needed public static final String COL_ID = "_id"; // These fields can be anything you want. public static final String COL_GUID = "guid"; public static final String COL_TITLE = "title"; public static final String COL_DESCRIPTION = "description"; public static final String COL_PLAINTITLE = "plaintitle"; public static final String COL_PLAINSNIPPET = "plainsnippet"; public static final String COL_IMAGEURL = "imageurl"; public static final String COL_ENCLOSURELINK = "enclosurelink"; public static final String COL_LINK = "link"; public static final String COL_AUTHOR = "author"; public static final String COL_PUBDATE = "pubdate"; public static final String COL_UNREAD = "unread"; public static final String COL_NOTIFIED = "notified"; public static final String COL_JSON = "json"; // These fields corresponds to columns in Feed table public static final String COL_FEED = "feed"; public static final String COL_TAG = "tag"; public static final String COL_FEEDTITLE = "feedtitle"; // For database projection so order is consistent public static final String[] FIELDS = { COL_ID, COL_TITLE, COL_DESCRIPTION, COL_PLAINTITLE, COL_PLAINSNIPPET, COL_IMAGEURL, COL_LINK, COL_AUTHOR, COL_PUBDATE, COL_UNREAD, COL_FEED, COL_TAG, COL_ENCLOSURELINK, COL_FEEDTITLE, COL_NOTIFIED, COL_JSON, COL_GUID }; /* * The SQL code that creates a Table. * Note that the last row does NOT end in a comma like the others. * This is a common source of error. */ public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + COL_ID + " INTEGER PRIMARY KEY," + COL_GUID + " TEXT NOT NULL," + COL_TITLE + " TEXT NOT NULL," + COL_DESCRIPTION + " TEXT NOT NULL," + COL_PLAINTITLE + " TEXT NOT NULL," + COL_PLAINSNIPPET + " TEXT NOT NULL," + COL_IMAGEURL + " TEXT," + COL_LINK + " TEXT," + COL_ENCLOSURELINK + " TEXT," + COL_AUTHOR + " TEXT," + COL_PUBDATE + " TEXT," + COL_JSON + " TEXT," + COL_UNREAD + " INTEGER NOT NULL DEFAULT 1," + COL_NOTIFIED + " INTEGER NOT NULL DEFAULT 0," + COL_FEED + " INTEGER NOT NULL," + COL_TAG + " TEXT NOT NULL," + COL_FEEDTITLE + " TEXT NOT NULL," + // Handle foreign key stuff " FOREIGN KEY(" + COL_FEED + ") REFERENCES " + FeedSQL.TABLE_NAME + "(" + FeedSQL.COL_ID + ") ON DELETE CASCADE," + // Handle unique constraint " UNIQUE(" + COL_GUID + "," + COL_FEED + ") ON CONFLICT " + "IGNORE" + ")"; // Trigger which updates Tags of items when feeds' tags are updated public static final String CREATE_TAG_TRIGGER = "CREATE TEMP TRIGGER IF NOT EXISTS trigger_tag_updater " + " AFTER UPDATE OF " + Util.arrayToCommaString(FeedSQL.COL_TAG, FeedSQL.COL_TITLE) + " ON " + FeedSQL.TABLE_NAME + " WHEN " + "new." + FeedSQL.COL_TAG + " IS NOT old." + FeedSQL.COL_TAG + " OR " + "new." + FeedSQL.COL_TITLE + " IS NOT old." + FeedSQL.COL_TITLE + " BEGIN " + " UPDATE " + FeedItemSQL.TABLE_NAME + " SET " + COL_TAG + " = " + " new." + FeedSQL.COL_TAG + ", " + COL_FEEDTITLE + " = " + " new." + FeedSQL.COL_TITLE + " WHERE " + COL_FEED + " IS old." + FeedSQL.COL_ID + "; END"; // Fields corresponding to database columns public long id = -1; public String guid = null; public String title = null; public String description = null; public String plaintitle = null; public String plainsnippet = null; public String imageurl = null; public String enclosurelink = null; public String author = null; private DateTime pubDate = null; public String link = null; public String tag = null; public String feedtitle = null; public int notified = 0; public String json = null; // Convenience field for parsing json private JSONObject jsonobject = null; // Convenience field for list views. Only converted first time private String domain; /** * Given an enclosurelink/link of http://www.bla.com/foo/bar, this method * returns bla.com * @return the domain of the enclosure link, or if null, the link's domain */ public String getDomain() { if (domain == null) { domain = enclosurelink != null ? enclosurelink : link; // Strip http:// int start = domain.indexOf("://"); if (start > 0) start += 3; else start = 0; // If www, strip that too if (domain.indexOf("www.") == start) { start += 4; } // Strip /foo/bar int end = domain.indexOf("/", start); if (end < 1) domain = domain.substring(start); else domain = domain.substring(start, end); } return domain; } /** * * @return Last bit of a URL. Example, bob/sam/floff will return floff */ public String getEnclosureFilename() { if (enclosurelink == null) return null; String[] parts = enclosurelink.split("/"); if (parts.length == 0) return null; // Return last bit return parts[parts.length - 1]; } public boolean isUnread() { return unread == 1; } public void setUnread(boolean unread) { this.unread = unread ? 1 : 0; } private int unread = 1; public long feed_id = -1; /** * No need to do anything, fields are already set to default values above */ public FeedItemSQL() { } /** * Convert information from the database into a FeedItem object. */ public FeedItemSQL(final Cursor cursor) { // Indices expected to match order in FIELDS! this.id = cursor.getLong(0); this.title = cursor.getString(1); this.description = cursor.getString(2); this.plaintitle = cursor.getString(3); this.plainsnippet = cursor.getString(4); this.imageurl = cursor.getString(5); this.link = cursor.getString(6); this.author = cursor.getString(7); setPubDate(cursor.getString(8)); this.unread = cursor.getInt(9); this.feed_id = cursor.getLong(10); this.tag = cursor.getString(11); this.enclosurelink = cursor.getString(12); this.feedtitle = cursor.getString(13); this.notified = cursor.getInt(14); this.json = cursor.getString(15); this.guid = cursor.getString(16); } public DateTime getPubDate() { return pubDate; } public void setPubDate(DateTime datetime) { pubDate = datetime; } /** * Output the date time in ISO8601 format (yyyy-MM-ddTHH:mm:ss.SSSZZ). * * @return ISO8601 time formatted string. */ public String getPubDateString() { if (pubDate == null) return null; return pubDate.toString(); } /** * Set the date time in ISO8601 format (yyyy-MM-ddTHH:mm:ss.SSSZZ). */ public void setPubDate(String datetime) { if (datetime == null) pubDate = null; else pubDate = DateTime.parse(datetime); } /** * Parse a timestamp and return what should be set on the database item. * @param datetime a timestamp to parse. Allowed to be null * @return null, or valid timestamp */ public static String getPubDateFromString(String datetime) { if (datetime == null) { return null; } try { return DateTime.parse(datetime).toString(); } catch (Exception e) { return DateTime.now().toString(); } } /** * Return the parsed json object * * @return JsonObject */ public JSONObject getJson() { if (this.json == null) { return null; } if (this.jsonobject == null) { try { this.jsonobject = new JSONObject(this.json); } catch (JSONException e) { Log.d(TAG, e.getLocalizedMessage()); } } return this.jsonobject; } // Some interesting values found in certain namespaces public String getTorrentMagnetURI() { return getJSONString("torrent_magneturi"); } public String getTorrentSeeds() { return getJSONString("torrent_seeds"); } public String getTorrentPeers() { return getJSONString("torrent_peers"); } public String getTorrentVerified() { return getJSONString("torrent_verified"); } protected String getJSONString(final String name) { JSONObject json = getJson(); if (json == null) { return null; } if (json.has(name)) { try { return json.getString(name); } catch (JSONException e) { Log.d(TAG, e.getLocalizedMessage()); return null; } } else { return null; } } /** * Return the fields in a ContentValues object, suitable for insertion * into the database. */ public ContentValues getContent() { final ContentValues values = new ContentValues(); // Note that ID is NOT included here values.put(COL_TITLE, title); values.put(COL_DESCRIPTION, description); values.put(COL_PLAINTITLE, plaintitle); values.put(COL_PLAINSNIPPET, plainsnippet); values.put(COL_FEED, feed_id); values.put(COL_UNREAD, unread); values.put(COL_FEEDTITLE, feedtitle); values.put(COL_NOTIFIED, notified); Util.PutNullable(values, COL_JSON, json); Util.PutNullable(values, COL_IMAGEURL, imageurl); Util.PutNullable(values, COL_LINK, link); Util.PutNullable(values, COL_GUID, guid); Util.PutNullable(values, COL_AUTHOR, author); Util.PutNullable(values, COL_PUBDATE, getPubDateString()); Util.PutNullable(values, COL_TAG, tag); Util.PutNullable(values, COL_ENCLOSURELINK, enclosurelink); return values; } @Override public int hashCode() { return Long.valueOf(id).hashCode(); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } return obj instanceof FeedItemSQL && Long.valueOf(id).equals(((FeedItemSQL) obj).id); } }