com.oakesville.mythling.app.AppData.java Source code

Java tutorial

Introduction

Here is the source code for com.oakesville.mythling.app.AppData.java

Source

/**
 * Copyright 2015 Donald Oakes
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.oakesville.mythling.app;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.oakesville.mythling.BuildConfig;
import com.oakesville.mythling.R;
import com.oakesville.mythling.media.ChannelGroup;
import com.oakesville.mythling.media.Cut;
import com.oakesville.mythling.media.Download;
import com.oakesville.mythling.media.Item;
import com.oakesville.mythling.media.MediaList;
import com.oakesville.mythling.media.MediaSettings.MediaType;
import com.oakesville.mythling.media.SearchResults;
import com.oakesville.mythling.media.StorageGroup;
import com.oakesville.mythling.util.MediaListParser;
import com.oakesville.mythling.util.MythTvParser;
import com.oakesville.mythling.util.MythlingParser;

import android.app.DownloadManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.util.Log;
import android.util.LruCache;
import android.view.Display;
import android.view.WindowManager;

public class AppData {
    private static final String TAG = AppData.class.getSimpleName();

    private static final String MEDIA_LIST_JSON_FILE = "mediaList.json";
    private static final String STORAGE_GROUPS_JSON_FILE = "storageGroups.json";
    private static final String CHANNEL_GROUPS_JSON_FILE = "channelGroups.json";
    private static final String DOWNLOADS_JSON_FILE = "downloadedItems.json";
    private static final String QUEUE_FILE_SUFFIX = "Queue.json";

    private Context appContext;

    public AppData(Context appContext) {
        this.appContext = appContext;
    }

    public boolean isExpired() {
        AppSettings appSettings = new AppSettings(appContext);
        long last = appSettings.getLastLoad();
        if (last <= 0)
            return true;

        long current = System.currentTimeMillis();
        long expiry = appSettings.getExpiryMinutes() * 60 * 1000;

        return (current - last) > expiry;
    }

    private MediaList mediaList;

    public MediaList getMediaList() {
        return mediaList;
    }

    public void setMediaList(MediaList mediaList) {
        this.mediaList = mediaList;
    }

    private Map<String, StorageGroup> storageGroups;

    public Map<String, StorageGroup> getStorageGroups() {
        return storageGroups;
    }

    public void setStorageGroups(Map<String, StorageGroup> sgs) {
        this.storageGroups = sgs;
    }

    private Map<String, ChannelGroup> channelGroups;

    public Map<String, ChannelGroup> getChannelGroups() {
        return channelGroups;
    }

    public void setChannelGroups(Map<String, ChannelGroup> chgroups) {
        this.channelGroups = chgroups;
    }

    private Map<String, Download> downloads = new HashMap<String, Download>();

    private SearchResults searchResults;

    public SearchResults getSearchResults() {
        return searchResults;
    }

    public void setSearchResults(SearchResults results) {
        this.searchResults = results;
    }

    public MediaList readMediaList(MediaType mediaType) throws IOException, JSONException, ParseException {
        Map<String, StorageGroup> storageGroups = readStorageGroups();
        if (storageGroups != null) {
            File cacheDir = appContext.getCacheDir();
            File mediaListJsonFile = new File(cacheDir.getPath() + "/" + MEDIA_LIST_JSON_FILE);
            if (mediaListJsonFile.exists()) {
                AppSettings appSettings = new AppSettings(appContext);
                String mediaListJson = new String(readFile(mediaListJsonFile));
                MediaListParser mediaListParser = appSettings.getMediaListParser(mediaListJson);
                mediaList = mediaListParser.parseMediaList(mediaType, storageGroups);
                mediaList.setDownloads(getDownloads());
            }
        }
        return mediaList;
    }

    public Map<String, StorageGroup> readStorageGroups() throws IOException, JSONException, ParseException {
        File cacheDir = appContext.getCacheDir();
        File storageGroupsJsonFile = new File(cacheDir.getPath() + "/" + STORAGE_GROUPS_JSON_FILE);
        if (storageGroupsJsonFile.exists()) {
            AppSettings appSettings = new AppSettings(appContext);
            String storageGroupsJson = new String(readFile(storageGroupsJsonFile));
            MythTvParser storageGroupParser = new MythTvParser(appSettings, storageGroupsJson);
            storageGroups = storageGroupParser.parseStorageGroups();
        }
        return storageGroups;
    }

    public Map<String, ChannelGroup> readChannelGroups() throws IOException, JSONException, ParseException {
        File cacheDir = appContext.getCacheDir();
        File channelGroupsJsonFile = new File(cacheDir.getPath() + "/" + CHANNEL_GROUPS_JSON_FILE);
        if (channelGroupsJsonFile.exists()) {
            AppSettings appSettings = new AppSettings(appContext);
            String channelGroupsJson = new String(readFile(channelGroupsJsonFile));
            MythTvParser channelGroupParser = new MythTvParser(appSettings, channelGroupsJson);
            channelGroups = channelGroupParser.parseChannelGroups();
        }
        return channelGroups;
    }

    public void clearChannelGroups() {
        channelGroups = null;
        File cacheDir = appContext.getCacheDir();
        File channelGroupsJsonFile = new File(cacheDir.getPath() + "/" + CHANNEL_GROUPS_JSON_FILE);
        if (channelGroupsJsonFile.exists())
            channelGroupsJsonFile.delete();
    }

    public void writeMediaList(String json) throws IOException, JSONException {
        File cacheDir = appContext.getCacheDir();
        File jsonFile = new File(cacheDir.getPath() + "/" + MEDIA_LIST_JSON_FILE);
        writeFile(jsonFile, json.getBytes());
    }

    public void writeStorageGroups(String json) throws IOException, JSONException {
        File cacheDir = appContext.getCacheDir();
        File jsonFile = new File(cacheDir.getPath() + "/" + STORAGE_GROUPS_JSON_FILE);
        writeFile(jsonFile, json.getBytes());
    }

    public void writeChannelGroups(String json) throws IOException, JSONException {
        File cacheDir = appContext.getCacheDir();
        File jsonFile = new File(cacheDir.getPath() + "/" + CHANNEL_GROUPS_JSON_FILE);
        writeFile(jsonFile, json.getBytes());
    }

    private void readDownloads() throws IOException, JSONException, ParseException {
        downloads.clear();
        File cacheDir = appContext.getCacheDir();
        File downloadsJsonFile = new File(cacheDir.getPath() + "/" + DOWNLOADS_JSON_FILE);
        if (downloadsJsonFile.exists()) {
            String downloadsJson = new String(readFile(downloadsJsonFile));
            JSONArray downloadsArr = new JSONArray(downloadsJson);
            for (int i = 0; i < downloadsArr.length(); i++) {
                JSONObject downloadObj = downloadsArr.getJSONObject(i);
                Download download = new Download(downloadObj);
                downloads.put(download.getItemId(), download);
            }
        }
    }

    public Map<String, Download> getDownloads() throws IOException, JSONException, ParseException {
        readDownloads();
        DownloadManager dm = (DownloadManager) appContext.getSystemService(Context.DOWNLOAD_SERVICE);
        Map<String, Download> filtered = new HashMap<String, Download>();
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, -7);
        long lastWeek = cal.getTimeInMillis();
        List<String> itemsToRemove = null;
        for (String itemId : downloads.keySet()) {
            Download download = downloads.get(itemId);
            if (dm.getUriForDownloadedFile(download.getDownloadId()) != null) // make sure the file exists
                filtered.put(itemId, download);
            else if (lastWeek > download.getStarted().getTime()) { // remove missing items older than a week
                if (itemsToRemove == null)
                    itemsToRemove = new ArrayList<String>();
                itemsToRemove.add(download.getItemId());
            }
        }
        if (itemsToRemove != null) {
            for (String itemToRemove : itemsToRemove)
                downloads.remove(itemToRemove);
            persistDownloads();
        }

        return filtered;
    }

    public Download getDownload(String path) throws IOException, JSONException, ParseException {
        for (Download download : getDownloads().values()) {
            if (path.endsWith(download.getPath()))
                return download;
        }
        return null;
    }

    public void writeDownloads(String json) throws IOException, JSONException {
        File cacheDir = appContext.getCacheDir();
        File jsonFile = new File(cacheDir.getPath() + "/" + DOWNLOADS_JSON_FILE);
        writeFile(jsonFile, json.getBytes());
    }

    public void addDownload(Download download) throws IOException, JSONException {
        downloads.put(download.getItemId(), download);
        persistDownloads();
    }

    public boolean addDownloadCutList(String itemId, ArrayList<Cut> cutList) throws IOException, JSONException {
        Download download = downloads.get(itemId);
        if (download != null) {
            download.setCutList(cutList);
            persistDownloads();
            return true;
        }
        return false;
    }

    private void persistDownloads() throws IOException, JSONException {
        JSONArray downloadsArr = new JSONArray();
        for (Download download : downloads.values())
            downloadsArr.put(download.toJson());
        writeDownloads(downloadsArr.toString());
    }

    public Long removeDownload(String itemId) throws IOException, JSONException {
        Download download = downloads.remove(itemId);
        persistDownloads();
        return download.getDownloadId();
    }

    private Map<MediaType, List<Item>> queues = new HashMap<MediaType, List<Item>>();

    public List<Item> getQueue(MediaType type) {
        return queues.get(type);
    }

    public void setQueue(MediaType type, List<Item> queue) {
        queues.put(type, queue);
    }

    public String getQueueJson(MediaType type) throws JSONException {
        List<Item> itemsList = getQueue(type);
        if (itemsList == null)
            return null;
        JSONObject jsonObj = new JSONObject();
        JSONArray items = new JSONArray();
        for (Item item : itemsList) {
            // TODO real item serialization
            JSONObject w = new JSONObject();
            w.put("path", item.getPath());
            w.put("title", item.getTitle());
            w.put("format", item.getFormat());
            items.put(w);
        }
        jsonObj.put(type.toString(), items);

        return jsonObj.toString(2);
    }

    public List<Item> readQueue(MediaType type) throws IOException, JSONException, ParseException {
        File cacheDir = appContext.getCacheDir();
        File queueFile = new File(cacheDir.getPath() + "/" + type + QUEUE_FILE_SUFFIX);
        if (queueFile.exists()) {
            String queueJson = new String(readFile(queueFile));
            MythlingParser parser = new MythlingParser(new AppSettings(appContext), queueJson);
            List<Item> queue = parser.parseQueue(type, storageGroups);
            queues.put(type, queue);
        }
        return queues.get(type);
    }

    public void writeQueue(MediaType type, String json) throws IOException, JSONException {
        File cacheDir = appContext.getCacheDir();
        File jsonFile = new File(cacheDir.getPath() + "/" + type + QUEUE_FILE_SUFFIX);
        writeFile(jsonFile, json.getBytes());
    }

    private byte[] readFile(File file) throws IOException {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            byte[] bytes = new byte[(int) file.length()];
            fis.read(bytes);
            return bytes;
        } finally {
            if (fis != null)
                fis.close();
        }
    }

    private void writeFile(File file, byte[] contents) throws IOException {

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            fos.write(contents);
        } finally {
            if (fos != null)
                fos.close();
        }
    }

    private static int bitmapCacheMem = (int) ((Runtime.getRuntime().maxMemory() / 1024) / 4); // one quarter of total (kb)
    private static LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(bitmapCacheMem) {
        protected int sizeOf(String key, Bitmap value) {
            return value.getByteCount() / 1024; // kb taken up by this entry
        }
    };

    public Bitmap getImageBitMap(String path) {
        Bitmap bitmap = bitmapCache.get(path);
        if (bitmap == null) {
            bitmap = readImageBitmap(path);
            if (bitmap != null)
                bitmapCache.put(path, bitmap);
        }
        return bitmap;
    }

    public Bitmap readImageBitmap(String path) {
        File cacheDir = appContext.getCacheDir();
        File imageFile = new File(cacheDir.getPath() + "/" + path);
        if (imageFile.exists()) {
            if (BuildConfig.DEBUG)
                Log.d(TAG, "reading image from file: " + imageFile);
            // avoid out-of-memory errors by checking image dimensions
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(imageFile.getPath(), options);
            // calculate sample size (based on screen size)
            options.inSampleSize = calculateImageSampleSize(options.outWidth, options.outHeight, getScreenSize().x,
                    getScreenSize().y);
            options.inJustDecodeBounds = false;
            // TODO: save resampled image to save disk space
            return BitmapFactory.decodeFile(imageFile.getPath(), options);
        } else {
            return null;
        }
    }

    public void writeImage(String path, byte[] bytes) throws IOException {
        File cacheDir = appContext.getCacheDir();
        String dirPath = path.substring(0, path.lastIndexOf('/'));
        File dir = new File(cacheDir + "/" + dirPath);
        if (!dir.exists() && !dir.mkdirs())
            throw new IOException(appContext.getString(R.string.unable_to_create_directories_) + dir);
        File imageFile = new File(cacheDir.getPath() + "/" + path);
        if (BuildConfig.DEBUG)
            Log.d(TAG, "writing image to file: " + imageFile);
        writeFile(imageFile, bytes);
    }

    private static Point screenSize;

    public Point getScreenSize() {
        if (screenSize == null) {
            WindowManager wm = (WindowManager) appContext.getSystemService(Context.WINDOW_SERVICE);
            Display display = wm.getDefaultDisplay();
            screenSize = new Point();
            display.getSize(screenSize);
        }
        return screenSize;
    }

    public int calculateImageSampleSize(int width, int height, int reqWidth, int reqHeight) {
        int sampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
            // get the largest sample size that's a power of 2 and keeps both
            // height and width larger than the requested height and width
            while ((halfHeight / sampleSize) > reqHeight && (halfWidth / sampleSize) > reqWidth) {
                sampleSize *= 2;
            }
        }
        return sampleSize;
    }
}