1 /*
2  * Copyright (C) 2021 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.qc;
18 
19 import static com.android.car.settings.common.PreferenceController.AVAILABLE_FOR_VIEWING_FOR_ZONE;
20 import static com.android.car.settings.common.PreferenceController.AVAILABLE_FOR_ZONE;
21 import static com.android.car.settings.common.PreferenceController.HIDDEN_FOR_ZONE;
22 import static com.android.car.settings.common.PreferenceXmlParser.PREF_AVAILABILITY_STATUS_HIDDEN;
23 import static com.android.car.settings.common.PreferenceXmlParser.PREF_AVAILABILITY_STATUS_READ;
24 
25 import android.app.PendingIntent;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.net.Uri;
29 import android.os.Bundle;
30 
31 import com.android.car.qc.QCItem;
32 
33 import java.lang.reflect.Constructor;
34 import java.lang.reflect.InvocationTargetException;
35 
36 /**
37  * Base class for QCItems provided by CarSettings.
38  */
39 public abstract class SettingsQCItem {
40 
41     private final Context mContext;
42     private int mAvailabilityStatusForZone;
43 
SettingsQCItem(Context context)44     public SettingsQCItem(Context context) {
45         mContext = context;
46     }
47 
setAvailabilityStatusForZone(String availabilityStatusForZone)48     protected void setAvailabilityStatusForZone(String availabilityStatusForZone) {
49         if (PREF_AVAILABILITY_STATUS_READ.equals(availabilityStatusForZone)) {
50             mAvailabilityStatusForZone = AVAILABLE_FOR_VIEWING_FOR_ZONE;
51         } else if (PREF_AVAILABILITY_STATUS_HIDDEN.equals(availabilityStatusForZone)) {
52             mAvailabilityStatusForZone = HIDDEN_FOR_ZONE;
53         } else {
54             mAvailabilityStatusForZone = AVAILABLE_FOR_ZONE;
55         }
56     }
57 
58     /**
59      * @return an complete instance of the {@link QCItem}.
60      */
getQCItem()61     abstract QCItem getQCItem();
62 
63     /**
64      * @return a {@link android.content.ContentResolver#SCHEME_CONTENT content} {@link Uri} which
65      * backs the {@link QCItem} returned by {@link #getQCItem()}.
66      */
getUri()67     abstract Uri getUri();
68 
69     /**
70      * @return the context for the {@link SettingsQCItem}.
71      */
getContext()72     protected Context getContext() {
73         return mContext;
74     }
75 
76     /**
77      * @return a boolean that indicates whether the QCItem is hidden for the current zone.
78      */
isHiddenForZone()79     protected boolean isHiddenForZone() {
80         return mAvailabilityStatusForZone == HIDDEN_FOR_ZONE;
81     }
82 
83     /**
84      * @return a boolean that indicates whether the QCItem is only readable for the current zone.
85      */
isReadOnlyForZone()86     protected boolean isReadOnlyForZone() {
87         return mAvailabilityStatusForZone == AVAILABLE_FOR_VIEWING_FOR_ZONE;
88     }
89 
90     /**
91      * @return a boolean that indicates whether the QCItem is writable for the current zone.
92      */
isWritableForZone()93     protected boolean isWritableForZone() {
94         return mAvailabilityStatusForZone == AVAILABLE_FOR_ZONE;
95     }
96 
97     /**
98      * Handles the actions sent by the {@link Intent intents} bound to the {@link QCItem} returned
99      * by {@link #getQCItem()}.
100      *
101      * @param intent which has the action taken on a {@link QCItem}.
102      */
onNotifyChange(Intent intent)103     void onNotifyChange(Intent intent) {}
104 
105     /**
106      * Standardize the primary intent for the QCItem.
107      */
getActivityIntent(Intent intent)108     PendingIntent getActivityIntent(Intent intent) {
109         return PendingIntent.getActivity(getContext(),
110                 0 /* requestCode */, intent,
111                 PendingIntent.FLAG_IMMUTABLE);
112     }
113 
114     /**
115      * See {@link #getBroadcastIntent(Bundle, int)}
116      */
getBroadcastIntent()117     PendingIntent getBroadcastIntent() {
118         return getBroadcastIntent(/* extras= */ null);
119     }
120 
121     /**
122      * See {@link #getBroadcastIntent(Bundle, int)}
123      */
getBroadcastIntent(Bundle extras)124     PendingIntent getBroadcastIntent(Bundle extras) {
125         return getBroadcastIntent(extras, /* requestCode= */ 0);
126     }
127 
128     /**
129      * Standardize the intents returned to indicate actions by the QCItem.
130      * <p>
131      *     The {@link PendingIntent} is linked to {@link SettingsQCBroadcastReceiver} where the
132      *     Intent Action is found by {@code getUri().toString()}.
133      *
134      * @return a {@link PendingIntent} linked to {@link SettingsQCBroadcastReceiver}.
135      */
getBroadcastIntent(Bundle extras, int requestCode)136     PendingIntent getBroadcastIntent(Bundle extras, int requestCode) {
137         Intent intent = new Intent(getUri().toString())
138                 .setData(getUri())
139                 .setClass(getContext(), SettingsQCBroadcastReceiver.class)
140                 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
141         if (extras != null) {
142             intent.putExtras(extras);
143         }
144         return PendingIntent.getBroadcast(getContext(), requestCode, intent,
145                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
146     }
147 
148     /**
149      * Build an instance of a {@link SettingsQCItem} which has a {@link Context}-only constructor.
150      */
createInstance(Context context, Class<? extends SettingsQCItem> qcItem)151     static SettingsQCItem createInstance(Context context,
152             Class<? extends SettingsQCItem> qcItem) {
153         try {
154             Constructor<? extends SettingsQCItem> constructor =
155                     qcItem.getConstructor(Context.class);
156             Object[] params = new Object[]{context.getApplicationContext()};
157             return constructor.newInstance(params);
158         } catch (NoSuchMethodException | InstantiationException | IllegalArgumentException
159                 | InvocationTargetException | IllegalAccessException e) {
160             throw new IllegalStateException(
161                     "Invalid SettingsQCItem class: " + qcItem, e);
162         }
163     }
164 
165     /**
166      * Settings QCItems which require background work, such as updating lists should implement a
167      * {@link SettingsQCBackgroundWorker} and return it here. An example of background work is
168      * updating a list of Wifi networks available in the area.
169      *
170      * @return a {@link Class<? extends SettingsQCBackgroundWorker>} to perform background work for
171      * the QCItem.
172      */
getBackgroundWorkerClass()173     Class<? extends SettingsQCBackgroundWorker> getBackgroundWorkerClass() {
174         return null;
175     }
176 }
177