blob: 0cd822636891dd7bae36bd726f6bf086b6e52964 [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
Bijan Amirzada41242f22014-03-21 12:12:18 -070017package com.android.browser;
Michael Kolb370a4f32010-10-06 10:45:32 -070018
19import android.content.Context;
John Reckb3417f02011-01-14 11:01:05 -080020import android.content.res.TypedArray;
Leon Scroggins7e5f7352010-10-18 13:25:31 -040021import android.graphics.drawable.Drawable;
Leon Scroggins74dbe012010-10-15 10:54:27 -040022import android.text.TextUtils;
Michael Kolb370a4f32010-10-06 10:45:32 -070023import android.util.AttributeSet;
Michael Kolb5a72f182011-01-13 20:35:06 -080024import android.util.TypedValue;
Michael Kolb370a4f32010-10-06 10:45:32 -070025import android.view.Gravity;
26import android.view.View;
27import android.view.View.OnClickListener;
28import android.widget.ImageButton;
29import android.widget.ImageView;
30import android.widget.LinearLayout;
kaiyiz3d7e4d52013-09-17 17:56:27 +080031import android.widget.RelativeLayout;
Michael Kolb370a4f32010-10-06 10:45:32 -070032import android.widget.TextView;
33
34import java.util.ArrayList;
35import java.util.List;
36
Bijan Amirzada41242f22014-03-21 12:12:18 -070037import com.android.browser.R;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080038
Michael Kolb370a4f32010-10-06 10:45:32 -070039/**
40 * Simple bread crumb view
41 * Use setController to receive callbacks from user interactions
42 * Use pushView, popView, clear, and getTopData to change/access the view stack
43 */
kaiyiz3d7e4d52013-09-17 17:56:27 +080044public class BreadCrumbView extends RelativeLayout implements OnClickListener {
John Reckb3417f02011-01-14 11:01:05 -080045 private static final int DIVIDER_PADDING = 12; // dips
John Reck95f88e42011-09-26 09:25:43 -070046 private static final int CRUMB_PADDING = 8; // dips
Michael Kolb370a4f32010-10-06 10:45:32 -070047
John Reck71efc2b2011-05-09 16:54:28 -070048 public interface Controller {
49 public void onTop(BreadCrumbView view, int level, Object data);
Michael Kolb370a4f32010-10-06 10:45:32 -070050 }
51
52 private ImageButton mBackButton;
kaiyiz3d7e4d52013-09-17 17:56:27 +080053 private LinearLayout mCrumbLayout;
54 private LinearLayout mBackLayout;
Michael Kolb370a4f32010-10-06 10:45:32 -070055 private Controller mController;
56 private List<Crumb> mCrumbs;
57 private boolean mUseBackButton;
Leon Scroggins7e5f7352010-10-18 13:25:31 -040058 private Drawable mSeparatorDrawable;
John Reckb3417f02011-01-14 11:01:05 -080059 private float mDividerPadding;
John Reck89f73c12010-12-01 10:10:14 -080060 private int mMaxVisible = -1;
John Reck71efc2b2011-05-09 16:54:28 -070061 private Context mContext;
John Reck95f88e42011-09-26 09:25:43 -070062 private int mCrumbPadding;
Michael Kolb370a4f32010-10-06 10:45:32 -070063
64 /**
65 * @param context
66 * @param attrs
67 * @param defStyle
68 */
69 public BreadCrumbView(Context context, AttributeSet attrs, int defStyle) {
70 super(context, attrs, defStyle);
71 init(context);
72 }
73
74 /**
75 * @param context
76 * @param attrs
77 */
78 public BreadCrumbView(Context context, AttributeSet attrs) {
79 super(context, attrs);
80 init(context);
81 }
82
83 /**
84 * @param context
85 */
86 public BreadCrumbView(Context context) {
87 super(context);
88 init(context);
89 }
90
91 private void init(Context ctx) {
John Reck71efc2b2011-05-09 16:54:28 -070092 mContext = ctx;
93 setFocusable(true);
kaiyiz3d7e4d52013-09-17 17:56:27 +080094 setGravity(Gravity.CENTER_VERTICAL);
Michael Kolb370a4f32010-10-06 10:45:32 -070095 mUseBackButton = false;
96 mCrumbs = new ArrayList<Crumb>();
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080097 mSeparatorDrawable = ctx.getResources().getDrawable(
98 android.R.drawable.divider_horizontal_dark);
John Reck95f88e42011-09-26 09:25:43 -070099 float density = mContext.getResources().getDisplayMetrics().density;
100 mDividerPadding = DIVIDER_PADDING * density;
101 mCrumbPadding = (int) (CRUMB_PADDING * density);
kaiyiz3d7e4d52013-09-17 17:56:27 +0800102 addCrumbLayout();
103 addBackLayout();
Michael Kolb370a4f32010-10-06 10:45:32 -0700104 }
105
106 public void setUseBackButton(boolean useflag) {
107 mUseBackButton = useflag;
John Reck89f73c12010-12-01 10:10:14 -0800108 updateVisible();
Michael Kolb370a4f32010-10-06 10:45:32 -0700109 }
110
111 public void setController(Controller ctl) {
112 mController = ctl;
113 }
114
John Reck89f73c12010-12-01 10:10:14 -0800115 public int getMaxVisible() {
116 return mMaxVisible;
117 }
118
119 public void setMaxVisible(int max) {
120 mMaxVisible = max;
121 updateVisible();
122 }
123
124 public int getTopLevel() {
125 return mCrumbs.size();
126 }
127
Michael Kolb370a4f32010-10-06 10:45:32 -0700128 public Object getTopData() {
129 Crumb c = getTopCrumb();
130 if (c != null) {
131 return c.data;
132 }
133 return null;
134 }
135
136 public int size() {
137 return mCrumbs.size();
138 }
139
140 public void clear() {
141 while (mCrumbs.size() > 1) {
142 pop(false);
143 }
144 pop(true);
145 }
146
147 public void notifyController() {
148 if (mController != null) {
149 if (mCrumbs.size() > 0) {
John Reck71efc2b2011-05-09 16:54:28 -0700150 mController.onTop(this, mCrumbs.size(), getTopCrumb().data);
Michael Kolb370a4f32010-10-06 10:45:32 -0700151 } else {
John Reck71efc2b2011-05-09 16:54:28 -0700152 mController.onTop(this, 0, null);
Michael Kolb370a4f32010-10-06 10:45:32 -0700153 }
154 }
155 }
156
Leon Scroggins905250c2010-12-17 15:25:33 -0500157 public View pushView(String name, Object data) {
158 return pushView(name, true, data);
Leon Scroggins74dbe012010-10-15 10:54:27 -0400159 }
160
Leon Scroggins905250c2010-12-17 15:25:33 -0500161 public View pushView(String name, boolean canGoBack, Object data) {
Leon Scroggins74dbe012010-10-15 10:54:27 -0400162 Crumb crumb = new Crumb(name, canGoBack, data);
Michael Kolb370a4f32010-10-06 10:45:32 -0700163 pushCrumb(crumb);
Leon Scroggins905250c2010-12-17 15:25:33 -0500164 return crumb.crumbView;
Michael Kolb370a4f32010-10-06 10:45:32 -0700165 }
166
167 public void pushView(View view, Object data) {
168 Crumb crumb = new Crumb(view, true, data);
169 pushCrumb(crumb);
170 }
171
172 public void popView() {
173 pop(true);
174 }
175
176 private void addBackButton() {
177 mBackButton = new ImageButton(mContext);
kaiyiz3d7e4d52013-09-17 17:56:27 +0800178 mBackButton.setImageResource(R.drawable.icon_up);
Michael Kolb5a72f182011-01-13 20:35:06 -0800179 TypedValue outValue = new TypedValue();
180 getContext().getTheme().resolveAttribute(
181 android.R.attr.selectableItemBackground, outValue, true);
182 int resid = outValue.resourceId;
183 mBackButton.setBackgroundResource(resid);
kaiyiz3d7e4d52013-09-17 17:56:27 +0800184 mBackButton.setPadding(mCrumbPadding, 0, mCrumbPadding, 0);
Michael Kolb370a4f32010-10-06 10:45:32 -0700185 mBackButton.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
186 LayoutParams.MATCH_PARENT));
187 mBackButton.setOnClickListener(this);
John Reckaac2ce02012-09-21 14:17:47 -0700188 mBackButton.setContentDescription(mContext.getText(
189 R.string.accessibility_button_bookmarks_folder_up));
kaiyiz3d7e4d52013-09-17 17:56:27 +0800190 mBackLayout.addView(mBackButton);
191 }
192
193 private void addParentLabel() {
194 TextView tv = new TextView(mContext);
195 tv.setTextAppearance(mContext, android.R.style.TextAppearance_Medium);
196 tv.setPadding(mCrumbPadding, 0, 0, 0);
197 tv.setGravity(Gravity.CENTER_VERTICAL);
198 tv.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
199 LayoutParams.WRAP_CONTENT));
200 tv.setText("/ .../");
201 tv.setSingleLine();
202 tv.setVisibility(View.GONE);
203 mCrumbLayout.addView(tv);
204 }
205
206 private void addCrumbLayout() {
207 mCrumbLayout = new LinearLayout(mContext);
208 LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
209 LayoutParams.WRAP_CONTENT);
210 params.addRule(ALIGN_PARENT_LEFT, TRUE);
211 params.setMargins(0, 0, 4 * mCrumbPadding, 0);
212 mCrumbLayout.setLayoutParams(params);
213 mCrumbLayout.setVisibility(View.VISIBLE);
214 addParentLabel();
215 addView(mCrumbLayout);
216 }
217
218 private void addBackLayout() {
219 mBackLayout= new LinearLayout(mContext);
220 LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
221 LayoutParams.WRAP_CONTENT);
222 params.addRule(ALIGN_PARENT_RIGHT, TRUE);
223 mBackLayout.setLayoutParams(params);
224 mBackLayout.setVisibility(View.GONE);
225 addSeparator();
226 addBackButton();
227 addView(mBackLayout);
Michael Kolb370a4f32010-10-06 10:45:32 -0700228 }
229
230 private void pushCrumb(Crumb crumb) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700231 mCrumbs.add(crumb);
kaiyiz3d7e4d52013-09-17 17:56:27 +0800232 mCrumbLayout.addView(crumb.crumbView);
John Reck89f73c12010-12-01 10:10:14 -0800233 updateVisible();
Michael Kolb370a4f32010-10-06 10:45:32 -0700234 crumb.crumbView.setOnClickListener(this);
235 }
236
237 private void addSeparator() {
John Reckb3417f02011-01-14 11:01:05 -0800238 View sep = makeDividerView();
239 sep.setLayoutParams(makeDividerLayoutParams());
kaiyiz3d7e4d52013-09-17 17:56:27 +0800240 mBackLayout.addView(sep);
Michael Kolb370a4f32010-10-06 10:45:32 -0700241 }
242
John Reckb3417f02011-01-14 11:01:05 -0800243 private ImageView makeDividerView() {
244 ImageView result = new ImageView(mContext);
245 result.setImageDrawable(mSeparatorDrawable);
246 result.setScaleType(ImageView.ScaleType.FIT_XY);
247 return result;
248 }
249
kaiyiz3d7e4d52013-09-17 17:56:27 +0800250 private LinearLayout.LayoutParams makeDividerLayoutParams() {
251 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
252 LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
John Reckb3417f02011-01-14 11:01:05 -0800253 return params;
254 }
255
Michael Kolb370a4f32010-10-06 10:45:32 -0700256 private void pop(boolean notify) {
257 int n = mCrumbs.size();
258 if (n > 0) {
259 removeLastView();
Michael Kolb370a4f32010-10-06 10:45:32 -0700260 mCrumbs.remove(n - 1);
261 if (mUseBackButton) {
262 Crumb top = getTopCrumb();
Leon Scroggins8a2a0e72010-10-15 15:49:40 -0400263 if (top != null && top.canGoBack) {
kaiyiz3d7e4d52013-09-17 17:56:27 +0800264 mBackLayout.setVisibility(View.VISIBLE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700265 } else {
kaiyiz3d7e4d52013-09-17 17:56:27 +0800266 mBackLayout.setVisibility(View.GONE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700267 }
268 }
John Reck89f73c12010-12-01 10:10:14 -0800269 updateVisible();
Michael Kolb370a4f32010-10-06 10:45:32 -0700270 if (notify) {
271 notifyController();
272 }
273 }
274 }
275
John Reck89f73c12010-12-01 10:10:14 -0800276 private void updateVisible() {
kaiyiz3d7e4d52013-09-17 17:56:27 +0800277 // start at index 1 (0 == parent label)
John Reck89f73c12010-12-01 10:10:14 -0800278 int childIndex = 1;
279 if (mMaxVisible >= 0) {
280 int invisibleCrumbs = size() - mMaxVisible;
281 if (invisibleCrumbs > 0) {
282 int crumbIndex = 0;
283 while (crumbIndex < invisibleCrumbs) {
284 // Set the crumb to GONE.
kaiyiz3d7e4d52013-09-17 17:56:27 +0800285 mCrumbLayout.getChildAt(childIndex).setVisibility(View.GONE);
John Reck89f73c12010-12-01 10:10:14 -0800286 childIndex++;
287 // Move to the next crumb.
288 crumbIndex++;
289 }
290 }
kaiyiz3d7e4d52013-09-17 17:56:27 +0800291 // Make sure the last is visible.
292 int childCount = mCrumbLayout.getChildCount();
John Reck89f73c12010-12-01 10:10:14 -0800293 while (childIndex < childCount) {
kaiyiz3d7e4d52013-09-17 17:56:27 +0800294 mCrumbLayout.getChildAt(childIndex).setVisibility(View.VISIBLE);
John Reck89f73c12010-12-01 10:10:14 -0800295 childIndex++;
296 }
297 } else {
298 int count = getChildCount();
299 for (int i = childIndex; i < count ; i++) {
300 getChildAt(i).setVisibility(View.VISIBLE);
301 }
302 }
303 if (mUseBackButton) {
304 boolean canGoBack = getTopCrumb() != null ? getTopCrumb().canGoBack : false;
kaiyiz3d7e4d52013-09-17 17:56:27 +0800305 mBackLayout.setVisibility(canGoBack ? View.VISIBLE : View.GONE);
306 if (canGoBack) {
307 mCrumbLayout.getChildAt(0).setVisibility(VISIBLE);
308 } else {
309 mCrumbLayout.getChildAt(0).setVisibility(GONE);
310 }
John Reck89f73c12010-12-01 10:10:14 -0800311 } else {
kaiyiz3d7e4d52013-09-17 17:56:27 +0800312 mBackLayout.setVisibility(View.GONE);
John Reck89f73c12010-12-01 10:10:14 -0800313 }
314 }
315
Michael Kolb370a4f32010-10-06 10:45:32 -0700316 private void removeLastView() {
kaiyiz3d7e4d52013-09-17 17:56:27 +0800317 int ix = mCrumbLayout.getChildCount();
Michael Kolb370a4f32010-10-06 10:45:32 -0700318 if (ix > 0) {
kaiyiz3d7e4d52013-09-17 17:56:27 +0800319 mCrumbLayout.removeViewAt(ix-1);
Michael Kolb370a4f32010-10-06 10:45:32 -0700320 }
321 }
322
John Reckd4893b02010-12-07 17:38:34 -0800323 Crumb getTopCrumb() {
Michael Kolb370a4f32010-10-06 10:45:32 -0700324 Crumb crumb = null;
325 if (mCrumbs.size() > 0) {
326 crumb = mCrumbs.get(mCrumbs.size() - 1);
327 }
328 return crumb;
329 }
330
331 @Override
332 public void onClick(View v) {
333 if (mBackButton == v) {
334 popView();
335 notifyController();
336 } else {
337 // pop until view matches crumb view
338 while (v != getTopCrumb().crumbView) {
339 pop(false);
340 }
341 notifyController();
342 }
343 }
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400344 @Override
345 public int getBaseline() {
346 int ix = getChildCount();
347 if (ix > 0) {
348 // If there is at least one crumb, the baseline will be its
349 // baseline.
350 return getChildAt(ix-1).getBaseline();
351 }
352 return super.getBaseline();
353 }
354
355 @Override
356 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
357 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
358 int height = mSeparatorDrawable.getIntrinsicHeight();
Dianne Hackborn7831da92010-12-03 00:19:01 -0800359 if (getMeasuredHeight() < height) {
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400360 // This should only be an issue if there are currently no separators
361 // showing; i.e. if there is one crumb and no back button.
362 int mode = View.MeasureSpec.getMode(heightMeasureSpec);
363 switch(mode) {
364 case View.MeasureSpec.AT_MOST:
365 if (View.MeasureSpec.getSize(heightMeasureSpec) < height) {
366 return;
367 }
368 break;
369 case View.MeasureSpec.EXACTLY:
370 return;
371 default:
372 break;
373 }
Dianne Hackborn7831da92010-12-03 00:19:01 -0800374 setMeasuredDimension(getMeasuredWidth(), height);
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400375 }
376 }
Michael Kolb370a4f32010-10-06 10:45:32 -0700377
378 class Crumb {
379
380 public View crumbView;
381 public boolean canGoBack;
382 public Object data;
383
384 public Crumb(String title, boolean backEnabled, Object tag) {
385 init(makeCrumbView(title), backEnabled, tag);
386 }
387
388 public Crumb(View view, boolean backEnabled, Object tag) {
389 init(view, backEnabled, tag);
390 }
391
392 private void init(View view, boolean back, Object tag) {
393 canGoBack = back;
394 crumbView = view;
395 data = tag;
396 }
397
398 private TextView makeCrumbView(String name) {
399 TextView tv = new TextView(mContext);
Kulanthaivel Palanichamy9e4211c2015-02-09 11:27:14 -0800400 tv.setTextAppearance(mContext, R.style.BookmarkText);
John Reck95f88e42011-09-26 09:25:43 -0700401 tv.setPadding(mCrumbPadding, 0, mCrumbPadding, 0);
Michael Kolb370a4f32010-10-06 10:45:32 -0700402 tv.setGravity(Gravity.CENTER_VERTICAL);
403 tv.setText(name);
404 tv.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
405 LayoutParams.MATCH_PARENT));
John Reck95f88e42011-09-26 09:25:43 -0700406 tv.setSingleLine();
Leon Scroggins74dbe012010-10-15 10:54:27 -0400407 tv.setEllipsize(TextUtils.TruncateAt.END);
Michael Kolb370a4f32010-10-06 10:45:32 -0700408 return tv;
409 }
410
411 }
412
413}