OSDN Git Service

Correctly propagate bundle extra from intent to fragment
[android-x86/packages-apps-Settings.git] / src / com / android / settings / SettingsActivity.java
1 /*
2  * Copyright (C) 2014 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;
18
19 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO;
20
21 import android.app.ActionBar;
22 import android.app.ActivityManager;
23 import android.app.Fragment;
24 import android.app.FragmentManager;
25 import android.app.FragmentTransaction;
26 import android.content.BroadcastReceiver;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.SharedPreferences;
32 import android.content.pm.ActivityInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.PackageManager.NameNotFoundException;
35 import android.graphics.Bitmap;
36 import android.graphics.Canvas;
37 import android.graphics.drawable.Drawable;
38 import android.os.AsyncTask;
39 import android.os.Bundle;
40 import android.os.UserHandle;
41 import android.os.UserManager;
42 import android.support.annotation.VisibleForTesting;
43 import android.support.v14.preference.PreferenceFragment;
44 import android.support.v4.content.LocalBroadcastManager;
45 import android.support.v7.preference.Preference;
46 import android.support.v7.preference.PreferenceManager;
47 import android.text.TextUtils;
48 import android.transition.TransitionManager;
49 import android.util.FeatureFlagUtils;
50 import android.util.Log;
51 import android.view.View;
52 import android.view.View.OnClickListener;
53 import android.view.ViewGroup;
54 import android.widget.Button;
55 import android.widget.Toolbar;
56
57 import com.android.internal.util.ArrayUtils;
58 import com.android.settings.Settings.WifiSettingsActivity;
59 import com.android.settings.applications.manageapplications.ManageApplications;
60 import com.android.settings.backup.BackupSettingsActivity;
61 import com.android.settings.core.FeatureFlags;
62 import com.android.settings.core.SubSettingLauncher;
63 import com.android.settings.core.gateway.SettingsGateway;
64 import com.android.settings.dashboard.DashboardFeatureProvider;
65 import com.android.settings.dashboard.DashboardSummary;
66 import com.android.settings.overlay.FeatureFactory;
67 import com.android.settings.wfd.WifiDisplaySettings;
68 import com.android.settings.widget.SwitchBar;
69 import com.android.settingslib.core.instrumentation.Instrumentable;
70 import com.android.settingslib.core.instrumentation.SharedPreferencesLogger;
71 import com.android.settingslib.development.DevelopmentSettingsEnabler;
72 import com.android.settingslib.drawer.DashboardCategory;
73 import com.android.settingslib.drawer.SettingsDrawerActivity;
74
75 import java.util.ArrayList;
76 import java.util.List;
77
78 public class SettingsActivity extends SettingsDrawerActivity
79         implements PreferenceManager.OnPreferenceTreeClickListener,
80         PreferenceFragment.OnPreferenceStartFragmentCallback,
81         ButtonBarHandler, FragmentManager.OnBackStackChangedListener {
82
83     private static final String LOG_TAG = "Settings";
84
85     // Constants for state save/restore
86     private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
87
88     /**
89      * When starting this activity, the invoking Intent can contain this extra
90      * string to specify which fragment should be initially displayed.
91      * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
92      * will call isValidFragment() to confirm that the fragment class name is valid for this
93      * activity.
94      */
95     public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
96
97     /**
98      * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
99      * this extra can also be specified to supply a Bundle of arguments to pass
100      * to that fragment when it is instantiated during the initial creation
101      * of the activity.
102      */
103     public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
104
105     /**
106      * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
107      */
108     public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
109
110     public static final String BACK_STACK_PREFS = ":settings:prefs";
111
112     // extras that allow any preference activity to be launched as part of a wizard
113
114     // show Back and Next buttons? takes boolean parameter
115     // Back will then return RESULT_CANCELED and Next RESULT_OK
116     protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
117
118     // add a Skip button?
119     private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
120
121     // specify custom text for the Back or Next buttons, or cause a button to not appear
122     // at all by setting it to null
123     protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
124     protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
125
126     /**
127      * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
128      * those extra can also be specify to supply the title or title res id to be shown for
129      * that fragment.
130      */
131     public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
132     /**
133      * The package name used to resolve the title resource id.
134      */
135     public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
136             ":settings:show_fragment_title_res_package_name";
137     public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
138             ":settings:show_fragment_title_resid";
139     public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
140             ":settings:show_fragment_as_shortcut";
141
142     public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
143             ":settings:show_fragment_as_subsetting";
144
145     @Deprecated
146     public static final String EXTRA_HIDE_DRAWER = ":settings:hide_drawer";
147
148     public static final String META_DATA_KEY_FRAGMENT_CLASS =
149             "com.android.settings.FRAGMENT_CLASS";
150
151     private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
152
153     private String mFragmentClass;
154
155     private CharSequence mInitialTitle;
156     private int mInitialTitleResId;
157
158     private BroadcastReceiver mDevelopmentSettingsListener;
159
160     private boolean mBatteryPresent = true;
161     private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
162         @Override
163         public void onReceive(Context context, Intent intent) {
164             String action = intent.getAction();
165             if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
166                 boolean batteryPresent = Utils.isBatteryPresent(intent);
167
168                 if (mBatteryPresent != batteryPresent) {
169                     mBatteryPresent = batteryPresent;
170                     updateTilesList();
171                 }
172             }
173         }
174     };
175
176     private SwitchBar mSwitchBar;
177
178     private Button mNextButton;
179
180     private boolean mIsShowingDashboard;
181
182     private ViewGroup mContent;
183
184     // Categories
185     private ArrayList<DashboardCategory> mCategories = new ArrayList<>();
186
187     private DashboardFeatureProvider mDashboardFeatureProvider;
188
189     public SwitchBar getSwitchBar() {
190         return mSwitchBar;
191     }
192
193     @Override
194     public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
195         new SubSettingLauncher(this)
196                 .setDestination(pref.getFragment())
197                 .setArguments(pref.getExtras())
198                 .setSourceMetricsCategory(caller instanceof Instrumentable
199                         ? ((Instrumentable) caller).getMetricsCategory()
200                         : Instrumentable.METRICS_CATEGORY_UNKNOWN)
201                 .setTitle(-1)
202                 .launch();
203         return true;
204     }
205
206     @Override
207     public boolean onPreferenceTreeClick(Preference preference) {
208         return false;
209     }
210
211     @Override
212     public SharedPreferences getSharedPreferences(String name, int mode) {
213         if (name.equals(getPackageName() + "_preferences")) {
214             return new SharedPreferencesLogger(this, getMetricsTag(),
215                     FeatureFactory.getFactory(this).getMetricsFeatureProvider());
216         }
217         return super.getSharedPreferences(name, mode);
218     }
219
220     private String getMetricsTag() {
221         String tag = getClass().getName();
222         if (getIntent() != null && getIntent().hasExtra(EXTRA_SHOW_FRAGMENT)) {
223             tag = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
224         }
225         if (tag.startsWith("com.android.settings.")) {
226             tag = tag.replace("com.android.settings.", "");
227         }
228         return tag;
229     }
230
231     @Override
232     protected void onCreate(Bundle savedState) {
233         super.onCreate(savedState);
234         long startTime = System.currentTimeMillis();
235
236         final FeatureFactory factory = FeatureFactory.getFactory(this);
237
238         mDashboardFeatureProvider = factory.getDashboardFeatureProvider(this);
239
240         // Should happen before any call to getIntent()
241         getMetaData();
242
243         final Intent intent = getIntent();
244         if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
245             getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
246         }
247
248         // Getting Intent properties can only be done after the super.onCreate(...)
249         final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
250
251         final ComponentName cn = intent.getComponent();
252         final String className = cn.getClassName();
253
254         mIsShowingDashboard = className.equals(Settings.class.getName());
255
256         // This is a "Sub Settings" when:
257         // - this is a real SubSettings
258         // - or :settings:show_fragment_as_subsetting is passed to the Intent
259         final boolean isSubSettings = this instanceof SubSettings ||
260                 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
261
262         // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content
263         // insets
264         if (isSubSettings) {
265             setTheme(R.style.Theme_SubSettings);
266         }
267
268         setContentView(mIsShowingDashboard ?
269                 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
270
271         mContent = findViewById(R.id.main_content);
272
273         getFragmentManager().addOnBackStackChangedListener(this);
274
275         if (savedState != null) {
276             // We are restarting from a previous saved state; used that to initialize, instead
277             // of starting fresh.
278             setTitleFromIntent(intent);
279
280             ArrayList<DashboardCategory> categories =
281                     savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
282             if (categories != null) {
283                 mCategories.clear();
284                 mCategories.addAll(categories);
285                 setTitleFromBackStack();
286             }
287         } else {
288             launchSettingFragment(initialFragmentName, isSubSettings, intent);
289         }
290
291         if (mIsShowingDashboard) {
292             findViewById(R.id.search_bar).setVisibility(View.VISIBLE);
293             findViewById(R.id.action_bar).setVisibility(View.GONE);
294             final Toolbar toolbar = findViewById(R.id.search_action_bar);
295             FeatureFactory.getFactory(this).getSearchFeatureProvider()
296                     .initSearchToolbar(this, toolbar);
297             setActionBar(toolbar);
298
299             // Please forgive me for what I am about to do.
300             //
301             // Need to make the navigation icon non-clickable so that the entire card is clickable
302             // and goes to the search UI. Also set the background to null so there's no ripple.
303             View navView = toolbar.getNavigationView();
304             navView.setClickable(false);
305             navView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
306             navView.setBackground(null);
307         }
308
309         ActionBar actionBar = getActionBar();
310         if (actionBar != null) {
311             boolean deviceProvisioned = Utils.isDeviceProvisioned(this);
312             actionBar.setDisplayHomeAsUpEnabled(deviceProvisioned);
313             actionBar.setHomeButtonEnabled(deviceProvisioned);
314             actionBar.setDisplayShowTitleEnabled(!mIsShowingDashboard);
315         }
316         mSwitchBar = findViewById(R.id.switch_bar);
317         if (mSwitchBar != null) {
318             mSwitchBar.setMetricsTag(getMetricsTag());
319         }
320
321         // see if we should show Back/Next buttons
322         if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
323
324             View buttonBar = findViewById(R.id.button_bar);
325             if (buttonBar != null) {
326                 buttonBar.setVisibility(View.VISIBLE);
327
328                 Button backButton = (Button) findViewById(R.id.back_button);
329                 backButton.setOnClickListener(new OnClickListener() {
330                     public void onClick(View v) {
331                         setResult(RESULT_CANCELED, null);
332                         finish();
333                     }
334                 });
335                 Button skipButton = (Button) findViewById(R.id.skip_button);
336                 skipButton.setOnClickListener(new OnClickListener() {
337                     public void onClick(View v) {
338                         setResult(RESULT_OK, null);
339                         finish();
340                     }
341                 });
342                 mNextButton = (Button) findViewById(R.id.next_button);
343                 mNextButton.setOnClickListener(new OnClickListener() {
344                     public void onClick(View v) {
345                         setResult(RESULT_OK, null);
346                         finish();
347                     }
348                 });
349
350                 // set our various button parameters
351                 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
352                     String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
353                     if (TextUtils.isEmpty(buttonText)) {
354                         mNextButton.setVisibility(View.GONE);
355                     } else {
356                         mNextButton.setText(buttonText);
357                     }
358                 }
359                 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
360                     String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
361                     if (TextUtils.isEmpty(buttonText)) {
362                         backButton.setVisibility(View.GONE);
363                     } else {
364                         backButton.setText(buttonText);
365                     }
366                 }
367                 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
368                     skipButton.setVisibility(View.VISIBLE);
369                 }
370             }
371         }
372
373         if (DEBUG_TIMING) {
374             Log.d(LOG_TAG, "onCreate took " + (System.currentTimeMillis() - startTime) + " ms");
375         }
376     }
377
378     @VisibleForTesting
379     void launchSettingFragment(String initialFragmentName, boolean isSubSettings, Intent intent) {
380         if (!mIsShowingDashboard && initialFragmentName != null) {
381             setTitleFromIntent(intent);
382
383             Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
384             switchToFragment(initialFragmentName, initialArguments, true, false,
385                     mInitialTitleResId, mInitialTitle, false);
386         } else {
387             // Show search icon as up affordance if we are displaying the main Dashboard
388             mInitialTitleResId = R.string.dashboard_title;
389
390             switchToFragment(DashboardSummary.class.getName(), null /* args */, false, false,
391                     mInitialTitleResId, mInitialTitle, false);
392         }
393     }
394
395     private void setTitleFromIntent(Intent intent) {
396         final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
397         if (initialTitleResId > 0) {
398             mInitialTitle = null;
399             mInitialTitleResId = initialTitleResId;
400
401             final String initialTitleResPackageName = intent.getStringExtra(
402                     EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
403             if (initialTitleResPackageName != null) {
404                 try {
405                     Context authContext = createPackageContextAsUser(initialTitleResPackageName,
406                             0 /* flags */, new UserHandle(UserHandle.myUserId()));
407                     mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
408                     setTitle(mInitialTitle);
409                     mInitialTitleResId = -1;
410                     return;
411                 } catch (NameNotFoundException e) {
412                     Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
413                 }
414             } else {
415                 setTitle(mInitialTitleResId);
416             }
417         } else {
418             mInitialTitleResId = -1;
419             final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
420             mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
421             setTitle(mInitialTitle);
422         }
423     }
424
425     @Override
426     public void onBackStackChanged() {
427         setTitleFromBackStack();
428     }
429
430     private void setTitleFromBackStack() {
431         final int count = getFragmentManager().getBackStackEntryCount();
432
433         if (count == 0) {
434             if (mInitialTitleResId > 0) {
435                 setTitle(mInitialTitleResId);
436             } else {
437                 setTitle(mInitialTitle);
438             }
439             return;
440         }
441
442         FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
443         setTitleFromBackStackEntry(bse);
444     }
445
446     private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
447         final CharSequence title;
448         final int titleRes = bse.getBreadCrumbTitleRes();
449         if (titleRes > 0) {
450             title = getText(titleRes);
451         } else {
452             title = bse.getBreadCrumbTitle();
453         }
454         if (title != null) {
455             setTitle(title);
456         }
457     }
458
459     @Override
460     protected void onSaveInstanceState(Bundle outState) {
461         super.onSaveInstanceState(outState);
462         saveState(outState);
463     }
464
465     /**
466      * For testing purposes to avoid crashes from final variables in Activity's onSaveInstantState.
467      */
468     @VisibleForTesting
469     void saveState(Bundle outState) {
470         if (mCategories.size() > 0) {
471             outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
472         }
473     }
474
475     @Override
476     protected void onResume() {
477         super.onResume();
478
479         mDevelopmentSettingsListener = new BroadcastReceiver() {
480             @Override
481             public void onReceive(Context context, Intent intent) {
482                 updateTilesList();
483             }
484         };
485         LocalBroadcastManager.getInstance(this).registerReceiver(mDevelopmentSettingsListener,
486                 new IntentFilter(DevelopmentSettingsEnabler.DEVELOPMENT_SETTINGS_CHANGED_ACTION));
487
488         registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
489
490         updateTilesList();
491     }
492
493     @Override
494     protected void onPause() {
495         super.onPause();
496         LocalBroadcastManager.getInstance(this).unregisterReceiver(mDevelopmentSettingsListener);
497         mDevelopmentSettingsListener = null;
498         unregisterReceiver(mBatteryInfoReceiver);
499     }
500
501     @Override
502     public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
503         final Bitmap icon = getBitmapFromXmlResource(R.drawable.ic_launcher_settings);
504         taskDescription.setIcon(icon);
505         super.setTaskDescription(taskDescription);
506     }
507
508     protected boolean isValidFragment(String fragmentName) {
509         // Almost all fragments are wrapped in this,
510         // except for a few that have their own activities.
511         for (int i = 0; i < SettingsGateway.ENTRY_FRAGMENTS.length; i++) {
512             if (SettingsGateway.ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
513         }
514         return false;
515     }
516
517     @Override
518     public Intent getIntent() {
519         Intent superIntent = super.getIntent();
520         String startingFragment = getStartingFragmentClass(superIntent);
521         // This is called from super.onCreate, isMultiPane() is not yet reliable
522         // Do not use onIsHidingHeaders either, which relies itself on this method
523         if (startingFragment != null) {
524             Intent modIntent = new Intent(superIntent);
525             modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
526             Bundle args = superIntent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
527             if (args != null) {
528                 args = new Bundle(args);
529             } else {
530                 args = new Bundle();
531             }
532             args.putParcelable("intent", superIntent);
533             modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
534             return modIntent;
535         }
536         return superIntent;
537     }
538
539     /**
540      * Checks if the component name in the intent is different from the Settings class and
541      * returns the class name to load as a fragment.
542      */
543     private String getStartingFragmentClass(Intent intent) {
544         if (mFragmentClass != null) return mFragmentClass;
545
546         String intentClass = intent.getComponent().getClassName();
547         if (intentClass.equals(getClass().getName())) return null;
548
549         if ("com.android.settings.RunningServices".equals(intentClass)
550                 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
551             // Old names of manage apps.
552             intentClass = ManageApplications.class.getName();
553         }
554
555         return intentClass;
556     }
557
558     /**
559      * Called by a preference panel fragment to finish itself.
560      *
561      * @param resultCode Optional result code to send back to the original
562      *                   launching fragment.
563      * @param resultData Optional result data to send back to the original
564      *                   launching fragment.
565      */
566     public void finishPreferencePanel(int resultCode, Intent resultData) {
567         setResult(resultCode, resultData);
568         finish();
569     }
570
571     /**
572      * Switch to a specific Fragment with taking care of validation, Title and BackStack
573      */
574     private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
575             boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
576         if (validate && !isValidFragment(fragmentName)) {
577             throw new IllegalArgumentException("Invalid fragment for this activity: "
578                     + fragmentName);
579         }
580         Fragment f = Fragment.instantiate(this, fragmentName, args);
581         FragmentTransaction transaction = getFragmentManager().beginTransaction();
582         transaction.replace(R.id.main_content, f);
583         if (withTransition) {
584             TransitionManager.beginDelayedTransition(mContent);
585         }
586         if (addToBackStack) {
587             transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
588         }
589         if (titleResId > 0) {
590             transaction.setBreadCrumbTitle(titleResId);
591         } else if (title != null) {
592             transaction.setBreadCrumbTitle(title);
593         }
594         transaction.commitAllowingStateLoss();
595         getFragmentManager().executePendingTransactions();
596         return f;
597     }
598
599     private void updateTilesList() {
600         // Generally the items that are will be changing from these updates will
601         // not be in the top list of tiles, so run it in the background and the
602         // SettingsDrawerActivity will pick up on the updates automatically.
603         AsyncTask.execute(new Runnable() {
604             @Override
605             public void run() {
606                 doUpdateTilesList();
607             }
608         });
609     }
610
611     private void doUpdateTilesList() {
612         PackageManager pm = getPackageManager();
613         final UserManager um = UserManager.get(this);
614         final boolean isAdmin = um.isAdminUser();
615         final FeatureFactory featureFactory = FeatureFactory.getFactory(this);
616         boolean somethingChanged = false;
617         String packageName = getPackageName();
618         somethingChanged = setTileEnabled(
619                 new ComponentName(packageName, WifiSettingsActivity.class.getName()),
620                 pm.hasSystemFeature(PackageManager.FEATURE_WIFI), isAdmin) || somethingChanged;
621
622         somethingChanged = setTileEnabled(new ComponentName(packageName,
623                         Settings.BluetoothSettingsActivity.class.getName()),
624                 pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH), isAdmin)
625                 || somethingChanged;
626
627
628         // Enable DataUsageSummaryActivity if the data plan feature flag is turned on otherwise
629         // enable DataPlanUsageSummaryActivity.
630         somethingChanged = setTileEnabled(
631                 new ComponentName(packageName, Settings.DataUsageSummaryActivity.class.getName()),
632                 Utils.isBandwidthControlEnabled() /* enabled */,
633                 isAdmin) || somethingChanged;
634
635         somethingChanged = setTileEnabled(
636                 new ComponentName(packageName,
637                         Settings.ConnectedDeviceDashboardActivity.class.getName()),
638                 !UserManager.isDeviceInDemoMode(this) /* enabled */,
639                 isAdmin) || somethingChanged;
640
641         somethingChanged = setTileEnabled(new ComponentName(packageName,
642                         Settings.SimSettingsActivity.class.getName()),
643                 Utils.showSimCardTile(this), isAdmin)
644                 || somethingChanged;
645
646         final boolean isBatterySettingsV2Enabled = featureFactory
647                 .getPowerUsageFeatureProvider(this)
648                 .isBatteryV2Enabled();
649         // Enable new battery page if v2 enabled
650         somethingChanged = setTileEnabled(new ComponentName(packageName,
651                         Settings.PowerUsageSummaryActivity.class.getName()),
652                 mBatteryPresent && isBatterySettingsV2Enabled, isAdmin) || somethingChanged;
653         // Enable legacy battery page if v2 disabled
654         somethingChanged = setTileEnabled(new ComponentName(packageName,
655                         Settings.PowerUsageSummaryLegacyActivity.class.getName()),
656                 mBatteryPresent && !isBatterySettingsV2Enabled, isAdmin) || somethingChanged;
657
658         final boolean isDataUsageSettingsV2Enabled =
659                 FeatureFlagUtils.isEnabled(this, FeatureFlags.DATA_USAGE_SETTINGS_V2);
660         // Enable new data usage page if v2 enabled
661         somethingChanged = setTileEnabled(new ComponentName(packageName,
662                         Settings.DataUsageSummaryActivity.class.getName()),
663                 Utils.isBandwidthControlEnabled() && isDataUsageSettingsV2Enabled, isAdmin)
664                 || somethingChanged;
665         // Enable legacy data usage page if v2 disabled
666         somethingChanged = setTileEnabled(new ComponentName(packageName,
667                         Settings.DataUsageSummaryLegacyActivity.class.getName()),
668                 Utils.isBandwidthControlEnabled() && !isDataUsageSettingsV2Enabled, isAdmin)
669                 || somethingChanged;
670
671         somethingChanged = setTileEnabled(new ComponentName(packageName,
672                         Settings.UserSettingsActivity.class.getName()),
673                 UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
674                         && !Utils.isMonkeyRunning(), isAdmin)
675                 || somethingChanged;
676
677         somethingChanged = setTileEnabled(new ComponentName(packageName,
678                         Settings.NetworkDashboardActivity.class.getName()),
679                 !UserManager.isDeviceInDemoMode(this), isAdmin)
680                 || somethingChanged;
681
682         somethingChanged = setTileEnabled(new ComponentName(packageName,
683                         Settings.DateTimeSettingsActivity.class.getName()),
684                 !UserManager.isDeviceInDemoMode(this), isAdmin)
685                 || somethingChanged;
686
687         final boolean showDev = DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(this)
688                 && !Utils.isMonkeyRunning();
689
690         somethingChanged = setTileEnabled(new ComponentName(packageName,
691                         Settings.DevelopmentSettingsDashboardActivity.class.getName()),
692                 showDev, isAdmin)
693                 || somethingChanged;
694
695         // Enable/disable backup settings depending on whether the user is admin.
696         somethingChanged = setTileEnabled(new ComponentName(packageName,
697                 BackupSettingsActivity.class.getName()), true, isAdmin)
698                 || somethingChanged;
699
700         somethingChanged = setTileEnabled(new ComponentName(packageName,
701                         Settings.WifiDisplaySettingsActivity.class.getName()),
702                 WifiDisplaySettings.isAvailable(this), isAdmin)
703                 || somethingChanged;
704
705         // Enable/disable the Me Card page.
706         final boolean aboutPhoneV2Enabled = featureFactory
707                 .getAccountFeatureProvider()
708                 .isAboutPhoneV2Enabled(this);
709         somethingChanged = setTileEnabled(new ComponentName(packageName,
710                         Settings.MyDeviceInfoActivity.class.getName()),
711                 aboutPhoneV2Enabled, isAdmin)
712                 || somethingChanged;
713         somethingChanged = setTileEnabled(new ComponentName(packageName,
714                         Settings.DeviceInfoSettingsActivity.class.getName()),
715                 !aboutPhoneV2Enabled, isAdmin)
716                 || somethingChanged;
717
718         if (UserHandle.MU_ENABLED && !isAdmin) {
719
720             // When on restricted users, disable all extra categories (but only the settings ones).
721             final List<DashboardCategory> categories = mDashboardFeatureProvider.getAllCategories();
722             synchronized (categories) {
723                 for (DashboardCategory category : categories) {
724                     final int tileCount = category.getTilesCount();
725                     for (int i = 0; i < tileCount; i++) {
726                         final ComponentName component = category.getTile(i).intent.getComponent();
727
728                         final String name = component.getClassName();
729                         final boolean isEnabledForRestricted = ArrayUtils.contains(
730                                 SettingsGateway.SETTINGS_FOR_RESTRICTED, name);
731                         if (packageName.equals(component.getPackageName())
732                                 && !isEnabledForRestricted) {
733                             somethingChanged = setTileEnabled(component, false, isAdmin)
734                                     || somethingChanged;
735                         }
736                     }
737                 }
738             }
739         }
740
741         // Final step, refresh categories.
742         if (somethingChanged) {
743             Log.d(LOG_TAG, "Enabled state changed for some tiles, reloading all categories");
744             updateCategories();
745         } else {
746             Log.d(LOG_TAG, "No enabled state changed, skipping updateCategory call");
747         }
748     }
749
750     /**
751      * @return whether or not the enabled state actually changed.
752      */
753     private boolean setTileEnabled(ComponentName component, boolean enabled, boolean isAdmin) {
754         if (UserHandle.MU_ENABLED && !isAdmin && getPackageName().equals(component.getPackageName())
755                 && !ArrayUtils.contains(SettingsGateway.SETTINGS_FOR_RESTRICTED,
756                 component.getClassName())) {
757             enabled = false;
758         }
759         return setTileEnabled(component, enabled);
760     }
761
762     private void getMetaData() {
763         try {
764             ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
765                     PackageManager.GET_META_DATA);
766             if (ai == null || ai.metaData == null) return;
767             mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
768         } catch (NameNotFoundException nnfe) {
769             // No recovery
770             Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
771         }
772     }
773
774     // give subclasses access to the Next button
775     public boolean hasNextButton() {
776         return mNextButton != null;
777     }
778
779     public Button getNextButton() {
780         return mNextButton;
781     }
782
783     @VisibleForTesting
784     Bitmap getBitmapFromXmlResource(int drawableRes) {
785         Drawable drawable = getResources().getDrawable(drawableRes, getTheme());
786         Canvas canvas = new Canvas();
787         Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
788                 drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
789         canvas.setBitmap(bitmap);
790         drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
791         drawable.draw(canvas);
792
793         return bitmap;
794     }
795 }