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 */
19package com.android.dialer.oem;
20
21import android.content.ActivityNotFoundException;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.ResolveInfo;
25import com.android.dialer.common.LogUtil;
26import java.util.regex.Pattern;
27
28/**
29 * Util class to handle special char sequence and launch corresponding intent based the sequence.
30 */
31public class MotorolaHiddenMenuKeySequence {
32  private static final String EXTRA_HIDDEN_MENU_CODE = "HiddenMenuCode";
33  private static MotorolaHiddenMenuKeySequence instance = null;
34
35  private static String[] hiddenKeySequenceArray = null;
36  private static String[] hiddenKeySequenceIntentArray = null;
37  private static String[] hiddenKeyPatternArray = null;
38  private static String[] hiddenKeyPatternIntentArray = null;
39  private static boolean featureHiddenMenuEnabled = false;
40
41  /**
42   * Handle input char sequence.
43   *
44   * @param context context
45   * @param input input sequence
46   * @return true if the input matches any pattern
47   */
48  static boolean handleCharSequence(Context context, String input) {
49    getInstance(context);
50    if (!featureHiddenMenuEnabled) {
51      return false;
52    }
53    return handleKeySequence(context, input) || handleKeyPattern(context, input);
54  }
55
56  /**
57   * Public interface to return the Singleton instance
58   *
59   * @param context the Context
60   * @return the MotorolaHiddenMenuKeySequence singleton instance
61   */
62  private static synchronized MotorolaHiddenMenuKeySequence getInstance(Context context) {
63    if (null == instance) {
64      instance = new MotorolaHiddenMenuKeySequence(context);
65    }
66    return instance;
67  }
68
69  private MotorolaHiddenMenuKeySequence(Context context) {
70    featureHiddenMenuEnabled = MotorolaUtils.isSupportingHiddenMenu(context);
71    // In case we do have a SPN from resource we need to match from service; otherwise we are
72    // free to go
73    if (featureHiddenMenuEnabled) {
74
75      hiddenKeySequenceArray =
76          context.getResources().getStringArray(R.array.motorola_hidden_menu_key_sequence);
77      hiddenKeySequenceIntentArray =
78          context.getResources().getStringArray(R.array.motorola_hidden_menu_key_sequence_intents);
79      hiddenKeyPatternArray =
80          context.getResources().getStringArray(R.array.motorola_hidden_menu_key_pattern);
81      hiddenKeyPatternIntentArray =
82          context.getResources().getStringArray(R.array.motorola_hidden_menu_key_pattern_intents);
83
84      if (hiddenKeySequenceArray.length != hiddenKeySequenceIntentArray.length
85          || hiddenKeyPatternArray.length != hiddenKeyPatternIntentArray.length
86          || (hiddenKeySequenceArray.length == 0 && hiddenKeyPatternArray.length == 0)) {
87        LogUtil.e(
88            "MotorolaHiddenMenuKeySequence",
89            "the key sequence array is not matching, turn off feature."
90                + "key sequence: %d != %d, key pattern %d != %d",
91            hiddenKeySequenceArray.length,
92            hiddenKeySequenceIntentArray.length,
93            hiddenKeyPatternArray.length,
94            hiddenKeyPatternIntentArray.length);
95        featureHiddenMenuEnabled = false;
96      }
97    }
98  }
99
100  private static boolean handleKeyPattern(Context context, String input) {
101    int len = input.length();
102    if (len <= 3 || hiddenKeyPatternArray == null || hiddenKeyPatternIntentArray == null) {
103      return false;
104    }
105
106    for (int i = 0; i < hiddenKeyPatternArray.length; i++) {
107      if ((Pattern.compile(hiddenKeyPatternArray[i])).matcher(input).matches()) {
108        return sendIntent(context, input, hiddenKeyPatternIntentArray[i]);
109      }
110    }
111    return false;
112  }
113
114  private static boolean handleKeySequence(Context context, String input) {
115    int len = input.length();
116    if (len <= 3 || hiddenKeySequenceArray == null || hiddenKeySequenceIntentArray == null) {
117      return false;
118    }
119
120    for (int i = 0; i < hiddenKeySequenceArray.length; i++) {
121      if (hiddenKeySequenceArray[i].equals(input)) {
122        return sendIntent(context, input, hiddenKeySequenceIntentArray[i]);
123      }
124    }
125    return false;
126  }
127
128  private static boolean sendIntent(
129      final Context context, final String input, final String action) {
130    LogUtil.d("MotorolaHiddenMenuKeySequence.sendIntent", "input: %s", input);
131    try {
132      Intent intent = new Intent(action);
133      intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
134      intent.putExtra(EXTRA_HIDDEN_MENU_CODE, input);
135
136      ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 0);
137
138      if (resolveInfo != null
139          && resolveInfo.activityInfo != null
140          && resolveInfo.activityInfo.enabled) {
141        context.startActivity(intent);
142        return true;
143      } else {
144        LogUtil.w("MotorolaHiddenMenuKeySequence.sendIntent", "not able to resolve the intent");
145      }
146    } catch (ActivityNotFoundException e) {
147      LogUtil.e(
148          "MotorolaHiddenMenuKeySequence.sendIntent", "handleHiddenMenu Key Pattern Exception", e);
149    }
150    return false;
151  }
152}
153