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 setInstanceForTest(MotorolaHiddenMenuKeySequence instance)74 static void setInstanceForTest(MotorolaHiddenMenuKeySequence instance) { 75 MotorolaHiddenMenuKeySequence.instance = instance; 76 } 77 78 @VisibleForTesting MotorolaHiddenMenuKeySequence( Context context, SystemPropertiesAccessor systemPropertiesAccessor)79 MotorolaHiddenMenuKeySequence( 80 Context context, SystemPropertiesAccessor systemPropertiesAccessor) { 81 if (MotorolaUtils.isSupportingHiddenMenu(context)) { 82 Collections.addAll( 83 hiddenKeySequences, 84 context.getResources().getStringArray(R.array.motorola_hidden_menu_key_sequence)); 85 Collections.addAll( 86 hiddenKeySequenceIntents, 87 context.getResources().getStringArray(R.array.motorola_hidden_menu_key_sequence_intents)); 88 Collections.addAll( 89 hiddenKeyPatterns, 90 context.getResources().getStringArray(R.array.motorola_hidden_menu_key_pattern)); 91 Collections.addAll( 92 hiddenKeyPatternIntents, 93 context.getResources().getStringArray(R.array.motorola_hidden_menu_key_pattern_intents)); 94 featureHiddenMenuEnabled = true; 95 } 96 97 if ("tracfone".equals(systemPropertiesAccessor.get("ro.carrier"))) { 98 addHiddenKeySequence("#83865625#", "com.motorola.extensions.TFUnlock"); 99 addHiddenKeySequence("#83782887#", "com.motorola.extensions.TFStatus"); 100 featureHiddenMenuEnabled = true; 101 } 102 103 if (hiddenKeySequences.size() != hiddenKeySequenceIntents.size() 104 || hiddenKeyPatterns.size() != hiddenKeyPatternIntents.size() 105 || (hiddenKeySequences.isEmpty() && hiddenKeyPatterns.isEmpty())) { 106 LogUtil.e( 107 "MotorolaHiddenMenuKeySequence", 108 "the key sequence array is not matching, turn off feature." 109 + "key sequence: %d != %d, key pattern %d != %d", 110 hiddenKeySequences.size(), 111 hiddenKeySequenceIntents.size(), 112 hiddenKeyPatterns.size(), 113 hiddenKeyPatternIntents.size()); 114 featureHiddenMenuEnabled = false; 115 } 116 } 117 addHiddenKeySequence(String keySequence, String intentAction)118 private void addHiddenKeySequence(String keySequence, String intentAction) { 119 hiddenKeySequences.add(keySequence); 120 hiddenKeySequenceIntents.add(intentAction); 121 } 122 handleKeyPattern(Context context, String input)123 private static boolean handleKeyPattern(Context context, String input) { 124 MotorolaHiddenMenuKeySequence instance = getInstance(context); 125 126 int len = input.length(); 127 if (len <= 3 128 || instance.hiddenKeyPatterns == null 129 || instance.hiddenKeyPatternIntents == null) { 130 return false; 131 } 132 133 for (int i = 0; i < instance.hiddenKeyPatterns.size(); i++) { 134 if (Pattern.matches(instance.hiddenKeyPatterns.get(i), input)) { 135 return sendIntent(context, input, instance.hiddenKeyPatternIntents.get(i)); 136 } 137 } 138 return false; 139 } 140 handleKeySequence(Context context, String input)141 private static boolean handleKeySequence(Context context, String input) { 142 MotorolaHiddenMenuKeySequence instance = getInstance(context); 143 144 int len = input.length(); 145 if (len <= 3 146 || instance.hiddenKeySequences == null 147 || instance.hiddenKeySequenceIntents == null) { 148 return false; 149 } 150 151 for (int i = 0; i < instance.hiddenKeySequences.size(); i++) { 152 if (instance.hiddenKeySequences.get(i).equals(input)) { 153 return sendIntent(context, input, instance.hiddenKeySequenceIntents.get(i)); 154 } 155 } 156 return false; 157 } 158 sendIntent( final Context context, final String input, final String action)159 private static boolean sendIntent( 160 final Context context, final String input, final String action) { 161 LogUtil.d("MotorolaHiddenMenuKeySequence.sendIntent", "input: %s", input); 162 try { 163 Intent intent = new Intent(action); 164 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); 165 intent.putExtra(EXTRA_HIDDEN_MENU_CODE, input); 166 167 ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 0); 168 169 if (resolveInfo != null 170 && resolveInfo.activityInfo != null 171 && resolveInfo.activityInfo.enabled) { 172 context.startActivity(intent); 173 return true; 174 } else { 175 LogUtil.w("MotorolaHiddenMenuKeySequence.sendIntent", "not able to resolve the intent"); 176 } 177 } catch (ActivityNotFoundException e) { 178 LogUtil.e( 179 "MotorolaHiddenMenuKeySequence.sendIntent", "handleHiddenMenu Key Pattern Exception", e); 180 } 181 return false; 182 } 183 } 184