Highlight row (instead of ripple) when come from search.

- Moved HighlightableAdapter to its own class
- Replaced ripple with blue background
- Bluebackground only stays for 5 seconds. After that it's replaced by
  ?attr/selectableBackground

Misc fixes.

- Fix NPE in UserSettings
- Update char limit on a new string

Change-Id: I4687e54e71fd7b9243f520b7630563df58be23d4
Fixes: 71715698
Fixes: 72761974
Test: robotests
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index c5d477a..2a593c2 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -30,9 +30,7 @@
 import android.support.annotation.XmlRes;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
-import android.support.v7.preference.PreferenceGroupAdapter;
 import android.support.v7.preference.PreferenceScreen;
-import android.support.v7.preference.PreferenceViewHolder;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
@@ -49,6 +47,7 @@
 import com.android.settings.search.actionbar.SearchMenuController;
 import com.android.settings.support.actionbar.HelpMenuController;
 import com.android.settings.support.actionbar.HelpResourceProvider;
+import com.android.settings.widget.HighlightablePreferenceGroupAdapter;
 import com.android.settings.widget.LoadingViewController;
 import com.android.settingslib.CustomDialogPreference;
 import com.android.settingslib.CustomEditTextPreference;
@@ -65,9 +64,6 @@
 
     private static final String TAG = "SettingsPreference";
 
-    @VisibleForTesting
-    static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
-
     private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
 
     protected final FooterPreferenceMixin mFooterPreferenceMixin =
@@ -75,14 +71,11 @@
 
 
     private static final int ORDER_FIRST = -1;
-    private static final int ORDER_LAST = Integer.MAX_VALUE -1;
 
     private SettingsDialogFragment mDialogFragment;
     // Cache the content resolver for async callbacks
     private ContentResolver mContentResolver;
 
-    private String mPreferenceKey;
-
     private RecyclerView.Adapter mCurrentRootAdapter;
     private boolean mIsDataSetObserverRegistered = false;
     private RecyclerView.AdapterDataObserver mDataSetObserver =
@@ -146,8 +139,9 @@
 
         // Check if we should keep the preferences expanded.
         if (arguments != null) {
-            mPreferenceKey = arguments.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY);
-            if (!TextUtils.isEmpty(mPreferenceKey)) {
+            final String highlightKey =
+                    arguments.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY);
+            if (!TextUtils.isEmpty(highlightKey)) {
                 final PreferenceScreen screen = getPreferenceScreen();
                 if (screen != null) {
                     screen.setInitialExpandedChildrenCount(Integer.MAX_VALUE);
@@ -205,7 +199,9 @@
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
 
-        outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
+        if (mAdapter != null) {
+            outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mAdapter.isHighlightRequested());
+        }
     }
 
     @Override
@@ -217,10 +213,7 @@
     @Override
     public void onResume() {
         super.onResume();
-
-        if (mPreferenceKey != null) {
-            highlightPreferenceIfNeeded();
-        }
+        highlightPreferenceIfNeeded();
     }
 
     @Override
@@ -263,13 +256,11 @@
     }
 
     public void highlightPreferenceIfNeeded() {
-        if (isAdded() && !mPreferenceHighlighted &&!TextUtils.isEmpty(mPreferenceKey)) {
-            getView().postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    highlightPreference(mPreferenceKey);
-                }
-            }, DELAY_HIGHLIGHT_DURATION_MILLIS);
+        if (!isAdded()) {
+            return;
+        }
+        if (mAdapter != null) {
+            mAdapter.requestHighlight(getView(), getListView());
         }
     }
 
@@ -340,24 +331,6 @@
         return mEmptyView;
     }
 
-    /**
-     * Return a valid ListView position or -1 if none is found
-     */
-    private int canUseListViewForHighLighting(String key) {
-        if (getListView() == null) {
-            return -1;
-        }
-
-        RecyclerView listView = getListView();
-        RecyclerView.Adapter adapter = listView.getAdapter();
-
-        if (adapter != null && adapter instanceof PreferenceGroupAdapter) {
-            return findListPositionFromKey((PreferenceGroupAdapter) adapter, key);
-        }
-
-        return -1;
-    }
-
     @Override
     public RecyclerView.LayoutManager onCreateLayoutManager() {
         mLayoutManager = new LinearLayoutManager(getContext());
@@ -366,7 +339,9 @@
 
     @Override
     protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
-        mAdapter = new HighlightablePreferenceGroupAdapter(preferenceScreen);
+        mAdapter = new HighlightablePreferenceGroupAdapter(preferenceScreen,
+                getArguments().getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY),
+                mPreferenceHighlighted);
         return mAdapter;
     }
 
@@ -375,7 +350,7 @@
     }
 
     protected void cacheRemoveAllPrefs(PreferenceGroup group) {
-        mPreferenceCache = new ArrayMap<String, Preference>();
+        mPreferenceCache = new ArrayMap<>();
         final int N = group.getPreferenceCount();
         for (int i = 0; i < N; i++) {
             Preference p = group.getPreference(i);
@@ -401,29 +376,6 @@
         return mPreferenceCache != null ? mPreferenceCache.size() : 0;
     }
 
-    private void highlightPreference(String key) {
-        final int position = canUseListViewForHighLighting(key);
-        if (position < 0) {
-            return;
-        }
-
-        mPreferenceHighlighted = true;
-        mLayoutManager.scrollToPosition(position);
-        mAdapter.highlight(position);
-    }
-
-    private int findListPositionFromKey(PreferenceGroupAdapter adapter, String key) {
-        final int count = adapter.getItemCount();
-        for (int n = 0; n < count; n++) {
-            final Preference preference = adapter.getItem(n);
-            final String preferenceKey = preference.getKey();
-            if (preferenceKey != null && preferenceKey.equals(key)) {
-                return n;
-            }
-        }
-        return -1;
-    }
-
     protected boolean removePreference(String key) {
         return removePreference(getPreferenceScreen(), key);
     }
@@ -692,11 +644,11 @@
     }
 
     protected boolean hasNextButton() {
-        return ((ButtonBarHandler)getActivity()).hasNextButton();
+        return ((ButtonBarHandler) getActivity()).hasNextButton();
     }
 
     protected Button getNextButton() {
-        return ((ButtonBarHandler)getActivity()).getNextButton();
+        return ((ButtonBarHandler) getActivity()).getNextButton();
     }
 
     public void finish() {
@@ -741,45 +693,10 @@
         } else {
             Log.w(TAG,
                     "Parent isn't SettingsActivity nor PreferenceActivity, thus there's no way to "
-                    + "launch the given Fragment (name: " + fragmentClass
-                    + ", requestCode: " + requestCode + ")");
+                            + "launch the given Fragment (name: " + fragmentClass
+                            + ", requestCode: " + requestCode + ")");
             return false;
         }
     }
 
-    public static class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter {
-
-        @VisibleForTesting(otherwise=VisibleForTesting.NONE)
-        int initialHighlightedPosition = -1;
-
-        private int mHighlightPosition = -1;
-
-        public HighlightablePreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
-            super(preferenceGroup);
-        }
-
-        public void highlight(int position) {
-            mHighlightPosition = position;
-            initialHighlightedPosition = position;
-            notifyDataSetChanged();
-        }
-
-        @Override
-        public void onBindViewHolder(PreferenceViewHolder holder, int position) {
-            super.onBindViewHolder(holder, position);
-            if (position == mHighlightPosition) {
-                View v = holder.itemView;
-                v.post(() -> {
-                    if (v.getBackground() != null) {
-                        final int centerX = v.getWidth() / 2;
-                        final int centerY = v.getHeight() / 2;
-                        v.getBackground().setHotspot(centerX, centerY);
-                    }
-                    v.setPressed(true);
-                    v.setPressed(false);
-                    mHighlightPosition = -1;
-                });
-            }
-        }
-    }
 }
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index fcb8aef..c8c4c25 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -309,7 +309,7 @@
     public void onDestroy() {
         super.onDestroy();
 
-        if (!mUserCaps.mEnabled) {
+        if (mUserCaps == null || !mUserCaps.mEnabled) {
             return;
         }
 
diff --git a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
new file mode 100644
index 0000000..e1999ef
--- /dev/null
+++ b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 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.settings.widget;
+
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceGroupAdapter;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.util.TypedValue;
+import android.view.View;
+
+import com.android.settings.R;
+
+public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter {
+
+    @VisibleForTesting
+    static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 600L;
+    private static final long HIGHLIGHT_DURATION = 5000L;
+
+    private final int mHighlightColor;
+    private final int mNormalBackgroundRes;
+    private final String mHighlightKey;
+
+    private boolean mHighlightRequested;
+    private int mHighlightPosition = RecyclerView.NO_POSITION;
+
+    public HighlightablePreferenceGroupAdapter(PreferenceGroup preferenceGroup, String key,
+            boolean highlightRequested) {
+        super(preferenceGroup);
+        mHighlightKey = key;
+        mHighlightRequested = highlightRequested;
+        final Context context = preferenceGroup.getContext();
+        final TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+                outValue, true /* resolveRefs */);
+        mNormalBackgroundRes = outValue.resourceId;
+        mHighlightColor = context.getColor(R.color.preference_highligh_color);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder, int position) {
+        super.onBindViewHolder(holder, position);
+        updateBackground(holder, position);
+    }
+
+    @VisibleForTesting
+    void updateBackground(PreferenceViewHolder holder, int position) {
+        View v = holder.itemView;
+        if (position == mHighlightPosition) {
+            v.setBackgroundColor(mHighlightColor);
+            v.setTag(R.id.preference_highlighted, true);
+            v.postDelayed(() -> {
+                mHighlightPosition = RecyclerView.NO_POSITION;
+                removeHighlightBackground(v);
+            }, HIGHLIGHT_DURATION);
+        } else if (Boolean.TRUE.equals(v.getTag(R.id.preference_highlighted))) {
+            removeHighlightBackground(v);
+        }
+    }
+
+    public void requestHighlight(View root, RecyclerView recyclerView) {
+        if (mHighlightRequested || recyclerView == null || TextUtils.isEmpty(mHighlightKey)) {
+            return;
+        }
+        root.postDelayed(() -> {
+            final int position = getPreferenceAdapterPosition(mHighlightKey);
+            if (position < 0) {
+                return;
+            }
+            mHighlightRequested = true;
+            recyclerView.getLayoutManager().scrollToPosition(position);
+            mHighlightPosition = position;
+            notifyItemChanged(position);
+        }, DELAY_HIGHLIGHT_DURATION_MILLIS);
+    }
+
+    public boolean isHighlightRequested() {
+        return mHighlightRequested;
+    }
+
+    private void removeHighlightBackground(View v) {
+        v.setBackgroundResource(mNormalBackgroundRes);
+        v.setTag(R.id.preference_highlighted, false);
+    }
+}