1 /*
2  * Copyright (C) 2020 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.uxr;
18 
19 import android.car.drivingstate.CarUxRestrictions;
20 import android.content.Context;
21 import android.util.Log;
22 
23 import androidx.annotation.IdRes;
24 import androidx.annotation.NonNull;
25 import androidx.annotation.XmlRes;
26 
27 import com.android.car.ui.recyclerview.ContentLimiting;
28 import com.android.car.ui.utils.CarUxRestrictionsUtil;
29 import com.android.car.uxr.CarUxRestrictionsAppConfig.ListConfig;
30 
31 /**
32  * A class that can work together with a {@link ContentLimiting} {@link
33  * androidx.recyclerview.widget.RecyclerView.Adapter} object to provide content limiting ability
34  * based on changes to the state of the car, by listening for the latest {@link CarUxRestrictions}.
35  *
36  * <p>This class manages 3 things:
37  * <ul>
38  *     <li> Communications with the User Experience Restriction Engine
39  *     <li> Looking up app-side overrides for customizing the content-limiting behavior of a given
40  *     list of items in a particular screen
41  *     <li> Relaying the relevant parts of that information to the registered
42  *     adapter at the right time
43  * </ul>
44  *
45  * <p>The app-side overrides are accessed via the {@link CarUxRestrictionsAppConfig} object.
46  *
47  * <p>Because all but one of the dependencies for this class can be instantiated as soon as a
48  * {@link Context} is available, we provide a separate {@link #setAdapter(ContentLimiting)}
49  * API for linking the targeted adapter. That way the registration can happen in a different part of
50  * code, and potentially in a different lifecycle method to provide maximum flexibility.
51  */
52 public class UxrContentLimiterImpl implements UxrContentLimiter {
53 
54     private ContentLimiting mAdapter;
55     private ListConfig mListConfig;
56 
57     private final CarUxRestrictionsUtil mCarUxRestrictionsUtil;
58     private final CarUxRestrictionsAppConfig mCarUxRestrictionsAppConfig;
59     private final CarUxRestrictionsUtil.OnUxRestrictionsChangedListener mListener =
60             new Listener();
61 
62     private class Listener implements CarUxRestrictionsUtil.OnUxRestrictionsChangedListener {
63         private static final String TAG = "ContentLimitListener";
64 
65         @Override
onRestrictionsChanged(@onNull CarUxRestrictions carUxRestrictions)66         public void onRestrictionsChanged(@NonNull CarUxRestrictions carUxRestrictions) {
67             if (mAdapter == null) {
68                 Log.w(TAG, "No adapter registered.");
69                 return;
70             }
71 
72             int maxItems = getMaxItemsToUse(carUxRestrictions, mAdapter.getConfigurationId());
73             logD("New limit " + maxItems);
74             mAdapter.setMaxItems(maxItems);
75         }
76 
getMaxItemsToUse(CarUxRestrictions carUxRestrictions, @IdRes int id)77         private int getMaxItemsToUse(CarUxRestrictions carUxRestrictions, @IdRes int id) {
78             // Unrelated restrictions are active. Quit early.
79             if ((carUxRestrictions.getActiveRestrictions()
80                     & CarUxRestrictions.UX_RESTRICTIONS_LIMIT_CONTENT)
81                     == 0) {
82                 logD("Lists are unrestricted.");
83                 return ContentLimiting.UNLIMITED;
84             }
85 
86             if (mListConfig == null || mListConfig.getContentLimit() == null) {
87                 logD("No configs found for adapter with the ID: " + id
88                         + " Using the default limit");
89                 return carUxRestrictions.getMaxCumulativeContentItems();
90             }
91 
92             logD("Using the provided override.");
93             return mListConfig.getContentLimit();
94         }
95 
logD(String s)96         private void logD(String s) {
97             if (Log.isLoggable(TAG, Log.DEBUG)) {
98                 Log.d(TAG, s);
99             }
100         }
101     }
102 
103     /**
104      * Constructs a {@link UxrContentLimiterImpl} object given the app context and the XML resource
105      * file to parse the User Experience Restriction override configs from.
106      *
107      * @param context - the app context
108      * @param xmlRes  - the UXR override config XML resource
109      */
UxrContentLimiterImpl(Context context, @XmlRes int xmlRes)110     public UxrContentLimiterImpl(Context context, @XmlRes int xmlRes) {
111         mCarUxRestrictionsUtil = CarUxRestrictionsUtil.getInstance(context);
112         mCarUxRestrictionsAppConfig = CarUxRestrictionsAppConfig.getInstance(context, xmlRes);
113     }
114 
115     @Override
setAdapter(ContentLimiting adapter)116     public void setAdapter(ContentLimiting adapter) {
117         mAdapter = adapter;
118         int key = mAdapter.getConfigurationId();
119         if (mCarUxRestrictionsAppConfig.getMapping().containsKey(key)) {
120             mListConfig = mCarUxRestrictionsAppConfig.getMapping().get(key);
121             Integer overriddenMessageResId = mListConfig.getScrollingLimitedMessageResId();
122             if (overriddenMessageResId != null) {
123                 mAdapter.setScrollingLimitedMessageResId(overriddenMessageResId);
124             }
125         }
126     }
127 
128     /**
129      * Start listening for changes to {@link CarUxRestrictions}.
130      */
start()131     public void start() {
132         mCarUxRestrictionsUtil.register(mListener);
133     }
134 
135     /**
136      * Stop listening for changes to {@link CarUxRestrictions}.
137      */
stop()138     public void stop() {
139         mCarUxRestrictionsUtil.unregister(mListener);
140     }
141 }
142