blob: b0cb520219c81102e7d89d2150339f1d775871ef [file] [log] [blame]
// 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.ui.widget;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.os.Build;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import org.chromium.base.SysUtils;
/**
* Toast wrapper, makes sure toasts are not HW accelerated on low-end devices and presented
* correctly (i.e. use VrToast while in virtual reality).
*
* Can (and should) also be used for Chromium-related additions and extensions.
*/
public class Toast {
public static final int LENGTH_SHORT = android.widget.Toast.LENGTH_SHORT;
public static final int LENGTH_LONG = android.widget.Toast.LENGTH_LONG;
private static int sExtraYOffset;
private android.widget.Toast mToast;
private ViewGroup mSWLayout;
public Toast(Context context) {
this(context, UiWidgetFactory.getInstance().createToast(context));
}
private Toast(Context context, android.widget.Toast toast) {
mToast = toast;
if (SysUtils.isLowEndDevice() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Don't HW accelerate Toasts. Unfortunately the only way to do that is to make
// toast.getView().getContext().getApplicationInfo() return lies to prevent
// WindowManagerGlobal.addView() from adding LayoutParams.FLAG_HARDWARE_ACCELERATED.
mSWLayout = new FrameLayout(new ContextWrapper(context) {
@Override
public ApplicationInfo getApplicationInfo() {
ApplicationInfo info = new ApplicationInfo(super.getApplicationInfo());
// On Lollipop the condition we need to fail is
// "targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP"
// (and for Chrome targetSdkVersion is always the latest)
info.targetSdkVersion = Build.VERSION_CODES.KITKAT;
// On M+ the condition we need to fail is
// "flags & ApplicationInfo.FLAG_HARDWARE_ACCELERATED"
info.flags &= ~ApplicationInfo.FLAG_HARDWARE_ACCELERATED;
return info;
}
});
setView(toast.getView());
}
mToast.setGravity(
mToast.getGravity(), mToast.getXOffset(), mToast.getYOffset() + sExtraYOffset);
}
public android.widget.Toast getAndroidToast() {
return mToast;
}
public void show() {
mToast.show();
}
public void cancel() {
mToast.cancel();
}
public void setView(View view) {
if (mSWLayout != null) {
mSWLayout.removeAllViews();
if (view != null) {
mSWLayout.addView(view, WRAP_CONTENT, WRAP_CONTENT);
mToast.setView(mSWLayout);
} else {
// When null view is set we propagate it to the toast to trigger appropriate
// handling (for example show() throws an exception when view is null).
mToast.setView(null);
}
} else {
mToast.setView(view);
}
}
public View getView() {
if (mToast.getView() == null) {
return null;
}
if (mSWLayout != null) {
return mSWLayout.getChildAt(0);
} else {
return mToast.getView();
}
}
public void setDuration(int duration) {
mToast.setDuration(duration);
}
public int getDuration() {
return mToast.getDuration();
}
public void setMargin(float horizontalMargin, float verticalMargin) {
mToast.setMargin(horizontalMargin, verticalMargin);
}
public float getHorizontalMargin() {
return mToast.getHorizontalMargin();
}
public float getVerticalMargin() {
return mToast.getVerticalMargin();
}
public void setGravity(int gravity, int xOffset, int yOffset) {
mToast.setGravity(gravity, xOffset, yOffset);
}
public int getGravity() {
return mToast.getGravity();
}
public int getXOffset() {
return mToast.getXOffset();
}
public int getYOffset() {
return mToast.getYOffset();
}
public void setText(int resId) {
mToast.setText(resId);
}
public void setText(CharSequence s) {
mToast.setText(s);
}
@SuppressLint("ShowToast")
public static Toast makeText(Context context, CharSequence text, int duration) {
return new Toast(context, UiWidgetFactory.getInstance().makeToast(context, text, duration));
}
public static Toast makeText(Context context, int resId, int duration)
throws Resources.NotFoundException {
return makeText(context, context.getResources().getText(resId), duration);
}
/**
* Set extra Y offset for toasts all toasts created with this class. This can be overridden by
* calling {@link Toast#setGravity(int, int, int)} on an individual toast.
* @param yOffsetPx The Y offset from the normal toast position in px.
*/
public static void setGlobalExtraYOffset(int yOffsetPx) {
sExtraYOffset = yOffsetPx;
}
/**
* Shows a toast anchored on a view.
* @param context The context to use for the toast.
* @param view The view to anchor the toast.
* @param description The string shown in the toast.
* @return Whether a toast has been shown successfully.
*/
@SuppressLint("RtlHardcoded")
public static boolean showAnchoredToast(Context context, View view, CharSequence description) {
if (description == null) return false;
final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
final int screenHeight = context.getResources().getDisplayMetrics().heightPixels;
final int[] screenPos = new int[2];
view.getLocationOnScreen(screenPos);
final int width = view.getWidth();
final int height = view.getHeight();
final int horizontalGravity =
(screenPos[0] < screenWidth / 2) ? Gravity.LEFT : Gravity.RIGHT;
final int xOffset = (screenPos[0] < screenWidth / 2)
? screenPos[0] + width / 2
: screenWidth - screenPos[0] - width / 2;
final int yOffset = (screenPos[1] < screenHeight / 2) ? screenPos[1] + height / 2
: screenPos[1] - height * 3 / 2;
Toast toast = Toast.makeText(context, description, Toast.LENGTH_SHORT);
toast.setGravity(Gravity.TOP | horizontalGravity, xOffset, yOffset);
toast.show();
return true;
}
}