-
-
Notifications
You must be signed in to change notification settings - Fork 187
Add folder upload support #624
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
Open
tobihagemann
wants to merge
26
commits into
develop
Choose a base branch
from
feature/upload-folder
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
28ff7cf
Add folder upload support
tobihagemann dac8dff
Add upload service with foreground execution and resume support
tobihagemann 9abeabc
Fix race conditions, error handling, and plurals in upload service
tobihagemann 9f36ab9
Clean up upload service: simplify DAO, remove dead code, fix dialog s…
tobihagemann 62a5063
Fix multi-resume checkpoint loss, simplify dialog and presenter code
tobihagemann 414d878
Fix resume navigation and broaden folder resume exception handling
tobihagemann 9c1ca9f
Add tests for UploadFolderStructure and StreamHelper
tobihagemann 890a78d
Suspend vault auto-lock during upload picker and service flows
tobihagemann 44a6a76
Align upload feature with app architecture patterns
tobihagemann fff3b4e
Migrate changed-file upload to foreground service with temp file cleanup
tobihagemann e1c1ad4
Migrate text editor save to foreground upload service
tobihagemann ebcef06
Queue concurrent uploads and handle dispatch failures
tobihagemann 5267dcf
Fix suspended lock leak, temp file leak, and asymmetric test coverage
tobihagemann 4e6a3c4
Fix lock suspension leaks in presenter destroyed() and upload resume …
tobihagemann 82f5e04
Replace boolean lock suspension with time-bounded keep-alive leases
tobihagemann d871bf8
Fix picker lease leaks and clean up stale checkpoints for abandoned u…
tobihagemann 552b901
Add targeted UI updates via UploadUiUpdates event bus during upload
tobihagemann 27eaf97
Show replace dialog for folder uploads and persist choice for resume
tobihagemann 107cf4c
Fix cancel race, stale checkpoint, and service linger in UploadService
tobihagemann dbf0f20
Show upload status indicator in vault list with animated fill-up icon
tobihagemann 2f0e558
Prevent manual vault lock while upload is in progress
tobihagemann 876ee59
Replace vault lock toast with Cancel & Lock confirmation dialog
tobihagemann 8233d64
Add per-vault upload cancellation so locking one vault does not abort…
tobihagemann 49b50f5
Fix stale checkpoint on pre-cancel and persist URI permissions for fi…
tobihagemann 320e957
Fix file resume to respect checkpoint replacing flag instead of hardc…
tobihagemann bf20294
Include exception in Timber debug log for URI permission failure
tobihagemann File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -102,7 +102,7 @@ android { | |
| } | ||
|
|
||
| greendao { | ||
| schemaVersion 13 | ||
| schemaVersion 14 | ||
| } | ||
|
|
||
| configurations.all { | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
data/src/main/java/org/cryptomator/data/db/Upgrade13To14.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| package org.cryptomator.data.db | ||
|
|
||
| import org.greenrobot.greendao.database.Database | ||
| import javax.inject.Inject | ||
| import javax.inject.Singleton | ||
|
|
||
| @Singleton | ||
| internal class Upgrade13To14 @Inject constructor() : DatabaseUpgrade(13, 14) { | ||
|
|
||
| override fun internalApplyTo(db: Database, origin: Int) { | ||
| db.beginTransaction() | ||
| try { | ||
| createUploadCheckpointTable(db) | ||
| db.setTransactionSuccessful() | ||
| } finally { | ||
| db.endTransaction() | ||
| } | ||
| } | ||
|
|
||
| private fun createUploadCheckpointTable(db: Database) { | ||
| Sql.createTable("UPLOAD_CHECKPOINT_ENTITY") // | ||
| .id() // | ||
| .requiredInt("VAULT_ID") // | ||
| .requiredText("TYPE") // | ||
| .requiredText("TARGET_FOLDER_PATH") // | ||
| .optionalText("SOURCE_FOLDER_URI") // | ||
| .optionalText("SOURCE_FOLDER_NAME") // | ||
| .optionalText("PENDING_FILE_URIS") // | ||
| .requiredText("COMPLETED_FILES") // | ||
| .requiredInt("TOTAL_FILE_COUNT") // | ||
| .requiredInt("TIMESTAMP") // | ||
| .optionalInt("REPLACING") // | ||
| .executeOn(db) | ||
|
|
||
| Sql.createUniqueIndex("IDX_UPLOAD_CHECKPOINT_ENTITY_VAULT_ID") // | ||
| .on("UPLOAD_CHECKPOINT_ENTITY") // | ||
| .asc("VAULT_ID") // | ||
| .executeOn(db) | ||
| } | ||
| } |
97 changes: 97 additions & 0 deletions
97
data/src/main/java/org/cryptomator/data/db/UploadCheckpointDao.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| package org.cryptomator.data.db; | ||
|
|
||
| import android.content.ContentValues; | ||
| import android.database.Cursor; | ||
| import android.database.sqlite.SQLiteDatabase; | ||
|
|
||
| import org.cryptomator.data.db.entities.UploadCheckpointEntity; | ||
|
|
||
| import java.util.HashSet; | ||
| import java.util.Set; | ||
|
|
||
| import javax.inject.Inject; | ||
| import javax.inject.Singleton; | ||
|
|
||
| @Singleton | ||
| public class UploadCheckpointDao { | ||
|
|
||
| private static final String TABLE_NAME = "UPLOAD_CHECKPOINT_ENTITY"; | ||
|
|
||
| private final DatabaseFactory databaseFactory; | ||
|
|
||
| @Inject | ||
| public UploadCheckpointDao(DatabaseFactory databaseFactory) { | ||
| this.databaseFactory = databaseFactory; | ||
| } | ||
|
|
||
| private SQLiteDatabase getDb() { | ||
| return databaseFactory.getWritableDatabase(); | ||
| } | ||
|
|
||
| public long insertOrReplace(UploadCheckpointEntity entity) { | ||
| ContentValues values = toContentValues(entity); | ||
| return getDb().insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE); | ||
| } | ||
|
|
||
| public UploadCheckpointEntity findByVaultId(long vaultId) { | ||
| try (Cursor cursor = getDb().query(TABLE_NAME, null, "VAULT_ID = ?", | ||
| new String[]{String.valueOf(vaultId)}, null, null, null)) { | ||
| if (cursor.moveToFirst()) { | ||
| return fromCursor(cursor); | ||
| } | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| public void deleteByVaultId(long vaultId) { | ||
| getDb().delete(TABLE_NAME, "VAULT_ID = ?", new String[]{String.valueOf(vaultId)}); | ||
| } | ||
|
|
||
| public Set<Long> findAllVaultIdsWithCheckpoints() { | ||
| Set<Long> result = new HashSet<>(); | ||
| try (Cursor cursor = getDb().query(TABLE_NAME, new String[]{"VAULT_ID"}, | ||
| null, null, null, null, null)) { | ||
| while (cursor.moveToNext()) { | ||
| result.add(cursor.getLong(0)); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| public void updateCompletedFiles(long vaultId, String completedFilesJson) { | ||
| ContentValues values = new ContentValues(); | ||
| values.put("COMPLETED_FILES", completedFilesJson); | ||
| getDb().update(TABLE_NAME, values, "VAULT_ID = ?", new String[]{String.valueOf(vaultId)}); | ||
| } | ||
|
|
||
| private ContentValues toContentValues(UploadCheckpointEntity entity) { | ||
| ContentValues values = new ContentValues(); | ||
| values.put("VAULT_ID", entity.getVaultId()); | ||
| values.put("TYPE", entity.getType()); | ||
| values.put("TARGET_FOLDER_PATH", entity.getTargetFolderPath()); | ||
| values.put("SOURCE_FOLDER_URI", entity.getSourceFolderUri()); | ||
| values.put("SOURCE_FOLDER_NAME", entity.getSourceFolderName()); | ||
| values.put("PENDING_FILE_URIS", entity.getPendingFileUris()); | ||
| values.put("COMPLETED_FILES", entity.getCompletedFiles()); | ||
| values.put("TOTAL_FILE_COUNT", entity.getTotalFileCount()); | ||
| values.put("TIMESTAMP", entity.getTimestamp()); | ||
| values.put("REPLACING", entity.isReplacing() ? 1 : 0); | ||
| return values; | ||
| } | ||
|
|
||
| private UploadCheckpointEntity fromCursor(Cursor cursor) { | ||
| UploadCheckpointEntity entity = new UploadCheckpointEntity(); | ||
| entity.setId(cursor.getLong(cursor.getColumnIndexOrThrow("_id"))); | ||
| entity.setVaultId(cursor.getLong(cursor.getColumnIndexOrThrow("VAULT_ID"))); | ||
| entity.setType(cursor.getString(cursor.getColumnIndexOrThrow("TYPE"))); | ||
| entity.setTargetFolderPath(cursor.getString(cursor.getColumnIndexOrThrow("TARGET_FOLDER_PATH"))); | ||
| entity.setSourceFolderUri(cursor.getString(cursor.getColumnIndexOrThrow("SOURCE_FOLDER_URI"))); | ||
| entity.setSourceFolderName(cursor.getString(cursor.getColumnIndexOrThrow("SOURCE_FOLDER_NAME"))); | ||
| entity.setPendingFileUris(cursor.getString(cursor.getColumnIndexOrThrow("PENDING_FILE_URIS"))); | ||
| entity.setCompletedFiles(cursor.getString(cursor.getColumnIndexOrThrow("COMPLETED_FILES"))); | ||
| entity.setTotalFileCount(cursor.getInt(cursor.getColumnIndexOrThrow("TOTAL_FILE_COUNT"))); | ||
| entity.setTimestamp(cursor.getLong(cursor.getColumnIndexOrThrow("TIMESTAMP"))); | ||
| entity.setReplacing(cursor.getInt(cursor.getColumnIndexOrThrow("REPLACING")) != 0); | ||
| return entity; | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.