Skip to content

ui: Implement support for Android Dynamic Shortcuts #55

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
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

tturner
Copy link
Contributor

@tturner tturner commented Jun 11, 2023

This is an updated version of the pull request #48 to try to make it more likely to be merged.

The hard work was done by @gergely-sallai Gergely Sallai in their fork. I have tweaked it as follows:

  • update to compile against the current master branch
  • made a small cosmetic change to the alignment of the UI component
  • squashed into a single commit for simplicity

I hope this is the right way to get this merged and that I haven't broken any github etiquette!

Original pull request information reproduced below:

Added support for Android Dynamic Shortcuts. Dynamic Shortcuts is a standard Android feature that is supported by most of the launchers.

How does this work for users?
Now users can request the app to add shortcuts to their favourite tunnels. When the user enables this for a specific tunnel, 2 dynamic shortcuts are created, one to UP the tunnel, other is to DOWN it.

Additionally this functionality enables users to use certain automation apps to automatically toggle tunnels. For example the built-in Bixby Routines on Samsung devices. (This was my original motivation for adding this feature, but I was trying to incorporate it in a way that could benefit all users.)

What changes were made to previous components to support this?
TunnelToggleActivity is now more broadly used. Previously it seemed to be only called from TileService in certain situations.
A new button has been added next to the tunnel toggle switch for each tunnel item.
Added androidx shortcuts library
Limitations and possible points for future improvement
To my best knowledge all of these -- expect the first point -- were there previous to my PR, and I can help to sort these out if needed.

Tooltip popup for the "add shortcuts" button is only available for devices running API 26 and up. This could be solved generally via TooltipCompat, but I didn't want to complicate the code because of this. As doing this with the compat lib can not be done purely from XML..
TunnelToggleActivity can be shown briefly when a tunnel is toggled, and it can look weird. This could possibly be solved by adding some design to the activity and showing it a bit longer in a controlled manner, maybe with some animation.
There may be some misconfigurations around material colors, it caused me some rendering differences on certain OS version with the button colors. I believe this could be solved with a refactor to support Material You (M3) and its dynamic colors, and fall back to a default on older versions. I do not know what is the intended design language of the app, so I didn't dare to refactor it just yet. :)

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
This work was done by Gergely Sallai in their fork.
I have tweaked it as follows:
- update to compile against the current master branch
- made a small cosmetic change to the alignment of the UI component
- squashed into a single commit for simplicity

Signed-off-by: Tom Turner <[email protected]>
@MoLanShao

This comment was marked as off-topic.

@zx2c4-bot zx2c4-bot force-pushed the master branch 4 times, most recently from 827495b to 4ba8794 Compare October 22, 2023 00:35
@zx2c4-bot zx2c4-bot force-pushed the master branch 4 times, most recently from c1d59a2 to 6aab7cc Compare May 8, 2025 02:59
var hasShortcut: Boolean? = null
get() {
if (field == null)
// Opportunistically fetch this if we don't have a cached one, and rely on data bindings to update it eventually
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment indentation looks a little funny.

private fun downIdFor(name: String): String = "$name-DOWN"

private fun createShortcutIntent(action: String, tunnelName: String): Intent =
Intent(context, TunnelToggleActivity::class.java).apply {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also weird indentation here?

lifecycleScope.launch {
val tunnelAction = when(intent.action) {
"com.wireguard.android.action.SET_TUNNEL_UP" -> Tunnel.State.UP
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these strings be constants somewhere?

val tunnelAction = when(intent.action) {
"com.wireguard.android.action.SET_TUNNEL_UP" -> Tunnel.State.UP
"com.wireguard.android.action.SET_TUNNEL_DOWN" -> Tunnel.State.DOWN
else -> Tunnel.State.TOGGLE // Implicit toggle to keep previous behaviour
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Keep previous behavior" is not a useful code comment. Just remove that comment.

/**
* TileService is only available for API 24+, if it's available it'll be updated,
* otherwise it's ignored.
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment doesn't help much either. Get rid of it.

@@ -108,6 +109,18 @@ abstract class BaseFragment : Fragment(), OnSelectedTunnelChangedListener {
}
}

fun setShortcutState(view: CompoundButton, checked: Boolean) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can that just be a generic View? We don't actually need any CompoundButton members, right?

}

fun hasShortcut(name: String) =
ShortcutManagerCompat.getDynamicShortcuts(context).any { it.id.startsWith(name) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

startsWith is not a safe thing here, because tunnel names can be prefixes of each other.

@@ -65,6 +66,10 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() {
// Make sure nothing touches the tunnel.
if (wasLastUsed)
lastUsedTunnel = null
// Make sure we also remove any existing shortcuts.
if (shortcutManager.hasShortcut(tunnel.name)) {
shortcutManager.removeShortcuts(tunnel.name)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we just call remove unconditionally, and it'll be a no-op if they're not already there? What do we gain by first checking?

@zx2c4 zx2c4 self-assigned this May 12, 2025
@zx2c4
Copy link
Member

zx2c4 commented May 12, 2025

Thanks for this patch. And sorry it's taken so long to review it.

I left some comments for @gergely-sallai or @tturner to address.

One question: why not just have a single toggle action, rather than separate up&down actions? Seems to add a lot of GUI clutter.

android:onCheckedChanged="@{fragment::setShortcutState}"
android:tooltipText="@string/tooltip_tunnel_shortcut"
app:buttonTint="?attr/colorSecondary"
tools:checked="@sample/interface_names.json/names/shortcut/shortcut" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this can be more hidden or less prominent or something. This is not something most users want.

Another approach -- and the one we use on iOS -- which I think might be better is to just add toggle shortcuts for the last N used tunnels.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants