blob: 113d55b40c10fdeb32b3341795da5fa5ad6dbec7 [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
Leon Scroggins905250c2010-12-17 15:25:33 -0500138 public View pushView(String name, Object data) {
139 return pushView(name, true, data);
Leon Scroggins74dbe012010-10-15 10:54:27 -0400140 }
141
Leon Scroggins905250c2010-12-17 15:25:33 -0500142 public View pushView(String name, boolean canGoBack, Object data) {
Leon Scroggins74dbe012010-10-15 10:54:27 -0400143 Crumb crumb = new Crumb(name, canGoBack, data);
Michael Kolb370a4f32010-10-06 10:45:32 -0700144 pushCrumb(crumb);
Leon Scroggins905250c2010-12-17 15:25:33 -0500145 return crumb.crumbView;
Michael Kolb370a4f32010-10-06 10:45:32 -0700146 }
147
148 public void pushView(View view, Object data) {
149 Crumb crumb = new Crumb(view, true, data);
150 pushCrumb(crumb);
151 }
152
153 public void popView() {
154 pop(true);
155 }
156
157 private void addBackButton() {
158 mBackButton = new ImageButton(mContext);
159 mBackButton.setImageResource(R.drawable.ic_back_normal);
160 mBackButton.setBackgroundResource(R.drawable.browserbarbutton);
161 mBackButton.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
162 LayoutParams.MATCH_PARENT));
163 mBackButton.setOnClickListener(this);
Leon Scroggins905250c2010-12-17 15:25:33 -0500164 mBackButton.setVisibility(View.GONE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700165 addView(mBackButton, 0);
166 }
167
168 private void pushCrumb(Crumb crumb) {
John Reck89f73c12010-12-01 10:10:14 -0800169 if (mCrumbs.size() > 0) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700170 addSeparator();
171 }
172 mCrumbs.add(crumb);
173 addView(crumb.crumbView);
John Reck89f73c12010-12-01 10:10:14 -0800174 updateVisible();
Michael Kolb370a4f32010-10-06 10:45:32 -0700175 crumb.crumbView.setOnClickListener(this);
176 }
177
178 private void addSeparator() {
179 ImageView sep = new ImageView(mContext);
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400180 sep.setImageDrawable(mSeparatorDrawable);
Michael Kolb370a4f32010-10-06 10:45:32 -0700181 sep.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
182 LayoutParams.MATCH_PARENT));
183 addView(sep);
184 }
185
186 private void pop(boolean notify) {
187 int n = mCrumbs.size();
188 if (n > 0) {
189 removeLastView();
190 if (!mUseBackButton || (n > 1)) {
191 // remove separator
192 removeLastView();
193 }
194 mCrumbs.remove(n - 1);
195 if (mUseBackButton) {
196 Crumb top = getTopCrumb();
Leon Scroggins8a2a0e72010-10-15 15:49:40 -0400197 if (top != null && top.canGoBack) {
198 mBackButton.setVisibility(View.VISIBLE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700199 } else {
Leon Scroggins905250c2010-12-17 15:25:33 -0500200 mBackButton.setVisibility(View.GONE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700201 }
202 }
John Reck89f73c12010-12-01 10:10:14 -0800203 updateVisible();
Michael Kolb370a4f32010-10-06 10:45:32 -0700204 if (notify) {
205 notifyController();
206 }
207 }
208 }
209
John Reck89f73c12010-12-01 10:10:14 -0800210 private void updateVisible() {
211 // start at index 1 (0 == back button)
212 int childIndex = 1;
213 if (mMaxVisible >= 0) {
214 int invisibleCrumbs = size() - mMaxVisible;
215 if (invisibleCrumbs > 0) {
216 int crumbIndex = 0;
217 while (crumbIndex < invisibleCrumbs) {
218 // Set the crumb to GONE.
219 getChildAt(childIndex).setVisibility(View.GONE);
220 childIndex++;
221 // Each crumb is followed by a separator (except the last
222 // one). Also make it GONE
223 if (getChildAt(childIndex) != null) {
224 getChildAt(childIndex).setVisibility(View.GONE);
225 }
226 childIndex++;
227 // Move to the next crumb.
228 crumbIndex++;
229 }
230 }
231 // Make sure the last two are visible.
232 int childCount = getChildCount();
233 while (childIndex < childCount) {
234 getChildAt(childIndex).setVisibility(View.VISIBLE);
235 childIndex++;
236 }
237 } else {
238 int count = getChildCount();
239 for (int i = childIndex; i < count ; i++) {
240 getChildAt(i).setVisibility(View.VISIBLE);
241 }
242 }
243 if (mUseBackButton) {
244 boolean canGoBack = getTopCrumb() != null ? getTopCrumb().canGoBack : false;
Leon Scroggins905250c2010-12-17 15:25:33 -0500245 mBackButton.setVisibility(canGoBack ? View.VISIBLE : View.GONE);
John Reck89f73c12010-12-01 10:10:14 -0800246 } else {
247 mBackButton.setVisibility(View.GONE);
248 }
249 }
250
Michael Kolb370a4f32010-10-06 10:45:32 -0700251 private void removeLastView() {
252 int ix = getChildCount();
253 if (ix > 0) {
254 removeViewAt(ix-1);
255 }
256 }
257
John Reckd4893b02010-12-07 17:38:34 -0800258 Crumb getTopCrumb() {
Michael Kolb370a4f32010-10-06 10:45:32 -0700259 Crumb crumb = null;
260 if (mCrumbs.size() > 0) {
261 crumb = mCrumbs.get(mCrumbs.size() - 1);
262 }
263 return crumb;
264 }
265
266 @Override
267 public void onClick(View v) {
268 if (mBackButton == v) {
269 popView();
270 notifyController();
271 } else {
272 // pop until view matches crumb view
273 while (v != getTopCrumb().crumbView) {
274 pop(false);
275 }
276 notifyController();
277 }
278 }
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400279 @Override
280 public int getBaseline() {
281 int ix = getChildCount();
282 if (ix > 0) {
283 // If there is at least one crumb, the baseline will be its
284 // baseline.
285 return getChildAt(ix-1).getBaseline();
286 }
287 return super.getBaseline();
288 }
289
290 @Override
291 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
292 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
293 int height = mSeparatorDrawable.getIntrinsicHeight();
Dianne Hackborn7831da92010-12-03 00:19:01 -0800294 if (getMeasuredHeight() < height) {
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400295 // This should only be an issue if there are currently no separators
296 // showing; i.e. if there is one crumb and no back button.
297 int mode = View.MeasureSpec.getMode(heightMeasureSpec);
298 switch(mode) {
299 case View.MeasureSpec.AT_MOST:
300 if (View.MeasureSpec.getSize(heightMeasureSpec) < height) {
301 return;
302 }
303 break;
304 case View.MeasureSpec.EXACTLY:
305 return;
306 default:
307 break;
308 }
Dianne Hackborn7831da92010-12-03 00:19:01 -0800309 setMeasuredDimension(getMeasuredWidth(), height);
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400310 }
311 }
Michael Kolb370a4f32010-10-06 10:45:32 -0700312
313 class Crumb {
314
315 public View crumbView;
316 public boolean canGoBack;
317 public Object data;
318
319 public Crumb(String title, boolean backEnabled, Object tag) {
320 init(makeCrumbView(title), backEnabled, tag);
321 }
322
323 public Crumb(View view, boolean backEnabled, Object tag) {
324 init(view, backEnabled, tag);
325 }
326
327 private void init(View view, boolean back, Object tag) {
328 canGoBack = back;
329 crumbView = view;
330 data = tag;
331 }
332
333 private TextView makeCrumbView(String name) {
334 TextView tv = new TextView(mContext);
335 tv.setTextAppearance(mContext, android.R.style.TextAppearance_Medium);
336 tv.setPadding(16, 0, 16, 0);
337 tv.setGravity(Gravity.CENTER_VERTICAL);
338 tv.setText(name);
339 tv.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
340 LayoutParams.MATCH_PARENT));
Leon Scroggins74dbe012010-10-15 10:54:27 -0400341 tv.setMaxWidth(mContext.getResources().getInteger(
342 R.integer.max_width_crumb));
343 tv.setMaxLines(1);
344 tv.setEllipsize(TextUtils.TruncateAt.END);
Michael Kolb370a4f32010-10-06 10:45:32 -0700345 return tv;
346 }
347
348 }
349
350}