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 ActionType.ACTION_DND_RULE_PREFERENCE,})
68 @Retention(RetentionPolicy.SOURCE)
69 public @interface ActionType {
71 int ACTION_APP_PREFERENCE = 1;
72 int ACTION_NOTIF_PREFERENCE = 2;
73 int ACTION_DND_RULE_PREFERENCE = 3;
76 public static final String PREF_KEY_APP_HEADER = "pref_app_header";
78 private static final String TAG = "AppDetailFeature";
80 private final Context mAppContext;
81 private final Activity mActivity;
82 private final Fragment mFragment;
83 private final int mMetricsCategory;
84 private final View mHeader;
85 private Lifecycle mLifecycle;
86 private RecyclerView mRecyclerView;
87 private Drawable mIcon;
88 private String mIconContentDescription;
89 private CharSequence mLabel;
90 private CharSequence mSummary;
91 private String mPackageName;
92 private Intent mAppNotifPrefIntent;
94 private int mUid = UserHandle.USER_NULL;
100 private boolean mHasAppInfoLink;
102 private boolean mIsInstantApp;
104 private View.OnClickListener mEditRuleNameOnClickListener;
107 * Creates a new instance of the controller.
109 * @param fragment The fragment that header will be placed in.
110 * @param header Optional: header view if it's already created.
112 public static EntityHeaderController newInstance(Activity activity, Fragment fragment,
114 return new EntityHeaderController(activity, fragment, header);
117 private EntityHeaderController(Activity activity, Fragment fragment, View header) {
118 mActivity = activity;
119 mAppContext = activity.getApplicationContext();
120 mFragment = fragment;
121 mMetricsCategory = FeatureFactory.getFactory(mAppContext).getMetricsFeatureProvider()
122 .getMetricsCategory(fragment);
123 if (header != null) {
126 mHeader = LayoutInflater.from(fragment.getContext())
127 .inflate(R.layout.settings_entity_header, null /* root */);
131 public EntityHeaderController setRecyclerView(RecyclerView recyclerView, Lifecycle lifecycle) {
132 mRecyclerView = recyclerView;
133 mLifecycle = lifecycle;
138 * Set the icon in the header. Callers should also consider calling setIconContentDescription
139 * to provide a description of this icon for accessibility purposes.
141 public EntityHeaderController setIcon(Drawable icon) {
143 mIcon = icon.getConstantState().newDrawable(mAppContext.getResources());
149 * Convenience method to set the header icon from an ApplicationsState.AppEntry. Callers should
150 * also consider calling setIconContentDescription to provide a description of this icon for
151 * accessibility purposes.
153 public EntityHeaderController setIcon(ApplicationsState.AppEntry appEntry) {
154 if (appEntry.icon != null) {
155 mIcon = appEntry.icon.getConstantState().newDrawable(mAppContext.getResources());
160 public EntityHeaderController setIconContentDescription(String contentDescription) {
161 mIconContentDescription = contentDescription;
165 public EntityHeaderController setLabel(CharSequence label) {
170 public EntityHeaderController setLabel(ApplicationsState.AppEntry appEntry) {
171 mLabel = appEntry.label;
175 public EntityHeaderController setSummary(CharSequence summary) {
180 public EntityHeaderController setSummary(PackageInfo packageInfo) {
181 if (packageInfo != null) {
182 mSummary = packageInfo.versionName;
187 public EntityHeaderController setHasAppInfoLink(boolean hasAppInfoLink) {
188 mHasAppInfoLink = hasAppInfoLink;
192 public EntityHeaderController setButtonActions(@ActionType int action1,
193 @ActionType int action2) {
199 public EntityHeaderController setPackageName(String packageName) {
200 mPackageName = packageName;
204 public EntityHeaderController setUid(int uid) {
209 public EntityHeaderController setAppNotifPrefIntent(Intent appNotifPrefIntent) {
210 mAppNotifPrefIntent = appNotifPrefIntent;
214 public EntityHeaderController setIsInstantApp(boolean isInstantApp) {
215 this.mIsInstantApp = isInstantApp;
219 public EntityHeaderController setEditZenRuleNameListener(View.OnClickListener listener) {
220 this.mEditRuleNameOnClickListener = listener;
225 * Done mutating entity header, rebinds everything and return a new {@link LayoutPreference}.
227 public LayoutPreference done(Activity activity, Context uiContext) {
228 final LayoutPreference pref = new LayoutPreference(uiContext, done(activity));
229 // Makes sure it's the first preference onscreen.
230 pref.setOrder(-1000);
231 pref.setSelectable(false);
232 pref.setKey(PREF_KEY_APP_HEADER);
237 * Done mutating entity header, rebinds everything (optionally skip rebinding buttons).
239 public View done(Activity activity, boolean rebindActions) {
240 styleActionBar(activity);
241 ImageView iconView = mHeader.findViewById(R.id.entity_header_icon);
242 if (iconView != null) {
243 iconView.setImageDrawable(mIcon);
244 iconView.setContentDescription(mIconContentDescription);
246 setText(R.id.entity_header_title, mLabel);
247 setText(R.id.entity_header_summary, mSummary);
249 setText(R.id.install_type,
250 mHeader.getResources().getString(R.string.install_type_instant));
261 * Only binds entity header with button actions.
263 public EntityHeaderController bindHeaderButtons() {
264 final View entityHeaderContent = mHeader.findViewById(R.id.entity_header_content);
265 final ImageButton button1 = mHeader.findViewById(android.R.id.button1);
266 final ImageButton button2 = mHeader.findViewById(android.R.id.button2);
267 bindAppInfoLink(entityHeaderContent);
268 bindButton(button1, mAction1);
269 bindButton(button2, mAction2);
273 private void bindAppInfoLink(View entityHeaderContent) {
274 if (!mHasAppInfoLink) {
275 // Caller didn't ask for app link, skip.
278 if (entityHeaderContent == null
279 || mPackageName == null
280 || mPackageName.equals(Utils.OS_PKG)
281 || mUid == UserHandle.USER_NULL) {
282 Log.w(TAG, "Missing ingredients to build app info link, skip");
285 entityHeaderContent.setOnClickListener(new View.OnClickListener() {
287 public void onClick(View v) {
288 if (FeatureFlagUtils.isEnabled(mAppContext, FeatureFlags.APP_INFO_V2)) {
289 AppInfoBase.startAppInfoFragment(
290 AppInfoDashboardFragment.class, R.string.application_info_label,
291 mPackageName, mUid, mFragment, 0 /* request */,
294 AppInfoBase.startAppInfoFragment(
295 InstalledAppDetails.class, R.string.application_info_label,
296 mPackageName, mUid, mFragment, 0 /* request */,
305 * Styles the action bar (elevation, scrolling behaviors, color, etc).
307 * This method must be called after {@link Fragment#onCreate(Bundle)}.
309 public EntityHeaderController styleActionBar(Activity activity) {
310 if (activity == null) {
311 Log.w(TAG, "No activity, cannot style actionbar.");
314 final ActionBar actionBar = activity.getActionBar();
315 if (actionBar == null) {
316 Log.w(TAG, "No actionbar, cannot style actionbar.");
319 actionBar.setBackgroundDrawable(
320 new ColorDrawable(Utils.getColorAttr(activity, android.R.attr.colorSecondary)));
321 actionBar.setElevation(0);
322 if (mRecyclerView != null && mLifecycle != null) {
323 ActionBarShadowController.attachToRecyclerView(mActivity, mLifecycle, mRecyclerView);
330 * Done mutating entity header, rebinds everything.
333 View done(Activity activity) {
334 return done(activity, true /* rebindActions */);
337 private void bindButton(ImageButton button, @ActionType int action) {
338 if (button == null) {
342 case ActionType.ACTION_DND_RULE_PREFERENCE: {
343 if (mEditRuleNameOnClickListener == null) {
344 button.setVisibility(View.GONE);
346 button.setImageResource(R.drawable.ic_mode_edit);
347 button.setVisibility(View.VISIBLE);
348 button.setOnClickListener(mEditRuleNameOnClickListener);
352 case ActionType.ACTION_NOTIF_PREFERENCE: {
353 if (mAppNotifPrefIntent == null) {
354 button.setVisibility(View.GONE);
356 button.setOnClickListener(new View.OnClickListener() {
358 public void onClick(View v) {
359 FeatureFactory.getFactory(mAppContext).getMetricsFeatureProvider()
360 .actionWithSource(mAppContext, mMetricsCategory,
361 ACTION_OPEN_APP_NOTIFICATION_SETTING);
362 mFragment.startActivity(mAppNotifPrefIntent);
365 button.setVisibility(View.VISIBLE);
369 case ActionType.ACTION_APP_PREFERENCE: {
370 final Intent intent = resolveIntent(
371 new Intent(Intent.ACTION_APPLICATION_PREFERENCES).setPackage(mPackageName));
372 if (intent == null) {
373 button.setImageDrawable(null);
374 button.setVisibility(View.GONE);
377 button.setOnClickListener(new View.OnClickListener() {
379 public void onClick(View v) {
380 FeatureFactory.getFactory(mAppContext).getMetricsFeatureProvider()
381 .actionWithSource(mAppContext, mMetricsCategory,
382 ACTION_OPEN_APP_SETTING);
383 mFragment.startActivity(intent);
386 button.setImageResource(R.drawable.ic_settings_24dp);
387 button.setVisibility(View.VISIBLE);
390 case ActionType.ACTION_NONE: {
391 button.setVisibility(View.GONE);
397 private Intent resolveIntent(Intent i) {
398 ResolveInfo result = mAppContext.getPackageManager().resolveActivity(i, 0);
399 if (result != null) {
400 return new Intent(i.getAction())
401 .setClassName(result.activityInfo.packageName, result.activityInfo.name);
406 private void setText(@IdRes int id, CharSequence text) {
407 TextView textView = mHeader.findViewById(id);
408 if (textView != null) {
409 textView.setText(text);
410 textView.setVisibility(TextUtils.isEmpty(text) ? View.GONE : View.VISIBLE);