1 /*
2  * Copyright (C) 2021 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 
17 package com.android.settings.homepage;
18 
19 import android.content.DialogInterface;
20 import android.os.Bundle;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.text.TextUtils;
24 import android.util.Log;
25 
26 import androidx.annotation.VisibleForTesting;
27 import androidx.preference.PreferenceScreen;
28 import androidx.recyclerview.widget.RecyclerView;
29 
30 import com.android.settings.SettingsActivity;
31 import com.android.settings.widget.HighlightableTopLevelPreferenceAdapter;
32 
33 /** A highlight mixin for the top level settings fragment. */
34 public class TopLevelHighlightMixin implements Parcelable, DialogInterface.OnShowListener,
35         DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
36 
37     private static final String TAG = "TopLevelHighlightMixin";
38 
39     private String mCurrentKey;
40     // Stores the previous key for the profile select dialog cancel event
41     private String mPreviousKey;
42     // Stores the key hidden for the search page presence
43     private String mHiddenKey;
44     private DialogInterface mDialog;
45     private HighlightableTopLevelPreferenceAdapter mTopLevelAdapter;
46     private boolean mActivityEmbedded;
47 
TopLevelHighlightMixin(boolean activityEmbedded)48     public TopLevelHighlightMixin(boolean activityEmbedded) {
49         mActivityEmbedded = activityEmbedded;
50     }
51 
TopLevelHighlightMixin(Parcel source)52     public TopLevelHighlightMixin(Parcel source) {
53         mCurrentKey = source.readString();
54         mPreviousKey = source.readString();
55         mHiddenKey = source.readString();
56         mActivityEmbedded = source.readBoolean();
57     }
58 
59     @Override
writeToParcel(Parcel dest, int flags)60     public void writeToParcel(Parcel dest, int flags) {
61         dest.writeString(mCurrentKey);
62         dest.writeString(mPreviousKey);
63         dest.writeString(mHiddenKey);
64         dest.writeBoolean(mActivityEmbedded);
65     }
66 
67     @Override
describeContents()68     public int describeContents() {
69         return 0;
70     }
71 
72     public static final Creator<TopLevelHighlightMixin> CREATOR =
73             new Creator<TopLevelHighlightMixin>() {
74                 @Override
75                 public TopLevelHighlightMixin createFromParcel(Parcel source) {
76                     return new TopLevelHighlightMixin(source);
77                 }
78 
79                 @Override
80                 public TopLevelHighlightMixin[] newArray(int size) {
81                     return new TopLevelHighlightMixin[size];
82                 }
83             };
84 
85     @Override
onShow(DialogInterface dialog)86     public void onShow(DialogInterface dialog) {
87         mDialog = dialog;
88     }
89 
90     @Override
onDismiss(DialogInterface dialog)91     public void onDismiss(DialogInterface dialog) {
92         mDialog = null;
93     }
94 
95     @Override
onCancel(DialogInterface dialog)96     public void onCancel(DialogInterface dialog) {
97         if (mTopLevelAdapter != null) {
98             mCurrentKey = mPreviousKey;
99             mPreviousKey = null;
100             mTopLevelAdapter.highlightPreference(mCurrentKey, /* scrollNeeded= */ false);
101         }
102     }
103 
setActivityEmbedded(boolean activityEmbedded)104     void setActivityEmbedded(boolean activityEmbedded) {
105         mActivityEmbedded = activityEmbedded;
106     }
107 
isActivityEmbedded()108     boolean isActivityEmbedded() {
109         return mActivityEmbedded;
110     }
111 
onCreateAdapter(TopLevelSettings topLevelSettings, PreferenceScreen preferenceScreen, boolean scrollNeeded)112     RecyclerView.Adapter onCreateAdapter(TopLevelSettings topLevelSettings,
113             PreferenceScreen preferenceScreen, boolean scrollNeeded) {
114         if (TextUtils.isEmpty(mCurrentKey)) {
115             mCurrentKey = getHighlightPrefKeyFromArguments(topLevelSettings.getArguments());
116         }
117 
118         Log.d(TAG, "onCreateAdapter, pref key: " + mCurrentKey);
119 
120         // Remove the animator to avoid a RecyclerView crash.
121         RecyclerView recyclerView = topLevelSettings.getListView();
122         recyclerView.setItemAnimator(null);
123 
124         mTopLevelAdapter = new HighlightableTopLevelPreferenceAdapter(
125                 (SettingsHomepageActivity) topLevelSettings.getActivity(), preferenceScreen,
126                 recyclerView, mCurrentKey, scrollNeeded);
127         return mTopLevelAdapter;
128     }
129 
reloadHighlightMenuKey(Bundle arguments)130     void reloadHighlightMenuKey(Bundle arguments) {
131         if (mTopLevelAdapter == null) {
132             return;
133         }
134         ensureDialogDismissed();
135 
136         mCurrentKey = getHighlightPrefKeyFromArguments(arguments);
137         Log.d(TAG, "reloadHighlightMenuKey, pref key: " + mCurrentKey);
138         mTopLevelAdapter.highlightPreference(mCurrentKey, /* scrollNeeded= */ true);
139     }
140 
setHighlightPreferenceKey(String prefKey)141     void setHighlightPreferenceKey(String prefKey) {
142         if (mTopLevelAdapter != null) {
143             ensureDialogDismissed();
144             mPreviousKey = mCurrentKey;
145             mCurrentKey = prefKey;
146             mTopLevelAdapter.highlightPreference(prefKey, /* scrollNeeded= */ false);
147         }
148     }
149 
150     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
getHighlightPreferenceKey()151     public String getHighlightPreferenceKey() {
152         return mCurrentKey;
153     }
154 
highlightPreferenceIfNeeded()155     void highlightPreferenceIfNeeded() {
156         if (mTopLevelAdapter != null) {
157             mTopLevelAdapter.requestHighlight();
158         }
159     }
160 
setMenuHighlightShowed(boolean show)161     void setMenuHighlightShowed(boolean show) {
162         if (mTopLevelAdapter == null) {
163             return;
164         }
165         ensureDialogDismissed();
166 
167         if (show) {
168             mCurrentKey = mHiddenKey;
169             mHiddenKey = null;
170         } else {
171             if (mHiddenKey == null) {
172                 mHiddenKey = mCurrentKey;
173             }
174             mCurrentKey = null;
175         }
176         mTopLevelAdapter.highlightPreference(mCurrentKey, /* scrollNeeded= */ show);
177     }
178 
setHighlightMenuKey(String menuKey, boolean scrollNeeded)179     void setHighlightMenuKey(String menuKey, boolean scrollNeeded) {
180         if (mTopLevelAdapter == null) {
181             return;
182         }
183         ensureDialogDismissed();
184 
185         final String prefKey = HighlightableMenu.lookupPreferenceKey(menuKey);
186         if (TextUtils.isEmpty(prefKey)) {
187             Log.e(TAG, "Invalid highlight menu key: " + menuKey);
188         } else {
189             Log.d(TAG, "Menu key: " + menuKey);
190             mCurrentKey = prefKey;
191             mTopLevelAdapter.highlightPreference(prefKey, scrollNeeded);
192         }
193     }
194 
getHighlightPrefKeyFromArguments(Bundle arguments)195     private static String getHighlightPrefKeyFromArguments(Bundle arguments) {
196         final String menuKey = arguments.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY);
197         final String prefKey = HighlightableMenu.lookupPreferenceKey(menuKey);
198         if (TextUtils.isEmpty(prefKey)) {
199             Log.e(TAG, "Invalid highlight menu key: " + menuKey);
200         } else {
201             Log.d(TAG, "Menu key: " + menuKey);
202         }
203         return prefKey;
204     }
205 
ensureDialogDismissed()206     private void ensureDialogDismissed() {
207         if (mDialog != null) {
208             onCancel(mDialog);
209             mDialog.dismiss();
210         }
211     }
212 }
213