blob: e4940e9a34beda9aa583dd93349ba0d91be891a4 [file] [log] [blame]
Michael Kolb370a4f32010-10-06 10:45:32 -07001/*
2 * Copyright (C) 2010 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
17package com.android.browser;
18
19import android.content.Context;
Leon Scroggins7e5f7352010-10-18 13:25:31 -040020import android.graphics.drawable.Drawable;
Leon Scroggins74dbe012010-10-15 10:54:27 -040021import android.text.TextUtils;
Michael Kolb370a4f32010-10-06 10:45:32 -070022import android.util.AttributeSet;
23import android.view.Gravity;
24import android.view.View;
25import android.view.View.OnClickListener;
26import android.widget.ImageButton;
27import android.widget.ImageView;
28import android.widget.LinearLayout;
29import android.widget.TextView;
30
31import java.util.ArrayList;
32import java.util.List;
33
34/**
35 * Simple bread crumb view
36 * Use setController to receive callbacks from user interactions
37 * Use pushView, popView, clear, and getTopData to change/access the view stack
38 */
39public class BreadCrumbView extends LinearLayout implements OnClickListener {
40
41 interface Controller {
42 public void onTop(int level, Object data);
43 }
44
45 private ImageButton mBackButton;
46 private Controller mController;
47 private List<Crumb> mCrumbs;
48 private boolean mUseBackButton;
Leon Scroggins7e5f7352010-10-18 13:25:31 -040049 private Drawable mSeparatorDrawable;
John Reck89f73c12010-12-01 10:10:14 -080050 private int mMaxVisible = -1;
Michael Kolb370a4f32010-10-06 10:45:32 -070051
52 /**
53 * @param context
54 * @param attrs
55 * @param defStyle
56 */
57 public BreadCrumbView(Context context, AttributeSet attrs, int defStyle) {
58 super(context, attrs, defStyle);
59 init(context);
60 }
61
62 /**
63 * @param context
64 * @param attrs
65 */
66 public BreadCrumbView(Context context, AttributeSet attrs) {
67 super(context, attrs);
68 init(context);
69 }
70
71 /**
72 * @param context
73 */
74 public BreadCrumbView(Context context) {
75 super(context);
76 init(context);
77 }
78
79 private void init(Context ctx) {
80 mUseBackButton = false;
81 mCrumbs = new ArrayList<Crumb>();
Leon Scroggins7e5f7352010-10-18 13:25:31 -040082 mSeparatorDrawable = ctx.getResources().getDrawable(
83 R.drawable.crumb_divider);
John Reck89f73c12010-12-01 10:10:14 -080084 addBackButton();
Michael Kolb370a4f32010-10-06 10:45:32 -070085 }
86
87 public void setUseBackButton(boolean useflag) {
88 mUseBackButton = useflag;
John Reck89f73c12010-12-01 10:10:14 -080089 updateVisible();
Michael Kolb370a4f32010-10-06 10:45:32 -070090 }
91
92 public void setController(Controller ctl) {
93 mController = ctl;
94 }
95
John Reck89f73c12010-12-01 10:10:14 -080096 public int getMaxVisible() {
97 return mMaxVisible;
98 }
99
100 public void setMaxVisible(int max) {
101 mMaxVisible = max;
102 updateVisible();
103 }
104
105 public int getTopLevel() {
106 return mCrumbs.size();
107 }
108
Michael Kolb370a4f32010-10-06 10:45:32 -0700109 public Object getTopData() {
110 Crumb c = getTopCrumb();
111 if (c != null) {
112 return c.data;
113 }
114 return null;
115 }
116
117 public int size() {
118 return mCrumbs.size();
119 }
120
121 public void clear() {
122 while (mCrumbs.size() > 1) {
123 pop(false);
124 }
125 pop(true);
126 }
127
128 public void notifyController() {
129 if (mController != null) {
130 if (mCrumbs.size() > 0) {
131 mController.onTop(mCrumbs.size(), getTopCrumb().data);
132 } else {
133 mController.onTop(0, null);
134 }
135 }
136 }
137
138 public void pushView(String name, Object data) {
Leon Scroggins74dbe012010-10-15 10:54:27 -0400139 pushView(name, true, data);
140 }
141
142 public void pushView(String name, boolean canGoBack, Object data) {
143 Crumb crumb = new Crumb(name, canGoBack, data);
Michael Kolb370a4f32010-10-06 10:45:32 -0700144 pushCrumb(crumb);
145 }
146
147 public void pushView(View view, Object data) {
148 Crumb crumb = new Crumb(view, true, data);
149 pushCrumb(crumb);
150 }
151
152 public void popView() {
153 pop(true);
154 }
155
156 private void addBackButton() {
157 mBackButton = new ImageButton(mContext);
158 mBackButton.setImageResource(R.drawable.ic_back_normal);
159 mBackButton.setBackgroundResource(R.drawable.browserbarbutton);
160 mBackButton.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
161 LayoutParams.MATCH_PARENT));
162 mBackButton.setOnClickListener(this);
Leon Scroggins8a2a0e72010-10-15 15:49:40 -0400163 mBackButton.setVisibility(View.INVISIBLE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700164 addView(mBackButton, 0);
165 }
166
167 private void pushCrumb(Crumb crumb) {
John Reck89f73c12010-12-01 10:10:14 -0800168 if (mCrumbs.size() > 0) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700169 addSeparator();
170 }
171 mCrumbs.add(crumb);
172 addView(crumb.crumbView);
John Reck89f73c12010-12-01 10:10:14 -0800173 updateVisible();
Michael Kolb370a4f32010-10-06 10:45:32 -0700174 crumb.crumbView.setOnClickListener(this);
175 }
176
177 private void addSeparator() {
178 ImageView sep = new ImageView(mContext);
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400179 sep.setImageDrawable(mSeparatorDrawable);
Michael Kolb370a4f32010-10-06 10:45:32 -0700180 sep.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
181 LayoutParams.MATCH_PARENT));
182 addView(sep);
183 }
184
185 private void pop(boolean notify) {
186 int n = mCrumbs.size();
187 if (n > 0) {
188 removeLastView();
189 if (!mUseBackButton || (n > 1)) {
190 // remove separator
191 removeLastView();
192 }
193 mCrumbs.remove(n - 1);
194 if (mUseBackButton) {
195 Crumb top = getTopCrumb();
Leon Scroggins8a2a0e72010-10-15 15:49:40 -0400196 if (top != null && top.canGoBack) {
197 mBackButton.setVisibility(View.VISIBLE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700198 } else {
Leon Scroggins8a2a0e72010-10-15 15:49:40 -0400199 mBackButton.setVisibility(View.INVISIBLE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700200 }
201 }
John Reck89f73c12010-12-01 10:10:14 -0800202 updateVisible();
Michael Kolb370a4f32010-10-06 10:45:32 -0700203 if (notify) {
204 notifyController();
205 }
206 }
207 }
208
John Reck89f73c12010-12-01 10:10:14 -0800209 private void updateVisible() {
210 // start at index 1 (0 == back button)
211 int childIndex = 1;
212 if (mMaxVisible >= 0) {
213 int invisibleCrumbs = size() - mMaxVisible;
214 if (invisibleCrumbs > 0) {
215 int crumbIndex = 0;
216 while (crumbIndex < invisibleCrumbs) {
217 // Set the crumb to GONE.
218 getChildAt(childIndex).setVisibility(View.GONE);
219 childIndex++;
220 // Each crumb is followed by a separator (except the last
221 // one). Also make it GONE
222 if (getChildAt(childIndex) != null) {
223 getChildAt(childIndex).setVisibility(View.GONE);
224 }
225 childIndex++;
226 // Move to the next crumb.
227 crumbIndex++;
228 }
229 }
230 // Make sure the last two are visible.
231 int childCount = getChildCount();
232 while (childIndex < childCount) {
233 getChildAt(childIndex).setVisibility(View.VISIBLE);
234 childIndex++;
235 }
236 } else {
237 int count = getChildCount();
238 for (int i = childIndex; i < count ; i++) {
239 getChildAt(i).setVisibility(View.VISIBLE);
240 }
241 }
242 if (mUseBackButton) {
243 boolean canGoBack = getTopCrumb() != null ? getTopCrumb().canGoBack : false;
244 mBackButton.setVisibility(canGoBack ? View.VISIBLE : View.INVISIBLE);
245 } else {
246 mBackButton.setVisibility(View.GONE);
247 }
248 }
249
Michael Kolb370a4f32010-10-06 10:45:32 -0700250 private void removeLastView() {
251 int ix = getChildCount();
252 if (ix > 0) {
253 removeViewAt(ix-1);
254 }
255 }
256
257 private Crumb getTopCrumb() {
258 Crumb crumb = null;
259 if (mCrumbs.size() > 0) {
260 crumb = mCrumbs.get(mCrumbs.size() - 1);
261 }
262 return crumb;
263 }
264
265 @Override
266 public void onClick(View v) {
267 if (mBackButton == v) {
268 popView();
269 notifyController();
270 } else {
271 // pop until view matches crumb view
272 while (v != getTopCrumb().crumbView) {
273 pop(false);
274 }
275 notifyController();
276 }
277 }
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400278 @Override
279 public int getBaseline() {
280 int ix = getChildCount();
281 if (ix > 0) {
282 // If there is at least one crumb, the baseline will be its
283 // baseline.
284 return getChildAt(ix-1).getBaseline();
285 }
286 return super.getBaseline();
287 }
288
289 @Override
290 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
291 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
292 int height = mSeparatorDrawable.getIntrinsicHeight();
Dianne Hackborn7831da92010-12-03 00:19:01 -0800293 if (getMeasuredHeight() < height) {
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400294 // This should only be an issue if there are currently no separators
295 // showing; i.e. if there is one crumb and no back button.
296 int mode = View.MeasureSpec.getMode(heightMeasureSpec);
297 switch(mode) {
298 case View.MeasureSpec.AT_MOST:
299 if (View.MeasureSpec.getSize(heightMeasureSpec) < height) {
300 return;
301 }
302 break;
303 case View.MeasureSpec.EXACTLY:
304 return;
305 default:
306 break;
307 }
Dianne Hackborn7831da92010-12-03 00:19:01 -0800308 setMeasuredDimension(getMeasuredWidth(), height);
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400309 }
310 }
Michael Kolb370a4f32010-10-06 10:45:32 -0700311
312 class Crumb {
313
314 public View crumbView;
315 public boolean canGoBack;
316 public Object data;
317
318 public Crumb(String title, boolean backEnabled, Object tag) {
319 init(makeCrumbView(title), backEnabled, tag);
320 }
321
322 public Crumb(View view, boolean backEnabled, Object tag) {
323 init(view, backEnabled, tag);
324 }
325
326 private void init(View view, boolean back, Object tag) {
327 canGoBack = back;
328 crumbView = view;
329 data = tag;
330 }
331
332 private TextView makeCrumbView(String name) {
333 TextView tv = new TextView(mContext);
334 tv.setTextAppearance(mContext, android.R.style.TextAppearance_Medium);
335 tv.setPadding(16, 0, 16, 0);
336 tv.setGravity(Gravity.CENTER_VERTICAL);
337 tv.setText(name);
338 tv.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
339 LayoutParams.MATCH_PARENT));
Leon Scroggins74dbe012010-10-15 10:54:27 -0400340 tv.setMaxWidth(mContext.getResources().getInteger(
341 R.integer.max_width_crumb));
342 tv.setMaxLines(1);
343 tv.setEllipsize(TextUtils.TruncateAt.END);
Michael Kolb370a4f32010-10-06 10:45:32 -0700344 return tv;
345 }
346
347 }
348
349}