1 /*
2  * Copyright (C) 2019 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.car.settings.applications.defaultapps;
18 
19 import android.car.drivingstate.CarUxRestrictions;
20 import android.content.Context;
21 import android.os.UserHandle;
22 import android.text.TextUtils;
23 
24 import androidx.annotation.NonNull;
25 import androidx.preference.TwoStatePreference;
26 
27 import com.android.car.settings.R;
28 import com.android.car.settings.common.ConfirmationDialogFragment;
29 import com.android.car.settings.common.FragmentController;
30 import com.android.car.settings.common.GroupSelectionPreferenceController;
31 import com.android.car.settings.common.Logger;
32 import com.android.car.ui.preference.CarUiRadioButtonPreference;
33 import com.android.settingslib.applications.DefaultAppInfo;
34 
35 import java.util.ArrayList;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 
40 /** Defines the shared logic in picking a default application. */
41 public abstract class DefaultAppsPickerBasePreferenceController extends
42         GroupSelectionPreferenceController {
43 
44     private static final Logger LOG = new Logger(DefaultAppsPickerBasePreferenceController.class);
45     private static final String DIALOG_KEY_ARG = "key_arg";
46     protected static final String NONE_PREFERENCE_KEY = "";
47 
48     private final Map<String, DefaultAppInfo> mDefaultAppInfoMap = new HashMap<>();
49     private final ConfirmationDialogFragment.ConfirmListener mConfirmListener = arguments -> {
50         setCurrentDefault(arguments.getString(DIALOG_KEY_ARG));
51         notifyCheckedKeyChanged();
52     };
53 
DefaultAppsPickerBasePreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)54     public DefaultAppsPickerBasePreferenceController(Context context, String preferenceKey,
55             FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
56         super(context, preferenceKey, fragmentController, uxRestrictions);
57     }
58 
59     @Override
onCreateInternal()60     protected void onCreateInternal() {
61         ConfirmationDialogFragment.resetListeners(
62                 (ConfirmationDialogFragment) getFragmentController().findDialogByTag(
63                         ConfirmationDialogFragment.TAG),
64                 mConfirmListener,
65                 /* rejectListener= */ null,
66                 /* neutralListener= */ null);
67     }
68 
69     @Override
70     @NonNull
getGroupPreferences()71     protected List<TwoStatePreference> getGroupPreferences() {
72         List<TwoStatePreference> entries = new ArrayList<>();
73         if (includeNonePreference()) {
74             entries.add(createNoneOption());
75         }
76 
77         List<DefaultAppInfo> currentCandidates = getCandidates();
78         if (currentCandidates != null) {
79             for (DefaultAppInfo info : currentCandidates) {
80                 mDefaultAppInfoMap.put(info.getKey(), info);
81                 entries.add(createOption(info));
82             }
83         } else {
84             LOG.i("no candidate provided");
85         }
86 
87         return entries;
88     }
89 
90     @Override
handleGroupItemSelected(TwoStatePreference preference)91     protected final boolean handleGroupItemSelected(TwoStatePreference preference) {
92         String selectedKey = preference.getKey();
93         if (TextUtils.equals(selectedKey, getCurrentCheckedKey())) {
94             return false;
95         }
96 
97         CharSequence message = getConfirmationMessage(mDefaultAppInfoMap.get(selectedKey));
98         if (!TextUtils.isEmpty(message)) {
99             ConfirmationDialogFragment dialogFragment =
100                     new ConfirmationDialogFragment.Builder(getContext())
101                             .setMessage(message.toString())
102                             .setPositiveButton(android.R.string.ok, mConfirmListener)
103                             .setNegativeButton(android.R.string.cancel, /* rejectListener= */ null)
104                             .addArgumentString(DIALOG_KEY_ARG, selectedKey)
105                             .build();
106             getFragmentController().showDialog(dialogFragment, ConfirmationDialogFragment.TAG);
107             return false;
108         }
109 
110         setCurrentDefault(selectedKey);
111         return true;
112     }
113 
114     @Override
getCurrentCheckedKey()115     protected final String getCurrentCheckedKey() {
116         return getCurrentDefaultKey();
117     }
118 
createOption(DefaultAppInfo info)119     protected TwoStatePreference createOption(DefaultAppInfo info) {
120         CarUiRadioButtonPreference preference = new CarUiRadioButtonPreference(getContext());
121         preference.setKey(info.getKey());
122         preference.setTitle(info.loadLabel());
123         preference.setIcon(DefaultAppUtils.getSafeIcon(info.loadIcon(),
124                 getContext().getResources().getInteger(R.integer.default_app_safe_icon_size)));
125         preference.setEnabled(info.enabled);
126         return preference;
127     }
128 
129     /** Gets all of the candidates that should be considered when choosing a default application. */
130     @NonNull
getCandidates()131     protected abstract List<DefaultAppInfo> getCandidates();
132 
133     /** Gets the key of the currently selected candidate. */
getCurrentDefaultKey()134     protected abstract String getCurrentDefaultKey();
135 
136     /**
137      * Sets the key of the currently selected candidate. The implementation of this method should
138      * modify the value returned by {@link #getCurrentDefaultKey()}}.
139      *
140      * @param key represents the key from {@link DefaultAppInfo} which should mark the default
141      *            application.
142      */
setCurrentDefault(String key)143     protected abstract void setCurrentDefault(String key);
144 
145     /**
146      * Defines the warning dialog message to be shown when a default app is selected.
147      */
getConfirmationMessage(DefaultAppInfo info)148     protected CharSequence getConfirmationMessage(DefaultAppInfo info) {
149         return null;
150     }
151 
152     /** Gets the current process user id. */
getCurrentProcessUserId()153     protected int getCurrentProcessUserId() {
154         return UserHandle.myUserId();
155     }
156 
157     /**
158      * Determines whether the list of default apps should include "none". Implementation classes can
159      * override this value to {@code false} in order to remove the "none" preference.
160      */
includeNonePreference()161     protected boolean includeNonePreference() {
162         return true;
163     }
164 
createNoneOption()165     private CarUiRadioButtonPreference createNoneOption() {
166         CarUiRadioButtonPreference preference = new CarUiRadioButtonPreference(getContext());
167         preference.setKey(NONE_PREFERENCE_KEY);
168         preference.setTitle(R.string.app_list_preference_none);
169         preference.setIcon(R.drawable.ic_remove_circle);
170         return preference;
171     }
172 }
173