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