Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a lifecycle aware async task instead of a loader to open collection #4972

Merged
merged 2 commits into from
Sep 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions AnkiDroid/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,12 @@ task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'crea

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.0.2'
def support_lib_version = '27.1.1'
implementation "com.android.support:appcompat-v7:$support_lib_version"
// Note: the design support library can be quite buggy, so test everything thoroughly before updating it
implementation 'com.android.support:design:27.0.2'
implementation 'com.android.support:customtabs:27.0.2'
implementation 'com.android.support:recyclerview-v7:27.0.2'
implementation "com.android.support:design:$support_lib_version"
implementation "com.android.support:customtabs:$support_lib_version"
implementation "com.android.support:recyclerview-v7:$support_lib_version"
implementation 'io.requery:sqlite-android:3.24.0'
implementation 'android.arch.persistence:db:1.1.1'
implementation 'com.afollestad.material-dialogs:core:0.9.6.0'
Expand Down
60 changes: 17 additions & 43 deletions AnkiDroid/src/main/java/com/ichi2/anki/AnkiActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.view.View;
Expand All @@ -38,8 +36,7 @@

import timber.log.Timber;

public class AnkiActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Collection>,
SimpleMessageDialog.SimpleMessageDialogListener {
public class AnkiActivity extends AppCompatActivity implements SimpleMessageDialog.SimpleMessageDialogListener {

public final int SIMPLE_NOTIFICATION_ID = 0;
public static final int REQUEST_REVIEW = 901;
Expand Down Expand Up @@ -97,6 +94,7 @@ public boolean onOptionsItemSelected(MenuItem item) {

// called when the CollectionLoader finishes... usually will be over-ridden
protected void onCollectionLoaded(Collection col) {
hideProgressBar();
}


Expand Down Expand Up @@ -250,49 +248,25 @@ private void enableActivityAnimation(int animation) {

// Method for loading the collection which is inherited by all AnkiActivitys
public void startLoadingCollection() {
// Initialize the open collection loader
Timber.d("AnkiActivity.startLoadingCollection()");
if (!colIsOpen()) {
showProgressBar();
if (colIsOpen()) {
onCollectionLoaded(getCol());
return;
}
getSupportLoaderManager().restartLoader(0, null, this);
}


// Kick user back to DeckPicker on collection load error unless this method is overridden
protected void onCollectionLoadError() {
Intent deckPicker = new Intent(this, DeckPicker.class);
deckPicker.putExtra("collectionLoadError", true); // don't currently do anything with this
deckPicker.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityWithAnimation(deckPicker, ActivityTransitionAnimation.LEFT);
}


// CollectionLoader Listener callbacks
@Override
public Loader<Collection> onCreateLoader(int id, Bundle args) {
// Currently only using one loader, so ignore id
return new CollectionLoader(this);
}


@Override
public void onLoadFinished(Loader<Collection> loader, Collection col) {
hideProgressBar();
if (col != null && colIsOpen()) {
onCollectionLoaded(col);
} else {
onCollectionLoadError();
}
}


@Override
public void onLoaderReset(Loader<Collection> arg0) {
// We don't currently retain any references, so no need to free any data here
// Open collection asynchronously if it hasn't already been opened
showProgressBar();
CollectionLoader.load(this, col -> {
if (col != null) {
onCollectionLoaded(col);
} else {
Intent deckPicker = new Intent(this, DeckPicker.class);
deckPicker.putExtra("collectionLoadError", true); // don't currently do anything with this
deckPicker.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityWithAnimation(deckPicker, ActivityTransitionAnimation.LEFT);
}
});
}


public void showProgressBar() {
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
if (progressBar != null) {
Expand Down
10 changes: 4 additions & 6 deletions AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.java
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ private void onFinishedStartup() {
getCol().modSchema();
} catch (ConfirmModSchemaException e) {
// If libanki determines it's necessary to confirm the full sync then show a confirmation dialog
// We have to show the dialog via the DialogHandler since this method is called via a Loader
// We have to show the dialog via the DialogHandler since this method is called via an async task
Resources res = getResources();
Message handlerMessage = Message.obtain();
handlerMessage.what = DialogHandler.MSG_SHOW_FORCE_FULL_SYNC_DIALOG;
Expand All @@ -841,12 +841,10 @@ private void onFinishedStartup() {
automaticSync();
}

@Override
protected void onCollectionLoadError() {
private void showCollectionErrorDialog() {
getDialogHandler().sendEmptyMessage(DialogHandler.MSG_SHOW_COLLECTION_LOADING_ERROR_DIALOG);
}


public void addNote() {
Intent intent = new Intent(DeckPicker.this, NoteEditor.class);
intent.putExtra(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_DECKPICKER);
Expand Down Expand Up @@ -1144,7 +1142,7 @@ public void onPostExecute(DeckTask.TaskData result) {
}
if (result == null || !result.getBoolean()) {
UIUtils.showThemedToast(DeckPicker.this, getResources().getString(R.string.deck_repair_error), true);
onCollectionLoadError();
showCollectionErrorDialog();
}
}

Expand Down Expand Up @@ -1871,7 +1869,7 @@ public void onPostExecute(TaskData result) {
}
if (result == null) {
Timber.e("null result loading deck counts");
onCollectionLoadError();
showCollectionErrorDialog();
return;
}
List<Sched.DeckDueTreeNode> nodes = (List<Sched.DeckDueTreeNode>) result.getObjArray()[0];
Expand Down
75 changes: 31 additions & 44 deletions AnkiDroid/src/main/java/com/ichi2/async/CollectionLoader.java
Original file line number Diff line number Diff line change
@@ -1,70 +1,57 @@
package com.ichi2.async;

import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleOwner;
import android.os.AsyncTask;

import com.ichi2.anki.AnkiDroidApp;
import com.ichi2.anki.CollectionHelper;
import com.ichi2.libanki.Collection;

import timber.log.Timber;

public class CollectionLoader extends AsyncTaskLoader<Collection> {
public final class CollectionLoader extends AsyncTask<Void, Void, Collection> {
private LifecycleOwner mLifecycleOwner;
private Callback mCallback;

public CollectionLoader(Context context) {
super(context);
public interface Callback {
mikehardy marked this conversation as resolved.
Show resolved Hide resolved
void execute(Collection col);
}

public static void load(LifecycleOwner lifecycleOwner, Callback callback) {
CollectionLoader loader = new CollectionLoader(lifecycleOwner, callback);
loader.execute();
}

private CollectionLoader(LifecycleOwner lifecycleOwner, Callback callback) {
mLifecycleOwner = lifecycleOwner;
mCallback = callback;
}

@Override
public Collection loadInBackground() {
protected Collection doInBackground(Void... params) {
// Don't touch collection if lockCollection flag is set
if (CollectionHelper.getInstance().isCollectionLocked()) {
Timber.w("onStartLoading() :: Another thread has requested to keep the collection closed.");
return null;
}
// load collection
try {
Timber.d("CollectionLoader accessing collection");
return CollectionHelper.getInstance().getCol(getContext());
return CollectionHelper.getInstance().getCol(AnkiDroidApp.getInstance().getApplicationContext());
} catch (RuntimeException e) {
Timber.e(e, "loadInBackground - RuntimeException on opening collection");
AnkiDroidApp.sendExceptionReport(e, "CollectionLoader.loadInBackground");
return null;
}
}

@Override
public void deliverResult(Collection col) {
Timber.d("CollectionLoader.deliverResult()");
// Loader has been reset so don't forward data to listener
if (isReset()) {
if (col != null) {
return;
}
}
// Loader is running so forward data to listener
if (isStarted()) {
super.deliverResult(col);
}
}


@Override
protected void onStartLoading() {
// Don't touch collection if lockCollection flag is set
if (CollectionHelper.getInstance().isCollectionLocked()) {
Timber.w("onStartLoading() :: Another thread has requested to keep the collection closed.");
return;
protected void onPostExecute(Collection col) {
super.onPostExecute(col);
if (mLifecycleOwner.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.CREATED)) {
mCallback.execute(col);
}
// Since the CollectionHelper only opens if necessary, we can just force every time
forceLoad();
}

@Override
protected void onStopLoading() {
// The Loader has been put in a stopped state, so we should attempt to cancel the current load (if there is one).
Timber.d("CollectionLoader.onStopLoading()");
cancelLoad();
}

@Override
protected void onReset() {
// Ensure the loader is stopped.
Timber.d("CollectionLoader.onReset()");
onStopLoading();
}

}