Save toolbar state
Fixes: 141370968
Test: Manually
Change-Id: I0a982f675188b22bfd3c2e81485b848d380dab5f
diff --git a/car-ui-lib/res/values-port/config.xml b/car-ui-lib/res/values-port/config.xml
new file mode 100644
index 0000000..bb74d4c
--- /dev/null
+++ b/car-ui-lib/res/values-port/config.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- Whether or not the toolbar title and tabs can be shown at the same time -->
+ <bool name="car_ui_toolbar_title_and_tabs_are_mutually_exclusive">false</bool>
+</resources>
diff --git a/car-ui-lib/res/values/config.xml b/car-ui-lib/res/values/config.xml
index 7dc9c0e..047434a 100644
--- a/car-ui-lib/res/values/config.xml
+++ b/car-ui-lib/res/values/config.xml
@@ -44,4 +44,7 @@
<!-- Width of the scrollbar container. -->
<dimen name="car_ui_scrollbar_container_width">@dimen/car_ui_margin</dimen>
-</resources>
\ No newline at end of file
+ <!-- Whether or not the toolbar title and tabs can be shown at the same time -->
+ <bool name="car_ui_toolbar_title_and_tabs_are_mutually_exclusive">true</bool>
+
+</resources>
diff --git a/car-ui-lib/src/com/android/car/ui/toolbar/SearchView.java b/car-ui-lib/src/com/android/car/ui/toolbar/SearchView.java
index 2e534f7..ef29354 100644
--- a/car-ui-lib/src/com/android/car/ui/toolbar/SearchView.java
+++ b/car-ui-lib/src/com/android/car/ui/toolbar/SearchView.java
@@ -130,6 +130,11 @@
mSearchText.setHint(hint);
}
+ /** Gets the search hint */
+ public CharSequence getHint() {
+ return mSearchText.getHint();
+ }
+
/**
* Sets a custom icon to display in the search box.
*/
diff --git a/car-ui-lib/src/com/android/car/ui/toolbar/Toolbar.java b/car-ui-lib/src/com/android/car/ui/toolbar/Toolbar.java
index ef3f4a8..1659f36 100644
--- a/car-ui-lib/src/com/android/car/ui/toolbar/Toolbar.java
+++ b/car-ui-lib/src/com/android/car/ui/toolbar/Toolbar.java
@@ -21,6 +21,8 @@
import android.content.ContextWrapper;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
@@ -116,6 +118,8 @@
SEARCH,
}
+ private final boolean mTitleAndTabsAreMutuallyExclusive;
+
private ImageView mNavIcon;
private ImageView mLogo;
private ViewGroup mNavIconContainer;
@@ -178,6 +182,9 @@
attrs, R.styleable.CarUiToolbar, defStyleAttr, defStyleRes);
try {
+ mTitleAndTabsAreMutuallyExclusive = context.getResources().getBoolean(
+ R.bool.car_ui_toolbar_title_and_tabs_are_mutually_exclusive);
+
mTitle.setText(a.getString(R.styleable.CarUiToolbar_title));
setLogo(a.getResourceId(R.styleable.CarUiToolbar_logo, 0));
setBackgroundShown(a.getBoolean(R.styleable.CarUiToolbar_showBackground, true));
@@ -247,6 +254,81 @@
handleToolbarHeightChangeListeners(getHeight());
}
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState ss = new SavedState(superState);
+ ss.mTitle = getTitle();
+ ss.mNavButtonMode = getNavButtonMode();
+ ss.mSearchHint = getSearchHint();
+ ss.mBackgroundShown = getBackgroundShown();
+ ss.mShowMenuItemsWhileSearching = getShowMenuItemsWhileSearching();
+ ss.mState = getState();
+ return ss;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ if (!(state instanceof SavedState)) {
+ Log.w(TAG, "onRestoreInstanceState called with an unsupported state");
+ super.onRestoreInstanceState(state);
+ } else {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ setTitle(ss.mTitle);
+ setNavButtonMode(ss.mNavButtonMode);
+ setSearchHint(ss.mSearchHint);
+ setBackgroundShown(ss.mBackgroundShown);
+ setShowMenuItemsWhileSearching(ss.mShowMenuItemsWhileSearching);
+ setState(ss.mState);
+ }
+ }
+
+ private static class SavedState extends BaseSavedState {
+ private CharSequence mTitle;
+ private State mState;
+ private NavButtonMode mNavButtonMode;
+ private CharSequence mSearchHint;
+ private boolean mBackgroundShown;
+ private boolean mShowMenuItemsWhileSearching;
+
+ SavedState(Parcelable in) {
+ super(in);
+ }
+
+ SavedState(Parcel in) {
+ super(in);
+ mTitle = in.readCharSequence();
+ mNavButtonMode = NavButtonMode.valueOf(in.readString());
+ mSearchHint = in.readCharSequence();
+ mBackgroundShown = in.readBoolean();
+ mShowMenuItemsWhileSearching = in.readBoolean();
+ mState = State.valueOf(in.readString());
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeCharSequence(mTitle);
+ out.writeString(mNavButtonMode.name());
+ out.writeCharSequence(mSearchHint);
+ out.writeBoolean(mBackgroundShown);
+ out.writeBoolean(mShowMenuItemsWhileSearching);
+ out.writeString(mState.name());
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
/**
* Sets the title of the toolbar to a string resource.
*
@@ -265,6 +347,10 @@
mTitle.setText(title);
}
+ public CharSequence getTitle() {
+ return mTitle.getText();
+ }
+
/**
* Gets the {@link TabLayout} for this toolbar.
*/
@@ -278,6 +364,7 @@
*/
public void addTab(TabLayout.Tab tab) {
mTabLayout.addTab(tab);
+ setState(getState());
}
/**
@@ -310,20 +397,21 @@
setState(mState);
}
- /**
- * Sets the hint for the search bar.
- */
+ /** Sets the hint for the search bar. */
public void setSearchHint(int resId) {
mSearchView.setHint(resId);
}
- /**
- * Sets the hint for the search bar.
- */
+ /** Sets the hint for the search bar. */
public void setSearchHint(CharSequence hint) {
mSearchView.setHint(hint);
}
+ /** Gets the search hint */
+ public CharSequence getSearchHint() {
+ return mSearchView.getHint();
+ }
+
/**
* An enum of possible styles the nav button could be in. All styles will still call
* {@link OnBackListener#onBack()}.
@@ -368,9 +456,7 @@
}
}
- /**
- * Show/hide the background. When hidden, the toolbar is completely transparent.
- */
+ /** Show/hide the background. When hidden, the toolbar is completely transparent. */
public void setBackgroundShown(boolean shown) {
if (shown) {
super.setBackground(getContext().getDrawable(R.color.car_ui_toolbar_background_color));
@@ -379,6 +465,11 @@
}
}
+ /** Returns true is the toolbar background is shown */
+ public boolean getBackgroundShown() {
+ return super.getBackground() != null;
+ }
+
/**
* Sets the {@link MenuItem Menuitems} to display.
*/
@@ -391,7 +482,9 @@
return;
}
- mMenuItems = items;
+ // Copy the list so that if the list is modified and setMenuItems is called again,
+ // the equals() check will fail. Note that the MenuItems are not copied here.
+ mMenuItems = new ArrayList<>(items);
mOverflowItems.clear();
mMenuItemsContainer.removeAllViews();
@@ -452,6 +545,11 @@
setState(mState);
}
+ /** Returns if {@link MenuItem MenuItems} are shown while searching */
+ public boolean getShowMenuItemsWhileSearching() {
+ return mShowMenuItemsWhileSearching;
+ }
+
/**
* Sets the search query.
*/
@@ -507,6 +605,7 @@
}
}
};
+
mNavIcon.setVisibility(state != State.HOME ? VISIBLE : INVISIBLE);
mNavIcon.setImageResource(mNavButtonMode == NavButtonMode.BACK
? R.drawable.car_ui_icon_arrow_back
@@ -515,8 +614,11 @@
mNavIconContainer.setVisibility(state != State.HOME || mHasLogo ? VISIBLE : GONE);
mNavIconContainer.setOnClickListener(state != State.HOME ? backClickListener : null);
mNavIconContainer.setClickable(state != State.HOME);
- mTitle.setVisibility(state == State.HOME || state == State.SUBPAGE ? VISIBLE : GONE);
- mTabLayout.setVisibility(state == State.HOME ? VISIBLE : GONE);
+ boolean hasTabs = mTabLayout.getTabCount() > 0;
+ boolean showTitle = state == State.SUBPAGE || state == State.HOME
+ && (!mTitleAndTabsAreMutuallyExclusive || !hasTabs);
+ mTitle.setVisibility(showTitle ? VISIBLE : GONE);
+ mTabLayout.setVisibility(state == State.HOME && hasTabs ? VISIBLE : GONE);
mSearchView.setVisibility(state == State.SEARCH ? VISIBLE : GONE);
boolean showButtons = state != State.SEARCH || mShowMenuItemsWhileSearching;
mMenuItemsContainer.setVisibility(showButtons ? VISIBLE : GONE);
diff --git a/car-ui-lib/tests/paintbooth/AndroidManifest.xml b/car-ui-lib/tests/paintbooth/AndroidManifest.xml
index 136cb5a..8b3cce7 100644
--- a/car-ui-lib/tests/paintbooth/AndroidManifest.xml
+++ b/car-ui-lib/tests/paintbooth/AndroidManifest.xml
@@ -51,5 +51,9 @@
android:name=".preferences.PreferenceActivity"
android:exported="false"
android:parentActivityName=".MainActivity"/>
+ <activity
+ android:name=".toolbar.ToolbarActivity"
+ android:exported="false"
+ android:parentActivityName=".MainActivity"/>
</application>
</manifest>
diff --git a/car-ui-lib/tests/paintbooth/res/layout/grid_paged_recycler_view_activity.xml b/car-ui-lib/tests/paintbooth/res/layout/grid_paged_recycler_view_activity.xml
index 2e88b79..7c75f39 100644
--- a/car-ui-lib/tests/paintbooth/res/layout/grid_paged_recycler_view_activity.xml
+++ b/car-ui-lib/tests/paintbooth/res/layout/grid_paged_recycler_view_activity.xml
@@ -9,7 +9,8 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:title="@string/app_name"/>
+ app:title="@string/app_name"
+ app:state="subpage"/>
<com.android.car.ui.pagedrecyclerview.PagedRecyclerView
android:id="@+id/grid_list"
@@ -17,4 +18,4 @@
app:numOfColumns="4"
android:layout_width="match_parent"
android:layout_height="match_parent" />
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/car-ui-lib/tests/paintbooth/res/layout/paged_recycler_view_activity.xml b/car-ui-lib/tests/paintbooth/res/layout/paged_recycler_view_activity.xml
index 2c8b1c1..526cd0a 100644
--- a/car-ui-lib/tests/paintbooth/res/layout/paged_recycler_view_activity.xml
+++ b/car-ui-lib/tests/paintbooth/res/layout/paged_recycler_view_activity.xml
@@ -27,6 +27,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/app_name"
+ app:logo="@drawable/ic_launcher"
app:state="subpage"/>
<com.android.car.ui.pagedrecyclerview.PagedRecyclerView
diff --git a/car-ui-lib/tests/paintbooth/res/layout/toolbar_custom_view.xml b/car-ui-lib/tests/paintbooth/res/layout/toolbar_custom_view.xml
new file mode 100644
index 0000000..67b584a
--- /dev/null
+++ b/car-ui-lib/tests/paintbooth/res/layout/toolbar_custom_view.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/text_box_1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Text Box 1"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/image_view"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"/>
+
+ <ImageView
+ android:id="@+id/image_view"
+ android:src="@drawable/ic_launcher"
+ android:layout_width="20dp"
+ android:layout_height="20dp"
+ app:layout_constraintStart_toEndOf="@id/text_box_1"
+ app:layout_constraintEnd_toStartOf="@id/text_box_2"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"/>
+
+ <TextView
+ android:id="@+id/text_box_2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Text Box 2"
+ app:layout_constraintStart_toEndOf="@id/image_view"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"/>
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/car-ui-lib/tests/paintbooth/src/com/android/car/ui/paintbooth/MainActivity.java b/car-ui-lib/tests/paintbooth/src/com/android/car/ui/paintbooth/MainActivity.java
index 3c47f0b..ba7d1ec 100644
--- a/car-ui-lib/tests/paintbooth/src/com/android/car/ui/paintbooth/MainActivity.java
+++ b/car-ui-lib/tests/paintbooth/src/com/android/car/ui/paintbooth/MainActivity.java
@@ -33,6 +33,7 @@
import com.android.car.ui.paintbooth.pagedrecyclerview.GridPagedRecyclerViewActivity;
import com.android.car.ui.paintbooth.pagedrecyclerview.PagedRecyclerViewActivity;
import com.android.car.ui.paintbooth.preferences.PreferenceActivity;
+import com.android.car.ui.paintbooth.toolbar.ToolbarActivity;
import java.util.Arrays;
import java.util.List;
@@ -48,7 +49,8 @@
Pair.create("Dialogs sample", DialogsActivity.class),
Pair.create("List sample", PagedRecyclerViewActivity.class),
Pair.create("Grid sample", GridPagedRecyclerViewActivity.class),
- Pair.create("Preferences sample", PreferenceActivity.class)
+ Pair.create("Preferences sample", PreferenceActivity.class),
+ Pair.create("Toolbar sample", ToolbarActivity.class)
);
private class ViewHolder extends RecyclerView.ViewHolder {
diff --git a/car-ui-lib/tests/paintbooth/src/com/android/car/ui/paintbooth/toolbar/ToolbarActivity.java b/car-ui-lib/tests/paintbooth/src/com/android/car/ui/paintbooth/toolbar/ToolbarActivity.java
new file mode 100644
index 0000000..a39dc89
--- /dev/null
+++ b/car-ui-lib/tests/paintbooth/src/com/android/car/ui/paintbooth/toolbar/ToolbarActivity.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.ui.paintbooth.toolbar;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.core.util.Pair;
+
+import com.android.car.ui.pagedrecyclerview.PagedRecyclerView;
+import com.android.car.ui.paintbooth.R;
+import com.android.car.ui.toolbar.MenuItem;
+import com.android.car.ui.toolbar.TabLayout;
+import com.android.car.ui.toolbar.Toolbar;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ToolbarActivity extends Activity {
+
+ private List<MenuItem> mMenuItems = new ArrayList<>();
+ private List<Pair<CharSequence, View.OnClickListener>> mButtons = new ArrayList<>();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.paged_recycler_view_activity);
+
+ Toolbar toolbar = requireViewById(R.id.toolbar);
+ toolbar.registerOnBackListener(() -> {
+ if (toolbar.getState() == Toolbar.State.SEARCH) {
+ toolbar.setState(Toolbar.State.SUBPAGE);
+ return true;
+ }
+ return false;
+ });
+
+ toolbar.getRootView().setBackgroundColor(0xFFFFFF00);
+
+ mMenuItems.add(MenuItem.Builder.createSearch(this, i ->
+ toolbar.setState(Toolbar.State.SEARCH)));
+
+ toolbar.setMenuItems(mMenuItems);
+
+ mButtons.add(Pair.create("Change title", v ->
+ toolbar.setTitle(toolbar.getTitle() + " X")));
+
+ mButtons.add(Pair.create("Add menu item", v -> {
+ mMenuItems.add(MenuItem.Builder.createSettings(this, i ->
+ Toast.makeText(this, "Clicked", Toast.LENGTH_SHORT).show()));
+ toolbar.setMenuItems(mMenuItems);
+ }));
+
+ mButtons.add(Pair.create("Toggle nav button mode", v -> {
+ if (toolbar.getNavButtonMode() == Toolbar.NavButtonMode.BACK) {
+ toolbar.setNavButtonMode(Toolbar.NavButtonMode.CLOSE);
+ } else {
+ toolbar.setNavButtonMode(Toolbar.NavButtonMode.BACK);
+ }
+ }));
+
+ mButtons.add(Pair.create("Toggle state", v -> {
+ if (toolbar.getState() == Toolbar.State.SUBPAGE) {
+ toolbar.setState(Toolbar.State.HOME);
+ } else {
+ toolbar.setState(Toolbar.State.SUBPAGE);
+ }
+ }));
+
+ mButtons.add(Pair.create("Toggle search hint", v -> {
+ if (toolbar.getSearchHint().equals("Foo")) {
+ toolbar.setSearchHint("Bar");
+ } else {
+ toolbar.setSearchHint("Foo");
+ }
+ }));
+
+ mButtons.add(Pair.create("Toggle background", v ->
+ toolbar.setBackgroundShown(!toolbar.getBackgroundShown())));
+
+ mButtons.add(Pair.create("Toggle show menu items while searching", v ->
+ toolbar.setShowMenuItemsWhileSearching(!toolbar.getShowMenuItemsWhileSearching())));
+
+ mButtons.add(Pair.create("Show custom view", v ->
+ toolbar.setCustomView(R.layout.toolbar_custom_view)));
+
+ mButtons.add(Pair.create("Add tab", v ->
+ toolbar.addTab(new TabLayout.Tab(getDrawable(R.drawable.ic_launcher), "Foo"))));
+
+ PagedRecyclerView prv = requireViewById(R.id.list);
+ prv.setAdapter(mAdapter);
+ }
+
+ private static class ViewHolder extends PagedRecyclerView.ViewHolder {
+ private final Button mButton;
+
+ ViewHolder(View itemView) {
+ super(itemView);
+ mButton = itemView.requireViewById(R.id.button);
+ }
+
+ public void bind(CharSequence title, View.OnClickListener listener) {
+ mButton.setText(title);
+ mButton.setOnClickListener(listener);
+ }
+ }
+
+ private PagedRecyclerView.Adapter mAdapter = new PagedRecyclerView.Adapter() {
+
+ public int getItemCount() {
+ return mButtons.size();
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
+ View item = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent,
+ false);
+ return new ViewHolder(item);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull PagedRecyclerView.ViewHolder holder, int position) {
+ Pair<CharSequence, View.OnClickListener> pair = mButtons.get(position);
+ ((ViewHolder) holder).bind(pair.first, pair.second);
+ }
+ };
+}