mobile/android/base/widget/DoorHanger.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
     2  * This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 package org.mozilla.gecko.widget;
     8 import org.mozilla.gecko.R;
     9 import org.mozilla.gecko.Tabs;
    10 import org.mozilla.gecko.prompts.PromptInput;
    12 import org.json.JSONArray;
    13 import org.json.JSONException;
    14 import org.json.JSONObject;
    16 import android.content.Context;
    17 import android.content.res.Resources;
    18 import android.graphics.Rect;
    19 import android.os.Build;
    20 import android.text.SpannableString;
    21 import android.text.TextUtils;
    22 import android.text.method.LinkMovementMethod;
    23 import android.text.style.ForegroundColorSpan;
    24 import android.text.style.URLSpan;
    25 import android.util.Log;
    26 import android.view.LayoutInflater;
    27 import android.view.View;
    28 import android.view.ViewGroup;
    29 import android.widget.Button;
    30 import android.widget.CheckBox;
    31 import android.widget.ImageView;
    32 import android.widget.LinearLayout;
    33 import android.widget.Spinner;
    34 import android.widget.SpinnerAdapter;
    35 import android.widget.TextView;
    37 import java.util.ArrayList;
    38 import java.util.List;
    40 public class DoorHanger extends LinearLayout {
    41     private static final String LOGTAG = "GeckoDoorHanger";
    43     private static int sInputPadding = -1;
    44     private static int sSpinnerTextColor = -1;
    45     private static int sSpinnerTextSize = -1;
    47     private static LayoutParams sButtonParams;
    48     static {
    49         sButtonParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT, 1.0f);
    50     }
    52     private final TextView mTextView;
    53     private final ImageView mIcon;
    54     private final LinearLayout mChoicesLayout;
    56     // Divider between doorhangers.
    57     private final View mDivider;
    59     // The tab associated with this notification.
    60     private final int mTabId;
    62     // Value used to identify the notification.
    63     private final String mValue;
    65     private Resources mResources;
    67     private List<PromptInput> mInputs;
    68     private CheckBox mCheckBox;
    70     private int mPersistence = 0;
    71     private boolean mPersistWhileVisible = false;
    72     private long mTimeout = 0;
    74     // Color used for dividers above and between buttons.
    75     private int mDividerColor;
    77     public static enum Theme {
    78         LIGHT,
    79         DARK
    80     }
    82     public interface OnButtonClickListener {
    83         public void onButtonClick(DoorHanger dh, String tag);
    84     }
    86     public DoorHanger(Context context, Theme theme) {
    87         this(context, 0, null, theme);
    88     }
    90     public DoorHanger(Context context, int tabId, String value) {
    91         this(context, tabId, value, Theme.LIGHT);
    92     }
    94     private DoorHanger(Context context, int tabId, String value, Theme theme) {
    95         super(context);
    97         mTabId = tabId;
    98         mValue = value;
    99         mResources = getResources();
   101         if (sInputPadding == -1) {
   102             sInputPadding = mResources.getDimensionPixelSize(R.dimen.doorhanger_padding);
   103         }
   104         if (sSpinnerTextColor == -1) {
   105             sSpinnerTextColor = mResources.getColor(R.color.text_color_primary_disable_only);
   106         }
   107         if (sSpinnerTextSize == -1) {
   108             sSpinnerTextSize = mResources.getDimensionPixelSize(R.dimen.doorhanger_spinner_textsize);
   109         }
   111         setOrientation(VERTICAL);
   113         LayoutInflater.from(context).inflate(R.layout.doorhanger, this);
   114         mTextView = (TextView) findViewById(R.id.doorhanger_title);
   115         mIcon = (ImageView) findViewById(R.id.doorhanger_icon);
   116         mChoicesLayout = (LinearLayout) findViewById(R.id.doorhanger_choices);
   117         mDivider = findViewById(R.id.divider_doorhanger);
   119         setTheme(theme);
   120     }
   122     private void setTheme(Theme theme) {
   123         if (theme == Theme.LIGHT) {
   124             // The default styles declared in doorhanger.xml are light-themed, so we just
   125             // need to set the divider color that we'll use in addButton.
   126             mDividerColor = mResources.getColor(R.color.doorhanger_divider_light);
   128         } else if (theme == Theme.DARK) {
   129             mDividerColor = mResources.getColor(R.color.doorhanger_divider_dark);
   131             // Set a dark background, and use a smaller text size for dark-themed DoorHangers.
   132             setBackgroundColor(mResources.getColor(R.color.doorhanger_background_dark));
   133             mTextView.setTextSize(mResources.getDimension(R.dimen.doorhanger_textsize_small));
   134         }
   135     }
   137     public int getTabId() {
   138         return mTabId;
   139     }
   141     public String getValue() {
   142         return mValue;
   143     }
   145     public List<PromptInput> getInputs() {
   146         return mInputs;
   147     }
   149     public CheckBox getCheckBox() {
   150         return mCheckBox;
   151     }
   153     public void showDivider() {
   154         mDivider.setVisibility(View.VISIBLE);
   155     }
   157     public void hideDivider() {
   158         mDivider.setVisibility(View.GONE);
   159     }
   161     public void setMessage(String message) {
   162         mTextView.setText(message);
   163     }
   165     public void setIcon(int resId) {
   166         mIcon.setImageResource(resId);
   167         mIcon.setVisibility(View.VISIBLE);
   168     }
   170     public void addLink(String label, String url, String delimiter) {
   171         String title = mTextView.getText().toString();
   172         SpannableString titleWithLink = new SpannableString(title + delimiter + label);
   173         URLSpan linkSpan = new URLSpan(url) {
   174             @Override
   175             public void onClick(View view) {
   176                 Tabs.getInstance().loadUrlInTab(getURL());
   177             }
   178         };
   180         // Prevent text outside the link from flashing when clicked.
   181         ForegroundColorSpan colorSpan = new ForegroundColorSpan(mTextView.getCurrentTextColor());
   182         titleWithLink.setSpan(colorSpan, 0, title.length(), 0);
   184         titleWithLink.setSpan(linkSpan, title.length() + 1, titleWithLink.length(), 0);
   185         mTextView.setText(titleWithLink);
   186         mTextView.setMovementMethod(LinkMovementMethod.getInstance());
   187     }
   189     public void addButton(final String text, final String tag, final OnButtonClickListener listener) {
   190         final Button button = (Button) LayoutInflater.from(getContext()).inflate(R.layout.doorhanger_button, null);
   191         button.setText(text);
   192         button.setTag(tag);
   194         button.setOnClickListener(new Button.OnClickListener() {
   195             @Override
   196             public void onClick(View v) {
   197                 listener.onButtonClick(DoorHanger.this, tag);
   198             }
   199         });
   201         if (mChoicesLayout.getChildCount() == 0) {
   202             // If this is the first button we're adding, make the choices layout visible.
   203             mChoicesLayout.setVisibility(View.VISIBLE);
   204             // Make the divider above the buttons visible.
   205             View divider = findViewById(R.id.divider_choices);
   206             divider.setVisibility(View.VISIBLE);
   207             divider.setBackgroundColor(mDividerColor);
   208         } else {
   209             // Add a vertical divider between additional buttons.
   210             Divider divider = new Divider(getContext(), null);
   211             divider.setOrientation(Divider.Orientation.VERTICAL);
   212             divider.setBackgroundColor(mDividerColor);
   213             mChoicesLayout.addView(divider);
   214         }
   216         mChoicesLayout.addView(button, sButtonParams);
   217     }
   219     public void setOptions(final JSONObject options) {
   220         final int persistence = options.optInt("persistence");
   221         if (persistence > 0) {
   222             mPersistence = persistence;
   223         }
   225         mPersistWhileVisible = options.optBoolean("persistWhileVisible");
   227         final long timeout = options.optLong("timeout");
   228         if (timeout > 0) {
   229             mTimeout = timeout;
   230         }
   232         final JSONObject link = options.optJSONObject("link");
   233         if (link != null) {
   234             try {
   235                 final String linkLabel = link.getString("label");
   236                 final String linkUrl = link.getString("url");
   237                 addLink(linkLabel, linkUrl, " ");
   238             } catch (JSONException e) { }
   239         }
   241         final JSONArray inputs = options.optJSONArray("inputs");
   242         if (inputs != null) {
   243             mInputs = new ArrayList<PromptInput>();
   245             final ViewGroup group = (ViewGroup) findViewById(R.id.doorhanger_inputs);
   246             group.setVisibility(VISIBLE);
   248             for (int i = 0; i < inputs.length(); i++) {
   249                 try {
   250                     PromptInput input = PromptInput.getInput(inputs.getJSONObject(i));
   251                     mInputs.add(input);
   253                     View v = input.getView(getContext());
   254                     styleInput(input, v);
   255                     group.addView(v);
   256                 } catch(JSONException ex) { }
   257             }
   258         }
   260         final String checkBoxText = options.optString("checkbox");
   261         if (!TextUtils.isEmpty(checkBoxText)) {
   262             mCheckBox = (CheckBox) findViewById(R.id.doorhanger_checkbox);
   263             mCheckBox.setText(checkBoxText);
   264             mCheckBox.setVisibility(VISIBLE);
   265         }
   266     }
   268     private void styleInput(PromptInput input, View view) {
   269         if (input instanceof PromptInput.MenulistInput) {
   270             styleSpinner(input, view);
   271         } else {
   272             // add some top and bottom padding to separate inputs
   273             view.setPadding(0, sInputPadding,
   274                             0, sInputPadding);
   275         }
   276     }
   278     private void styleSpinner(PromptInput input, View view) {
   279         PromptInput.MenulistInput spinInput = (PromptInput.MenulistInput) input;
   281         /* Spinners have some intrinsic padding. To force the spinner's text to line up with
   282          * the doorhanger text, we have to take that padding into account.
   283          * 
   284          * |-----A-------| <-- Normal doorhanger message
   285          * |-B-|---C+D---| <-- (optional) Spinner Label
   286          * |-B-|-C-|--D--| <-- Spinner
   287          *
   288          * A - Desired padding (sInputPadding)
   289          * B - Final padding applied to input element (sInputPadding - rect.left - textPadding).
   290          * C - Spinner background drawable padding (rect.left).
   291          * D - Spinner inner TextView padding (textPadding).
   292          */
   294         // First get the padding of the selected view inside the spinner. Since the spinner
   295         // hasn't been shown yet, we get this view directly from the adapter.
   296         Spinner spinner = spinInput.spinner;
   297         SpinnerAdapter adapter = spinner.getAdapter();
   298         View dropView = adapter.getView(0, null, spinner);
   299         int textPadding = 0;
   300         if (dropView != null) {
   301             textPadding = dropView.getPaddingLeft();
   302         }
   304         // Then get the intrinsic padding built into the background image of the spinner.
   305         Rect rect = new Rect();
   306         spinner.getBackground().getPadding(rect);
   308         // Set the difference in padding to the spinner view to align it with doorhanger text.
   309         view.setPadding(sInputPadding - rect.left - textPadding, 0, rect.right, sInputPadding);
   311         if (spinInput.textView != null) {
   312             spinInput.textView.setTextColor(sSpinnerTextColor);
   313             spinInput.textView.setTextSize(sSpinnerTextSize);
   315             // If this spinner has a label, offset it to also be aligned with the doorhanger text.
   316             spinInput.textView.setPadding(rect.left + textPadding, 0, 0, 0);
   317         }
   318     }
   321     /*
   322      * Checks with persistence and timeout options to see if it's okay to remove a doorhanger.
   323      *
   324      * @param isShowing Whether or not this doorhanger is currently visible to the user.
   325      *                 (e.g. the DoorHanger view might be VISIBLE, but its parent could be hidden)
   326      */
   327     public boolean shouldRemove(boolean isShowing) {
   328         if (mPersistWhileVisible && isShowing) {
   329             // We still want to decrement mPersistence, even if the popup is showing
   330             if (mPersistence != 0)
   331                 mPersistence--;
   332             return false;
   333         }
   335         // If persistence is set to -1, the doorhanger will never be
   336         // automatically removed.
   337         if (mPersistence != 0) {
   338             mPersistence--;
   339             return false;
   340         }
   342         if (System.currentTimeMillis() <= mTimeout) {
   343             return false;
   344         }
   346         return true;
   347     }
   348 }

mercurial