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

Closes #1092 - Fixed contents cut off on smaller screen issue. #1095

Merged
merged 7 commits into from
Oct 25, 2022

Conversation

liutng
Copy link
Contributor

@liutng liutng commented Oct 16, 2022

Hi,

This PR closes #1092.

Change details are listed below.

Implemented

Small screens optimization

  • Added NestedScrollView to MainActivity to make it scrollable when the screen space is not enough for the UI to expand.
  • Added NestedScrollView to ScanActivity to make it scrollable when the screen space is not enough for the UI to expand.

Known issue

Visual

  • Screen may blink to black and soon recover when adjusting the app's window size, this will happen when dynamically adjusting app's window size in split-screen mode which most modern Android phones support.

@liutng liutng marked this pull request as ready for review October 17, 2022 14:49
@TheLastProject
Copy link
Member

While it is nice to be able to scroll, I'm not sure this is a better design. I think focusing on scaling would be much better.

For example, for me, the + button is over the "Welcome to Catima" text and there is no way users would know to scroll, so they don't get the hint about how to add cards:

image

In the ScanActivity the problem is worse, as the buttons are now pushed outside of the view, so users on small screens may not be aware they have other options than enabling the camera:

image

I know some logic exists to the scale text dynamically, maybe that will help? It may be useless for this case, but I figured I'd point it out just in case:

TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(cardIdFieldView,
settings.getFontSizeMin(settings.getLargeFont()), settings.getFontSizeMax(settings.getLargeFont()),
1, TypedValue.COMPLEX_UNIT_SP);

Aside from scaling the text it may also be good to hide the icon under certain screen heights to save some more space.

@liutng
Copy link
Contributor Author

liutng commented Oct 18, 2022

No problem, I will look into this and find a solution to properly scale it.

@liutng
Copy link
Contributor Author

liutng commented Oct 18, 2022

I made a new fix to this. Does it look better to you? video

@TheLastProject
Copy link
Member

That looks much much better! I do worry a bit about the "We can't access the camera" text disappearing, but there indeed is very little space for it so it may be the best possible option. Definitely seems like the right direction :)

Added a new mechanism to dynamically scale UI based on the screen ratio.
@liutng
Copy link
Contributor Author

liutng commented Oct 18, 2022

The updated code is now pushed.

Copy link
Member

@TheLastProject TheLastProject left a comment

Choose a reason for hiding this comment

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

It doesn't quite work right and there are some parts I don't understand

Comment on lines 404 to 406
} else
scaleScreen();

Copy link
Member

Choose a reason for hiding this comment

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

For single if statements I kinda get the no-brackets style (although I personally wouldn't use it), but I'd really prefer brackets here. For me personally, it is a lot more readable.

Comment on lines 253 to 281
private void scaleScreen() {
DisplayMetrics metrics = getResources().getDisplayMetrics();
float ratio = (float) metrics.heightPixels / metrics.widthPixels;
boolean shouldScaleSmaller = ratio <= 0.8f;
boolean shouldScaleMedium = ratio <= 1.1f && !shouldScaleSmaller;
if (shouldScaleMedium) {
customBarcodeScannerBinding.cameraPermissionDeniedLayout.cameraPermissionDeniedIcon.setVisibility( View.GONE );
} else {
int buttonMinHeight = getResources().getDimensionPixelSize(R.dimen.scan_button_min_height);
customBarcodeScannerBinding.cameraPermissionDeniedLayout.cameraPermissionDeniedTitle.setVisibility(shouldScaleSmaller ? View.GONE : View.VISIBLE);
customBarcodeScannerBinding.cameraPermissionDeniedLayout.cameraPermissionDeniedIcon.setVisibility(shouldScaleSmaller ? View.GONE : View.VISIBLE);
customBarcodeScannerBinding.cameraPermissionDeniedLayout.cameraPermissionDeniedMessage.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(shouldScaleSmaller ? R.dimen.no_data_min_textSize : R.dimen.no_data_max_textSize));
customBarcodeScannerBinding.addFromImage.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(shouldScaleSmaller ? R.dimen.scan_button_min_textSize : R.dimen.scan_button_max_textSize));
customBarcodeScannerBinding.addManually.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(shouldScaleSmaller ? R.dimen.scan_button_min_textSize : R.dimen.scan_button_max_textSize));
customBarcodeScannerBinding.addFromImage.setMinimumHeight(shouldScaleSmaller ? getResources().getDimensionPixelSize(R.dimen.scan_button_min_height) : buttonDefaultMinHeight);
customBarcodeScannerBinding.addFromImage.setMinimumWidth(shouldScaleSmaller ? buttonMinHeight : buttonDefaultMinHeight);
customBarcodeScannerBinding.addManually.setMinimumHeight(shouldScaleSmaller ? buttonMinHeight : buttonDefaultMinHeight);
customBarcodeScannerBinding.addManually.setMinimumWidth(shouldScaleSmaller ? buttonMinHeight : buttonDefaultMinHeight);
customBarcodeScannerBinding.addFromImage.setMinHeight(shouldScaleSmaller ? buttonMinHeight : buttonDefaultMinHeight);
customBarcodeScannerBinding.addFromImage.setMinWidth(shouldScaleSmaller ? buttonMinHeight : buttonDefaultMinHeight);
customBarcodeScannerBinding.addManually.setMinHeight(shouldScaleSmaller ? buttonMinHeight : buttonDefaultMinHeight);
customBarcodeScannerBinding.addManually.setMinWidth(shouldScaleSmaller ? buttonMinHeight : buttonDefaultMinHeight);
}
}

private void saveDefaultUIValues() {
buttonDefaultMinHeight = customBarcodeScannerBinding.addFromImage.getMinHeight();
buttonDefaultMaxHeight = customBarcodeScannerBinding.addFromImage.getMinWidth();
}
Copy link
Member

Choose a reason for hiding this comment

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

Originally I was going to suggest moving the saving the default UI values into the start of scaleScreen, but looking at it more I realize I'm not quite sure what all the setMin* is for. As I understand Android UI (which, admittedly, I don't really do well) the fact that the buttons use wrap_content for both width and height means they should scale to whatever the font size is.

I also tried this on my Fairphone 3 and the buttons still overlayed the text, so I guess the scaling isn't quite right. On top of that I tried removing all the setMin* and noticed no difference at all, not even when comparing the screenshots before and after my change by rapidly alt-tabbing between them.

image


I have the feeling this way of resizing may simply not be the correct way to go about it. Even when tweaking the numbers, we may still run into unexpected cases.

I think it would possibly be cleaner to just tell Android the buttons at the bottom should keep their regular space and the help text at the top should be squashed to fit in the rest of the space, possibly using setAutoSizeTextTypeUniformWithConfiguration to scale. But again, UI is one of my weakest points, especially in the way Android XML works. I'm hoping some Android community could perhaps help us figure out the best way to go about this.

Copy link
Contributor Author

@liutng liutng Oct 19, 2022

Choose a reason for hiding this comment

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

Originally I was going to suggest moving the saving the default UI values into the start of scaleScreen, but looking at it more I realize I'm not quite sure what all the setMin* is for. As I understand Android UI (which, admittedly, I don't really do well) the fact that the buttons use wrap_content for both width and height means they should scale to whatever the font size is.

Yes, buttons will be scaled based on the size of the text, however, there's a minimum size for buttons to be scaled. When I set the button text size to smaller, the buttons' sizes won't change because they have already reached the minimum size. To solve this problem, I have to override the minimum size of those buttons to scale them down even further.

I also tried this on my Fairphone 3 and the buttons still overlayed the text, so I guess the scaling isn't quite right. On top of that I tried removing all the setMin* and noticed no difference at all, not even when comparing the screenshots before and after my change by rapidly alt-tabbing between them.

I think maybe it is not reliable to set a specific screen ratio for the UI to scale, I'd better find another way to do it.

I think it would possibly be cleaner to just tell Android the buttons at the bottom should keep their regular space and the help text at the top should be squashed to fit in the rest of the space, possibly using setAutoSizeTextTypeUniformWithConfiguration to scale. But again, UI is one of my weakest points, especially in the way Android XML works. I'm hoping some Android community could perhaps help us figure out the best way to go about this.

Thank you for the advice and I will look into this to try to find a better way to scale it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After having tried to solve the problem with TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(), I found out this function may only be meant for scaling texts based on the font size setting in Android system settings.

When I was poking around with Google's documents about support for different sizes of screens(the link is here), I realized that it might be a good idea to use screen's height as the scale factor other than ratio, this is because for most of the phones, whether the content can be vertically contained or not is mainly decided by the height of the screen. I think using height as the scale factor might be more universally compatible with most phones. I have run it on my Android emulator with different screen sizes and it looked fine.

I made a new commit b44d998 to push the code.

Used screen height as the scaling factor instead of screen ratio.
Copy link
Member

@TheLastProject TheLastProject left a comment

Choose a reason for hiding this comment

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

I really appreciate all your research and all you've been trying, it's been great to see what code leads to what result. However, I think the current method is a lot of code that still has some edge cases so maybe we should give up on trying to make it "perfect" and just go for something simple that will work well enough :)

I feel sorry for making you "throw away" all your hard work, especially because it was mostly caused by me trying to find the perfect solution without realizing the complexity of it. I know that can be really frustrating. However, your hard work did help tremendously to see what did and didn't work so it was still very useful from a research perspective.

app/src/main/java/protect/card_locker/ScanActivity.java Outdated Show resolved Hide resolved
float mediumSizePx = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,MEDIUM_SCALE_FACTOR_DIP,getResources().getDisplayMetrics());
boolean shouldScaleSmaller = screenHeight < mediumSizePx;

binding.include.welcomeIcon.setVisibility(shouldScaleSmaller ? View.GONE : View.VISIBLE);
Copy link
Member

Choose a reason for hiding this comment

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

Didn't notice this before but it seems this crashes the archive option (you can confirm this by putting 1 card in archive and then clicking the menu in the top right and choosing "Archive"):

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: me.hackerchick.catima.debug, PID: 19126
    java.lang.RuntimeException: Unable to resume activity {me.hackerchick.catima.debug/protect.card_locker.MainActivity}: java.lang.NullPointerException: Attempt to read from field 'protect.card_locker.databinding.ContentMainBinding protect.card_locker.databinding.MainActivityBinding.include' on a null object reference
        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4824)
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4857)
        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54)
        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2253)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7870)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
     Caused by: java.lang.NullPointerException: Attempt to read from field 'protect.card_locker.databinding.ContentMainBinding protect.card_locker.databinding.MainActivityBinding.include' on a null object reference
        at protect.card_locker.MainActivity.scaleScreen(MainActivity.java:857)
        at protect.card_locker.MainActivity.onResume(MainActivity.java:407)
        at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1488)
        at android.app.Activity.performResume(Activity.java:8197)
        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4814)
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4857) 
        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54) 
        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45) 
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2253) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loopOnce(Looper.java:201) 
        at android.os.Looper.loop(Looper.java:288) 
        at android.app.ActivityThread.main(ActivityThread.java:7870) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003) 

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ooops.. I didn't think about this scenario. It's now fixed in commit b0008cd

liutng and others added 4 commits October 23, 2022 13:16
Fixed a crash issue in MainActivity when card is archived.
Fixed a compile error.
Deleted  some unused variables in ScanActivity.
@TheLastProject
Copy link
Member

Looks great now, thank you! :)

@TheLastProject TheLastProject merged commit 68935f1 into CatimaLoyalty:master Oct 25, 2022
@liutng
Copy link
Contributor Author

liutng commented Oct 26, 2022

Looks great now, thank you! :)

It's always my pleasure. :)

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.

Empty state screen doesn't scale well
2 participants