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.dialer.ui.dialpad;
18 
19 import static android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_DIALPAD;
20 
21 import android.app.Application;
22 import android.car.Car;
23 import android.car.drivingstate.CarUxRestrictions;
24 import android.car.drivingstate.CarUxRestrictionsManager;
25 import android.content.Context;
26 
27 import androidx.annotation.NonNull;
28 import androidx.lifecycle.AndroidViewModel;
29 import androidx.lifecycle.LiveData;
30 import androidx.lifecycle.MutableLiveData;
31 import androidx.lifecycle.Transformations;
32 
33 import com.android.car.dialer.R;
34 
35 /**
36  * A view model to track the current dialpad restriction based.
37  *
38  * The current dialpad mode derives from the {@code CarUxRestrictionsManager} input, emergency
39  * numbers dialing restriction configuration and the digit already dialed.
40  */
41 public class DialpadRestrictionViewModel extends AndroidViewModel {
42     /** Current dialpad restriction mode descriptor. */
43     public enum DialpadUxrMode {
44         /** User can dial any number. */
45         UNRESTRICTED,
46         /**
47          * Dialing is restricted to emergency numbers only and the digit count limit is not yet
48          * reached.
49          */
50         RESTRICTED,
51         /**
52          * User cannot dial further digits: either the emergency number digit count limit is reached
53          * or the dialing is restricted entirely.
54          */
55         RESTRICTED_LIMIT_REACHED,
56         /** Dialing is disallowed. */
57         DISABLED
58     }
59 
60     private final CarUxRestrictionsManager mRestrictionsManager;
61 
62     private final MutableLiveData<DialpadUxrMode> mDialPadMode =
63             new MutableLiveData<>(DialpadUxrMode.UNRESTRICTED);
64 
65     private final LiveData<DialpadUxrMode> mObservableDialPadMode =
66             Transformations.distinctUntilChanged(mDialPadMode);
67 
68     private final int mRestrictedModeMaxDigitCount;
69 
70     @NonNull
71     private String mCurrentPhoneNumber = "";
72 
DialpadRestrictionViewModel(@onNull Application application)73     public DialpadRestrictionViewModel(@NonNull Application application) {
74         super(application);
75 
76         mRestrictedModeMaxDigitCount = getNoDialpadUxrMaxAllowedDigits(application);
77 
78         if (shouldEnforceNoDialpadRestriction(mRestrictedModeMaxDigitCount)) {
79             mRestrictionsManager = (CarUxRestrictionsManager)
80                     Car.createCar(application).getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
81 
82             onUxRestrictionsChanged(mRestrictionsManager.getCurrentCarUxRestrictions());
83             mRestrictionsManager.registerListener(this::onUxRestrictionsChanged);
84         } else {
85             mRestrictionsManager = null;
86         }
87     }
88 
89     @Override
onCleared()90     protected void onCleared() {
91         if (mRestrictionsManager != null) {
92             mRestrictionsManager.unregisterListener();
93         }
94     }
95 
getNoDialpadUxrMaxAllowedDigits(@onNull Context context)96     private static int getNoDialpadUxrMaxAllowedDigits(@NonNull Context context) {
97         return context.getResources().getInteger(R.integer.config_no_dialpad_uxr_max_allow_digits);
98     }
99 
shouldEnforceNoDialpadRestriction(int restrictedModeMaxDigitCount)100     private static boolean shouldEnforceNoDialpadRestriction(int restrictedModeMaxDigitCount) {
101         return restrictedModeMaxDigitCount >= 0;
102     }
103 
104     /**
105      * Return {@code true} if the current configuration implies enforcement of "no dialpad" UXR
106      * restriction.
107      */
shouldEnforceNoDialpadRestriction(@onNull Context context)108     public static boolean shouldEnforceNoDialpadRestriction(@NonNull Context context) {
109         return shouldEnforceNoDialpadRestriction(getNoDialpadUxrMaxAllowedDigits(context));
110     }
111 
112     /**
113      * Returns a {@code LiveData} to observe the current dialpad mode.
114      *
115      * Note: only state changes are propagated to observers.
116      */
getDialpadMode()117     public LiveData<DialpadUxrMode> getDialpadMode() {
118         return mObservableDialPadMode;
119     }
120 
121     /**
122      * Updates the currently dialed part of the phone number, which is immediately used to evaluate
123      * the restriction.
124      */
setCurrentPhoneNumber(@onNull String phoneNumber)125     public void setCurrentPhoneNumber(@NonNull String phoneNumber) {
126         mCurrentPhoneNumber = phoneNumber;
127 
128         if (mDialPadMode.getValue() != DialpadUxrMode.UNRESTRICTED) {
129             mDialPadMode.setValue(evaluateDialpadRestrictionMode(mCurrentPhoneNumber));
130         }
131     }
132 
evaluateDialpadMode(CarUxRestrictions restrictions, String phoneNumber)133     private DialpadUxrMode evaluateDialpadMode(CarUxRestrictions restrictions, String phoneNumber) {
134         return (restrictions.getActiveRestrictions() & UX_RESTRICTIONS_NO_DIALPAD) == 0
135                 ? DialpadUxrMode.UNRESTRICTED : evaluateDialpadRestrictionMode(phoneNumber);
136     }
137 
138     /**
139      * Evaluates the dialpad mode for a given phone number, assuming the dialpad restrictions are
140      * enforced by the configuration.
141      */
evaluateDialpadRestrictionMode(String phoneNumber)142     private DialpadUxrMode evaluateDialpadRestrictionMode(String phoneNumber) {
143         if (mRestrictedModeMaxDigitCount == 0) {
144             return DialpadUxrMode.DISABLED;
145         } else if (phoneNumber.length() < mRestrictedModeMaxDigitCount) {
146             return DialpadUxrMode.RESTRICTED;
147         } else {
148             return DialpadUxrMode.RESTRICTED_LIMIT_REACHED;
149         }
150     }
151 
onUxRestrictionsChanged(CarUxRestrictions restrictions)152     private void onUxRestrictionsChanged(CarUxRestrictions restrictions) {
153         mDialPadMode.setValue(evaluateDialpadMode(restrictions, mCurrentPhoneNumber));
154     }
155 }
156 
157