package com.door43.translationstudio.core;
import android.content.Context;
import android.support.annotation.Nullable;
import com.door43.tools.reporting.Logger;
import com.door43.translationstudio.AppContext;
import com.door43.util.Zip;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
* Created by joel on 8/29/2015.
*/
public class Library {
private static final int MIN_CHECKING_LEVEL = 3; // the minimum level to be considered a source translation
private static final String DEFAULT_RESOURCE_SLUG = "ulb";
private static final String IMAGES_DIR = "images";
public static final String TAG = Library.class.toString();
// private static final String IMAGES_DOWNLOADED_TAG = "images_downloaded_and_extracted";
private final Indexer mAppIndex;
public static String DATABASE_NAME = "app";
private final Context mContext;
private final File mAssetsDir;
private Downloader mDownloader;
private static IndexerSQLiteHelper appIndexHelper;
/**
*
* @param context
* @param rootApiUrl the api url
* @param assetsDir the directory where binary assets are stored such as images from the api
* @throws IOException
*/
public Library(Context context, String rootApiUrl, File assetsDir) throws IOException {
initalizeHelpers(context);
mContext = context;
mAppIndex = new Indexer(context, DATABASE_NAME, appIndexHelper);
mDownloader = new Downloader(rootApiUrl);
mAssetsDir = assetsDir;
}
/**
* Initializes the static index sqlite helpers
* @param context
*/
private synchronized static void initalizeHelpers(Context context) throws IOException {
if(appIndexHelper == null) {
appIndexHelper = new IndexerSQLiteHelper(context, DATABASE_NAME);
}
}
/**
* Deletes the index and rebuilds it from scratch
* This will result in a completely empty index.
*/
public void delete() {
mAppIndex.delete();
mAppIndex.rebuild();
}
/**
* Returns the directory where project images are stored.
* Images are stored within project directories and named with the complex frame id
* examples:
* "obs/01-01.jpg" frame image
* "obs/01.jpg" chapter image
* "obs/icon.jpg" project icon
* @return
*/
public File getImagesDir() {
File file = new File(mAssetsDir, IMAGES_DIR);
file.mkdirs();
return file;
}
/**
* Imports the default index and languages into the library
*
* @param index the default application sqlite index
*
*/
public void deploy(File index) throws Exception {
appIndexHelper.close();
mContext.getDatabasePath(appIndexHelper.getDatabaseName()).delete();
File indexDest = mContext.getDatabasePath(appIndexHelper.getDatabaseName());
indexDest.getParentFile().mkdirs();
FileUtils.moveFile(index, indexDest);
appIndexHelper = null;
}
/**
* Returns the active index.
* This allows us to switch between the app index and the server index
*
* @return
*/
private Indexer getActiveIndex() {
return mAppIndex;
}
/**
* Checks if the library exists
* @return
*/
public boolean exists() {
return mAppIndex.getProjectSlugs().length > 0;
}
/**
* Downloads the chunk markers for this project from the server
* @param projectSlug
*/
private void downloadChunkMarkerList(String projectSlug) {
mDownloader.downloadChunkMarkerList(projectSlug, mAppIndex);
}
/**
* Downloads the source language catalog from the server
* @param projectId
*/
private void downloadSourceLanguageList(String projectId) {
if(mDownloader.downloadSourceLanguageList(projectId, mAppIndex)) {
for(String sourceLanguageId:mAppIndex.getSourceLanguageSlugs(projectId)) {
mDownloader.downloadResourceList(projectId, sourceLanguageId, mAppIndex);
}
}
}
/**
* Returns a list of updates that are available on the server
* @param listener an optional progress listener
* @return
*/
public LibraryUpdates checkServerForUpdates(OnProgressListener listener) {
mAppIndex.beginTransaction();
if(mDownloader.downloadProjectList(mAppIndex)) {
String[] projectIds = mAppIndex.getProjectSlugs();
for (int i = 0; i < projectIds.length; i ++) {
String projectId = projectIds[i];
downloadChunkMarkerList(projectId);
downloadSourceLanguageList(projectId);
if(listener != null) {
if(!listener.onProgress((i + 1), projectIds.length)) {
break;
}
}
}
}
if(listener != null) {
listener.onIndeterminate();
}
mAppIndex.endTransaction(true);
return getAvailableUpdates();
}
/**
* Generates the available library updates from the server and app index.
* The network is not used durring this operation.
* Only projects with downloaded source are considered eligible for updates.
* @return
*/
public LibraryUpdates getAvailableUpdates() {
LibraryUpdates updates = new LibraryUpdates();
SourceTranslation[] sourceTranslations = mAppIndex.getSourceTranslationsWithUpdates();
for(SourceTranslation sourceTranslation:sourceTranslations) {
updates.addUpdate(sourceTranslation);
}
return updates;
}
/**
* Downloads the target languages from the server
* @return
*/
public boolean downloadTargetLanguages() {
mAppIndex.beginTransaction();
boolean success = mDownloader.downloadTargetLanguages(mAppIndex);
mAppIndex.endTransaction(success);
return success;
}
/**
* Returns an array of chunk markers for the project
* @param projectSlug
* @return
*/
public ChunkMarker[] getChunkMarkers(String projectSlug) {
return mAppIndex.getChunkMarkers(projectSlug);
}
/**
* Downloads all of the projects from the server
*
* @param projectProgressListener
* @param sourceTranslationListener
* @return
*/
public Boolean downloadAllProjects(OnProgressListener projectProgressListener, OnProgressListener sourceTranslationListener) {
boolean success = true;
int currentProject = 1;
int numProjects = mAppIndex.getProjectSlugs().length;
// download
mAppIndex.beginTransaction();
for (String projectId : mAppIndex.getProjectSlugs()) {
for (String sourceLanguageId : mAppIndex.getSourceLanguageSlugs(projectId)) {
for(String resourceId : mAppIndex.getResourceSlugs(projectId, sourceLanguageId)) {
SourceTranslation sourceTranslation = SourceTranslation.simple(projectId, sourceLanguageId, resourceId);
// only download resources that meet the minimum checking level or have been downloaded before
Resource resource = getResource(sourceTranslation);
if(resource != null && resource.getCheckingLevel() < MIN_CHECKING_LEVEL) {
continue;
}
boolean downloadSuccess = startSourceTranslationDownload(sourceTranslation, sourceTranslationListener);
if(!downloadSuccess) {
Logger.w(TAG, "Failed to download " + sourceTranslation.getId());
success = false;
}
}
}
if(projectProgressListener != null) {
if(!projectProgressListener.onProgress(currentProject, numProjects)) {
break;
}
currentProject ++;
}
}
mAppIndex.endTransaction(success);
return success;
}
/**
* Downloads a source translation from the server
* @param translation
* @param listener
* @return
*/
public Boolean downloadSourceTranslation(SourceTranslation translation, OnProgressListener listener) {
mAppIndex.beginTransaction();
boolean success = startSourceTranslationDownload(translation, listener);
mAppIndex.endTransaction(success);
return success;
}
/**
* begins downloading a source translation.
* This will not start any transactions so it is safe to place in a transaction along with other queries
* This method cannot be interrupted by the thread in order to maintain data integrity
* @param sourceTranslation
* @return
*/
private Boolean startSourceTranslationDownload(SourceTranslation sourceTranslation, OnProgressListener listener) {
boolean success = true;
// source
if(mDownloader.downloadSource(sourceTranslation, mAppIndex)) {
mAppIndex.markSourceCatalogUpToDate(sourceTranslation);
} else {
success = false;
}
if(listener != null) {
listener.onProgress(1, 6);
}
// words
if(mDownloader.downloadWords(sourceTranslation, mAppIndex)) {
mAppIndex.markWordsCatalogUpToDate(sourceTranslation);
}
if(listener != null) {
listener.onProgress(2, 6);
}
// word assignments
if(mDownloader.downloadWordAssignments(sourceTranslation, mAppIndex)) {
mAppIndex.markWordAssignmentsCatalogUpToDate(sourceTranslation);
}
if(listener != null) {
listener.onProgress(3, 6);
}
// notes
if(mDownloader.downloadNotes(sourceTranslation, mAppIndex)) {
mAppIndex.markNotesCatalogUpToDate(sourceTranslation);
}
if(listener != null) {
listener.onProgress(4, 6);
}
// questions
if(mDownloader.downloadCheckingQuestions(sourceTranslation, mAppIndex)) {
mAppIndex.markQuestionsCatalogUpToDate(sourceTranslation);
}
if(listener != null) {
listener.onProgress(5, 6);
}
if(listener != null) {
listener.onProgress(6, 6);
}
// TODO: 4/5/2016 eventually ta
// Images are not downloaded here; rather, fetched on demand since they're large.
return success;
}
/**
* Downloads images from the server
* @param listener
* @return
*/
public Boolean downloadImages(OnProgressListener listener) {
boolean success = mDownloader.downloadImages(listener);
if(!success) {
FileUtils.deleteQuietly(getImagesDir());
}
return success;
}
/**
* Indicates whether the imagery download is complete and extraction was successful.
*
* <p>If any part of the process was not complete, this returns false. This method makes no
* statement as to the freshness of the information downloaded.</p>
* @return true if the download is complete
*/
public boolean hasImages() {
String[] names = getImagesDir().list(new FilenameFilter() {
@Override
public boolean accept(File dir, String filename) {
return !new File(dir, filename).isDirectory();
}
});
return names != null && names.length > 0;
}
/**
* Exports the library in a zip archive
* @param destDir the directory where the library will be exported
* @return the path to the exported file
*/
@Nullable
public File export(File destDir) {
SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
String date = s.format(new Date());
File destFile = new File(destDir, "library_" + date + ".zip");
destDir.mkdirs();
try {
destFile.createNewFile();
// Tar.tar(mContext.getDatabasePath(getActiveIndex().getIndexId()).getPath(), destFile.getPath());
Zip.zip(mContext.getDatabasePath(getActiveIndex().getIndexId()).getPath(), destFile.getPath());
} catch (IOException e) {
Logger.e(TAG, "Failed to export the library", e);
return null;
}
return destFile;
}
/**
* Exports a json array of target translations on this device
* @param targetTranslations the target translations to export as json
* @param preferredSourceLanguages the preferred source languages to return the project information in
* @return
*/
public String exportTranslationLibrary(TargetTranslation[] targetTranslations, String[] preferredSourceLanguages) {
// TODO: 10/23/2015 implement this follow structure... we might want to place this in an exporter class
// and pass in the version requested so we can support backwards compatible request but still
// upgrade the structure for newer devices.
// [
// {
// "id":"obs",
// "project":{
// "name":"Open Bible Stories",
// "description":"The Bible in 50 stories",
// "meta":[]
// },
// "language":{
// "slug":"en",
// "name":"English",
// "direction":"rtl"
// },
// "target_languages":[
// {
// "slug":"de",
// "name":"Deutsch",
// "direction":"rtl"
// },
// ...
// ]
// }
// ]
return "[]";
}
/**
* Returns an array of target languages
* @return
*/
public TargetLanguage[] getTargetLanguages() {
return getActiveIndex().getTargetLanguages();
}
/**
* Returns an array of projects
*
* @param sourceLanguageSlug the preferred language in which the project names will be returned. The default is english
* @return
*/
public Project[] getProjects(String sourceLanguageSlug) {
return mAppIndex.getProjects(sourceLanguageSlug);
}
/**
* Returns a list of project categories
* Only projects with source are included
*
* @param languageId the preferred language in which the category names will be returned. The default is english
* @return
*/
public ProjectCategory[] getProjectCategories(String languageId) {
return getProjectCategories(new ProjectCategory(null, null, null, languageId, 0l));
}
/**
* Returns a list of project categories beneath the given category
* Only projects with source are included
*
* @param parentCategory
* @return
*/
public ProjectCategory[] getProjectCategories(ProjectCategory parentCategory) {
return mAppIndex.getCategoryBranch(parentCategory.sourcelanguageId, parentCategory.parentCategoryId);
}
/**
* Returns the preferred source language if it exists
*
* If the source language does not exist it will default to english.
* If english does not exist it will return the first available source language (this happens in the query)
*
* @param projectId
* @param sourceLanguageSlug
* @return null if no source langauges exist for the project
*/
@Nullable
public SourceLanguage getPreferredSourceLanguage(String projectId, String sourceLanguageSlug) {
// preferred language
SourceLanguage sourceLanguage = getActiveIndex().getSourceLanguage(projectId, sourceLanguageSlug);
// try to use default (en)
if (sourceLanguage == null || (!sourceLanguage.code.equals(sourceLanguageSlug) && !sourceLanguageSlug.equals("en"))) {
sourceLanguage = getActiveIndex().getSourceLanguage(projectId, "en");
}
return sourceLanguage;
}
/**
* Returns an array of source languages for the project
* @param projectSlug the id of the project who's source languages will be returned
* @return
*/
public SourceLanguage[] getSourceLanguages(String projectSlug) {
return getActiveIndex().getSourceLanguages(projectSlug);
}
/**
* Returns a source language in the project
* @param projectId
* @param sourceLanguageId
* @return
*/
public SourceLanguage getSourceLanguage(String projectId, String sourceLanguageId) {
return getActiveIndex().getSourceLanguage(projectId, sourceLanguageId);
}
/**
* Returns an array of resources for the source language
* @param projectSlug
* @param sourceLanguageSlug
* @return
*/
public Resource[] getResources(String projectSlug, String sourceLanguageSlug) {
return getActiveIndex().getResources(projectSlug, sourceLanguageSlug);
}
/**
* Returns a resource
* @param sourceTranslation
* @return
*/
public Resource getResource(SourceTranslation sourceTranslation) {
return getActiveIndex().getResource(SourceTranslation.simple(sourceTranslation.projectSlug, sourceTranslation.sourceLanguageSlug, sourceTranslation.resourceSlug));
}
/**
* Returns the project with the information provided in the preferred source language
* If the source language does not exist it will use the default language
*
* @param projectId
* @param languageId the language in which the project information should be returned
* @return
*/
public Project getProject(String projectId, String languageId) {
return getActiveIndex().getProject(projectId, languageId);
}
/**
* Calculates the progress of a target translation.
* This can take some time so don't run this on the main thread
* @param targetTranslation
* @return
*/
public float getTranslationProgress(TargetTranslation targetTranslation) {
int numFinishedItems = targetTranslation.numFinished();
SourceLanguage sourceLanguage = getPreferredSourceLanguage(targetTranslation.getProjectId(), Locale.getDefault().getLanguage());
if(sourceLanguage != null) {
SourceTranslation sourceTranslation = getDefaultSourceTranslation(targetTranslation.getProjectId(), sourceLanguage.getId());
int numAvailableTranslations = mAppIndex.numTranslatable(sourceTranslation);
if (numAvailableTranslations > 0) {
return (float) numFinishedItems / (float) numAvailableTranslations;
} else {
return 0;
}
} else {
Logger.w(TAG, "Cannot get progress of " + targetTranslation.getId() + " because a source language does not exist");
return 0;
}
}
/**
* Returns an array of chapters for the source translation
* @param sourceTranslation
* @return
*/
public Chapter[] getChapters(SourceTranslation sourceTranslation) {
return getActiveIndex().getChapters(sourceTranslation);
}
/**
* Returns a single chapter
* @param sourceTranslation
* @param chapterId
* @return
*/
@Nullable
public Chapter getChapter(SourceTranslation sourceTranslation, String chapterId) {
return getActiveIndex().getChapter(sourceTranslation, chapterId);
}
/**
* Returns an array of frames in the chapter
* @param sourceTranslation
* @param chapterSlug
* @return
*/
public Frame[] getFrames(SourceTranslation sourceTranslation, String chapterSlug) {
List<Frame> frames = new ArrayList<>(Arrays.asList(getActiveIndex().getFrames(sourceTranslation, chapterSlug)));
if(frames.size() > 0) {
// TRICKY: a bug in the v2 api gives the last frame in the last chapter and id of 00 which messes up the sorting
Frame firstFrame = frames.get(0);
if (Integer.parseInt(firstFrame.getId()) == 0) {
frames.remove(0);
frames.add(firstFrame);
}
return frames.toArray(new Frame[frames.size()]);
} else {
return new Frame[0];
}
}
/**
* Returns an array of frame slugs
* @param sourceTranslation
* @param chapterSlug
* @return
*/
public String[] getFrameSlugs(SourceTranslation sourceTranslation, String chapterSlug) {
List<String> frameSlugs = new ArrayList<>(Arrays.asList(getActiveIndex().getFrameSlugs(sourceTranslation, chapterSlug)));
if(frameSlugs.size() > 0) {
// TRICKY: a bug in the v2 api gives the last frame in the last chapter and id of 00 which messes up the sorting
String firstFrame = frameSlugs.get(0);
if (Integer.parseInt(firstFrame) == 0) {
frameSlugs.remove(0);
frameSlugs.add(firstFrame);
}
return frameSlugs.toArray(new String[frameSlugs.size()]);
} else {
return new String[0];
}
}
/**
* Returns a single frame
* @param sourceTranslation
* @param chapterId
* @param frameId
* @return
*/
@Nullable
public Frame getFrame(SourceTranslation sourceTranslation, String chapterId, String frameId) {
if(frameId == null || chapterId == null) {
return null;
}
return getActiveIndex().getFrame(sourceTranslation, chapterId, frameId);
}
/**
* Returns a single target language
* @param targetLanguageId
* @return
*/
public TargetLanguage getTargetLanguage(String targetLanguageId) {
return getActiveIndex().getTargetLanguage(targetLanguageId);
}
/**
* Returns the source translation by it's id
* @param sourceTranslationId
* @return null if the source translation does not exist
*/
@Nullable
public SourceTranslation getSourceTranslation(String sourceTranslationId) {
if(sourceTranslationId != null) {
String projectId = SourceTranslation.getProjectIdFromId(sourceTranslationId);
String sourceLanguageId = SourceTranslation.getSourceLanguageIdFromId(sourceTranslationId);
String resourceId = SourceTranslation.getResourceIdFromId(sourceTranslationId);
return getSourceTranslation(projectId, sourceLanguageId, resourceId);
}
return null;
}
/**
* Returns the source translation
* @param projectSlug
* @param sourceLanguageSlug
* @param resourceSlug
* @return null if the source translation does not exist
*/
@Nullable
public SourceTranslation getSourceTranslation(String projectSlug, String sourceLanguageSlug, String resourceSlug) {
return getActiveIndex().getSourceTranslation(projectSlug, sourceLanguageSlug, resourceSlug);
}
/**
* Returns the source translation with the default resource.
* If the default resource does not exist it will use the first available resource
*
* @param projectSlug
* @param sourceLanguageSlug
* @return null if the source translation does not exist
*/
@Nullable
public SourceTranslation getDefaultSourceTranslation(String projectSlug, String sourceLanguageSlug) {
SourceTranslation sourceTranslation = mAppIndex.getSourceTranslation(projectSlug, sourceLanguageSlug, DEFAULT_RESOURCE_SLUG);
if(sourceTranslation == null) {
String[] resourceSlugs = mAppIndex.getResourceSlugs(projectSlug, sourceLanguageSlug);
if(resourceSlugs.length > 0) {
return mAppIndex.getSourceTranslation(projectSlug, sourceLanguageSlug, resourceSlugs[0]);
}
} else {
return sourceTranslation;
}
return null;
}
/**
* Returns an array of source translations in a project that have met the minimum checking level
* @param projectId
*/
public SourceTranslation[] getSourceTranslations(String projectId) {
// TODO: write a query for this
List<SourceTranslation> sourceTranslations = new ArrayList<>();
String[] sourceLanguageIds = getActiveIndex().getSourceLanguageSlugs(projectId);
for(String sourceLanguageId:sourceLanguageIds) {
String[] resourceIds = getActiveIndex().getResourceSlugs(projectId, sourceLanguageId);
for(String resourceId:resourceIds) {
SourceTranslation sourceTranslation = getSourceTranslation(projectId, sourceLanguageId, resourceId);
if(sourceTranslation != null && sourceTranslation.getCheckingLevel() >= MIN_CHECKING_LEVEL) {
sourceTranslations.add(sourceTranslation);
}
}
}
return sourceTranslations.toArray(new SourceTranslation[sourceTranslations.size()]);
}
/**
* Returns an array of source translations in a project that have not yet met the minimum checking level
*
* @param projectId
*/
public SourceTranslation[] getDraftTranslations(String projectId) {
List<SourceTranslation> draftTranslations = new ArrayList<>();
String[] sourceLanguageIds = getActiveIndex().getSourceLanguageSlugs(projectId);
for(String sourceLanguageId:sourceLanguageIds) {
draftTranslations.addAll(getDraftTranslations(projectId, sourceLanguageId));
}
return draftTranslations.toArray(new SourceTranslation[draftTranslations.size()]);
}
/**
* Returns an array of source translations in a project and source language that have not yet met the minimum checking level
*
* @param projectId
* @param sourceLanguageId
* @return
*/
public List<SourceTranslation> getDraftTranslations(String projectId, String sourceLanguageId) {
List<SourceTranslation> draftTranslations = new ArrayList<>();
String[] resourceIds = getActiveIndex().getResourceSlugs(projectId, sourceLanguageId);
for(String resourceId:resourceIds) {
SourceTranslation sourceTranslation = getDraftTranslation(projectId, sourceLanguageId, resourceId);
if(sourceTranslation != null) {
draftTranslations.add(sourceTranslation);
}
}
return draftTranslations;
}
/**
* Returns a source translation that has not yet met the minimum checking level.
*
* @param projectId
* @param sourceLanguageId
* @param resourceId
* @return
*/
public SourceTranslation getDraftTranslation(String projectId, String sourceLanguageId, String resourceId) {
SourceTranslation sourceTranslation = getSourceTranslation(projectId, sourceLanguageId, resourceId);
if(sourceTranslation != null && sourceTranslation.getCheckingLevel() < MIN_CHECKING_LEVEL) {
return sourceTranslation;
} else {
return null;
}
}
/**
* Returns the source translation that has not yet met the minimum checking level
* @param sourceTranslationId
* @return
*/
public SourceTranslation getDraftTranslation(String sourceTranslationId) {
SourceTranslation sourceTranslation = getSourceTranslation(sourceTranslationId);
if(sourceTranslation != null && sourceTranslation.getCheckingLevel() < MIN_CHECKING_LEVEL) {
return sourceTranslation;
} else {
return null;
}
}
/**
* Returns an array of notes in the frame
* @param sourceTranslation
* @param chapterSlug
* @param frameSlug
* @return
*/
public TranslationNote[] getTranslationNotes(SourceTranslation sourceTranslation, String chapterSlug, String frameSlug) {
return mAppIndex.getTranslationNotes(sourceTranslation, chapterSlug, frameSlug);
}
/**
* Returns a single translation note
* @param sourceTranslation
* @param chapterId
* @param frameId
* @param translationNoteId
* @return
*/
public TranslationNote getTranslationNote(SourceTranslation sourceTranslation, String chapterId, String frameId, String translationNoteId) {
return getActiveIndex().getNote(sourceTranslation, chapterId, frameId, translationNoteId);
}
/**
* Returns an array of translation words in the source translation
* @param sourceTranslation
* @return
*/
public TranslationWord[] getTranslationWords(SourceTranslation sourceTranslation) {
return getActiveIndex().getWords(sourceTranslation);
}
/**
* Returns an array of translation words in the frame
* @param sourceTranslation
* @param chapterId
* @param frameId
* @return
*/
public TranslationWord[] getTranslationWords(SourceTranslation sourceTranslation, String chapterId, String frameId) {
return getActiveIndex().getWordsForFrame(sourceTranslation, chapterId, frameId);
}
/**
* Returns a translation word from the source translation
* @param sourceTranslation
* @param translationWordId
* @return
*/
public TranslationWord getTranslationWord(SourceTranslation sourceTranslation, String translationWordId) {
return getActiveIndex().getWord(sourceTranslation, translationWordId);
}
/**
* Returns a translation academy entry from the source translation
* @param sourceTranslation
* @param volume
*@param manual @return
*/
public TranslationArticle getTranslationArticle(SourceTranslation sourceTranslation, String volume, String manual, String articleId) {
return getActiveIndex().getTranslationArticle(sourceTranslation, volume, manual, articleId);
}
/**
* Returns an array of checking questions in the frame
* @param sourceTranslation
* @param chapterId
* @param frameId
* @return
*/
public CheckingQuestion[] getCheckingQuestions(SourceTranslation sourceTranslation, String chapterId, String frameId) {
return getActiveIndex().getCheckingQuestions(sourceTranslation, chapterId, frameId);
}
/**
* Returns a checking question in the frame
* @param sourceTranslation
* @param chapterId
* @param frameId
* @param checkingQuestionId
* @return
*/
public CheckingQuestion getCheckingQuestion(SourceTranslation sourceTranslation, String chapterId, String frameId, String checkingQuestionId) {
return getActiveIndex().getCheckingQuestion(sourceTranslation, chapterId, frameId, checkingQuestionId);
}
/**
* Checks if the project has any source downloaded
*
* @param projectId
* @return
*/
public boolean projectHasSource(String projectId) {
String[] sourceLanguageIds = getActiveIndex().getSourceLanguageSlugs(projectId);
for(String sourceLanguageId:sourceLanguageIds) {
if(sourceLanguageHasSource(projectId, sourceLanguageId)) {
return true;
}
}
return false;
}
/**
* Checks if the source language has any source downloaded
* @param projectId
* @param sourceLanguageId
* @return
*/
public boolean sourceLanguageHasSource(String projectId, String sourceLanguageId) {
String[] resourceIds = getActiveIndex().getResourceSlugs(projectId, sourceLanguageId);
for(String resourceId:resourceIds) {
if(sourceTranslationHasSource(SourceTranslation.simple(projectId, sourceLanguageId, resourceId))) {
return true;
}
}
return false;
}
/**
* Checks if the source translation has any source downloaded
* @param sourceTranslation
* @return
*/
public boolean sourceTranslationHasSource(SourceTranslation sourceTranslation) {
return getActiveIndex().getChapterSlugs(sourceTranslation).length > 0;
}
/**
* Deletes a project from the library
* @param projectId
*/
public void deleteProject(String projectId) {
String[] sourceLanguageIds = mAppIndex.getSourceLanguageSlugs(projectId);
mAppIndex.beginTransaction();
for(String sourceLanguageId:sourceLanguageIds) {
Resource[] resources = mAppIndex.getResources(projectId, sourceLanguageId);
for(Resource r:resources) {
mAppIndex.deleteResource(r.getDBId());
// restore resource after cascade delete
mAppIndex.saveResource(r, r.getSourceLanguageDBId());
}
// mAppIndex.deleteSourceLanguage(projectId, sourceLanguageId);
}
mAppIndex.endTransaction(true);
// mAppIndex.deleteProject(projectId);
}
/**
* Returns the number of target languages without loading them from the disk.
* @return
*/
public int getTargetLanguagesLength() {
return getActiveIndex().getNumTargetLanguages();
}
/**
* Downloads updates from the server
* @param updates
* @param listener
* @return
*/
public boolean downloadUpdates(LibraryUpdates updates, OnProgressListener listener) {
mAppIndex.beginTransaction();
int progress = 0;
int numUpdates = updates.numSourceTranslationUpdates();
String[] projectSlugs = updates.getUpdatedProjects();
outerloop:
for(String projectSlug:projectSlugs) {
String[] sourceLanguageSlugs = updates.getUpdatedSourceLanguages(projectSlug);
for(String sourceLanguageSlug:sourceLanguageSlugs) {
String[] resourceSlugs = updates.getUpdatedResources(projectSlug, sourceLanguageSlug);
for(String resourceSlug:resourceSlugs) {
if(startSourceTranslationDownload(SourceTranslation.simple(projectSlug, sourceLanguageSlug, resourceSlug), null)) {
updates.removeSourceLanguageUpdate(projectSlug, sourceLanguageSlug);
}
if(listener != null) {
progress ++;
if(!listener.onProgress(progress, numUpdates)) {
break outerloop;
}
}
}
}
}
mAppIndex.endTransaction(true);
return true;
}
/**
* Returns the format of the chapter body
* @param mSourceTranslation
* @param chapterSlug
* @return
*/
public TranslationFormat getChapterBodyFormat(SourceTranslation mSourceTranslation, String chapterSlug) {
return mAppIndex.getChapterBodyFormat(mSourceTranslation, chapterSlug);
}
/**
* Returns the body of the entire chapter.
* This concats all the frame body's together
* @param mSourceTranslation
* @param chapterSlug
* @return
*/
public String getChapterBody(SourceTranslation mSourceTranslation, String chapterSlug) {
return mAppIndex.getChapterBody(mSourceTranslation, chapterSlug);
}
/**
* Returns a target language by searching for it's human readable name
* @param name
* @return
*/
public TargetLanguage findTargetLanguageByName(String name) {
return getActiveIndex().getTargetLanguageByName(name);
}
/**
* This is a temporary method so we can index tA.
* tA is not currently available in the api so we bundle it and index it manually
* @param sourceTranslation
* @param catalog
* @return
* @deprecated you probably shouldn't use this method
*/
public boolean manuallyIndexTranslationAcademy(SourceTranslation sourceTranslation, String catalog) {
getActiveIndex().beginTransaction();
boolean success = getActiveIndex().indexTranslationAcademy(sourceTranslation, catalog);
getActiveIndex().endTransaction(success);
return success;
}
/**
* This is a temporary method so we can index chunk markers
* Chunk markers are not currently available in the api so we must inject the catalog urls manually
* @return
* @deprecated you probably shouldn't use this method
*/
public boolean manuallyInjectChunkMarkerCatalogUrl() {
getActiveIndex().beginTransaction();
boolean success = false;
try {
success = getActiveIndex().manuallyInjectChunkMarkerUrls();
} catch (Exception e) {
e.printStackTrace();
}
getActiveIndex().endTransaction(success);
return success;
}
/**
* Resets all the date_modified values
*/
public void setExpired() {
mAppIndex.setExpired();
}
public interface OnProgressListener {
/**
* Progress the progress on an operation between 0 and max
* @param progress
* @param max
* @return the process should stop if returns false
*/
boolean onProgress(int progress, int max);
/**
* Identifes the current task as not quantifiable
* @return the process should stop if returns false
*/
boolean onIndeterminate();
}
}