/**
* Copyright (c) 2013-2014. Francisco Contreras, Holland Salazar.
* Copyright (c) 2015. Tobias Strebitzer, Francisco Contreras, Holland Salazar.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
* Neither the name of the Baker Framework nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written
* permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**/
package com.bakerframework.baker.model;
import android.content.res.AssetManager;
import android.support.annotation.NonNull;
import android.util.Log;
import android.widget.Toast;
import com.bakerframework.baker.BakerApplication;
import com.bakerframework.baker.R;
import com.bakerframework.baker.events.DownloadManifestCompleteEvent;
import com.bakerframework.baker.events.DownloadManifestErrorEvent;
import com.bakerframework.baker.events.FetchPurchasesCompleteEvent;
import com.bakerframework.baker.events.FetchPurchasesErrorEvent;
import com.bakerframework.baker.events.IssueCollectionErrorEvent;
import com.bakerframework.baker.events.IssueCollectionLoadedEvent;
import com.bakerframework.baker.helper.FileHelper;
import com.bakerframework.baker.jobs.DownloadManifestJob;
import com.bakerframework.baker.jobs.FetchPurchasesJob;
import com.bakerframework.baker.settings.Configuration;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.solovyev.android.checkout.Inventory;
import org.solovyev.android.checkout.Sku;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import de.greenrobot.event.EventBus;
import static org.solovyev.android.checkout.ProductTypes.IN_APP;
import static org.solovyev.android.checkout.ProductTypes.SUBSCRIPTION;
public class LocalIssueCollection implements IssueCollection {
private final HashMap<String, Issue> issueMap;
private List<String> categories;
// Data Processing
final String JSON_ENCODING = "utf-8";
final SimpleDateFormat SDF_INPUT = new SimpleDateFormat(BakerApplication.getInstance().getString(R.string.format_input_date), Locale.US);
final SimpleDateFormat SDF_OUTPUT = new SimpleDateFormat(BakerApplication.getInstance().getString(R.string.format_output_date), Locale.US);
// Categories
public static final String ALL_CATEGORIES_STRING = "All Categories";
public LocalIssueCollection() {
issueMap = new HashMap<>();
// EventBus.getDefault().register(this);
}
public List<String> getCategories() {
return categories;
}
public List<Issue> getIssues() {
if(issueMap == null) {
return new ArrayList<>();
}else{
return new ArrayList<>(issueMap.values());
}
}
// Reload data from backend
public void load() {
ArrayList<String> issues;
if (Configuration.shouldReadStandaloneFromCustomDirectory()) {
// @TODO: Implement expansion file download
/*
if (this.expansionFileExists()) {
Log.d(this.getClass().getName(), "The expansion file exists.");
File directory = new File(Configuration.getMagazinesDirectory());
if (directory.exists() && (directory.list().length > 0)) {
if (this.getExtractionFinished()) {
Log.d(this.getClass().getName(), "Magazines directory not empty and extraction finished.");
this.getValidIssuesFromSharedStorage();
} else {
Log.d(this.getClass().getName(), "Magazines directory not empty but the extraction did not finished. Trying again.");
this.extractFromExpansionFile();
}
} else {
Log.d(this.getClass().getName(), "No magazines detected on the magazines directory.");
this.saveExtractionFinished(false);
this.extractFromExpansionFile();
}
} else {
Log.d(this.getClass().getName(), "The expansion file does not exist.");
this.downloadExpansionFile();
}
*/
}else{
issues = this.getValidIssuesAssets();
readStandaloneIssues(issues);
// Trigger issues loaded event
EventBus.getDefault().post(new IssueCollectionLoadedEvent());
}
}
private void readStandaloneIssues(final ArrayList<String> issues) {
JSONArray jsonArray = new JSONArray();
for (String issueFileName : issues) {
Log.d(this.getClass().getName(), "The file is: " + issueFileName);
jsonArray.put(this.getIssueData(issueFileName));
}
try {
this.processJson(jsonArray);
} catch (JSONException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
private JSONObject getIssueData(final String issueName) {
String books;
boolean fromAssets = !BakerApplication.getInstance().getResources().getBoolean(R.bool.sa_read_from_custom_directory);
final String bookJson = "book.json";
JSONObject result = new JSONObject();
BufferedReader reader = null;
try {
result.put("name", issueName);
AssetManager assetManager = BakerApplication.getInstance().getAssets();
String bookJsonPath = issueName.concat(File.separator).concat(bookJson);
if (fromAssets) {
books = BakerApplication.getInstance().getString(R.string.sa_books_directory).concat(File.separator).concat(bookJsonPath);
reader = new BufferedReader(new InputStreamReader(assetManager.open(books)));
} else {
books = Configuration.getMagazinesDirectory().concat(File.separator).concat(bookJsonPath);
reader = new BufferedReader(new InputStreamReader(new FileInputStream(books)));
}
String line = "";
StringBuilder jsonString = new StringBuilder();
do {
jsonString.append(line);
line = reader.readLine();
} while (line != null);
Log.d(this.getClass().getName(), "The book.json read was: " + jsonString.toString());
JSONObject jsonRaw = new JSONObject(jsonString.toString());
result.put("title", jsonRaw.getString("title"));
result.put("url", jsonRaw.getString("url"));
result.put("info", jsonRaw.getString("title"));
result.put("cover", "file://" + (jsonRaw.has("cover") ? jsonRaw.getString("cover") : "cover.png"));
result.put("date", jsonRaw.getString("date"));
} catch (JSONException ex) {
Log.e(this.getClass().getName(), "Error getting issue information from " + issueName, ex);
} catch (IOException ex) {
Log.e(this.getClass().getName(), "Error getting issue information from " + issueName, ex);
} finally {
try {
reader.close();
} catch (Exception ex) {
//
}
}
return result;
}
private ArrayList<String> getValidIssuesAssets() {
final String path = Configuration.getStandaloneBooksDirectory();
ArrayList<String> issues = new ArrayList<String>();
try {
AssetManager assetManager = BakerApplication.getInstance().getAssets();
String assetList[] = assetManager.list(path);
String fileName;
for (String asset : assetList) {
fileName = path.concat(File.separator).concat(asset);
if (assetManager.list(fileName).length > 0) {
if (this.hasBookJson(fileName)) {
Log.d(this.getClass().getName(), "Valid issue found: " + fileName);
issues.add(asset);
}
}
}
} catch (Exception ex) {
Log.e(this.getClass().getName(), "Error getting issues from assets", ex);
}
return issues;
}
private boolean hasBookJson(final String issuePath) {
boolean result = false;
final String bookJson = "book.json";
AssetManager assetManager = BakerApplication.getInstance().getAssets();
InputStream inputStream = null;
try {
inputStream = assetManager.open(issuePath.concat(File.separator).concat(bookJson));
result = true;
} catch (Exception ex) {
result = false;
} finally {
try {
if (null != inputStream) {
inputStream.close();
}
} catch (IOException e) {
Log.e(this.getClass().getName(), "Error opening the book.json for " + issuePath);
}
}
return result;
}
private void processJson(final JSONArray jsonArray) throws JSONException, UnsupportedEncodingException {
JSONObject json;
JSONArray jsonCategories;
List<String> categories;
List<String> issueNameList = new ArrayList<>();
// Loop through issues
int length = jsonArray.length();
for (int i = 0; i < length; i++) {
json = new JSONObject(jsonArray.getString(i));
// Get issue data from json
String issueName = jsonString(json.getString("name"));
String issueProductId = json.isNull("product_id") ? null : jsonString(json.getString("product_id"));
String issueTitle = jsonString(json.getString("title"));
String issueInfo = jsonString(json.getString("info"));
String issueDate = jsonDate(json.getString("date"));
String issueCover = jsonString(json.getString("cover"));
String issueUrl = jsonString(json.getString("url"));
int issueSize = json.has("size") ? json.getInt("size") : 0;
Issue issue;
if(issueMap.containsKey(issueName)) {
// Get issue from issue map
issue = issueMap.get(issueName);
// Flag fields for update
if(!issue.getCover().equals(issueCover)) {
issue.setCoverChanged(true);
}
if(!issue.getUrl().equals(issueUrl)) {
issue.setUrlChanged(true);
}
}else{
// Create new issue and store in issue map
issue = new Issue(issueName);
issueMap.put(issueName, issue);
}
// Set issue data
issue.setTitle(issueTitle);
issue.setProductId(issueProductId);
issue.setInfo(issueInfo);
issue.setDate(issueDate);
issue.setCover(issueCover);
issue.setUrl(issueUrl);
issue.setSize(issueSize);
issue.setStandalone(true);
// Set categories
if(json.has("categories")) {
jsonCategories = json.getJSONArray("categories");
categories = new ArrayList<>();
for (int j = 0; j < jsonCategories.length(); j++) {
categories.add(jsonCategories.get(j).toString());
}
issue.setCategories(categories);
}else{
issue.setCategories(new ArrayList<String>());
}
// Add name to issue name list
issueNameList.add(issueName);
}
// Get rid of old issues that are no longer in the manifest
for(Issue issue : issueMap.values()) {
if(!issueNameList.contains(issue.getName())) {
issueMap.remove(issue);
}
}
}
// Helpers
private String jsonDate(String value) {
try {
return SDF_OUTPUT.format(SDF_INPUT.parse(value));
} catch (ParseException e) {
return "";
}
}
private String jsonString(String value) throws UnsupportedEncodingException {
if(value != null) {
return new String(value.getBytes(JSON_ENCODING), JSON_ENCODING);
}else{
return null;
}
}
public List<String> extractAllCategories() {
// Collect all categories from issues
List<String> allCategories = new ArrayList<>();
for(Issue issue : issueMap.values()) {
for(String category : issue.getCategories()) {
if(allCategories.indexOf(category) == -1) {
allCategories.add(category);
}
}
}
// Sort categories
Collections.sort(allCategories);
// Append all categories item
allCategories.add(0, ALL_CATEGORIES_STRING);
return allCategories;
}
public List<Issue> getDownloadingIssues() {
List<Issue> downloadingIssues = new ArrayList<>();
for (Issue issue : issueMap.values()) {
if(issue.isDownloading()) {
downloadingIssues.add(issue);
}
}
return downloadingIssues;
}
public void cancelDownloadingIssues(final List<Issue> downloadingIssues) {
for (Issue issue : downloadingIssues) {
if(issue.isDownloading()) {
issue.cancelDownloadJob();
}
}
}
public Issue getIssueByName(String issueName) {
return issueMap.get(issueName);
}
/*
@Override
public void onEventMainThread(Object event) {
Log.d("RemoteIssueCollection", "onEventMainThread called");
}
*/
}