Home | History | Annotate | Download | only in browse
      1 /*
      2  * Copyright (C) 2012 Google Inc.
      3  * Licensed to The Android Open Source Project.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.mail.browse;
     19 
     20 import android.content.Context;
     21 import android.view.Gravity;
     22 import android.view.LayoutInflater;
     23 import android.view.View;
     24 import android.view.ViewGroup;
     25 import android.widget.Adapter;
     26 import android.widget.CursorAdapter;
     27 
     28 import com.android.mail.browse.ConversationViewAdapter.ConversationViewType;
     29 import com.android.mail.ui.ConversationViewFragment;
     30 import com.android.mail.utils.LogUtils;
     31 
     32 public abstract class ConversationOverlayItem {
     33     private int mHeight;  // in px
     34     private int mTop;  // in px
     35     private boolean mNeedsMeasure;
     36 
     37     public static final String LOG_TAG = ConversationViewFragment.LAYOUT_TAG;
     38 
     39     private int mPosition;
     40 
     41     // The view to focus when this overlay item should be focused.
     42     protected View mRootView;
     43 
     44     /**
     45      * @see Adapter#getItemViewType(int)
     46      */
     47     public abstract @ConversationViewType int getType();
     48 
     49     /**
     50      * Inflate and perform one-time initialization on a view for later binding.
     51      */
     52     public abstract View createView(Context context, LayoutInflater inflater,
     53             ViewGroup parent);
     54 
     55     /**
     56      * @see CursorAdapter#bindView(View, Context, android.database.Cursor)
     57      * @param v a view to bind to
     58      * @param measureOnly true iff we are binding this view only to measure its height (so items
     59      * know they can cut certain corners that do not affect a view's height)
     60      */
     61     public abstract void bindView(View v, boolean measureOnly);
     62 
     63     /**
     64      * Returns true if this overlay view is meant to be positioned right on top of the overlay
     65      * below. This special positioning allows {@link ConversationContainer} to stack overlays
     66      * together even when zoomed into a conversation, when the overlay spacers spread farther
     67      * apart.
     68      */
     69     public abstract boolean isContiguous();
     70 
     71     public View.OnKeyListener getOnKeyListener() {
     72         return null;
     73     }
     74 
     75     /**
     76      * Returns true if this overlay view is in its expanded state.
     77      */
     78     public boolean isExpanded() {
     79         return true;
     80     }
     81 
     82     public int getGravity() {
     83         return Gravity.BOTTOM;
     84     }
     85 
     86     /**
     87      * This method's behavior is critical and requires some 'splainin.
     88      * <p>
     89      * Subclasses that return a zero-size height to the {@link ConversationContainer} will
     90      * cause the scrolling/recycling logic there to remove any matching view from the container.
     91      * The item should switch to returning a non-zero height when its view should re-appear.
     92      * <p>
     93      * It's imperative that this method stay in sync with the current height of the HTML spacer
     94      * that matches this overlay.
     95      */
     96     public int getHeight() {
     97         return mHeight;
     98     }
     99 
    100     /**
    101      * Set a new height.
    102      *
    103      * @param h a new height
    104      * @return true if the value changed
    105      */
    106     public boolean setHeight(int h) {
    107         LogUtils.i(LOG_TAG, "IN setHeight=%dpx of overlay item: %s", h, this);
    108         if (mHeight != h) {
    109             mHeight = h;
    110             mNeedsMeasure = true;
    111             return true;
    112         }
    113         return false;
    114     }
    115 
    116     public int getTop() {
    117         return mTop;
    118     }
    119 
    120     public void setTop(int top) {
    121         mTop = top;
    122     }
    123 
    124     public boolean isMeasurementValid() {
    125         return !mNeedsMeasure;
    126     }
    127 
    128     public void markMeasurementValid() {
    129         mNeedsMeasure = false;
    130     }
    131 
    132     public void invalidateMeasurement() {
    133         mNeedsMeasure = true;
    134     }
    135 
    136     public boolean canBecomeSnapHeader() {
    137         return false;
    138     }
    139 
    140     public boolean canPushSnapHeader() {
    141         return false;
    142     }
    143 
    144     public boolean belongsToMessage(ConversationMessage message) {
    145         return false;
    146     }
    147 
    148     public void setMessage(ConversationMessage message) {
    149     }
    150 
    151     /**
    152      * Given a view that is already bound to this item, force the view to re-render the item's
    153      * current model data. This is typically called after a data model update, to update the
    154      * affected view in-place.
    155      */
    156     public void onModelUpdated(View v) {
    157     }
    158 
    159     public void setPosition(int position) {
    160         mPosition = position;
    161     }
    162 
    163     public int getPosition() {
    164         return mPosition;
    165     }
    166 
    167     /**
    168      * This is a hack. Now that one view can update the
    169      * state of another view, we need a mechanism when the
    170      * view's associated item changes to update the state of the
    171      * view. Typically, classes that override this class should not
    172      * override this method.<br><br>
    173      *
    174      * This method is used by
    175      * {@link com.android.mail.browse.ConversationViewAdapter.BorderItem}
    176      * to update the height of the border based on whether the neighboring messages
    177      * are collapsed or expanded.<br><br>
    178      *
    179      * It is also used by {@link com.android.mail.browse.ConversationViewAdapter.MessageHeaderItem}
    180      * in the case where the snap header is tapped to collapse the message but the
    181      * message header is still on screen. Since the message header is still on screen,
    182      * it does not get bound but will get a rebind.<br><br>
    183      *
    184      * The only other way to handle this case would be to call
    185      * {@link com.android.mail.browse.ConversationViewAdapter#notifyDataSetChanged()}
    186      * but that makes the entire screen flicker since the entire adapter performs
    187      * a layout of the every item.
    188      * @param view the view to be re-bound
    189      */
    190     public void rebindView(View view) {
    191         // DO NOTHING
    192     }
    193 
    194     public View getFocusableView() {
    195         // Focus the root view by default
    196         return mRootView;
    197     }
    198 
    199     public void registerOnKeyListeners(View... views) {
    200         final View.OnKeyListener listener = getOnKeyListener();
    201         if (listener != null) {
    202             for (View v : views) {
    203                 if (v != null) {
    204                     v.setOnKeyListener(listener);
    205                 }
    206             }
    207         }
    208     }
    209 }
    210