package org.liberty.android.fantastischmemo.downloader.quizlet; import android.net.Uri; import android.util.Log; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.liberty.android.fantastischmemo.common.AMEnv; import org.liberty.android.fantastischmemo.common.AnyMemoDBOpenHelper; import org.liberty.android.fantastischmemo.common.AnyMemoDBOpenHelperManager; import org.liberty.android.fantastischmemo.dao.CardDao; import org.liberty.android.fantastischmemo.downloader.common.DownloadItem; import org.liberty.android.fantastischmemo.downloader.common.DownloaderUtils; import org.liberty.android.fantastischmemo.entity.Card; import org.liberty.android.fantastischmemo.entity.Category; import org.liberty.android.fantastischmemo.entity.LearningData; import org.liberty.android.fantastischmemo.modules.PerApplication; import org.liberty.android.fantastischmemo.utils.AMFileUtil; import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import javax.net.ssl.HttpsURLConnection; @PerApplication public class QuizletDownloadHelper { private static final String TAG = QuizletDownloadHelper.class.getSimpleName(); private DownloaderUtils downloaderUtils; private AMFileUtil amFileUtil; @Inject public QuizletDownloadHelper(DownloaderUtils downloaderUtils, AMFileUtil amFileUtil) { this.downloaderUtils = downloaderUtils; this.amFileUtil = amFileUtil; } /** * Fetch cardsets list from Quizlet * * @param userId * user name * @param authToken * oauth token * @return cardsets list * @throws IOException * IOException If http response code is not 2xx * @throws JSONException * If the response is invalid JSON */ public List<DownloadItem> getUserPrivateCardsetsList(String userId, String authToken) throws IOException, JSONException { List<DownloadItem> downloadItemList = new ArrayList<DownloadItem>(); URL url = new URL(AMEnv.QUIZLET_API_ENDPOINT + "/users/" + userId + "/sets"); String response = makeApiCall(url, authToken); JSONArray jsonArray = new JSONArray(response); for (int i = 0; i < jsonArray.length(); i++) { JSONObject jsonItem = jsonArray.getJSONObject(i); String address = jsonItem.getString("url"); String description = new StringBuilder().append("<br />") .append(jsonItem.getInt("term_count")).append("<br />") .append(jsonItem.getLong("created_date")).append("<br />") .append(jsonItem.getString("description")).append("<br />") .append(jsonItem.getString("created_by")).toString(); DownloadItem item = new DownloadItem( DownloadItem.ItemType.Database, jsonItem.getString("title"), description, address); item.setExtras("id", "" + jsonItem.getInt("id")); downloadItemList.add(item); } return downloadItemList; } /** * Download cardsets list from Quizlet and save to a db file * * @param setId * cardset ID * @param authToken * oauth token * @return The path of saved db file * @throws IOException * IOException If http response code is not 2xx * @throws JSONException * If the response is invalid JSON */ public String downloadCardset(String setId, String authToken) throws IOException, JSONException { URL url; // Public cardset download needs cilent id, private cardset download // needs authtoken if (authToken != null) { url = new URL(AMEnv.QUIZLET_API_ENDPOINT + "/sets/" + setId); } else { String urlString = String.format(AMEnv.QUIZLET_API_ENDPOINT + "/sets/" + "%1$s?client_id=%2$s", URLEncoder.encode(setId, "UTF-8"), URLEncoder.encode(AMEnv.QUIZLET_CLIENT_ID, "UTF-8")); url = new URL(urlString); } String response = makeApiCall(url, authToken); JSONObject rootObject = new JSONObject(response); JSONArray flashcardsArray = rootObject.getJSONArray("terms"); int termCount = rootObject.getInt("term_count"); boolean hasImage = rootObject.getBoolean("has_images"); List<Card> cardList = new ArrayList<Card>(termCount); // handle image String dbname = downloaderUtils.validateDBName(rootObject .getString("title")) + ".db"; String imagePath = AMEnv.DEFAULT_IMAGE_PATH + dbname + "/"; if (hasImage) { FileUtils.forceMkdir(new File(imagePath)); } for (int i = 0; i < flashcardsArray.length(); i++) { JSONObject jsonItem = flashcardsArray.getJSONObject(i); String question = jsonItem.getString("term"); String answer = jsonItem.getString("definition"); // Download images, ignore image downloading error. try { if (jsonItem.has("image") && !jsonItem.isNull("image") && hasImage) { JSONObject imageItem = jsonItem.getJSONObject("image"); String imageUrl = imageItem.getString("url"); String downloadFilename = Uri.parse(imageUrl) .getLastPathSegment(); downloaderUtils.downloadFile(imageUrl, imagePath + downloadFilename); answer += "<br /><img src=\"" + downloadFilename + "\"/>"; } } catch (Exception e) { Log.e(TAG, "Error downloading image.", e); } Card card = new Card(); card.setQuestion(question); card.setAnswer(answer); card.setCategory(new Category()); card.setLearningData(new LearningData()); cardList.add(card); } /* Make a valid dbname from the title */ String dbpath = AMEnv.DEFAULT_ROOT_PATH; String fullpath = dbpath + dbname; amFileUtil.deleteFileWithBackup(fullpath); AnyMemoDBOpenHelper helper = AnyMemoDBOpenHelperManager .getHelper(fullpath); try { CardDao cardDao = helper.getCardDao(); cardDao.createCards(cardList); long count = helper.getCardDao().getTotalCount(null); if (count <= 0L) { throw new RuntimeException("Downloaded empty db."); } } finally { AnyMemoDBOpenHelperManager.releaseHelper(helper); } return fullpath; } /** * Make API call to Quizlet server with oauth * * @param url * API call endpoint * @param authToken * oauth auth token * @return Response of API call * @throws IOException * If http response code is not 2xx */ private String makeApiCall(URL url, String authToken) throws IOException { HttpsURLConnection conn = null; try { conn = (HttpsURLConnection) url.openConnection(); if (authToken != null) { conn.addRequestProperty("Authorization", "Bearer " + authToken); } String response = new String(IOUtils.toByteArray(conn .getInputStream())); if (conn.getResponseCode() / 100 >= 3) { throw new IOException("Response code: " + conn.getResponseCode() + " Response is: " + response); } return response; } finally { conn.disconnect(); } } // Following functions for public card sets /** * Search for public card sets from Quizlet by title * * @param title * card title * @param page * current page * @return Search result of card sets list * @throws IOException * IOException If http response code is not 2xx */ public List<DownloadItem> getCardListByTitle(String title, int page) throws IOException { String urlString = String.format(AMEnv.QUIZLET_API_ENDPOINT + "/search/sets?client_id=%1$s&q=%2$s&page=%3$d", URLEncoder.encode(AMEnv.QUIZLET_CLIENT_ID, "UTF-8"), URLEncoder.encode(title, "UTF-8"), page); URL url = new URL(urlString); String responseString = makeApiCall(url, null); try { JSONObject jsonObject = new JSONObject(responseString); JSONArray setsArray = jsonObject.getJSONArray("sets"); return parseSetsJSONArray(setsArray); } catch (JSONException e) { throw new RuntimeException(e); } } /** * Search for public card sets from Quizlet by user name * * @param username * card creator user name * @param page * current page * @return Search result of card sets list * @throws IOException * IOException If http response code is not 2xx */ public List<DownloadItem> getCardListByUser(String username, int page) throws IOException { String urlString = String.format(AMEnv.QUIZLET_API_ENDPOINT + "/search/sets?client_id=%1$s&creator=%2$s&page=%3$d", URLEncoder.encode(AMEnv.QUIZLET_CLIENT_ID, "UTF-8"), URLEncoder.encode(username, "UTF-8"), page); URL url = new URL(urlString); String responseString = makeApiCall(url, null); try { JSONObject jsonObject = new JSONObject(responseString); JSONArray setsArray = jsonObject.getJSONArray("sets"); return parseSetsJSONArray(setsArray); } catch (JSONException e) { throw new RuntimeException(e); } } /** * Helper method to parse the response of set list class * * @param setsArray * A JSON array of sets * @return a list of download items */ private List<DownloadItem> parseSetsJSONArray(JSONArray setsArray) throws IOException { try { List<DownloadItem> itemList = new ArrayList<DownloadItem>( setsArray.length()); for (int i = 0; i < setsArray.length(); i++) { JSONObject jsonItem = setsArray.getJSONObject(i); String address = jsonItem.getString("url"); String description = new StringBuilder().append("<br />") .append(jsonItem.getInt("term_count")).append("<br />") .append(jsonItem.getLong("created_date")) .append("<br />") .append(jsonItem.getString("description")) .append("<br />") .append(jsonItem.getString("created_by")).toString(); DownloadItem item = new DownloadItem( DownloadItem.ItemType.Database, jsonItem.getString("title"), description, address); item.setExtras("id", "" + jsonItem.getInt("id")); itemList.add(item); } return itemList; } catch (JSONException e) { throw new RuntimeException(e); } } }