2 * Copyright (C) 2016 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.widget;
19 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent
20 .ACTION_OPEN_APP_NOTIFICATION_SETTING;
21 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_OPEN_APP_SETTING;
23 import android.annotation.IdRes;
24 import android.annotation.UserIdInt;
25 import android.app.ActionBar;
26 import android.app.Activity;
27 import android.app.Fragment;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.pm.PackageInfo;
31 import android.content.pm.ResolveInfo;
32 import android.graphics.drawable.ColorDrawable;
33 import android.graphics.drawable.Drawable;
34 import android.os.Bundle;
35 import android.os.UserHandle;
36 import android.support.annotation.IntDef;
37 import android.support.annotation.VisibleForTesting;
38 import android.support.v7.widget.RecyclerView;
39 import android.text.TextUtils;
40 import android.util.FeatureFlagUtils;
41 import android.util.Log;
42 import android.view.LayoutInflater;
43 import android.view.View;
44 import android.widget.ImageButton;
45 import android.widget.ImageView;
46 import android.widget.TextView;
48 import com.android.settings.R;
49 import com.android.settings.Utils;
50 import com.android.settings.applications.AppInfoBase;
51 import com.android.settings.applications.InstalledAppDetails;
52 import com.android.settings.applications.LayoutPreference;
53 import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
54 import com.android.settings.core.FeatureFlags;
55 import com.android.settings.overlay.FeatureFactory;
56 import com.android.settingslib.applications.ApplicationsState;
57 import com.android.settingslib.core.lifecycle.Lifecycle;
59 import java.lang.annotation.Retention;
60 import java.lang.annotation.RetentionPolicy;
62 public class EntityHeaderController {
64 @IntDef({ActionType.ACTION_NONE,
65 ActionType.ACTION_APP_PREFERENCE,
66 ActionType.ACTION_NOTIF_PREFERENCE})
67 @Retention(RetentionPolicy.SOURCE)
68 public @interface ActionType {
70 int ACTION_APP_PREFERENCE = 1;
71 int ACTION_NOTIF_PREFERENCE = 2;
74 public static final String PREF_KEY_APP_HEADER = "pref_app_header";
76 private static final String TAG = "AppDetailFeature";
78 private final Context mAppContext;
79 private final Activity mActivity;
80 private final Fragment mFragment;
81 private final int mMetricsCategory;
82 private final View mHeader;
83 private Lifecycle mLifecycle;
84 private RecyclerView mRecyclerView;
85 private Drawable mIcon;
86 private String mIconContentDescription;
87 private CharSequence mLabel;
88 private CharSequence mSummary;
89 private String mPackageName;
90 private Intent mAppNotifPrefIntent;
92 private int mUid = UserHandle.USER_NULL;
98 private boolean mHasAppInfoLink;
100 private boolean mIsInstantApp;
103 * Creates a new instance of the controller.
105 * @param fragment The fragment that header will be placed in.
106 * @param header Optional: header view if it's already created.
108 public static EntityHeaderController newInstance(Activity activity, Fragment fragment,
110 return new EntityHeaderController(activity, fragment, header);
113 private EntityHeaderController(Activity activity, Fragment fragment, View header) {
114 mActivity = activity;
115 mAppContext = activity.getApplicationContext();
116 mFragment = fragment;
117 mMetricsCategory = FeatureFactory.getFactory(mAppContext).getMetricsFeatureProvider()
118 .getMetricsCategory(fragment);
119 if (header != null) {
122 mHeader = LayoutInflater.from(fragment.getContext())
123 .inflate(R.layout.settings_entity_header, null /* root */);
127 public EntityHeaderController setRecyclerView(RecyclerView recyclerView, Lifecycle lifecycle) {
128 mRecyclerView = recyclerView;
129 mLifecycle = lifecycle;
134 * Set the icon in the header. Callers should also consider calling setIconContentDescription
135 * to provide a description of this icon for accessibility purposes.
137 public EntityHeaderController setIcon(Drawable icon) {
139 mIcon = icon.getConstantState().newDrawable(mAppContext.getResources());
145 * Convenience method to set the header icon from an ApplicationsState.AppEntry. Callers should
146 * also consider calling setIconContentDescription to provide a description of this icon for
147 * accessibility purposes.
149 public EntityHeaderController setIcon(ApplicationsState.AppEntry appEntry) {
150 if (appEntry.icon != null) {
151 mIcon = appEntry.icon.getConstantState().newDrawable(mAppContext.getResources());
156 public EntityHeaderController setIconContentDescription(String contentDescription) {
157 mIconContentDescription = contentDescription;
161 public EntityHeaderController setLabel(CharSequence label) {
166 public EntityHeaderController setLabel(ApplicationsState.AppEntry appEntry) {
167 mLabel = appEntry.label;
171 public EntityHeaderController setSummary(CharSequence summary) {
176 public EntityHeaderController setSummary(PackageInfo packageInfo) {
177 if (packageInfo != null) {
178 mSummary = packageInfo.versionName;
183 public EntityHeaderController setHasAppInfoLink(boolean hasAppInfoLink) {
184 mHasAppInfoLink = hasAppInfoLink;
188 public EntityHeaderController setButtonActions(@ActionType int action1,
189 @ActionType int action2) {
195 public EntityHeaderController setPackageName(String packageName) {
196 mPackageName = packageName;
200 public EntityHeaderController setUid(int uid) {
205 public EntityHeaderController setAppNotifPrefIntent(Intent appNotifPrefIntent) {
206 mAppNotifPrefIntent = appNotifPrefIntent;
210 public EntityHeaderController setIsInstantApp(boolean isInstantApp) {
211 this.mIsInstantApp = isInstantApp;
216 * Done mutating entity header, rebinds everything and return a new {@link LayoutPreference}.
218 public LayoutPreference done(Activity activity, Context uiContext) {
219 final LayoutPreference pref = new LayoutPreference(uiContext, done(activity));
220 // Makes sure it's the first preference onscreen.
221 pref.setOrder(-1000);
222 pref.setSelectable(false);
223 pref.setKey(PREF_KEY_APP_HEADER);
228 * Done mutating entity header, rebinds everything (optionally skip rebinding buttons).
230 public View done(Activity activity, boolean rebindActions) {
231 styleActionBar(activity);
232 ImageView iconView = mHeader.findViewById(R.id.entity_header_icon);
233 if (iconView != null) {
234 iconView.setImageDrawable(mIcon);
235 iconView.setContentDescription(mIconContentDescription);
237 setText(R.id.entity_header_title, mLabel);
238 setText(R.id.entity_header_summary, mSummary);
240 setText(R.id.install_type,
241 mHeader.getResources().getString(R.string.install_type_instant));
252 * Only binds entity header with button actions.
254 public EntityHeaderController bindHeaderButtons() {
255 final View entityHeaderContent = mHeader.findViewById(R.id.entity_header_content);
256 final ImageButton button1 = mHeader.findViewById(android.R.id.button1);
257 final ImageButton button2 = mHeader.findViewById(android.R.id.button2);
258 bindAppInfoLink(entityHeaderContent);
259 bindButton(button1, mAction1);
260 bindButton(button2, mAction2);
264 private void bindAppInfoLink(View entityHeaderContent) {
265 if (!mHasAppInfoLink) {
266 // Caller didn't ask for app link, skip.
269 if (entityHeaderContent == null
270 || mPackageName == null
271 || mPackageName.equals(Utils.OS_PKG)
272 || mUid == UserHandle.USER_NULL) {
273 Log.w(TAG, "Missing ingredients to build app info link, skip");
276 entityHeaderContent.setOnClickListener(new View.OnClickListener() {
278 public void onClick(View v) {
279 if (FeatureFlagUtils.isEnabled(mAppContext, FeatureFlags.APP_INFO_V2)) {
280 AppInfoBase.startAppInfoFragment(
281 AppInfoDashboardFragment.class, R.string.application_info_label,
282 mPackageName, mUid, mFragment, 0 /* request */,
285 AppInfoBase.startAppInfoFragment(
286 InstalledAppDetails.class, R.string.application_info_label,
287 mPackageName, mUid, mFragment, 0 /* request */,
296 * Styles the action bar (elevation, scrolling behaviors, color, etc).
298 * This method must be called after {@link Fragment#onCreate(Bundle)}.
300 public EntityHeaderController styleActionBar(Activity activity) {
301 if (activity == null) {
302 Log.w(TAG, "No activity, cannot style actionbar.");
305 final ActionBar actionBar = activity.getActionBar();
306 if (actionBar == null) {
307 Log.w(TAG, "No actionbar, cannot style actionbar.");
310 actionBar.setBackgroundDrawable(
311 new ColorDrawable(Utils.getColorAttr(activity, android.R.attr.colorSecondary)));
312 actionBar.setElevation(0);
313 if (mRecyclerView != null && mLifecycle != null) {
314 ActionBarShadowController.attachToRecyclerView(mActivity, mLifecycle, mRecyclerView);
321 * Done mutating entity header, rebinds everything.
324 View done(Activity activity) {
325 return done(activity, true /* rebindActions */);
328 private void bindButton(ImageButton button, @ActionType int action) {
329 if (button == null) {
333 case ActionType.ACTION_NOTIF_PREFERENCE: {
334 if (mAppNotifPrefIntent == null) {
335 button.setVisibility(View.GONE);
337 button.setOnClickListener(new View.OnClickListener() {
339 public void onClick(View v) {
340 FeatureFactory.getFactory(mAppContext).getMetricsFeatureProvider()
341 .actionWithSource(mAppContext, mMetricsCategory,
342 ACTION_OPEN_APP_NOTIFICATION_SETTING);
343 mFragment.startActivity(mAppNotifPrefIntent);
346 button.setVisibility(View.VISIBLE);
350 case ActionType.ACTION_APP_PREFERENCE: {
351 final Intent intent = resolveIntent(
352 new Intent(Intent.ACTION_APPLICATION_PREFERENCES).setPackage(mPackageName));
353 if (intent == null) {
354 button.setImageDrawable(null);
355 button.setVisibility(View.GONE);
358 button.setOnClickListener(new View.OnClickListener() {
360 public void onClick(View v) {
361 FeatureFactory.getFactory(mAppContext).getMetricsFeatureProvider()
362 .actionWithSource(mAppContext, mMetricsCategory,
363 ACTION_OPEN_APP_SETTING);
364 mFragment.startActivity(intent);
367 button.setImageResource(R.drawable.ic_settings_24dp);
368 button.setVisibility(View.VISIBLE);
371 case ActionType.ACTION_NONE: {
372 button.setVisibility(View.GONE);
378 private Intent resolveIntent(Intent i) {
379 ResolveInfo result = mAppContext.getPackageManager().resolveActivity(i, 0);
380 if (result != null) {
381 return new Intent(i.getAction())
382 .setClassName(result.activityInfo.packageName, result.activityInfo.name);
387 private void setText(@IdRes int id, CharSequence text) {
388 TextView textView = mHeader.findViewById(id);
389 if (textView != null) {
390 textView.setText(text);
391 textView.setVisibility(TextUtils.isEmpty(text) ? View.GONE : View.VISIBLE);