Repository navigation
Feat: "Add to Home screen" option to create folder shortcuts with deep link #219
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
Lightz2002
wants to merge
14
commits into
opencloud-eu:main
Choose a base branch
from
Lightz2002:main
base: main
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.
+501
−62
Open
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
cd4fc3f
add folder path to home screen
dd2968d
Add Filename Validation
Lightz2002 2df383d
shorten method name
adee8e7
Merge branch 'opencloud-eu:main' into main
Lightz2002 530696f
prevent app crash after folder deleted or renamed
be34190
fix wrong notif message handling
ba4bd66
Revert "fix wrong notif message handling"
ebd2f41
Merge branch 'opencloud-eu:main' into main
Lightz2002 d0800fa
invert wrong toast message
62f843b
Add exported trampoline so pinned folder shortcuts can open the app
810e2cc
Switch to the shortcut folder's account before resolving it
21d6489
Enforce Android shortcut label length limits
a87ab9e
Show shortcut-added toast only from the pin result callback
4fcea71
Handle Add to Home screen in the selection action mode
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
Some comments aren't visible on the classic Files Changed page.
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
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
131 changes: 131 additions & 0 deletions
131
.../eu/opencloud/android/presentation/files/addtohomescreen/AddToHomeScreenDialogFragment.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,131 @@ | ||
| package eu.opencloud.android.presentation.files.addtohomescreen | ||
|
|
||
| import android.app.Dialog | ||
| import android.os.Bundle | ||
| import android.view.WindowManager | ||
| import android.widget.EditText | ||
| import androidx.appcompat.app.AlertDialog | ||
| import androidx.coordinatorlayout.widget.CoordinatorLayout | ||
| import androidx.core.widget.doOnTextChanged | ||
| import androidx.fragment.app.DialogFragment | ||
| import com.google.android.material.textfield.TextInputLayout | ||
| import eu.opencloud.android.R | ||
| import eu.opencloud.android.domain.files.model.OCFile | ||
| import eu.opencloud.android.presentation.files.filelist.MainFileListFragment.Companion.forbiddenChars | ||
| import eu.opencloud.android.utils.PreferenceUtils | ||
|
|
||
| class AddToHomeScreenDialogFragment : DialogFragment() { | ||
|
|
||
| private lateinit var folder: OCFile | ||
| private lateinit var listener: AddToHomeScreenListener | ||
|
|
||
| override fun onCreate(savedInstanceState: Bundle?) { | ||
| super.onCreate(savedInstanceState) | ||
| folder = requireArguments().getParcelable(ARG_FOLDER)!! | ||
| listener = parentFragment as AddToHomeScreenListener | ||
| } | ||
|
|
||
| override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { | ||
| val inflater = requireActivity().layoutInflater | ||
| val view = inflater.inflate(R.layout.edit_box_dialog, null) | ||
|
|
||
| view.filterTouchesWhenObscured = | ||
| PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(context) | ||
|
|
||
| val coordinatorLayout: CoordinatorLayout = requireActivity().findViewById(R.id.coordinator_layout) | ||
| coordinatorLayout.filterTouchesWhenObscured = | ||
| PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(context) | ||
|
|
||
| val inputText: EditText = view.findViewById(R.id.user_input) | ||
| val inputLayout: TextInputLayout = view.findViewById(R.id.edit_box_input_text_layout) | ||
|
|
||
| inputText.setText(folder.fileName) | ||
| inputText.selectAll() | ||
| inputText.requestFocus() | ||
|
|
||
| val builder = AlertDialog.Builder(requireActivity()) | ||
| builder.setView(view) | ||
| .setPositiveButton(R.string.add_to_home_screen_dialog_add_button) { dialog, _ -> | ||
| val name = inputText.text.toString().trim() | ||
| if (name.isNotBlank()) { | ||
| listener.onAddToHomeScreen(name, folder) | ||
| } | ||
| dialog.dismiss() | ||
| } | ||
| .setNegativeButton(android.R.string.cancel, null) | ||
| .setTitle(R.string.add_to_home_screen_dialog_title) | ||
|
|
||
| val alertDialog = builder.create() | ||
|
|
||
| alertDialog.setOnShowListener { | ||
| val okButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) | ||
| okButton.isEnabled = inputText.text.isNullOrBlank().not() | ||
|
|
||
| okButton.setOnClickListener { | ||
| val name = inputText.text.toString().trim() | ||
| val error = when { | ||
| name.isBlank() -> getString(R.string.add_to_home_screen_dialog_error_empty) | ||
| name.length > FolderShortcutHelper.MAX_LONG_LABEL_LENGTH -> String.format( | ||
| getString(R.string.add_to_home_screen_dialog_error_length_max), | ||
| FolderShortcutHelper.MAX_LONG_LABEL_LENGTH | ||
| ) | ||
| forbiddenChars.any { name.contains(it) } -> getString(R.string.filename_forbidden_characters) | ||
| else -> null | ||
| } | ||
|
|
||
| if (error == null) { | ||
| listener.onAddToHomeScreen(name, folder) | ||
| alertDialog.dismiss() | ||
| } else { | ||
| inputLayout.error = error | ||
| } | ||
| } | ||
| } | ||
|
|
||
| inputText.doOnTextChanged { text, _, _, _ -> | ||
| val okButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) | ||
| var error: String? = null | ||
|
|
||
| if (text.isNullOrBlank()) { | ||
| okButton.isEnabled = false | ||
| error = getString(R.string.add_to_home_screen_dialog_error_empty) | ||
| } else if (text.length > FolderShortcutHelper.MAX_LONG_LABEL_LENGTH) { | ||
| error = String.format( | ||
| getString(R.string.add_to_home_screen_dialog_error_length_max), | ||
| FolderShortcutHelper.MAX_LONG_LABEL_LENGTH | ||
| ) | ||
| } else if (forbiddenChars.any { text.contains(it) }) { | ||
| error = getString(R.string.filename_forbidden_characters) | ||
| } else { | ||
| okButton.isEnabled = true | ||
| } | ||
|
|
||
| if (error != null) { | ||
| okButton.isEnabled = false | ||
| inputLayout.error = error | ||
| } else { | ||
| inputLayout.error = null | ||
| } | ||
| } | ||
|
|
||
| alertDialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE) | ||
| return alertDialog | ||
| } | ||
|
|
||
| interface AddToHomeScreenListener { | ||
| fun onAddToHomeScreen(shortcutName: String, folder: OCFile) | ||
| } | ||
|
|
||
| companion object { | ||
| const val TAG = "ADD_TO_HOME_SCREEN_DIALOG" | ||
| private const val ARG_FOLDER = "ARG_FOLDER" | ||
|
|
||
| @JvmStatic | ||
| fun newInstance(folder: OCFile): AddToHomeScreenDialogFragment = | ||
| AddToHomeScreenDialogFragment().apply { | ||
| arguments = Bundle().apply { | ||
| putParcelable(ARG_FOLDER, folder) | ||
| } | ||
| } | ||
| } | ||
| } |
79 changes: 79 additions & 0 deletions
79
...main/java/eu/opencloud/android/presentation/files/addtohomescreen/FolderShortcutHelper.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,79 @@ | ||
| package eu.opencloud.android.presentation.files.addtohomescreen | ||
|
|
||
| import android.app.PendingIntent | ||
| import android.content.Context | ||
| import android.content.Intent | ||
| import android.content.pm.ShortcutInfo | ||
| import android.content.pm.ShortcutManager | ||
| import android.graphics.drawable.Icon | ||
| import android.os.Build | ||
| import android.widget.Toast | ||
| import androidx.annotation.RequiresApi | ||
| import eu.opencloud.android.R | ||
| import eu.opencloud.android.domain.files.model.OCFile | ||
|
|
||
| object FolderShortcutHelper { | ||
|
|
||
| const val EXTRA_SHORTCUT_FOLDER_REMOTE_ID = "SHORTCUT_FOLDER_REMOTE_ID" | ||
| const val EXTRA_SHORTCUT_FOLDER_REMOTE_PATH = "SHORTCUT_FOLDER_REMOTE_PATH" | ||
| const val EXTRA_SHORTCUT_FOLDER_SPACE_ID = "SHORTCUT_FOLDER_SPACE_ID" | ||
| const val EXTRA_SHORTCUT_FOLDER_ACCOUNT = "SHORTCUT_FOLDER_ACCOUNT" | ||
| const val ACTION_OPEN_SHORTCUT = | ||
| "eu.opencloud.android.ui.activity.action.OPEN_SHORTCUT" | ||
| const val ACTION_PIN_SHORTCUT_RESULT = | ||
| "eu.opencloud.android.presentation.files.action.PIN_SHORTCUT_RESULT" | ||
|
|
||
| const val MAX_SHORT_LABEL_LENGTH = 10 | ||
| const val MAX_LONG_LABEL_LENGTH = 25 | ||
|
|
||
| fun createPinnedShortcut(context: Context, folder: OCFile, shortcutName: String) { | ||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||
| createPinnedShortcutApi26(context, folder, shortcutName) | ||
| } else { | ||
| Toast.makeText(context, context.getString(R.string.add_to_home_screen_shortcut_not_supported), Toast.LENGTH_SHORT).show() | ||
| } | ||
| } | ||
|
|
||
| @RequiresApi(Build.VERSION_CODES.O) | ||
| private fun createPinnedShortcutApi26(context: Context, folder: OCFile, shortcutName: String) { | ||
| val shortcutManager = context.getSystemService(ShortcutManager::class.java) | ||
|
|
||
| if (shortcutManager?.isRequestPinShortcutSupported != true) { | ||
| Toast.makeText(context, context.getString(R.string.add_to_home_screen_shortcut_not_supported), Toast.LENGTH_SHORT).show() | ||
| return | ||
| } | ||
|
wernrab marked this conversation as resolved.
|
||
|
|
||
| val shortcutId = "folder_${folder.id}" | ||
|
|
||
| val shortcutIntent = Intent(context, ShortcutTrampolineActivity::class.java).apply { | ||
| action = ACTION_OPEN_SHORTCUT | ||
| putExtra(EXTRA_SHORTCUT_FOLDER_REMOTE_ID, folder.remoteId) | ||
| putExtra(EXTRA_SHORTCUT_FOLDER_REMOTE_PATH, folder.remotePath) | ||
| putExtra(EXTRA_SHORTCUT_FOLDER_SPACE_ID, folder.spaceId) | ||
| putExtra(EXTRA_SHORTCUT_FOLDER_ACCOUNT, folder.owner) | ||
| flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK | ||
| } | ||
|
|
||
| val shortcut = ShortcutInfo.Builder(context, shortcutId) | ||
| .setShortLabel(shortcutName.truncateForLabel(MAX_SHORT_LABEL_LENGTH)) | ||
| .setLongLabel(shortcutName.truncateForLabel(MAX_LONG_LABEL_LENGTH)) | ||
| .setIcon(Icon.createWithResource(context, R.mipmap.icon)) | ||
| .setIntent(shortcutIntent) | ||
|
Comment on lines
+57
to
+61
Copilot marked this conversation as resolved.
|
||
| .build() | ||
|
|
||
| val pinResultCallback = PendingIntent.getBroadcast( | ||
| context, | ||
| 0, | ||
| Intent(context, ShortcutPinResultReceiver::class.java).setAction(ACTION_PIN_SHORTCUT_RESULT), | ||
| PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE | ||
| ) | ||
|
|
||
| val requestAccepted = shortcutManager.requestPinShortcut(shortcut, pinResultCallback.intentSender) | ||
| if (!requestAccepted) { | ||
| Toast.makeText(context, context.getString(R.string.add_to_home_screen_shortcut_failed), Toast.LENGTH_SHORT).show() | ||
| } | ||
| } | ||
|
|
||
| private fun String.truncateForLabel(maxLength: Int): String = | ||
| if (length > maxLength) take(maxLength - 1) + "…" else this | ||
| } | ||
19 changes: 19 additions & 0 deletions
19
...java/eu/opencloud/android/presentation/files/addtohomescreen/ShortcutPinResultReceiver.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,19 @@ | ||
| package eu.opencloud.android.presentation.files.addtohomescreen | ||
|
|
||
| import android.content.BroadcastReceiver | ||
| import android.content.Context | ||
| import android.content.Intent | ||
| import android.widget.Toast | ||
| import eu.opencloud.android.R | ||
|
|
||
| class ShortcutPinResultReceiver : BroadcastReceiver() { | ||
|
|
||
| override fun onReceive(context: Context, intent: Intent) { | ||
| if (intent.action != FolderShortcutHelper.ACTION_PIN_SHORTCUT_RESULT) return | ||
| Toast.makeText( | ||
| context, | ||
| context.getString(R.string.add_to_home_screen_shortcut_added), | ||
| Toast.LENGTH_SHORT | ||
| ).show() | ||
| } | ||
| } |
34 changes: 34 additions & 0 deletions
34
...ava/eu/opencloud/android/presentation/files/addtohomescreen/ShortcutTrampolineActivity.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,34 @@ | ||
| package eu.opencloud.android.presentation.files.addtohomescreen | ||
|
|
||
| import android.app.Activity | ||
| import android.content.Intent | ||
| import android.os.Bundle | ||
| import eu.opencloud.android.ui.activity.FileDisplayActivity | ||
|
|
||
| class ShortcutTrampolineActivity : Activity() { | ||
|
|
||
| override fun onCreate(savedInstanceState: Bundle?) { | ||
| super.onCreate(savedInstanceState) | ||
| forwardShortcutIntent() | ||
| finish() | ||
| } | ||
|
|
||
| private fun forwardShortcutIntent() { | ||
| val shortcutIntent = intent ?: return | ||
| if (shortcutIntent.action != FolderShortcutHelper.ACTION_OPEN_SHORTCUT) return | ||
|
|
||
| val target = Intent(this, FileDisplayActivity::class.java).apply { | ||
| action = FolderShortcutHelper.ACTION_OPEN_SHORTCUT | ||
| copyStringExtra(shortcutIntent, FolderShortcutHelper.EXTRA_SHORTCUT_FOLDER_REMOTE_ID) | ||
| copyStringExtra(shortcutIntent, FolderShortcutHelper.EXTRA_SHORTCUT_FOLDER_REMOTE_PATH) | ||
| copyStringExtra(shortcutIntent, FolderShortcutHelper.EXTRA_SHORTCUT_FOLDER_SPACE_ID) | ||
| copyStringExtra(shortcutIntent, FolderShortcutHelper.EXTRA_SHORTCUT_FOLDER_ACCOUNT) | ||
| flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK | ||
| } | ||
| startActivity(target) | ||
| } | ||
|
|
||
| private fun Intent.copyStringExtra(source: Intent, key: String) { | ||
| source.getStringExtra(key)?.let { putExtra(key, it) } | ||
| } | ||
| } |
Oops, something went wrong.
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.