• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.widget;
18 
19 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent
20         .ACTION_OPEN_APP_NOTIFICATION_SETTING;
21 
22 import android.annotation.IdRes;
23 import android.annotation.UserIdInt;
24 import android.app.ActionBar;
25 import android.app.Activity;
26 import android.app.Fragment;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.pm.PackageInfo;
30 import android.graphics.drawable.ColorDrawable;
31 import android.graphics.drawable.Drawable;
32 import android.os.Bundle;
33 import android.os.UserHandle;
34 import android.support.annotation.IntDef;
35 import android.support.annotation.VisibleForTesting;
36 import android.support.v7.widget.RecyclerView;
37 import android.text.TextUtils;
38 import android.util.IconDrawableFactory;
39 import android.util.Log;
40 import android.view.LayoutInflater;
41 import android.view.View;
42 import android.widget.ImageButton;
43 import android.widget.ImageView;
44 import android.widget.TextView;
45 
46 import com.android.settings.R;
47 import com.android.settings.Utils;
48 import com.android.settings.applications.AppInfoBase;
49 import com.android.settings.applications.LayoutPreference;
50 import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
51 import com.android.settings.overlay.FeatureFactory;
52 import com.android.settingslib.applications.ApplicationsState;
53 import com.android.settingslib.core.lifecycle.Lifecycle;
54 
55 import java.lang.annotation.Retention;
56 import java.lang.annotation.RetentionPolicy;
57 
58 public class EntityHeaderController {
59 
60     @IntDef({ActionType.ACTION_NONE,
61             ActionType.ACTION_NOTIF_PREFERENCE,
62             ActionType.ACTION_DND_RULE_PREFERENCE,})
63     @Retention(RetentionPolicy.SOURCE)
64     public @interface ActionType {
65         int ACTION_NONE = 0;
66         int ACTION_NOTIF_PREFERENCE = 1;
67         int ACTION_DND_RULE_PREFERENCE = 2;
68     }
69 
70     public static final String PREF_KEY_APP_HEADER = "pref_app_header";
71 
72     private static final String TAG = "AppDetailFeature";
73 
74     private final Context mAppContext;
75     private final Activity mActivity;
76     private final Fragment mFragment;
77     private final int mMetricsCategory;
78     private final View mHeader;
79     private Lifecycle mLifecycle;
80     private RecyclerView mRecyclerView;
81     private Drawable mIcon;
82     private String mIconContentDescription;
83     private CharSequence mLabel;
84     private CharSequence mSummary;
85     // Required for hearing aid devices.
86     private CharSequence mSecondSummary;
87     private String mPackageName;
88     private Intent mAppNotifPrefIntent;
89     @UserIdInt
90     private int mUid = UserHandle.USER_NULL;
91     @ActionType
92     private int mAction1;
93     @ActionType
94     private int mAction2;
95 
96     private boolean mHasAppInfoLink;
97 
98     private boolean mIsInstantApp;
99 
100     private View.OnClickListener mEditRuleNameOnClickListener;
101 
102     /**
103      * Creates a new instance of the controller.
104      *
105      * @param fragment The fragment that header will be placed in.
106      * @param header   Optional: header view if it's already created.
107      */
newInstance(Activity activity, Fragment fragment, View header)108     public static EntityHeaderController newInstance(Activity activity, Fragment fragment,
109             View header) {
110         return new EntityHeaderController(activity, fragment, header);
111     }
112 
EntityHeaderController(Activity activity, Fragment fragment, View 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) {
120             mHeader = header;
121         } else {
122             mHeader = LayoutInflater.from(fragment.getContext())
123                     .inflate(R.layout.settings_entity_header, null /* root */);
124         }
125     }
126 
setRecyclerView(RecyclerView recyclerView, Lifecycle lifecycle)127     public EntityHeaderController setRecyclerView(RecyclerView recyclerView, Lifecycle lifecycle) {
128         mRecyclerView = recyclerView;
129         mLifecycle = lifecycle;
130         return this;
131     }
132 
133     /**
134      * Set the icon in the header. Callers should also consider calling setIconContentDescription
135      * to provide a description of this icon for accessibility purposes.
136      */
setIcon(Drawable icon)137     public EntityHeaderController setIcon(Drawable icon) {
138         if (icon != null) {
139             mIcon = icon.getConstantState().newDrawable(mAppContext.getResources());
140         }
141         return this;
142     }
143 
144     /**
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.
148      */
setIcon(ApplicationsState.AppEntry appEntry)149     public EntityHeaderController setIcon(ApplicationsState.AppEntry appEntry) {
150         mIcon = IconDrawableFactory.newInstance(mAppContext).getBadgedIcon(appEntry.info);
151         return this;
152     }
153 
setIconContentDescription(String contentDescription)154     public EntityHeaderController setIconContentDescription(String contentDescription) {
155         mIconContentDescription = contentDescription;
156         return this;
157     }
158 
setLabel(CharSequence label)159     public EntityHeaderController setLabel(CharSequence label) {
160         mLabel = label;
161         return this;
162     }
163 
setLabel(ApplicationsState.AppEntry appEntry)164     public EntityHeaderController setLabel(ApplicationsState.AppEntry appEntry) {
165         mLabel = appEntry.label;
166         return this;
167     }
168 
setSummary(CharSequence summary)169     public EntityHeaderController setSummary(CharSequence summary) {
170         mSummary = summary;
171         return this;
172     }
173 
setSummary(PackageInfo packageInfo)174     public EntityHeaderController setSummary(PackageInfo packageInfo) {
175         if (packageInfo != null) {
176             mSummary = packageInfo.versionName;
177         }
178         return this;
179     }
180 
setSecondSummary(CharSequence summary)181     public EntityHeaderController setSecondSummary(CharSequence summary) {
182         mSecondSummary = summary;
183         return this;
184     }
185 
setSecondSummary(PackageInfo packageInfo)186     public EntityHeaderController setSecondSummary(PackageInfo packageInfo) {
187         if (packageInfo != null) {
188             mSummary = packageInfo.versionName;
189         }
190         return this;
191     }
192 
setHasAppInfoLink(boolean hasAppInfoLink)193     public EntityHeaderController setHasAppInfoLink(boolean hasAppInfoLink) {
194         mHasAppInfoLink = hasAppInfoLink;
195         return this;
196     }
197 
setButtonActions(@ctionType int action1, @ActionType int action2)198     public EntityHeaderController setButtonActions(@ActionType int action1,
199             @ActionType int action2) {
200         mAction1 = action1;
201         mAction2 = action2;
202         return this;
203     }
204 
setPackageName(String packageName)205     public EntityHeaderController setPackageName(String packageName) {
206         mPackageName = packageName;
207         return this;
208     }
209 
setUid(int uid)210     public EntityHeaderController setUid(int uid) {
211         mUid = uid;
212         return this;
213     }
214 
setAppNotifPrefIntent(Intent appNotifPrefIntent)215     public EntityHeaderController setAppNotifPrefIntent(Intent appNotifPrefIntent) {
216         mAppNotifPrefIntent = appNotifPrefIntent;
217         return this;
218     }
219 
setIsInstantApp(boolean isInstantApp)220     public EntityHeaderController setIsInstantApp(boolean isInstantApp) {
221         this.mIsInstantApp = isInstantApp;
222         return this;
223     }
224 
setEditZenRuleNameListener(View.OnClickListener listener)225     public EntityHeaderController setEditZenRuleNameListener(View.OnClickListener listener) {
226         this.mEditRuleNameOnClickListener = listener;
227         return this;
228     }
229 
230     /**
231      * Done mutating entity header, rebinds everything and return a new {@link LayoutPreference}.
232      */
done(Activity activity, Context uiContext)233     public LayoutPreference done(Activity activity, Context uiContext) {
234         final LayoutPreference pref = new LayoutPreference(uiContext, done(activity));
235         // Makes sure it's the first preference onscreen.
236         pref.setOrder(-1000);
237         pref.setSelectable(false);
238         pref.setKey(PREF_KEY_APP_HEADER);
239         return pref;
240     }
241 
242     /**
243      * Done mutating entity header, rebinds everything (optionally skip rebinding buttons).
244      */
done(Activity activity, boolean rebindActions)245     public View done(Activity activity, boolean rebindActions) {
246         styleActionBar(activity);
247         ImageView iconView = mHeader.findViewById(R.id.entity_header_icon);
248         if (iconView != null) {
249             iconView.setImageDrawable(mIcon);
250             iconView.setContentDescription(mIconContentDescription);
251         }
252         setText(R.id.entity_header_title, mLabel);
253         setText(R.id.entity_header_summary, mSummary);
254         setText(R.id.entity_header_second_summary, mSecondSummary);
255         if (mIsInstantApp) {
256             setText(R.id.install_type,
257                     mHeader.getResources().getString(R.string.install_type_instant));
258         }
259 
260         if (rebindActions) {
261             bindHeaderButtons();
262         }
263 
264         return mHeader;
265     }
266 
267     /**
268      * Only binds entity header with button actions.
269      */
bindHeaderButtons()270     public EntityHeaderController bindHeaderButtons() {
271         final View entityHeaderContent = mHeader.findViewById(R.id.entity_header_content);
272         final ImageButton button1 = mHeader.findViewById(android.R.id.button1);
273         final ImageButton button2 = mHeader.findViewById(android.R.id.button2);
274         bindAppInfoLink(entityHeaderContent);
275         bindButton(button1, mAction1);
276         bindButton(button2, mAction2);
277         return this;
278     }
279 
bindAppInfoLink(View entityHeaderContent)280     private void bindAppInfoLink(View entityHeaderContent) {
281         if (!mHasAppInfoLink) {
282             // Caller didn't ask for app link, skip.
283             return;
284         }
285         if (entityHeaderContent == null
286                 || mPackageName == null
287                 || mPackageName.equals(Utils.OS_PKG)
288                 || mUid == UserHandle.USER_NULL) {
289             Log.w(TAG, "Missing ingredients to build app info link, skip");
290             return;
291         }
292         entityHeaderContent.setOnClickListener(new View.OnClickListener() {
293             @Override
294             public void onClick(View v) {
295                 AppInfoBase.startAppInfoFragment(
296                         AppInfoDashboardFragment.class, R.string.application_info_label,
297                         mPackageName, mUid, mFragment, 0 /* request */,
298                         mMetricsCategory);
299             }
300         });
301         return;
302     }
303 
304     /**
305      * Styles the action bar (elevation, scrolling behaviors, color, etc).
306      * <p/>
307      * This method must be called after {@link Fragment#onCreate(Bundle)}.
308      */
styleActionBar(Activity activity)309     public EntityHeaderController styleActionBar(Activity activity) {
310         if (activity == null) {
311             Log.w(TAG, "No activity, cannot style actionbar.");
312             return this;
313         }
314         final ActionBar actionBar = activity.getActionBar();
315         if (actionBar == null) {
316             Log.w(TAG, "No actionbar, cannot style actionbar.");
317             return this;
318         }
319         actionBar.setBackgroundDrawable(
320                 new ColorDrawable(Utils.getColorAttr(activity, android.R.attr.colorPrimary)));
321         actionBar.setElevation(0);
322         if (mRecyclerView != null && mLifecycle != null) {
323             ActionBarShadowController.attachToRecyclerView(mActivity, mLifecycle, mRecyclerView);
324         }
325 
326         return this;
327     }
328 
329     /**
330      * Done mutating entity header, rebinds everything.
331      */
332     @VisibleForTesting
done(Activity activity)333     View done(Activity activity) {
334         return done(activity, true /* rebindActions */);
335     }
336 
bindButton(ImageButton button, @ActionType int action)337     private void bindButton(ImageButton button, @ActionType int action) {
338         if (button == null) {
339             return;
340         }
341         switch (action) {
342             case ActionType.ACTION_DND_RULE_PREFERENCE: {
343                 if (mEditRuleNameOnClickListener == null) {
344                     button.setVisibility(View.GONE);
345                 } else {
346                     button.setImageResource(R.drawable.ic_mode_edit);
347                     button.setVisibility(View.VISIBLE);
348                     button.setOnClickListener(mEditRuleNameOnClickListener);
349                 }
350                 return;
351             }
352             case ActionType.ACTION_NOTIF_PREFERENCE: {
353                 if (mAppNotifPrefIntent == null) {
354                     button.setVisibility(View.GONE);
355                 } else {
356                     button.setOnClickListener(new View.OnClickListener() {
357                         @Override
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);
363                         }
364                     });
365                     button.setVisibility(View.VISIBLE);
366                 }
367                 return;
368             }
369             case ActionType.ACTION_NONE: {
370                 button.setVisibility(View.GONE);
371                 return;
372             }
373         }
374     }
375 
376 
setText(@dRes int id, CharSequence text)377     private void setText(@IdRes int id, CharSequence text) {
378         TextView textView = mHeader.findViewById(id);
379         if (textView != null) {
380             textView.setText(text);
381             textView.setVisibility(TextUtils.isEmpty(text) ? View.GONE : View.VISIBLE);
382         }
383     }
384 }
385