1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * This file is derived in part from code issued under the following license. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 */ 19 package com.android.dialer.oem; 20 21 import android.content.ActivityNotFoundException; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.ResolveInfo; 25 import android.support.annotation.VisibleForTesting; 26 import com.android.dialer.common.LogUtil; 27 import java.util.ArrayList; 28 import java.util.Collections; 29 import java.util.List; 30 import java.util.regex.Pattern; 31 32 /** 33 * Util class to handle special char sequence and launch corresponding intent based the sequence. 34 */ 35 public class MotorolaHiddenMenuKeySequence { 36 private static final String EXTRA_HIDDEN_MENU_CODE = "HiddenMenuCode"; 37 38 private static MotorolaHiddenMenuKeySequence instance = null; 39 40 @VisibleForTesting final List<String> hiddenKeySequences = new ArrayList<>(); 41 @VisibleForTesting final List<String> hiddenKeySequenceIntents = new ArrayList<>(); 42 @VisibleForTesting final List<String> hiddenKeyPatterns = new ArrayList<>(); 43 @VisibleForTesting final List<String> hiddenKeyPatternIntents = new ArrayList<>(); 44 @VisibleForTesting boolean featureHiddenMenuEnabled = false; 45 46 /** 47 * Handle input char sequence. 48 * 49 * @param context context 50 * @param input input sequence 51 * @return true if the input matches any pattern 52 */ handleCharSequence(Context context, String input)53 static boolean handleCharSequence(Context context, String input) { 54 if (!getInstance(context).featureHiddenMenuEnabled) { 55 return false; 56 } 57 return handleKeySequence(context, input) || handleKeyPattern(context, input); 58 } 59 60 /** 61 * Public interface to return the Singleton instance 62 * 63 * @param context the Context 64 * @return the MotorolaHiddenMenuKeySequence singleton instance 65 */ getInstance(Context context)66 private static synchronized MotorolaHiddenMenuKeySequence getInstance(Context context) { 67 if (null == instance) { 68 instance = new MotorolaHiddenMenuKeySequence(context, new SystemPropertiesAccessor()); 69 } 70 return instance; 71 } 72 73 @VisibleForTesting MotorolaHiddenMenuKeySequence( Context context, SystemPropertiesAccessor systemPropertiesAccessor)74 MotorolaHiddenMenuKeySequence( 75 Context context, SystemPropertiesAccessor systemPropertiesAccessor) { 76 if (MotorolaUtils.isSupportingHiddenMenu(context)) { 77 Collections.addAll( 78 hiddenKeySequences, 79 context.getResources().getStringArray(R.array.motorola_hidden_menu_key_sequence)); 80 Collections.addAll( 81 hiddenKeySequenceIntents, 82 context.getResources().getStringArray(R.array.motorola_hidden_menu_key_sequence_intents)); 83 Collections.addAll( 84 hiddenKeyPatterns, 85 context.getResources().getStringArray(R.array.motorola_hidden_menu_key_pattern)); 86 Collections.addAll( 87 hiddenKeyPatternIntents, 88 context.getResources().getStringArray(R.array.motorola_hidden_menu_key_pattern_intents)); 89 featureHiddenMenuEnabled = true; 90 } 91 92 if ("tracfone".equals(systemPropertiesAccessor.get("ro.carrier"))) { 93 addHiddenKeySequence("#83865625#", "com.motorola.extensions.TFUnlock"); 94 addHiddenKeySequence("#83782887#", "com.motorola.extensions.TFStatus"); 95 featureHiddenMenuEnabled = true; 96 } 97 98 if (hiddenKeySequences.size() != hiddenKeySequenceIntents.size() 99 || hiddenKeyPatterns.size() != hiddenKeyPatternIntents.size() 100 || (hiddenKeySequences.isEmpty() && hiddenKeyPatterns.isEmpty())) { 101 LogUtil.e( 102 "MotorolaHiddenMenuKeySequence", 103 "the key sequence array is not matching, turn off feature." 104 + "key sequence: %d != %d, key pattern %d != %d", 105 hiddenKeySequences.size(), 106 hiddenKeySequenceIntents.size(), 107 hiddenKeyPatterns.size(), 108 hiddenKeyPatternIntents.size()); 109 featureHiddenMenuEnabled = false; 110 } 111 } 112 addHiddenKeySequence(String keySequence, String intentAction)113 private void addHiddenKeySequence(String keySequence, String intentAction) { 114 hiddenKeySequences.add(keySequence); 115 hiddenKeySequenceIntents.add(intentAction); 116 } 117 handleKeyPattern(Context context, String input)118 private static boolean handleKeyPattern(Context context, String input) { 119 MotorolaHiddenMenuKeySequence instance = getInstance(context); 120 121 int len = input.length(); 122 if (len <= 3 123 || instance.hiddenKeyPatterns == null 124 || instance.hiddenKeyPatternIntents == null) { 125 return false; 126 } 127 128 for (int i = 0; i < instance.hiddenKeyPatterns.size(); i++) { 129 if (Pattern.matches(instance.hiddenKeyPatterns.get(i), input)) { 130 return sendIntent(context, input, instance.hiddenKeyPatternIntents.get(i)); 131 } 132 } 133 return false; 134 } 135 handleKeySequence(Context context, String input)136 private static boolean handleKeySequence(Context context, String input) { 137 MotorolaHiddenMenuKeySequence instance = getInstance(context); 138 139 int len = input.length(); 140 if (len <= 3 141 || instance.hiddenKeySequences == null 142 || instance.hiddenKeySequenceIntents == null) { 143 return false; 144 } 145 146 for (int i = 0; i < instance.hiddenKeySequences.size(); i++) { 147 if (instance.hiddenKeySequences.get(i).equals(input)) { 148 return sendIntent(context, input, instance.hiddenKeySequenceIntents.get(i)); 149 } 150 } 151 return false; 152 } 153 sendIntent( final Context context, final String input, final String action)154 private static boolean sendIntent( 155 final Context context, final String input, final String action) { 156 LogUtil.d("MotorolaHiddenMenuKeySequence.sendIntent", "input: %s", input); 157 try { 158 Intent intent = new Intent(action); 159 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); 160 intent.putExtra(EXTRA_HIDDEN_MENU_CODE, input); 161 162 ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 0); 163 164 if (resolveInfo != null 165 && resolveInfo.activityInfo != null 166 && resolveInfo.activityInfo.enabled) { 167 context.startActivity(intent); 168 return true; 169 } else { 170 LogUtil.w("MotorolaHiddenMenuKeySequence.sendIntent", "not able to resolve the intent"); 171 } 172 } catch (ActivityNotFoundException e) { 173 LogUtil.e( 174 "MotorolaHiddenMenuKeySequence.sendIntent", "handleHiddenMenu Key Pattern Exception", e); 175 } 176 return false; 177 } 178 } 179