Add a secret trigger to display debug information

You can set up a secret trigger to display SDK debug information. The debug information is useful for our technical support team when providing help.

The mobile user ID and SDK version are required. There are many other items you may want to include in a debug screen, such as:

  • OS version and platform,
  • build version of your app,
  • channel ID,
  • app key.

If you have this information available, it becomes easier to resolve problems. You can keep this information hidden until it's needed.

The following steps are one possible solution to add a secret trigger. You may want to use a different way to trigger and open the debug screen. You might also want to consider issues with accessibility.

📘

Note:

The following steps show making changes to the sample app. Changes to your app will be similar.

  1. Decide on a trigger location. You will need to find an appropriate place in your app from where a user starts the trigger. Often the configuration screen is used. If you have many elements on that screen, another view may be preferable. It's best if it's a screen where you're not already collecting gesture events. A view that has a text entry field offers more security than one that doesn't. In this case, we will be adding the event to the Send User Attributes screen in the sample app.
  2. Make the following changes to your app.
  1. Add the following variables to AttributesVC.m in the @implementation section:
    
    
    // Secret diagnostics // Variables required // Change pattern to indicate the sequence of triple-clicks required to activate // the secret action. // Note that 1=upper left, 2=upper right, 3=lower left, 4=lower right static int pattern[] = {1, 2, 3, 4, 3, 2, 3, 2}; int patternIndex = 0;

    The pattern static variable defines the screen locations where the user must triple-tap to trigger the event. In this case, we need the user to triple-tap in the following pattern:

    • upper left,
    • then the upper right,
    • then the lower left,
    • then the lower right,
    • then again, in the upper right,
    • then lower left,
    • then upper right.

    When the user follows the pattern, the event is triggered. Any deviation from the pattern resets it back to the beginning. We recommend you change the sample pattern. You might want to use a different pattern for each app or even each major revision of the app. Your support reps will need to be able to tell people how to trigger the diagnostic event. If it's too complicated, you may need to adjust it.

  2. Add the following three methods to the bottom of the view controller before @end in AttributesVC.m in the sample.
    
    
    // Secret diagnostics // Detect triple-taps in various quadrants of the screen // Change the values of fieldString and requiredString to your choice // Underlying algorithm from https://stackoverflow.com/questions/48441348/whats-the-best-way-to-implement-hidden-debug-options - (void) tryKey: (UITapGestureRecognizer *)sender { NSString *fieldString = self.nameTextField.text; NSString *requiredString = @"text"; if (![fieldString isEqualToString: requiredString]) { return; } CGPoint touchLocation = [sender locationInView:sender.view]; [self checkPatternAndOpen:[self mapTouchLocationToInt:touchLocation]]; } - (void)checkPatternAndOpen: (int)nextValue { NSLog(@"Next value is %d", nextValue); if (pattern[patternIndex] == nextValue) { // progressing patternIndex++; if (patternIndex == sizeof(pattern)/sizeof(pattern[0])) { // triggered, show debug screen patternIndex = 0; [self showDebugScreen]; } } else { // reset patternIndex = 0; } } - (int) mapTouchLocationToInt: (CGPoint)touchLocation { // Upper left of screen is 1, upper right is 2 // Lower left is 3, lower right is 4 // If screen is rotated, things get confusing float midX = self.view.bounds.size.width / 2; float midY = self.view.bounds.size.height / 2; if(touchLocation.y < midY) { if(touchLocation.x < midX) { return 1; } else { return 2; } } else { if(touchLocation.x < midX) { return 3; } else { return 4; } } } - (void) showDebugScreen { // Take an appropriate action - display the information, or perhaps mail it to // your support team. RegistrationVC *registrationVC = [self.storyboard instantiateViewControllerWithIdentifier:@"Registration"]; [self presentViewController:registrationVC animated:YES completion:nil]; }
  3. Next, connect the tryKey: method to the triple-tap gesture. In the viewDidLoad method, add the following before the call to [super viewDidLoad].
    
    
    // Secret diagnostics // Connect triple-tap gesture to tryKey: UITapGestureRecognizer * gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action: @selector(tryKey:)]; gesture.numberOfTapsRequired = 3; gesture.cancelsTouchesInView = false; [self.view addGestureRecognizer:gesture]; self.view.userInteractionEnabled = true;

    In this example, the showDebugScreen opens RegistrationVC. Thus, we need to include its header to the imports of AttributesVC.m.

    
    
    // Secret diagnostics // reporting screen #import "RegistrationVC.h"

    The action that is run when the event is triggered is in showDebugScreen. In the case of the sample app, it already has a screen that reports mobile user ID, channel ID, and app key. The debug screen opens if the tap events are triggered.

    📘

    Note:

    Make note of the text for fieldString in tryKey. In the example, the sample app will not start an action unless the value text is in the nameTextField field. The action is not started even if the trigger triple-taps happen. We chose the text value because it's already present in the class and can be reused from the class pool. Doing so obscures the code from those who decompile the class.

    To prevent people from changing how the pattern is stored, change the method names. You can encode it or construct it through computation. As your code can be extracted and examined, keep passwords out of your diagnostics.

    Another option other than opening a screen is to create an email message. The email message can be addressed to your support group. You can do this by calling MFMailComposeViewController.

  1. Add the following variables to AttributeSampleActivity:
    
    
    // Secret diagnostics // Variables required // Change pattern to indicate the sequence of touches required to activate // the secret action. private int[] pattern = {1, 2, 3, 4, 3, 2, 3, 2}; private HashSet pointers = new HashSet(); private int patternIndex = 0;

    The pattern instance variable represents the number of touches on the screen that must occur for the event to be triggered. In this case, we need the user to touch the screen in the following pattern:

    • Touch the screen with one finger, then two, then three, and then four fingers.
    • After that, the user must lift one finger leaving the other three fingers touching the screen.
    • Then lift another finger leaving two fingers touching the screen.
    • Next, touch the screen with another finger in addition to the two for a total of three.
    • Finally, one finger must be lifted, leaving two.

    When the user follows the pattern, the event is triggered. Any deviation from the pattern resets it back to the beginning. We recommend you change the sample pattern. You might want to use a different pattern for each app or even each major revision of the app. Your support reps will need to be able to tell people how to trigger the diagnostic event. If it's too complicated or some of your devices don't support the detection of four touches on a screen, you may need to adjust it.

  2. Add the following three methods to the bottom of the Activity in AttributeSampleActivity in the sample:
    
    
    // Secret diagnostics // We override dispatchTouchEvent and count the number of fingers down // based on the values in pattern. See: // https://stackoverflow.com/questions/48441348/whats-the-best-way-to-implement-hidden-debug-options @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch(ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: // new gesture, reset pointers.clear(); patternIndex = 0; pointers.add(Integer.valueOf(ev.getPointerId(ev.getActionIndex()))); checkPatternAndOpen(); break; case MotionEvent.ACTION_POINTER_DOWN: // new touch pointers.add(Integer.valueOf(ev.getPointerId(ev.getActionIndex()))); checkPatternAndOpen(); break; case MotionEvent.ACTION_POINTER_UP: // touch released pointers.remove(Integer.valueOf(ev.getPointerId(ev.getActionIndex()))); checkPatternAndOpen(); } return super.dispatchTouchEvent(ev); } private void checkPatternAndOpen() { if (pattern[patternIndex] == pointers.size()) { // progressing patternIndex++; if (patternIndex == pattern.length) { // triggered, show debug screen patternIndex = 0; showDebugScreen(); } } else { // reset patternIndex = 0; } } private void showDebugScreen() { // This requires that a field be the correct value. If it's not, // nothing will happen - even if the user enters the correct // sequence of touches. Here I'm just reusing an existing string in the // class and testing with .equals, but you can do your own calculations // and obscure them as much as you want. if(!attributeKey.getValue().equals("AttributesActivity")) { return; } // Open your diagnostic screen here. Don't forget to include // app key, mobile user ID and channel! // See https://developer.goacoustic.com/acoustic-campaign/docs/access-registration-details/ // If you make it easy for the user to email those details to you, // your problem is likely to be resolved more quickly. RegistrationDetails registrationDetails = MceSdk.getRegistrationClient().getRegistrationDetails(getApplicationContext()); if (registrationDetails.getChannelId() == null || registrationDetails.getChannelId().length() == 0) { Toast.makeText(AttributesSampleActivity.this, resourcesHelper.getString("no_sdk_reg_toast"), Toast.LENGTH_SHORT).show(); } else { Intent intent = new Intent(); intent.setClass(getApplicationContext(), RegistrationDetailsSampleActivity.class); startActivity(intent); } }

    The action that is run when the event is triggered is in showDebugScreen. In the case of the sample app, it already has a screen which reports mobile user ID, channel ID, and app key. The debug screen opens if the touch events are appropriately triggered.

    📘

    Note:

    Make note of the text for attributeKey in showDebugScreen. In our example, the sample app will not start an action unless the "key" field contains the value "AttributesActivity." The action is not started even if the trigger touches happen. We chose the text value because it's already present in the class and can be reused from the class pool. Doing so obscures the code slightly from those who decompile the class.

    To prevent people from changing how the pattern is stored, change the method names. You can encode it or construct it through computation. As your code can be extracted and examined, keep passwords out of your diagnostics.

    Another option other than opening a screen is to create an email message. The email message can be addressed to your support group. You can do this by creating an Intent.

Finally, go to the Send User Attributes view, carry out the documented gesture actions, and then trigger the secret sequence and you will activate the routine and display the debug information.

No matter how you do it, give users some way to get diagnostic information. This information helps your company and our support team to resolve problems.