Commit 4ae0cc63 authored by Wenyu Fu's avatar Wenyu Fu Committed by Commit Bot

Merge chrome.browser.utils.ViewUtils with base.ui.ViewUtils

Two of the files are serving as util classes to the rest of of the
components, merging them into one for the visibility and less
redundancy.

Change-Id: Iec5a039d7caeaa9ef78f7e7dfcb1b82caef63117
Bug: 1042487
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2003923
Commit-Queue: Wenyu Fu <wenyufu@chromium.org>
Auto-Submit: Wenyu Fu <wenyufu@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#732485}
parent 33d84f98
......@@ -23,7 +23,7 @@ import androidx.annotation.StyleRes;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.chrome.autofill_assistant.R;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.ui.base.ViewUtils;
import org.chromium.ui.widget.ChromeImageView;
import org.chromium.ui.widget.RippleBackgroundHelper;
......
......@@ -44,11 +44,11 @@ import org.chromium.chrome.browser.ntp.snippets.SectionHeaderView;
import org.chromium.chrome.browser.signin.PersonalizedSigninPromoView;
import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.chrome.feed.R;
import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
import org.chromium.components.browser_ui.widget.displaystyle.ViewResizer;
import org.chromium.ui.UiUtils;
import org.chromium.ui.base.ViewUtils;
import java.util.Arrays;
......
......@@ -17,7 +17,7 @@ import android.widget.TextView;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.favicon.RoundedIconGenerator;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.ui.base.ViewUtils;
/**
* The View representing a single explore sites category.
......
......@@ -14,7 +14,7 @@ import androidx.annotation.Nullable;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.ui.base.ViewUtils;
/**
* Utilities to deal with favicons.
......
......@@ -4,7 +4,7 @@
package org.chromium.chrome.browser.notifications;
import static org.chromium.chrome.browser.util.ViewUtils.dpToPx;
import static org.chromium.ui.base.ViewUtils.dpToPx;
import android.content.Context;
import android.content.res.Resources;
......
......@@ -4,7 +4,7 @@
package org.chromium.chrome.browser.ntp;
import static org.chromium.chrome.browser.util.ViewUtils.dpToPx;
import static org.chromium.ui.base.ViewUtils.dpToPx;
import android.content.Context;
import android.content.res.Configuration;
......
......@@ -12,7 +12,7 @@ import android.view.View;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.ui.base.ViewUtils;
/**
* The New Tab Page for use in the incognito profile.
......
......@@ -51,11 +51,11 @@ import org.chromium.chrome.browser.suggestions.tile.Tile;
import org.chromium.chrome.browser.suggestions.tile.TileGridLayout;
import org.chromium.chrome.browser.suggestions.tile.TileGroup;
import org.chromium.chrome.browser.suggestions.tile.TileRenderer;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.chrome.browser.vr.VrModeObserver;
import org.chromium.chrome.browser.vr.VrModuleProvider;
import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.base.ViewUtils;
/**
* Layout for the new tab page. This positions the page elements in the correct vertical positions.
......
......@@ -32,9 +32,9 @@ import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
import org.chromium.chrome.browser.suggestions.tile.TileGroup;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabImpl;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
import org.chromium.components.browser_ui.widget.displaystyle.ViewResizer;
import org.chromium.ui.base.ViewUtils;
/**
* The native new tab page, represented by some basic data such as title and url, and an Android
......
......@@ -27,8 +27,8 @@ import org.chromium.chrome.browser.gesturenav.HistoryNavigationLayout;
import org.chromium.chrome.browser.native_page.NativePage;
import org.chromium.chrome.browser.native_page.NativePageHost;
import org.chromium.chrome.browser.util.UrlConstants;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.base.ViewUtils;
/**
* The native recent tabs page. Lists recently closed tabs, open windows and tabs from the user's
......
......@@ -11,8 +11,8 @@ import org.chromium.chrome.browser.ntp.FakeboxDelegate;
import org.chromium.chrome.browser.ntp.NewTabPageLayout;
import org.chromium.chrome.browser.ntp.SnapScrollHelper;
import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.base.ViewUtils;
/**
* Simple wrapper on top of a RecyclerView that will acquire focus when tapped. Ensures the
......
......@@ -23,8 +23,8 @@ import androidx.annotation.VisibleForTesting;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.util.KeyNavigationUtil;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.components.browser_ui.styles.ChromeColors;
import org.chromium.ui.base.ViewUtils;
import java.util.ArrayList;
......
......@@ -12,7 +12,7 @@ import org.chromium.base.annotations.NativeMethods;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.task.AsyncTask;
import org.chromium.chrome.browser.AppHooks;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.ui.base.ViewUtils;
import java.util.HashSet;
import java.util.LinkedHashMap;
......
......@@ -29,12 +29,12 @@ import org.chromium.chrome.browser.signin.SigninPromoUtil;
import org.chromium.chrome.browser.signin.SigninUtils;
import org.chromium.chrome.browser.sync.ProfileSyncService;
import org.chromium.chrome.browser.sync.ProfileSyncService.SyncStateChangedListener;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.components.signin.AccountManagerFacade;
import org.chromium.components.signin.AccountsChangeObserver;
import org.chromium.components.signin.ChromeSigninController;
import org.chromium.components.signin.metrics.SigninAccessPoint;
import org.chromium.components.sync.AndroidSyncSettings;
import org.chromium.ui.base.ViewUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
......
......@@ -32,9 +32,9 @@ import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.suggestions.ImageFetcher;
import org.chromium.chrome.browser.suggestions.SiteSuggestion;
import org.chromium.chrome.browser.suggestions.SuggestionsConfig.TileStyle;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.components.feature_engagement.EventConstants;
import org.chromium.components.feature_engagement.Tracker;
import org.chromium.ui.base.ViewUtils;
import java.util.HashMap;
import java.util.List;
......
......@@ -21,10 +21,10 @@ import org.chromium.chrome.browser.compositor.resources.ResourceFactory;
import org.chromium.chrome.browser.contextualsearch.SwipeRecognizer;
import org.chromium.chrome.browser.toolbar.ControlContainer;
import org.chromium.chrome.browser.toolbar.ToolbarProgressBar;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.components.browser_ui.widget.ClipDrawableProgressBar.DrawingInfo;
import org.chromium.components.browser_ui.widget.ViewResourceFrameLayout;
import org.chromium.ui.KeyboardVisibilityDelegate;
import org.chromium.ui.base.ViewUtils;
import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
import org.chromium.ui.widget.OptimizedFrameLayout;
......
......@@ -49,9 +49,9 @@ import org.chromium.chrome.browser.toolbar.ToolbarTabController;
import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator.UrlExpansionObserver;
import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper;
import org.chromium.chrome.browser.ui.widget.textbubble.TextBubble;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.components.security_state.ConnectionSecurityLevel;
import org.chromium.ui.UiUtils;
import org.chromium.ui.base.ViewUtils;
/**
* Layout class that contains the base shared logic for manipulating the toolbar component. For
......
......@@ -76,12 +76,12 @@ import org.chromium.chrome.browser.toolbar.ToolbarManager;
import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager;
import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator.UrlExpansionObserver;
import org.chromium.chrome.browser.util.ColorUtils;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.components.browser_ui.styles.ChromeColors;
import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener;
import org.chromium.components.browser_ui.widget.animation.Interpolators;
import org.chromium.components.feature_engagement.EventConstants;
import org.chromium.ui.base.LocalizationUtils;
import org.chromium.ui.base.ViewUtils;
import org.chromium.ui.interpolators.BakedBezierInterpolator;
import java.lang.annotation.Retention;
......
......@@ -53,7 +53,6 @@ import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegateImpl;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.util.UrlConstants;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.util.ChromeRenderTestRule;
......@@ -68,6 +67,7 @@ import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.net.test.EmbeddedTestServerRule;
import org.chromium.ui.base.ViewUtils;
import org.chromium.ui.modelutil.ListObservable;
import org.chromium.ui.test.util.NightModeTestUtils;
......
......@@ -17,7 +17,7 @@ import androidx.annotation.StringRes;
import org.chromium.chrome.browser.preferences.Pref;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.ui.base.ViewUtils;
import org.chromium.ui.widget.Toast;
import java.util.Locale;
......
......@@ -20,7 +20,6 @@ android_library("java") {
"android/java/src/org/chromium/chrome/browser/util/PlatformUtil.java",
"android/java/src/org/chromium/chrome/browser/util/UrlConstants.java",
"android/java/src/org/chromium/chrome/browser/util/UrlUtilities.java",
"android/java/src/org/chromium/chrome/browser/util/ViewUtils.java",
]
deps = [
"//base:base_java",
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.util;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Region;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
/**
* View-related utility methods.
*/
public class ViewUtils {
private static final int[] sLocationTmp = new int[2];
/**
* Invalidates a view and all of its descendants.
*/
private static void recursiveInvalidate(View view) {
view.invalidate();
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
int childCount = group.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = group.getChildAt(i);
if (child.getVisibility() == View.VISIBLE) {
recursiveInvalidate(child);
}
}
}
}
/**
* Sets the enabled property of a View and all of its descendants.
*/
public static void setEnabledRecursive(View view, boolean enabled) {
view.setEnabled(enabled);
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
for (int i = 0; i < group.getChildCount(); i++) {
setEnabledRecursive(group.getChildAt(i), enabled);
}
}
}
/**
* Captures a bitmap of a View and draws it to a Canvas.
*/
public static void captureBitmap(View view, Canvas canvas) {
// Invalidate all the descendants of view, before calling view.draw(). Otherwise, some of
// the descendant views may optimize away their drawing. http://crbug.com/415251
recursiveInvalidate(view);
view.draw(canvas);
}
/**
* Return the position of {@code childView} relative to {@code rootView}. {@code childView}
* must be a child of {@code rootView}. This returns the relative layout position, which does
* not include translations.
* @param rootView The parent of {@code childView} to calculate the position relative to.
* @param childView The {@link View} to calculate the position of.
* @param outPosition The resulting position with the format [x, y].
*/
public static void getRelativeLayoutPosition(View rootView, View childView, int[] outPosition) {
assert outPosition.length == 2;
outPosition[0] = 0;
outPosition[1] = 0;
if (rootView == null || childView == rootView) return;
while (childView != null) {
outPosition[0] += childView.getLeft();
outPosition[1] += childView.getTop();
if (childView.getParent() == rootView) break;
childView = (View) childView.getParent();
}
}
/**
* Return the position of {@code childView} relative to {@code rootView}. {@code childView}
* must be a child of {@code rootView}. This returns the relative draw position, which includes
* translations.
* @param rootView The parent of {@code childView} to calculate the position relative to.
* @param childView The {@link View} to calculate the position of.
* @param outPosition The resulting position with the format [x, y].
*/
public static void getRelativeDrawPosition(View rootView, View childView, int[] outPosition) {
assert outPosition.length == 2;
outPosition[0] = 0;
outPosition[1] = 0;
if (rootView == null || childView == rootView) return;
while (childView != null) {
outPosition[0] = (int) (outPosition[0] + childView.getX());
outPosition[1] = (int) (outPosition[1] + childView.getY());
if (childView.getParent() == rootView) break;
childView = (View) childView.getParent();
}
}
/**
* Helper for overriding {@link ViewGroup#gatherTransparentRegion} for views that are fully
* opaque and have children extending beyond their bounds. If the transparent region
* optimization is turned on (which is the case whenever the view hierarchy contains a
* SurfaceView somewhere), the children might otherwise confuse the SurfaceFlinger.
*/
public static void gatherTransparentRegionsForOpaqueView(View view, Region region) {
view.getLocationInWindow(sLocationTmp);
region.op(sLocationTmp[0], sLocationTmp[1],
sLocationTmp[0] + view.getRight() - view.getLeft(),
sLocationTmp[1] + view.getBottom() - view.getTop(), Region.Op.DIFFERENCE);
}
/**
* Converts density-independent pixels (dp) to pixels on the screen (px).
*
* @param dp Density-independent pixels are based on the physical density of the screen.
* @return The physical pixels on the screen which correspond to this many
* density-independent pixels for this screen.
*/
public static int dpToPx(Context context, float dp) {
return dpToPx(context.getResources().getDisplayMetrics(), dp);
}
/**
* Converts density-independent pixels (dp) to pixels on the screen (px).
*
* @param dp Density-independent pixels are based on the physical density of the screen.
* @return The physical pixels on the screen which correspond to this many
* density-independent pixels for this screen.
*/
public static int dpToPx(DisplayMetrics metrics, float dp) {
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics));
}
/**
* Sets clip children for the provided ViewGroup and all of its ancestors.
* @param view The ViewGroup whose children should (not) be clipped.
* @param clip Whether to clip children to the parent bounds.
*/
public static void setAncestorsShouldClipChildren(ViewGroup view, boolean clip) {
ViewGroup parent = view;
while (parent != null) {
parent.setClipChildren(clip);
if (!(parent.getParent() instanceof ViewGroup)) break;
if (parent.getId() == android.R.id.content) break;
parent = (ViewGroup) parent.getParent();
}
}
/**
* Creates a {@link RoundedBitmapDrawable} using the provided {@link Bitmap} and cornerRadius.
* @param resources The {@link Resources}.
* @param icon The {@link Bitmap} to round.
* @param cornerRadius The corner radius.
* @return A {@link RoundedBitmapDrawable} for the provided {@link Bitmap}.
*/
public static RoundedBitmapDrawable createRoundedBitmapDrawable(
Resources resources, Bitmap icon, int cornerRadius) {
RoundedBitmapDrawable roundedIcon = RoundedBitmapDrawableFactory.create(resources, icon);
roundedIcon.setCornerRadius(cornerRadius);
return roundedIcon;
}
}
......@@ -4,12 +4,24 @@
package org.chromium.ui.base;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Region;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
/**
* A utility class that has helper methods for Android view.
*/
public final class ViewUtils {
private static final int[] sLocationTmp = new int[2];
// Prevent instantiation.
private ViewUtils() {}
......@@ -34,4 +46,150 @@ public final class ViewUtils {
private static boolean isFocusable(View view) {
return view.isInTouchMode() ? view.isFocusableInTouchMode() : view.isFocusable();
}
/**
* Invalidates a view and all of its descendants.
*/
private static void recursiveInvalidate(View view) {
view.invalidate();
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
int childCount = group.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = group.getChildAt(i);
if (child.getVisibility() == View.VISIBLE) {
recursiveInvalidate(child);
}
}
}
}
/**
* Sets the enabled property of a View and all of its descendants.
*/
public static void setEnabledRecursive(View view, boolean enabled) {
view.setEnabled(enabled);
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
for (int i = 0; i < group.getChildCount(); i++) {
setEnabledRecursive(group.getChildAt(i), enabled);
}
}
}
/**
* Captures a bitmap of a View and draws it to a Canvas.
*/
public static void captureBitmap(View view, Canvas canvas) {
// Invalidate all the descendants of view, before calling view.draw(). Otherwise, some of
// the descendant views may optimize away their drawing. http://crbug.com/415251
recursiveInvalidate(view);
view.draw(canvas);
}
/**
* Return the position of {@code childView} relative to {@code rootView}. {@code childView}
* must be a child of {@code rootView}. This returns the relative layout position, which does
* not include translations.
* @param rootView The parent of {@code childView} to calculate the position relative to.
* @param childView The {@link View} to calculate the position of.
* @param outPosition The resulting position with the format [x, y].
*/
public static void getRelativeLayoutPosition(View rootView, View childView, int[] outPosition) {
assert outPosition.length == 2;
outPosition[0] = 0;
outPosition[1] = 0;
if (rootView == null || childView == rootView) return;
while (childView != null) {
outPosition[0] += childView.getLeft();
outPosition[1] += childView.getTop();
if (childView.getParent() == rootView) break;
childView = (View) childView.getParent();
}
}
/**
* Return the position of {@code childView} relative to {@code rootView}. {@code childView}
* must be a child of {@code rootView}. This returns the relative draw position, which includes
* translations.
* @param rootView The parent of {@code childView} to calculate the position relative to.
* @param childView The {@link View} to calculate the position of.
* @param outPosition The resulting position with the format [x, y].
*/
public static void getRelativeDrawPosition(View rootView, View childView, int[] outPosition) {
assert outPosition.length == 2;
outPosition[0] = 0;
outPosition[1] = 0;
if (rootView == null || childView == rootView) return;
while (childView != null) {
outPosition[0] = (int) (outPosition[0] + childView.getX());
outPosition[1] = (int) (outPosition[1] + childView.getY());
if (childView.getParent() == rootView) break;
childView = (View) childView.getParent();
}
}
/**
* Helper for overriding {@link ViewGroup#gatherTransparentRegion} for views that are fully
* opaque and have children extending beyond their bounds. If the transparent region
* optimization is turned on (which is the case whenever the view hierarchy contains a
* SurfaceView somewhere), the children might otherwise confuse the SurfaceFlinger.
*/
public static void gatherTransparentRegionsForOpaqueView(View view, Region region) {
view.getLocationInWindow(sLocationTmp);
region.op(sLocationTmp[0], sLocationTmp[1],
sLocationTmp[0] + view.getRight() - view.getLeft(),
sLocationTmp[1] + view.getBottom() - view.getTop(), Region.Op.DIFFERENCE);
}
/**
* Converts density-independent pixels (dp) to pixels on the screen (px).
*
* @param dp Density-independent pixels are based on the physical density of the screen.
* @return The physical pixels on the screen which correspond to this many
* density-independent pixels for this screen.
*/
public static int dpToPx(Context context, float dp) {
return dpToPx(context.getResources().getDisplayMetrics(), dp);
}
/**
* Converts density-independent pixels (dp) to pixels on the screen (px).
*
* @param dp Density-independent pixels are based on the physical density of the screen.
* @return The physical pixels on the screen which correspond to this many
* density-independent pixels for this screen.
*/
public static int dpToPx(DisplayMetrics metrics, float dp) {
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics));
}
/**
* Sets clip children for the provided ViewGroup and all of its ancestors.
* @param view The ViewGroup whose children should (not) be clipped.
* @param clip Whether to clip children to the parent bounds.
*/
public static void setAncestorsShouldClipChildren(ViewGroup view, boolean clip) {
ViewGroup parent = view;
while (parent != null) {
parent.setClipChildren(clip);
if (!(parent.getParent() instanceof ViewGroup)) break;
if (parent.getId() == android.R.id.content) break;
parent = (ViewGroup) parent.getParent();
}
}
/**
* Creates a {@link RoundedBitmapDrawable} using the provided {@link Bitmap} and cornerRadius.
* @param resources The {@link Resources}.
* @param icon The {@link Bitmap} to round.
* @param cornerRadius The corner radius.
* @return A {@link RoundedBitmapDrawable} for the provided {@link Bitmap}.
*/
public static RoundedBitmapDrawable createRoundedBitmapDrawable(
Resources resources, Bitmap icon, int cornerRadius) {
RoundedBitmapDrawable roundedIcon = RoundedBitmapDrawableFactory.create(resources, icon);
roundedIcon.setCornerRadius(cornerRadius);
return roundedIcon;
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment