Support for viewing tags

Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
This commit is contained in:
tobiasKaminsky 2023-04-11 14:30:46 +02:00 committed by Andy Scherzinger
parent ffac056474
commit f196bfd874
No known key found for this signature in database
GPG key ID: 6CADC7E3523C308B
14 changed files with 1268 additions and 39 deletions

File diff suppressed because it is too large Load diff

View file

@ -48,20 +48,23 @@ class OCFileListFragmentStaticServerIT : AbstractIT() {
fun showFiles() { fun showFiles() {
val sut = testActivityRule.launchActivity(null) val sut = testActivityRule.launchActivity(null)
val textFile = OCFile("/1.png") OCFile("/1.png").apply {
textFile.mimeType = "image/png" mimeType = "image/png"
textFile.fileLength = 1024000 fileLength = 1024000
textFile.modificationTimestamp = 1188206955000 modificationTimestamp = 1188206955000
textFile.parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
sut.storageManager.saveFile(textFile) sut.storageManager.saveFile(this)
}
val imageFile = OCFile("/image.png") OCFile("/image.png").apply {
imageFile.mimeType = "image/png" mimeType = "image/png"
imageFile.isPreviewAvailable = false isPreviewAvailable = false
imageFile.fileLength = 3072000 fileLength = 3072000
imageFile.modificationTimestamp = 746443755000 modificationTimestamp = 746443755000
imageFile.parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
sut.storageManager.saveFile(imageFile) tags = listOf("Top secret")
sut.storageManager.saveFile(this)
}
OCFile("/video.mp4").apply { OCFile("/video.mp4").apply {
mimeType = "video/mp4" mimeType = "video/mp4"
@ -69,6 +72,7 @@ class OCFileListFragmentStaticServerIT : AbstractIT() {
fileLength = 12092000 fileLength = 12092000
modificationTimestamp = 746143952000 modificationTimestamp = 746143952000
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
tags = listOf("Confidential", "+5")
sut.storageManager.saveFile(this) sut.storageManager.saveFile(this)
} }

View file

@ -117,5 +117,7 @@ data class FileEntity(
@ColumnInfo(name = ProviderTableMeta.FILE_LOCK_TIMEOUT) @ColumnInfo(name = ProviderTableMeta.FILE_LOCK_TIMEOUT)
val lockTimeout: Int?, val lockTimeout: Int?,
@ColumnInfo(name = ProviderTableMeta.FILE_LOCK_TOKEN) @ColumnInfo(name = ProviderTableMeta.FILE_LOCK_TOKEN)
val lockToken: String? val lockToken: String?,
@ColumnInfo(name = ProviderTableMeta.FILE_TAGS)
val tags: String?
) )

View file

@ -440,6 +440,7 @@ public class FileDataStorageManager {
*/ */
private ContentValues createContentValuesBase(OCFile fileOrFolder) { private ContentValues createContentValuesBase(OCFile fileOrFolder) {
final ContentValues cv = new ContentValues(); final ContentValues cv = new ContentValues();
final Gson gson = new Gson();
cv.put(ProviderTableMeta.FILE_MODIFIED, fileOrFolder.getModificationTimestamp()); cv.put(ProviderTableMeta.FILE_MODIFIED, fileOrFolder.getModificationTimestamp());
cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, fileOrFolder.getModificationTimestampAtLastSyncForData()); cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, fileOrFolder.getModificationTimestampAtLastSyncForData());
cv.put(ProviderTableMeta.FILE_PARENT, fileOrFolder.getParentId()); cv.put(ProviderTableMeta.FILE_PARENT, fileOrFolder.getParentId());
@ -464,7 +465,8 @@ public class FileDataStorageManager {
cv.put(ProviderTableMeta.FILE_OWNER_ID, fileOrFolder.getOwnerId()); cv.put(ProviderTableMeta.FILE_OWNER_ID, fileOrFolder.getOwnerId());
cv.put(ProviderTableMeta.FILE_OWNER_DISPLAY_NAME, fileOrFolder.getOwnerDisplayName()); cv.put(ProviderTableMeta.FILE_OWNER_DISPLAY_NAME, fileOrFolder.getOwnerDisplayName());
cv.put(ProviderTableMeta.FILE_NOTE, fileOrFolder.getNote()); cv.put(ProviderTableMeta.FILE_NOTE, fileOrFolder.getNote());
cv.put(ProviderTableMeta.FILE_SHAREES, new Gson().toJson(fileOrFolder.getSharees())); cv.put(ProviderTableMeta.FILE_SHAREES, gson.toJson(fileOrFolder.getSharees()));
cv.put(ProviderTableMeta.FILE_TAGS, gson.toJson(fileOrFolder.getTags()));
cv.put(ProviderTableMeta.FILE_RICH_WORKSPACE, fileOrFolder.getRichWorkspace()); cv.put(ProviderTableMeta.FILE_RICH_WORKSPACE, fileOrFolder.getRichWorkspace());
return cv; return cv;
} }
@ -952,6 +954,20 @@ public class FileDataStorageManager {
} }
} }
String tags = fileEntity.getTags();
if (tags == null || tags.isEmpty() ||
JSON_NULL_STRING.equals(tags) || JSON_EMPTY_ARRAY.equals(tags)) {
ocFile.setTags(new ArrayList<>());
} else {
try {
String[] tagsArray = gson.fromJson(tags, String[].class);
ocFile.setTags(new ArrayList<>(Arrays.asList(tagsArray)));
} catch (JsonSyntaxException e) {
// ignore saved value due to api change
ocFile.setTags(new ArrayList<>());
}
}
String metadataSize = fileEntity.getMetadataSize(); String metadataSize = fileEntity.getMetadataSize();
// Surprisingly JSON deserialization causes significant overhead. // Surprisingly JSON deserialization causes significant overhead.
// Avoid it in common, trivial cases (null/empty). // Avoid it in common, trivial cases (null/empty).

View file

@ -114,6 +114,7 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
private String lockToken; private String lockToken;
@Nullable @Nullable
private ImageDimension imageDimension; private ImageDimension imageDimension;
private List<String> tags;
/** /**
* URI to the local path of the file contents, if stored in the device; cached after first call to {@link * URI to the local path of the file contents, if stored in the device; cached after first call to {@link
@ -966,4 +967,12 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
public ImageDimension getImageDimension() { public ImageDimension getImageDimension() {
return imageDimension; return imageDimension;
} }
public List<String> getTags() {
return tags;
}
public void setTags(List<String> tags) {
this.tags = tags;
}
} }

View file

@ -35,7 +35,7 @@ import java.util.List;
*/ */
public class ProviderMeta { public class ProviderMeta {
public static final String DB_NAME = "filelist"; public static final String DB_NAME = "filelist";
public static final int DB_VERSION = 68; public static final int DB_VERSION = 69;
private ProviderMeta() { private ProviderMeta() {
// No instance // No instance
@ -125,6 +125,7 @@ public class ProviderMeta {
public static final String FILE_LOCK_TIMESTAMP = "lock_timestamp"; public static final String FILE_LOCK_TIMESTAMP = "lock_timestamp";
public static final String FILE_LOCK_TIMEOUT = "lock_timeout"; public static final String FILE_LOCK_TIMEOUT = "lock_timeout";
public static final String FILE_LOCK_TOKEN = "lock_token"; public static final String FILE_LOCK_TOKEN = "lock_token";
public static final String FILE_TAGS = "tags";
public static final List<String> FILE_ALL_COLUMNS = Collections.unmodifiableList(Arrays.asList( public static final List<String> FILE_ALL_COLUMNS = Collections.unmodifiableList(Arrays.asList(
_ID, _ID,
@ -171,7 +172,8 @@ public class ProviderMeta {
FILE_LOCK_TIMESTAMP, FILE_LOCK_TIMESTAMP,
FILE_LOCK_TIMEOUT, FILE_LOCK_TIMEOUT,
FILE_LOCK_TOKEN, FILE_LOCK_TOKEN,
FILE_METADATA_SIZE)); FILE_METADATA_SIZE,
FILE_TAGS));
public static final String FILE_DEFAULT_SORT_ORDER = FILE_NAME + " collate nocase asc"; public static final String FILE_DEFAULT_SORT_ORDER = FILE_NAME + " collate nocase asc";
// Columns of ocshares table // Columns of ocshares table

View file

@ -32,4 +32,6 @@ internal interface ListItemViewHolder : ListGridItemViewHolder {
val lastModification: TextView val lastModification: TextView
val overflowMenu: ImageView val overflowMenu: ImageView
val sharedAvatars: AvatarGroupLayout val sharedAvatars: AvatarGroupLayout
val tag: TextView
val tagMore: TextView
} }

View file

@ -415,6 +415,24 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
holder.getSharedAvatars().removeAllViews(); holder.getSharedAvatars().removeAllViews();
} }
// tags
if (file.getTags().isEmpty()) {
holder.getTag().setVisibility(View.GONE);
holder.getTagMore().setVisibility(View.GONE);
} else {
holder.getTag().setVisibility(View.VISIBLE);
holder.getTag().setText(file.getTags().get(0));
if (file.getTags().size() > 1) {
holder.getTagMore().setVisibility(View.VISIBLE);
holder.getTagMore().setText(String.format(activity.getString(R.string.tags_more),
(file.getTags().size() - 1)));
} else {
holder.getTagMore().setVisibility(View.GONE);
}
}
// npe fix: looks like file without local storage path somehow get here // npe fix: looks like file without local storage path somehow get here
final String storagePath = file.getStoragePath(); final String storagePath = file.getStoragePath();
if (onlyOnDevice && storagePath != null) { if (onlyOnDevice && storagePath != null) {

View file

@ -48,6 +48,10 @@ internal class OCFileListItemViewHolder(private var binding: ListItemBinding) :
get() = binding.Filename get() = binding.Filename
override val thumbnail: ImageView override val thumbnail: ImageView
get() = binding.thumbnailLayout.thumbnail get() = binding.thumbnailLayout.thumbnail
override val tag: TextView
get() = binding.tag
override val tagMore: TextView
get() = binding.tagMore
override fun showVideoOverlay() { override fun showVideoOverlay() {
binding.thumbnailLayout.videoOverlay.visibility = View.VISIBLE binding.thumbnailLayout.videoOverlay.visibility = View.VISIBLE

View file

@ -34,6 +34,7 @@ import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.files.model.RemoteFile; import com.owncloud.android.lib.resources.files.model.RemoteFile;
import com.owncloud.android.lib.resources.shares.ShareeUser;
import com.owncloud.android.ui.helpers.FileOperationsHelper; import com.owncloud.android.ui.helpers.FileOperationsHelper;
import java.io.File; import java.io.File;
@ -233,7 +234,7 @@ public final class FileStorageUtils {
file.setOwnerId(remote.getOwnerId()); file.setOwnerId(remote.getOwnerId());
file.setOwnerDisplayName(remote.getOwnerDisplayName()); file.setOwnerDisplayName(remote.getOwnerDisplayName());
file.setNote(remote.getNote()); file.setNote(remote.getNote());
file.setSharees(new ArrayList<>(Arrays.asList(remote.getSharees()))); file.setSharees(new ArrayList<ShareeUser>(Arrays.asList(remote.getSharees())));
file.setRichWorkspace(remote.getRichWorkspace()); file.setRichWorkspace(remote.getRichWorkspace());
file.setLocked(remote.isLocked()); file.setLocked(remote.isLocked());
file.setLockType(remote.getLockType()); file.setLockType(remote.getLockType());
@ -243,6 +244,7 @@ public final class FileStorageUtils {
file.setLockTimestamp(remote.getLockTimestamp()); file.setLockTimestamp(remote.getLockTimestamp());
file.setLockTimeout(remote.getLockTimeout()); file.setLockTimeout(remote.getLockTimeout());
file.setLockToken(remote.getLockToken()); file.setLockToken(remote.getLockToken());
file.setTags(new ArrayList<String>(Arrays.asList(remote.getTags())));
return file; return file;
} }

View file

@ -95,33 +95,65 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <com.google.android.material.chip.Chip
android:id="@+id/file_size" android:id="@+id/tag"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/placeholder_fileSize" android:layout_marginEnd="@dimen/standard_eighth_margin"
android:textColor="@color/list_item_lastmod_and_filesize_text" android:textColor="@color/list_item_lastmod_and_filesize_text"
android:textSize="@dimen/two_line_secondary_text_size" /> android:textSize="@dimen/two_line_secondary_text_size"
app:chipStartPadding="@dimen/standard_eighth_margin"
app:chipEndPadding="@dimen/standard_eighth_margin"
android:checkable="false"
android:ellipsize="middle" />
<TextView <com.google.android.material.chip.Chip
android:id="@+id/file_separator" android:id="@+id/tag_more"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="end" android:layout_marginStart="@dimen/standard_eighth_margin"
android:paddingStart="@dimen/zero" android:layout_marginEnd="@dimen/standard_eighth_margin"
android:paddingEnd="@dimen/standard_quarter_padding"
android:text="@string/info_separator"
android:textColor="@color/list_item_lastmod_and_filesize_text" android:textColor="@color/list_item_lastmod_and_filesize_text"
android:textSize="@dimen/two_line_secondary_text_size" /> android:textSize="@dimen/two_line_secondary_text_size"
app:chipStartPadding="@dimen/standard_eighth_margin"
app:chipEndPadding="@dimen/standard_eighth_margin"
app:chipBackgroundColor="@color/bg_default"
android:layout_weight="1" />
<TextView <LinearLayout
android:id="@+id/last_mod"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:gravity="end" android:orientation="horizontal"
android:text="@string/placeholder_media_time" android:layout_weight="1">
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:textSize="@dimen/two_line_secondary_text_size" /> <TextView
android:id="@+id/file_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/placeholder_fileSize"
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:textSize="@dimen/two_line_secondary_text_size" />
<TextView
android:id="@+id/file_separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingStart="@dimen/zero"
android:paddingEnd="@dimen/standard_quarter_padding"
android:text="@string/info_separator"
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:textSize="@dimen/two_line_secondary_text_size" />
<TextView
android:id="@+id/last_mod"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:text="@string/placeholder_media_time"
android:textColor="@color/list_item_lastmod_and_filesize_text"
android:textSize="@dimen/two_line_secondary_text_size" />
</LinearLayout>
</LinearLayout> </LinearLayout>
@ -161,9 +193,9 @@
<com.owncloud.android.ui.AvatarGroupLayout <com.owncloud.android.ui.AvatarGroupLayout
android:id="@+id/sharedAvatars" android:id="@+id/sharedAvatars"
android:layout_width="100dp" android:layout_width="100dp"
android:gravity="center_vertical"
android:layout_height="@dimen/file_icon_size" android:layout_height="@dimen/file_icon_size"
android:contentDescription="@string/shared_avatar_desc" android:contentDescription="@string/shared_avatar_desc"
android:gravity="center_vertical"
android:visibility="visible" /> android:visibility="visible" />
<ImageView <ImageView

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- Beta indicator --> <!-- Beta indicator -->
<bool name="is_beta">false</bool> <bool name="is_beta">true</bool>
<bool name="dev_version_direct_download_enabled">false</bool> <bool name="dev_version_direct_download_enabled">false</bool>
<!-- App name and other strings--> <!-- App name and other strings-->

View file

@ -1077,4 +1077,5 @@
<string name="document_scan_export_dialog_images">Multiple images</string> <string name="document_scan_export_dialog_images">Multiple images</string>
<string name="download_cannot_create_file">Cannot create local file</string> <string name="download_cannot_create_file">Cannot create local file</string>
<string name="download_download_invalid_local_file_name">Invalid filename for local file</string> <string name="download_download_invalid_local_file_name">Invalid filename for local file</string>
<string name="tags_more">+%1$d</string>
</resources> </resources>

View file

@ -8,7 +8,7 @@ buildscript {
daggerVersion = "2.45" daggerVersion = "2.45"
markwonVersion = "4.6.2" markwonVersion = "4.6.2"
prismVersion = "2.0.0" prismVersion = "2.0.0"
androidLibraryVersion = "master-SNAPSHOT" androidLibraryVersion = "tags-SNAPSHOT"
mockitoVersion = "4.11.0" mockitoVersion = "4.11.0"
mockitoKotlinVersion = "4.1.0" mockitoKotlinVersion = "4.1.0"
mockkVersion = "1.13.3" mockkVersion = "1.13.3"