Home | History | Annotate | Download | only in applications
      1 /*
      2  * Copyright (C) 2015 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.applications;
     18 
     19 import android.app.AlertDialog;
     20 import android.content.Intent;
     21 import android.content.IntentFilter;
     22 import android.content.pm.ApplicationInfo;
     23 import android.content.pm.IntentFilterVerificationInfo;
     24 import android.content.pm.PackageManager;
     25 import android.content.pm.ResolveInfo;
     26 import android.net.Uri;
     27 import android.os.Bundle;
     28 import android.os.UserHandle;
     29 import android.support.v7.preference.DropDownPreference;
     30 import android.support.v7.preference.Preference;
     31 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
     32 import android.util.ArraySet;
     33 import android.util.Log;
     34 import android.view.View;
     35 import android.view.View.OnClickListener;
     36 
     37 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     38 import com.android.settings.R;
     39 import com.android.settings.Utils;
     40 
     41 import java.util.List;
     42 
     43 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
     44 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
     45 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
     46 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
     47 
     48 public class AppLaunchSettings extends AppInfoWithHeader implements OnClickListener,
     49         Preference.OnPreferenceChangeListener {
     50     private static final String TAG = "AppLaunchSettings";
     51 
     52     private static final String KEY_APP_LINK_STATE = "app_link_state";
     53     private static final String KEY_SUPPORTED_DOMAIN_URLS = "app_launch_supported_domain_urls";
     54     private static final String KEY_CLEAR_DEFAULTS = "app_launch_clear_defaults";
     55 
     56     private static final Intent sBrowserIntent;
     57     static {
     58         sBrowserIntent = new Intent()
     59                 .setAction(Intent.ACTION_VIEW)
     60                 .addCategory(Intent.CATEGORY_BROWSABLE)
     61                 .setData(Uri.parse("http:"));
     62     }
     63 
     64     private PackageManager mPm;
     65 
     66     private boolean mIsBrowser;
     67     private boolean mHasDomainUrls;
     68     private DropDownPreference mAppLinkState;
     69     private AppDomainsPreference mAppDomainUrls;
     70     private ClearDefaultsPreference mClearDefaultsPreference;
     71 
     72     @Override
     73     public void onCreate(Bundle savedInstanceState) {
     74         super.onCreate(savedInstanceState);
     75 
     76         addPreferencesFromResource(R.xml.installed_app_launch_settings);
     77         mAppDomainUrls = (AppDomainsPreference) findPreference(KEY_SUPPORTED_DOMAIN_URLS);
     78         mClearDefaultsPreference = (ClearDefaultsPreference) findPreference(KEY_CLEAR_DEFAULTS);
     79         mAppLinkState = (DropDownPreference) findPreference(KEY_APP_LINK_STATE);
     80 
     81         mPm = getActivity().getPackageManager();
     82 
     83         mIsBrowser = isBrowserApp(mPackageName);
     84         mHasDomainUrls =
     85                 (mAppEntry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
     86 
     87         if (!mIsBrowser) {
     88             List<IntentFilterVerificationInfo> iviList = mPm.getIntentFilterVerifications(mPackageName);
     89             List<IntentFilter> filters = mPm.getAllIntentFilters(mPackageName);
     90             CharSequence[] entries = getEntries(mPackageName, iviList, filters);
     91             mAppDomainUrls.setTitles(entries);
     92             mAppDomainUrls.setValues(new int[entries.length]);
     93         }
     94         buildStateDropDown();
     95     }
     96 
     97     // An app is a "browser" if it has an activity resolution that wound up
     98     // marked with the 'handleAllWebDataURI' flag.
     99     private boolean isBrowserApp(String packageName) {
    100         sBrowserIntent.setPackage(packageName);
    101         List<ResolveInfo> list = mPm.queryIntentActivitiesAsUser(sBrowserIntent,
    102                 PackageManager.MATCH_ALL, UserHandle.myUserId());
    103         final int count = list.size();
    104         for (int i = 0; i < count; i++) {
    105             ResolveInfo info = list.get(i);
    106             if (info.activityInfo != null && info.handleAllWebDataURI) {
    107                 return true;
    108             }
    109         }
    110         return false;
    111     }
    112 
    113     private void buildStateDropDown() {
    114         if (mIsBrowser) {
    115             // Browsers don't show the app-link prefs
    116             mAppLinkState.setShouldDisableView(true);
    117             mAppLinkState.setEnabled(false);
    118             mAppDomainUrls.setShouldDisableView(true);
    119             mAppDomainUrls.setEnabled(false);
    120         } else {
    121             // Designed order of states in the dropdown:
    122             //
    123             // * always
    124             // * ask
    125             // * never
    126             mAppLinkState.setEntries(new CharSequence[] {
    127                     getString(R.string.app_link_open_always),
    128                     getString(R.string.app_link_open_ask),
    129                     getString(R.string.app_link_open_never),
    130             });
    131             mAppLinkState.setEntryValues(new CharSequence[] {
    132                     Integer.toString(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS),
    133                     Integer.toString(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK),
    134                     Integer.toString(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER),
    135             });
    136 
    137             mAppLinkState.setEnabled(mHasDomainUrls);
    138             if (mHasDomainUrls) {
    139                 // Present 'undefined' as 'ask' because the OS treats them identically for
    140                 // purposes of the UI (and does the right thing around pending domain
    141                 // verifications that might arrive after the user chooses 'ask' in this UI).
    142                 final int state = mPm.getIntentVerificationStatusAsUser(mPackageName, UserHandle.myUserId());
    143                 mAppLinkState.setValue(
    144                         Integer.toString((state == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED)
    145                                 ? INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK
    146                                         : state));
    147 
    148                 // Set the callback only after setting the initial selected item
    149                 mAppLinkState.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
    150                     @Override
    151                     public boolean onPreferenceChange(Preference preference, Object newValue) {
    152                         return updateAppLinkState(Integer.parseInt((String) newValue));
    153                     }
    154                 });
    155             }
    156         }
    157     }
    158 
    159     private boolean updateAppLinkState(final int newState) {
    160         if (mIsBrowser) {
    161             // We shouldn't get into this state, but if we do make sure
    162             // not to cause any permanent mayhem.
    163             return false;
    164         }
    165 
    166         final int userId = UserHandle.myUserId();
    167         final int priorState = mPm.getIntentVerificationStatusAsUser(mPackageName, userId);
    168 
    169         if (priorState == newState) {
    170             return false;
    171         }
    172 
    173         boolean success = mPm.updateIntentVerificationStatusAsUser(mPackageName, newState, userId);
    174         if (success) {
    175             // Read back the state to see if the change worked
    176             final int updatedState = mPm.getIntentVerificationStatusAsUser(mPackageName, userId);
    177             success = (newState == updatedState);
    178         } else {
    179             Log.e(TAG, "Couldn't update intent verification status!");
    180         }
    181         return success;
    182     }
    183 
    184     private CharSequence[] getEntries(String packageName, List<IntentFilterVerificationInfo> iviList,
    185             List<IntentFilter> filters) {
    186         ArraySet<String> result = Utils.getHandledDomains(mPm, packageName);
    187         return result.toArray(new CharSequence[result.size()]);
    188     }
    189 
    190     @Override
    191     protected boolean refreshUi() {
    192         mClearDefaultsPreference.setPackageName(mPackageName);
    193         mClearDefaultsPreference.setAppEntry(mAppEntry);
    194         return true;
    195     }
    196 
    197     @Override
    198     protected AlertDialog createDialog(int id, int errorCode) {
    199         // No dialogs for preferred launch settings.
    200         return null;
    201     }
    202 
    203     @Override
    204     public void onClick(View v) {
    205         // Nothing to do
    206     }
    207 
    208     @Override
    209     public boolean onPreferenceChange(Preference preference, Object newValue) {
    210         // actual updates are handled by the app link dropdown callback
    211         return true;
    212     }
    213 
    214     @Override
    215     public int getMetricsCategory() {
    216         return MetricsEvent.APPLICATIONS_APP_LAUNCH;
    217     }
    218 }
    219