termux-app

Форк
0
247 строк · 11.3 Кб
1
package com.termux.shared.view;
2

3
import android.app.Activity;
4
import android.content.Context;
5
import android.content.ContextWrapper;
6
import android.content.res.Configuration;
7
import android.graphics.Point;
8
import android.graphics.Rect;
9
import android.os.Build;
10
import android.util.TypedValue;
11
import android.view.View;
12
import android.view.ViewGroup;
13

14
import androidx.annotation.NonNull;
15
import androidx.annotation.Nullable;
16
import androidx.appcompat.app.ActionBar;
17
import androidx.appcompat.app.AppCompatActivity;
18

19
import com.termux.shared.logger.Logger;
20

21
public class ViewUtils {
22

23
    /** Log root view events. */
24
    public static boolean VIEW_UTILS_LOGGING_ENABLED = false;
25

26
    private static final String LOG_TAG = "ViewUtils";
27

28
    /**
29
     * Sets whether view utils logging is enabled or not.
30
     *
31
     * @param value The boolean value that defines the state.
32
     */
33
    public static void setIsViewUtilsLoggingEnabled(boolean value) {
34
        VIEW_UTILS_LOGGING_ENABLED = value;
35
    }
36

37
    /**
38
     * Check if a {@link View} is fully visible and not hidden or partially covered by another view.
39
     *
40
     * https://stackoverflow.com/a/51078418/14686958
41
     *
42
     * @param view The {@link View} to check.
43
     * @param statusBarHeight The status bar height received by {@link View.OnApplyWindowInsetsListener}.
44
     * @return Returns {@code true} if view is fully visible.
45
     */
46
    public static boolean isViewFullyVisible(View view, int statusBarHeight) {
47
        Rect[] windowAndViewRects = getWindowAndViewRects(view, statusBarHeight);
48
        if (windowAndViewRects == null)
49
            return false;
50
        return windowAndViewRects[0].contains(windowAndViewRects[1]);
51
    }
52

53
    /**
54
     * Get the {@link Rect} of a {@link View} and the  {@link Rect} of the window inside which it
55
     * exists.
56
     *
57
     * https://stackoverflow.com/a/51078418/14686958
58
     *
59
     * @param view The {@link View} inside the window whose {@link Rect} to get.
60
     * @param statusBarHeight The status bar height received by {@link View.OnApplyWindowInsetsListener}.
61
     * @return Returns {@link Rect[]} if view is visible where Rect[0] will contain window
62
     * {@link Rect} and Rect[1] will contain view {@link Rect}. This will be {@code null}
63
     * if view is not visible.
64
     */
65
    @Nullable
66
    public static Rect[] getWindowAndViewRects(View view, int statusBarHeight) {
67
        if (view == null || !view.isShown())
68
            return null;
69

70
        boolean view_utils_logging_enabled = VIEW_UTILS_LOGGING_ENABLED;
71

72
        // windowRect - will hold available area where content remain visible to users
73
        // Takes into account screen decorations (e.g. statusbar)
74
        Rect windowRect = new Rect();
75
        view.getWindowVisibleDisplayFrame(windowRect);
76

77
        // If there is actionbar, get his height
78
        int actionBarHeight = 0;
79
        boolean isInMultiWindowMode = false;
80
        Context context = view.getContext();
81
        if (context instanceof AppCompatActivity) {
82
            ActionBar actionBar = ((AppCompatActivity) context).getSupportActionBar();
83
            if (actionBar != null) actionBarHeight = actionBar.getHeight();
84
            isInMultiWindowMode = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) && ((AppCompatActivity) context).isInMultiWindowMode();
85
        } else if (context instanceof Activity) {
86
            android.app.ActionBar actionBar = ((Activity) context).getActionBar();
87
            if (actionBar != null) actionBarHeight = actionBar.getHeight();
88
            isInMultiWindowMode = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) && ((Activity) context).isInMultiWindowMode();
89
        }
90

91
        int displayOrientation = getDisplayOrientation(context);
92

93
        // windowAvailableRect - takes into account actionbar and statusbar height
94
        Rect windowAvailableRect;
95
        windowAvailableRect = new Rect(windowRect.left, windowRect.top + actionBarHeight, windowRect.right, windowRect.bottom);
96

97
        // viewRect - holds position of the view in window
98
        // (methods as getGlobalVisibleRect, getHitRect, getDrawingRect can return different result,
99
        // when partialy visible)
100
        Rect viewRect;
101
        final int[] viewsLocationInWindow = new int[2];
102
        view.getLocationInWindow(viewsLocationInWindow);
103
        int viewLeft = viewsLocationInWindow[0];
104
        int viewTop = viewsLocationInWindow[1];
105

106
        if (view_utils_logging_enabled) {
107
            Logger.logVerbose(LOG_TAG, "getWindowAndViewRects:");
108
            Logger.logVerbose(LOG_TAG, "windowRect: " + toRectString(windowRect) + ", windowAvailableRect: " + toRectString(windowAvailableRect));
109
            Logger.logVerbose(LOG_TAG, "viewsLocationInWindow: " + toPointString(new Point(viewLeft, viewTop)));
110
            Logger.logVerbose(LOG_TAG, "activitySize: " + toPointString(getDisplaySize(context, true)) +
111
                ", displaySize: " + toPointString(getDisplaySize(context, false)) +
112
                ", displayOrientation=" + displayOrientation);
113
        }
114

115
        if (isInMultiWindowMode) {
116
            if (displayOrientation == Configuration.ORIENTATION_PORTRAIT) {
117
                // The windowRect.top of the window at the of split screen mode should start right
118
                // below the status bar
119
                if (statusBarHeight != windowRect.top) {
120
                    if (view_utils_logging_enabled)
121
                        Logger.logVerbose(LOG_TAG, "Window top does not equal statusBarHeight " + statusBarHeight + " in multi-window portrait mode. Window is possibly bottom app in split screen mode. Adding windowRect.top " + windowRect.top + " to viewTop.");
122
                    viewTop += windowRect.top;
123
                } else {
124
                    if (view_utils_logging_enabled)
125
                        Logger.logVerbose(LOG_TAG, "windowRect.top equals statusBarHeight " + statusBarHeight + " in multi-window portrait mode. Window is possibly top app in split screen mode.");
126
                }
127

128
            } else if (displayOrientation == Configuration.ORIENTATION_LANDSCAPE) {
129
                // If window is on the right in landscape mode of split screen, the viewLeft actually
130
                // starts at windowRect.left instead of 0 returned by getLocationInWindow
131
                viewLeft += windowRect.left;
132
            }
133
        }
134

135
        int viewRight = viewLeft + view.getWidth();
136
        int viewBottom = viewTop + view.getHeight();
137
        viewRect = new Rect(viewLeft, viewTop, viewRight, viewBottom);
138

139
        if (displayOrientation == Configuration.ORIENTATION_LANDSCAPE && viewRight > windowAvailableRect.right) {
140
            if (view_utils_logging_enabled)
141
                Logger.logVerbose(LOG_TAG, "viewRight " + viewRight + " is greater than windowAvailableRect.right " + windowAvailableRect.right + " in landscape mode. Setting windowAvailableRect.right to viewRight since it may not include navbar height.");
142
            windowAvailableRect.right = viewRight;
143
        }
144

145
        return new Rect[]{windowAvailableRect, viewRect};
146
    }
147

148
    /**
149
     * Check if {@link Rect} r2 is above r2. An empty rectangle never contains another rectangle.
150
     *
151
     * @param r1 The base rectangle.
152
     * @param r2 The rectangle being tested that should be above.
153
     * @return Returns {@code true} if r2 is above r1.
154
     */
155
    public static boolean isRectAbove(@NonNull Rect r1, @NonNull Rect r2) {
156
        // check for empty first
157
        return r1.left < r1.right && r1.top < r1.bottom
158
            // now check if above
159
            && r1.left <= r2.left && r1.bottom >= r2.bottom;
160
    }
161

162
    /**
163
     * Get device orientation.
164
     *
165
     * Related: https://stackoverflow.com/a/29392593/14686958
166
     *
167
     * @param context The {@link Context} to check with.
168
     * @return {@link Configuration#ORIENTATION_PORTRAIT} or {@link Configuration#ORIENTATION_LANDSCAPE}.
169
     */
170
    public static int getDisplayOrientation(@NonNull Context context) {
171
        Point size = getDisplaySize(context, false);
172
        return (size.x < size.y) ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
173
    }
174

175
    /**
176
     * Get device display size.
177
     *
178
     * @param context The {@link Context} to check with. It must be {@link Activity} context, otherwise
179
     *                android will throw:
180
     *                `java.lang.IllegalArgumentException: Used non-visual Context to obtain an instance of WindowManager. Please use an Activity or a ContextWrapper around one instead.`
181
     * @param activitySize The set to {@link true}, then size returned will be that of the activity
182
     *                     and can be smaller than physical display size in multi-window mode.
183
     * @return Returns the display size as {@link Point}.
184
     */
185
    public static Point getDisplaySize( @NonNull Context context, boolean activitySize) {
186
        // android.view.WindowManager.getDefaultDisplay() and Display.getSize() are deprecated in
187
        // API 30 and give wrong values in API 30 for activitySize=false in multi-window
188
        androidx.window.WindowManager windowManager = new androidx.window.WindowManager(context);
189
        androidx.window.WindowMetrics windowMetrics;
190
        if (activitySize)
191
            windowMetrics = windowManager.getCurrentWindowMetrics();
192
        else
193
            windowMetrics = windowManager.getMaximumWindowMetrics();
194
        return new Point(windowMetrics.getBounds().width(), windowMetrics.getBounds().height());
195
    }
196

197
    /** Convert {@link Rect} to {@link String}. */
198
    public static String toRectString(Rect rect) {
199
        if (rect == null) return "null";
200
        return "(" + rect.left + "," + rect.top + "), (" + rect.right + "," + rect.bottom + ")";
201
    }
202

203
    /** Convert {@link Point} to {@link String}. */
204
    public static String toPointString(Point point) {
205
        if (point == null) return "null";
206
        return "(" + point.x + "," + point.y + ")";
207
    }
208

209
    /** Get the {@link Activity} associated with the {@link Context} if available. */
210
    @Nullable
211
    public static Activity getActivity(Context context) {
212
        while (context instanceof ContextWrapper) {
213
            if (context instanceof Activity) {
214
                return (Activity)context;
215
            }
216
            context = ((ContextWrapper)context).getBaseContext();
217
        }
218
        return null;
219
    }
220

221

222
    /** Convert value in device independent pixels (dp) to pixels (px) units. */
223
    public static float dpToPx(Context context, float dp) {
224
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
225
    }
226

227
    /** Convert value in pixels (px) to device independent pixels (dp) units. */
228
    public static float pxToDp(Context context, float px) {
229
        return px / context.getResources().getDisplayMetrics().density;
230
    }
231

232

233
    public static void setLayoutMarginsInDp(@NonNull View view, int left, int top, int right, int bottom) {
234
        Context context = view.getContext();
235
        setLayoutMarginsInPixels(view, (int) dpToPx(context, left), (int) dpToPx(context, top),
236
            (int) dpToPx(context, right), (int) dpToPx(context, bottom));
237
    }
238

239
    public static void setLayoutMarginsInPixels(@NonNull View view, int left, int top, int right, int bottom) {
240
        if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
241
            ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
242
            params.setMargins(left, top, right, bottom);
243
            view.setLayoutParams(params);
244
        }
245
    }
246

247
}
248

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.