com.hust.duc.businessobjects.db.SubscriptionsDb.java Source code

Java tutorial

Introduction

Here is the source code for com.hust.duc.businessobjects.db.SubscriptionsDb.java

Source

/*
 * SkyTube
 * Copyright (C) 2016  Ramon Mifsud
 *
 * 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 (version 3 of the License).
 *
 * 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.hust.duc.businessobjects.db;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.hust.duc.businessobjects.YouTubeChannel;
import com.hust.duc.businessobjects.YouTubeVideo;
import com.hust.duc.gui.app.NewTubeApp;

/**
 * A database (DB) that stores user subscriptions (with respect to YouTube channels).
 */
public class SubscriptionsDb extends SQLiteOpenHelper {

    private static volatile SubscriptionsDb subscriptionsDb = null;

    private static final int DATABASE_VERSION = 2;
    private static final String DATABASE_NAME = "subs.db";

    private SubscriptionsDb(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    public static synchronized SubscriptionsDb getSubscriptionsDb() {
        if (subscriptionsDb == null) {
            subscriptionsDb = new SubscriptionsDb(NewTubeApp.getContext());
        }

        return subscriptionsDb;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SubscriptionsTable.getCreateStatement());
        db.execSQL(SubscriptionsVideosTable.getCreateStatement());
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // Version 2 of the database introduces the SubscriptionsVideosTable, which stores videos found in each subscribed channel
        if (oldVersion == 1 && newVersion == 2) {
            db.execSQL(SubscriptionsVideosTable.getCreateStatement());
        }
    }

    /**
     * Saves the given channel into the subscriptions DB.
     *
     * @param channel Channel the user wants to subscribe to.
     *
     * @return True if the operation was successful; false otherwise.
     */
    public boolean subscribe(YouTubeChannel channel) {
        ContentValues values = new ContentValues();
        values.put(SubscriptionsTable.COL_CHANNEL_ID, channel.getId());
        values.put(SubscriptionsTable.COL_LAST_VISIT_TIME, System.currentTimeMillis());

        saveChannelVideos(channel);
        return getWritableDatabase().insert(SubscriptionsTable.TABLE_NAME, null, values) != -1;
    }

    /**
     * Removes the given channel from the subscriptions DB.
     *
     * @param channel Channel the user wants to unsubscribe to.
     *
     * @return True if the operation was successful; false otherwise.
     */
    public boolean unsubscribe(YouTubeChannel channel) {
        getWritableDatabase().delete(SubscriptionsVideosTable.TABLE_NAME,
                SubscriptionsVideosTable.COL_CHANNEL_ID + " = ?", new String[] { channel.getId() });

        int rowsDeleted = getWritableDatabase().delete(SubscriptionsTable.TABLE_NAME,
                SubscriptionsTable.COL_CHANNEL_ID + " = ?", new String[] { channel.getId() });

        return (rowsDeleted >= 0);
    }

    /**
     * @return A list of channels that the user subscribed to.
     *
     * @throws IOException
     */
    public List<YouTubeChannel> getSubscribedChannels() throws IOException {
        return getSubscribedChannels(true);
    }

    /**
     * Returns A list of channels that the user subscribed to.
     *
     * @param shouldCheckForNewVideos  If true it will check if the channel has new videos since last channel visit.
     *
     * @return A list of channels that the user subscribed to.
     * @throws IOException
     */
    public List<YouTubeChannel> getSubscribedChannels(boolean shouldCheckForNewVideos) throws IOException {
        ArrayList<YouTubeChannel> subsChannels = new ArrayList<>();
        Cursor cursor = getReadableDatabase().query(SubscriptionsTable.TABLE_NAME,
                new String[] { SubscriptionsTable.COL_CHANNEL_ID }, null, null, null, null,
                SubscriptionsTable.COL_ID + " ASC");

        if (cursor.moveToNext()) {
            int colChannelIdNum = cursor.getColumnIndexOrThrow(SubscriptionsTable.COL_CHANNEL_ID);
            String channelId = null;
            YouTubeChannel channel = null;

            do {
                channelId = cursor.getString(colChannelIdNum);
                channel = new YouTubeChannel();
                channel.init(channelId, true /* = user is subscribed to this channel*/, shouldCheckForNewVideos);
                subsChannels.add(channel);
            } while (cursor.moveToNext());
        }
        cursor.close();

        return subsChannels;
    }

    /**
     * @return The total number of subscribed channels.
     */
    public int getTotalSubscribedChannels() {
        String query = String.format("SELECT COUNT(*) FROM %s", SubscriptionsTable.TABLE_NAME);
        Cursor cursor = SubscriptionsDb.getSubscriptionsDb().getReadableDatabase().rawQuery(query, null);
        int totalSubbedChannels = 0;

        if (cursor.moveToFirst())
            totalSubbedChannels = cursor.getInt(0);

        cursor.close();
        return totalSubbedChannels;
    }

    /**
     * Checks if the user is subscribed to the given channel.
     *
     * @param channelId   Channel ID
     * @return True if the user is subscribed; false otherwise.
     * @throws IOException
     */
    public boolean isUserSubscribedToChannel(String channelId) throws IOException {
        Cursor cursor = getReadableDatabase().query(SubscriptionsTable.TABLE_NAME,
                new String[] { SubscriptionsTable.COL_ID }, SubscriptionsTable.COL_CHANNEL_ID + " = ?",
                new String[] { channelId }, null, null, null);
        boolean isUserSubbed = cursor.moveToNext();

        cursor.close();
        return isUserSubbed;
    }

    /**
     * Updates the given channel's last visit time.
     *
     * @param channelId   Channel ID
     *
     * @return   last visit time, if the update was successful;  -1 otherwise.
     */
    public long updateLastVisitTime(String channelId) {
        SQLiteDatabase db = getWritableDatabase();
        long currentTime = System.currentTimeMillis();

        ContentValues values = new ContentValues();
        values.put(SubscriptionsTable.COL_LAST_VISIT_TIME, currentTime);

        int count = db.update(SubscriptionsTable.TABLE_NAME, values, SubscriptionsTable.COL_CHANNEL_ID + " = ?",
                new String[] { channelId });

        return (count > 0 ? currentTime : -1);
    }

    /**
     * Returns the last time the user has visited this channel.
     *
     * @param channel
     *
     * @return   last visit time, if the update was successful;  -1 otherwise.
     * @throws IOException
     */
    public long getLastVisitTime(YouTubeChannel channel) {
        Cursor cursor = getReadableDatabase().query(SubscriptionsTable.TABLE_NAME,
                new String[] { SubscriptionsTable.COL_LAST_VISIT_TIME }, SubscriptionsTable.COL_CHANNEL_ID + " = ?",
                new String[] { channel.getId() }, null, null, null);
        long lastVisitTime = -1;

        if (cursor.moveToNext()) {
            int colLastVisitTIme = cursor.getColumnIndexOrThrow(SubscriptionsTable.COL_LAST_VISIT_TIME);
            lastVisitTime = cursor.getLong(colLastVisitTIme);
        }

        cursor.close();
        return lastVisitTime;
    }

    private boolean hasVideo(YouTubeVideo video) {
        String query = String.format("SELECT COUNT(*) FROM %s WHERE %s = ?", SubscriptionsVideosTable.TABLE_NAME,
                SubscriptionsVideosTable.COL_YOUTUBE_VIDEO_ID);
        Cursor cursor = SubscriptionsDb.getSubscriptionsDb().getReadableDatabase().rawQuery(query,
                new String[] { video.getId() });
        if (cursor.moveToFirst()) {
            return cursor.getInt(0) > 0;
        }
        return false;
    }

    /**
     * Check if the given channel has new videos (by looking into the {@link SubscriptionsVideosTable}
     * [i.e. video cache table]).
     *
     * @param channel Channel to check.
     *
     * @return True if the user hasn't visited the channel and new videos have been uploaded in the
     * meantime; false otherwise.
     */
    public boolean channelHasNewVideos(YouTubeChannel channel) {
        DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
        String query = String.format("SELECT COUNT(*) FROM %s WHERE %s = ? AND %s > ?",
                SubscriptionsVideosTable.TABLE_NAME, SubscriptionsVideosTable.COL_CHANNEL_ID,
                SubscriptionsVideosTable.COL_YOUTUBE_VIDEO_DATE);
        Cursor cursor = SubscriptionsDb.getSubscriptionsDb().getReadableDatabase().rawQuery(query, new String[] {
                channel.getId(),
                fmt.parseDateTime(new DateTime(new Date(channel.getLastVisitTime())).toString()).toString() });
        boolean channelHasNewVideos = false;

        if (cursor.moveToFirst())
            channelHasNewVideos = cursor.getInt(0) > 0;

        cursor.close();
        return channelHasNewVideos;
    }

    /**
     * Loop through each video saved in the passed {@link YouTubeChannel} and save it into the database, if it's not already been saved
     * @param channel
     */
    public void saveChannelVideos(YouTubeChannel channel) {
        Gson gson = new Gson();
        DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
        for (YouTubeVideo video : channel.getYouTubeVideos()) {
            if (!hasVideo(video)) {
                ContentValues values = new ContentValues();
                values.put(SubscriptionsVideosTable.COL_CHANNEL_ID, channel.getId());
                values.put(SubscriptionsVideosTable.COL_YOUTUBE_VIDEO_ID, video.getId());
                values.put(SubscriptionsVideosTable.COL_YOUTUBE_VIDEO, gson.toJson(video).getBytes());
                values.put(SubscriptionsVideosTable.COL_YOUTUBE_VIDEO_DATE,
                        fmt.parseDateTime(video.getPublishDate().toStringRfc3339()).toString());

                getWritableDatabase().insert(SubscriptionsVideosTable.TABLE_NAME, null, values);
            }
        }
    }

    public boolean trimSubscriptionVideos() {
        int result = getWritableDatabase().delete(SubscriptionsVideosTable.TABLE_NAME,
                String.format("%s < DATETIME('now', '-1 month')", SubscriptionsVideosTable.COL_YOUTUBE_VIDEO_DATE),
                null);
        return result > 0;
    }

    public List<YouTubeVideo> getSubscriptionVideos() {
        Cursor cursor = getReadableDatabase().query(SubscriptionsVideosTable.TABLE_NAME,
                new String[] { SubscriptionsVideosTable.COL_YOUTUBE_VIDEO }, null, null, null, null,
                SubscriptionsVideosTable.COL_YOUTUBE_VIDEO_DATE + " DESC");
        List<YouTubeVideo> videos = new ArrayList<>();

        if (cursor.moveToNext()) {
            do {
                byte[] blob = cursor.getBlob(cursor.getColumnIndex(SubscriptionsVideosTable.COL_YOUTUBE_VIDEO));
                YouTubeVideo video = new Gson().fromJson(new String(blob), new TypeToken<YouTubeVideo>() {
                }.getType());
                videos.add(video);
            } while (cursor.moveToNext());
        }

        cursor.close();
        return videos;
    }

}