1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.browser; 18 19 import android.content.Context; 20 import android.graphics.Bitmap; 21 import android.os.Handler; 22 import android.util.AttributeSet; 23 import android.view.KeyEvent; 24 import android.view.LayoutInflater; 25 import android.view.View; 26 import android.view.ViewGroup; 27 import android.widget.AdapterView; 28 import android.widget.BaseAdapter; 29 import android.widget.ImageView; 30 import android.widget.LinearLayout; 31 import android.widget.ListView; 32 import android.widget.TextView; 33 34 public class ActiveTabsPage extends LinearLayout { 35 private final BrowserActivity mBrowserActivity; 36 private final LayoutInflater mFactory; 37 private final TabControl mControl; 38 private final TabsListAdapter mAdapter; 39 private final ListView mListView; 40 41 public ActiveTabsPage(BrowserActivity context, TabControl control) { 42 super(context); 43 mBrowserActivity = context; 44 mControl = control; 45 mFactory = LayoutInflater.from(context); 46 mFactory.inflate(R.layout.active_tabs, this); 47 mListView = (ListView) findViewById(R.id.list); 48 mAdapter = new TabsListAdapter(); 49 mListView.setAdapter(mAdapter); 50 mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 51 public void onItemClick(AdapterView<?> parent, View view, 52 int position, long id) { 53 if (mControl.canCreateNewTab()) { 54 position--; 55 } 56 boolean needToAttach = false; 57 if (position == -1) { 58 // Create a new tab 59 mBrowserActivity.openTabToHomePage(); 60 } else { 61 // Open the corresponding tab 62 // If the tab is the current one, switchToTab will 63 // do nothing and return, so we need to make sure 64 // it gets attached back to its mContentView in 65 // removeActiveTabPage 66 needToAttach = !mBrowserActivity.switchToTab(position); 67 } 68 mBrowserActivity.removeActiveTabPage(needToAttach); 69 } 70 }); 71 } 72 73 /** 74 * Special class to hold the close drawable. Its sole purpose is to allow 75 * the parent to be pressed without being pressed itself. This way the line 76 * of a tab can be pressed, but the close button itself is not. 77 */ 78 private static class CloseHolder extends ImageView { 79 public CloseHolder(Context context, AttributeSet attrs) { 80 super(context, attrs); 81 } 82 83 @Override 84 public void setPressed(boolean pressed) { 85 // If the parent is pressed, do not set to pressed. 86 if (pressed && ((View) getParent()).isPressed()) { 87 return; 88 } 89 super.setPressed(pressed); 90 } 91 } 92 93 private class TabsListAdapter extends BaseAdapter { 94 private boolean mNotified = true; 95 private int mReturnedCount; 96 private Handler mHandler = new Handler(); 97 98 public int getCount() { 99 int count = mControl.getTabCount(); 100 if (mControl.canCreateNewTab()) { 101 count++; 102 } 103 // XXX: This is a workaround to be more like a real adapter. Most 104 // adapters call notifyDataSetChanged() whenever the internal data 105 // has changed. Since TabControl is our internal data, we don't 106 // know when that changes. 107 // 108 // Keep track of the last count we returned and whether we called 109 // notifyDataSetChanged(). If we did not initiate a data set 110 // change, and the count is different, send the notify and return 111 // the old count. 112 if (!mNotified && count != mReturnedCount) { 113 notifyChange(); 114 return mReturnedCount; 115 } 116 mReturnedCount = count; 117 mNotified = false; 118 return count; 119 } 120 public Object getItem(int position) { 121 return null; 122 } 123 public long getItemId(int position) { 124 return position; 125 } 126 public int getViewTypeCount() { 127 return 2; 128 } 129 public int getItemViewType(int position) { 130 if (mControl.canCreateNewTab()) { 131 position--; 132 } 133 // Do not recycle the "add new tab" item. 134 return position == -1 ? IGNORE_ITEM_VIEW_TYPE : 1; 135 } 136 public View getView(int position, View convertView, ViewGroup parent) { 137 final int tabCount = mControl.getTabCount(); 138 if (mControl.canCreateNewTab()) { 139 position--; 140 } 141 142 if (convertView == null) { 143 convertView = mFactory.inflate(position == -1 ? 144 R.layout.tab_view_add_tab : R.layout.tab_view, null); 145 } 146 147 if (position != -1) { 148 TextView title = 149 (TextView) convertView.findViewById(R.id.title); 150 TextView url = (TextView) convertView.findViewById(R.id.url); 151 ImageView favicon = 152 (ImageView) convertView.findViewById(R.id.favicon); 153 View close = convertView.findViewById(R.id.close); 154 Tab tab = mControl.getTab(position); 155 tab.populatePickerData(); 156 title.setText(tab.getTitle()); 157 url.setText(tab.getUrl()); 158 Bitmap icon = tab.getFavicon(); 159 if (icon != null) { 160 favicon.setImageBitmap(icon); 161 } else { 162 favicon.setImageResource(R.drawable.app_web_browser_sm); 163 } 164 final int closePosition = position; 165 close.setOnClickListener(new View.OnClickListener() { 166 public void onClick(View v) { 167 mBrowserActivity.closeTab( 168 mControl.getTab(closePosition)); 169 if (tabCount == 1) { 170 mBrowserActivity.openTabToHomePage(); 171 mBrowserActivity.removeActiveTabPage(false); 172 } else { 173 mNotified = true; 174 notifyDataSetChanged(); 175 } 176 } 177 }); 178 } 179 return convertView; 180 } 181 182 void notifyChange() { 183 mHandler.post(new Runnable() { 184 public void run() { 185 mNotified = true; 186 notifyDataSetChanged(); 187 } 188 }); 189 } 190 } 191 } 192