Skip to content
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

Add UI for image-attachment "focus" #2620

Merged
merged 28 commits into from
Sep 21, 2022
Merged

Conversation

mcclure
Copy link
Collaborator

@mcclure mcclure commented Jul 16, 2022

Choose "Set focus" in the attachment menu, tap once to select focus point, tap "OK". Has been successfully tested with mastodon.social (image has two identical very tall image attachments, but in one the focus point is set near the top and in the other it is set near the bottom).

Currently very ugly, no visual feedback of where/whether you tapped and I think a naive user would be totally confused by what the dialog even does: See new screenshots

But, it works, so should be a good starting point for a more complete attempt.

Things I think should change before submit:

  • Thumbnails in compose view should refocus on focus point
  • Some visual feedback of tap, a bullseye or something
  • I need to do more aggressive testing (changing focus points multiple times, changing caption before and after etc)

Things I'm 50% sure should change before submit:

  • It would be nice if you could drag a finger to move around the focus point instead of only taps working (this may require replacing chrisbanes.photoview)
  • Currently if you tap outside the photo view (like slightly to the right or left) the tap does not register. That's very bad (may require replacing chrisbanes.photoview)
  • Some text above the image would be good. Possibly either a full explanation of how focus points work or a link to one. It could be an unfamiliar concept.
  • Some sort of preview would be nice. Ideally the preview would let you choose a couple different aspect ratios like 16:9 or 1:1. Maybe that could be saved for a followup PR.
  • LATE EDIT IN: Circle thickness is a fixed 10 pixels, but in my testing (see below) this appears to be relative to the underlying image. It should be a fixed thickness relative to the screen size.

Things I'm not sure about at all:

  • What should the menu item say? Currently it's "Set focus". Would something like "Image focus" or "Set focus point" be better?
  • TODO item in ComposeViewModel.kt
  • Currently the pane is inset (like "set caption"), maybe it would be good if the pane were "full screen" (like the cropper)
  • LATE EDIT IN: Should there be a focus checkmark image like there is for captions?
  • LATE EDIT IN: If you zoom in, then tap, it zooms all the way back out. This is functionally kind of good but feels "janky".
  • LATE EDIT IN: Pane closes if you rotate the phone

mcclure added 2 commits July 16, 2022 01:01
…s. Choose "Set focus" in the attachment menu, tap once to select focus point (no visual feedback currently), tap "OK". Works in tests.
@@ -324,11 +325,12 @@ class ComposeViewModel @Inject constructor(
return combineLiveData(deletionObservable, sendFlow.asLiveData()) { _, _ -> }
}

suspend fun updateDescription(localId: Int, description: String): Boolean {
// Updates a QueuedMedia item arbitrarily, then sends description and focus to server
suspend fun updateMediaItem(localId: Int, mutator: (QueuedMedia) -> QueuedMedia): Boolean {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Should this be private?

Copy link
Collaborator

Choose a reason for hiding this comment

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

yes

@mcclure
Copy link
Collaborator Author

mcclure commented Jul 17, 2022

Commit 613358e removes the code duplication in ComposeViewModel which I wanted, removes the function duplication in MastodonApi which Conny wanted, and has now been aggressively tested (IE change caption then focus, change focus then caption, change both more than once, everything is correct in the end)

@connyduck
Copy link
Collaborator

  • Thumbnails in compose view should refocus on focus point
  • Some visual feedback of tap, a bullseye or something
  • Currently if you tap outside the photo view (like slightly to the right or left) the tap does not register. That's very bad (may require replacing chrisbanes.photoview)

Refocusing thumbnails and visual feedback definitely, the third is not that important for a first version. Maybe also show the selected focus point on the thumbnail? How about a simple circle in primary color with theme background color as outline as indicator?

What should the menu item say? Currently it's "Set focus". Would something like "Image focus" or "Set focus point" be better?

I like "set focus point" most.

Currently the pane is inset (like "set caption"), maybe it would be good if the pane were "full screen" (like the cropper)

I think fullscreen would be better, but it isn't important.

Should there be a focus checkmark image like there is for captions?

If you want, definitely not needed for a first version

@mcclure
Copy link
Collaborator Author

mcclure commented Jul 23, 2022

The attachment previews in the compose view now focus properly where appropriate. ProgressImageView now inherits from MediaPreviewImageView to accomplish this. I also discovered Attachment.Focus and replaced my uses of PointF with it.

@mcclure
Copy link
Collaborator Author

mcclure commented Sep 3, 2022

Now have "previews" working, a focus is drawn when you tap in the focus-set dialog and in the normal message view the attachment previews center on the focus points. I styled the preview in the dialog to look as much as possible like the equivalent dialog in Mastodon.social web client (I think it looks good, though it's too bad the "center point" is always white since it's not visible on a white background. (Maybe it should be drawn as an inversion? But that might look uglier than it just sometimes not being visible)). Click for full versions:

I am going to take this PR out of draft mode because I think this is the "minimum viable" version of the feature, but it is still pretty janky and it may be worth more improvements before merging. Warning, I am not sure I can fix all of the remaining steps myself with my current knowledge of Android GUI and the codebase.

I think the remaining "should happen before putting this in a release" are:

  • A "Tap to select the point where crops will focus" message in the box (I can do this myself) (Not necessary if we can get dragging in?)
  • In the examples above, the first two images are the same size but the third is a different size. I think the circle thickness is visibly different. This should be fixed. (I can maybe figure this out on my own but at this second I don't know how to do it? The easy way would just be to scale up or down the stroke width depending on the ratio of the canvas size to the size of the displayed widget, but! I'm not sure how to get the "on-screen size" of the widget we're drawing into and I'm not sure how to tell if the Android is in "Retina mode"). (Another possible fix here would be if the gray+circle were a second widget drawn over the first instead of us actually compositing the gray+circle into the PhotoView itself each time.)
  • Super irritating that you can't drag to move the focus (I don't know where to start with this, it would seem to involve disabling the pan/zoom features of PhotoView)
  • Super irritating that you can't touch OUTSIDE the photo to move the focus to the closest point (I don't know where to start with this, PhotoView has a OnOutsidePhotoTapListener event but it only tells you there WAS a touch, not where it was: Way to get location of tap outside view? Baseflow/PhotoView#823) (One way to fix this would be to have there be a widget which is the entire width of the dialog, either by making the PhotoView wider or by placing a second widget on top of the PhotoView. But I don't know how to do that.) (This would also probably make more sense if the dialog were "full screen" as described above.)

Feedback welcomed on message text / how the focus selection looks.

@mcclure mcclure marked this pull request as ready for review September 3, 2022 23:38
Copy link
Collaborator Author

@mcclure mcclure left a comment

Choose a reason for hiding this comment

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

asdfadsffg i didn't mean to click this button

import java.security.MessageDigest
import java.util.concurrent.locks.Lock

// Private, but necessary to implement BitmapTransformation, function extracted from Glide
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This file now contains rather a lot of code adapted from Glide. There was no sample code for creating a custom BitmapTransformation in their docs so I used the RoundedCorners class and the TransformationUtils.roundedCorners method as bases. getAlphaSafeBitmap and getAlphaSafeConfig are taken direct from Glide TransformationUtils (but converted to Kotlin) and the "boilerplate" methods are almost identical to the ones from RoundedCorners. I do not think this introduces license issues because (1) we are already bound to the Glide license and (2) the code taken is "minimal" in the sense that it practically could not be any different than it is.

I am going to file a bug on Glide asking them to make getAlphaSafeBitmap and getAlphaSafeConfig public methods as I do not think it is possible to implement a BitmapTransformation without them.

val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG)
strokePaint.setAntiAlias(true)
strokePaint.setStyle(Paint.Style.STROKE)
val strokeWidth = 10.0f
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

As described above this should not be a fixed number

val result = pool[toTransform.width, toTransform.height, safeConfig]

val plainPaint = Paint()
val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Very unclear if ANTI_ALIAS_FLAG is required, I tried it both ways and I THINK it looks better with but it's hard to tell

}

if (!toTransform.equals(inBitmap)) {
pool.put(toTransform)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I am very, VERY confused if this is desirable or not. I think it's good because this will cache the image with the circle/shadow drawn on so if the set focus dialog is opened a second time it will open faster.

@mcclure
Copy link
Collaborator Author

mcclure commented Sep 5, 2022

Took in Conny's commit and I think this is mergeable? One last question:

curtainPaint.style = Paint.Style.FILL

strokePaint.setStyle(Paint.Style.STROKE)
strokePaint.setStrokeWidth(strokeWidth)
Copy link
Collaborator Author

@mcclure mcclure Sep 5, 2022

Choose a reason for hiding this comment

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

Will this be in display-independent pixels or do I need to add * getResources().getDisplayMetrics().density ?
EDIT: RESOLVED, it's not display-independent

@Tak
Copy link
Collaborator

Tak commented Sep 7, 2022

Is it possible to apply the focus point to the preview in the compose dialog? (could be a followup after merging)

@mcclure
Copy link
Collaborator Author

mcclure commented Sep 7, 2022

Is it possible to apply the focus point to the preview in the compose dialog? (could be a followup after merging)

It does this as of 7b3fd74, there is no unique indicator that a focus is present but the preview image does focus at the appropriate point

@Tak
Copy link
Collaborator

Tak commented Sep 7, 2022

Oh, I think I see - is it possible that the focal point is getting reversed in this scenario? Here, I'm setting the focal point in the upper left, and the preview is panning to the right edge. (This is against f4e4f8b )

Screenshot_20220907-190449
Screenshot_20220907-190515

@mcclure
Copy link
Collaborator Author

mcclure commented Sep 19, 2022

@Tak Ahh, thank you, good catch-- the X axis was inverted but the testcase I used on the previous push only tested the Y axis...!

I have a fix which I will push tomorrow.

@@ -169,6 +170,7 @@ class ComposeActivity :
uriNew,
size,
itemOld.description,
null, // Intentionally reset focus when cropping
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Is this the best approach? We could do something weird where we try to calculate where/whether the focal point is within the cropped area, but this feels like second guessing the user so I just reset.

@@ -185,7 +187,7 @@ class ComposeViewModel @Inject constructor(
return mediaItem
}

private fun addUploadedMedia(id: String, type: QueuedMedia.Type, uri: Uri, description: String?) {
private fun addUploadedMedia(id: String, type: QueuedMedia.Type, uri: Uri, description: String?, focus: Attachment.Focus?) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Long term observation: Adding focus to "travel along with" description when attachments hibernate/resurrect required adding plumbing in a LOT of places. It might make more sense to make a "Metadata" struct like the one already in Attachment and pass that around instead of description (although not Attachment.Meta itself, as that includes "duration", a metadata returned from the server but not tracked by us when composing attachments), in case later there is some third thing that we track per attachment (a low res version, I don't know)

holder.imageView.setImageResource(R.drawable.ic_music_box_preview_24dp)
} else {
Glide.with(holder.itemView.context)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Long term observation: This clause has (and had before I started changing it) significant code duplication VS MediaPreviewAdapter. Maybe it's worth a refactor here

@mcclure
Copy link
Collaborator Author

mcclure commented Sep 19, 2022

New changes:

  • Fix x flipping bug
  • Drafts, scheduled tweets, . Focus properly focuses in the draft view preview.
  • Crop window narrows itself vertically so it no longer has large amounts of empty space. Note, this ONLY affects vertical width, not horizontal width, horizontal width seems fine already.

Will post some screenshots of the new window sizing after I fix merging and do some exhaustive tests.

@mcclure
Copy link
Collaborator Author

mcclure commented Sep 19, 2022

How the new dialog sizes look-- they are sized to the image plus a little bit of room to draw the circle:

The window does NOT narrow horizontally if the image is wider than it is tall, I think that looks fine as is.

Landscape:

Screenshot_20220918-193824

@mcclure
Copy link
Collaborator Author

mcclure commented Sep 19, 2022

I've tested this version and I think it is a candidate for merging. The only remaining issues/questions:

  • Window disappears when the phone autorotates the screen. (Caption window now resizes itself properly.)
  • Should explanation text go above the image or is it obvious enough?

@connyduck
Copy link
Collaborator

It works so well now, I love it. Thank you very much.

Window disappears when the phone autorotates the screen. (Caption window now resizes itself properly.)

We had the same problem for the caption dialog, see here how it was fixed. It is no big deal though.

Should explanation text go above the image or is it obvious enough?

You mean the "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails." that the web interface has? Would be nice, yes but it is also no blocker for merging this.

Android studio gives me a lot of code style warnings, can you fix those please? It is mostly property access syntax vs getter/setter and Kotlin functions instead of java Math ones.

@mcclure
Copy link
Collaborator Author

mcclure commented Sep 20, 2022

I'm finding it a little hard to get full lists of Android Studio warnings, but I think those last two commits fix all the warnings in code the PR touches.

I left some warnings as-is:

  • Many claims that "Tusky" is a typo 🙄
  • Ignored an if-null foldable in FocusIndicatorView because the suggested alternative would be pretty ugly
  • Ignored "'SOFT_INPUT_ADJUST_RESIZE: Int' is deprecated. Deprecated in Java" in FocusDialog; I copied this code from CaptionDialog and I don't at present know either what it does or what to replace it with

I also explicitly @SuppressLint'd and commented an accessibility warning because FocusIndicatorView implements a touch event but not the "click" event from the screen reader and there's legitimately nothing that can be done with the screen reader click. FocusIndicatorView interaction is not meaningful without an x,y position. If there's something better I should do here instead (implement performClick but return false because we didn't handle it? hide focus select from screen readers completely? or is there actually a way to make this pane accessible?) I'd be happy to do that instead.

I wouldn't mind trying to replicate the CaptionDialog refactor at some later point, but I'd prefer to merge this PR first so that the PR does not develop conflicts with the develop branch.

@connyduck
Copy link
Collaborator

I also explicitly @SuppressLint'd and commented an accessibility warning because FocusIndicatorView implements a touch event but not the "click" event from the screen reader and there's legitimately nothing that can be done with the screen reader click. FocusIndicatorView interaction is not meaningful without an x,y position. If there's something better I should do here instead (implement performClick but return false because we didn't handle it? hide focus select from screen readers completely? or is there actually a way to make this pane accessible?) I'd be happy to do that instead.

I'm not sure. I'd leave it for now and wait for feedback from people using Tusky with screenreaders.

@connyduck connyduck merged commit 7684f06 into tuskyapp:develop Sep 21, 2022
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.

3 participants