Skip to content

Commit 2e8ac4a

Browse files
committed
fix(share-activity): ui/ux
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
1 parent cd9937a commit 2e8ac4a

3 files changed

Lines changed: 158 additions & 151 deletions

File tree

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@
635635
android:exported="false"
636636
android:label="@string/share_dialog_title"
637637
android:launchMode="singleTop"
638-
android:theme="@style/Theme.ownCloud.Dialog.NoTitle"
638+
android:theme="@style/Theme.ownCloud.Toolbar"
639639
android:windowSoftInputMode="adjustResize">
640640
<intent-filter>
641641
<action android:name="android.intent.action.SEARCH" />
Lines changed: 144 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Nextcloud - Android Client
33
*
4+
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
45
* SPDX-FileCopyrightText: 2018-2022 Tobias Kaminsky <tobias@kaminsky.me>
56
* SPDX-FileCopyrightText: 2019 Chris Narkiewicz <hello@ezaquarii.com>
67
* SPDX-FileCopyrightText: 2017 Andy Scherzinger <info@andy-scherzinger.de>
@@ -10,183 +11,177 @@
1011
* SPDX-FileCopyrightText: 2015 María Asensio Valverde <masensio@solidgear.es>
1112
* SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only)
1213
*/
13-
package com.owncloud.android.ui.activity;
14-
15-
import android.app.Activity;
16-
import android.graphics.Bitmap;
17-
import android.graphics.drawable.LayerDrawable;
18-
import android.os.Bundle;
19-
20-
import com.nextcloud.client.account.User;
21-
import com.owncloud.android.R;
22-
import com.owncloud.android.databinding.ShareActivityBinding;
23-
import com.owncloud.android.datamodel.OCFile;
24-
import com.owncloud.android.datamodel.SyncedFolderObserver;
25-
import com.owncloud.android.datamodel.SyncedFolderProvider;
26-
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
27-
import com.owncloud.android.lib.common.operations.RemoteOperation;
28-
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
29-
import com.owncloud.android.lib.common.utils.Log_OC;
30-
import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation;
31-
import com.owncloud.android.lib.resources.files.model.RemoteFile;
32-
import com.owncloud.android.lib.resources.shares.ShareType;
33-
import com.owncloud.android.operations.GetSharesForFileOperation;
34-
import com.owncloud.android.ui.fragment.FileDetailSharingFragment;
35-
import com.owncloud.android.ui.fragment.FileDetailsSharingProcessFragment;
36-
import com.owncloud.android.utils.DisplayUtils;
37-
import com.owncloud.android.utils.MimeTypeUtil;
38-
39-
import java.util.Optional;
40-
41-
import javax.inject.Inject;
42-
43-
import androidx.fragment.app.Fragment;
44-
import androidx.fragment.app.FragmentTransaction;
45-
46-
/**
47-
* Activity for sharing files.
48-
*/
49-
public class ShareActivity extends FileActivity {
14+
package com.owncloud.android.ui.activity
15+
16+
import android.content.Intent
17+
import android.os.Bundle
18+
import androidx.lifecycle.lifecycleScope
19+
import com.nextcloud.client.account.User
20+
import com.owncloud.android.R
21+
import com.owncloud.android.databinding.ShareActivityBinding
22+
import com.owncloud.android.datamodel.OCFile
23+
import com.owncloud.android.datamodel.SyncedFolderObserver
24+
import com.owncloud.android.datamodel.ThumbnailsCacheManager
25+
import com.owncloud.android.lib.common.operations.RemoteOperation
26+
import com.owncloud.android.lib.common.operations.RemoteOperationResult
27+
import com.owncloud.android.lib.common.utils.Log_OC
28+
import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation
29+
import com.owncloud.android.lib.resources.files.model.RemoteFile
30+
import com.owncloud.android.lib.resources.shares.ShareType
31+
import com.owncloud.android.operations.GetSharesForFileOperation
32+
import com.owncloud.android.ui.fragment.FileDetailSharingFragment
33+
import com.owncloud.android.ui.fragment.FileDetailsSharingProcessFragment
34+
import com.owncloud.android.utils.DisplayUtils
35+
import com.owncloud.android.utils.MimeTypeUtil
36+
import kotlinx.coroutines.Dispatchers
37+
import kotlinx.coroutines.launch
38+
import kotlinx.coroutines.withContext
39+
40+
class ShareActivity : FileActivity() {
41+
42+
override fun onCreate(savedInstanceState: Bundle?) {
43+
super.onCreate(savedInstanceState)
44+
45+
val binding = ShareActivityBinding.inflate(layoutInflater)
46+
setContentView(binding.root)
47+
48+
val currentUser = user.orElse(null) ?: run {
49+
finish()
50+
return
51+
}
52+
val file = file ?: run {
53+
finish()
54+
return
55+
}
56+
57+
setupHeader(binding, file, currentUser)
58+
fetchRemoteFileSize(file, currentUser, binding)
5059

51-
private static final String TAG = ShareActivity.class.getSimpleName();
60+
if (savedInstanceState == null) {
61+
showSharingFragment(file, currentUser)
62+
}
63+
}
5264

53-
static final String TAG_SHARE_FRAGMENT = "SHARE_FRAGMENT";
65+
override fun onStart() {
66+
super.onStart()
67+
Log_OC.d(TAG, "Refreshing lists on account set")
68+
refreshSharesFromStorageManager()
69+
}
5470

55-
@Inject
56-
SyncedFolderProvider syncedFolderProvider;
71+
override fun doShareWith(shareeName: String, shareType: ShareType) {
72+
val file = file ?: return
73+
supportFragmentManager.beginTransaction()
74+
.replace(
75+
R.id.share_fragment_container,
76+
FileDetailsSharingProcessFragment.newInstance(file, shareeName, shareType, false),
77+
FileDetailsSharingProcessFragment.TAG
78+
)
79+
.commit()
80+
}
5781

58-
@Override
59-
protected void onCreate(Bundle savedInstanceState) {
60-
super.onCreate(savedInstanceState);
82+
override fun onRemoteOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) {
83+
super.onRemoteOperationFinish(operation, result)
6184

62-
ShareActivityBinding binding = ShareActivityBinding.inflate(getLayoutInflater());
63-
setContentView(binding.getRoot());
85+
val isShareNotFound = operation is GetSharesForFileOperation &&
86+
result.code == RemoteOperationResult.ResultCode.SHARE_NOT_FOUND
6487

65-
OCFile file = getFile();
66-
Optional<User> optionalUser = getUser();
67-
if (optionalUser.isEmpty()) {
68-
finish();
69-
return;
88+
if (result.isSuccess || isShareNotFound) {
89+
Log_OC.d(TAG, "Refreshing view on successful operation or finished refresh")
90+
refreshSharesFromStorageManager()
7091
}
92+
}
93+
94+
override fun onShareProcessClosed() {
95+
finish()
96+
}
7197

72-
// Icon
73-
if (file.isFolder()) {
74-
boolean isAutoUploadFolder = SyncedFolderObserver.INSTANCE.isAutoUploadFolder(file, optionalUser.get());
98+
private fun setupHeader(binding: ShareActivityBinding, file: OCFile, user: User) {
99+
binding.shareBackButton.setOnClickListener { navigateToParentFolder(user) }
100+
setupFileIcon(binding, file, user)
101+
with(binding) {
102+
shareFileName.text = getString(R.string.share_file, file.fileName)
103+
viewThemeUtils.platform.colorViewBackground(shareHeaderDivider)
104+
shareFileSize.text = DisplayUtils.bytesToHumanReadable(file.fileLength)
105+
}
106+
}
75107

76-
Integer overlayIconId = file.getFileOverlayIconId(isAutoUploadFolder);
77-
LayerDrawable drawable = MimeTypeUtil.getFolderIcon(preferences.isDarkModeEnabled(), overlayIconId, this, viewThemeUtils);
78-
binding.shareFileIcon.setImageDrawable(drawable);
108+
private fun setupFileIcon(binding: ShareActivityBinding, file: OCFile, user: User) {
109+
if (file.isFolder) {
110+
val isAutoUploadFolder = SyncedFolderObserver.isAutoUploadFolder(file, user)
111+
val overlayIconId = file.getFileOverlayIconId(isAutoUploadFolder)
112+
binding.shareFileIcon.setImageDrawable(
113+
MimeTypeUtil.getFolderIcon(preferences.isDarkModeEnabled(), overlayIconId, this, viewThemeUtils)
114+
)
79115
} else {
80-
binding.shareFileIcon.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(),
81-
file.getFileName(),
82-
this,
83-
viewThemeUtils));
116+
binding.shareFileIcon.setImageDrawable(
117+
MimeTypeUtil.getFileTypeIcon(file.mimeType, file.fileName, this, viewThemeUtils)
118+
)
84119
if (MimeTypeUtil.isImage(file)) {
85-
String remoteId = String.valueOf(file.getRemoteId());
86-
Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(remoteId);
87-
if (thumbnail != null) {
88-
binding.shareFileIcon.setImageBitmap(thumbnail);
120+
ThumbnailsCacheManager.getBitmapFromDiskCache(file.remoteId.toString())?.let {
121+
binding.shareFileIcon.setImageBitmap(it)
89122
}
90123
}
91124
}
125+
}
92126

93-
// Name
94-
binding.shareFileName.setText(getResources().getString(R.string.share_file, file.getFileName()));
95-
96-
viewThemeUtils.platform.colorViewBackground(binding.shareHeaderDivider);
97-
98-
// Size
99-
binding.shareFileSize.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
100-
101-
Activity activity = this;
102-
new Thread(() -> {
103-
RemoteOperationResult result = new ReadFileRemoteOperation(getFile().getRemotePath())
104-
.execute(optionalUser.get(),
105-
activity);
106-
107-
if (result.isSuccess()) {
108-
RemoteFile remoteFile = (RemoteFile) result.getData().get(0);
109-
long length = remoteFile.getLength();
110-
111-
getFile().setFileLength(length);
112-
runOnUiThread(() -> binding.shareFileSize.setText(DisplayUtils.bytesToHumanReadable(length)));
127+
private fun fetchRemoteFileSize(file: OCFile, user: User, binding: ShareActivityBinding) {
128+
lifecycleScope.launch(Dispatchers.IO) {
129+
val result = ReadFileRemoteOperation(file.remotePath).execute(user, this@ShareActivity)
130+
if (result.isSuccess) {
131+
val length = (result.data.first() as RemoteFile).length
132+
file.fileLength = length
133+
withContext(Dispatchers.Main) {
134+
binding.shareFileSize.text = DisplayUtils.bytesToHumanReadable(length)
135+
}
113136
}
114-
}).start();
115-
116-
if (savedInstanceState == null) {
117-
// Add Share fragment on first creation
118-
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
119-
Fragment fragment = FileDetailSharingFragment.newInstance(getFile(), optionalUser.get());
120-
ft.replace(R.id.share_fragment_container, fragment, TAG_SHARE_FRAGMENT);
121-
ft.commit();
122137
}
123138
}
124139

125-
@Override
126-
protected void onStart() {
127-
super.onStart();
128-
129-
// Load data into the list
130-
Log_OC.d(TAG, "Refreshing lists on account set");
131-
refreshSharesFromStorageManager();
132-
}
133-
134-
@Override
135-
protected void doShareWith(String shareeName, ShareType shareType) {
136-
getSupportFragmentManager().beginTransaction().replace(R.id.share_fragment_container,
137-
FileDetailsSharingProcessFragment.newInstance(getFile(),
138-
shareeName,
139-
shareType,
140-
false),
141-
FileDetailsSharingProcessFragment.TAG)
142-
.commit();
140+
private fun showSharingFragment(file: OCFile, user: User) {
141+
val fragment = FileDetailSharingFragment.newInstance(file, user)
142+
supportFragmentManager.beginTransaction()
143+
.replace(R.id.share_fragment_container, fragment, TAG_SHARE_FRAGMENT)
144+
.commit()
143145
}
144146

145-
/**
146-
* Updates the view associated to the activity after the finish of some operation over files in the current
147-
* account.
148-
*
149-
* @param operation Removal operation performed.
150-
* @param result Result of the removal.
151-
*/
152-
@Override
153-
public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
154-
super.onRemoteOperationFinish(operation, result);
155-
156-
if (result.isSuccess() ||
157-
(operation instanceof GetSharesForFileOperation &&
158-
result.getCode() == RemoteOperationResult.ResultCode.SHARE_NOT_FOUND
159-
)
160-
) {
161-
Log_OC.d(TAG, "Refreshing view on successful operation or finished refresh");
162-
refreshSharesFromStorageManager();
147+
private fun refreshSharesFromStorageManager() {
148+
shareFileFragment?.takeIf { it.isAdded }?.run {
149+
refreshCapabilitiesFromDB()
150+
refreshSharesFromDB()
163151
}
164152
}
165153

166-
/**
167-
* Updates the view, reading data from {@link com.owncloud.android.datamodel.FileDataStorageManager}.
168-
*/
169-
private void refreshSharesFromStorageManager() {
154+
private val shareFileFragment: FileDetailSharingFragment?
155+
get() = supportFragmentManager.findFragmentByTag(TAG_SHARE_FRAGMENT) as? FileDetailSharingFragment
170156

171-
FileDetailSharingFragment shareFileFragment = getShareFileFragment();
172-
if (shareFileFragment != null
173-
&& shareFileFragment.isAdded()) { // only if added to the view hierarchy!!
174-
shareFileFragment.refreshCapabilitiesFromDB();
175-
shareFileFragment.refreshSharesFromDB();
157+
private fun navigateToParentFolder(user: User) {
158+
val file = file ?: run {
159+
finish()
160+
return
161+
}
162+
val parentFolder = if (file.isFolder) {
163+
file
164+
} else {
165+
storageManager.getFileByDecryptedRemotePath(file.parentRemotePath)
166+
}
167+
168+
if (parentFolder == null) {
169+
finish()
170+
return
176171
}
177-
}
178172

179-
/**
180-
* Shortcut to get access to the {@link FileDetailSharingFragment} instance, if any
181-
*
182-
* @return A {@link FileDetailSharingFragment} instance, or null
183-
*/
184-
private FileDetailSharingFragment getShareFileFragment() {
185-
return (FileDetailSharingFragment) getSupportFragmentManager().findFragmentByTag(TAG_SHARE_FRAGMENT);
173+
Intent(this, FileDisplayActivity::class.java).apply {
174+
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
175+
putExtra(EXTRA_FILE, parentFolder)
176+
putExtra(EXTRA_USER, user)
177+
}.also {
178+
startActivity(it)
179+
finish()
180+
}
186181
}
187182

188-
@Override
189-
public void onShareProcessClosed() {
190-
finish();
183+
companion object {
184+
private val TAG: String = ShareActivity::class.java.simpleName
185+
const val TAG_SHARE_FRAGMENT: String = "SHARE_FRAGMENT"
191186
}
192187
}

app/src/main/res/layout/share_activity.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,24 @@
2121
android:layout_height="wrap_content"
2222
android:padding="@dimen/standard_padding">
2323

24+
<ImageButton
25+
android:id="@+id/shareBackButton"
26+
android:layout_width="wrap_content"
27+
android:layout_height="wrap_content"
28+
android:layout_alignParentStart="true"
29+
android:layout_centerVertical="true"
30+
android:background="?attr/selectableItemBackgroundBorderless"
31+
android:contentDescription="@string/common_back"
32+
android:src="@drawable/ic_arrow_back" />
33+
2434
<ImageView
2535
android:id="@+id/shareFileIcon"
2636
android:layout_width="@dimen/file_icon_size"
2737
android:layout_height="@dimen/file_icon_size"
28-
android:layout_gravity="center_vertical"
38+
android:layout_centerVertical="true"
39+
android:layout_marginStart="@dimen/standard_half_margin"
2940
android:layout_marginEnd="@dimen/standard_half_margin"
41+
android:layout_toEndOf="@+id/shareBackButton"
3042
android:contentDescription="@null"
3143
android:src="@drawable/file" />
3244

0 commit comments

Comments
 (0)