/* * ErrorBanner.java * Copyright (C) 2015 Nicholas Killewald * * This file is distributed under the terms of the BSD license. * The source package should have a LICENSE file at the toplevel. */ package net.exclaimindustries.geohashdroid.widgets; import android.app.Activity; import android.content.Context; import android.preference.PreferenceManager; import android.support.annotation.ColorRes; import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.view.View; import android.view.ViewTreeObserver; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; import net.exclaimindustries.geohashdroid.R; import net.exclaimindustries.geohashdroid.util.GHDConstants; /** * <p> * An <code>ErrorBanner</code> is meant to appear on the top of a View (usually * the root View of an Activity) and slides in and out of place to report on * errors, warnings, notifications, etc. * </p> * * <p> * Given it's used for informational purposes, not just errors, this class is * perhaps increasingly ill-named. * </p> */ public class ErrorBanner extends LinearLayout { private TextView mMessage; private ImageButton mClose; private boolean mAlreadyLaidOut = false; private boolean mIsNightMode; /** * Use these in {@link #setErrorStatus(Status)} to set a premade * background for these sorts of errors. */ public enum Status { /** A normal error banner (white or black). */ NORMAL, /** An error banner screaming a warning (yellow). */ WARNING, /** An error banner just giving up with an error (red). */ ERROR, /** A banner that proclaims victory (green)! */ VICTORY } public ErrorBanner(Context context) { this(context, null); } public ErrorBanner(Context context, AttributeSet attrs) { super(context, attrs); inflate(context, R.layout.error_banner, this); // Get us the message widget for later. mMessage = (TextView)findViewById(R.id.error_text); // The button is always close. mClose = (ImageButton)findViewById(R.id.close); mClose.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // Away it goes! animateBanner(false); } }); // Night? Maybe? mIsNightMode = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(GHDConstants.PREF_NIGHT_MODE, false); // Set the close button as need be. if(mIsNightMode) mClose.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.cancel_button_dark)); // On startup, we want to make sure the view is off-screen until we're // told different. getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // Got a height! Hopefully. if(!mAlreadyLaidOut) { mAlreadyLaidOut = true; setBannerVisible(false); setVisibility(View.VISIBLE); } } }); } /** * Sets the banner's visibility. Note that this is NOT the same as the View * visibility (GONE, INVISIBLE, VISIBLE). This sets whether or not the * banner is hidden away off the top of the view and alpha'd to 0. You * would use this one to <i>instantly</i> change the visibility, not animate * it. * * @param visible true to be visible, false to be hidden */ public void setBannerVisible(boolean visible) { if(!visible) { // The translation should be negative the height of the view, which // should neatly hide it away while allowing it to slide back in // later if need be. setTranslationY(-getHeight()); setAlpha(0.0f); } else { // Otherwise, it goes back to its normal spot. setTranslationY(0.0f); setAlpha(1.0f); } } /** * Slides the banner in or out of view. Make sure it's set up first! * * @param visible true to show, false to slide away */ public void animateBanner(boolean visible) { if(!visible) { // Slide out! animate().translationY(-getHeight()).alpha(0.0f); } else { // Slide in! animate().translationY(0.0f).alpha(1.0f); } } /** * Sets the text on the banner. * * @param text text to display */ public void setText(final String text) { ((Activity)getContext()).runOnUiThread(new Runnable() { @Override public void run() { setCloseVisible(true); mMessage.setText(text); } }); } /** * Convenience method to set a commonly-used background color. * * @param b type of background to set */ public void setErrorStatus(@NonNull Status b) { @ColorRes int color = 0; switch(b) { case NORMAL: color = (mIsNightMode ? R.color.error_banner_normal_dark : R.color.error_banner_normal); break; case WARNING: color = (mIsNightMode ? R.color.error_banner_warning_dark : R.color.error_banner_warning); break; case ERROR: color = (mIsNightMode ? R.color.error_banner_error_dark : R.color.error_banner_error); break; case VICTORY: color = (mIsNightMode ? R.color.error_banner_victory_dark : R.color.error_banner_victory); } setBackgroundErrorColor(color); } /** * Sets the background color. For instance, you'd set this to red for * errors, yellow for warnings, white if you forgot to change the color, * that sort of thing. * * @param color the new color to set */ public void setBackgroundErrorColor(@ColorRes final int color) { ((Activity)getContext()).runOnUiThread(new Runnable() { @Override public void run() { setCloseVisible(true); setBackgroundColor(ContextCompat.getColor(getContext(), color)); } }); } /** * <p> * Sets the close button to be visible or not. Making it invisible * effectively makes the banner uncloseable from the UI. * </p> * * <p> * Note that, by design, {@link #setText(String)}, {@link #setBackgroundErrorColor(int)}, * and {@link #setErrorStatus(Status)} will automatically make this visible * again, since those are typically called when making a new banner anyway. * Make sure you hide the close button LAST. * </p> * * @param visible true to make visible, false to hide */ public void setCloseVisible(final boolean visible) { ((Activity)getContext()).runOnUiThread(new Runnable() { @Override public void run() { mClose.setVisibility(visible ? View.VISIBLE : View.GONE); } }); } }