OSDN Git Service

9b2c5291e280b433bc09488f7a70074afcc16844
[android-x86/packages-apps-Settings.git] / src / com / android / settings / widget / EntityHeaderController.java
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 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_OPEN_APP_SETTING;
22
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;
47
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;
58
59 import java.lang.annotation.Retention;
60 import java.lang.annotation.RetentionPolicy;
61
62 public class EntityHeaderController {
63
64     @IntDef({ActionType.ACTION_NONE,
65             ActionType.ACTION_APP_PREFERENCE,
66             ActionType.ACTION_NOTIF_PREFERENCE})
67     @Retention(RetentionPolicy.SOURCE)
68     public @interface ActionType {
69         int ACTION_NONE = 0;
70         int ACTION_APP_PREFERENCE = 1;
71         int ACTION_NOTIF_PREFERENCE = 2;
72     }
73
74     public static final String PREF_KEY_APP_HEADER = "pref_app_header";
75
76     private static final String TAG = "AppDetailFeature";
77
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;
91     @UserIdInt
92     private int mUid = UserHandle.USER_NULL;
93     @ActionType
94     private int mAction1;
95     @ActionType
96     private int mAction2;
97
98     private boolean mHasAppInfoLink;
99
100     private boolean mIsInstantApp;
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      */
108     public static EntityHeaderController newInstance(Activity activity, Fragment fragment,
109             View header) {
110         return new EntityHeaderController(activity, fragment, header);
111     }
112
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
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      */
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      */
149     public EntityHeaderController setIcon(ApplicationsState.AppEntry appEntry) {
150         if (appEntry.icon != null) {
151             mIcon = appEntry.icon.getConstantState().newDrawable(mAppContext.getResources());
152         }
153         return this;
154     }
155
156     public EntityHeaderController setIconContentDescription(String contentDescription) {
157         mIconContentDescription = contentDescription;
158         return this;
159     }
160
161     public EntityHeaderController setLabel(CharSequence label) {
162         mLabel = label;
163         return this;
164     }
165
166     public EntityHeaderController setLabel(ApplicationsState.AppEntry appEntry) {
167         mLabel = appEntry.label;
168         return this;
169     }
170
171     public EntityHeaderController setSummary(CharSequence summary) {
172         mSummary = summary;
173         return this;
174     }
175
176     public EntityHeaderController setSummary(PackageInfo packageInfo) {
177         if (packageInfo != null) {
178             mSummary = packageInfo.versionName;
179         }
180         return this;
181     }
182
183     public EntityHeaderController setHasAppInfoLink(boolean hasAppInfoLink) {
184         mHasAppInfoLink = hasAppInfoLink;
185         return this;
186     }
187
188     public EntityHeaderController setButtonActions(@ActionType int action1,
189             @ActionType int action2) {
190         mAction1 = action1;
191         mAction2 = action2;
192         return this;
193     }
194
195     public EntityHeaderController setPackageName(String packageName) {
196         mPackageName = packageName;
197         return this;
198     }
199
200     public EntityHeaderController setUid(int uid) {
201         mUid = uid;
202         return this;
203     }
204
205     public EntityHeaderController setAppNotifPrefIntent(Intent appNotifPrefIntent) {
206         mAppNotifPrefIntent = appNotifPrefIntent;
207         return this;
208     }
209
210     public EntityHeaderController setIsInstantApp(boolean isInstantApp) {
211         this.mIsInstantApp = isInstantApp;
212         return this;
213     }
214
215     /**
216      * Done mutating entity header, rebinds everything and return a new {@link LayoutPreference}.
217      */
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);
224         return pref;
225     }
226
227     /**
228      * Done mutating entity header, rebinds everything (optionally skip rebinding buttons).
229      */
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);
236         }
237         setText(R.id.entity_header_title, mLabel);
238         setText(R.id.entity_header_summary, mSummary);
239         if (mIsInstantApp) {
240             setText(R.id.install_type,
241                     mHeader.getResources().getString(R.string.install_type_instant));
242         }
243
244         if (rebindActions) {
245             bindHeaderButtons();
246         }
247
248         return mHeader;
249     }
250
251     /**
252      * Only binds entity header with button actions.
253      */
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);
261         return this;
262     }
263
264     private void bindAppInfoLink(View entityHeaderContent) {
265         if (!mHasAppInfoLink) {
266             // Caller didn't ask for app link, skip.
267             return;
268         }
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");
274             return;
275         }
276         entityHeaderContent.setOnClickListener(new View.OnClickListener() {
277             @Override
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 */,
283                             mMetricsCategory);
284                 } else {
285                     AppInfoBase.startAppInfoFragment(
286                             InstalledAppDetails.class, R.string.application_info_label,
287                             mPackageName, mUid, mFragment, 0 /* request */,
288                             mMetricsCategory);
289                 }
290             }
291         });
292         return;
293     }
294
295     /**
296      * Styles the action bar (elevation, scrolling behaviors, color, etc).
297      * <p/>
298      * This method must be called after {@link Fragment#onCreate(Bundle)}.
299      */
300     public EntityHeaderController styleActionBar(Activity activity) {
301         if (activity == null) {
302             Log.w(TAG, "No activity, cannot style actionbar.");
303             return this;
304         }
305         final ActionBar actionBar = activity.getActionBar();
306         if (actionBar == null) {
307             Log.w(TAG, "No actionbar, cannot style actionbar.");
308             return this;
309         }
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);
315         }
316
317         return this;
318     }
319
320     /**
321      * Done mutating entity header, rebinds everything.
322      */
323     @VisibleForTesting
324     View done(Activity activity) {
325         return done(activity, true /* rebindActions */);
326     }
327
328     private void bindButton(ImageButton button, @ActionType int action) {
329         if (button == null) {
330             return;
331         }
332         switch (action) {
333             case ActionType.ACTION_NOTIF_PREFERENCE: {
334                 if (mAppNotifPrefIntent == null) {
335                     button.setVisibility(View.GONE);
336                 } else {
337                     button.setOnClickListener(new View.OnClickListener() {
338                         @Override
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);
344                         }
345                     });
346                     button.setVisibility(View.VISIBLE);
347                 }
348                 return;
349             }
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);
356                     return;
357                 }
358                 button.setOnClickListener(new View.OnClickListener() {
359                     @Override
360                     public void onClick(View v) {
361                         FeatureFactory.getFactory(mAppContext).getMetricsFeatureProvider()
362                                 .actionWithSource(mAppContext, mMetricsCategory,
363                                         ACTION_OPEN_APP_SETTING);
364                         mFragment.startActivity(intent);
365                     }
366                 });
367                 button.setImageResource(R.drawable.ic_settings_24dp);
368                 button.setVisibility(View.VISIBLE);
369                 return;
370             }
371             case ActionType.ACTION_NONE: {
372                 button.setVisibility(View.GONE);
373                 return;
374             }
375         }
376     }
377
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);
383         }
384         return null;
385     }
386
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);
392         }
393     }
394 }