com.duy.pascal.ui.debug.activities.DebugActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.duy.pascal.ui.debug.activities.DebugActivity.java

Source

/*
 *  Copyright (c) 2017 Tran Le Duy
 *
 * 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.duy.pascal.ui.debug.activities;

import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Vibrator;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.support.design.widget.TextInputLayout;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.PopupWindow;
import android.widget.TextView;
import android.widget.Toast;

import com.duy.pascal.interperter.ast.node.Node;
import com.duy.pascal.interperter.ast.runtime.value.AssignableValue;
import com.duy.pascal.interperter.ast.runtime.value.RuntimeValue;
import com.duy.pascal.interperter.ast.variablecontext.VariableContext;
import com.duy.pascal.interperter.config.DebugMode;
import com.duy.pascal.interperter.debugable.IDebugListener;
import com.duy.pascal.interperter.declaration.lang.function.AbstractCallableFunction;
import com.duy.pascal.interperter.libraries.io.IOLib;
import com.duy.pascal.interperter.linenumber.LineNumber;
import com.duy.pascal.ui.R;
import com.duy.pascal.ui.code.CompileManager;
import com.duy.pascal.ui.code.ExceptionManager;
import com.duy.pascal.ui.common.utils.IOUtils;
import com.duy.pascal.ui.debug.CallStack;
import com.duy.pascal.ui.debug.fragments.FragmentFrame;
import com.duy.pascal.ui.dialog.DialogHelper;
import com.duy.pascal.ui.editor.view.EditorView;
import com.duy.pascal.ui.editor.view.LineUtils;
import com.duy.pascal.ui.runnable.AbstractExecActivity;
import com.duy.pascal.ui.runnable.IProgramHandler;
import com.duy.pascal.ui.utils.DLog;
import com.duy.pascal.ui.view.LockableScrollView;
import com.duy.pascal.ui.view.console.ConsoleView;
import com.google.firebase.analytics.FirebaseAnalytics;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;

public class DebugActivity extends AbstractExecActivity implements IDebugListener, IProgramHandler {

    private final Handler mHandler = new Handler();
    private ConsoleView mConsoleView;
    private EditorView mCodeView;
    private Toolbar toolbar;
    private LockableScrollView mScrollView;
    private AlertDialog mAlertDialog;
    private PopupWindow mPopupWindow;
    private AtomicBoolean mEnded = new AtomicBoolean(false);
    private Vibrator mVibrator;
    private Runnable showDialog = new Runnable() {
        @Override
        public void run() {

        }
    };

    private FragmentFrame mFameFragment;
    private DrawerLayout mDrawerLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        hideStatusBar();
        setContentView(R.layout.activity_debug);
        bindView();

        FirebaseAnalytics.getInstance(this).logEvent("open_debug", new Bundle());
        mVibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);

        mConsoleView.updateSize();
        mConsoleView.showPrompt();
        mConsoleView.writeString("Enable DEBUG mode\n");
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                debugProgram();
            }
        }, 100);
    }

    private void bindView() {
        toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        mConsoleView = findViewById(R.id.console_view);
        mCodeView = findViewById(R.id.code_editor);
        mScrollView = findViewById(R.id.vertical_scroll);
        mCodeView.setVerticalScroll(mScrollView);

        mDrawerLayout = findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, toolbar,
                R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        mDrawerLayout.addDrawerListener(drawerToggle);
        drawerToggle.syncState();

        mFameFragment = (FragmentFrame) getSupportFragmentManager().findFragmentByTag("FragmentFrame");
    }

    @Override
    public void onBackPressed() {
        if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
            mDrawerLayout.closeDrawers();
            return;
        }
        super.onBackPressed();
    }

    @Override
    public void onError(Throwable e) {
        if (isFinishing())
            return;

        ExceptionManager exceptionManager = new ExceptionManager(this);
        DialogHelper.createFinishDialog(this, R.string.runtime_error, exceptionManager.getMessage(e)).show();
        if (DEBUG)
            e.printStackTrace();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_debug, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public void showDialogComplete() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage(R.string.complete)
                .setPositiveButton(R.string.exit, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                        mMessageHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                finish();
                            }
                        }, 100);
                    }
                }).setNegativeButton(R.string.view_console, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
        try {
            builder.create().show();
        } catch (Exception ignored) {
        }
    }

    @Override
    public void showKeyBoard() {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        mConsoleView.requestFocus();
        if (imm != null) {
            imm.showSoftInput(mConsoleView, InputMethodManager.SHOW_IMPLICIT);
        }
    }

    @Override
    public void debugProgram() {
        Bundle extras = getIntent().getExtras();
        if (extras != null && extras.getSerializable(CompileManager.EXTRA_FILE) != null) {
            mFilePath = ((File) extras.getSerializable(CompileManager.EXTRA_FILE)).getPath();
            if (mFilePath.isEmpty())
                return;
            File file = new File(mFilePath);
            if (!file.exists()) {
                finish();
                return;
            }
            String code = null;
            try {
                code = IOUtils.toString(new FileReader(file));
            } catch (IOException e) {
                e.printStackTrace();
                finish();
                return;
            }
            mCodeView.setTextHighlighted(code);
            mCodeView.highlightAll();

            setTitle(file.getName());
            mEnded.set(false);
            setEnableDebug(true); //disable DEBUG
            createAndRunProgram(mFilePath); //execute file
        } else {
            finish();
        }
    }

    @Override
    public void onLine(Node node, @Nullable final LineNumber lineNumber) {
        DLog.d(TAG, "onLine() called with: runtimeValue = [" + node + "], line = [" + lineNumber + "]");
        if (lineNumber == null) {
            return;
        }
        scrollTo(lineNumber);
    }

    private void scrollTo(@NonNull final LineNumber lineNumber) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                mCodeView.pinLine(lineNumber);
                int yCoordinate = LineUtils.getYAtLine(mScrollView, mCodeView.getLineCount(), lineNumber.getLine());
                int heightVisible = mCodeView.getHeightVisible();
                if (!(mScrollView.getScrollY() < yCoordinate
                        && mScrollView.getScrollY() + heightVisible > yCoordinate)) {
                    mScrollView.smoothScrollTo(0, yCoordinate);
                }
            }
        });
    }

    @Override
    public void onLine(RuntimeValue executable, final LineNumber lineNumber) {
        DLog.d(TAG,
                "onLine() called with: executable = [" + executable.getClass() + "], line = [" + lineNumber + "]");
        if (lineNumber == null)
            return;
        scrollTo(lineNumber);
    }

    @Override
    public void onEvaluatingExpr(LineNumber lineNumber, String expression) {
        DLog.d(TAG, "onEvaluatingExpr() called with: line = [" + lineNumber + "], " + "expression = [" + expression
                + "]");

    }

    /**
     * This method will be show a small popup window for show result of expression
     *
     * @param lineNumber - the line of expression
     * @param expr     - input
     * @param result   - result value of expr
     */
    @Override
    public void onEvaluatedExpr(final LineNumber lineNumber, final String expr, final String result) {
        DLog.d(TAG, "onEvaluatedExpr() called with: line = [" + lineNumber + "], expr = [" + expr + "], result = ["
                + result + "]");
        showPopupAt(lineNumber, expr + " = " + result);
    }

    @WorkerThread
    private void showPopupAt(final LineNumber lineNumber, final String msg) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (isFinishing())
                    return;
                //get relative position of expression at edittext
                Point position = mCodeView.getDebugPosition(lineNumber.getLine(), lineNumber.getColumn(),
                        Gravity.TOP);
                DLog.d(TAG, "generate: " + position);
                dismissPopup();
                //create new popup
                PopupWindow window = new PopupWindow(DebugActivity.this);
                LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
                View container = inflater.inflate(R.layout.popup_expr_result, null);
                container.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
                int windowHeight = container.getMeasuredHeight();
                int windowWidth = container.getMeasuredWidth();

                window.setContentView(container);
                window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
                window.setTouchable(true);
                window.setSplitTouchEnabled(true);
                window.setOutsideTouchable(true);

                window.showAtLocation(mCodeView, Gravity.NO_GRAVITY, position.x - windowWidth / 3,
                        position.y + toolbar.getHeight() - windowHeight);

                TextView txtResult = container.findViewById(R.id.txt_result);
                txtResult.setText(msg);
                AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.5f);
                alphaAnimation.setDuration(1000);
                alphaAnimation.setRepeatMode(Animation.REVERSE);
                alphaAnimation.setRepeatCount(Animation.INFINITE);
                txtResult.startAnimation(alphaAnimation);
                DebugActivity.this.mPopupWindow = window;
            }
        });
    }

    private void dismissPopup() {
        if (mPopupWindow != null) {
            if (this.mPopupWindow.isShowing()) {
                mPopupWindow.dismiss();
            }
        }
    }

    @Override
    public void onAssignValue(LineNumber lineNumber, final AssignableValue left, @NonNull final Object oldValue,
            final Object newValue, @NonNull VariableContext context) {
        DLog.d(TAG, "onAssignValue() called with: lineNumber = [" + lineNumber + "], left = [" + left
                + "], value = [" + newValue + "]");
    }

    @Override
    public void onPreFunctionCall(AbstractCallableFunction function, RuntimeValue[] arguments) {
        DLog.d(TAG, "onPreFunctionCall() called with: function = [" + function + "], arguments = ["
                + Arrays.toString(arguments) + "]");

    }

    @Override
    public void onFunctionCalled(AbstractCallableFunction function, RuntimeValue[] arguments, Object result) {
        DLog.d(TAG, "onFunctionCalled() called with: function = [" + function + "], arguments = ["
                + Arrays.toString(arguments) + "], result = [" + result + "]");

    }

    @Override
    public void onEvalParameterFunction(LineNumber lineNumber, String name, @Nullable Object value) {
        if (value != null) {
            showPopupAt(lineNumber, name + " = " + value.toString());
        }
    }

    @Override
    public void onFinish() {
        dismissPopup();
        this.mEnded.set(true);
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                mCodeView.pinLine(null);
            }
        });
    }

    @Override
    public void showMessage(LineNumber pos, String msg) {
        showPopupAt(pos, msg);
    }

    @Override
    public void onValueVariableChanged(final CallStack currentFrame) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                mFameFragment.update(currentFrame);
            }
        });
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int i = item.getItemId();
        if (i == R.id.action_step_info) {
            if (mProgram != null) {
                mProgram.setDebugMode(DebugMode.STEP_INFO);
            }
            resumeProgram();
            return true;
        } else if (i == R.id.action_step_over) {
            if (mProgram != null) {
                mProgram.setDebugMode(DebugMode.STEP_OVER);
            }
            resumeProgram();
            return true;
        } else if (i == R.id.action_add_watch) {
            addWatchVariable();
            return true;

        } else if (i == R.id.action_show_soft) {
            showKeyBoard();
            return true;

        } else if (i == R.id.action_rerun) {
            CompileManager.debug(this, mFilePath);
            finish();
            return true;

        }
        return super.onOptionsItemSelected(item);
    }

    private void resumeProgram() {
        if (mProgram != null && !mEnded.get())
            mProgram.resume();
        else {
            mVibrator.vibrate(100);
            Toast.makeText(this, R.string.program_stopped, Toast.LENGTH_SHORT).show();
        }
    }

    private void addWatchVariable() {
    }

    @Override
    protected void onDestroy() {
        if (mAlertDialog != null)
            mAlertDialog.dismiss();
        super.onDestroy();
    }

    @Override
    public ConsoleView getConsoleView() {
        return mConsoleView;
    }

    @Override
    public void onNewMessage(String msg) {
        DLog.d(TAG, "onNewMessage() called with: msg = [" + msg + "]");

    }

    @Override
    public synchronized void startInput(final IOLib lock) {
        this.mLock = lock;
        showDialogInput();
    }

    private void showDialogInput() {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                AlertDialog.Builder builder = new AlertDialog.Builder(DebugActivity.this);
                builder.setView(R.layout.dialog_input);
                mAlertDialog = builder.create();
                mAlertDialog = builder.create();
                mAlertDialog.setCanceledOnTouchOutside(false);
                if (!isFinishing()) {
                    mAlertDialog.show();
                    final EditText editText = mAlertDialog.findViewById(R.id.edit_input);
                    ((TextInputLayout) mAlertDialog.findViewById(R.id.hint))
                            .setHint(getString(R.string.enter_data));
                    View.OnClickListener onClickListener = new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if (mLock instanceof IOLib) {
                                ((IOLib) mLock).setInputBuffer(editText.getText().toString());
                            }
                            mConsoleView.writeString(editText.getText().toString());
                            mAlertDialog.cancel();
                        }
                    };
                    mAlertDialog.findViewById(R.id.btn_ok).setOnClickListener(onClickListener);
                    mAlertDialog.findViewById(R.id.btn_cancel).setVisibility(View.GONE);
                }
            }
        });
    }

    @Override
    public Activity getActivity() {
        return this;
    }
}