1 /* 2 * Copyright (C) 2018 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.settingslib.widget; 18 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.text.TextUtils; 22 import android.util.Log; 23 import android.view.View; 24 import android.widget.Button; 25 import android.widget.ImageView; 26 import android.widget.TextView; 27 28 import androidx.annotation.NonNull; 29 import androidx.annotation.Nullable; 30 import androidx.annotation.StringRes; 31 import androidx.annotation.VisibleForTesting; 32 33 /** 34 * This class is used to initialize view which was inflated 35 * from {@link R.xml.app_entities_header.xml}. 36 * 37 * <p>How to use AppEntitiesHeaderController? 38 * 39 * <p>1. Add a {@link LayoutPreference} in layout XML file. 40 * <pre> 41 * <com.android.settingslib.widget.LayoutPreference 42 * android:key="app_entities_header" 43 * android:layout="@layout/app_entities_header"/> 44 * </pre> 45 * 46 * <p>2. Use AppEntitiesHeaderController to call below methods, then you can initialize 47 * view of <code>app_entities_header</code>. 48 * 49 * <pre> 50 * 51 * View headerView = ((LayoutPreference) screen.findPreference("app_entities_header")) 52 * .findViewById(R.id.app_entities_header); 53 * 54 * final AppEntityInfo appEntityInfo = new AppEntityInfo.Builder() 55 * .setIcon(icon) 56 * .setTitle(title) 57 * .setSummary(summary) 58 * .setOnClickListener(view -> doSomething()) 59 * .build(); 60 * 61 * AppEntitiesHeaderController.newInstance(context, headerView) 62 * .setHeaderTitleRes(R.string.xxxxx) 63 * .setHeaderDetailsRes(R.string.xxxxx) 64 * .setHeaderDetailsClickListener(onClickListener) 65 * .setAppEntity(0, appEntityInfo) 66 * .setAppEntity(1, appEntityInfo) 67 * .setAppEntity(2, appEntityInfo) 68 * .apply(); 69 * </pre> 70 */ 71 public class AppEntitiesHeaderController { 72 73 private static final String TAG = "AppEntitiesHeaderCtl"; 74 75 @VisibleForTesting 76 public static final int MAXIMUM_APPS = 3; 77 78 private final Context mContext; 79 private final TextView mHeaderTitleView; 80 private final TextView mHeaderEmptyView; 81 private final Button mHeaderDetailsView; 82 83 private final AppEntityInfo[] mAppEntityInfos; 84 private final View mAppViewsContainer; 85 private final View[] mAppEntityViews; 86 private final ImageView[] mAppIconViews; 87 private final TextView[] mAppTitleViews; 88 private final TextView[] mAppSummaryViews; 89 90 private int mHeaderTitleRes; 91 private int mHeaderDetailsRes; 92 private int mHeaderEmptyRes; 93 private CharSequence mHeaderDetails; 94 private View.OnClickListener mDetailsOnClickListener; 95 96 /** 97 * Creates a new instance of the controller. 98 * 99 * @param context the Context the view is running in 100 * @param appEntitiesHeaderView view was inflated from <code>app_entities_header</code> 101 */ newInstance(@onNull Context context, @NonNull View appEntitiesHeaderView)102 public static AppEntitiesHeaderController newInstance(@NonNull Context context, 103 @NonNull View appEntitiesHeaderView) { 104 return new AppEntitiesHeaderController(context, appEntitiesHeaderView); 105 } 106 AppEntitiesHeaderController(Context context, View appEntitiesHeaderView)107 private AppEntitiesHeaderController(Context context, View appEntitiesHeaderView) { 108 mContext = context; 109 mHeaderTitleView = appEntitiesHeaderView.findViewById(R.id.header_title); 110 mHeaderDetailsView = appEntitiesHeaderView.findViewById(R.id.header_details); 111 mHeaderEmptyView = appEntitiesHeaderView.findViewById(R.id.empty_view); 112 mAppViewsContainer = appEntitiesHeaderView.findViewById(R.id.app_views_container); 113 114 mAppEntityInfos = new AppEntityInfo[MAXIMUM_APPS]; 115 mAppIconViews = new ImageView[MAXIMUM_APPS]; 116 mAppTitleViews = new TextView[MAXIMUM_APPS]; 117 mAppSummaryViews = new TextView[MAXIMUM_APPS]; 118 119 mAppEntityViews = new View[]{ 120 appEntitiesHeaderView.findViewById(R.id.app1_view), 121 appEntitiesHeaderView.findViewById(R.id.app2_view), 122 appEntitiesHeaderView.findViewById(R.id.app3_view) 123 }; 124 125 // Initialize view in advance, so we won't take too much time to do it when controller is 126 // binding view. 127 for (int index = 0; index < MAXIMUM_APPS; index++) { 128 final View appView = mAppEntityViews[index]; 129 mAppIconViews[index] = (ImageView) appView.findViewById(R.id.app_icon); 130 mAppTitleViews[index] = (TextView) appView.findViewById(R.id.app_title); 131 mAppSummaryViews[index] = (TextView) appView.findViewById(R.id.app_summary); 132 } 133 } 134 135 /** 136 * Set the text resource for app entities header title. 137 */ setHeaderTitleRes(@tringRes int titleRes)138 public AppEntitiesHeaderController setHeaderTitleRes(@StringRes int titleRes) { 139 mHeaderTitleRes = titleRes; 140 return this; 141 } 142 143 /** 144 * Set the text resource for app entities header details. 145 */ setHeaderDetailsRes(@tringRes int detailsRes)146 public AppEntitiesHeaderController setHeaderDetailsRes(@StringRes int detailsRes) { 147 mHeaderDetailsRes = detailsRes; 148 return this; 149 } 150 151 /** 152 * Set the text for app entities header details. 153 */ setHeaderDetails(CharSequence detailsText)154 public AppEntitiesHeaderController setHeaderDetails(CharSequence detailsText) { 155 mHeaderDetails = detailsText; 156 return this; 157 } 158 159 /** 160 * Register a callback to be invoked when header details view is clicked. 161 */ setHeaderDetailsClickListener( @ullable View.OnClickListener clickListener)162 public AppEntitiesHeaderController setHeaderDetailsClickListener( 163 @Nullable View.OnClickListener clickListener) { 164 mDetailsOnClickListener = clickListener; 165 return this; 166 } 167 168 /** 169 * Sets the string resource id for the empty text. 170 */ setHeaderEmptyRes(@tringRes int emptyRes)171 public AppEntitiesHeaderController setHeaderEmptyRes(@StringRes int emptyRes) { 172 mHeaderEmptyRes = emptyRes; 173 return this; 174 } 175 176 /** 177 * Set an app entity at a specified position view. 178 * 179 * @param index the index at which the specified view is to be inserted 180 * @param appEntityInfo the information of an app entity 181 * @return this {@code AppEntitiesHeaderController} object 182 */ setAppEntity(int index, @NonNull AppEntityInfo appEntityInfo)183 public AppEntitiesHeaderController setAppEntity(int index, 184 @NonNull AppEntityInfo appEntityInfo) { 185 mAppEntityInfos[index] = appEntityInfo; 186 return this; 187 } 188 189 /** 190 * Remove an app entity at a specified position view. 191 * 192 * @param index the index at which the specified view is to be removed 193 * @return this {@code AppEntitiesHeaderController} object 194 */ removeAppEntity(int index)195 public AppEntitiesHeaderController removeAppEntity(int index) { 196 mAppEntityInfos[index] = null; 197 return this; 198 } 199 200 /** 201 * Clear all app entities in app entities header. 202 * 203 * @return this {@code AppEntitiesHeaderController} object 204 */ clearAllAppEntities()205 public AppEntitiesHeaderController clearAllAppEntities() { 206 for (int index = 0; index < MAXIMUM_APPS; index++) { 207 removeAppEntity(index); 208 } 209 return this; 210 } 211 212 /** 213 * Done mutating app entities header, rebinds everything. 214 */ apply()215 public void apply() { 216 bindHeaderTitleView(); 217 218 if (isAppEntityInfosEmpty()) { 219 setEmptyViewVisible(true); 220 return; 221 } 222 setEmptyViewVisible(false); 223 bindHeaderDetailsView(); 224 225 // Rebind all apps view 226 for (int index = 0; index < MAXIMUM_APPS; index++) { 227 bindAppEntityView(index); 228 } 229 } 230 bindHeaderTitleView()231 private void bindHeaderTitleView() { 232 CharSequence titleText = ""; 233 try { 234 titleText = mContext.getText(mHeaderTitleRes); 235 } catch (Resources.NotFoundException e) { 236 Log.e(TAG, "Resource of header title can't not be found!", e); 237 } 238 mHeaderTitleView.setText(titleText); 239 mHeaderTitleView.setVisibility( 240 TextUtils.isEmpty(titleText) ? View.GONE : View.VISIBLE); 241 } 242 bindHeaderDetailsView()243 private void bindHeaderDetailsView() { 244 CharSequence detailsText = mHeaderDetails; 245 if (TextUtils.isEmpty(detailsText)) { 246 try { 247 detailsText = mContext.getText(mHeaderDetailsRes); 248 } catch (Resources.NotFoundException e) { 249 Log.e(TAG, "Resource of header details can't not be found!", e); 250 } 251 } 252 mHeaderDetailsView.setText(detailsText); 253 mHeaderDetailsView.setVisibility( 254 TextUtils.isEmpty(detailsText) ? View.GONE : View.VISIBLE); 255 mHeaderDetailsView.setOnClickListener(mDetailsOnClickListener); 256 } 257 bindAppEntityView(int index)258 private void bindAppEntityView(int index) { 259 final AppEntityInfo appEntityInfo = mAppEntityInfos[index]; 260 mAppEntityViews[index].setVisibility(appEntityInfo != null ? View.VISIBLE : View.GONE); 261 262 if (appEntityInfo != null) { 263 mAppEntityViews[index].setOnClickListener(appEntityInfo.getClickListener()); 264 265 mAppIconViews[index].setImageDrawable(appEntityInfo.getIcon()); 266 267 final CharSequence title = appEntityInfo.getTitle(); 268 mAppTitleViews[index].setVisibility( 269 TextUtils.isEmpty(title) ? View.INVISIBLE : View.VISIBLE); 270 mAppTitleViews[index].setText(title); 271 272 final CharSequence summary = appEntityInfo.getSummary(); 273 mAppSummaryViews[index].setVisibility( 274 TextUtils.isEmpty(summary) ? View.INVISIBLE : View.VISIBLE); 275 mAppSummaryViews[index].setText(summary); 276 } 277 } 278 setEmptyViewVisible(boolean visible)279 private void setEmptyViewVisible(boolean visible) { 280 if (mHeaderEmptyRes != 0) { 281 mHeaderEmptyView.setText(mHeaderEmptyRes); 282 } 283 mHeaderEmptyView.setVisibility(visible ? View.VISIBLE : View.GONE); 284 mHeaderDetailsView.setVisibility(visible ? View.GONE : View.VISIBLE); 285 mAppViewsContainer.setVisibility(visible ? View.GONE : View.VISIBLE); 286 } 287 isAppEntityInfosEmpty()288 private boolean isAppEntityInfosEmpty() { 289 for (AppEntityInfo info : mAppEntityInfos) { 290 if (info != null) { 291 return false; 292 } 293 } 294 return true; 295 } 296 } 297