Make seek bar support direct manipulation mode

And update the background of seekbar thumb to support direct
manipulation mode, including:
1. Rename the existing seekbar_background to seekbar_progress
2. Create a new seekbar_background and set it as the seekbar's
background
3. Update the seekbar_thumb so that it can draw 2 rings around the thumb
when the seekbar is selected
4. Use media source color to set the color of the thumb, excluding the
color of the 2 rings around the thumb

Bug: 161483857
Test: manual
Change-Id: I183b5c803e9e6638e73735761128182019c4b697
diff --git a/res/color/progress_bar_thumb_inner_ring_color.xml b/res/color/progress_bar_thumb_inner_ring_color.xml
new file mode 100644
index 0000000..d3a9d4e
--- /dev/null
+++ b/res/color/progress_bar_thumb_inner_ring_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2020 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.
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:color="@color/car_ui_rotary_focus_fill_color"/>
+    <item android:color="@android:color/transparent"/>
+</selector>
diff --git a/res/color/progress_bar_thumb_outer_ring_color.xml b/res/color/progress_bar_thumb_outer_ring_color.xml
new file mode 100644
index 0000000..1bc1926
--- /dev/null
+++ b/res/color/progress_bar_thumb_outer_ring_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2020 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.
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:color="@color/car_ui_rotary_focus_stroke_color"/>
+    <item android:color="@android:color/transparent"/>
+</selector>
diff --git a/res/drawable/seekbar_foreground.xml b/res/drawable/seekbar_foreground.xml
new file mode 100644
index 0000000..3942e21
--- /dev/null
+++ b/res/drawable/seekbar_foreground.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2020, 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- SeekBar highlight is drawn when it's focused but not in direct manipulation mode. When
+         in direct manipulation mode (android:state_selected="true"), the highlight is drawn on
+         the thumb instead. -->
+    <item android:state_selected="false" android:state_focused="true">
+        <shape android:shape="rectangle">
+            <solid android:color="@color/car_ui_rotary_focus_fill_color"/>
+            <stroke android:width="@dimen/car_ui_rotary_focus_stroke_width"
+                    android:color="@color/car_ui_rotary_focus_stroke_color"/>
+        </shape>
+    </item>
+</selector>
diff --git a/res/drawable/seekbar_background.xml b/res/drawable/seekbar_progress.xml
similarity index 95%
rename from res/drawable/seekbar_background.xml
rename to res/drawable/seekbar_progress.xml
index ec08455..3e68a73 100644
--- a/res/drawable/seekbar_background.xml
+++ b/res/drawable/seekbar_progress.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright 2016, The Android Open Source Project
+  Copyright 2020, 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.
diff --git a/res/drawable/seekbar_thumb.xml b/res/drawable/seekbar_thumb.xml
index 1d0b4a1..eb0c253 100644
--- a/res/drawable/seekbar_thumb.xml
+++ b/res/drawable/seekbar_thumb.xml
@@ -14,10 +14,28 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval">
-    <solid android:color="@color/progress_bar_thumb_color"/>
-    <size
-        android:width="@dimen/playback_seekbar_thumb_width"
-        android:height="@dimen/playback_seekbar_thumb_height"/>
-</shape>
\ No newline at end of file
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/thumb_center">
+        <shape android:shape="oval">
+            <solid android:color="@color/progress_bar_thumb_color"/>
+            <size android:width="@dimen/playback_seekbar_thumb_width"
+                  android:height="@dimen/playback_seekbar_thumb_height"/>
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="ring"
+               android:innerRadius="@dimen/playback_seekbar_thumb_inner_ring_inner_radius"
+               android:thickness="@dimen/playback_seekbar_thumb_inner_ring_thickness"
+               android:useLevel="false">
+            <solid android:color="@color/progress_bar_thumb_inner_ring_color"/>
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="ring"
+               android:innerRadius="@dimen/playback_seekbar_thumb_outer_ring_inner_radius"
+               android:thickness="@dimen/playback_seekbar_thumb_outer_ring_thickness"
+               android:useLevel="false">
+            <solid android:color="@color/progress_bar_thumb_outer_ring_color"/>
+        </shape>
+    </item>
+</layer-list>
diff --git a/res/layout/fragment_playback.xml b/res/layout/fragment_playback.xml
index 130eb1d..0c933ba 100644
--- a/res/layout/fragment_playback.xml
+++ b/res/layout/fragment_playback.xml
@@ -77,13 +77,13 @@
             android:clickable="false"
             android:paddingEnd="@dimen/playback_seekbar_padding_x"
             android:paddingStart="@dimen/playback_seekbar_padding_x"
-            android:progressDrawable="@drawable/seekbar_background"
+            android:progressDrawable="@drawable/seekbar_progress"
             android:thumb="@drawable/seekbar_thumb"
             android:thumbOffset="@dimen/playback_seekbar_thumb_offset"
             android:splitTrack="false"
             android:progressTint="@color/progress_bar_highlight"
             android:progressBackgroundTint="@color/progress_bar_background"
-            android:background="@null"/>
+            android:foreground="@drawable/seekbar_foreground"/>
     </com.android.car.ui.FocusArea>
 
     <Space
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b667604..dbb6ab8 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -40,6 +40,10 @@
     <!-- Size of the thumb in the playback seekbar -->
     <dimen name="playback_seekbar_thumb_height">16dp</dimen>
     <dimen name="playback_seekbar_thumb_width">16dp</dimen>
+    <dimen name="playback_seekbar_thumb_inner_ring_inner_radius">8dp</dimen>
+    <dimen name="playback_seekbar_thumb_inner_ring_thickness">8dp</dimen>
+    <dimen name="playback_seekbar_thumb_outer_ring_inner_radius">16dp</dimen>
+    <dimen name="playback_seekbar_thumb_outer_ring_thickness">8dp</dimen>
     <dimen name="playback_seekbar_thumb_offset">0px</dimen>
     <!-- Paddings of playback seekbar -->
     <dimen name="playback_seekbar_padding_x">0dp</dimen>
diff --git a/src/com/android/car/media/PlaybackFragment.java b/src/com/android/car/media/PlaybackFragment.java
index afd0f72..0dbf7bd 100644
--- a/src/com/android/car/media/PlaybackFragment.java
+++ b/src/com/android/car/media/PlaybackFragment.java
@@ -21,8 +21,11 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 import android.os.Bundle;
 import android.util.Log;
 import android.util.Size;
@@ -55,6 +58,7 @@
 import com.android.car.ui.recyclerview.ScrollingLimitedViewHolder;
 import com.android.car.ui.toolbar.MenuItem;
 import com.android.car.ui.toolbar.Toolbar;
+import com.android.car.ui.utils.DirectManipulationHelper;
 import com.android.car.uxr.LifeCycleObserverUxrContentLimiter;
 import com.android.car.uxr.UxrContentLimiterImpl;
 
@@ -463,6 +467,7 @@
         mQueue = view.findViewById(R.id.queue_list);
         mSeekBarContainer = view.findViewById(R.id.seek_bar_container);
         mSeekBar = view.findViewById(R.id.seek_bar);
+        DirectManipulationHelper.setSupportsRotateDirectly(mSeekBar, true);
         mAppBarController = new AppBarController(view);
 
         mAppBarController.setTitle(R.string.fragment_playback_title);
@@ -518,15 +523,13 @@
                 if (useMediaSourceColor) {
                     getPlaybackViewModel().getMediaSourceColors().observe(getViewLifecycleOwner(),
                             sourceColors -> {
-                                int color = sourceColors != null ? sourceColors.getAccentColor(
-                                        defaultColor)
+                                int color = sourceColors != null
+                                        ? sourceColors.getAccentColor(defaultColor)
                                         : defaultColor;
-                                mSeekBar.setThumbTintList(ColorStateList.valueOf(color));
-                                mSeekBar.setProgressTintList(ColorStateList.valueOf(color));
+                                setSeekBarColor(color);
                             });
                 } else {
-                    mSeekBar.setThumbTintList(ColorStateList.valueOf(defaultColor));
-                    mSeekBar.setProgressTintList(ColorStateList.valueOf(defaultColor));
+                    setSeekBarColor(defaultColor);
                 }
             } else {
                 mSeekBar.setVisibility(View.GONE);
@@ -759,6 +762,24 @@
         return MediaSourceViewModel.get(getActivity().getApplication(), MEDIA_SOURCE_MODE_BROWSE);
     }
 
+    private void setSeekBarColor(int color) {
+        mSeekBar.setProgressTintList(ColorStateList.valueOf(color));
+
+        // If the thumb drawable consists of a center drawable, only change the color of the center
+        // drawable. Otherwise change the color of the entire thumb drawable.
+        Drawable thumb = mSeekBar.getThumb();
+        if (thumb instanceof LayerDrawable) {
+            LayerDrawable thumbDrawable = (LayerDrawable) thumb;
+            Drawable thumbCenter = thumbDrawable.findDrawableByLayerId(R.id.thumb_center);
+            if (thumbCenter != null) {
+                thumbCenter.setColorFilter(color, PorterDuff.Mode.SRC);
+                thumbDrawable.setDrawableByLayerId(R.id.thumb_center, thumbCenter);
+                return;
+            }
+        }
+        mSeekBar.setThumbTintList(ColorStateList.valueOf(color));
+    }
+
     /**
      * Sets a listener of this PlaybackFragment events. In order to avoid memory leaks, consumers
      * must reset this reference by setting the listener to null.