blob: 4939b48e37518a56b3f5deb1fe101fc0d1a34999 [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;
Michael Kolb370a4f32010-10-06 10:45:32 -070050
51 /**
52 * @param context
53 * @param attrs
54 * @param defStyle
55 */
56 public BreadCrumbView(Context context, AttributeSet attrs, int defStyle) {
57 super(context, attrs, defStyle);
58 init(context);
59 }
60
61 /**
62 * @param context
63 * @param attrs
64 */
65 public BreadCrumbView(Context context, AttributeSet attrs) {
66 super(context, attrs);
67 init(context);
68 }
69
70 /**
71 * @param context
72 */
73 public BreadCrumbView(Context context) {
74 super(context);
75 init(context);
76 }
77
78 private void init(Context ctx) {
79 mUseBackButton = false;
80 mCrumbs = new ArrayList<Crumb>();
Leon Scroggins7e5f7352010-10-18 13:25:31 -040081 mSeparatorDrawable = ctx.getResources().getDrawable(
82 R.drawable.crumb_divider);
Michael Kolb370a4f32010-10-06 10:45:32 -070083 }
84
85 public void setUseBackButton(boolean useflag) {
86 mUseBackButton = useflag;
87 if (mUseBackButton && (mBackButton == null)) {
88 addBackButton();
89 } else if (!mUseBackButton && (mBackButton != null)) {
90 removeView(mBackButton);
91 mBackButton = null;
92 }
93 }
94
95 public void setController(Controller ctl) {
96 mController = ctl;
97 }
98
99 public Object getTopData() {
100 Crumb c = getTopCrumb();
101 if (c != null) {
102 return c.data;
103 }
104 return null;
105 }
106
107 public int size() {
108 return mCrumbs.size();
109 }
110
111 public void clear() {
112 while (mCrumbs.size() > 1) {
113 pop(false);
114 }
115 pop(true);
116 }
117
118 public void notifyController() {
119 if (mController != null) {
120 if (mCrumbs.size() > 0) {
121 mController.onTop(mCrumbs.size(), getTopCrumb().data);
122 } else {
123 mController.onTop(0, null);
124 }
125 }
126 }
127
128 public void pushView(String name, Object data) {
Leon Scroggins74dbe012010-10-15 10:54:27 -0400129 pushView(name, true, data);
130 }
131
132 public void pushView(String name, boolean canGoBack, Object data) {
133 Crumb crumb = new Crumb(name, canGoBack, data);
Michael Kolb370a4f32010-10-06 10:45:32 -0700134 pushCrumb(crumb);
135 }
136
137 public void pushView(View view, Object data) {
138 Crumb crumb = new Crumb(view, true, data);
139 pushCrumb(crumb);
140 }
141
142 public void popView() {
143 pop(true);
144 }
145
146 private void addBackButton() {
147 mBackButton = new ImageButton(mContext);
148 mBackButton.setImageResource(R.drawable.ic_back_normal);
149 mBackButton.setBackgroundResource(R.drawable.browserbarbutton);
150 mBackButton.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
151 LayoutParams.MATCH_PARENT));
152 mBackButton.setOnClickListener(this);
Leon Scroggins8a2a0e72010-10-15 15:49:40 -0400153 mBackButton.setVisibility(View.INVISIBLE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700154 addView(mBackButton, 0);
155 }
156
157 private void pushCrumb(Crumb crumb) {
158 if (!mUseBackButton || (mCrumbs.size() > 0)) {
159 addSeparator();
160 }
161 mCrumbs.add(crumb);
162 addView(crumb.crumbView);
163 if (mUseBackButton) {
Leon Scroggins8a2a0e72010-10-15 15:49:40 -0400164 mBackButton.setVisibility(crumb.canGoBack ? View.VISIBLE : View.INVISIBLE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700165 }
166 crumb.crumbView.setOnClickListener(this);
167 }
168
169 private void addSeparator() {
170 ImageView sep = new ImageView(mContext);
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400171 sep.setImageDrawable(mSeparatorDrawable);
Michael Kolb370a4f32010-10-06 10:45:32 -0700172 sep.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
173 LayoutParams.MATCH_PARENT));
174 addView(sep);
175 }
176
177 private void pop(boolean notify) {
178 int n = mCrumbs.size();
179 if (n > 0) {
180 removeLastView();
181 if (!mUseBackButton || (n > 1)) {
182 // remove separator
183 removeLastView();
184 }
185 mCrumbs.remove(n - 1);
186 if (mUseBackButton) {
187 Crumb top = getTopCrumb();
Leon Scroggins8a2a0e72010-10-15 15:49:40 -0400188 if (top != null && top.canGoBack) {
189 mBackButton.setVisibility(View.VISIBLE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700190 } else {
Leon Scroggins8a2a0e72010-10-15 15:49:40 -0400191 mBackButton.setVisibility(View.INVISIBLE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700192 }
193 }
194 if (notify) {
195 notifyController();
196 }
197 }
198 }
199
200 private void removeLastView() {
201 int ix = getChildCount();
202 if (ix > 0) {
203 removeViewAt(ix-1);
204 }
205 }
206
207 private Crumb getTopCrumb() {
208 Crumb crumb = null;
209 if (mCrumbs.size() > 0) {
210 crumb = mCrumbs.get(mCrumbs.size() - 1);
211 }
212 return crumb;
213 }
214
215 @Override
216 public void onClick(View v) {
217 if (mBackButton == v) {
218 popView();
219 notifyController();
220 } else {
221 // pop until view matches crumb view
222 while (v != getTopCrumb().crumbView) {
223 pop(false);
224 }
225 notifyController();
226 }
227 }
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400228 @Override
229 public int getBaseline() {
230 int ix = getChildCount();
231 if (ix > 0) {
232 // If there is at least one crumb, the baseline will be its
233 // baseline.
234 return getChildAt(ix-1).getBaseline();
235 }
236 return super.getBaseline();
237 }
238
239 @Override
240 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
241 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
242 int height = mSeparatorDrawable.getIntrinsicHeight();
243 if (mMeasuredHeight < height) {
244 // This should only be an issue if there are currently no separators
245 // showing; i.e. if there is one crumb and no back button.
246 int mode = View.MeasureSpec.getMode(heightMeasureSpec);
247 switch(mode) {
248 case View.MeasureSpec.AT_MOST:
249 if (View.MeasureSpec.getSize(heightMeasureSpec) < height) {
250 return;
251 }
252 break;
253 case View.MeasureSpec.EXACTLY:
254 return;
255 default:
256 break;
257 }
258 setMeasuredDimension(mMeasuredWidth, height);
259 }
260 }
Michael Kolb370a4f32010-10-06 10:45:32 -0700261
262 class Crumb {
263
264 public View crumbView;
265 public boolean canGoBack;
266 public Object data;
267
268 public Crumb(String title, boolean backEnabled, Object tag) {
269 init(makeCrumbView(title), backEnabled, tag);
270 }
271
272 public Crumb(View view, boolean backEnabled, Object tag) {
273 init(view, backEnabled, tag);
274 }
275
276 private void init(View view, boolean back, Object tag) {
277 canGoBack = back;
278 crumbView = view;
279 data = tag;
280 }
281
282 private TextView makeCrumbView(String name) {
283 TextView tv = new TextView(mContext);
284 tv.setTextAppearance(mContext, android.R.style.TextAppearance_Medium);
285 tv.setPadding(16, 0, 16, 0);
286 tv.setGravity(Gravity.CENTER_VERTICAL);
287 tv.setText(name);
288 tv.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
289 LayoutParams.MATCH_PARENT));
Leon Scroggins74dbe012010-10-15 10:54:27 -0400290 tv.setMaxWidth(mContext.getResources().getInteger(
291 R.integer.max_width_crumb));
292 tv.setMaxLines(1);
293 tv.setEllipsize(TextUtils.TruncateAt.END);
Michael Kolb370a4f32010-10-06 10:45:32 -0700294 return tv;
295 }
296
297 }
298
299}