blob: 1501d211d191e9cf9b46d3636b979a14ff08640e [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;
Pankaj Garg634bf432015-07-13 09:54:21 -070063 private TextView mOverflowView;
Michael Kolb370a4f32010-10-06 10:45:32 -070064
65 /**
66 * @param context
67 * @param attrs
68 * @param defStyle
69 */
70 public BreadCrumbView(Context context, AttributeSet attrs, int defStyle) {
71 super(context, attrs, defStyle);
72 init(context);
73 }
74
75 /**
76 * @param context
77 * @param attrs
78 */
79 public BreadCrumbView(Context context, AttributeSet attrs) {
80 super(context, attrs);
81 init(context);
82 }
83
84 /**
85 * @param context
86 */
87 public BreadCrumbView(Context context) {
88 super(context);
89 init(context);
90 }
91
92 private void init(Context ctx) {
John Reck71efc2b2011-05-09 16:54:28 -070093 mContext = ctx;
94 setFocusable(true);
kaiyiz3d7e4d52013-09-17 17:56:27 +080095 setGravity(Gravity.CENTER_VERTICAL);
Michael Kolb370a4f32010-10-06 10:45:32 -070096 mUseBackButton = false;
97 mCrumbs = new ArrayList<Crumb>();
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080098 mSeparatorDrawable = ctx.getResources().getDrawable(
99 android.R.drawable.divider_horizontal_dark);
John Reck95f88e42011-09-26 09:25:43 -0700100 float density = mContext.getResources().getDisplayMetrics().density;
101 mDividerPadding = DIVIDER_PADDING * density;
102 mCrumbPadding = (int) (CRUMB_PADDING * density);
kaiyiz3d7e4d52013-09-17 17:56:27 +0800103 addCrumbLayout();
104 addBackLayout();
Michael Kolb370a4f32010-10-06 10:45:32 -0700105 }
106
107 public void setUseBackButton(boolean useflag) {
108 mUseBackButton = useflag;
John Reck89f73c12010-12-01 10:10:14 -0800109 updateVisible();
Michael Kolb370a4f32010-10-06 10:45:32 -0700110 }
111
112 public void setController(Controller ctl) {
113 mController = ctl;
114 }
115
John Reck89f73c12010-12-01 10:10:14 -0800116 public int getMaxVisible() {
117 return mMaxVisible;
118 }
119
120 public void setMaxVisible(int max) {
121 mMaxVisible = max;
122 updateVisible();
123 }
124
125 public int getTopLevel() {
126 return mCrumbs.size();
127 }
128
Michael Kolb370a4f32010-10-06 10:45:32 -0700129 public Object getTopData() {
130 Crumb c = getTopCrumb();
131 if (c != null) {
132 return c.data;
133 }
134 return null;
135 }
136
137 public int size() {
138 return mCrumbs.size();
139 }
140
141 public void clear() {
142 while (mCrumbs.size() > 1) {
143 pop(false);
144 }
145 pop(true);
146 }
147
148 public void notifyController() {
149 if (mController != null) {
150 if (mCrumbs.size() > 0) {
John Reck71efc2b2011-05-09 16:54:28 -0700151 mController.onTop(this, mCrumbs.size(), getTopCrumb().data);
Michael Kolb370a4f32010-10-06 10:45:32 -0700152 } else {
John Reck71efc2b2011-05-09 16:54:28 -0700153 mController.onTop(this, 0, null);
Michael Kolb370a4f32010-10-06 10:45:32 -0700154 }
155 }
156 }
157
Leon Scroggins905250c2010-12-17 15:25:33 -0500158 public View pushView(String name, Object data) {
159 return pushView(name, true, data);
Leon Scroggins74dbe012010-10-15 10:54:27 -0400160 }
161
Leon Scroggins905250c2010-12-17 15:25:33 -0500162 public View pushView(String name, boolean canGoBack, Object data) {
Leon Scroggins74dbe012010-10-15 10:54:27 -0400163 Crumb crumb = new Crumb(name, canGoBack, data);
Michael Kolb370a4f32010-10-06 10:45:32 -0700164 pushCrumb(crumb);
Leon Scroggins905250c2010-12-17 15:25:33 -0500165 return crumb.crumbView;
Michael Kolb370a4f32010-10-06 10:45:32 -0700166 }
167
Pankaj Garg634bf432015-07-13 09:54:21 -0700168 public void addOverflowLabel(TextView view) {
169 mOverflowView = view;
170 if (view != null) {
171 view.setTextAppearance(mContext, R.style.BookmarkPathText);
172 view.setPadding(mCrumbPadding, 0, mCrumbPadding, 0);
173 view.setGravity(Gravity.CENTER_VERTICAL);
174 view.setText("... >");
175 }
176 }
177
Michael Kolb370a4f32010-10-06 10:45:32 -0700178 public void pushView(View view, Object data) {
179 Crumb crumb = new Crumb(view, true, data);
180 pushCrumb(crumb);
181 }
182
183 public void popView() {
184 pop(true);
185 }
186
187 private void addBackButton() {
188 mBackButton = new ImageButton(mContext);
kaiyiz3d7e4d52013-09-17 17:56:27 +0800189 mBackButton.setImageResource(R.drawable.icon_up);
Michael Kolb5a72f182011-01-13 20:35:06 -0800190 TypedValue outValue = new TypedValue();
191 getContext().getTheme().resolveAttribute(
192 android.R.attr.selectableItemBackground, outValue, true);
193 int resid = outValue.resourceId;
194 mBackButton.setBackgroundResource(resid);
kaiyiz3d7e4d52013-09-17 17:56:27 +0800195 mBackButton.setPadding(mCrumbPadding, 0, mCrumbPadding, 0);
Michael Kolb370a4f32010-10-06 10:45:32 -0700196 mBackButton.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
197 LayoutParams.MATCH_PARENT));
198 mBackButton.setOnClickListener(this);
John Reckaac2ce02012-09-21 14:17:47 -0700199 mBackButton.setContentDescription(mContext.getText(
200 R.string.accessibility_button_bookmarks_folder_up));
kaiyiz3d7e4d52013-09-17 17:56:27 +0800201 mBackLayout.addView(mBackButton);
202 }
203
204 private void addParentLabel() {
205 TextView tv = new TextView(mContext);
206 tv.setTextAppearance(mContext, android.R.style.TextAppearance_Medium);
207 tv.setPadding(mCrumbPadding, 0, 0, 0);
208 tv.setGravity(Gravity.CENTER_VERTICAL);
209 tv.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
210 LayoutParams.WRAP_CONTENT));
211 tv.setText("/ .../");
212 tv.setSingleLine();
213 tv.setVisibility(View.GONE);
214 mCrumbLayout.addView(tv);
215 }
216
217 private void addCrumbLayout() {
218 mCrumbLayout = new LinearLayout(mContext);
219 LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
220 LayoutParams.WRAP_CONTENT);
221 params.addRule(ALIGN_PARENT_LEFT, TRUE);
222 params.setMargins(0, 0, 4 * mCrumbPadding, 0);
223 mCrumbLayout.setLayoutParams(params);
224 mCrumbLayout.setVisibility(View.VISIBLE);
Pankaj Garg634bf432015-07-13 09:54:21 -0700225 //addParentLabel();
kaiyiz3d7e4d52013-09-17 17:56:27 +0800226 addView(mCrumbLayout);
227 }
228
229 private void addBackLayout() {
230 mBackLayout= new LinearLayout(mContext);
231 LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
232 LayoutParams.WRAP_CONTENT);
233 params.addRule(ALIGN_PARENT_RIGHT, TRUE);
234 mBackLayout.setLayoutParams(params);
235 mBackLayout.setVisibility(View.GONE);
236 addSeparator();
237 addBackButton();
238 addView(mBackLayout);
Michael Kolb370a4f32010-10-06 10:45:32 -0700239 }
240
241 private void pushCrumb(Crumb crumb) {
Michael Kolb370a4f32010-10-06 10:45:32 -0700242 mCrumbs.add(crumb);
kaiyiz3d7e4d52013-09-17 17:56:27 +0800243 mCrumbLayout.addView(crumb.crumbView);
John Reck89f73c12010-12-01 10:10:14 -0800244 updateVisible();
Michael Kolb370a4f32010-10-06 10:45:32 -0700245 crumb.crumbView.setOnClickListener(this);
246 }
247
248 private void addSeparator() {
John Reckb3417f02011-01-14 11:01:05 -0800249 View sep = makeDividerView();
250 sep.setLayoutParams(makeDividerLayoutParams());
kaiyiz3d7e4d52013-09-17 17:56:27 +0800251 mBackLayout.addView(sep);
Michael Kolb370a4f32010-10-06 10:45:32 -0700252 }
253
John Reckb3417f02011-01-14 11:01:05 -0800254 private ImageView makeDividerView() {
255 ImageView result = new ImageView(mContext);
256 result.setImageDrawable(mSeparatorDrawable);
257 result.setScaleType(ImageView.ScaleType.FIT_XY);
258 return result;
259 }
260
kaiyiz3d7e4d52013-09-17 17:56:27 +0800261 private LinearLayout.LayoutParams makeDividerLayoutParams() {
262 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
263 LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
John Reckb3417f02011-01-14 11:01:05 -0800264 return params;
265 }
266
Michael Kolb370a4f32010-10-06 10:45:32 -0700267 private void pop(boolean notify) {
268 int n = mCrumbs.size();
269 if (n > 0) {
270 removeLastView();
Michael Kolb370a4f32010-10-06 10:45:32 -0700271 mCrumbs.remove(n - 1);
272 if (mUseBackButton) {
273 Crumb top = getTopCrumb();
Leon Scroggins8a2a0e72010-10-15 15:49:40 -0400274 if (top != null && top.canGoBack) {
kaiyiz3d7e4d52013-09-17 17:56:27 +0800275 mBackLayout.setVisibility(View.VISIBLE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700276 } else {
kaiyiz3d7e4d52013-09-17 17:56:27 +0800277 mBackLayout.setVisibility(View.GONE);
Michael Kolb370a4f32010-10-06 10:45:32 -0700278 }
279 }
John Reck89f73c12010-12-01 10:10:14 -0800280 updateVisible();
Michael Kolb370a4f32010-10-06 10:45:32 -0700281 if (notify) {
282 notifyController();
283 }
284 }
285 }
286
John Reck89f73c12010-12-01 10:10:14 -0800287 private void updateVisible() {
kaiyiz3d7e4d52013-09-17 17:56:27 +0800288 // start at index 1 (0 == parent label)
Pankaj Garg634bf432015-07-13 09:54:21 -0700289 int childIndex = 0;
John Reck89f73c12010-12-01 10:10:14 -0800290 if (mMaxVisible >= 0) {
291 int invisibleCrumbs = size() - mMaxVisible;
292 if (invisibleCrumbs > 0) {
293 int crumbIndex = 0;
Pankaj Garg634bf432015-07-13 09:54:21 -0700294 if (mOverflowView != null) {
295 mOverflowView.setVisibility(VISIBLE);
296 mOverflowView.setOnClickListener(this);
297 }
John Reck89f73c12010-12-01 10:10:14 -0800298 while (crumbIndex < invisibleCrumbs) {
299 // Set the crumb to GONE.
kaiyiz3d7e4d52013-09-17 17:56:27 +0800300 mCrumbLayout.getChildAt(childIndex).setVisibility(View.GONE);
John Reck89f73c12010-12-01 10:10:14 -0800301 childIndex++;
302 // Move to the next crumb.
303 crumbIndex++;
304 }
Pankaj Garg634bf432015-07-13 09:54:21 -0700305 } else {
306 if (mOverflowView != null) {
307 mOverflowView.setVisibility(GONE);
308 }
John Reck89f73c12010-12-01 10:10:14 -0800309 }
kaiyiz3d7e4d52013-09-17 17:56:27 +0800310 // Make sure the last is visible.
311 int childCount = mCrumbLayout.getChildCount();
John Reck89f73c12010-12-01 10:10:14 -0800312 while (childIndex < childCount) {
kaiyiz3d7e4d52013-09-17 17:56:27 +0800313 mCrumbLayout.getChildAt(childIndex).setVisibility(View.VISIBLE);
John Reck89f73c12010-12-01 10:10:14 -0800314 childIndex++;
315 }
316 } else {
317 int count = getChildCount();
318 for (int i = childIndex; i < count ; i++) {
319 getChildAt(i).setVisibility(View.VISIBLE);
320 }
321 }
322 if (mUseBackButton) {
323 boolean canGoBack = getTopCrumb() != null ? getTopCrumb().canGoBack : false;
kaiyiz3d7e4d52013-09-17 17:56:27 +0800324 mBackLayout.setVisibility(canGoBack ? View.VISIBLE : View.GONE);
325 if (canGoBack) {
326 mCrumbLayout.getChildAt(0).setVisibility(VISIBLE);
327 } else {
328 mCrumbLayout.getChildAt(0).setVisibility(GONE);
329 }
John Reck89f73c12010-12-01 10:10:14 -0800330 } else {
kaiyiz3d7e4d52013-09-17 17:56:27 +0800331 mBackLayout.setVisibility(View.GONE);
John Reck89f73c12010-12-01 10:10:14 -0800332 }
333 }
334
Michael Kolb370a4f32010-10-06 10:45:32 -0700335 private void removeLastView() {
kaiyiz3d7e4d52013-09-17 17:56:27 +0800336 int ix = mCrumbLayout.getChildCount();
Michael Kolb370a4f32010-10-06 10:45:32 -0700337 if (ix > 0) {
kaiyiz3d7e4d52013-09-17 17:56:27 +0800338 mCrumbLayout.removeViewAt(ix-1);
Michael Kolb370a4f32010-10-06 10:45:32 -0700339 }
340 }
341
John Reckd4893b02010-12-07 17:38:34 -0800342 Crumb getTopCrumb() {
Michael Kolb370a4f32010-10-06 10:45:32 -0700343 Crumb crumb = null;
344 if (mCrumbs.size() > 0) {
345 crumb = mCrumbs.get(mCrumbs.size() - 1);
346 }
347 return crumb;
348 }
349
350 @Override
351 public void onClick(View v) {
352 if (mBackButton == v) {
353 popView();
354 notifyController();
Pankaj Garg634bf432015-07-13 09:54:21 -0700355 } else if (mOverflowView == v) {
356 int maxVisible = getMaxVisible();
357 while (maxVisible > 0) {
358 pop(false);
359 maxVisible--;
360 }
361 notifyController();
Michael Kolb370a4f32010-10-06 10:45:32 -0700362 } else {
363 // pop until view matches crumb view
364 while (v != getTopCrumb().crumbView) {
365 pop(false);
366 }
367 notifyController();
368 }
369 }
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400370 @Override
371 public int getBaseline() {
372 int ix = getChildCount();
373 if (ix > 0) {
374 // If there is at least one crumb, the baseline will be its
375 // baseline.
376 return getChildAt(ix-1).getBaseline();
377 }
378 return super.getBaseline();
379 }
380
381 @Override
382 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
383 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
384 int height = mSeparatorDrawable.getIntrinsicHeight();
Dianne Hackborn7831da92010-12-03 00:19:01 -0800385 if (getMeasuredHeight() < height) {
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400386 // This should only be an issue if there are currently no separators
387 // showing; i.e. if there is one crumb and no back button.
388 int mode = View.MeasureSpec.getMode(heightMeasureSpec);
389 switch(mode) {
390 case View.MeasureSpec.AT_MOST:
391 if (View.MeasureSpec.getSize(heightMeasureSpec) < height) {
392 return;
393 }
394 break;
395 case View.MeasureSpec.EXACTLY:
396 return;
397 default:
398 break;
399 }
Dianne Hackborn7831da92010-12-03 00:19:01 -0800400 setMeasuredDimension(getMeasuredWidth(), height);
Leon Scroggins7e5f7352010-10-18 13:25:31 -0400401 }
402 }
Michael Kolb370a4f32010-10-06 10:45:32 -0700403
404 class Crumb {
405
406 public View crumbView;
407 public boolean canGoBack;
408 public Object data;
409
410 public Crumb(String title, boolean backEnabled, Object tag) {
411 init(makeCrumbView(title), backEnabled, tag);
412 }
413
414 public Crumb(View view, boolean backEnabled, Object tag) {
415 init(view, backEnabled, tag);
416 }
417
418 private void init(View view, boolean back, Object tag) {
419 canGoBack = back;
420 crumbView = view;
421 data = tag;
422 }
423
424 private TextView makeCrumbView(String name) {
425 TextView tv = new TextView(mContext);
Pankaj Garg634bf432015-07-13 09:54:21 -0700426 tv.setTextAppearance(mContext, R.style.BookmarkPathText);
John Reck95f88e42011-09-26 09:25:43 -0700427 tv.setPadding(mCrumbPadding, 0, mCrumbPadding, 0);
Michael Kolb370a4f32010-10-06 10:45:32 -0700428 tv.setGravity(Gravity.CENTER_VERTICAL);
Pankaj Garg634bf432015-07-13 09:54:21 -0700429 tv.setText(name + " >");
Michael Kolb370a4f32010-10-06 10:45:32 -0700430 tv.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
431 LayoutParams.MATCH_PARENT));
John Reck95f88e42011-09-26 09:25:43 -0700432 tv.setSingleLine();
Leon Scroggins74dbe012010-10-15 10:54:27 -0400433 tv.setEllipsize(TextUtils.TruncateAt.END);
Michael Kolb370a4f32010-10-06 10:45:32 -0700434 return tv;
435 }
436
437 }
438
439}