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.ui; 19 20 import android.os.Bundle; 21 22 import com.android.mail.analytics.Analytics; 23 import com.android.mail.utils.LogUtils; 24 import com.google.common.collect.Lists; 25 26 import java.util.ArrayList; 27 28 /** 29 * Represents the view mode for the tablet Gmail activity. 30 * Transitions between modes should be done through this central object, and UI components that are 31 * dependent on the mode should listen to changes on this object. 32 */ 33 public class ViewMode { 34 /** 35 * A listener for changes on a ViewMode. To listen to mode changes, implement this 36 * interface and register your object with the single ViewMode held by the ActivityController 37 * instance. On mode changes, the onViewModeChanged method will be called with the new mode. 38 */ 39 public interface ModeChangeListener { 40 /** 41 * Called when the mode has changed. 42 */ 43 void onViewModeChanged(int newMode); 44 } 45 46 /** 47 * Mode when showing a single conversation. 48 */ 49 public static final int CONVERSATION = 1; 50 /** 51 * Mode when showing a list of conversations 52 */ 53 public static final int CONVERSATION_LIST = 2; 54 /** 55 * Mode when showing results from user search. 56 */ 57 public static final int SEARCH_RESULTS_LIST = 3; 58 /** 59 * Mode when showing results from user search. 60 */ 61 public static final int SEARCH_RESULTS_CONVERSATION = 4; 62 /** 63 * Mode when showing the "waiting for sync" message. 64 */ 65 public static final int WAITING_FOR_ACCOUNT_INITIALIZATION = 5; 66 /** 67 * Mode when showing ads. 68 */ 69 public static final int AD = 6; 70 /** 71 * Uncertain mode. The mode has not been initialized. 72 */ 73 public static final int UNKNOWN = 0; 74 75 // Key used to save this {@link ViewMode}. 76 private static final String VIEW_MODE_KEY = "view-mode"; 77 private final ArrayList<ModeChangeListener> mListeners = Lists.newArrayList(); 78 /** 79 * The actual mode the activity is in. We start out with an UNKNOWN mode, and require entering 80 * a valid mode after the object has been created. 81 */ 82 private int mMode = UNKNOWN; 83 84 public static final String LOG_TAG = "ViewMode"; 85 86 // friendly names (not user-facing) for each view mode, indexed by ordinal value. 87 private static final String[] MODE_NAMES = { 88 "Unknown", 89 "Conversation", 90 "Conversation list", 91 "Search results list", 92 "Search results conversation", 93 "Waiting for sync", 94 "Ad" 95 }; 96 97 public ViewMode() { 98 // Do nothing 99 } 100 101 @Override 102 public String toString() { 103 return "[mode=" + MODE_NAMES[mMode] + "]"; 104 } 105 106 public String getModeString() { 107 return MODE_NAMES[mMode]; 108 } 109 110 /** 111 * Adds a listener from this view mode. 112 * Must happen in the UI thread. 113 */ 114 public void addListener(ModeChangeListener listener) { 115 mListeners.add(listener); 116 } 117 118 /** 119 * Dispatches a change event for the mode. 120 * Always happens in the UI thread. 121 */ 122 private void dispatchModeChange() { 123 ArrayList<ModeChangeListener> list = new ArrayList<ModeChangeListener>(mListeners); 124 for (ModeChangeListener listener : list) { 125 assert (listener != null); 126 listener.onViewModeChanged(mMode); 127 } 128 } 129 130 /** 131 * Requests a transition of the mode to show the conversation list as the prominent view. 132 * 133 */ 134 public void enterConversationListMode() { 135 setModeInternal(CONVERSATION_LIST); 136 } 137 138 /** 139 * Requests a transition of the mode to show a conversation as the prominent view. 140 * 141 */ 142 public void enterConversationMode() { 143 setModeInternal(CONVERSATION); 144 } 145 146 /** 147 * Requests a transition of the mode to show a list of search results as the 148 * prominent view. 149 * 150 */ 151 public void enterSearchResultsListMode() { 152 setModeInternal(SEARCH_RESULTS_LIST); 153 } 154 155 /** 156 * Requests a transition of the mode to show a conversation that was part of 157 * search results. 158 * 159 */ 160 public void enterSearchResultsConversationMode() { 161 setModeInternal(SEARCH_RESULTS_CONVERSATION); 162 } 163 164 /** 165 * Requests a transition of the mode to show the "waiting for sync" messages 166 * 167 */ 168 public void enterWaitingForInitializationMode() { 169 setModeInternal(WAITING_FOR_ACCOUNT_INITIALIZATION); 170 } 171 172 /** 173 * Requests a transition of the mode to show an ad. 174 */ 175 public void enterAdMode() { 176 setModeInternal(AD); 177 } 178 179 /** 180 * @return The current mode. 181 */ 182 public int getMode() { 183 return mMode; 184 } 185 186 /** 187 * Return whether the current mode is considered a list mode. 188 */ 189 public boolean isListMode() { 190 return isListMode(mMode); 191 } 192 193 public static boolean isListMode(final int mode) { 194 return mode == CONVERSATION_LIST || mode == SEARCH_RESULTS_LIST; 195 } 196 197 public boolean isConversationMode() { 198 return isConversationMode(mMode); 199 } 200 201 public static boolean isConversationMode(final int mode) { 202 return mode == CONVERSATION || mode == SEARCH_RESULTS_CONVERSATION; 203 } 204 205 public static boolean isSearchMode(final int mode) { 206 return mode == SEARCH_RESULTS_LIST || mode == SEARCH_RESULTS_CONVERSATION; 207 } 208 209 public boolean isWaitingForSync() { 210 return isWaitingForSync(mMode); 211 } 212 213 public static boolean isWaitingForSync(final int mode) { 214 return mode == WAITING_FOR_ACCOUNT_INITIALIZATION; 215 } 216 217 public boolean isAdMode() { 218 return isAdMode(mMode); 219 } 220 221 public static boolean isAdMode(final int mode) { 222 return mode == AD; 223 } 224 225 /** 226 * Restoring from a saved state restores only the mode. It does not restore the listeners of 227 * this object. 228 * @param inState 229 */ 230 public void handleRestore(Bundle inState) { 231 if (inState == null) { 232 return; 233 } 234 // Restore the previous mode, and UNKNOWN if nothing exists. 235 final int newMode = inState.getInt(VIEW_MODE_KEY, UNKNOWN); 236 if (newMode != UNKNOWN) { 237 setModeInternal(newMode); 238 } 239 } 240 241 /** 242 * Save the existing mode only. Does not save the existing listeners. 243 * @param outState 244 */ 245 public void handleSaveInstanceState(Bundle outState) { 246 if (outState == null) { 247 return; 248 } 249 outState.putInt(VIEW_MODE_KEY, mMode); 250 } 251 252 /** 253 * Removes a listener from this view mode. 254 * Must happen in the UI thread. 255 */ 256 public void removeListener(ModeChangeListener listener) { 257 mListeners.remove(listener); 258 } 259 260 /** 261 * Sets the internal mode. 262 * @return Whether or not a change occurred. 263 */ 264 private boolean setModeInternal(int mode) { 265 if (mMode == mode) { 266 if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) { 267 LogUtils.d(LOG_TAG, new Error(), "ViewMode: debouncing change attempt mode=%s", 268 mode); 269 } else { 270 LogUtils.i(LOG_TAG, "ViewMode: debouncing change attempt mode=%s", mode); 271 } 272 return false; 273 } 274 275 if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) { 276 LogUtils.d(LOG_TAG, new Error(), "ViewMode: executing change old=%s new=%s", mMode, 277 mode); 278 } else { 279 LogUtils.i(LOG_TAG, "ViewMode: executing change old=%s new=%s", mMode, mode); 280 } 281 282 mMode = mode; 283 dispatchModeChange(); 284 Analytics.getInstance().sendView("ViewMode" + toString()); 285 return true; 286 } 287 } 288