diff --git a/src/androidTest/java/com/owncloud/android/ui/helpers/FileOperationsHelperTest.java b/src/androidTest/java/com/owncloud/android/ui/helpers/FileOperationsHelperTest.java
new file mode 100644
index 000000000000..0137fbc9a10e
--- /dev/null
+++ b/src/androidTest/java/com/owncloud/android/ui/helpers/FileOperationsHelperTest.java
@@ -0,0 +1,4 @@
+package com.owncloud.android.ui.helpers;
+
+public class FileOperationsHelperTest {
+}
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 4edba5c5e1c1..fd2b53f214f7 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -111,12 +111,14 @@
android:theme="@style/Theme.ownCloud.Launcher">
-
+
+
+
diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java
index 7815fa40e2e4..cb9619ccbf38 100644
--- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java
+++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java
@@ -163,6 +163,7 @@ public class FileDisplayActivity extends FileActivity
public static final String RESTART = "RESTART";
public static final String ALL_FILES = "ALL_FILES";
public static final String PHOTO_SEARCH = "PHOTO_SEARCH";
+ public static final String OPEN_FILE = "NC_OPEN_FILE";
private SyncBroadcastReceiver mSyncBroadcastReceiver;
private UploadFinishReceiver mUploadFinishReceiver;
@@ -329,6 +330,11 @@ public void onClick(View v) {
upgradeNotificationForInstantUpload();
checkOutdatedServer();
+
+ if (OPEN_FILE.equals(getIntent().getAction())) {
+ getSupportFragmentManager().executePendingTransactions();
+ onOpenFileIntent(getIntent());
+ }
}
private Activity getActivity() {
@@ -511,6 +517,8 @@ protected void onNewIntent(Intent intent) {
showDetails(file);
} else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
handleOpenFileViaIntent(intent);
+ } else if (OPEN_FILE.equals(intent.getAction())) {
+ onOpenFileIntent(intent);
} else if (RESTART.equals(intent.getAction())) {
finish();
startActivity(intent);
@@ -568,6 +576,14 @@ protected void onNewIntent(Intent intent) {
}
}
+ private void onOpenFileIntent(Intent intent) {
+ String extra = intent.getStringExtra(EXTRA_FILE);
+ OCFile file = getStorageManager().getFileByPath(extra);
+
+ OCFileListFragment fileListFragment = getListOfFilesFragment();
+ fileListFragment.onItemClicked(file);
+ }
+
private void doShareWith(String shareeName, ShareType shareType) {
getFileOperationsHelper().shareFileWithSharee(getFile(), shareeName, shareType,
getAppropriatePermissions(shareType));
diff --git a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
index 5d255e9bf659..420ae3ba31b2 100644
--- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
+++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
@@ -90,6 +90,7 @@
import com.owncloud.android.ui.events.FavoriteEvent;
import com.owncloud.android.ui.events.SearchEvent;
import com.owncloud.android.ui.helpers.FileOperationsHelper;
+import com.owncloud.android.ui.helpers.MenuHelper;
import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface;
import com.owncloud.android.ui.preview.PreviewImageFragment;
import com.owncloud.android.ui.preview.PreviewMediaFragment;
@@ -494,7 +495,7 @@ public void showActivityDetailView(OCFile file) {
@Override
public void onOverflowIconClicked(OCFile file, View view) {
PopupMenu popup = new PopupMenu(getActivity(), view);
- popup.inflate(R.menu.item_file);
+ MenuHelper.inflateItemFileMenu(popup.getMenuInflater(), popup.getMenu());
Account currentAccount = ((FileActivity) getActivity()).getAccount();
FileMenuFilter mf = new FileMenuFilter(mAdapter.getFiles().size(),
Collections.singleton(file),
@@ -630,7 +631,7 @@ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mActiveActionMode = mode;
MenuInflater inflater = getActivity().getMenuInflater();
- inflater.inflate(R.menu.item_file, menu);
+ MenuHelper.inflateItemFileMenu(inflater, menu);
mode.invalidate();
//set gray color
@@ -1109,6 +1110,12 @@ public boolean onFileActionChosen(MenuItem item, Set checkedFiles) {
mContainerActivity.getFileOperationsHelper().toggleEncryption(singleFile, false);
return true;
}
+ case R.id.action_add_shortcut_to_homescreen: {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
+ mContainerActivity.getFileOperationsHelper().addShortcutToHomescreen(singleFile);
+ }
+ return true;
+ }
}
}
diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
index 79097e48add7..a900cf75c118 100755
--- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
+++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
@@ -29,12 +29,20 @@
import android.Manifest;
import android.accounts.Account;
import android.app.Activity;
+import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
@@ -45,6 +53,13 @@
import android.view.View;
import android.webkit.MimeTypeMap;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.core.content.FileProvider;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+
import com.evernote.android.job.JobRequest;
import com.nextcloud.client.account.CurrentAccountProvider;
import com.nextcloud.client.account.User;
@@ -53,6 +68,7 @@
import com.owncloud.android.R;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.ThumbnailsCacheManager;
import com.owncloud.android.files.StreamMediaFileOperation;
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
@@ -68,6 +84,7 @@
import com.owncloud.android.ui.activity.ConflictsResolveActivity;
import com.owncloud.android.ui.activity.ExternalSiteWebView;
import com.owncloud.android.ui.activity.FileActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.activity.RichDocumentsEditorWebView;
import com.owncloud.android.ui.activity.ShareActivity;
import com.owncloud.android.ui.activity.TextEditorWebView;
@@ -77,6 +94,7 @@
import com.owncloud.android.ui.events.SyncEventFinished;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.FileStorageUtils;
+import com.owncloud.android.utils.MimeTypeUtil;
import com.owncloud.android.utils.PermissionUtil;
import com.owncloud.android.utils.UriUtils;
@@ -98,13 +116,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.core.content.FileProvider;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentTransaction;
-
/**
* Helper implementation for file operations locally and remote.
*/
@@ -1028,6 +1039,73 @@ private void deleteOldFiles(Activity activity) {
}
}
+ /**
+ * Adds a pinned shortcut to the home screen that points to the passed file/folder.
+ *
+ * @param file The file/folder to which a pinned shortcut should be added to the home screen.
+ */
+ public void addShortcutToHomescreen(OCFile file) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ final ShortcutManager shortcutManager = fileActivity.getSystemService(ShortcutManager.class);
+
+ if (shortcutManager.isRequestPinShortcutSupported()) {
+ final Intent intent = new Intent(fileActivity, FileDisplayActivity.class);
+ intent.setAction(FileDisplayActivity.OPEN_FILE);
+ intent.putExtra(FileActivity.EXTRA_FILE, file.getRemotePath());
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+ final String shortcutId = "nextcloud_shortcut_" + file.getRemoteId();
+
+ Icon icon;
+
+ Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
+ ThumbnailsCacheManager.PREFIX_THUMBNAIL + file.getRemoteId());
+ if (thumbnail != null) {
+ thumbnail = bitmapToAdaptiveBitmap(thumbnail, fileActivity);
+ icon = Icon.createWithAdaptiveBitmap(thumbnail);
+ } else if (file.isFolder()) {
+ icon = Icon.createWithResource(fileActivity,
+ MimeTypeUtil.getFolderTypeIconId(file.isSharedWithMe() ||
+ file.isSharedWithSharee(), file.isSharedViaLink(), file.isEncrypted(), file.getMountType()));
+ } else {
+ icon = Icon.createWithResource(fileActivity,
+ MimeTypeUtil.getFileTypeIconId(file.getMimeType(), file.getFileName()));
+ }
+
+ final ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(fileActivity, shortcutId)
+ .setShortLabel(file.getFileName())
+ .setLongLabel("Open " + file.getFileName())
+ .setIcon(icon)
+ .setIntent(intent)
+ .build();
+
+ final Intent pinnedShortcutCallbackIntent =
+ shortcutManager.createShortcutResultIntent(pinShortcutInfo);
+
+ final PendingIntent successCallback = PendingIntent.getBroadcast(fileActivity, 0,
+ pinnedShortcutCallbackIntent, 0);
+
+ shortcutManager.requestPinShortcut(pinShortcutInfo,
+ successCallback.getIntentSender());
+ }
+ }
+ }
+
+ private static Bitmap bitmapToAdaptiveBitmap(final Bitmap orig, final Context context) {
+ final float screenDensity = context.getResources().getDisplayMetrics().density;
+ final int adaptiveIconSize = Math.round(108 * screenDensity);
+ final int adaptiveIconOuterSides = Math.round(18 * screenDensity);
+
+ Drawable drawable = new BitmapDrawable(context.getResources(), orig);
+
+ final Bitmap bitmap = Bitmap.createBitmap(adaptiveIconSize, adaptiveIconSize, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(adaptiveIconOuterSides, adaptiveIconOuterSides, adaptiveIconSize - adaptiveIconOuterSides,
+ adaptiveIconSize - adaptiveIconOuterSides);
+ drawable.draw(canvas);
+ return bitmap;
+ }
+
public static File createImageFile(Activity activity) {
File storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
diff --git a/src/main/java/com/owncloud/android/ui/helpers/MenuHelper.java b/src/main/java/com/owncloud/android/ui/helpers/MenuHelper.java
new file mode 100644
index 000000000000..3ce7771198a5
--- /dev/null
+++ b/src/main/java/com/owncloud/android/ui/helpers/MenuHelper.java
@@ -0,0 +1,28 @@
+package com.owncloud.android.ui.helpers;
+
+import android.os.Build;
+import android.view.Menu;
+import android.view.MenuInflater;
+
+import com.owncloud.android.R;
+
+/**
+ * Collection of helper functions for handling menus.
+ */
+public final class MenuHelper {
+
+ private MenuHelper() {
+
+ }
+
+ /**
+ * Inflates the item file menu and handles menu items according to SDK version.
+ *
+ * @param inflater The inflater to use for inflation of the item file menu.
+ * @param menu The menu into which to inflate.
+ */
+ public static void inflateItemFileMenu(final MenuInflater inflater, final Menu menu) {
+ inflater.inflate(R.menu.item_file, menu);
+ menu.findItem(R.id.action_add_shortcut_to_homescreen).setVisible(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O);
+ }
+}
diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java b/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java
index 9269460f4d79..69d058f77761 100644
--- a/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java
+++ b/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java
@@ -69,6 +69,7 @@
import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment;
import com.owncloud.android.ui.fragment.FileFragment;
+import com.owncloud.android.ui.helpers.MenuHelper;
import com.owncloud.android.utils.BitmapUtils;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.MimeType;
@@ -322,7 +323,7 @@ public void onStop() {
@Override
public void onCreateOptionsMenu(@NotNull Menu menu, @NotNull MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- inflater.inflate(R.menu.item_file, menu);
+ MenuHelper.inflateItemFileMenu(inflater, menu);
int nightModeFlag = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java b/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java
index 5484f18895c8..ef4aaade88fe 100644
--- a/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java
+++ b/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java
@@ -72,6 +72,7 @@
import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment;
import com.owncloud.android.ui.fragment.FileFragment;
+import com.owncloud.android.ui.helpers.MenuHelper;
import com.owncloud.android.utils.MimeTypeUtil;
import java.lang.ref.WeakReference;
@@ -340,7 +341,7 @@ private void stopAudio() {
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
menu.removeItem(R.id.action_search);
- inflater.inflate(R.menu.item_file, menu);
+ MenuHelper.inflateItemFileMenu(inflater, menu);
}
@Override
diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java b/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java
index cb899fb76bb6..5b8c9b480100 100644
--- a/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java
+++ b/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java
@@ -40,6 +40,7 @@
import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment;
+import com.owncloud.android.ui.helpers.MenuHelper;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.MimeTypeUtil;
@@ -235,7 +236,7 @@ protected void onPostExecute(final StringWriter stringWriter) {
@Override
public void onCreateOptionsMenu(@NotNull Menu menu, @NotNull MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- inflater.inflate(R.menu.item_file, menu);
+ MenuHelper.inflateItemFileMenu(inflater, menu);
MenuItem menuItem = menu.findItem(R.id.action_search);
menuItem.setVisible(true);
diff --git a/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java b/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java
index 969ed3058921..5cc9f46a2583 100644
--- a/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java
+++ b/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java
@@ -161,6 +161,13 @@ public static Drawable getFolderTypeIcon(boolean isSharedViaUsers, boolean isSha
public static Drawable getFolderTypeIcon(boolean isSharedViaUsers, boolean isSharedViaLink,
boolean isEncrypted, Account account, WebdavEntry.MountType mountType,
Context context) {
+ int drawableId = getFolderTypeIconId(isSharedViaUsers, isSharedViaLink, isEncrypted, mountType);
+
+ return ThemeUtils.tintDrawable(drawableId, ThemeUtils.elementColor(account, context));
+ }
+
+ public static int getFolderTypeIconId(boolean isSharedViaUsers, boolean isSharedViaLink,
+ boolean isEncrypted, WebdavEntry.MountType mountType) {
int drawableId;
if (isSharedViaLink) {
@@ -177,7 +184,7 @@ public static Drawable getFolderTypeIcon(boolean isSharedViaUsers, boolean isSha
drawableId = R.drawable.folder;
}
- return ThemeUtils.tintDrawable(drawableId, ThemeUtils.elementColor(account, context));
+ return drawableId;
}
public static Drawable getDefaultFolderIcon(Context context) {
diff --git a/src/main/res/menu/item_file.xml b/src/main/res/menu/item_file.xml
index 26a5e3ee7675..04068ade273c 100644
--- a/src/main/res/menu/item_file.xml
+++ b/src/main/res/menu/item_file.xml
@@ -137,4 +137,10 @@
app:showAsAction="never"
android:showAsAction="never" />
+
+
diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml
index 59d32acaca5a..81909b13a371 100644
--- a/src/main/res/values-de/strings.xml
+++ b/src/main/res/values-de/strings.xml
@@ -803,6 +803,7 @@
Was-gibt\'s-Neues-Bild
Überspringen
Neu in %1$s
+ Zum Startbildschirm hinzufügen
- Inhalte von %1$d Datei konnten nicht synchronisiert werden (Konflikte: %2$d)
- Inhalte von %1$d Dateien konnten nicht synchronisiert werden (Konflikte: %2$d)
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index a8f3d431c62f..bd75ce3f2df0 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -920,6 +920,7 @@
Delete
Create new
%1$s %2$s
+ Add to home screen
Choose what to sync
Free up space