Merge "Prefix all toolbar ids with car_ui_toolbar" into pi-car-dev
diff --git a/car-media-common/res/values/integers.xml b/car-media-common/res/values/integers.xml
index a1e7faa..2c6177d 100644
--- a/car-media-common/res/values/integers.xml
+++ b/car-media-common/res/values/integers.xml
@@ -27,10 +27,13 @@
<!-- Number of columns in app selector -->
<integer name="num_app_selector_columns">3</integer>
- <!-- For the playback widget (com.android.car.media.common.PlaybackFragment), defines the
- maximum square into which requested media items bitmaps must fit (larger bitmaps are
- scaled down).
+ <!-- Defines the maximum square into which requested media items bitmaps must fit (smaller
+ bitmaps are not modified, larger ones are resized to fit into the square, preserving their
+ aspect ratio). A 256x256 bitmap takes 250 KB or memory, and a 512x512 takes 1 MB so this
+ number should be set conservatively while making sure the displayed bitmaps look good.
+ This value is also passed to media applications so they can pre-fetch their artwork at an
+ optimal resolution.
-->
- <integer name="playback_widget_bitmap_max_size_px">256</integer>
+ <integer name="media_items_bitmap_max_size_px">256</integer>
</resources>
diff --git a/car-media-common/src/com/android/car/media/common/MediaConstants.java b/car-media-common/src/com/android/car/media/common/MediaConstants.java
index 967680c..a3ec31c 100644
--- a/car-media-common/src/com/android/car/media/common/MediaConstants.java
+++ b/car-media-common/src/com/android/car/media/common/MediaConstants.java
@@ -16,11 +16,22 @@
package com.android.car.media.common;
+import androidx.media.MediaBrowserServiceCompat;
+
/**
* Holds constants used when dealing with MediaBrowserServices that support the
* content style API for media.
*/
public final class MediaConstants {
+
+ /**
+ * Integer extra indicating the recommended size (in pixels) for media art bitmaps. The value
+ * is passed in the rootHints Bundle of {@link MediaBrowserServiceCompat#onGetRoot} and can be
+ * retrieved with: rootHints.getInt("android.media.extras.MEDIA_ART_SIZE_HINT_PIXELS", 0).
+ */
+ public static final String EXTRA_MEDIA_ART_SIZE_HINT_PIXELS =
+ "android.media.extras.MEDIA_ART_SIZE_HINT_PIXELS";
+
/**
* Bundle extra holding the Pending Intent to launch to let users resolve the current error.
* See {@link #ERROR_RESOLUTION_ACTION_LABEL} for more details.
diff --git a/car-media-common/src/com/android/car/media/common/PlaybackFragment.java b/car-media-common/src/com/android/car/media/common/PlaybackFragment.java
index b66f9d9..8d04733 100644
--- a/car-media-common/src/com/android/car/media/common/PlaybackFragment.java
+++ b/car-media-common/src/com/android/car/media/common/PlaybackFragment.java
@@ -91,7 +91,7 @@
// Let the Media center trampoline figure out what to open.
v -> startActivity(new Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE)));
- int max = activity.getResources().getInteger(R.integer.playback_widget_bitmap_max_size_px);
+ int max = activity.getResources().getInteger(R.integer.media_items_bitmap_max_size_px);
Size maxArtSize = new Size(max, max);
mAlbumArtBinder = new ImageBinder<>(PlaceholderType.FOREGROUND, maxArtSize,
drawable -> {
diff --git a/car-media-common/src/com/android/car/media/common/playback/PlaybackViewModel.java b/car-media-common/src/com/android/car/media/common/playback/PlaybackViewModel.java
index 5a655ef..5d6de9c 100644
--- a/car-media-common/src/com/android/car/media/common/playback/PlaybackViewModel.java
+++ b/car-media-common/src/com/android/car/media/common/playback/PlaybackViewModel.java
@@ -375,6 +375,13 @@
}
/**
+ * Returns the currently supported playback actions
+ */
+ public long getSupportedActions() {
+ return mState.getActions();
+ }
+
+ /**
* Returns the duration of the media item in milliseconds. The current position in this
* duration can be obtained by calling {@link #getProgress()}.
*/
diff --git a/car-media-common/src/com/android/car/media/common/source/MediaBrowserConnector.java b/car-media-common/src/com/android/car/media/common/source/MediaBrowserConnector.java
index 36f90c8..b16164a 100644
--- a/car-media-common/src/com/android/car/media/common/source/MediaBrowserConnector.java
+++ b/car-media-common/src/com/android/car/media/common/source/MediaBrowserConnector.java
@@ -22,9 +22,12 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Bundle;
import android.support.v4.media.MediaBrowserCompat;
import android.util.Log;
+import com.android.car.media.common.MediaConstants;
+
/**
* A helper class to connect to a single {@link MediaBrowserCompat}. Connecting to a new one
* automatically disconnects the previous browser. Changes of the currently connected browser are
@@ -43,6 +46,7 @@
private final Context mContext;
private final Callback mCallback;
+ private final int mMaxBitmapSizePx;
@Nullable private ComponentName mBrowseService;
@Nullable private MediaBrowserCompat mBrowser;
@@ -55,6 +59,8 @@
MediaBrowserConnector(@NonNull Context context, @NonNull Callback callback) {
mContext = context;
mCallback = callback;
+ mMaxBitmapSizePx = mContext.getResources().getInteger(
+ com.android.car.media.common.R.integer.media_items_bitmap_max_size_px);
}
/** Counter so callbacks from obsolete connections can be ignored. */
@@ -139,6 +145,8 @@
@NonNull
protected MediaBrowserCompat createMediaBrowser(@NonNull ComponentName browseService,
@NonNull MediaBrowserCompat.ConnectionCallback callback) {
- return new MediaBrowserCompat(mContext, browseService, callback, null);
+ Bundle rootHints = new Bundle();
+ rootHints.putInt(MediaConstants.EXTRA_MEDIA_ART_SIZE_HINT_PIXELS, mMaxBitmapSizePx);
+ return new MediaBrowserCompat(mContext, browseService, callback, rootHints);
}
}
diff --git a/car-ui-lib/res/layout/car_ui_list_preference_dialog.xml b/car-ui-lib/res/layout/car_ui_list_preference_dialog.xml
new file mode 100644
index 0000000..b3c9855
--- /dev/null
+++ b/car-ui-lib/res/layout/car_ui_list_preference_dialog.xml
@@ -0,0 +1,36 @@
+<?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.
+ -->
+
+<FrameLayout
+ 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"
+ android:id="@+id/dialog_container">
+
+ <com.android.car.ui.recyclerview.CarUiRecyclerView
+ android:id="@+id/radio_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <com.android.car.ui.toolbar.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:state="subpage"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/car-ui-lib/res/layout/car_ui_radio_button_item.xml b/car-ui-lib/res/layout/car_ui_radio_button_item.xml
new file mode 100644
index 0000000..e6733b1
--- /dev/null
+++ b/car-ui-lib/res/layout/car_ui_radio_button_item.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+
+<RadioButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/radio_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
diff --git a/car-ui-lib/res/values/bools.xml b/car-ui-lib/res/values/bools.xml
index 2e59118..8eae505 100644
--- a/car-ui-lib/res/values/bools.xml
+++ b/car-ui-lib/res/values/bools.xml
@@ -32,4 +32,6 @@
<bool name="car_ui_preference_switch_toggle_show_animation">true</bool>
<bool name="car_ui_preference_switch_toggle_use_text_track">false</bool>
<bool name="car_ui_preference_show_chevron">false</bool>
+
+ <bool name="car_ui_preference_list_show_full_screen">true</bool>
</resources>
diff --git a/car-ui-lib/res/values/styles.xml b/car-ui-lib/res/values/styles.xml
index 76dbce2..d39029f 100644
--- a/car-ui-lib/res/values/styles.xml
+++ b/car-ui-lib/res/values/styles.xml
@@ -55,6 +55,13 @@
<item name="android:scrollbars">none</item>
</style>
+ <style name="Preference.CarUi.ListPreference" parent="android:Theme.DeviceDefault.Dialog">
+ <item name="android:windowNoTitle">true</item>
+ <!-- Set this to true if you want Full Screen without status bar -->
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowIsFloating">false</item>
+ </style>
+
<!-- Preference Styles -->
<style name="Preference.CarUi">
@@ -113,6 +120,10 @@
<item name="android:layout">@layout/car_ui_preference_fragment</item>
</style>
+ <style name="RadioButton.CarUi" parent="android:Widget.DeviceDefault.Light.CompoundButton.RadioButton">
+ <item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
+ </style>
+
<style name="PreferenceFragmentList.CarUi">
<item name="android:paddingTop">0dp</item>
<item name="android:paddingBottom">0dp</item>
diff --git a/car-ui-lib/res/values/themes.xml b/car-ui-lib/res/values/themes.xml
index 6574a31..70e6872 100644
--- a/car-ui-lib/res/values/themes.xml
+++ b/car-ui-lib/res/values/themes.xml
@@ -200,6 +200,8 @@
<!-- Used by CarUiRecyclerView -->
<item name="carUiRecyclerViewStyle">@style/Widget.CarUi.CarUiRecyclerView</item>
+
+ <item name="radioButtonStyle">@style/RadioButton.CarUi</item>
</style>
<style name="CarUiPreferenceTheme">
diff --git a/car-ui-lib/src/com/android/car/ui/preference/CarUiRecyclerViewRadioButtonAdapter.java b/car-ui-lib/src/com/android/car/ui/preference/CarUiRecyclerViewRadioButtonAdapter.java
new file mode 100644
index 0000000..93c74dd
--- /dev/null
+++ b/car-ui-lib/src/com/android/car/ui/preference/CarUiRecyclerViewRadioButtonAdapter.java
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+package com.android.car.ui.preference;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RadioButton;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.car.ui.R;
+
+import java.util.List;
+
+/**
+ * The adapter for the recyclerview containing radio buttons as the items.
+ */
+public class CarUiRecyclerViewRadioButtonAdapter extends
+ RecyclerView.Adapter<CarUiRecyclerViewRadioButtonAdapter.ViewHolder> {
+
+ /**
+ * Callback that will be issued after any radio button is clicked.
+ */
+ public interface OnRadioButtonClickedListener {
+ /**
+ * Will be called when radio button is clicked.
+ *
+ * @param position of the radio button.
+ */
+ void onClick(int position);
+ }
+
+ private OnRadioButtonClickedListener mOnRadioButtonClickedListener;
+
+ private List<String> mList;
+ private int mSelectedPosition = -1;
+
+ public CarUiRecyclerViewRadioButtonAdapter(List<String> list, int position) {
+ mList = list;
+ mSelectedPosition = position;
+ }
+
+ @Override
+ public CarUiRecyclerViewRadioButtonAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
+ int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.car_ui_radio_button_item, parent, false);
+ return new CarUiRecyclerViewRadioButtonAdapter.ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(CarUiRecyclerViewRadioButtonAdapter.ViewHolder holder,
+ int position) {
+ String entry = mList.get(position);
+ //since only one radio button is allowed to be selected,
+ // this condition un-checks previous selections
+ holder.mRadioButton.setChecked(mSelectedPosition == position);
+ holder.mRadioButton.setText(entry);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mList.size();
+ }
+
+ /** Registers a new {@link OnRadioButtonClickedListener} listener. */
+ public void registerListener(OnRadioButtonClickedListener listener) {
+ mOnRadioButtonClickedListener = listener;
+ }
+
+ /** Unregisters a {@link OnRadioButtonClickedListener} listener\. */
+ public void unregisterOnBackListener(OnRadioButtonClickedListener listener) {
+ mOnRadioButtonClickedListener = null;
+ }
+
+ /** The viewholder class for recyclerview containing radio buttons. */
+ public class ViewHolder extends RecyclerView.ViewHolder {
+
+ public RadioButton mRadioButton;
+
+ public ViewHolder(View view) {
+ super(view);
+ mRadioButton = (RadioButton) view.findViewById(R.id.radio_button);
+
+ mRadioButton.setOnClickListener(v -> {
+ mSelectedPosition = getAdapterPosition();
+ notifyDataSetChanged();
+ if (mOnRadioButtonClickedListener != null) {
+ mOnRadioButtonClickedListener.onClick(mSelectedPosition);
+ }
+ });
+ }
+ }
+}
diff --git a/car-ui-lib/src/com/android/car/ui/preference/ListPreferenceDialogFragment.java b/car-ui-lib/src/com/android/car/ui/preference/ListPreferenceDialogFragment.java
index 2d0a51d..2d68940 100644
--- a/car-ui-lib/src/com/android/car/ui/preference/ListPreferenceDialogFragment.java
+++ b/car-ui-lib/src/com/android/car/ui/preference/ListPreferenceDialogFragment.java
@@ -17,21 +17,37 @@
package com.android.car.ui.preference;
import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import androidx.annotation.NonNull;
+import androidx.fragment.app.DialogFragment;
import androidx.preference.ListPreference;
+import com.android.car.ui.R;
+import com.android.car.ui.recyclerview.CarUiRecyclerView;
+import com.android.car.ui.toolbar.Toolbar;
+
+import java.util.ArrayList;
+
/**
* Presents a dialog with a list of options associated with a {@link ListPreference}.
*
* <p>Note: this is borrowed as-is from androidx.preference.ListPreferenceDialogFragmentCompat
* with updates to formatting to match the project style. Automotive applications should use this
* implementations in order to launch the system themed platform {@link AlertDialog} instead of the
- * one in the support library.
+ * one in the support library. In addition this list preference can be shown in a full screen
+ * dialog. Full screen dialog will have a custom layout returned by {@link #onCreateDialogView} and
+ * the window size is changes in {@link #onStart()}.
*/
-public class ListPreferenceDialogFragment extends PreferenceDialogFragment {
+public class ListPreferenceDialogFragment extends PreferenceDialogFragment implements
+ Toolbar.OnBackListener, CarUiRecyclerViewRadioButtonAdapter.OnRadioButtonClickedListener {
private static final String SAVE_STATE_INDEX = "ListPreferenceDialogFragment.index";
private static final String SAVE_STATE_ENTRIES = "ListPreferenceDialogFragment.entries";
@@ -41,6 +57,9 @@
private int mClickedDialogEntryIndex;
private CharSequence[] mEntries;
private CharSequence[] mEntryValues;
+ private View mDialogView;
+ private CarUiRecyclerView mCarUiRecyclerView;
+ private boolean mShowFullScreen;
/**
* Returns a new instance of {@link ListPreferenceDialogFragment} for the {@link
@@ -55,8 +74,14 @@
}
@Override
+ public View onCreateDialogView(Context context) {
+ return mShowFullScreen ? mDialogView : super.onCreateDialogView(context);
+ }
+
+ @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
if (savedInstanceState == null) {
ListPreference preference = getListPreference();
@@ -73,6 +98,52 @@
mEntries = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRIES);
mEntryValues = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES);
}
+
+ mShowFullScreen = getContext().getResources().getBoolean(
+ R.bool.car_ui_preference_list_show_full_screen);
+ if (!mShowFullScreen) {
+ return;
+ }
+
+ setStyle(DialogFragment.STYLE_NORMAL, R.style.Preference_CarUi_ListPreference);
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ mDialogView = inflater.inflate(R.layout.car_ui_list_preference_dialog, null);
+
+ Toolbar toolbar = mDialogView.findViewById(R.id.toolbar);
+ toolbar.registerOnBackListener(this);
+
+ ArrayList<String> entries = new ArrayList<>();
+ for (int i = 0; i < mEntries.length; i++) {
+ entries.add(mEntries[i].toString());
+ }
+
+ mCarUiRecyclerView = mDialogView.findViewById(R.id.radio_list);
+ CarUiRecyclerViewRadioButtonAdapter adapter = new CarUiRecyclerViewRadioButtonAdapter(
+ entries, mClickedDialogEntryIndex);
+ mCarUiRecyclerView.setAdapter(adapter);
+ adapter.registerListener(this);
+
+ mDialogView.getViewTreeObserver()
+ .addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ mDialogView.getViewTreeObserver()
+ .removeOnGlobalLayoutListener(this);
+ int recyclerViewHeight =
+ getDialog().getWindow().getDecorView().getHeight();
+
+ mCarUiRecyclerView.setPadding(mCarUiRecyclerView.getPaddingLeft(),
+ toolbar.getHeight(),
+ mCarUiRecyclerView.getPaddingRight(),
+ mCarUiRecyclerView.getPaddingBottom());
+
+ ViewGroup.LayoutParams params =
+ mCarUiRecyclerView.getLayoutParams();
+ params.height = recyclerViewHeight;
+ mCarUiRecyclerView.setLayoutParams(params);
+ }
+ });
}
@Override
@@ -91,20 +162,31 @@
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
super.onPrepareDialogBuilder(builder);
- builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex,
- (dialog, which) -> {
- mClickedDialogEntryIndex = which;
+ if (mShowFullScreen) {
+ builder.setPositiveButton(null, null);
+ builder.setNegativeButton(null, null);
+ builder.setTitle(null);
+ return;
+ } else {
+ builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex,
+ (dialog, which) -> onClick(which));
- // Clicking on an item simulates the positive button click, and dismisses the
- // dialog.
- ListPreferenceDialogFragment.this.onClick(dialog,
- DialogInterface.BUTTON_POSITIVE);
- dialog.dismiss();
- });
+ // The typical interaction for list-based dialogs is to have click-on-an-item dismiss
+ // the
+ // dialog instead of the user having to press 'Ok'.
+ builder.setPositiveButton(null, null);
+ }
+ }
- // The typical interaction for list-based dialogs is to have click-on-an-item dismiss the
- // dialog instead of the user having to press 'Ok'.
- builder.setPositiveButton(null, null);
+ @Override
+ public void onStart() {
+ super.onStart();
+ Dialog dialog = getDialog();
+ if (dialog != null && mShowFullScreen) {
+ int width = ViewGroup.LayoutParams.MATCH_PARENT;
+ int height = ViewGroup.LayoutParams.MATCH_PARENT;
+ dialog.getWindow().setLayout(width, height);
+ }
}
@Override
@@ -118,4 +200,17 @@
}
}
+ @Override
+ public boolean onBack() {
+ onClick(getDialog(), DialogInterface.BUTTON_NEGATIVE);
+ getDialog().dismiss();
+ return true;
+ }
+
+ @Override
+ public void onClick(int position) {
+ mClickedDialogEntryIndex = position;
+ onClick(getDialog(), DialogInterface.BUTTON_POSITIVE);
+ getDialog().dismiss();
+ }
}
diff --git a/car-ui-lib/tests/robotests/src/com/android/car/ui/recyclerview/CarUiRecyclerViewTest.java b/car-ui-lib/tests/robotests/src/com/android/car/ui/recyclerview/CarUiRecyclerViewTest.java
index d8793d1..5b2a1e3 100644
--- a/car-ui-lib/tests/robotests/src/com/android/car/ui/recyclerview/CarUiRecyclerViewTest.java
+++ b/car-ui-lib/tests/robotests/src/com/android/car/ui/recyclerview/CarUiRecyclerViewTest.java
@@ -79,9 +79,9 @@
.inflate(R.layout.test_linear_car_ui_recycler_view, null);
mCarUiRecyclerView = mView.findViewById(R.id.test_prv);
- mCarUiRecyclerView.setAdapter(mAdapter);
- assertThat(mCarUiRecyclerView.getLayoutManager()).isInstanceOf(LinearLayoutManager.class);
+ assertThat(mCarUiRecyclerView.getEffectiveLayoutManager()).isInstanceOf(
+ LinearLayoutManager.class);
}
@Test
@@ -90,9 +90,9 @@
.inflate(R.layout.test_grid_car_ui_recycler_view, null);
mCarUiRecyclerView = mView.findViewById(R.id.test_prv);
- mCarUiRecyclerView.setAdapter(mAdapter);
- assertThat(mCarUiRecyclerView.getLayoutManager()).isInstanceOf(GridLayoutManager.class);
+ assertThat(mCarUiRecyclerView.getEffectiveLayoutManager()).isInstanceOf(
+ GridLayoutManager.class);
}
@Test