7
7
import android .view .View ;
8
8
import android .view .ViewGroup ;
9
9
import android .view .ViewTreeObserver ;
10
+ import android .view .WindowInsets ;
10
11
import android .view .inputmethod .EditorInfo ;
11
12
import android .widget .FrameLayout ;
12
13
import android .widget .LinearLayout ;
13
14
14
15
import androidx .annotation .Nullable ;
16
+ import androidx .core .view .WindowInsetsCompat ;
15
17
16
18
import com .termux .app .TermuxActivity ;
17
19
import com .termux .shared .logger .Logger ;
@@ -64,15 +66,18 @@ public class TermuxActivityRootView extends LinearLayout implements ViewTreeObse
64
66
public TermuxActivity mActivity ;
65
67
public Integer marginBottom ;
66
68
public Integer lastMarginBottom ;
69
+ public long lastMarginBottomTime ;
70
+ public long lastMarginBottomExtraTime ;
67
71
68
72
/** Log root view events. */
69
73
private boolean ROOT_VIEW_LOGGING_ENABLED = false ;
70
74
71
75
private static final String LOG_TAG = "TermuxActivityRootView" ;
72
76
77
+ private static int mStatusBarHeight ;
78
+
73
79
public TermuxActivityRootView (Context context ) {
74
80
super (context );
75
-
76
81
}
77
82
78
83
public TermuxActivityRootView (Context context , @ Nullable AttributeSet attrs ) {
@@ -118,23 +123,35 @@ public void onGlobalLayout() {
118
123
View bottomSpaceView = mActivity .getTermuxActivityBottomSpaceView ();
119
124
if (bottomSpaceView == null ) return ;
120
125
126
+ boolean root_view_logging_enabled = ROOT_VIEW_LOGGING_ENABLED ;
127
+
128
+ if (root_view_logging_enabled )
129
+ Logger .logVerbose (LOG_TAG , ":\n onGlobalLayout:" );
130
+
121
131
FrameLayout .LayoutParams params = (FrameLayout .LayoutParams ) getLayoutParams ();
122
132
123
133
// 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 );
125
135
if (windowAndViewRects == null )
126
136
return ;
127
137
128
138
Rect windowAvailableRect = windowAndViewRects [0 ];
129
139
Rect bottomSpaceViewRect = windowAndViewRects [1 ];
130
140
131
141
// 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 );
133
144
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 ) ;
135
146
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
+ }
138
155
139
156
// If the bottomSpaceViewRect is visible, then remove the margin if needed
140
157
if (isVisible ) {
@@ -148,15 +165,25 @@ public void onGlobalLayout() {
148
165
// set appropriate margins when views are changed quickly since some changes
149
166
// may be missed.
150
167
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
+
153
171
// Once the view has been redrawn with new margin, we set margin back to 0 so that
154
172
// when next time onMeasure() is called, margin 0 is used. This is necessary for
155
173
// cases when view has been redrawn with new margin because bottom space view was
156
174
// hidden by keyboard and then view was redrawn again due to layout change (like
157
175
// keyboard symbol view is switched to), android will add margin below its new position
158
176
// 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
+
160
187
return ;
161
188
}
162
189
@@ -166,21 +193,28 @@ public void onGlobalLayout() {
166
193
// onGlobalLayout: windowAvailableRect 1408, bottomSpaceViewRect 1232, diff -176, bottom 0, isVisible true, isVisibleBecauseMargin false, isVisibleBecauseExtraMargin false
167
194
// onGlobalLayout: Bottom margin already equals 0
168
195
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
+ }
174
208
}
175
209
176
210
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" );
179
213
params .setMargins (0 , 0 , 0 , 0 );
180
214
setLayoutParams (params );
181
215
} 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" );
184
218
// This is done so that when next time onMeasure() is called, lastMarginBottom is used.
185
219
// This is done since we **expect** the keyboard to have same dimensions next time layout
186
220
// changes, so best set margin while view is drawn the first time, otherwise it will
@@ -193,8 +227,9 @@ public void onGlobalLayout() {
193
227
// ELse find the part of the extra keys/terminal that is hidden and add a margin accordingly
194
228
else {
195
229
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 );
198
233
199
234
boolean setMargin = params .bottomMargin != pxHidden ;
200
235
@@ -205,23 +240,45 @@ public void onGlobalLayout() {
205
240
// onMeasure: Setting bottom margin to 176
206
241
// onGlobalLayout: windowAvailableRect 1232, bottomSpaceViewRect 1408, diff 176, bottom 176, isVisible false, isVisibleBecauseMargin false, isVisibleBecauseExtraMargin false
207
242
// 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
+ }
211
252
setMargin = true ;
212
253
}
213
254
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
+
214
262
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 );
217
265
params .setMargins (0 , 0 , 0 , pxHidden );
218
266
setLayoutParams (params );
219
267
lastMarginBottom = pxHidden ;
220
268
} 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 );
223
271
}
224
272
}
225
273
}
226
274
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
+
227
284
}
0 commit comments