mirror of
https://github.com/nextcloud/notes-android.git
synced 2024-10-22 20:55:44 +03:00
#761 Import notes one by one to avoid read timeout for the first sync
Signed-off-by: Stefan Niedermann <info@niedermann.it>
This commit is contained in:
parent
411c61c21d
commit
5801f00bfb
13 changed files with 214 additions and 19 deletions
|
@ -96,7 +96,7 @@ public class ImportAccountActivity extends AppCompatActivity {
|
||||||
Log.i(TAG, "Loading capabilities for " + ssoAccount.name);
|
Log.i(TAG, "Loading capabilities for " + ssoAccount.name);
|
||||||
final var capabilities = CapabilitiesClient.getCapabilities(getApplicationContext(), ssoAccount, null, ApiProvider.getInstance());
|
final var capabilities = CapabilitiesClient.getCapabilities(getApplicationContext(), ssoAccount, null, ApiProvider.getInstance());
|
||||||
final String displayName = CapabilitiesClient.getDisplayName(getApplicationContext(), ssoAccount, ApiProvider.getInstance());
|
final String displayName = CapabilitiesClient.getDisplayName(getApplicationContext(), ssoAccount, ApiProvider.getInstance());
|
||||||
importAccountViewModel.addAccount(ssoAccount.url, ssoAccount.userId, ssoAccount.name, capabilities, displayName, new IResponseCallback<Account>() {
|
final var status$ = importAccountViewModel.addAccount(ssoAccount.url, ssoAccount.userId, ssoAccount.name, capabilities, displayName, new IResponseCallback<>() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update syncing when adding account
|
* Update syncing when adding account
|
||||||
|
@ -123,6 +123,16 @@ public class ImportAccountActivity extends AppCompatActivity {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
runOnUiThread(() -> status$.observe(ImportAccountActivity.this, (status) -> {
|
||||||
|
binding.progressText.setVisibility(View.VISIBLE);
|
||||||
|
Log.v(TAG, "Status: " + status.count + " of " + status.total);
|
||||||
|
if(status.count > 0) {
|
||||||
|
binding.progressCircular.setIndeterminate(false);
|
||||||
|
}
|
||||||
|
binding.progressText.setText(getString(R.string.progress_import, status.count + 1, status.total));
|
||||||
|
binding.progressCircular.setProgress(status.count);
|
||||||
|
binding.progressCircular.setMax(status.total);
|
||||||
|
}));
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
t.printStackTrace();
|
t.printStackTrace();
|
||||||
ApiProvider.getInstance().invalidateAPICache(ssoAccount);
|
ApiProvider.getInstance().invalidateAPICache(ssoAccount);
|
||||||
|
@ -162,6 +172,7 @@ public class ImportAccountActivity extends AppCompatActivity {
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
binding.addButton.setEnabled(true);
|
binding.addButton.setEnabled(true);
|
||||||
binding.progressCircular.setVisibility(View.GONE);
|
binding.progressCircular.setVisibility(View.GONE);
|
||||||
|
binding.progressText.setVisibility(View.GONE);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,11 +11,10 @@ import it.niedermann.owncloud.notes.persistence.NotesRepository;
|
||||||
import it.niedermann.owncloud.notes.persistence.entity.Account;
|
import it.niedermann.owncloud.notes.persistence.entity.Account;
|
||||||
import it.niedermann.owncloud.notes.shared.model.Capabilities;
|
import it.niedermann.owncloud.notes.shared.model.Capabilities;
|
||||||
import it.niedermann.owncloud.notes.shared.model.IResponseCallback;
|
import it.niedermann.owncloud.notes.shared.model.IResponseCallback;
|
||||||
|
import it.niedermann.owncloud.notes.shared.model.ImportStatus;
|
||||||
|
|
||||||
public class ImportAccountViewModel extends AndroidViewModel {
|
public class ImportAccountViewModel extends AndroidViewModel {
|
||||||
|
|
||||||
private static final String TAG = ImportAccountViewModel.class.getSimpleName();
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final NotesRepository repo;
|
private final NotesRepository repo;
|
||||||
|
|
||||||
|
@ -24,7 +23,7 @@ public class ImportAccountViewModel extends AndroidViewModel {
|
||||||
this.repo = NotesRepository.getInstance(application);
|
this.repo = NotesRepository.getInstance(application);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAccount(@NonNull String url, @NonNull String username, @NonNull String accountName, @NonNull Capabilities capabilities, @Nullable String displayName, @NonNull IResponseCallback<Account> callback) {
|
public LiveData<ImportStatus> addAccount(@NonNull String url, @NonNull String username, @NonNull String accountName, @NonNull Capabilities capabilities, @Nullable String displayName, @NonNull IResponseCallback<Account> callback) {
|
||||||
repo.addAccount(url, username, accountName, capabilities, displayName, callback);
|
return repo.addAccount(url, username, accountName, capabilities, displayName, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -668,27 +668,41 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
|
||||||
AccountImporter.onActivityResult(requestCode, resultCode, data, this, (ssoAccount) -> {
|
AccountImporter.onActivityResult(requestCode, resultCode, data, this, (ssoAccount) -> {
|
||||||
CapabilitiesWorker.update(this);
|
CapabilitiesWorker.update(this);
|
||||||
executor.submit(() -> {
|
executor.submit(() -> {
|
||||||
|
final var importSnackbar = BrandedSnackbar.make(binding.drawerLayout, R.string.progress_import_indeterminate, Snackbar.LENGTH_INDEFINITE);
|
||||||
Log.i(TAG, "Added account: " + "name:" + ssoAccount.name + ", " + ssoAccount.url + ", userId" + ssoAccount.userId);
|
Log.i(TAG, "Added account: " + "name:" + ssoAccount.name + ", " + ssoAccount.url + ", userId" + ssoAccount.userId);
|
||||||
try {
|
try {
|
||||||
Log.i(TAG, "Refreshing capabilities for " + ssoAccount.name);
|
Log.i(TAG, "Refreshing capabilities for " + ssoAccount.name);
|
||||||
final var capabilities = CapabilitiesClient.getCapabilities(getApplicationContext(), ssoAccount, null, ApiProvider.getInstance());
|
final var capabilities = CapabilitiesClient.getCapabilities(getApplicationContext(), ssoAccount, null, ApiProvider.getInstance());
|
||||||
final String displayName = CapabilitiesClient.getDisplayName(getApplicationContext(), ssoAccount, ApiProvider.getInstance());
|
final String displayName = CapabilitiesClient.getDisplayName(getApplicationContext(), ssoAccount, ApiProvider.getInstance());
|
||||||
mainViewModel.addAccount(ssoAccount.url, ssoAccount.userId, ssoAccount.name, capabilities, displayName, new IResponseCallback<Account>() {
|
final var status$ = mainViewModel.addAccount(ssoAccount.url, ssoAccount.userId, ssoAccount.name, capabilities, displayName, new IResponseCallback<Account>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account result) {
|
public void onSuccess(Account result) {
|
||||||
executor.submit(() -> {
|
executor.submit(() -> {
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
importSnackbar.setText(R.string.account_imported);
|
||||||
|
importSnackbar.setAction(R.string.simple_switch, (v) -> mainViewModel.postCurrentAccount(mainViewModel.getLocalAccountByAccountName(ssoAccount.name)));
|
||||||
|
});
|
||||||
Log.i(TAG, capabilities.toString());
|
Log.i(TAG, capabilities.toString());
|
||||||
final var a = mainViewModel.getLocalAccountByAccountName(ssoAccount.name);
|
|
||||||
runOnUiThread(() -> mainViewModel.postCurrentAccount(a));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(@NonNull Throwable t) {
|
public void onError(@NonNull Throwable t) {
|
||||||
runOnUiThread(() -> ExceptionDialogFragment.newInstance(t).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName()));
|
runOnUiThread(() -> {
|
||||||
|
importSnackbar.dismiss();
|
||||||
|
ExceptionDialogFragment.newInstance(t).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
runOnUiThread(() -> status$.observe(this, (status) -> {
|
||||||
|
importSnackbar.show();
|
||||||
|
Log.v(TAG, "Status: " + status.count + " of " + status.total);
|
||||||
|
if(status.count > 0) {
|
||||||
|
importSnackbar.setText(getString(R.string.progress_import, status.count + 1, status.total));
|
||||||
|
}
|
||||||
|
}));
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
importSnackbar.dismiss();
|
||||||
ApiProvider.getInstance().invalidateAPICache(ssoAccount);
|
ApiProvider.getInstance().invalidateAPICache(ssoAccount);
|
||||||
// Happens when importing an already existing account the second time
|
// Happens when importing an already existing account the second time
|
||||||
if (e instanceof TokenMismatchException && mainViewModel.getLocalAccountByAccountName(ssoAccount.name) != null) {
|
if (e instanceof TokenMismatchException && mainViewModel.getLocalAccountByAccountName(ssoAccount.name) != null) {
|
||||||
|
|
|
@ -46,6 +46,7 @@ import it.niedermann.owncloud.notes.persistence.entity.SingleNoteWidgetData;
|
||||||
import it.niedermann.owncloud.notes.shared.model.Capabilities;
|
import it.niedermann.owncloud.notes.shared.model.Capabilities;
|
||||||
import it.niedermann.owncloud.notes.shared.model.CategorySortingMethod;
|
import it.niedermann.owncloud.notes.shared.model.CategorySortingMethod;
|
||||||
import it.niedermann.owncloud.notes.shared.model.IResponseCallback;
|
import it.niedermann.owncloud.notes.shared.model.IResponseCallback;
|
||||||
|
import it.niedermann.owncloud.notes.shared.model.ImportStatus;
|
||||||
import it.niedermann.owncloud.notes.shared.model.Item;
|
import it.niedermann.owncloud.notes.shared.model.Item;
|
||||||
import it.niedermann.owncloud.notes.shared.model.NavigationCategory;
|
import it.niedermann.owncloud.notes.shared.model.NavigationCategory;
|
||||||
|
|
||||||
|
@ -538,8 +539,8 @@ public class MainViewModel extends AndroidViewModel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAccount(@NonNull String url, @NonNull String username, @NonNull String accountName, @NonNull Capabilities capabilities, @Nullable String displayName, @NonNull IResponseCallback<Account> callback) {
|
public LiveData<ImportStatus> addAccount(@NonNull String url, @NonNull String username, @NonNull String accountName, @NonNull Capabilities capabilities, @Nullable String displayName, @NonNull IResponseCallback<Account> callback) {
|
||||||
repo.addAccount(url, username, accountName, capabilities, displayName, callback);
|
return repo.addAccount(url, username, accountName, capabilities, displayName, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<Note> getFullNote$(long id) {
|
public LiveData<Note> getFullNote$(long id) {
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
package it.niedermann.owncloud.notes.persistence;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import com.nextcloud.android.sso.AccountImporter;
|
||||||
|
import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import it.niedermann.owncloud.notes.persistence.entity.Account;
|
||||||
|
import it.niedermann.owncloud.notes.persistence.sync.NotesAPI;
|
||||||
|
import it.niedermann.owncloud.notes.shared.model.IResponseCallback;
|
||||||
|
import it.niedermann.owncloud.notes.shared.model.ImportStatus;
|
||||||
|
import it.niedermann.owncloud.notes.shared.util.ApiVersionUtil;
|
||||||
|
|
||||||
|
|
||||||
|
public class NotesImportTask {
|
||||||
|
|
||||||
|
private static final String TAG = NotesImportTask.class.getSimpleName();
|
||||||
|
|
||||||
|
private final NotesAPI notesAPI;
|
||||||
|
@NonNull
|
||||||
|
private final NotesRepository repo;
|
||||||
|
@NonNull
|
||||||
|
private final Account localAccount;
|
||||||
|
@NonNull
|
||||||
|
private final ExecutorService executor;
|
||||||
|
@NonNull
|
||||||
|
private final ExecutorService fetchExecutor;
|
||||||
|
|
||||||
|
NotesImportTask(@NonNull Context context, @NonNull NotesRepository repo, @NonNull Account localAccount, @NonNull ExecutorService executor, @NonNull ApiProvider apiProvider) throws NextcloudFilesAppAccountNotFoundException {
|
||||||
|
this(context, repo, localAccount, executor, Executors.newFixedThreadPool(20), apiProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
private NotesImportTask(@NonNull Context context, @NonNull NotesRepository repo, @NonNull Account localAccount, @NonNull ExecutorService executor, @NonNull ExecutorService fetchExecutor, @NonNull ApiProvider apiProvider) throws NextcloudFilesAppAccountNotFoundException {
|
||||||
|
this.repo = repo;
|
||||||
|
this.localAccount = localAccount;
|
||||||
|
this.executor = executor;
|
||||||
|
this.fetchExecutor = fetchExecutor;
|
||||||
|
this.notesAPI = apiProvider.getNotesAPI(context, AccountImporter.getSingleSignOnAccount(context, localAccount.getAccountName()), ApiVersionUtil.getPreferredApiVersion(localAccount.getApiVersion()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<ImportStatus> importNotes(@NonNull IResponseCallback<Void> callback) {
|
||||||
|
final var status$ = new MutableLiveData<ImportStatus>();
|
||||||
|
Log.i(TAG, "STARTING IMPORT");
|
||||||
|
executor.submit(() -> {
|
||||||
|
Log.i(TAG, "… Fetching notes IDs");
|
||||||
|
final var status = new ImportStatus();
|
||||||
|
final var remoteIds = notesAPI.getNotesIDs().blockingSingle();
|
||||||
|
status.total = remoteIds.size();
|
||||||
|
status$.postValue(status);
|
||||||
|
Log.i(TAG, "… Total count: " + remoteIds.size());
|
||||||
|
final var latch = new CountDownLatch(remoteIds.size());
|
||||||
|
for (long id : remoteIds) {
|
||||||
|
fetchExecutor.submit(() -> {
|
||||||
|
try {
|
||||||
|
repo.addNote(localAccount.getId(), notesAPI.getNote(id).blockingSingle().getResponse());
|
||||||
|
} catch (Throwable t) {
|
||||||
|
Log.w(TAG, "Could not import note with remoteId " + id + ": " + t.getMessage());
|
||||||
|
status.warnings.add(t);
|
||||||
|
}
|
||||||
|
status.count++;
|
||||||
|
status$.postValue(status);
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
latch.await();
|
||||||
|
Log.i(TAG, "IMPORT FINISHED");
|
||||||
|
callback.onSuccess(null);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
callback.onError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return status$;
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,6 +58,7 @@ import it.niedermann.owncloud.notes.shared.model.DBStatus;
|
||||||
import it.niedermann.owncloud.notes.shared.model.ENavigationCategoryType;
|
import it.niedermann.owncloud.notes.shared.model.ENavigationCategoryType;
|
||||||
import it.niedermann.owncloud.notes.shared.model.IResponseCallback;
|
import it.niedermann.owncloud.notes.shared.model.IResponseCallback;
|
||||||
import it.niedermann.owncloud.notes.shared.model.ISyncCallback;
|
import it.niedermann.owncloud.notes.shared.model.ISyncCallback;
|
||||||
|
import it.niedermann.owncloud.notes.shared.model.ImportStatus;
|
||||||
import it.niedermann.owncloud.notes.shared.model.NavigationCategory;
|
import it.niedermann.owncloud.notes.shared.model.NavigationCategory;
|
||||||
import it.niedermann.owncloud.notes.shared.model.NotesSettings;
|
import it.niedermann.owncloud.notes.shared.model.NotesSettings;
|
||||||
import it.niedermann.owncloud.notes.shared.model.SyncResultStatus;
|
import it.niedermann.owncloud.notes.shared.model.SyncResultStatus;
|
||||||
|
@ -86,6 +87,7 @@ public class NotesRepository {
|
||||||
private final ApiProvider apiProvider;
|
private final ApiProvider apiProvider;
|
||||||
private final ExecutorService executor;
|
private final ExecutorService executor;
|
||||||
private final ExecutorService syncExecutor;
|
private final ExecutorService syncExecutor;
|
||||||
|
private final ExecutorService importExecutor;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final NotesDatabase db;
|
private final NotesDatabase db;
|
||||||
private final String defaultNonEmptyTitle;
|
private final String defaultNonEmptyTitle;
|
||||||
|
@ -138,16 +140,17 @@ public class NotesRepository {
|
||||||
|
|
||||||
public static synchronized NotesRepository getInstance(@NonNull Context context) {
|
public static synchronized NotesRepository getInstance(@NonNull Context context) {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
instance = new NotesRepository(context, NotesDatabase.getInstance(context.getApplicationContext()), Executors.newCachedThreadPool(), Executors.newSingleThreadExecutor(), ApiProvider.getInstance());
|
instance = new NotesRepository(context, NotesDatabase.getInstance(context.getApplicationContext()), Executors.newCachedThreadPool(), Executors.newSingleThreadExecutor(), Executors.newSingleThreadExecutor(), ApiProvider.getInstance());
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private NotesRepository(@NonNull final Context context, @NonNull final NotesDatabase db, @NonNull final ExecutorService executor, @NonNull final ExecutorService syncExecutor, @NonNull ApiProvider apiProvider) {
|
private NotesRepository(@NonNull final Context context, @NonNull final NotesDatabase db, @NonNull final ExecutorService executor, @NonNull final ExecutorService syncExecutor, @NonNull final ExecutorService importExecutor, @NonNull ApiProvider apiProvider) {
|
||||||
this.context = context.getApplicationContext();
|
this.context = context.getApplicationContext();
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
this.syncExecutor = syncExecutor;
|
this.syncExecutor = syncExecutor;
|
||||||
|
this.importExecutor = importExecutor;
|
||||||
this.apiProvider = apiProvider;
|
this.apiProvider = apiProvider;
|
||||||
this.defaultNonEmptyTitle = NoteUtil.generateNonEmptyNoteTitle("", this.context);
|
this.defaultNonEmptyTitle = NoteUtil.generateNonEmptyNoteTitle("", this.context);
|
||||||
this.syncOnlyOnWifiKey = context.getApplicationContext().getResources().getString(R.string.pref_key_wifi_only);
|
this.syncOnlyOnWifiKey = context.getApplicationContext().getResources().getString(R.string.pref_key_wifi_only);
|
||||||
|
@ -166,13 +169,36 @@ public class NotesRepository {
|
||||||
// Accounts
|
// Accounts
|
||||||
|
|
||||||
@AnyThread
|
@AnyThread
|
||||||
public void addAccount(@NonNull String url, @NonNull String username, @NonNull String accountName, @NonNull Capabilities capabilities, @Nullable String displayName, @NonNull IResponseCallback<Account> callback) {
|
public LiveData<ImportStatus> addAccount(@NonNull String url, @NonNull String username, @NonNull String accountName, @NonNull Capabilities capabilities, @Nullable String displayName, @NonNull IResponseCallback<Account> callback) {
|
||||||
final var createdAccount = db.getAccountDao().getAccountById(db.getAccountDao().insert(new Account(url, username, accountName, displayName, capabilities)));
|
final var account = db.getAccountDao().getAccountById(db.getAccountDao().insert(new Account(url, username, accountName, displayName, capabilities)));
|
||||||
if (createdAccount == null) {
|
if (account == null) {
|
||||||
callback.onError(new Exception("Could not read created account."));
|
callback.onError(new Exception("Could not read created account."));
|
||||||
} else {
|
} else {
|
||||||
callback.onSuccess(createdAccount);
|
if (isSyncPossible()) {
|
||||||
|
syncActive.put(account.getId(), true);
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "... starting now");
|
||||||
|
final NotesImportTask importTask = new NotesImportTask(context, this, account, importExecutor, apiProvider);
|
||||||
|
return importTask.importNotes(new IResponseCallback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Void result) {
|
||||||
|
callback.onSuccess(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(@NonNull Throwable t) {
|
||||||
|
callback.onError(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (NextcloudFilesAppAccountNotFoundException e) {
|
||||||
|
Log.e(TAG, "... Could not find " + SingleSignOnAccount.class.getSimpleName() + " for account name " + account.getAccountName());
|
||||||
|
callback.onError(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback.onError(new NetworkErrorException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new MutableLiveData<>(new ImportStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
|
|
|
@ -12,6 +12,7 @@ import com.nextcloud.android.sso.api.ParsedResponse;
|
||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import it.niedermann.owncloud.notes.persistence.entity.Note;
|
import it.niedermann.owncloud.notes.persistence.entity.Note;
|
||||||
|
@ -69,6 +70,26 @@ public class NotesAPI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Observable<List<Long>> getNotesIDs() {
|
||||||
|
if (ApiVersion.API_VERSION_1_0.equals(usedApiVersion)) {
|
||||||
|
return notesAPI_1_0.getNotesIDs().map(response -> response.getResponse().stream().map(Note::getRemoteId).collect(Collectors.toList()));
|
||||||
|
} else if (ApiVersion.API_VERSION_0_2.equals(usedApiVersion)) {
|
||||||
|
return notesAPI_0_2.getNotesIDs().map(response -> response.getResponse().stream().map(Note::getRemoteId).collect(Collectors.toList()));
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("Used API version " + usedApiVersion + " does not support getNotesIDs().");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Observable<ParsedResponse<Note>> getNote(long remoteId) {
|
||||||
|
if (ApiVersion.API_VERSION_1_0.equals(usedApiVersion)) {
|
||||||
|
return notesAPI_1_0.getNote(remoteId);
|
||||||
|
} else if (ApiVersion.API_VERSION_0_2.equals(usedApiVersion)) {
|
||||||
|
return notesAPI_0_2.getNote(remoteId);
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("Used API version " + usedApiVersion + " does not support getNote().");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Call<Note> createNote(Note note) {
|
public Call<Note> createNote(Note note) {
|
||||||
if (ApiVersion.API_VERSION_1_0.equals(usedApiVersion)) {
|
if (ApiVersion.API_VERSION_1_0.equals(usedApiVersion)) {
|
||||||
return notesAPI_1_0.createNote(note);
|
return notesAPI_1_0.createNote(note);
|
||||||
|
|
|
@ -25,9 +25,15 @@ public interface NotesAPI_0_2 {
|
||||||
@GET("notes")
|
@GET("notes")
|
||||||
Observable<ParsedResponse<List<Note>>> getNotes(@Query("pruneBefore") long lastModified, @Header("If-None-Match") String lastETag);
|
Observable<ParsedResponse<List<Note>>> getNotes(@Query("pruneBefore") long lastModified, @Header("If-None-Match") String lastETag);
|
||||||
|
|
||||||
|
@GET("notes?exclude=etag,readonly,content,title,category,favorite,modified")
|
||||||
|
Observable<ParsedResponse<List<Note>>> getNotesIDs();
|
||||||
|
|
||||||
@POST("notes")
|
@POST("notes")
|
||||||
Call<Note> createNote(@Body NotesAPI.Note_0_2 note);
|
Call<Note> createNote(@Body NotesAPI.Note_0_2 note);
|
||||||
|
|
||||||
|
@GET("notes/{remoteId}")
|
||||||
|
Observable<ParsedResponse<Note>> getNote(@Path("remoteId") long remoteId);
|
||||||
|
|
||||||
@PUT("notes/{remoteId}")
|
@PUT("notes/{remoteId}")
|
||||||
Call<Note> editNote(@Body NotesAPI.Note_0_2 note, @Path("remoteId") long remoteId);
|
Call<Note> editNote(@Body NotesAPI.Note_0_2 note, @Path("remoteId") long remoteId);
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,15 @@ public interface NotesAPI_1_0 {
|
||||||
@GET("notes")
|
@GET("notes")
|
||||||
Observable<ParsedResponse<List<Note>>> getNotes(@Query("pruneBefore") long lastModified, @Header("If-None-Match") String lastETag);
|
Observable<ParsedResponse<List<Note>>> getNotes(@Query("pruneBefore") long lastModified, @Header("If-None-Match") String lastETag);
|
||||||
|
|
||||||
|
@GET("notes?exclude=etag,readonly,content,title,category,favorite,modified")
|
||||||
|
Observable<ParsedResponse<List<Note>>> getNotesIDs();
|
||||||
|
|
||||||
@POST("notes")
|
@POST("notes")
|
||||||
Call<Note> createNote(@Body Note note);
|
Call<Note> createNote(@Body Note note);
|
||||||
|
|
||||||
|
@GET("notes/{remoteId}")
|
||||||
|
Observable<ParsedResponse<Note>> getNote(@Path("remoteId") long remoteId);
|
||||||
|
|
||||||
@PUT("notes/{remoteId}")
|
@PUT("notes/{remoteId}")
|
||||||
Call<Note> editNote(@Body Note note, @Path("remoteId") long remoteId);
|
Call<Note> editNote(@Body Note note, @Path("remoteId") long remoteId);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package it.niedermann.owncloud.notes.shared.model;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
public class ImportStatus {
|
||||||
|
public int count = 0;
|
||||||
|
public int total = 0;
|
||||||
|
public final Collection<Throwable> warnings = new LinkedList<>();
|
||||||
|
}
|
|
@ -65,12 +65,24 @@
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progress_circular"
|
android:id="@+id/progress_circular"
|
||||||
android:layout_width="wrap_content"
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/add_button"
|
android:layout_below="@id/add_button"
|
||||||
android:layout_centerHorizontal="true"
|
android:layout_centerHorizontal="true"
|
||||||
android:layout_marginTop="32dp"
|
android:layout_marginTop="@dimen/spacer_5x"
|
||||||
|
android:indeterminate="true"
|
||||||
android:indeterminateTint="@color/defaultBrand"
|
android:indeterminateTint="@color/defaultBrand"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/progress_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/progress_circular"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_marginTop="@dimen/spacer_2x"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:text="@string/progress_import_indeterminate" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
|
@ -12,6 +12,7 @@
|
||||||
<string name="action_search">Search</string>
|
<string name="action_search">Search</string>
|
||||||
<string name="action_sorting_method">Sorting method</string>
|
<string name="action_sorting_method">Sorting method</string>
|
||||||
<string name="simple_cancel">Cancel</string>
|
<string name="simple_cancel">Cancel</string>
|
||||||
|
<string name="simple_switch">Switch</string>
|
||||||
<string name="simple_edit">Edit</string>
|
<string name="simple_edit">Edit</string>
|
||||||
<string name="simple_remove">Remove</string>
|
<string name="simple_remove">Remove</string>
|
||||||
<string name="action_edit_save">Save</string>
|
<string name="action_edit_save">Save</string>
|
||||||
|
@ -315,4 +316,7 @@
|
||||||
<string name="settings_file_suffix_description">File extension for new notes in your Nextcloud</string>
|
<string name="settings_file_suffix_description">File extension for new notes in your Nextcloud</string>
|
||||||
<string name="settings_file_suffix_success">New file suffix: %1$s</string>
|
<string name="settings_file_suffix_success">New file suffix: %1$s</string>
|
||||||
<string name="http_status_code">HTTP status code: %1$d</string>
|
<string name="http_status_code">HTTP status code: %1$d</string>
|
||||||
|
<string name="progress_import_indeterminate">Importing notes…</string>
|
||||||
|
<string name="progress_import">Importing note %1$d of %2$d…</string>
|
||||||
|
<string name="account_imported">Account imported.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
1
fastlane/metadata/android/en-US/changelogs/3004014.txt
Normal file
1
fastlane/metadata/android/en-US/changelogs/3004014.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
- 🐞 Consistent timeouts when syncing (#761)
|
Loading…
Reference in a new issue