com.hrs.filltheform.dialog.FillTheFormDialog.java Source code

Java tutorial

Introduction

Here is the source code for com.hrs.filltheform.dialog.FillTheFormDialog.java

Source

/*
 * Copyright (C) 2015 HRS GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hrs.filltheform.dialog;

import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import android.widget.ImageButton;

import com.hrs.filltheform.R;
import com.hrs.filltheform.common.ConfigurationItem;
import com.hrs.filltheform.common.PropertyChangedListener;
import com.hrs.filltheform.data.ConfigurationVariables;
import com.hrs.filltheform.main.MainActivity;

import java.util.List;

/**
 * FillTheFormDialog shows a list of input data available for the selected AccessibilityNode. It also gives the option to open the MainActivity.
 */
public class FillTheFormDialog
        implements PropertyChangedListener, FillTheFormDialogModel.FillTheFormDialogModelHelper {

    private static final String FAST_MODE_ENABLED_KEY = "fast_mode_enabled_key";

    private WindowManager windowManager;
    private FrameLayout dialogView;
    private WindowManager.LayoutParams dialogParams;
    private View expandIcon;
    private View expandIconFastMode;
    private View dialogMenu;
    private RecyclerView configurationItemsView;
    private ImageButton fastModeButton;

    private final Context context;
    private final FillTheFormDialogModel model;
    private final int dialogInitialOffset;
    private final ConfigurationVariables configurationVariables;
    private ConfigurationItemsAdapter configurationItemsAdapter;
    private AccessibilityNodeInfoCompat selectedNodeInfo;

    public FillTheFormDialog(Context context) {
        this.context = context;
        // Get dialog dimensions
        Resources resources = context.getResources();
        int normalDialogWidth = resources.getDimensionPixelOffset(R.dimen.normal_dialog_width);
        int normalDialogHeight = resources.getDimensionPixelOffset(R.dimen.normal_dialog_height);
        int expandedDialogWidth = resources.getDimensionPixelOffset(R.dimen.expanded_dialog_width);
        int expandedDialogHeight = resources.getDimensionPixelOffset(R.dimen.expanded_dialog_height);
        this.dialogInitialOffset = normalDialogWidth;
        // Set up the model
        this.model = new FillTheFormDialogModel(this);
        model.setPropertyChangedListener(this);
        model.setNormalDialogDimensions(normalDialogWidth, normalDialogHeight);
        model.setExpandedDialogDimensions(expandedDialogWidth, expandedDialogHeight);
        model.setStatusBarHeight(getStatusBarHeight());
        // Prepare dialog view
        prepareDialogView();
        // Read fast mode config from shared prefs
        readFastModeConfigFromSharedPreferences();
        // Init configuration variables
        this.configurationVariables = new ConfigurationVariables(context);
    }

    public void showDialog(AccessibilityNodeInfoCompat nodeInfo, int accessibilityEventType,
            List<ConfigurationItem> selectedConfigurationItems) {
        if (selectedNodeInfo != null) {
            selectedNodeInfo.recycle();
            selectedNodeInfo = null;
        }
        selectedNodeInfo = nodeInfo;
        model.showDialog(getModelEventType(accessibilityEventType), selectedConfigurationItems);
    }

    private int getModelEventType(int accessibilityEventType) {
        switch (accessibilityEventType) {
        case AccessibilityEvent.TYPE_VIEW_LONG_CLICKED:
            return FillTheFormDialogModel.EVENT_TYPE_VIEW_LONG_CLICKED;
        case AccessibilityEvent.TYPE_VIEW_CLICKED:
            return FillTheFormDialogModel.EVENT_TYPE_VIEW_CLICKED;
        case AccessibilityEvent.TYPE_VIEW_FOCUSED:
            return FillTheFormDialogModel.EVENT_TYPE_VIEW_FOCUSED;
        default:
            return FillTheFormDialogModel.EVENT_TYPE_UNKNOWN;
        }
    }

    public void setConfigurationVariablePattern(String configurationVariablePattern) {
        if (model != null && configurationVariablePattern != null) {
            model.setConfigurationVariablePattern(configurationVariablePattern);
        }
    }

    private int getStatusBarHeight() {
        Resources resources = context.getResources();
        int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            return resources.getDimensionPixelSize(resourceId);
        }
        return 0;
    }

    private void prepareDialogView() {
        windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        dialogParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);

        dialogView = new FrameLayout(context);
        dialogView.setOnTouchListener(new View.OnTouchListener() {
            final Point screenSize = new Point();

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    windowManager.getDefaultDisplay().getSize(screenSize);
                    model.setScreenDimensions(screenSize.x, screenSize.y);
                    model.setInitialDialogPosition(dialogParams.x, dialogParams.y);
                    model.setInitialTouchEvent(event.getRawX(), event.getRawY());
                    return true;
                case MotionEvent.ACTION_UP:
                    model.onActionUp();
                    return true;
                case MotionEvent.ACTION_MOVE:
                    model.onActionMove(event.getRawX(), event.getRawY());
                    return true;
                }
                return false;
            }
        });

        @SuppressLint("InflateParams")
        final View dialogContent = LayoutInflater.from(context).inflate(R.layout.dialog, null);
        dialogMenu = dialogContent.findViewById(R.id.dialog_menu);
        expandIcon = dialogContent.findViewById(R.id.expand_icon);
        expandIconFastMode = dialogContent.findViewById(R.id.expand_icon_fast_mode);

        // Set up dialog content
        ImageButton closeButton = (ImageButton) dialogContent.findViewById(R.id.close_button);
        closeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                model.onCloseButtonClicked();
            }
        });
        ImageButton minimizeButton = (ImageButton) dialogContent.findViewById(R.id.minimize_button);
        minimizeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                model.onMinimizeButtonClicked();
            }
        });
        ImageButton openFillTheFormAppButton = (ImageButton) dialogContent
                .findViewById(R.id.open_fill_the_form_app_button);
        openFillTheFormAppButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                model.onOpenFillTheFormAppButtonClicked();
            }
        });
        fastModeButton = (ImageButton) dialogContent.findViewById(R.id.fast_mode_button);
        fastModeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                model.toggleFastMode();
            }
        });

        // Configuration items list
        configurationItemsView = (RecyclerView) dialogContent.findViewById(R.id.configuration_items_view);
        configurationItemsView.setHasFixedSize(true);
        configurationItemsView.setLayoutManager(new LinearLayoutManager(context));
        // Set adapter
        configurationItemsAdapter = new ConfigurationItemsAdapter(context, model);
        configurationItemsView.setAdapter(configurationItemsAdapter);
        dialogView.addView(dialogContent);
    }

    // Setting up the dialog position

    private void setUpInitialDialogPosition() {
        final Rect outBounds = new Rect();
        selectedNodeInfo.getBoundsInScreen(outBounds);

        dialogParams.gravity = Gravity.START | Gravity.TOP;

        dialogParams.width = model.getNormalDialogWidth();
        dialogParams.height = model.getNormalDialogHeight();

        dialogParams.x = outBounds.right - dialogInitialOffset;
        dialogParams.y = outBounds.top;

        model.setDialogPosition(dialogParams.x, dialogParams.y);
    }

    // Fill selected node with data

    private void fillTheSelectedNodeWithData(String inputData) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            fillPreLollipop(inputData);
        } else {
            Bundle arguments = new Bundle();
            arguments.putCharSequence(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, inputData);
            selectedNodeInfo.performAction(AccessibilityNodeInfoCompat.ACTION_SET_TEXT, arguments);
        }
    }

    private void fillPreLollipop(String inputData) {
        boolean shouldRecycle = true;
        AccessibilityNodeInfoCompat nodeInfo = null;
        List<AccessibilityNodeInfoCompat> nodeInfoList = selectedNodeInfo
                .findAccessibilityNodeInfosByViewId(selectedNodeInfo.getViewIdResourceName());
        if (nodeInfoList != null && nodeInfoList.size() > 0) {
            nodeInfo = nodeInfoList.get(0);
        }
        if (nodeInfo == null) {
            nodeInfo = selectedNodeInfo;
            shouldRecycle = false;
        }
        selectAll(nodeInfo);
        copyToClipboard(inputData);
        nodeInfo.performAction(AccessibilityNodeInfoCompat.ACTION_PASTE);
        if (shouldRecycle) {
            nodeInfo.recycle();
        }
    }

    private void copyToClipboard(String inputData) {
        ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText(inputData, inputData);
        clipboard.setPrimaryClip(clip);
    }

    private void selectAll(AccessibilityNodeInfoCompat nodeInfo) {
        if (!TextUtils.isEmpty(nodeInfo.getText())) {
            Bundle arguments = new Bundle();
            arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT, 0);
            arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT,
                    nodeInfo.getText().length());
            nodeInfo.performAction(AccessibilityNodeInfoCompat.ACTION_SET_SELECTION, arguments);
        }
    }

    private void pasteTheData(String inputData) {
        copyToClipboard(inputData);
        selectedNodeInfo.performAction(AccessibilityNodeInfoCompat.ACTION_PASTE);
    }

    // FillTheFormDialogModelHelper methods

    @Override
    public boolean isConfigurationVariableKey(String variableKey) {
        return configurationVariables.isConfigurationVariableKey(variableKey);
    }

    @Override
    public String getConfigurationVariableValue(String variableKey) {
        return configurationVariables.getValue(variableKey);
    }

    // Fast mode shared prefs management

    private void storeFastModeConfigInSharedPreferences() {
        SharedPreferences.Editor editor = context
                .getSharedPreferences(MainActivity.MY_SHARED_PREFERENCES, Context.MODE_PRIVATE).edit();
        editor.putBoolean(FAST_MODE_ENABLED_KEY, model.isFastModeEnabled());
        editor.apply();
    }

    private void readFastModeConfigFromSharedPreferences() {
        SharedPreferences prefs = context.getSharedPreferences(MainActivity.MY_SHARED_PREFERENCES,
                Context.MODE_PRIVATE);
        boolean fastModeEnabled = prefs.getBoolean(FAST_MODE_ENABLED_KEY, false);
        model.setFastModeEnabled(fastModeEnabled);
    }

    // Handle property changes

    @Override
    public void onPropertyChanged(String property) {
        switch (property) {
        case FillTheFormDialogModel.PROPERTY_DIALOG_VISIBILITY:
            if (model.isDialogVisible()) {
                windowManager.addView(dialogView, dialogParams);
            } else {
                removeDialogView();
            }
            break;
        case FillTheFormDialogModel.PROPERTY_EXPAND_ICON:
            if (model.isExpandIconVisible()) {
                expandIcon.setVisibility(View.VISIBLE);
            } else {
                expandIcon.setVisibility(View.GONE);
            }
            break;
        case FillTheFormDialogModel.PROPERTY_EXPAND_ICON_FAST_MODE:
            if (model.isFastModeEnabled()) {
                expandIconFastMode.setVisibility(View.VISIBLE);
            } else {
                expandIconFastMode.setVisibility(View.GONE);
            }
            break;
        case FillTheFormDialogModel.PROPERTY_CONFIGURATION_ITEMS_LIST:
            configurationItemsAdapter.notifyDataSetChanged();
            configurationItemsView.scrollToPosition(0);
            break;
        case FillTheFormDialogModel.PROPERTY_ACTION_SET_TEXT:
            fillTheSelectedNodeWithData(model.getSelectedConfigItemValue());
            configurationItemsAdapter.notifyDataSetChanged();
            break;
        case FillTheFormDialogModel.PROPERTY_ACTION_PASTE:
            pasteTheData(model.getSelectedConfigItemValue());
            configurationItemsAdapter.notifyDataSetChanged();
            break;
        case FillTheFormDialogModel.PROPERTY_DIALOG_EXPANDED:
            if (model.isDialogExpanded()) {
                dialogMenu.setVisibility(View.VISIBLE);
                dialogParams.width = model.getExpandedDialogWidth();
                dialogParams.height = model.getExpandedDialogHeight();
            } else {
                dialogMenu.setVisibility(View.GONE);
                dialogParams.width = model.getNormalDialogWidth();
                dialogParams.height = model.getNormalDialogHeight();
            }
            updateDialogViewSize();
            break;
        case FillTheFormDialogModel.PROPERTY_DIALOG_INITIAL_POSITION:
            if (!model.isDialogExpanded()) {
                setUpInitialDialogPosition();
            }
            break;
        case FillTheFormDialogModel.PROPERTY_DIALOG_POSITION:
            dialogParams.x = model.getDialogPositionX();
            dialogParams.y = model.getDialogPositionY();
            windowManager.updateViewLayout(dialogView, dialogParams);
            break;
        case FillTheFormDialogModel.PROPERTY_OPEN_FILL_THE_FORM_APP_BUTTON:
            Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
            launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
            context.startActivity(launchIntent);
            break;
        case FillTheFormDialogModel.PROPERTY_CLEAR_CONFIGURATION_VARIABLES:
            configurationVariables.clear();
            break;
        case FillTheFormDialogModel.PROPERTY_FAST_MODE:
            storeFastModeConfigInSharedPreferences();
            break;
        case FillTheFormDialogModel.PROPERTY_FAST_MODE_BUTTON:
            if (model.isFastModeEnabled()) {
                fastModeButton.setImageResource(R.drawable.ic_fast_mode);
            } else {
                fastModeButton.setImageResource(R.drawable.ic_normal_mode);
            }
            break;
        default:
            break;
        }
    }

    private void removeDialogView() {
        if (windowManager != null && dialogView != null && dialogView.isAttachedToWindow()) {
            windowManager.removeView(dialogView);
        }
    }

    private void updateDialogViewSize() {
        if (windowManager != null && dialogView != null && dialogView.isAttachedToWindow()) {
            windowManager.updateViewLayout(dialogView, dialogParams);
        }
    }

    // FillTheFormCompanion support

    public void hideDialog() {
        model.hideDialog();
    }

    public void setFastMode() {
        model.setFastModeEnabled(true);
    }

    public void setNormalMode() {
        model.setFastModeEnabled(false);
    }

    public void selectNextProfile() {
        model.selectNextProfile();
    }

    public void setProfiles(List<String> profiles) {
        model.setProfiles(profiles);
    }
}