Skip to content

Commit e5a9b99

Browse files
Fix issues with TermuxActivityRootView margin adjustment
Margin adjustment was causing screen flickering due to invalid values being calculated in landscape and split screen mode. Attempts to fix issue #2127
1 parent 00f805f commit e5a9b99

File tree

5 files changed

+214
-30
lines changed

5 files changed

+214
-30
lines changed

app/src/main/java/com/termux/app/TermuxActivity.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ public void onCreate(Bundle savedInstanceState) {
203203
mTermuxActivityRootView = findViewById(R.id.activity_termux_root_view);
204204
mTermuxActivityRootView.setActivity(this);
205205
mTermuxActivityBottomSpaceView = findViewById(R.id.activity_termux_bottom_space_view);
206+
mTermuxActivityRootView.setOnApplyWindowInsetsListener(new TermuxActivityRootView.WindowInsetsListener());
206207

207208
View content = findViewById(android.R.id.content);
208209
content.setOnApplyWindowInsetsListener((v, insets) -> {

app/src/main/java/com/termux/app/terminal/TermuxActivityRootView.java

Lines changed: 84 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
import android.view.View;
88
import android.view.ViewGroup;
99
import android.view.ViewTreeObserver;
10+
import android.view.WindowInsets;
1011
import android.view.inputmethod.EditorInfo;
1112
import android.widget.FrameLayout;
1213
import android.widget.LinearLayout;
1314

1415
import androidx.annotation.Nullable;
16+
import androidx.core.view.WindowInsetsCompat;
1517

1618
import com.termux.app.TermuxActivity;
1719
import com.termux.shared.logger.Logger;
@@ -64,15 +66,18 @@ public class TermuxActivityRootView extends LinearLayout implements ViewTreeObse
6466
public TermuxActivity mActivity;
6567
public Integer marginBottom;
6668
public Integer lastMarginBottom;
69+
public long lastMarginBottomTime;
70+
public long lastMarginBottomExtraTime;
6771

6872
/** Log root view events. */
6973
private boolean ROOT_VIEW_LOGGING_ENABLED = false;
7074

7175
private static final String LOG_TAG = "TermuxActivityRootView";
7276

77+
private static int mStatusBarHeight;
78+
7379
public TermuxActivityRootView(Context context) {
7480
super(context);
75-
7681
}
7782

7883
public TermuxActivityRootView(Context context, @Nullable AttributeSet attrs) {
@@ -118,23 +123,35 @@ public void onGlobalLayout() {
118123
View bottomSpaceView = mActivity.getTermuxActivityBottomSpaceView();
119124
if (bottomSpaceView == null) return;
120125

126+
boolean root_view_logging_enabled = ROOT_VIEW_LOGGING_ENABLED;
127+
128+
if (root_view_logging_enabled)
129+
Logger.logVerbose(LOG_TAG, ":\nonGlobalLayout:");
130+
121131
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) getLayoutParams();
122132

123133
// Get the position Rects of the bottom space view and the main window holding it
124-
Rect[] windowAndViewRects = ViewUtils.getWindowAndViewRects(bottomSpaceView);
134+
Rect[] windowAndViewRects = ViewUtils.getWindowAndViewRects(bottomSpaceView, mStatusBarHeight);
125135
if (windowAndViewRects == null)
126136
return;
127137

128138
Rect windowAvailableRect = windowAndViewRects[0];
129139
Rect bottomSpaceViewRect = windowAndViewRects[1];
130140

131141
// If the bottomSpaceViewRect is inside the windowAvailableRect, then it must be completely visible
132-
boolean isVisible = windowAvailableRect.contains(bottomSpaceViewRect);
142+
//boolean isVisible = windowAvailableRect.contains(bottomSpaceViewRect); // rect.right comparison often fails in landscape
143+
boolean isVisible = ViewUtils.isRectAbove(windowAvailableRect, bottomSpaceViewRect);
133144
boolean isVisibleBecauseMargin = (windowAvailableRect.bottom == bottomSpaceViewRect.bottom) && params.bottomMargin > 0;
134-
boolean isVisibleBecauseExtraMargin = (bottomSpaceViewRect.bottom - windowAvailableRect.bottom) < 0;
145+
boolean isVisibleBecauseExtraMargin = ((bottomSpaceViewRect.bottom - windowAvailableRect.bottom) < 0);
135146

136-
if (ROOT_VIEW_LOGGING_ENABLED)
137-
Logger.logVerbose(LOG_TAG, "onGlobalLayout: windowAvailableRect " + windowAvailableRect.bottom + ", bottomSpaceViewRect " + bottomSpaceViewRect.bottom + ", diff " + (bottomSpaceViewRect.bottom - windowAvailableRect.bottom) + ", bottom " + params.bottomMargin + ", isVisible " + isVisible + ", isVisibleBecauseMargin " + isVisibleBecauseMargin + ", isVisibleBecauseExtraMargin " + isVisibleBecauseExtraMargin);
147+
if (root_view_logging_enabled) {
148+
Logger.logVerbose(LOG_TAG, "windowAvailableRect " + ViewUtils.toRectString(windowAvailableRect) + ", bottomSpaceViewRect " + ViewUtils.toRectString(bottomSpaceViewRect));
149+
Logger.logVerbose(LOG_TAG, "windowAvailableRect.bottom " + windowAvailableRect.bottom +
150+
", bottomSpaceViewRect.bottom " +bottomSpaceViewRect.bottom +
151+
", diff " + (bottomSpaceViewRect.bottom - windowAvailableRect.bottom) + ", bottom " + params.bottomMargin +
152+
", isVisible " + windowAvailableRect.contains(bottomSpaceViewRect) + ", isRectAbove " + ViewUtils.isRectAbove(windowAvailableRect, bottomSpaceViewRect) +
153+
", isVisibleBecauseMargin " + isVisibleBecauseMargin + ", isVisibleBecauseExtraMargin " + isVisibleBecauseExtraMargin);
154+
}
138155

139156
// If the bottomSpaceViewRect is visible, then remove the margin if needed
140157
if (isVisible) {
@@ -148,15 +165,25 @@ public void onGlobalLayout() {
148165
// set appropriate margins when views are changed quickly since some changes
149166
// may be missed.
150167
if (isVisibleBecauseMargin) {
151-
if (ROOT_VIEW_LOGGING_ENABLED)
152-
Logger.logVerbose(LOG_TAG, "onGlobalLayout: Visible due to margin");
168+
if (root_view_logging_enabled)
169+
Logger.logVerbose(LOG_TAG, "Visible due to margin");
170+
153171
// Once the view has been redrawn with new margin, we set margin back to 0 so that
154172
// when next time onMeasure() is called, margin 0 is used. This is necessary for
155173
// cases when view has been redrawn with new margin because bottom space view was
156174
// hidden by keyboard and then view was redrawn again due to layout change (like
157175
// keyboard symbol view is switched to), android will add margin below its new position
158176
// if its greater than 0, which was already above the keyboard creating x2x margin.
159-
marginBottom = 0;
177+
// Adding time check since moving split screen divider in landscape causes jitter
178+
// and prevents some infinite loops
179+
if ((System.currentTimeMillis() - lastMarginBottomTime) > 40) {
180+
lastMarginBottomTime = System.currentTimeMillis();
181+
marginBottom = 0;
182+
} else {
183+
if (root_view_logging_enabled)
184+
Logger.logVerbose(LOG_TAG, "Ignoring restoring marginBottom to 0 since called to quickly");
185+
}
186+
160187
return;
161188
}
162189

@@ -166,21 +193,28 @@ public void onGlobalLayout() {
166193
// onGlobalLayout: windowAvailableRect 1408, bottomSpaceViewRect 1232, diff -176, bottom 0, isVisible true, isVisibleBecauseMargin false, isVisibleBecauseExtraMargin false
167194
// onGlobalLayout: Bottom margin already equals 0
168195
if (isVisibleBecauseExtraMargin) {
169-
if (ROOT_VIEW_LOGGING_ENABLED)
170-
Logger.logVerbose(LOG_TAG, "onGlobalLayout: Resetting margin since visible due to extra margin");
171-
setMargin = true;
172-
// lastMarginBottom must be invalid. May also happen when keyboards are changed.
173-
lastMarginBottom = null;
196+
// Adding time check since prevents infinite loops, like in landscape mode in freeform mode in Taskbar
197+
if ((System.currentTimeMillis() - lastMarginBottomExtraTime) > 40) {
198+
if (root_view_logging_enabled)
199+
Logger.logVerbose(LOG_TAG, "Resetting margin since visible due to extra margin");
200+
lastMarginBottomExtraTime = System.currentTimeMillis();
201+
// lastMarginBottom must be invalid. May also happen when keyboards are changed.
202+
lastMarginBottom = null;
203+
setMargin = true;
204+
} else {
205+
if (root_view_logging_enabled)
206+
Logger.logVerbose(LOG_TAG, "Ignoring resetting margin since visible due to extra margin since called to quickly");
207+
}
174208
}
175209

176210
if (setMargin) {
177-
if (ROOT_VIEW_LOGGING_ENABLED)
178-
Logger.logVerbose(LOG_TAG, "onGlobalLayout: Setting bottom margin to 0");
211+
if (root_view_logging_enabled)
212+
Logger.logVerbose(LOG_TAG, "Setting bottom margin to 0");
179213
params.setMargins(0, 0, 0, 0);
180214
setLayoutParams(params);
181215
} else {
182-
if (ROOT_VIEW_LOGGING_ENABLED)
183-
Logger.logVerbose(LOG_TAG, "onGlobalLayout: Bottom margin already equals 0");
216+
if (root_view_logging_enabled)
217+
Logger.logVerbose(LOG_TAG, "Bottom margin already equals 0");
184218
// This is done so that when next time onMeasure() is called, lastMarginBottom is used.
185219
// This is done since we **expect** the keyboard to have same dimensions next time layout
186220
// changes, so best set margin while view is drawn the first time, otherwise it will
@@ -193,8 +227,9 @@ public void onGlobalLayout() {
193227
// ELse find the part of the extra keys/terminal that is hidden and add a margin accordingly
194228
else {
195229
int pxHidden = bottomSpaceViewRect.bottom - windowAvailableRect.bottom;
196-
if (ROOT_VIEW_LOGGING_ENABLED)
197-
Logger.logVerbose(LOG_TAG, "onGlobalLayout: pxHidden " + pxHidden + ", bottom " + params.bottomMargin);
230+
231+
if (root_view_logging_enabled)
232+
Logger.logVerbose(LOG_TAG, "pxHidden " + pxHidden + ", bottom " + params.bottomMargin);
198233

199234
boolean setMargin = params.bottomMargin != pxHidden;
200235

@@ -205,23 +240,45 @@ public void onGlobalLayout() {
205240
// onMeasure: Setting bottom margin to 176
206241
// onGlobalLayout: windowAvailableRect 1232, bottomSpaceViewRect 1408, diff 176, bottom 176, isVisible false, isVisibleBecauseMargin false, isVisibleBecauseExtraMargin false
207242
// onGlobalLayout: Bottom margin already equals 176
208-
if ((bottomSpaceViewRect.bottom - windowAvailableRect.bottom) > 0) {
209-
if (ROOT_VIEW_LOGGING_ENABLED)
210-
Logger.logVerbose(LOG_TAG, "onGlobalLayout: Force setting margin since not visible despite margin");
243+
if (pxHidden > 0 && params.bottomMargin > 0) {
244+
if (pxHidden != params.bottomMargin) {
245+
if (root_view_logging_enabled)
246+
Logger.logVerbose(LOG_TAG, "Force setting margin to 0 since not visible due to wrong margin");
247+
pxHidden = 0;
248+
} else {
249+
if (root_view_logging_enabled)
250+
Logger.logVerbose(LOG_TAG, "Force setting margin since not visible despite required margin");
251+
}
211252
setMargin = true;
212253
}
213254

255+
if (pxHidden < 0) {
256+
if (root_view_logging_enabled)
257+
Logger.logVerbose(LOG_TAG, "Force setting margin to 0 since new margin is negative");
258+
pxHidden = 0;
259+
}
260+
261+
214262
if (setMargin) {
215-
if (ROOT_VIEW_LOGGING_ENABLED)
216-
Logger.logVerbose(LOG_TAG, "onGlobalLayout: Setting bottom margin to " + pxHidden);
263+
if (root_view_logging_enabled)
264+
Logger.logVerbose(LOG_TAG, "Setting bottom margin to " + pxHidden);
217265
params.setMargins(0, 0, 0, pxHidden);
218266
setLayoutParams(params);
219267
lastMarginBottom = pxHidden;
220268
} else {
221-
if (ROOT_VIEW_LOGGING_ENABLED)
222-
Logger.logVerbose(LOG_TAG, "onGlobalLayout: Bottom margin already equals " + pxHidden);
269+
if (root_view_logging_enabled)
270+
Logger.logVerbose(LOG_TAG, "Bottom margin already equals " + pxHidden);
223271
}
224272
}
225273
}
226274

275+
public static class WindowInsetsListener implements View.OnApplyWindowInsetsListener {
276+
@Override
277+
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
278+
mStatusBarHeight = WindowInsetsCompat.toWindowInsetsCompat(insets).getInsets(WindowInsetsCompat.Type.statusBars()).top;
279+
// Let view window handle insets however it wants
280+
return v.onApplyWindowInsets(insets);
281+
}
282+
}
283+
227284
}

app/src/main/java/com/termux/app/terminal/TermuxTerminalViewClient.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.termux.shared.markdown.MarkdownUtils;
3636
import com.termux.shared.termux.TermuxUtils;
3737
import com.termux.shared.view.KeyboardUtils;
38+
import com.termux.shared.view.ViewUtils;
3839
import com.termux.terminal.KeyHandler;
3940
import com.termux.terminal.TerminalEmulator;
4041
import com.termux.terminal.TerminalSession;
@@ -88,6 +89,7 @@ public void onStart() {
8889

8990
// Piggyback on the terminal view key logging toggle for now, should add a separate toggle in future
9091
mActivity.getTermuxActivityRootView().setIsRootViewLoggingEnabled(isTerminalViewKeyLoggingEnabled);
92+
ViewUtils.setIsViewUtilsLoggingEnabled(isTerminalViewKeyLoggingEnabled);
9193
}
9294

9395
/**

termux-shared/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ android {
88
implementation 'androidx.appcompat:appcompat:1.2.0'
99
implementation "androidx.annotation:annotation:1.2.0"
1010
implementation "androidx.core:core:1.5.0-rc01"
11+
implementation "androidx.window:window:1.0.0-alpha08"
1112
implementation "com.google.guava:guava:24.1-jre"
1213
implementation "io.noties.markwon:core:$markwonVersion"
1314
implementation "io.noties.markwon:ext-strikethrough:$markwonVersion"

0 commit comments

Comments
 (0)