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 */ getType()47 public abstract @ConversationViewType int getType(); 48 49 /** 50 * Inflate and perform one-time initialization on a view for later binding. 51 */ createView(Context context, LayoutInflater inflater, ViewGroup parent)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 */ bindView(View v, boolean measureOnly)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 */ isContiguous()69 public abstract boolean isContiguous(); 70 getOnKeyListener()71 public View.OnKeyListener getOnKeyListener() { 72 return null; 73 } 74 75 /** 76 * Returns true if this overlay view is in its expanded state. 77 */ isExpanded()78 public boolean isExpanded() { 79 return true; 80 } 81 getGravity()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 */ getHeight()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 */ setHeight(int h)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 getTop()116 public int getTop() { 117 return mTop; 118 } 119 setTop(int top)120 public void setTop(int top) { 121 mTop = top; 122 } 123 isMeasurementValid()124 public boolean isMeasurementValid() { 125 return !mNeedsMeasure; 126 } 127 markMeasurementValid()128 public void markMeasurementValid() { 129 mNeedsMeasure = false; 130 } 131 invalidateMeasurement()132 public void invalidateMeasurement() { 133 mNeedsMeasure = true; 134 } 135 canBecomeSnapHeader()136 public boolean canBecomeSnapHeader() { 137 return false; 138 } 139 canPushSnapHeader()140 public boolean canPushSnapHeader() { 141 return false; 142 } 143 belongsToMessage(ConversationMessage message)144 public boolean belongsToMessage(ConversationMessage message) { 145 return false; 146 } 147 setMessage(ConversationMessage message)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 */ onModelUpdated(View v)156 public void onModelUpdated(View v) { 157 } 158 setPosition(int position)159 public void setPosition(int position) { 160 mPosition = position; 161 } 162 getPosition()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 */ rebindView(View view)190 public void rebindView(View view) { 191 // DO NOTHING 192 } 193 getFocusableView()194 public View getFocusableView() { 195 // Focus the root view by default 196 return mRootView; 197 } 198 registerOnKeyListeners(View... views)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