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.assist;
18 
19 import android.car.drivingstate.CarUxRestrictions;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.database.ContentObserver;
23 import android.net.Uri;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.os.UserHandle;
27 import android.provider.Settings;
28 
29 import androidx.annotation.VisibleForTesting;
30 import androidx.preference.TwoStatePreference;
31 
32 import com.android.car.settings.common.FragmentController;
33 import com.android.car.settings.common.PreferenceController;
34 import com.android.internal.app.AssistUtils;
35 
36 import java.util.List;
37 
38 /** Common logic for preference controllers that configure the assistant's behavior. */
39 public abstract class AssistConfigBasePreferenceController extends
40         PreferenceController<TwoStatePreference> {
41 
42     final SettingObserver mSettingObserver;
43     private final AssistUtils mAssistUtils;
44 
AssistConfigBasePreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)45     public AssistConfigBasePreferenceController(Context context, String preferenceKey,
46             FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
47         this(context, preferenceKey, fragmentController, uxRestrictions, new AssistUtils(context));
48     }
49 
50     @VisibleForTesting
AssistConfigBasePreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions, AssistUtils assistUtils)51     AssistConfigBasePreferenceController(Context context, String preferenceKey,
52             FragmentController fragmentController, CarUxRestrictions uxRestrictions,
53             AssistUtils assistUtils) {
54         super(context, preferenceKey, fragmentController, uxRestrictions);
55         mAssistUtils = assistUtils;
56         mSettingObserver = new SettingObserver(getSettingUris(), this::refreshUi);
57     }
58 
59     @Override
getPreferenceType()60     protected Class<TwoStatePreference> getPreferenceType() {
61         return TwoStatePreference.class;
62     }
63 
64     @Override
getDefaultAvailabilityStatus()65     protected int getDefaultAvailabilityStatus() {
66         return mAssistUtils.getAssistComponentForUser(
67                 UserHandle.myUserId()) != null ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
68     }
69 
70     @Override
onStartInternal()71     protected void onStartInternal() {
72         mSettingObserver.register(getContext().getContentResolver(), true);
73     }
74 
75     @Override
onStopInternal()76     protected void onStopInternal() {
77         mSettingObserver.register(getContext().getContentResolver(), false);
78     }
79 
80     /** Gets the Setting Uris that should be observed */
getSettingUris()81     protected abstract List<Uri> getSettingUris();
82 
83     /**
84      * Creates an observer that listens for changes to {@link Settings.Secure#ASSISTANT} as well as
85      * any other URI defined by {@link #getSettingUris()}.
86      */
87     @VisibleForTesting
88     static class SettingObserver extends ContentObserver {
89         private static final Uri ASSIST_URI = Settings.Secure.getUriFor(Settings.Secure.ASSISTANT);
90         private final List<Uri> mUriList;
91         private final Runnable mSettingChangeListener;
92 
SettingObserver(List<Uri> uriList, Runnable settingChangeListener)93         SettingObserver(List<Uri> uriList, Runnable settingChangeListener) {
94             super(new Handler(Looper.getMainLooper()));
95             mUriList = uriList;
96             mSettingChangeListener = settingChangeListener;
97         }
98 
99         /** Registers or unregisters this observer to the given content resolver. */
register(ContentResolver cr, boolean register)100         void register(ContentResolver cr, boolean register) {
101             if (register) {
102                 cr.registerContentObserver(ASSIST_URI, /* notifyForDescendants= */ false,
103                         /* observer= */ this);
104                 if (mUriList != null) {
105                     for (Uri uri : mUriList) {
106                         cr.registerContentObserver(uri, /* notifyForDescendants= */ false,
107                                 /* observer= */ this);
108                     }
109                 }
110             } else {
111                 cr.unregisterContentObserver(this);
112             }
113         }
114 
115         @Override
onChange(boolean selfChange, Uri uri)116         public void onChange(boolean selfChange, Uri uri) {
117             super.onChange(selfChange, uri);
118 
119             if (shouldUpdatePreference(uri)) {
120                 mSettingChangeListener.run();
121             }
122         }
123 
shouldUpdatePreference(Uri uri)124         private boolean shouldUpdatePreference(Uri uri) {
125             return ASSIST_URI.equals(uri) || (mUriList != null && mUriList.contains(uri));
126         }
127     }
128 }
129